AI-XGBoost

 view release on metacpan or  search on metacpan

examples/iris.pl  view on Meta::CPAN

    versicolor => 1,
    virginica => 2
);

my $iris = Data::Dataset::Classic::Iris::get();

# Split train and test, label and features
my $train_dataset = [map {$iris->{$_}} grep {$_ ne 'species'} keys %$iris];
my $test_dataset = [map {$iris->{$_}} grep {$_ ne 'species'} keys %$iris];

sub transpose {
# Transposing without using PDL, Data::Table, Data::Frame or other modules
# to keep minimal dependencies
    my $array = shift;
    my @aux = ();
    for my $row (@$array) {
        for my $column (0 .. scalar @$row - 1) {
            push @{$aux[$column]}, $row->[$column];
        }
    }
    return \@aux;

lib/AI/XGBoost.pm  view on Meta::CPAN

use strict;
use warnings;

use AI::XGBoost::Booster;
use Exporter::Easy ( OK => ['train'] );

our $VERSION = '0.11';    # VERSION

# ABSTRACT: Perl wrapper for XGBoost library L<https://github.com/dmlc/xgboost>

sub train {
    my %args = @_;
    my ( $params, $data, $number_of_rounds ) = @args{qw(params data number_of_rounds)};

    my $booster = AI::XGBoost::Booster->new( cache => [$data] );
    if ( defined $params ) {
        while ( my ( $name, $value ) = each %$params ) {
            $booster->set_param( $name, $value );
        }
    }
    for my $iteration ( 0 .. $number_of_rounds - 1 ) {

lib/AI/XGBoost.pm  view on Meta::CPAN

     versicolor => 1,
     virginica => 2
 );
 
 my $iris = Data::Dataset::Classic::Iris::get();
 
 # Split train and test, label and features
 my $train_dataset = [map {$iris->{$_}} grep {$_ ne 'species'} keys %$iris];
 my $test_dataset = [map {$iris->{$_}} grep {$_ ne 'species'} keys %$iris];
 
 sub transpose {
 # Transposing without using PDL, Data::Table, Data::Frame or other modules
 # to keep minimal dependencies
     my $array = shift;
     my @aux = ();
     for my $row (@$array) {
         for my $column (0 .. scalar @$row - 1) {
             push @{$aux[$column]}, $row->[$column];
         }
     }
     return \@aux;

lib/AI/XGBoost/Booster.pm  view on Meta::CPAN


# ABSTRACT: XGBoost main class for training, prediction and evaluation

use Moose;
use AI::XGBoost::CAPI qw(:all);
use namespace::autoclean;

has _handle => ( is       => 'rw',
                 init_arg => undef, );

sub update {
    my $self = shift;
    my %args = @_;
    my ( $iteration, $dtrain ) = @args{qw(iteration dtrain)};
    XGBoosterUpdateOneIter( $self->_handle, $iteration, $dtrain->handle );
    return $self;
}

sub boost {
    my $self = shift;
    my %args = @_;
    my ( $dtrain, $grad, $hess ) = @args{qw(dtrain grad hess)};
    XGBoosterBoostOneIter( $self->_handle, $dtrain, $grad, $hess );
    return $self;
}

sub predict {
    my $self        = shift;
    my %args        = @_;
    my $data        = $args{'data'};
    my $result      = XGBoosterPredict( $self->_handle, $data->handle );
    my $result_size = scalar @$result;
    my $matrix_rows = $data->num_row;
    if ( $result_size != $matrix_rows && $result_size % $matrix_rows == 0 ) {
        my $col_size = $result_size / $matrix_rows;
        return [ map { [ @$result[ $_ * $col_size .. $_ * $col_size + $col_size - 1 ] ] } 0 .. $matrix_rows - 1 ];
    }
    return $result;
}

sub set_param {
    my $self = shift;
    my ( $name, $value ) = @_;
    XGBoosterSetParam( $self->_handle, $name, $value );
    return $self;
}

sub set_attr {
    my $self = shift;
    my ( $name, $value ) = @_;
    XGBoosterSetAttr( $self->_handle, $name, $value );
    return $self;
}

sub get_attr {
    my $self = shift;
    my ($name) = @_;
    XGBoosterGetAttr( $self->_handle, $name );
}

sub get_score {
    my $self = shift;
    my %args = @_;
    my ( $fmap, $importance_type ) = @args{qw(fmap importance_type)};

    if ( $importance_type eq "weight" ) {
        my @trees = $self->get_dump;
    } else {

    }

}

sub get_dump {
    my $self = shift;
    return XGBoosterDumpModelEx( $self->_handle, "", 1, "text" );
}

sub attributes {
    my $self = shift;
    return { map { $_ => $self->get_attr($_) } @{ XGBoosterGetAttrNames( $self->_handle ) } };
}

sub TO_JSON {
    my $self = shift;
    my $trees = XGBoosterDumpModelEx( $self->_handle, "", 1, "json" );
    return "[" . join( ',', @$trees ) . "]";
}

sub BUILD {
    my $self = shift;
    my $args = shift;
    $self->_handle( XGBoosterCreate( [ map { $_->handle } @{ $args->{'cache'} } ] ) );
}

sub DEMOLISH {
    my $self = shift();
    XGBoosterFree( $self->_handle );
}

__PACKAGE__->meta->make_immutable();

1;

__END__

lib/AI/XGBoost/CAPI.pm  view on Meta::CPAN

    ]
);
use AI::XGBoost::CAPI::RAW;
use FFI::Platypus;
use Exception::Class ( 'XGBoostException' );

our $VERSION = '0.11';    # VERSION

# ABSTRACT: Perl wrapper for XGBoost C API https://github.com/dmlc/xgboost

sub XGDMatrixCreateFromFile {
    my ( $filename, $silent ) = @_;
    $silent //= 1;
    my $matrix = 0;
    my $error = AI::XGBoost::CAPI::RAW::XGDMatrixCreateFromFile( $filename, $silent, \$matrix );
    _CheckCall($error);
    return $matrix;
}

sub XGDMatrixCreateFromMat {
    my ( $data, $missing ) = @_;
    $missing //= "NaN";

    # TODO Support simple arrays
    # TODO Support PDL
    # TODO ¿Adapters?
    my $data_adapter = [ map { @$_ } @$data ];
    my $nrows        = scalar @$data;
    my $ncols        = scalar @{ $data->[0] };
    my $matrix       = 0;
    my $error = AI::XGBoost::CAPI::RAW::XGDMatrixCreateFromMat( $data_adapter, $nrows, $ncols, $missing, \$matrix );
    _CheckCall($error);
    return $matrix;
}

sub XGDMatrixNumRow {
    my ($matrix) = @_;
    my $rows = 0;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixNumRow( $matrix, \$rows ) );
    return $rows;
}

sub XGDMatrixNumCol {
    my ($matrix) = @_;
    my $cols = 0;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixNumCol( $matrix, \$cols ) );
    return $cols;
}

sub XGDMatrixSetFloatInfo {
    my ( $matrix, $info, $data ) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixSetFloatInfo( $matrix, $info, $data, scalar @$data ) );
}

sub XGDMatrixGetFloatInfo {
    my ( $matrix, $info ) = @_;
    my $out_len    = 0;
    my $out_result = 0;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixGetFloatInfo( $matrix, $info, \$out_len, \$out_result ) );
    my $ffi = FFI::Platypus->new();
    return $ffi->cast( opaque => "float[$out_len]", $out_result );
}

sub XGDMatrixSetUintInfo {
    my ( $matrix, $info, $data ) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixSetUintInfo( $matrix, $info, $data, scalar @$data ) );
}

sub XGDMatrixGetUintInfo {
    my ( $matrix, $info ) = @_;
    my $out_len    = 0;
    my $out_result = 0;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixGetUintInfo( $matrix, $info, \$out_len, \$out_result ) );
    my $ffi = FFI::Platypus->new();
    return $ffi->cast( opaque => "uint32[$out_len]", $out_result );
}

sub XGDMatrixSaveBinary {
    my ( $matrix, $filename, $silent ) = @_;
    $silent //= 1;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixSaveBinary( $matrix, $filename, $silent ) );
}

sub XGDMatrixSliceDMatrix {
    my ( $matrix, $list_of_indices ) = @_;
    my $new_matrix = 0;
    my $error = AI::XGBoost::CAPI::RAW::XGDMatrixSliceDMatrix( $matrix, $list_of_indices, scalar @$list_of_indices,
                                                               \$new_matrix );
    _CheckCall($error);
    return $new_matrix;
}

sub XGDMatrixFree {
    my ($matrix) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGDMatrixFree($matrix) );
    return ();
}

sub XGBoosterCreate {
    my ($matrices) = @_;
    my $booster = 0;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterCreate( $matrices, scalar @$matrices, \$booster ) );
    return $booster;
}

sub XGBoosterSetParam {
    my ( $booster, $name, $value ) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterSetParam( $booster, $name, $value ) );
}

sub XGBoosterSetAttr {
    my ( $booster, $name, $value ) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterSetAttr( $booster, $name, $value ) );
}

sub XGBoosterGetAttr {
    my ( $booster, $name ) = @_;
    my $value   = 0;
    my $success = -1;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterGetAttr( $booster, $name, \$value, \$success ) );
    if ($success) {
        my $ffi = FFI::Platypus->new();
        return $ffi->cast( opaque => "string", $value );
    }
    return ();
}

sub XGBoosterGetAttrNames {
    my ($booster)  = @_;
    my $out_len    = 0;
    my $out_result = 0;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterGetAttrNames( $booster, \$out_len, \$out_result ) );
    my $ffi = FFI::Platypus->new();
    $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
    return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
}

sub XGBoosterUpdateOneIter {
    my ( $booster, $iter, $train_matrix ) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterUpdateOneIter( $booster, $iter, $train_matrix ) );
    return ();
}

sub XGBoosterBoostOneIter {
    my ( $booster, $train_matrix, $gradient, $hessian ) = @_;
    my $out_result = 0;
    _CheckCall(
                AI::XGBoost::CAPI::RAW::XGBoosterBoostOneIter(
                                                        $booster, $train_matrix, $gradient, $hessian, scalar(@$gradient)
                )
    );
    return ();
}

sub XGBoosterEvalOneIter {
    my ( $booster, $iter, $matrices, $matrices_names ) = @_;
    my $out_result                     = 0;
    my $number_of_matrices             = scalar @$matrices;
    my $ffi                            = FFI::Platypus->new();
    my $array_of_opaque_matrices_names = [ map { $ffi->cast( string => "opaque", $_ ) } @$matrices_names ];
    _CheckCall(
                AI::XGBoost::CAPI::RAW::XGBoosterEvalOneIter(
                                                            $booster, $iter, $matrices, $array_of_opaque_matrices_names,
                                                            $number_of_matrices, \$out_result
                )
    );
    $out_result = $ffi->cast( opaque => "opaque[$number_of_matrices]", $out_result );
    return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
}

sub XGBoosterPredict {
    my ( $booster, $data_matrix, $option_mask, $ntree_limit ) = @_;
    $option_mask //= 0;
    $ntree_limit //= 0;
    my $out_len    = 0;
    my $out_result = 0;
    _CheckCall(
                AI::XGBoost::CAPI::RAW::XGBoosterPredict( $booster,     $data_matrix, $option_mask,
                                                          $ntree_limit, \$out_len,    \$out_result
                )
    );
    my $ffi = FFI::Platypus->new();
    return $ffi->cast( opaque => "float[$out_len]", $out_result );
}

sub XGBoosterDumpModel {
    my ( $booster, $feature_map, $with_stats ) = @_;
    $feature_map //= "";
    $with_stats  //= 1;
    my $out_len    = 0;
    my $out_result = 0;
    _CheckCall(
           AI::XGBoost::CAPI::RAW::XGBoosterDumpModel( $booster, $feature_map, $with_stats, \$out_len, \$out_result ) );
    my $ffi = FFI::Platypus->new();
    $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
    return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
}

sub XGBoosterDumpModelEx {
    my ( $booster, $feature_map, $with_stats, $format ) = @_;
    $feature_map //= "";
    $with_stats  //= 1;
    my $out_len    = 0;
    my $out_result = 0;
    _CheckCall(
                AI::XGBoost::CAPI::RAW::XGBoosterDumpModelEx(
                                                   $booster, $feature_map, $with_stats, $format, \$out_len, \$out_result
                )
    );
    my $ffi = FFI::Platypus->new();
    $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
    return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
}

sub XGBoosterDumpModelWithFeatures {
    my ( $booster, $feature_names, $feature_types, $with_stats ) = @_;
    $with_stats //= 1;
    my $out_len                       = 0;
    my $out_result                    = 0;
    my $ffi                           = FFI::Platypus->new();
    my $number_of_features            = scalar @$feature_names;
    my $array_of_opaque_feature_names = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_names ];
    my $array_of_opaque_feature_types = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_types ];
    _CheckCall(
                AI::XGBoost::CAPI::RAW::XGBoosterDumpModelWithFeatures( $booster, $number_of_features,
                                                                        $array_of_opaque_feature_names,
                                                                        $array_of_opaque_feature_types,
                                                                        $with_stats, \$out_len, \$out_result
                )
    );
    $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
    return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
}

sub XGBoosterDumpModelExWithFeatures {
    my ( $booster, $feature_names, $feature_types, $with_stats, $format ) = @_;
    my $out_len                       = 0;
    my $out_result                    = 0;
    my $ffi                           = FFI::Platypus->new();
    my $number_of_features            = scalar @$feature_names;
    my $array_of_opaque_feature_names = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_names ];
    my $array_of_opaque_feature_types = [ map { $ffi->cast( string => "opaque", $_ ) } @$feature_types ];
    _CheckCall(
                AI::XGBoost::CAPI::RAW::XGBoosterDumpModelExWithFeatures( $booster, $number_of_features,
                                                                          $array_of_opaque_feature_names,
                                                                          $array_of_opaque_feature_types,
                                                                          $with_stats, $format, \$out_len, \$out_result
                )
    );
    $out_result = $ffi->cast( opaque => "opaque[$out_len]", $out_result );
    return [ map { $ffi->cast( opaque => "string", $_ ) } @$out_result ];
}

sub XGBoosterFree {
    my ($booster) = @_;
    _CheckCall( AI::XGBoost::CAPI::RAW::XGBoosterFree($booster) );
    return ();
}

# _CheckCall
#
#  Check return code and if necesary, launch an exception
#
sub _CheckCall {
    my ($return_code) = @_;
    if ($return_code) {
        my $error_message = AI::XGBoost::CAPI::RAW::XGBGetLastError();
        XGBoostException->throw( error => $error_message );
    }
}

1;

__END__

lib/AI/XGBoost/DMatrix.pm  view on Meta::CPAN


# ABSTRACT: XGBoost class for data

use Moose;
use AI::XGBoost::CAPI qw(:all);
use Carp;
use namespace::autoclean;

has handle => ( is => 'ro', );

sub From {
    my ( $package, %args ) = @_;
    return __PACKAGE__->FromFile( filename => $args{file}, silent => $args{silent} ) if ( defined $args{file} );
    return __PACKAGE__->FromMat( map { $_ => $args{$_} if defined $_ } qw(matrix missing label) )
      if ( defined $args{matrix} );
    Carp::cluck( "I don't know how to build a " . __PACKAGE__ . " with this data: " . join( ", ", %args ) );
}

sub FromFile {
    my ( $package, %args ) = @_;
    my $handle = XGDMatrixCreateFromFile( @args{qw(filename silent)} );
    return __PACKAGE__->new( handle => $handle );
}

sub FromMat {
    my ( $package, %args ) = @_;
    my $handle = XGDMatrixCreateFromMat( @args{qw(matrix missing)} );
    my $matrix = __PACKAGE__->new( handle => $handle );
    if ( defined $args{label} ) {
        $matrix->set_label( $args{label} );
    }
    return $matrix;
}

sub set_float_info {
    my $self = shift();
    my ( $field, $info ) = @_;
    XGDMatrixSetFloatInfo( $self->handle, $field, $info );
    return $self;
}

sub set_float_info_pdl {
    my $self = shift();
    my ( $field, $info ) = @_;
    XGDMatrixSetFloatInfo( $self->handle, $field, $info->flat()->unpdl() );
    return $self;
}

sub get_float_info {
    my $self  = shift();
    my $field = shift();
    XGDMatrixGetFloatInfo( $self->handle, $field );
}

sub set_uint_info {
    my $self = shift();
    my ( $field, $info ) = @_;
    XGDMatrixSetUintInfo( $self->handle, $field, $info );
    return $self;
}

sub get_uint_info {
    my $self  = shift();
    my $field = shift();
    XGDMatrixGetUintInfo( $self->handle, $field );
}

sub save_binary {
    my $self = shift();
    my ( $filename, $silent ) = @_;
    $silent //= 1;
    XGDMatrixSaveBinary( $self->handle, $filename, $silent );
    return $self;
}

sub set_label {
    my $self  = shift();
    my $label = shift();
    $self->set_float_info( 'label', $label );
}

sub set_label_pdl {
    my $self  = shift();
    my $label = shift();
    $self->set_float_info_pdl( 'label', $label->flat()->unpdl() );
}

sub get_label {
    my $self = shift();
    $self->get_float_info('label');
}

sub set_weight {
    my $self   = shift();
    my $weight = shift();
    $self->set_float_info( 'weight', $weight );
    return $self;
}

sub set_weight_pdl {
    my $self   = shift();
    my $weight = shift();
    $self->set_float_info( 'weight', $weight->flat()->unpdl() );
    return $self;
}

sub get_weight {
    my $self = shift();
    $self->get_float_info('weight');
}

sub set_base_margin {
    my $self   = shift();
    my $margin = shift();
    $self->set_float_info( 'base_margin', $margin );
    return $self;
}

sub get_base_margin {
    my $self = shift();
    $self->get_float_info('base_margin');
}

sub set_group {
    my $self  = shift();
    my $group = shift();
    XGDMatrixSetGroup( $self->handle, $group );
    return $self;
}

sub num_row {
    my $self = shift();
    XGDMatrixNumRow( $self->handle );
}

sub num_col {
    my $self = shift();
    XGDMatrixNumCol( $self->handle );
}

sub dims {
    my $self = shift();
    return ( $self->num_row(), $self->num_col() );
}

sub slice {
    my $self              = shift;
    my ($list_of_indices) = @_;
    my $handle            = XGDMatrixSliceDMatrix( $self->handle(), $list_of_indices );
    return __PACKAGE__->new( handle => $handle );
}

sub DEMOLISH {
    my $self = shift();
    XGDMatrixFree( $self->handle );
}

__PACKAGE__->meta->make_immutable();

1;

__END__



( run in 0.274 second using v1.01-cache-2.11-cpan-4d50c553e7e )