view release on metacpan or search on metacpan
META.yml
MANIFEST
examples/calculator.pl
examples/plot_network.pl
examples/char_lstm.pl
examples/get_ptb_data.sh
examples/lstm_bucketing.pl
examples/mnist.pl
examples/cudnn_lstm_bucketing.pl
Makefile.PL
Changes
META.json
t/test_recordio.t
t/test_random.t
t/test_init.t
t/test_model_parallel.t
examples/calculator.pl view on Meta::CPAN
use AI::MXNet ('mx');
## preparing the samples
## to train our network
sub samples {
my($batch_size, $func) = @_;
# get samples
my $n = 16384;
## creates a pdl with $n rows and two columns with random
## floats in the range between 0 and 1
my $data = PDL->random(2, $n);
## creates the pdl with $n rows and one column with labels
## labels are floats that either sum or product, etc of
## two random values in each corresponding row of the data pdl
my $label = $func->($data->slice('0,:'), $data->slice('1,:'));
# partition into train/eval sets
my $edge = int($n / 8);
my $validation_data = $data->slice(":,0:@{[ $edge - 1 ]}");
my $validation_label = $label->slice(":,0:@{[ $edge - 1 ]}");
my $train_data = $data->slice(":,$edge:");
my $train_label = $label->slice(":,$edge:");
# build iterators around the sets
return(mx->io->NDArrayIter(
batch_size => $batch_size,
data => $train_data,
label => $train_label,
), mx->io->NDArrayIter(
batch_size => $batch_size,
data => $validation_data,
label => $validation_label,
));
}
## the network model
sub nn_fc {
my $data = mx->sym->Variable('data');
my $ln = mx->sym->exp(mx->sym->FullyConnected(
data => mx->sym->log($data),
num_hidden => 1,
));
my $wide = mx->sym->Concat($data, $ln);
my $fc = mx->sym->FullyConnected(
$wide,
num_hidden => 1
);
return mx->sym->MAERegressionOutput(data => $fc, name => 'softmax');
}
sub learn_function {
my(%args) = @_;
my $func = $args{func};
my $batch_size = $args{batch_size}//128;
my($train_iter, $eval_iter) = samples($batch_size, $func);
my $sym = nn_fc();
## call as ./calculator.pl 1 to just print model and exit
if($ARGV[0]) {
my @dsz = @{$train_iter->data->[0][1]->shape};
my @lsz = @{$train_iter->label->[0][1]->shape};
my $shape = {
data => [ $batch_size, splice @dsz, 1 ],
softmax_label => [ $batch_size, splice @lsz, 1 ],
};
print mx->viz->plot_network($sym, shape => $shape)->graph->as_png;
exit;
}
my $model = mx->mod->Module(
symbol => $sym,
context => mx->cpu(),
);
$model->fit($train_iter,
eval_data => $eval_iter,
optimizer => 'adam',
optimizer_params => {
learning_rate => $args{lr}//0.01,
rescale_grad => 1/$batch_size,
lr_scheduler => AI::MXNet::FactorScheduler->new(
step => 100,
factor => 0.99
)
},
eval_metric => 'mse',
num_epoch => $args{epoch}//25,
);
# refit the model for calling on 1 sample at a time
my $iter = mx->io->NDArrayIter(
batch_size => 1,
data => PDL->pdl([[ 0, 0 ]]),
label => PDL->pdl([[ 0 ]]),
);
$model->reshape(
data_shapes => $iter->provide_data,
label_shapes => $iter->provide_label,
);
# wrap a helper around making predictions
my ($arg_params) = $model->get_params;
for my $k (sort keys %$arg_params)
{
print "$k -> ". $arg_params->{$k}->aspdl."\n";
}
return sub {
my($n, $m) = @_;
return $model->predict(mx->io->NDArrayIter(
batch_size => 1,
data => PDL->new([[ $n, $m ]]),
))->aspdl->list;
};
}
my $add = learn_function(func => sub {
my($n, $m) = @_;
return $n + $m;
});
my $sub = learn_function(func => sub {
my($n, $m) = @_;
examples/char_lstm.pl view on Meta::CPAN
--chkp-prefix prefix for checkpoint files, default='lstm_'
--cell-mode RNN cell mode (LSTM, GRU, RNN, default=LSTM)
--sample-size a size of inferred sample text (default=10000) after each epoch
--chkp-epoch save checkpoint after this many epoch, default=1 (saving every checkpoint)
=cut
package AI::MXNet::RNN::IO::ASCIIIterator;
use Mouse;
extends AI::MXNet::DataIter;
has 'data' => (is => 'ro', isa => 'PDL', required => 1);
has 'seq_size' => (is => 'ro', isa => 'Int', required => 1);
has '+batch_size' => (is => 'ro', isa => 'Int', required => 1);
has 'data_name' => (is => 'ro', isa => 'Str', default => 'data');
has 'label_name' => (is => 'ro', isa => 'Str', default => 'softmax_label');
has 'dtype' => (is => 'ro', isa => 'Dtype', default => 'float32');
has [qw/nd counter seq_counter vocab_size
data_size provide_data provide_label idx/] => (is => 'rw', init_arg => undef);
sub BUILD
{
my $self = shift;
$self->data_size($self->data->nelem);
my $segments = int(($self->data_size-$self->seq_size)/($self->batch_size*$self->seq_size));
$self->idx([0..$segments-1]);
$self->vocab_size($self->data->uniq->shape->at(0));
$self->counter(0);
$self->seq_counter(0);
$self->nd(mx->nd->array($self->data, dtype => $self->dtype));
my $shape = [$self->batch_size, $self->seq_size];
$self->provide_data([
AI::MXNet::DataDesc->new(
name => $self->data_name,
shape => $shape,
dtype => $self->dtype
)
]);
$self->provide_label([
AI::MXNet::DataDesc->new(
name => $self->label_name,
shape => $shape,
dtype => $self->dtype
)
examples/char_lstm.pl view on Meta::CPAN
method reset()
{
$self->counter(0);
@{ $self->idx } = List::Util::shuffle(@{ $self->idx });
}
method next()
{
return undef if $self->counter == @{$self->idx};
my $offset = $self->idx->[$self->counter]*$self->batch_size*$self->seq_size + $self->seq_counter;
my $data = $self->nd->slice(
[$offset, $offset + $self->batch_size*$self->seq_size-1]
)->reshape([$self->batch_size, $self->seq_size]);
my $label = $self->nd->slice(
[$offset + 1 , $offset + $self->batch_size*$self->seq_size]
)->reshape([$self->batch_size, $self->seq_size]);
$self->seq_counter($self->seq_counter + 1);
if($self->seq_counter == $seq_size - 1)
{
$self->counter($self->counter + 1);
$self->seq_counter(0);
}
return AI::MXNet::DataBatch->new(
data => [$data],
label => [$label],
provide_data => [
AI::MXNet::DataDesc->new(
name => $self->data_name,
shape => $data->shape,
dtype => $self->dtype
)
],
provide_label => [
AI::MXNet::DataDesc->new(
name => $self->label_name,
shape => $label->shape,
dtype => $self->dtype
)
],
);
}
package main;
my $file = "data/input.txt";
open(F, $file) or die "can't open $file: $!";
my $fdata;
{ local($/) = undef; $fdata = <F>; close(F) };
my %vocabulary; my $i = 0;
$fdata = pdl(map{ exists $vocabulary{$_} ? $vocabulary{$_} : ($vocabulary{$_} = $i++) } split(//, $fdata));
my $data_iter = AI::MXNet::RNN::IO::ASCIIIterator->new(
batch_size => $batch_size,
data => $fdata,
seq_size => $seq_size
);
my %reverse_vocab = reverse %vocabulary;
my $mode = "${cell_mode}Cell";
my $stack = mx->rnn->SequentialRNNCell();
for my $i (0..$num_layers-1)
{
my $cell = mx->rnn->$mode(num_hidden => $num_hidden, prefix => "lstm_${i}l0_");
if($bidirectional)
{
examples/char_lstm.pl view on Meta::CPAN
mx->rnn->$mode(
num_hidden => $num_hidden,
prefix => "lstm_${i}r0_"
),
output_prefix => "bi_lstm_$i"
);
}
$stack->add($cell);
}
my $data = mx->sym->Variable('data');
my $label = mx->sym->Variable('softmax_label');
my $embed = mx->sym->Embedding(
data => $data, input_dim => scalar(keys %vocabulary),
output_dim => $num_embed, name => 'embed'
);
$stack->reset;
my ($outputs, $states) = $stack->unroll($seq_size, inputs => $embed, merge_outputs => 1);
my $pred = mx->sym->Reshape($outputs, shape => [-1, $num_hidden*(1+($bidirectional ? 1 : 0))]);
$pred = mx->sym->FullyConnected(data => $pred, num_hidden => $data_iter->vocab_size, name => 'pred');
$label = mx->sym->Reshape($label, shape => [-1]);
my $net = mx->sym->SoftmaxOutput(data => $pred, label => $label, name => 'softmax');
my $contexts;
if(defined $gpus)
{
$contexts = [map { mx->gpu($_) } split(/,/, $gpus)];
}
else
{
$contexts = mx->cpu(0);
}
my $model = mx->mod->Module(
symbol => $net,
context => $contexts
);
$model->fit(
$data_iter,
eval_metric => mx->metric->Perplexity,
kvstore => $kv_store,
optimizer => $optimizer,
optimizer_params => {
learning_rate => $lr,
momentum => $mom,
wd => $wd,
clip_gradient => 5,
rescale_grad => 1/$batch_size,
lr_scheduler => AI::MXNet::FactorScheduler->new(step => 1000, factor => 0.99)
},
initializer => mx->init->Xavier(factor_type => "in", magnitude => 2.34),
num_epoch => $num_epoch,
batch_end_callback => mx->callback->Speedometer($batch_size, $disp_batches),
($chkp_epoch ? (epoch_end_callback => [mx->rnn->do_rnn_checkpoint($stack, $chkp_prefix, $chkp_epoch), \&sample]) : ())
);
sub sample {
return if not $sample_size;
$model->reshape(data_shapes=>[['data',[1, $seq_size]]], label_shapes=>[['softmax_label',[1, $seq_size]]]);
my $input = mx->nd->array($fdata->slice([0, $seq_size-1]))->reshape([1, $seq_size]);
$| = 1;
for (0..$sample_size-1)
{
$model->forward(mx->io->DataBatch(data=>[$input]), is_train => 0);
my $prob = $model->get_outputs(0)->[0][0]->at($seq_size-1)->aspdl;
my $next_char = Math::Random::Discrete->new($prob->reshape(-1)->unpdl, [0..scalar(keys %vocabulary)-1])->rand;
print "$reverse_vocab{$next_char}";
$input->at(0)->slice([0, $seq_size-2]) .= $input->at(0)->slice([1, $seq_size-1])->copy;
$input->at(0)->at($seq_size-1) .= $next_char;
}
$model->reshape(data_shapes=>[['data',[$batch_size, $seq_size]]], label_shapes=>[['softmax_label',[$batch_size, $seq_size]]]);
}
examples/cudnn_lstm_bucketing.pl view on Meta::CPAN
invalid_label => $invalid_label,
start_label => $start_label
);
return ($sentences, $vocab);
}
my $buckets = [10, 20, 30, 40, 50, 60];
my $start_label = 1;
my $invalid_label = 0;
func get_data($layout)
{
my ($train_sentences, $vocabulary) = tokenize_text(
'./data/ptb.train.txt', start_label => $start_label,
invalid_label => $invalid_label
);
my ($validation_sentences) = tokenize_text(
'./data/ptb.test.txt', vocab => $vocabulary,
start_label => $start_label, invalid_label => $invalid_label
);
my $data_train = mx->rnn->BucketSentenceIter(
$train_sentences, $batch_size, buckets => $buckets,
invalid_label => $invalid_label,
layout => $layout
);
my $data_val = mx->rnn->BucketSentenceIter(
$validation_sentences, $batch_size, buckets => $buckets,
invalid_label => $invalid_label,
layout => $layout
);
return ($data_train, $data_val, $vocabulary);
}
my $train = sub
{
my ($data_train, $data_val, $vocab) = get_data('TN');
my $cell;
if($stack_rnn)
{
my $stack = mx->rnn->SequentialRNNCell();
for my $i (0..$num_layers-1)
{
my $dropout_rate = 0;
if($i < $num_layers-1)
{
$dropout_rate = $dropout;
examples/cudnn_lstm_bucketing.pl view on Meta::CPAN
}
else
{
$cell = mx->rnn->FusedRNNCell(
$num_hidden, mode => 'lstm', num_layers => $num_layers,
bidirectional => $bidirectional, dropout => $dropout
);
}
my $sym_gen = sub { my $seq_len = shift;
my $data = mx->sym->Variable('data');
my $label = mx->sym->Variable('softmax_label');
my $embed = mx->sym->Embedding(data=>$data, input_dim=>scalar(keys %$vocab), output_dim=>$num_embed,name=>'embed');
my ($output) = $cell->unroll($seq_len, inputs=>$embed, merge_outputs=>1, layout=>'TNC');
my $pred = mx->sym->Reshape($output, shape=>[-1, $num_hidden*(1+$bidirectional)]);
$pred = mx->sym->FullyConnected(data=>$pred, num_hidden=>scalar(keys %$vocab), name=>'pred');
$label = mx->sym->Reshape($label, shape=>[-1]);
$pred = mx->sym->SoftmaxOutput(data=>$pred, label=>$label, name=>'softmax');
return ($pred, ['data'], ['softmax_label']);
};
my $contexts;
if(defined $gpus)
{
$contexts = [map { mx->gpu($_) } split(/,/, $gpus)];
}
else
{
$contexts = mx->cpu(0);
}
my $model = mx->mod->BucketingModule(
sym_gen => $sym_gen,
default_bucket_key => $data_train->default_bucket_key,
context => $contexts
);
my ($arg_params, $aux_params);
if($load_epoch)
{
(undef, $arg_params, $aux_params) = mx->rnn->load_rnn_checkpoint(
$cell, $model_prefix, $load_epoch);
}
$model->fit(
$data_train,
eval_data => $data_val,
eval_metric => mx->metric->Perplexity($invalid_label),
kvstore => $kv_store,
optimizer => $optimizer,
optimizer_params => {
learning_rate => $lr,
momentum => $mom,
wd => $wd,
},
begin_epoch => $load_epoch,
initializer => mx->init->Xavier(factor_type => "in", magnitude => 2.34),
num_epoch => $num_epoch,
batch_end_callback => mx->callback->Speedometer($batch_size, $disp_batches),
($model_prefix ? (epoch_end_callback => mx->rnn->do_rnn_checkpoint($cell, $model_prefix, 1)) : ())
);
};
my $test = sub {
assert($model_prefix, "Must specifiy path to load from");
my (undef, $data_val, $vocab) = get_data('NT');
my $stack;
if($stack_rnn)
{
$stack = mx->rnn->SequentialRNNCell();
for my $i (0..$num_layers-1)
{
my $cell = mx->rnn->LSTMCell(num_hidden => $num_hidden, prefix => "lstm_${i}l0_");
if($bidirectional)
{
$cell = mx->rnn->BidirectionalCell(
examples/cudnn_lstm_bucketing.pl view on Meta::CPAN
}
else
{
$stack = mx->rnn->FusedRNNCell(
$num_hidden, num_layers => $num_layers,
mode=>'lstm', bidirectional => $bidirectional
)->unfuse()
}
my $sym_gen = sub {
my $seq_len = shift;
my $data = mx->sym->Variable('data');
my $label = mx->sym->Variable('softmax_label');
my $embed = mx->sym->Embedding(
data => $data, input_dim => scalar(keys %$vocab),
output_dim => $num_embed, name => 'embed'
);
$stack->reset;
my ($outputs, $states) = $stack->unroll($seq_len, inputs => $embed, merge_outputs => 1);
my $pred = mx->sym->Reshape($outputs, shape => [-1, $num_hidden*(1+$bidirectional)]);
$pred = mx->sym->FullyConnected(data => $pred, num_hidden => scalar(keys %$vocab), name => 'pred');
$label = mx->sym->Reshape($label, shape => [-1]);
$pred = mx->sym->SoftmaxOutput(data => $pred, label => $label, name => 'softmax');
return ($pred, ['data'], ['softmax_label']);
};
my $contexts;
if($gpus)
{
$contexts = [map { mx->gpu($_) } split(/,/, $gpus)];
}
else
{
$contexts = mx->cpu(0);
}
my ($arg_params, $aux_params);
if($load_epoch)
{
(undef, $arg_params, $aux_params) = mx->rnn->load_rnn_checkpoint(
$stack, $model_prefix, $load_epoch);
}
my $model = mx->mod->BucketingModule(
sym_gen => $sym_gen,
default_bucket_key => $data_val->default_bucket_key,
context => $contexts
);
$model->bind(
data_shapes => $data_val->provide_data,
label_shapes => $data_val->provide_label,
for_training => 0,
force_rebind => 0
);
$model->set_params($arg_params, $aux_params);
my $score = $model->score($data_val,
mx->metric->Perplexity($invalid_label),
batch_end_callback=>mx->callback->Speedometer($batch_size, 5)
);
};
if($num_layers >= 4 and split(/,/,$gpus) >= 4 and not $stack_rnn)
{
print("WARNING: stack-rnn is recommended to train complex model on multiple GPUs\n");
}
examples/get_ptb_data.sh view on Meta::CPAN
#!/usr/bin/env bash
RNN_DIR=$(cd `dirname $0`; pwd)
DATA_DIR="${RNN_DIR}/data/"
if [[ ! -d "${DATA_DIR}" ]]; then
echo "${DATA_DIR} doesn't exist, will create one";
mkdir -p ${DATA_DIR}
fi
wget -P ${DATA_DIR} https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/ptb/ptb.train.txt;
wget -P ${DATA_DIR} https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/ptb/ptb.valid.txt;
wget -P ${DATA_DIR} https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/ptb/ptb.test.txt;
wget -P ${DATA_DIR} https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/tinyshakespeare/input.txt;
examples/lstm_bucketing.pl view on Meta::CPAN
'wd=f' => \(my $wd = 0.00001 ),
'batch-size=i' => \(my $batch_size = 32 ),
'disp-batches=i' => \(my $disp_batches = 50 ),
'chkp-prefix=s' => \(my $chkp_prefix = 'lstm_' ),
'chkp-epoch=i' => \(my $chkp_epoch = 0 ),
'help' => sub { HelpMessage(0) },
) or HelpMessage(1);
=head1 NAME
lstm_bucketing.pl - Example of training LSTM RNN on Penn Tree Bank data using high level RNN interface
=head1 SYNOPSIS
--num-layers number of stacked RNN layers, default=2
--num-hidden hidden layer size, default=200
--num-embed embedding layer size, default=200
--gpus list of gpus to run, e.g. 0 or 0,2,5. empty means using cpu.
Increase batch size when using multiple gpus for best performance.
--kv-store key-value store type, default='device'
--num-epochs max num of epochs, default=25
examples/lstm_bucketing.pl view on Meta::CPAN
start_label => $start_label
);
return ($sentences, $vocab);
}
my $buckets = [10, 20, 30, 40, 50, 60];
my $start_label = 1;
my $invalid_label = 0;
my ($train_sentences, $vocabulary) = tokenize_text(
'./data/ptb.train.txt', start_label => $start_label,
invalid_label => $invalid_label
);
my ($validation_sentences) = tokenize_text(
'./data/ptb.test.txt', vocab => $vocabulary,
start_label => $start_label, invalid_label => $invalid_label
);
my $data_train = mx->rnn->BucketSentenceIter(
$train_sentences, $batch_size, buckets => $buckets,
invalid_label => $invalid_label
);
my $data_val = mx->rnn->BucketSentenceIter(
$validation_sentences, $batch_size, buckets => $buckets,
invalid_label => $invalid_label
);
my $stack = mx->rnn->SequentialRNNCell();
for my $i (0..$num_layers-1)
{
$stack->add(mx->rnn->LSTMCell(num_hidden => $num_hidden, prefix => "lstm_l${i}_"));
}
my $sym_gen = sub {
my $seq_len = shift;
my $data = mx->sym->Variable('data');
my $label = mx->sym->Variable('softmax_label');
my $embed = mx->sym->Embedding(
data => $data, input_dim => scalar(keys %$vocabulary),
output_dim => $num_embed, name => 'embed'
);
$stack->reset;
my ($outputs, $states) = $stack->unroll($seq_len, inputs => $embed, merge_outputs => 1);
my $pred = mx->sym->Reshape($outputs, shape => [-1, $num_hidden]);
$pred = mx->sym->FullyConnected(data => $pred, num_hidden => scalar(keys %$vocabulary), name => 'pred');
$label = mx->sym->Reshape($label, shape => [-1]);
$pred = mx->sym->SoftmaxOutput(data => $pred, label => $label, name => 'softmax');
return ($pred, ['data'], ['softmax_label']);
};
my $contexts;
if(defined $gpus)
{
$contexts = [map { mx->gpu($_) } split(/,/, $gpus)];
}
else
{
$contexts = mx->cpu(0);
}
my $model = mx->mod->BucketingModule(
sym_gen => $sym_gen,
default_bucket_key => $data_train->default_bucket_key,
context => $contexts
);
$model->fit(
$data_train,
eval_data => $data_val,
eval_metric => mx->metric->Perplexity($invalid_label),
kvstore => $kv_store,
optimizer => $optimizer,
optimizer_params => {
learning_rate => $lr,
momentum => $mom,
wd => $wd,
},
initializer => mx->init->Xavier(factor_type => "in", magnitude => 2.34),
num_epoch => $num_epoch,
examples/mnist.pl view on Meta::CPAN
use strict;
use warnings;
# derived from http://mxnet.io/tutorials/python/mnist.html
use LWP::UserAgent ();
use PDL ();
#use Gtk2 '-init';
use AI::MXNet ('mx');
my $ua = LWP::UserAgent->new();
sub download_data {
my($url, $force_download) = @_;
$force_download = 1 if @_ < 2;
my $fname = (split m{/}, $url)[-1];
if($force_download or not -f $fname) {
$ua->get($url, ':content_file' => $fname);
}
return $fname;
}
sub read_data {
my($label_url, $image_url) = @_;
my($magic, $num, $rows, $cols);
open my($flbl), '<:gzip', download_data($label_url);
read $flbl, my($buf), 8;
($magic, $num) = unpack 'N2', $buf;
my $label = PDL->new();
$label->set_datatype($PDL::Types::PDL_B);
$label->setdims([ $num ]);
read $flbl, ${$label->get_dataref}, $num;
$label->upd_data();
open my($fimg), '<:gzip', download_data($image_url);
read $fimg, $buf, 16;
($magic, $num, $rows, $cols) = unpack 'N4', $buf;
my $image = PDL->new();
$image->set_datatype($PDL::Types::PDL_B);
$image->setdims([ $rows, $cols, $num ]);
read $fimg, ${$image->get_dataref}, $num * $rows * $cols;
$image->upd_data();
return($label, $image);
}
my $path='http://yann.lecun.com/exdb/mnist/';
my($train_lbl, $train_img) = read_data(
"${path}train-labels-idx1-ubyte.gz", "${path}train-images-idx3-ubyte.gz");
my($val_lbl, $val_img) = read_data(
"${path}t10k-labels-idx1-ubyte.gz", "${path}t10k-images-idx3-ubyte.gz");
sub show_sample {
print 'label: ', $train_lbl->slice('0:9'), "\n";
my $hbox = Gtk2::HBox->new(0, 2);
for my $i (0 .. 9) {
my $img = $train_img->slice(":,:,$i");
my($w, $h) = $img->dims;
$img->make_physical();
# ugh, pixbufs don't have a grayscale colorspace?!
# burst it to rgb I guess.
my $data = pack 'c*', map { $_, $_, $_ } unpack 'c*', ${$img->get_dataref};
$hbox->add(Gtk2::Image->new_from_pixbuf(
Gtk2::Gdk::Pixbuf->new_from_data($data, 'rgb', 0, 8, $w, $h, $w * 3)
));
}
my $win = Gtk2::Window->new('toplevel');
$win->signal_connect(delete_event => sub { Gtk2->main_quit() });
$win->add($hbox);
$win->show_all();
Gtk2->main();
}
sub show_network {
examples/mnist.pl view on Meta::CPAN
#show_sample();
sub to4d {
my($img) = @_;
return $img->reshape(28, 28, 1, ($img->dims)[2])->float / 255;
}
my $batch_size = 100;
my $train_iter = mx->io->NDArrayIter(
data => to4d($train_img),
label => $train_lbl,
batch_size => $batch_size,
shuffle => 1,
);
my $val_iter = mx->io->NDArrayIter(
data => to4d($val_img),
label => $val_lbl,
batch_size => $batch_size,
);
# Create a place holder variable for the input data
my $data = mx->sym->Variable('data');
sub nn_fc {
# Epoch[9] Train-accuracy=0.978889
# Epoch[9] Time cost=145.437
# Epoch[9] Validation-accuracy=0.964600
my($data) = @_;
# Flatten the data from 4-D shape (batch_size, num_channel, width, height)
# into 2-D (batch_size, num_channel*width*height)
$data = mx->sym->Flatten(data => $data);
# The first fully-connected layer
# my $fc1 = mx->sym->FullyConnected(data => $data, name => 'fc1', num_hidden => 128);
# # Apply relu to the output of the first fully-connnected layer
# my $act1 = mx->sym->Activation(data => $fc1, name => 'relu1', act_type => "relu");
# The second fully-connected layer and the according activation function
my $fc2 = mx->sym->FullyConnected(data => $data, name => 'fc2', num_hidden => 64);
my $act2 = mx->sym->Activation(data => $fc2, name => 'relu2', act_type => "relu");
# The thrid fully-connected layer, note that the hidden size should be 10, which is the number of unique digits
my $fc3 = mx->sym->FullyConnected(data => $act2, name => 'fc3', num_hidden => 10);
# The softmax and loss layer
my $mlp = mx->sym->SoftmaxOutput(data => $fc3, name => 'softmax');
return $mlp;
}
sub nn_conv {
my($data) = @_;
# Epoch[9] Batch [200] Speed: 1625.07 samples/sec Train-accuracy=0.992090
# Epoch[9] Batch [400] Speed: 1630.12 samples/sec Train-accuracy=0.992850
# Epoch[9] Train-accuracy=0.991357
# Epoch[9] Time cost=36.817
# Epoch[9] Validation-accuracy=0.988100
my $conv1= mx->symbol->Convolution(data => $data, name => 'conv1', num_filter => 20, kernel => [5,5], stride => [2,2]);
my $bn1 = mx->symbol->BatchNorm(data => $conv1, name => "bn1");
my $act1 = mx->symbol->Activation(data => $bn1, name => 'relu1', act_type => "relu");
my $mp1 = mx->symbol->Pooling(data => $act1, name => 'mp1', kernel => [2,2], stride =>[1,1], pool_type=>'max');
my $conv2= mx->symbol->Convolution(data => $mp1, name => 'conv2', num_filter => 50, kernel=>[3,3], stride=>[2,2]);
my $bn2 = mx->symbol->BatchNorm(data => $conv2, name=>"bn2");
my $act2 = mx->symbol->Activation(data => $bn2, name=>'relu2', act_type=>"relu");
my $mp2 = mx->symbol->Pooling(data => $act2, name => 'mp2', kernel=>[2,2], stride=>[1,1], pool_type=>'max');
my $fl = mx->symbol->Flatten(data => $mp2, name=>"flatten");
my $fc1 = mx->symbol->FullyConnected(data => $fl, name=>"fc1", num_hidden=>100);
my $act3 = mx->symbol->Activation(data => $fc1, name=>'relu3', act_type=>"relu");
my $fc2 = mx->symbol->FullyConnected(data => $act3, name=>'fc2', num_hidden=>30);
my $act4 = mx->symbol->Activation(data => $fc2, name=>'relu4', act_type=>"relu");
my $fc3 = mx->symbol->FullyConnected(data => $act4, name=>'fc3', num_hidden=>10);
my $softmax = mx->symbol->SoftmaxOutput(data => $fc3, name => 'softmax');
return $softmax;
}
my $mlp = $ARGV[0] ? nn_conv($data) : nn_fc($data);
#We visualize the network structure with output size (the batch_size is ignored.)
#my $shape = { data => [ $batch_size, 1, 28, 28 ] };
#show_network(mx->viz->plot_network($mlp, shape => $shape));
my $model = mx->mod->Module(
symbol => $mlp, # network structure
);
$model->fit(
$train_iter, # training data
num_epoch => 10, # number of data passes for training
eval_data => $val_iter, # validation data
batch_end_callback => mx->callback->Speedometer($batch_size, 200), # output progress for each 200 data batches
optimizer => 'adam',
);
examples/plot_network.pl view on Meta::CPAN
#!/usr/bin/perl
use strict;
use warnings;
use AI::MXNet qw(mx);
### model
my $data = mx->symbol->Variable('data');
my $conv1= mx->symbol->Convolution(data => $data, name => 'conv1', num_filter => 32, kernel => [3,3], stride => [2,2]);
my $bn1 = mx->symbol->BatchNorm(data => $conv1, name => "bn1");
my $act1 = mx->symbol->Activation(data => $bn1, name => 'relu1', act_type => "relu");
my $mp1 = mx->symbol->Pooling(data => $act1, name => 'mp1', kernel => [2,2], stride =>[2,2], pool_type=>'max');
my $conv2= mx->symbol->Convolution(data => $mp1, name => 'conv2', num_filter => 32, kernel=>[3,3], stride=>[2,2]);
my $bn2 = mx->symbol->BatchNorm(data => $conv2, name=>"bn2");
my $act2 = mx->symbol->Activation(data => $bn2, name=>'relu2', act_type=>"relu");
my $mp2 = mx->symbol->Pooling(data => $act2, name => 'mp2', kernel=>[2,2], stride=>[2,2], pool_type=>'max');
my $fl = mx->symbol->Flatten(data => $mp2, name=>"flatten");
my $fc1 = mx->symbol->FullyConnected(data => $fl, name=>"fc1", num_hidden=>30);
my $act3 = mx->symbol->Activation(data => $fc1, name=>'relu3', act_type=>"relu");
my $fc2 = mx->symbol->FullyConnected(data => $act3, name=>'fc2', num_hidden=>10);
my $softmax = mx->symbol->SoftmaxOutput(data => $fc2, name => 'softmax');
## creates the image file in working directory, you need GraphViz installed for this to work
mx->viz->plot_network($softmax, save_format => 'png')->render("network.png");
lib/AI/MXNet.pm view on Meta::CPAN
__END__
=encoding UTF-8
=head1 NAME
AI::MXNet - Perl interface to MXNet machine learning library
=head1 SYNOPSIS
## Convolutional NN for recognizing hand-written digits in MNIST dataset
## It's considered "Hello, World" for Neural Networks
## For more info about the MNIST problem please refer to http://neuralnetworksanddeeplearning.com/chap1.html
use strict;
use warnings;
use AI::MXNet qw(mx);
use AI::MXNet::TestUtils qw(GetMNIST_ubyte);
use Test::More tests => 1;
# symbol net
my $batch_size = 100;
### model
my $data = mx->symbol->Variable('data');
my $conv1= mx->symbol->Convolution(data => $data, name => 'conv1', num_filter => 32, kernel => [3,3], stride => [2,2]);
my $bn1 = mx->symbol->BatchNorm(data => $conv1, name => "bn1");
my $act1 = mx->symbol->Activation(data => $bn1, name => 'relu1', act_type => "relu");
my $mp1 = mx->symbol->Pooling(data => $act1, name => 'mp1', kernel => [2,2], stride =>[2,2], pool_type=>'max');
my $conv2= mx->symbol->Convolution(data => $mp1, name => 'conv2', num_filter => 32, kernel=>[3,3], stride=>[2,2]);
my $bn2 = mx->symbol->BatchNorm(data => $conv2, name=>"bn2");
my $act2 = mx->symbol->Activation(data => $bn2, name=>'relu2', act_type=>"relu");
my $mp2 = mx->symbol->Pooling(data => $act2, name => 'mp2', kernel=>[2,2], stride=>[2,2], pool_type=>'max');
my $fl = mx->symbol->Flatten(data => $mp2, name=>"flatten");
my $fc1 = mx->symbol->FullyConnected(data => $fl, name=>"fc1", num_hidden=>30);
my $act3 = mx->symbol->Activation(data => $fc1, name=>'relu3', act_type=>"relu");
my $fc2 = mx->symbol->FullyConnected(data => $act3, name=>'fc2', num_hidden=>10);
my $softmax = mx->symbol->SoftmaxOutput(data => $fc2, name => 'softmax');
# check data
GetMNIST_ubyte();
my $train_dataiter = mx->io->MNISTIter({
image=>"data/train-images-idx3-ubyte",
label=>"data/train-labels-idx1-ubyte",
data_shape=>[1, 28, 28],
batch_size=>$batch_size, shuffle=>1, flat=>0, silent=>0, seed=>10});
my $val_dataiter = mx->io->MNISTIter({
image=>"data/t10k-images-idx3-ubyte",
label=>"data/t10k-labels-idx1-ubyte",
data_shape=>[1, 28, 28],
batch_size=>$batch_size, shuffle=>1, flat=>0, silent=>0});
my $n_epoch = 1;
my $mod = mx->mod->new(symbol => $softmax);
$mod->fit(
$train_dataiter,
eval_data => $val_dataiter,
optimizer_params=>{learning_rate=>0.01, momentum=> 0.9},
num_epoch=>$n_epoch
);
my $res = $mod->score($val_dataiter, mx->metric->create('acc'));
ok($res->{accuracy} > 0.8);
=head1 DESCRIPTION
Perl interface to MXNet machine learning library.
=head1 BUGS AND INCOMPATIBILITIES
Parity with Python inteface is mostly achieved, few deprecated
and not often used features left unported for now.
lib/AI/MXNet/Callback.pm view on Meta::CPAN
AI::MXNet::Speedometer - A callback that logs training speed
=cut
=head1 DESCRIPTION
Calculate and log training speed periodically.
Parameters
----------
batch_size: int
batch_size of data
frequent: int
How many batches between calculations.
Defaults to calculating & logging every 50 batches.
auto_reset: Bool
Reset the metric after each log, defaults to true.
=cut
has 'batch_size' => (is => 'ro', isa => 'Int', required => 1);
has 'frequent' => (is => 'ro', isa => 'Int', default => 50);
has 'init' => (is => 'rw', isa => 'Int', default => 0);
lib/AI/MXNet/Executor.pm view on Meta::CPAN
$is_train=0: bool, optional
whether this forward is for evaluation purpose. If True,
a backward call is expected to follow. Otherwise following
backward is invalid.
%kwargs
Additional specification of input arguments.
Examples
--------
>>> # doing forward by specifying data
>>> $texec->forward(1, data => $mydata);
>>> # doing forward by not specifying things, but copy to the executor before hand
>>> $mydata->copyto($texec->arg_dict->{'data'});
>>> $texec->forward(1);
>>> # doing forward by specifying data and get outputs
>>> my $outputs = $texec->forward(1, data => $mydata);
>>> print $outputs->[0]->aspdl;
=cut
method forward(Int $is_train=0, %kwargs)
{
if(%kwargs)
{
my $arg_dict = $self->arg_dict;
while (my ($name, $array) = each %kwargs)
{
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
if($begin >= $end)
{
confess('Too many slices such that some splits are empty');
}
push @slices, [$begin, $end];
}
return \@slices;
}
# Load a array ref of arrays into a array ref of arrays specified by slices
func _load_general($data, $targets, $major_axis)
{
zip(sub {
my ($d_src, $d_targets, $axis) = @_;
if(blessed($d_targets) and $d_targets->isa('AI::MXNet::NDarray'))
{
$d_src->copyto($d_targets);
}
elsif(ref $d_targets eq 'ARRAY' and blessed $d_targets->[0])
{
zip(sub {
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
{
$d_src->copyto($d_dst);
}
}
else
{
$d_src->copyto($d_dst);
}
}
}
}, $data, $targets, $major_axis);
}
# Load data into sliced arrays
func _load_data($batch, $targets, $major_axis)
{
_load_general($batch->data, $targets, $major_axis);
}
# Load label into sliced arrays
func _load_label($batch, $targets, $major_axis)
{
_load_general($batch->label, $targets, $major_axis);
}
# Merge outputs that live on multiple context into one, so that they look
# like living on one context.
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
return \@rets;
}
## TODO
## this class is here because of https://github.com/gfx/p5-Mouse/pull/67
## once 2.4.7 version of Mouse in Ubuntu for affected Perl version
## these accessors should be merged into main class
package AI::MXNet::DataParallelExecutorGroup::_private;
use Mouse;
has [qw/output_layouts label_layouts arg_names aux_names
batch_size slices execs data_arrays
label_arrays param_arrays grad_arrays aux_arrays
data_layouts shared_data_arrays input_grad_arrays
_default_execs state_arrays/
] => (is => 'rw', init_arg => undef);
package AI::MXNet::DataParallelExecutorGroup;
use Mouse;
use AI::MXNet::Base;
use List::Util qw(sum);
=head1 DESCRIPTION
DataParallelExecutorGroup is a group of executors that lives on a group of devices.
This is a helper class used to implement data parallelization. Each mini-batch will
be split and run on the devices.
Parameters for constructor
----------
symbol : AI::MXNet::Symbol
The common symbolic computation graph for all executors.
contexts : ArrayRef[AI::MXNet::Context]
A array ref of contexts.
workload : ArrayRef[Num]
If not undef, could be an array ref of numbers that specify the workload to be assigned
to different context. Larger number indicate heavier workload.
data_shapes : ArrayRef[NameShape|AI::MXNet::DataDesc]
Should be a array ref of [name, shape] array refs, for the shapes of data. Note the order is
important and should be the same as the order that the `DataIter` provide the data.
label_shapes : Maybe[ArrayRef[NameShape|AI::MXNet::DataDesc]]
Should be a array ref of [$name, $shape] array refs, for the shapes of label. Note the order is
important and should be the same as the order that the `DataIter` provide the label.
param_names : ArrayRef[Str]
A array ref of strings, indicating the names of parameters (e.g. weights, filters, etc.)
in the computation graph.
for_training : Bool
Indicate whether the executors should be bind for training. When not doing training,
the memory for gradients will not be allocated.
inputs_need_grad : Bool
Indicate whether the gradients for the input data should be computed. This is currently
not used. It will be useful for implementing composition of modules.
shared_group : AI::MXNet::DataParallelExecutorGroup
Default is undef. This is used in bucketing. When not undef, it should be a executor
group corresponding to a different bucket. In other words, it will correspond to a different
symbol with the same set of parameters (e.g. unrolled RNNs with different lengths).
In this case the memory regions of the parameters will be shared.
logger : Logger
Default is AI::MXNet::Logging->get_logger.
fixed_param_names: Maybe[ArrayRef[Str]]
Indicate parameters to be fixed during training. Parameters in this array ref will not allocate
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
grad_req : ArrayRef[GradReq]|HashRef[GradReq]|GradReq
Requirement for gradient accumulation. Can be 'write', 'add', or 'null'
(default to 'write').
Can be specified globally (str) or for each argument (array ref, hash ref).
state_names: Maybe[ArrayRef[Str]]
=cut
has 'symbol' => (is => 'ro', isa => 'AI::MXNet::Symbol', required => 1);
has 'contexts' => (is => 'ro', isa => 'ArrayRef[AI::MXNet::Context]', required => 1);
has 'workload' => (is => 'ro', isa => 'ArrayRef[Num]', default => sub { [] });
has 'data_shapes' => (is => 'rw', isa => 'ArrayRef[NameShape|AI::MXNet::DataDesc]', required => 1);
has 'label_shapes' => (is => 'rw', isa => 'Maybe[ArrayRef[NameShape|AI::MXNet::DataDesc]]');
has 'param_names' => (is => 'ro', isa => 'ArrayRef[Str]', required => 1);
has 'for_training' => (is => 'ro', isa => 'Bool', required => 1);
has 'inputs_need_grad' => (is => 'ro', isa => 'Bool', default => 0);
has 'shared_group' => (is => 'ro', isa => 'Maybe[AI::MXNet::DataParallelExecutorGroup]');
has 'logger' => (is => 'ro', default => sub { AI::MXNet::Logging->get_logger });
has 'fixed_param_names' => (is => 'rw', isa => 'Maybe[ArrayRef[Str]]');
has 'state_names' => (is => 'rw', isa => 'Maybe[ArrayRef[Str]]');
has 'grad_req' => (is => 'rw', isa => 'ArrayRef[GradReq]|HashRef[GradReq]|GradReq', default=>'write');
has '_p' => (is => 'rw', init_arg => undef);
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
{
my $self = shift;
my $p = AI::MXNet::DataParallelExecutorGroup::_private->new;
$p->arg_names($self->symbol->list_arguments);
$p->aux_names($self->symbol->list_auxiliary_states);
$p->execs([]);
$self->_p($p);
$self->grad_req('null') if not $self->for_training;
$self->fixed_param_names([]) unless defined $self->fixed_param_names;
$self->state_names([]) unless defined $self->state_names;
my $data_shapes = [];
for my $d (@{ $self->data_shapes })
{
$d = AI::MXNet::DataDesc->new(name => $d->[0], shape => $d->[1])
unless blessed $d;
push @{ $data_shapes }, $d;
}
$self->data_shapes($data_shapes);
if(defined $self->label_shapes)
{
my $label_shapes = [];
for my $l (@{ $self->label_shapes })
{
$l = AI::MXNet::DataDesc->new(name => $l->[0], shape => $l->[1])
unless blessed $l;
push @{ $label_shapes }, $l;
}
$self->label_shapes($label_shapes);
}
my %data_names = map { $_->name => 1 } @{ $self->data_shapes };
my %param_names = map { $_ => 1 } @{ $self->param_names };
my %fixed_param_names = map { $_ => 1 } @{ $self->fixed_param_names };
my %grad_req;
if(not ref $self->grad_req)
{
for my $k (@{ $self->_p->arg_names })
{
if(exists $param_names{ $k })
{
$grad_req{$k} = exists $fixed_param_names{ $k } ? 'null' : $self->grad_req;
}
elsif(exists $data_names{ $k })
{
$grad_req{$k} = $self->inputs_need_grad ? $self->grad_req : 'null';
}
else
{
$grad_req{$k} = 'null';
}
}
}
elsif(ref $self->grad_req eq 'ARRAY')
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
@grad_req{ @{ $self->_p->arg_names } } = @{ $self->grad_req };
}
else
{
for my $k (@{ $self->_p->arg_names })
{
if(exists $param_names{ $k })
{
$grad_req{$k} = exists $fixed_param_names{ $k } ? 'null' : 'write';
}
elsif(exists $data_names{ $k })
{
$grad_req{$k} = $self->inputs_need_grad ? 'write' : 'null';
}
else
{
$grad_req{$k} = 'null';
}
}
%grad_req = (%grad_req, %{ $self->grad_req });
}
$self->grad_req(\%grad_req);
if(defined $self->shared_group)
{
$self->_p->shared_data_arrays($self->shared_group->_p->shared_data_arrays);
}
else
{
$self->_p->shared_data_arrays([map { +{} } 0..@{ $self->contexts }-1]);
}
$self->_p->output_layouts([
map {
AI::MXNet::DataDesc->get_batch_axis($self->symbol->slice($_)->attr('__layout__'))
} @{ $self->symbol->list_outputs }
]);
$self->bind_exec($self->data_shapes, $self->label_shapes, $self->shared_group);
}
=decide_slices
Decide the slices for each context according to the workload.
Parameters
----------
$data_shapes : ArrayRef[AI::MXNet::DataDesc]
=cut
method decide_slices(ArrayRef[AI::MXNet::DataDesc] $data_shapes)
{
confess("empty data_shapes array") unless @{ $data_shapes } > 0;
my $major_axis = [map { AI::MXNet::DataDesc->get_batch_axis($_->layout) } @{ $data_shapes }];
zip(sub {
my ($desc, $axis) = @_;
return if($axis == -1);
my $batch_size = $desc->shape->[$axis];
if(defined $self->_p->batch_size)
{
confess(
"all data must have the same batch size: "
. sprintf("batch_size = %d, but ", $self->_p->batch_size)
. sprintf("%s has shape %s", $desc->name, '('. join(',', @{ $desc->shape }) . ')')
) unless $batch_size == $self->_p->batch_size;
}
else
{
$self->_p->batch_size($batch_size);
$self->_p->slices(AI::MXNet::Executor::Group::_split_input_slice($self->_p->batch_size, $self->workload));
}
}, $data_shapes, $major_axis);
return $major_axis;
}
# Collect internal arrays from executors.
method _collect_arrays()
{
# convenient data structures
$self->_p->data_arrays([]);
for my $d (@{ $self->data_shapes })
{
my $name = $d->name;
my @tmp;
for my $i (0..@{ $self->_p->execs }-1)
{
push @tmp, [ $self->_p->slices->[$i], $self->_p->execs->[$i]->arg_dict->{$name} ];
}
push @{ $self->_p->data_arrays }, \@tmp;
}
if(defined $self->label_shapes)
{
$self->_p->label_arrays([]);
for my $l (@{ $self->label_shapes })
{
my $name = $l->name;
my @tmp;
for my $i (0..@{ $self->_p->execs }-1)
{
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
{
my @tmp;
for my $exec (@{ $self->_p->execs })
{
push @tmp, $exec->grad_arrays->[$i];
}
push @{ $self->_p->grad_arrays }, \@tmp;
}
}
}
my @data_names = map { $_->name } @{ $self->data_shapes };
my $j = 0; my %arg_names = map { $_ => $j++ } @{ $self->_p->arg_names };
if($self->inputs_need_grad)
{
$self->_p->input_grad_arrays([]);
for my $name (@data_names)
{
next unless exists $arg_names{$name};
my @tmp;
for my $exec (@{ $self->_p->execs })
{
push @tmp, $exec->grad_arrays->[$arg_names{$name}];
}
push @{ $self->_p->input_grad_arrays }, \@tmp;
}
}
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
push @{ $self->_p->aux_arrays }, \@tmp;
}
}
=head2 bind_exec
Bind executors on their respective devices.
Parameters
----------
$data_shapes : ArrayRef[AI::MXNet::DataDesc]
$label_shapes : Maybe[ArrayRef[AI::MXNet::DataDesc]]
$shared_group : Maybe[AI::MXNet::DataParallelExecutorGroup]
$reshape : Bool
=cut
method bind_exec(
ArrayRef[AI::MXNet::DataDesc] $data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc]] $label_shapes=,
Maybe[AI::MXNet::DataParallelExecutorGroup] $shared_group=,
Bool $reshape=0
)
{
assert($reshape or not @{ $self->_p->execs });
$self->_p->batch_size(undef);
# calculate workload and bind executors
$self->_p->data_layouts($self->decide_slices($data_shapes));
# call it to make sure labels has the same batch size as data
if(defined $label_shapes)
{
$self->_p->label_layouts($self->decide_slices($label_shapes));
}
for my $i (0..@{ $self->contexts }-1)
{
my $data_shapes_i = $self->_sliced_shape($data_shapes, $i, $self->_p->data_layouts);
my $label_shapes_i = [];
if(defined $label_shapes)
{
$label_shapes_i = $self->_sliced_shape($label_shapes, $i, $self->_p->label_layouts);
}
if($reshape)
{
my %combined_hash = map { $_->name => $_->shape } (@{ $data_shapes_i }, @{ $label_shapes_i });
$self->_p->execs->[$i] = $self->_p->_default_execs->[$i]->reshape(
\%combined_hash,
allow_up_sizing => 1,
);
}
else
{
push @{ $self->_p->execs }, $self->_bind_ith_exec($i, $data_shapes_i, $label_shapes_i, $shared_group);
}
}
$self->data_shapes($data_shapes);
$self->label_shapes($label_shapes);
$self->_collect_arrays;
}
=head2 reshape
Reshape executors.
Parameters
----------
$data_shapes : ArrayRef[AI::MXNet::DataDesc]
$label_shapes : Maybe[ArrayRef[AI::MXNet::DataDesc]]
=cut
method reshape(
ArrayRef[AI::MXNet::DataDesc] $data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc]] $label_shapes=
)
{
return if($data_shapes eq $self->data_shapes and $label_shapes eq $self->label_shapes);
if (not defined $self->_p->_default_execs)
{
$self->_p->_default_execs([@{ $self->_p->execs }]);
}
$self->bind_exec($data_shapes, $label_shapes, undef, 1);
}
=head2 set_params
Assign, i.e. copy parameters to all the executors.
Parameters
----------
$arg_params : HashRef[AI::MXNet::NDArray]
A dictionary of name to AI::MXNet::NDArray parameter mapping.
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
A dictionary of name to AI::MXNet::NDArray auxiliary variable mapping.
=cut
method set_params(HashRef[AI::MXNet::NDArray] $arg_params, HashRef[AI::MXNet::NDArray] $aux_params, Bool $allow_extra=0)
{
$_->copy_params_from($arg_params, $aux_params, $allow_extra) for @{ $self->_p->execs };
}
=head2 get_params
Copy data from each executor to arg_params and aux_params.
Parameters
----------
$arg_params : HashRef[AI::MXNet::NDArray]
target parameter arrays
$aux_params : HashRef[AI::MXNet::NDArray]
target aux arrays
Notes
-----
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
for my $dst (@{ $d_dst })
{
$dst .= $value;
}
}
}
}
=head2 forward
Split the data_batch according to a workload and run forward on each devices.
Parameters
----------
data_batch : AI::MXNet::DataBatch
Or could be any object implementing similar interface.
is_train : bool
The hint for the backend, indicating whether we are during training phase.
Default is undef, then the value $self->for_training will be used.
=cut
method forward(AI::MXNet::DataBatch $data_batch, Maybe[Bool] $is_train=)
{
AI::MXNet::Executor::Group::_load_data($data_batch, $self->_p->data_arrays, $self->_p->data_layouts);
$is_train //= $self->for_training;
if(defined $self->_p->label_arrays)
{
confess("assert not is_train or data_batch.label")
unless (not $is_train or $data_batch->label);
if($data_batch->label)
{
AI::MXNet::Executor::Group::_load_label($data_batch, $self->_p->label_arrays, $self->_p->label_layouts);
}
}
$_->forward($is_train) for @{ $self->_p->execs };
}
# Get the shapes of the outputs
method get_output_shapes()
{
my @shapes = map { $_->shape } @{ $self->execs->[0]->outputs };
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
return \@concat_shapes;
}
=head2 get_outputs
Gets outputs of the previous forward computation.
Parameters
----------
merge_multi_context : bool
Default is 1. In the case when data-parallelism is used, the outputs
will be collected from multiple devices. A 1 value indicates that we
should merge the collected results so that they look like from a single
executor.
Returns
-------
If merge_multi_context is 1, it is [$out1, $out2]. Otherwise, it
is [[$out1_dev1, $out1_dev2], [$out2_dev1, $out2_dev2]]. All the output
elements are `AI::MXNet::NDArray`.
=cut
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
return $outputs;
}
=head2 get_input_grads
Get the gradients with respect to the inputs of the module.
Parameters
----------
merge_multi_context : bool
Default is 1. In the case when data-parallelism is used, the outputs
will be collected from multiple devices. A 1 value indicates that we
should merge the collected results so that they look like from a single
executor.
Returns
-------
If merge_multi_context is 1, it is [$grad1, $grad2]. Otherwise, it
is [[$grad1_dev1, $grad1_dev2], [$grad2_dev1, $grad2_dev2]]. All the output
elements are AI::MXNet::NDArray.
=cut
method get_input_grads(Bool $merge_multi_context=1)
{
confess("assert \$self->inputs_need_grad") unless $self->inputs_need_grad;
if($merge_multi_context)
{
return AI::MXNet::Executor::Group::_merge_multi_context($self->_p->input_grad_arrays, $self->_p->data_layouts);
}
return $self->_p->input_grad_arrays;
}
=head2 backward
Run backward on all devices. A backward should be called after
a call to the forward function. Backward cannot be called unless
$self->for_training is 1.
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
{
push @labels_slice, $label;
}
}, $labels, $self->_p->label_layouts);
$eval_metric->update(\@labels_slice, $texec->outputs);
}, $self->_p->execs, $self->_p->slices);
}
method _bind_ith_exec(
Int $i,
ArrayRef[AI::MXNet::DataDesc] $data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc]] $label_shapes,
Maybe[AI::MXNet::DataParallelExecutorGroup] $shared_group
)
{
my $shared_exec = $shared_group ? $shared_group->_p->execs->[$i] : undef;
my $context = $self->contexts->[$i];
my $shared_data_arrays = $self->_p->shared_data_arrays->[$i];
my %input_shapes = map { $_->name => $_->shape } @{ $data_shapes };
if(defined $label_shapes)
{
%input_shapes = (%input_shapes, map { $_->name => $_->shape } @{ $label_shapes });
}
my %input_types = map { $_->name => $_->dtype } @{ $data_shapes };
my $executor = $self->symbol->simple_bind(
ctx => $context,
grad_req => $self->grad_req,
type_dict => \%input_types,
shared_arg_names => $self->param_names,
shared_exec => $shared_exec,
shared_buffer => $shared_data_arrays,
shapes => \%input_shapes
);
return $executor;
}
=head2 _sliced_shape
Get the sliced shapes for the i-th executor.
Parameters
lib/AI/MXNet/Executor/Group.pm view on Meta::CPAN
Parameters
----------
$mon : AI::MXNet::Monitor
=cut
method install_monitor(AI::MXNet::Monitor $mon)
{
$mon->install($_) for @{ $self->_p->execs };
}
method shared_data_arrays()
{
$self->_p->shared_data_arrays;
}
method execs()
{
$self->_p->execs;
}
1;
lib/AI/MXNet/IO.pm view on Meta::CPAN
use warnings;
use AI::MXNet::Base;
use AI::MXNet::Function::Parameters;
use Scalar::Util qw/blessed/;
=head1 NAME
AI::MXNet::IO - NDArray interface of mxnet.
=cut
# Convert data into canonical form.
method init_data(
AcceptableInput|HashRef[AcceptableInput]|ArrayRef[AcceptableInput]|Undef $data,
Undef|Int :$allow_empty=,
Str :$default_name
)
{
Carp::confess("data must be defined or allow_empty set to true value")
if(not defined $data and not $allow_empty);
$data //= [];
if(ref($data) and ref($data) ne 'ARRAY' and ref($data) ne 'HASH')
{
$data = [$data];
}
Carp::confess("data must not be empty or allow_empty set to true value")
if(ref($data) eq 'ARRAY' and not @{ $data } and not $allow_empty);
my @ret;
if(ref($data) eq 'ARRAY')
{
if(@{ $data } == 1)
{
@ret = ([$default_name, $data->[0]]);
}
else
{
my $i = -1;
@ret = map { $i++; ["_${i}_$default_name", $_] } @{ $data };
}
}
if(ref($data) eq 'HASH')
{
while(my ($k, $v) = each %{ $data })
{
push @ret, [$k, $v];
}
}
for my $d (@ret)
{
if(not (blessed $d->[1] and $d->[1]->isa('AI::MXNet::NDArray')))
{
$d->[1] = AI::MXNet::NDArray->array($d->[1]);
}
lib/AI/MXNet/IO.pm view on Meta::CPAN
);
}
method to_nameshape($other=, $reverse=)
{
[$self->name, $self->shape];
}
=head1 NAME
AI::MXNet::DataDesc - A container class for describing the data layout.
=cut
=head2 get_batch_axis
Get the dimension that corresponds to the batch size.
Parameters
----------
layout : str
layout string. For example, "NCHW".
Returns
-------
An axis indicating the batch_size dimension. When data-parallelism is
used, the data will be automatically split and concatenate along the batch_size
dimension. Axis can be -1, which means the whole array will be copied for each
data-parallelism device.
=cut
method get_batch_axis(Str|Undef $layout)
{
return 0 unless defined $layout;
return index($layout, 'N');
}
=head2 get_list
lib/AI/MXNet/IO.pm view on Meta::CPAN
)
} keys %{ $shapes }
];
}
package AI::MXNet::DataBatch;
use Mouse;
=head1 NAME
AI::MXNet::DataBatch - A container for a mini-batch of the data and related information.
=cut
=head1 DESCRIPTION
Default object for holding a mini-batch of data and related information.
=cut
has 'data' => (is => 'rw', isa => 'Maybe[ArrayRef[AI::MXNet::NDArray]]', required => 1);
has 'label' => (is => 'rw', isa => 'Maybe[ArrayRef[AI::MXNet::NDArray]]');
has 'pad' => (is => 'rw');
has 'index' => (is => 'rw');
has 'bucket_key' => (is => 'rw');
has 'provide_data' => (is => 'rw');
has 'provide_label' => (is => 'rw');
package AI::MXNet::DataIter;
use Mouse;
use overload '<>' => sub { shift->next },
'@{}' => sub { shift->list };
=head1 NAME
AI::MXNet::DataIter - A parent class for MXNet data iterators.
=cut
has 'batch_size' => (is => 'rw', isa => 'Int', default => 0);
=head2 reset
Reset the iterator.
=cut
method reset(){}
lib/AI/MXNet/IO.pm view on Meta::CPAN
my @ret;
while(<$self>)
{
push @ret, $_;
}
return \@ret;
}
=head2 next
Returns the next data batch from the iterator.
Returns
-------
$data : AI::MXNet::DataBatch
The data of next batch.
=cut
method next()
{
if($self->iter_next())
{
return AI::MXNet::DataBatch->new(
data => $self->getdata,
label => $self->getlabel,
pad => $self->getpad,
index => $self->getindex
);
}
else
{
return undef;
}
}
lib/AI/MXNet/IO.pm view on Meta::CPAN
Iterate to next batch.
Returns
-------
$has_next : Bool
=cut
method iter_next(){}
=head2 get_data
The data of current batch.
Returns
-------
data : AI::MXNet::NDArray
=cut
method get_data(){}
=head2 getlabel
The label of the current batch.
Returns
-------
label : AI::MXNet::NDArray
=cut
lib/AI/MXNet/IO.pm view on Meta::CPAN
=cut
=head1 DESCRIPTION
Resize a DataIter to a given number of batches per epoch.
May produce incomplete batch in the middle of an epoch due
to the padding from internal iterator.
Parameters
----------
data_iter : DataIter
Internal data iterator.
size : number of batches per epoch to resize to.
reset_internal : whether to reset internal iterator on ResizeIter.reset
=cut
has 'data_iter' => (is => 'ro', isa => 'AI::MXnet::DataIter', required => 1);
has 'size' => (is => 'ro', isa => 'Int', required => 1);
has 'reset_internal' => (is => 'rw', isa => 'Int', default => 1);
has 'cur' => (is => 'rw', isa => 'Int', default => 0);
has 'current_batch' => (is => 'rw', isa => 'Maybe[AI::MXNet::DataBatch]');
has [qw/provide_data
default_bucket_key
provide_label
batch_size/] => (is => 'rw', init_arg => undef);
sub BUILD
{
my $self = shift;
$self->provide_data($self->data_iter->provide_data);
$self->provide_label($self->data_iter->provide_label);
$self->batch_size($self->data_iter->batch_size);
if($self->data_iter->can('default_bucket_key'))
{
$self->default_bucket_key($self->data_iter->default_bucket_key);
}
}
method reset()
{
$self->cur(0);
if($self->reset_internal)
{
$self->data_iter->reset;
}
}
method iter_next()
{
return 0 if($self->cur == $self->size);
$self->current_batch($self->data_iter->next);
if(not defined $self->current_batch)
{
$self->data_iter->reset;
$self->current_batch($self->data_iter->next);
}
$self->cur($self->cur + 1);
return 1;
}
method get_data()
{
return $self->current_batch->data;
}
method getlabel()
{
return $self->current_batch->label;
}
method getindex()
{
return $self->current_batch->index;
lib/AI/MXNet/IO.pm view on Meta::CPAN
AI::MXNet::NDArrayIter - Predefined NDArray iterator.
=cut
=head1 DESCRIPTION
Predefined NDArray iterator. Accepts PDL or AI::MXNet::NDArray object as an input.
Parameters
----------
data: Maybe[AcceptableInput|HashRef[AcceptableInput]|ArrayRef[AcceptableInput]].
NDArrayIter supports single or multiple data and label.
label: Maybe[AcceptableInput|HashRef[AcceptableInput]|ArrayRef[AcceptableInput]].
Same as data, but is not given to the model during testing.
batch_size=1: Int
Batch Size
shuffle=0: Bool
Whether to shuffle the data
last_batch_handle='pad': 'pad', 'discard' or 'roll_over'
How to handle the last batch
Note
----
This iterator will pad, discard or roll over the last batch if
the size of data does not match batch_size. Roll over is intended
for training and can cause problems if used for prediction.
=cut
has 'data' => (is => 'rw', isa => 'Maybe[AcceptableInput|HashRef[AcceptableInput]|ArrayRef[AcceptableInput]]');
has 'data_list' => (is => 'rw', isa => 'ArrayRef[AI::MXNet::NDArray]');
has 'label' => (is => 'rw', isa => 'Maybe[AcceptableInput|HashRef[AcceptableInput]|ArrayRef[AcceptableInput]]');
has 'batch_size' => (is => 'rw', isa => 'Int', default => 1);
has '_shuffle' => (is => 'rw', init_arg => 'shuffle', isa => 'Bool', default => 0);
has 'last_batch_handle' => (is => 'rw', isa => 'Str', default => 'pad');
has 'label_name' => (is => 'rw', isa => 'Str', default => 'softmax_label');
has 'num_source' => (is => 'rw', isa => 'Int');
has 'cursor' => (is => 'rw', isa => 'Int');
has 'num_data' => (is => 'rw', isa => 'Int');
around BUILDARGS => sub {
my $orig = shift;
my $class = shift;
if(@_%2)
{
my $data = shift;
return $class->$orig(data => $data, @_);
}
return $class->$orig(@_);
};
sub BUILD
{
my $self = shift;
my $data = AI::MXNet::IO->init_data($self->data, allow_empty => 0, default_name => 'data');
my $label = AI::MXNet::IO->init_data($self->label, allow_empty => 1, default_name => $self->label_name);
my $num_data = $data->[0][1]->shape->[0];
confess("size of data dimension 0 $num_data < batch_size ${\ $self->batch_size }")
unless($num_data >= $self->batch_size);
if($self->_shuffle)
{
my @idx = shuffle(0..$num_data-1);
$_->[1] = AI::MXNet::NDArray->array(pdl_shuffle($_->[1]->aspdl, \@idx)) for @$data;
$_->[1] = AI::MXNet::NDArray->array(pdl_shuffle($_->[1]->aspdl, \@idx)) for @$label;
}
if($self->last_batch_handle eq 'discard')
{
my $new_n = $num_data - $num_data % $self->batch_size - 1;
$_->[1] = $_->[1]->slice([0, $new_n]) for @$data;
$_->[1] = $_->[1]->slice([0, $new_n]) for @$label;
}
my $data_list = [map { $_->[1] } (@{ $data }, @{ $label })];
my $num_source = @{ $data_list };
my $cursor = -$self->batch_size;
$self->data($data);
$self->data_list($data_list);
$self->label($label);
$self->num_source($num_source);
$self->cursor($cursor);
$self->num_data($num_data);
}
# The name and shape of data provided by this iterator
method provide_data()
{
return [map {
my ($k, $v) = @{ $_ };
my $shape = $v->shape;
$shape->[0] = $self->batch_size;
AI::MXNet::DataDesc->new(name => $k, shape => $shape, dtype => $v->dtype)
} @{ $self->data }];
}
# The name and shape of label provided by this iterator
method provide_label()
{
return [map {
my ($k, $v) = @{ $_ };
my $shape = $v->shape;
$shape->[0] = $self->batch_size;
AI::MXNet::DataDesc->new(name => $k, shape => $shape, dtype => $v->dtype)
} @{ $self->label }];
}
# Ignore roll over data and set to start
method hard_reset()
{
$self->cursor(-$self->batch_size);
}
method reset()
{
if($self->last_batch_handle eq 'roll_over' and $self->cursor > $self->num_data)
{
$self->cursor(-$self->batch_size + ($self->cursor%$self->num_data)%$self->batch_size);
}
else
{
$self->cursor(-$self->batch_size);
}
}
method iter_next()
{
$self->cursor($self->batch_size + $self->cursor);
return $self->cursor < $self->num_data;
}
method next()
{
if($self->iter_next)
{
return AI::MXNet::DataBatch->new(
data => $self->getdata,
label => $self->getlabel,
pad => $self->getpad,
index => undef
);
}
else
{
return undef;
}
}
# Load data from underlying arrays, internal use only
method _getdata($data_source)
{
confess("DataIter needs reset.") unless $self->cursor < $self->num_data;
if(($self->cursor + $self->batch_size) <= $self->num_data)
{
return [
map {
$_->[1]->slice([$self->cursor,$self->cursor+$self->batch_size-1])
} @{ $data_source }
];
}
else
{
my $pad = $self->batch_size - $self->num_data + $self->cursor - 1;
return [
map {
AI::MXNet::NDArray->concatenate(
[
$_->[1]->slice([$self->cursor, -1]),
$_->[1]->slice([0, $pad])
]
)
} @{ $data_source }
];
}
}
method getdata()
{
return $self->_getdata($self->data);
}
method getlabel()
{
return $self->_getdata($self->label);
}
method getpad()
{
if( $self->last_batch_handle eq 'pad'
and
($self->cursor + $self->batch_size) > $self->num_data
)
{
return $self->cursor + $self->batch_size - $self->num_data;
}
else
{
return 0;
}
}
package AI::MXNet::MXDataIter;
use Mouse;
use AI::MXNet::Base;
extends 'AI::MXNet::DataIter';
=head1 NAME
AI::MXNet::MXDataIter - A data iterator pre-built in C++ layer of MXNet.
=cut
has 'handle' => (is => 'ro', isa => 'DataIterHandle', required => 1);
has '_debug_skip_load' => (is => 'rw', isa => 'Int', default => 0);
has '_debug_at_begin' => (is => 'rw', isa => 'Int', default => 0);
has 'data_name' => (is => 'ro', isa => 'Str', default => 'data');
has 'label_name' => (is => 'ro', isa => 'Str', default => 'softmax_label');
has [qw/first_batch
provide_data
provide_label
batch_size/] => (is => 'rw', init_arg => undef);
sub BUILD
{
my $self = shift;
$self->first_batch($self->next);
my $data = $self->first_batch->data->[0];
$self->provide_data([
AI::MXNet::DataDesc->new(
name => $self->data_name,
shape => $data->shape,
dtype => $data->dtype
)
]);
my $label = $self->first_batch->label->[0];
$self->provide_label([
AI::MXNet::DataDesc->new(
name => $self->label_name,
shape => $label->shape,
dtype => $label->dtype
)
]);
$self->batch_size($data->shape->[0]);
}
sub DEMOLISH
{
check_call(AI::MXNetCAPI::DataIterFree(shift->handle));
}
=head2 debug_skip_load
Set the iterator to simply return always first batch.
lib/AI/MXNet/IO.pm view on Meta::CPAN
$self->_debug_at_begin(1);
$self->first_batch(undef);
check_call(AI::MXNetCAPI::DataIterBeforeFirst($self->handle));
}
method next()
{
if($self->_debug_skip_load and not $self->_debug_at_begin)
{
return AI::MXNet::DataBatch->new(
data => [$self->getdata],
label => [$self->getlabel],
pad => $self->getpad,
index => $self->getindex
);
}
if(defined $self->first_batch)
{
my $batch = $self->first_batch;
$self->first_batch(undef);
return $batch
}
$self->_debug_at_begin(0);
my $next_res = check_call(AI::MXNetCAPI::DataIterNext($self->handle));
if($next_res)
{
return AI::MXNet::DataBatch->new(
data => [$self->getdata],
label => [$self->getlabel],
pad => $self->getpad,
index => $self->getindex
);
}
else
{
return undef;
}
}
lib/AI/MXNet/IO.pm view on Meta::CPAN
if(defined $self->first_batch)
{
return 1;
}
else
{
return scalar(check_call(AI::MXNetCAPI::DataIterNext($self->handle)));
}
}
method getdata()
{
my $handle = check_call(AI::MXNetCAPI::DataIterGetData($self->handle));
return AI::MXNet::NDArray->new(handle => $handle);
}
method getlabel()
{
my $handle = check_call(AI::MXNetCAPI::DataIterGetLabel($self->handle));
return AI::MXNet::NDArray->new(handle => $handle);
}
lib/AI/MXNet/IO.pm view on Meta::CPAN
# Create an io iterator by handle.
func _make_io_iterator($handle)
{
my ($iter_name, $desc,
$arg_names, $arg_types, $arg_descs
) = @{ check_call(AI::MXNetCAPI::DataIterGetIterInfo($handle)) };
my $param_str = build_param_doc($arg_names, $arg_types, $arg_descs);
my $doc_str = "$desc\n\n"
."$param_str\n"
."name : string, required.\n"
." Name of the resulting data iterator.\n\n"
."Returns\n"
."-------\n"
."iterator: DataIter\n"
." The result iterator.";
my $iter = sub {
my $class = shift;
my (@args, %kwargs);
if(@_ and ref $_[-1] eq 'HASH')
{
%kwargs = %{ pop(@_) };
lib/AI/MXNet/IO.pm view on Meta::CPAN
\%kwargs
)
);
return AI::MXNet::MXDataIter->new(handle => $handle, %kwargs);
};
$iter_meta{$iter}{__name__} = $iter_name;
$iter_meta{$iter}{__doc__} = $doc_str;
return $iter;
}
# List and add all the data iterators to current module.
method _init_io_module()
{
for my $creator (@{ check_call(AI::MXNetCAPI::ListDataIters()) })
{
my $data_iter = _make_io_iterator($creator);
{
my $name = $iter_meta{ $data_iter }{__name__};
no strict 'refs';
{
*{__PACKAGE__."::$name"} = $data_iter;
}
}
}
}
# Initialize the io in startups
__PACKAGE__->_init_io_module;
1;
lib/AI/MXNet/Image.pm view on Meta::CPAN
AI::MXNet:Image - Read individual image files and perform augmentations.
=cut
=head2 imdecode
Decode an image from string. Requires OpenCV to work.
Parameters
----------
$buf : str, array ref, pdl, ndarray
Binary image data.
:$flag : int
0 for grayscale. 1 for colored.
:$to_rgb : int
0 for BGR format (OpenCV default). 1 for RGB format (MXNet default).
:$out : NDArray
Output buffer. Do not specify for automatic allocation.
=cut
method imdecode(Str|PDL $buf, Int :$flag=1, Int :$to_rgb=1, Maybe[AI::MXNet::NDArray] :$out=)
{
if(not ref $buf)
{
my $pdl_type = PDL::Type->new(DTYPE_MX_TO_PDL->{'uint8'});
my $len; { use bytes; $len = length $buf; }
my $pdl = PDL->new_from_specification($pdl_type, $len);
${$pdl->get_dataref} = $buf;
$pdl->upd_data;
$buf = $pdl;
}
if(not (blessed $buf and $buf->isa('AI::MXNet::NDArray')))
{
$buf = AI::MXNet::NDArray->array($buf, dtype=>'uint8');
}
return AI::MXNet::NDArray->_cvimdecode($buf, { flag => $flag, to_rgb => $to_rgb, ($out ? (out => $out) : ()) });
}
=head2 scale_down
lib/AI/MXNet/Image.pm view on Meta::CPAN
};
return $aug;
}
=head2 CreateAugmenter
Create augumenter list
Parameters:
-----------
Shape :$data_shape,
Bool :$resize=0,
Bool :$rand_crop=0,
Bool :$rand_resize=0,
Bool :$rand_mirror=0,
Maybe[Num|PDL] :$mean=,
Maybe[Num|PDL] :$std=,
Num :$brightness=0,
Num :$contrast=0,
Num :$saturation=0,
Num :$pca_noise=0,
Int :$inter_method=2
=cut
method CreateAugmenter(
Shape :$data_shape,
Bool :$resize=0,
Bool :$rand_crop=0,
Bool :$rand_resize=0,
Bool :$rand_mirror=0,
Maybe[Num|PDL] :$mean=,
Maybe[Num|PDL] :$std=,
Num :$brightness=0,
Num :$contrast=0,
Num :$saturation=0,
Num :$pca_noise=0,
Int :$inter_method=2
)
{
my @auglist;
if($resize > 0)
{
push @auglist, __PACKAGE__->ResizeAug($resize, $inter_method);
}
my $crop_size = [$data_shape->[2], $data_shape->[1]];
if($rand_resize)
{
assert($rand_crop);
push @auglist, __PACKAGE__->RandomSizedCropAug($crop_size, 0.3, [3.0/4.0, 4.0/3.0], $inter_method);
}
elsif($rand_crop)
{
push @auglist, __PACKAGE__->RandomCropAug($crop_size, $inter_method);
}
else
lib/AI/MXNet/Image.pm view on Meta::CPAN
method ImageIter(@args) { AI::MXNet::ImageIter->new(@args) }
package AI::MXNet::ImageIter;
use Mouse;
use AI::MXNet::Base;
extends 'AI::MXNet::DataIter';
=head1 NAME
AI::MXNet::ImageIter - Image data iterator.
=cut
=head1 DESCRIPTION
Image data iterator with a large number of augumentation choices.
Supports reading from both .rec files and raw image files with image list.
To load from .rec files, please specify path_imgrec. Also specify path_imgidx
to use data partition (for distributed training) or shuffling.
To load from raw image files, specify path_imglist and path_root.
Parameters
----------
batch_size : Int
Number of examples per batch
data_shape : Shape
Data shape in (channels, height, width).
For now, only RGB image with 3 channels is supported.
label_width : Int
dimension of label
path_imgrec : str
path to image record file (.rec).
Created with tools/im2rec.py or bin/im2rec
path_imglist : str
path to image list (.lst)
Created with tools/im2rec.py or with custom script.
lib/AI/MXNet/Image.pm view on Meta::CPAN
Root folder of image files
path_imgidx : str
Path to image index file. Needed for partition and shuffling when using .rec source.
shuffle : bool
Whether to shuffle all images at the start of each iteration.
Can be slow for HDD.
part_index : int
Partition index
num_parts : int
Total number of partitions.
data_name='data' Str
label_name='softmax_label' Str
kwargs : hash ref with any additional arguments for augmenters
=cut
has 'batch_size' => (is => 'ro', isa => 'Int', required => 1);
has 'data_shape' => (is => 'ro', isa => 'Shape', required => 1);
has 'label_width' => (is => 'ro', isa => 'Int', default => 1);
has 'data_name' => (is => 'ro', isa => 'Str', default => 'data');
has 'label_name' => (is => 'ro', isa => 'Str', default => 'softmax_label');
has [qw/path_imgrec
path_imglist
path_root
path_imgidx
/] => (is => 'ro', isa => 'Str');
has 'shuffle' => (is => 'ro', isa => 'Bool', default => 0);
has 'part_index' => (is => 'ro', isa => 'Int', default => 0);
has 'num_parts' => (is => 'ro', isa => 'Int', default => 0);
has 'aug_list' => (is => 'rw', isa => 'ArrayRef[CodeRef]');
has 'imglist' => (is => 'rw', isa => 'ArrayRef|HashRef');
has 'kwargs' => (is => 'ro', isa => 'HashRef');
has [qw/imgidx
imgrec
seq
cur
provide_data
provide_label
/] => (is => 'rw', init_arg => undef);
sub BUILD
{
my $self = shift;
assert($self->path_imgrec or $self->path_imglist or ref $self->imglist eq 'ARRAY');
if($self->path_imgrec)
{
print("loading recordio...\n");
lib/AI/MXNet/Image.pm view on Meta::CPAN
}
else
{
$label = AI::MXNet::NDArray->array($img->[0]);
$result{$key} = [$label, $img->[1]];
push @imgkeys, $key;
}
}
$self->imglist(\%result);
}
assert(@{ $self->data_shape } == 3 and $self->data_shape->[0] == 3);
$self->provide_data([
AI::MXNet::DataDesc->new(
name => $self->data_name,
shape => [$self->batch_size, @{ $self->data_shape }]
)
]);
if($self->label_width > 1)
{
$self->provide_label([
AI::MXNet::DataDesc->new(
name => $self->label_name,
shape => [$self->batch_size, $self->label_width]
)
]);
lib/AI/MXNet/Image.pm view on Meta::CPAN
}
if($self->num_parts > 1)
{
assert($self->part_index < $self->num_parts);
my $N = @{ $self->seq };
my $C = $N/$self->num_parts;
$self->seq([@{ $self->seq }[$self->part_index*$C..($self->part_index+1)*$C-1]]);
}
if(defined $self->aug_list or defined $self->kwargs)
{
$self->aug_list(AI::MXNet::Image->CreateAugmenter(data_shape => $self->data_shape, %{ $self->kwargs//{} }));
}
else
{
$self->aug_list([]);
}
$self->cur(0);
$self->reset();
}
method reset()
lib/AI/MXNet/Image.pm view on Meta::CPAN
my $s = $self->imgrec->read;
return undef if(not defined $s);
my ($header, $img) = AI::MXNet::RecordIO->unpack($s);
return ($header->label, $img)
}
}
method next()
{
my $batch_size = $self->batch_size;
my ($c, $h, $w) = @{ $self->data_shape };
my $batch_data = AI::MXNet::NDArray->empty([$batch_size, $c, $h, $w]);
my $batch_label = AI::MXNet::NDArray->empty(@{$self->provide_label->[0]}[1]);
my $i = 0;
while ($i < $batch_size)
{
my ($label, $s) = $self->next_sample;
last if not defined $label;
my $data = [AI::MXNet::Image->imdecode($s)];
if(@{ $data->[0]->shape } == 0)
{
AI::MXNet::Logging->debug('Invalid image, skipping.');
next;
}
for my $aug (@{ $self->aug_list })
{
$data = [map { @{ $aug->($_) } } @$data];
}
for my $d (@$data)
{
assert(($i < $batch_size), 'Batch size must be multiples of augmenter output length');
$batch_data->at($i) .= AI::MXNet::NDArray->transpose($d, { axes=>[2, 0, 1] });
$batch_label->at($i) .= $label;
$i++;
}
}
return undef if not $i;
return AI::MXNet::DataBatch->new(data=>[$batch_data], label=>[$batch_label], pad => $batch_size-$i);
}
1;
lib/AI/MXNet/KVStore.pm view on Meta::CPAN
sub DEMOLISH
{
check_call(AI::MXNetCAPI::KVStoreFree(shift->handle));
}
=head2 init
Initialize a single or a sequence of key-value pairs into the store.
For each key, one must init it before push and pull.
Only worker 0's (rank == 0) data are used.
This function returns after data have been initialized successfully
Parameters
----------
key : str or an array ref of str
The keys.
value : NDArray or an array ref of NDArray objects
The values.
Examples
--------
lib/AI/MXNet/KVStore.pm view on Meta::CPAN
----------
fname : str
Path to input states file.
=cut
method load_optimizer_states(Str $fname)
{
confess("Cannot save states for distributed training")
unless defined $self->_updater;
open(F, "<:raw", "$fname") or confess("can't open $fname for reading: $!");
my $data;
{ local($/) = undef; $data = <F>; }
close(F);
$self->_updater->set_states($data);
}
=head2 _set_updater
Set a push updater into the store.
This function only changes the local store. Use set_optimizer for
multi-machines.
Parameters
lib/AI/MXNet/Module.pm view on Meta::CPAN
## TODO
## this class is here because of https://github.com/gfx/p5-Mouse/pull/67
## once 2.4.7 version of Mouse in Ubuntu for affected Perl version
## these accessors should be merged into main class
package AI::MXNet::Module::Private;
use Mouse;
has [qw/_param_names _fixed_param_names
_aux_names _data_names _label_names _state_names
_output_names _arg_params _aux_params
_params_dirty _optimizer _kvstore
_update_on_kvstore _updater _work_load_list
_preload_opt_states _exec_group
_data_shapes _label_shapes _context _grad_req/
] => (is => 'rw', init_arg => undef);
package AI::MXNet::Module;
use AI::MXNet::Base;
use AI::MXNet::Function::Parameters;
use List::Util qw(max);
use Data::Dumper ();
use Mouse;
func _create_kvstore(
lib/AI/MXNet/Module.pm view on Meta::CPAN
=head1 NAME
AI::MXNet::Module - FeedForward interface of MXNet.
See AI::MXNet::Module::Base for the details.
=cut
extends 'AI::MXNet::Module::Base';
has '_symbol' => (is => 'ro', init_arg => 'symbol', isa => 'AI::MXNet::Symbol', required => 1);
has '_data_names' => (is => 'ro', init_arg => 'data_names', isa => 'ArrayRef[Str]');
has '_label_names' => (is => 'ro', init_arg => 'label_names', isa => 'Maybe[ArrayRef[Str]]');
has 'work_load_list' => (is => 'rw', isa => 'Maybe[ArrayRef[Int]]');
has 'fixed_param_names' => (is => 'rw', isa => 'Maybe[ArrayRef[Str]]');
has 'state_names' => (is => 'rw', isa => 'Maybe[ArrayRef[Str]]');
has 'logger' => (is => 'ro', default => sub { AI::MXNet::Logging->get_logger });
has '_p' => (is => 'rw', init_arg => undef);
has 'context' => (
is => 'ro',
isa => 'AI::MXNet::Context|ArrayRef[AI::MXNet::Context]',
default => sub { AI::MXNet::Context->cpu }
lib/AI/MXNet/Module.pm view on Meta::CPAN
$context = [$context];
}
$self->_p->_context($context);
my $work_load_list = $self->work_load_list;
if(not defined $work_load_list)
{
$work_load_list = [(1)x@{$self->_p->_context}];
}
assert(@{ $work_load_list } == @{ $self->_p->_context });
$self->_p->_work_load_list($work_load_list);
my @data_names = @{ $self->_data_names//['data'] };
my @label_names = @{ $self->_label_names//['softmax_label'] };
my @state_names = @{ $self->state_names//[] };
my $arg_names = $self->_symbol->list_arguments;
my @input_names = (@data_names, @label_names, @state_names);
my %input_names = map { $_ => 1 } @input_names;
$self->_p->_param_names([grep { not exists $input_names{$_} } @{ $arg_names }]);
$self->_p->_fixed_param_names($self->fixed_param_names//[]);
$self->_p->_state_names(\@state_names);
$self->_p->_aux_names($self->_symbol->list_auxiliary_states);
$self->_p->_data_names(\@data_names);
$self->_p->_label_names(\@label_names);
$self->_p->_output_names($self->_symbol->list_outputs);
$self->_p->_params_dirty(0);
$self->_check_input_names($self->_symbol, $self->_p->_data_names, "data", 1);
$self->_check_input_names($self->_symbol, $self->_p->_label_names, "label", 0);
$self->_check_input_names($self->_symbol, $self->_p->_state_names, "state", 1);
$self->_check_input_names($self->_symbol, $self->_p->_fixed_param_names, "fixed_param", 1);
}
method Module(@args) { return @args ? __PACKAGE__->new(@args) : __PACKAGE__ }
method BucketingModule(@args) { return AI::MXNet::Module::Bucketing->new(@args) }
=head2 load
lib/AI/MXNet/Module.pm view on Meta::CPAN
prefix : str
path prefix of saved model files. You should have
"prefix-symbol.json", "prefix-xxxx.params", and
optionally "prefix-xxxx.states", where xxxx is the
epoch number.
epoch : int
epoch to load.
load_optimizer_states : bool
whether to load optimizer states. Checkpoint needs
to have been made with save_optimizer_states=True.
data_names : array ref of str
Default is ['data'] for a typical model used in image classification.
label_names : array ref of str
Default is ['softmax_label'] for a typical model used in image
classification.
logger : Logger
Default is AI::MXNet::Logging.
context : Context or list of Context
Default is cpu(0).
work_load_list : array ref of number
Default is undef, indicating an uniform workload.
fixed_param_names: array ref of str
lib/AI/MXNet/Module.pm view on Meta::CPAN
if($save_optimizer_states)
{
my $state_name = sprintf('%s-%04d.states', $prefix, $epoch);
$self->save_optimizer_states($state_name);
AI::MXNet::Logging->info('Saved optimizer state to "%s"', $state_name);
}
}
=head2 model_save_checkpoint
Checkpoint the model data into file.
Parameters
----------
prefix : str
Prefix of model name.
epoch : int
The epoch number of the model.
symbol : AI::MXNet::Symbol
The input symbol
arg_params : hash ref of str to AI::MXNet::NDArray
lib/AI/MXNet/Module.pm view on Meta::CPAN
my $param_name = sprintf('%s-%04d.params', $prefix, $epoch);
$self->save_params($param_name, $arg_params, $aux_params);
AI::MXNet::Logging->info('Saved checkpoint to "%s"', $param_name);
}
# Internal function to reset binded state.
method _reset_bind()
{
$self->binded(0);
$self->_p->_exec_group(undef);
$self->_p->_data_shapes(undef);
$self->_p->_label_shapes(undef);
}
method data_names()
{
return $self->_p->_data_names;
}
method label_names()
{
return $self->_p->_label_names;
}
method output_names()
{
return $self->_p->_output_names;
}
method data_shapes()
{
assert($self->binded);
return $self->_p->_data_shapes;
}
method label_shapes()
{
assert($self->binded);
return $self->_p->_label_shapes;
}
method output_shapes()
{
lib/AI/MXNet/Module.pm view on Meta::CPAN
$self->params_initialized(1);
}
=head2 bind
Bind the symbols to construct executors. This is necessary before one
can perform computation with the module.
Parameters
----------
:$data_shapes : ArrayRef[AI::MXNet::DataDesc|NameShape]
Typically is $data_iter->provide_data.
:$label_shapes : Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]]
Typically is $data_iter->provide_label.
:$for_training : bool
Default is 1. Whether the executors should be bind for training.
:$inputs_need_grad : bool
Default is 0. Whether the gradients to the input data need to be computed.
Typically this is not needed. But this might be needed when implementing composition
of modules.
:$force_rebind : bool
Default is 0. This function does nothing if the executors are already
binded. But with this 1, the executors will be forced to rebind.
:$shared_module : Module
Default is undef. This is used in bucketing. When not undef, the shared module
essentially corresponds to a different bucket -- a module with different symbol
but with the same sets of parameters (e.g. unrolled RNNs with different lengths).
=cut
method bind(
ArrayRef[AI::MXNet::DataDesc|NameShape] :$data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]] :$label_shapes=,
Bool :$for_training=1,
Bool :$inputs_need_grad=0,
Bool :$force_rebind=0,
Maybe[AI::MXNet::Module] :$shared_module=,
GradReq|HashRef[GradReq]|ArrayRef[GradReq] :$grad_req='write',
Maybe[ArrayRef[Str]] :$state_names=$self->_p->_state_names
)
{
# force rebinding is typically used when one want to switch from
lib/AI/MXNet/Module.pm view on Meta::CPAN
}
$self->for_training($for_training);
$self->inputs_need_grad($inputs_need_grad);
$self->binded(1);
$self->_p->_grad_req($grad_req);
if(not $for_training)
{
assert(not $inputs_need_grad);
}
($data_shapes, $label_shapes) = $self->_parse_data_desc(
$self->data_names, $self->label_names, $data_shapes, $label_shapes
);
$self->_p->_data_shapes($data_shapes);
$self->_p->_label_shapes($label_shapes);
my $shared_group;
if($shared_module)
{
assert($shared_module->binded and $shared_module->params_initialized);
$shared_group = $shared_module->_p->_exec_group;
}
$self->_p->_exec_group(
AI::MXNet::DataParallelExecutorGroup->new(
symbol => $self->_symbol,
contexts => $self->_p->_context,
workload => $self->_p->_work_load_list,
data_shapes => $self->_p->_data_shapes,
label_shapes => $self->_p->_label_shapes,
param_names => $self->_p->_param_names,
state_names => $state_names,
for_training => $for_training,
inputs_need_grad => $inputs_need_grad,
shared_group => $shared_group,
logger => $self->logger,
fixed_param_names => $self->_p->_fixed_param_names,
grad_req => $grad_req
)
lib/AI/MXNet/Module.pm view on Meta::CPAN
{
$self->borrow_optimizer($shared_module)
}
}
=head2 reshape
Reshape the module for new input shapes.
Parameters
----------
:$data_shapes : ArrayRef[AI::MXNet::DataDesc]
Typically is $data_iter->provide_data.
:$label_shapes= : Maybe[ArrayRef[AI::MXNet::DataDesc]]
Typically is $data_iter->provide_label.
=cut
method reshape(
ArrayRef[AI::MXNet::DataDesc|NameShape] :$data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]] :$label_shapes=
)
{
assert($self->binded);
($data_shapes, $label_shapes) = $self->_parse_data_desc(
$self->data_names, $self->label_names, $data_shapes, $label_shapes
);
$self->_p->_data_shapes($data_shapes);
$self->_p->_label_shapes($label_shapes);
$self->_p->_exec_group->reshape($self->_p->_data_shapes, $self->_p->_label_shapes);
}
method init_optimizer(
Str|AI::MXNet::KVStore :$kvstore='local',
Optimizer :$optimizer='sgd',
HashRef :$optimizer_params={ learning_rate => 0.01 },
Bool :$force_init=0
)
{
assert($self->binded and $self->params_initialized);
lib/AI/MXNet/Module.pm view on Meta::CPAN
{
assert($shared_module->optimizer_initialized);
$self->_p->_optimizer($shared_module->_p->_optimizer);
$self->_p->_kvstore($shared_module->_p->_kvstore);
$self->_p->_update_on_kvstore($shared_module->_p->_update_on_kvstore);
$self->_p->_updater($shared_module->_p->_updater);
$self->optimizer_initialized(1);
}
method forward(
AI::MXNet::DataBatch $data_batch,
Maybe[Bool] :$is_train=
)
{
assert($self->binded and $self->params_initialized);
my @curr_data_shapes = map { $_->shape } @{ $self->data_shapes };
my @new_data_shapes = map { $_->shape } @{ $data_batch->data };
if(Data::Dumper->Dump(\@curr_data_shapes) ne Data::Dumper->Dump(\@new_data_shapes))
{
my $new_dshape;
if($data_batch->can('provide_data') and $data_batch->provide_data)
{
$new_dshape = $data_batch->provide_data;
}
else
{
$new_dshape = [];
zip(sub {
my ($i, $shape) = @_;
push @{ $new_dshape }, AI::MXNet::DataDesc->new(
$i->name, $shape, $i->dtype, $i->layout
);
}, $self->data_shapes, \@new_data_shapes);
}
my $new_lshape;
if($data_batch->can('provide_label') and $data_batch->provide_label)
{
$new_lshape = $data_batch->provide_label;
}
elsif($data_batch->can('label') and $data_batch->label)
{
$new_lshape = [];
zip(sub {
my ($i, $j) = @_;
push @{ $new_lshape }, AI::MXNet::DataDesc->new(
$i->name, $j->shape, $i->dtype, $i->layout
);
}, $self->label_shapes, $data_batch->label);
}
$self->reshape(data_shapes => $new_dshape, label_shapes => $new_lshape);
}
$self->_p->_exec_group->forward($data_batch, $is_train);
}
method backward(Maybe[AI::MXNet::NDArray|ArrayRef[AI::MXNet::NDArray]] $out_grads=)
{
assert($self->binded and $self->params_initialized);
$self->_p->_exec_group->backward($out_grads);
}
method update()
{
lib/AI/MXNet/Module.pm view on Meta::CPAN
method load_optimizer_states(Str $fname)
{
assert($self->optimizer_initialized);
if($self->_p->_update_on_kvstore)
{
$self->_p->_kvstore->load_optimizer_states($fname);
}
else
{
open(F, "<:raw", "$fname") or confess("can't open $fname for reading: $!");
my $data;
{ local($/) = undef; $data = <F>; }
close(F);
$self->_p->_updater->set_states($data);
}
}
method install_monitor(AI::MXNet::Monitor $mon)
{
assert($self->binded);
$self->_p->_exec_group->install_monitor($mon);
}
method _updater()
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
confess($msg);
}
else
{
AI::MXNet::Logging->warning($msg);
}
}
}
}
# Check that input names matches input data descriptors
method _check_names_match(
ArrayRef[Str] $data_names,
ArrayRef[NameShapeOrDataDesc] $data_shapes,
Str $name,
Bool $throw
)
{
return if (not @$data_shapes and @$data_names == 1 and $data_names->[0] eq 'softmax_label');
my @actual = map { @{$_}[0] } @{ $data_shapes };
if("@$data_names" ne "@actual")
{
my $msg = sprintf(
"Data provided by %s_shapes don't match names specified by %s_names (%s vs. %s)",
$name, $name, "@$data_shapes", "@$data_names"
);
if($throw)
{
confess($msg);
}
else
{
AI::MXNet::Logging->warning($msg);
}
}
}
method _parse_data_desc(
ArrayRef[Str] $data_names,
Maybe[ArrayRef[Str]] $label_names,
ArrayRef[NameShapeOrDataDesc] $data_shapes,
Maybe[ArrayRef[NameShapeOrDataDesc]] $label_shapes
)
{
$data_shapes = [map { blessed $_ ? $_ : AI::MXNet::DataDesc->new(@$_) } @$data_shapes];
$self->_check_names_match($data_names, $data_shapes, 'data', 1);
if($label_shapes)
{
$label_shapes = [map { blessed $_ ? $_ : AI::MXNet::DataDesc->new(@$_) } @$label_shapes];
$self->_check_names_match($label_names, $label_shapes, 'label', 0);
}
else
{
$self->_check_names_match($label_names, [], 'label', 0);
}
return ($data_shapes, $label_shapes);
}
=head1 DESCRIPTION
The base class of a modules. A module represents a computation component. The design
purpose of a module is that it abstract a computation "machine", that one can run forward,
backward, update parameters, etc. We aim to make the APIs easy to use, especially in the
case when we need to use imperative API to work with multiple modules (e.g. stochastic
depth network).
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
ready for computation.
- Parameter initialized. For modules with parameters, doing computation before initializing
the parameters might result in undefined outputs.
- Optimizer installed. An optimizer can be installed to a module. After this, the parameters
of the module can be updated according to the optimizer after gradients are computed
(forward-backward).
In order for a module to interact with others, a module should be able to report the
following information in its raw stage (before binded)
- data_names: array ref of string indicating the names of required data.
- output_names: array ref of string indicating the names of required outputs.
And also the following richer information after binded:
- state information
- binded: bool, indicating whether the memory buffers needed for computation
has been allocated.
- for_training: whether the module is binded for training (if binded).
- params_initialized: bool, indicating whether the parameters of this modules
has been initialized.
- optimizer_initialized: bool, indicating whether an optimizer is defined
and initialized.
- inputs_need_grad: bool, indicating whether gradients with respect to the
input data is needed. Might be useful when implementing composition of modules.
- input/output information
- data_shapes: am array ref of [name, shape]. In theory, since the memory is allocated,
we could directly provide the data arrays. But in the case of data parallelization,
the data arrays might not be of the same shape as viewed from the external world.
- label_shapes: an array ref of [name, shape]. This might be [] if the module does
not need labels (e.g. it does not contains a loss function at the top), or a module
is not binded for training.
- output_shapes: an array ref of [name, shape] for outputs of the module.
- parameters (for modules with parameters)
- get_params(): return an array ($arg_params, $aux_params). Each of those
is a hash ref of name to NDArray mapping. Those NDArrays always on
CPU. The actual parameters used for computing might be on other devices (GPUs),
this function will retrieve (a copy of) the latest parameters. Therefore, modifying
- get_params($arg_params, $aux_params): assign parameters to the devices
doing the computation.
- init_params(...): a more flexible interface to assign or initialize the parameters.
- setup
- bind(): prepare environment for computation.
- init_optimizer(): install optimizer for parameter updating.
- computation
- forward(data_batch): forward operation.
- backward(out_grads=): backward operation.
- update(): update parameters according to installed optimizer.
- get_outputs(): get outputs of the previous forward operation.
- get_input_grads(): get the gradients with respect to the inputs computed
in the previous backward operation.
- update_metric(metric, labels): update performance metric for the previous forward
computed results.
- other properties (mostly for backward compatability)
- symbol: the underlying symbolic graph for this module (if any)
This property is not necessarily constant. For example, for AI::MXNet::Module::Bucketing,
this property is simply the *current* symbol being used. For other modules,
this value might not be well defined.
When those intermediate-level API are implemented properly, the following
high-level API will be automatically available for a module:
- fit: train the module parameters on a data set
- predict: run prediction on a data set and collect outputs
- score: run prediction on a data set and evaluate performance
=cut
has 'logger' => (is => 'rw', default => sub { AI::MXNet::Logging->get_logger });
has '_symbol' => (is => 'rw', init_arg => 'symbol', isa => 'AI::MXNet::Symbol');
has [
qw/binded for_training inputs_need_grad
params_initialized optimizer_initialized/
] => (is => 'rw', isa => 'Bool', init_arg => undef, default => 0);
################################################################################
# High Level API
################################################################################
=head2 forward_backward
A convenient function that calls both forward and backward.
=cut
method forward_backward(AI::MXNet::DataBatch $data_batch)
{
$self->forward($data_batch, is_train => 1);
$self->backward();
}
=head2 score
Run prediction on eval_data and evaluate the performance according to
eval_metric.
Parameters
----------
$eval_data : AI::MXNet::DataIter
$eval_metric : AI::MXNet::EvalMetric
:$num_batch= : Maybe[Int]
Number of batches to run. Default is undef, indicating run until the AI::MXNet::DataIter
finishes.
:$batch_end_callback= : Maybe[Callback]
Could also be a array ref of functions.
:$reset=1 : Bool
Default 1, indicating whether we should reset $eval_data before starting
evaluating.
$epoch=0 : Int
Default is 0. For compatibility, this will be passed to callbacks (if any). During
training, this will correspond to the training epoch number.
=cut
method score(
AI::MXNet::DataIter $eval_data,
EvalMetric $eval_metric,
Maybe[Int] :$num_batch=,
Maybe[Callback]|ArrayRef[Callback] :$batch_end_callback=,
Maybe[Callback]|ArrayRef[Callback] :$score_end_callback=,
Bool :$reset=1,
Int :$epoch=0
)
{
assert($self->binded and $self->params_initialized);
$eval_data->reset if $reset;
if(not blessed $eval_metric or not $eval_metric->isa('AI::MXNet::EvalMetric'))
{
$eval_metric = AI::MXNet::Metric->create($eval_metric);
}
$eval_metric->reset();
my $actual_num_batch = 0;
my $nbatch = 0;
while(my $eval_batch = <$eval_data>)
{
last if (defined $num_batch and $nbatch == $num_batch);
$self->forward($eval_batch, is_train => 0);
$self->update_metric($eval_metric, $eval_batch->label);
if (defined $batch_end_callback)
{
my $batch_end_params = AI::MXNet::BatchEndParam->new(
epoch => $epoch,
nbatch => $nbatch,
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
}
return $eval_metric->get_name_value;
}
=head2 iter_predict
Iterate over predictions.
Parameters
----------
$eval_data : AI::MXNet::DataIter
:$num_batch= : Maybe[Int]
Default is undef, indicating running all the batches in the data iterator.
:$reset=1 : bool
Default is 1, indicating whether we should reset the data iter before start
doing prediction.
=cut
method iter_predict(AI::MXNet::DataIter $eval_data, Maybe[Int] :$num_batch=, Bool :$reset=1)
{
assert($self->binded and $self->params_initialized);
if($reset)
{
$eval_data->reset;
}
my $nbatch = 0;
my @out;
while(my $eval_batch = <$eval_data>)
{
last if defined $num_batch and $nbatch == $num_batch;
$self->forward($eval_batch, is_train => 0);
my $pad = $eval_batch->pad;
my $outputs = [
map { $_->slice([0, $_->shape->[0] - ($pad//0) - 1]) } @{ $self->get_outputs() }
];
push @out, [$outputs, $nbatch, $eval_batch];
$nbatch++;
}
return @out;
}
=head2 predict
Run prediction and collect the outputs.
Parameters
----------
$eval_data : AI::MXNet::DataIter
:$num_batch= : Maybe[Int]
Default is undef, indicating running all the batches in the data iterator.
:$merge_batches=1 : Bool
Default is 1.
:$reset=1 : Bool
Default is 1, indicating whether we should reset the data iter before start
doing prediction.
:$always_output_list=0 : Bool
Default is 0, see the doc for return values.
Returns
-------
When $merge_batches is 1 (by default), the return value will be an array ref
[$out1, $out2, $out3] where each element is concatenation of the outputs for
all the mini-batches. If $always_output_list` also is 0 (by default),
then in the case of a single output, $out1 is returned in stead of [$out1].
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
When $merge_batches is 0, the return value will be a nested array ref like
[[$out1_batch1, $out2_batch1], [$out1_batch2], ...]. This mode is useful because
in some cases (e.g. bucketing), the module does not necessarily produce the same
number of outputs.
The objects in the results are AI::MXNet::NDArray`s. If you need to work with pdl array,
just call ->aspdl() on each AI::MXNet::NDArray.
=cut
method predict(
AI::MXNet::DataIter $eval_data,
Maybe[Int] :$num_batch=, Bool :$merge_batches=1, Bool :$reset=1, Bool :$always_output_list=0
)
{
assert($self->binded and $self->params_initialized);
$eval_data->reset() if $reset;
my @output_list;
my $nbatch = 0;
while(my $eval_batch = <$eval_data>)
{
last if defined $num_batch and $nbatch == $num_batch;
$self->forward($eval_batch, is_train => 0);
my $pad = $eval_batch->pad;
my $outputs = [map { $_->slice([0, $_->shape->[0]-($pad//0)-1])->copy } @{ $self->get_outputs }];
push @output_list, $outputs;
}
return () unless @output_list;
if($merge_batches)
{
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
}
return @output_list;
}
=head2 fit
Train the module parameters.
Parameters
----------
$train_data : AI::MXNet::DataIter
:$eval_data= : Maybe[AI::MXNet::DataIter]
If not undef, it will be used as a validation set to evaluate the performance
after each epoch.
:$eval_metric='acc' : str or AI::MXNet::EvalMetric subclass object.
Default is 'accuracy'. The performance measure used to display during training.
Other possible predefined metrics are:
'ce' (CrossEntropy), 'f1', 'mae', 'mse', 'rmse', 'top_k_accuracy'
:$epoch_end_callback= : Maybe[Callback]|ArrayRef[Callback] function or array ref of functions.
Each callback will be called with the current $epoch, $symbol, $arg_params
and $aux_params.
:$batch_end_callback= : Maybe[Callback]|ArrayRef[Callback] function or array ref of functions.
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
:$begin_epoch=0 : Int
Default is 0. Indicates the starting epoch. Usually, if we are resuming from a
checkpoint saved at a previous training phase at epoch N, then we should specify
this value as N+1.
:$num_epoch : Int
Number of epochs for the training.
=cut
method fit(
AI::MXNet::DataIter $train_data,
Maybe[AI::MXNet::DataIter] :$eval_data=,
EvalMetric :$eval_metric='acc',
Maybe[Callback]|ArrayRef[Callback] :$epoch_end_callback=,
Maybe[Callback]|ArrayRef[Callback] :$batch_end_callback=,
Str :$kvstore='local',
Optimizer :$optimizer='sgd',
HashRef :$optimizer_params={ learning_rate => 0.01 },
Maybe[Callback]|ArrayRef[Callback] :$eval_end_callback=,
Maybe[Callback]|ArrayRef[Callback] :$eval_batch_end_callback=,
AI::MXNet::Initializer :$initializer=AI::MXNet::Initializer->Uniform(scale => 0.01),
Maybe[HashRef[AI::MXNet::NDArray]] :$arg_params=,
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
Bool :$allow_missing=0,
Bool :$force_rebind=0,
Bool :$force_init=0,
Int :$begin_epoch=0,
Int :$num_epoch,
Maybe[EvalMetric] :$validation_metric=,
Maybe[AI::MXNet::Monitor] :$monitor=
)
{
$self->bind(
data_shapes => $train_data->provide_data,
label_shapes => $train_data->provide_label,
for_training => 1,
force_rebind => $force_rebind
);
if($monitor)
{
$self->install_monitor($monitor);
}
$self->init_params(
initializer => $initializer,
arg_params => $arg_params,
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
################################################################################
# training loop
################################################################################
for my $epoch ($begin_epoch..$num_epoch-1)
{
my $tic = time;
$eval_metric->reset;
my $nbatch = 0;
my $end_of_batch = 0;
my $next_data_batch = <$train_data>;
while(not $end_of_batch)
{
my $data_batch = $next_data_batch;
$monitor->tic if $monitor;
$self->forward_backward($data_batch);
$self->update;
$next_data_batch = <$train_data>;
if(defined $next_data_batch)
{
$self->prepare($next_data_batch);
}
else
{
$end_of_batch = 1;
}
$self->update_metric($eval_metric, $data_batch->label);
$monitor->toc_print if $monitor;
if(defined $batch_end_callback)
{
my $batch_end_params = AI::MXNet::BatchEndParam->new(
epoch => $epoch,
nbatch => $nbatch,
eval_metric => $eval_metric
);
for my $callback (@{ _as_list($batch_end_callback) })
{
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
if($epoch_end_callback)
{
for my $callback (@{ _as_list($epoch_end_callback) })
{
&{$callback}($epoch, $self->get_symbol, $arg_params, $aux_params);
}
}
#----------------------------------------
# evaluation on validation set
if(defined $eval_data)
{
my $res = $self->score(
$eval_data,
$validation_metric,
score_end_callback => $eval_end_callback,
batch_end_callback => $eval_batch_end_callback,
epoch => $epoch
);
#TODO: pull this into default
while(my ($name, $val) = each %{ $res })
{
$self->logger->info('Epoch[%d] Validation-%s=%f', $epoch, $name, $val);
}
}
# end of 1 epoch, reset the data-iter for another epoch
$train_data->reset;
}
}
################################################################################
# Symbol information
################################################################################
=head2 get_symbol
The symbol used by this module.
=cut
method get_symbol() { $self->symbol }
=head2 data_names
An array ref of names for data required by this module.
=cut
method data_names() { confess("NotImplemented") }
=head2 output_names
An array ref of names for the outputs of this module.
=cut
method output_names() { confess("NotImplemented") }
################################################################################
# Input/Output information
################################################################################
=head2 data_shapes
An array ref of AI::MXNet::DataDesc objects specifying the data inputs to this module.
=cut
method data_shapes() { confess("NotImplemented") }
=head2 label_shapes
A array ref of AI::MXNet::DataDesc objects specifying the label inputs to this module.
If this module does not accept labels -- either it is a module without a loss
function, or it is not binded for training, then this should return an empty
array ref.
=cut
method label_shapes() { confess("NotImplemented") }
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
$self->set_params(\%arg_params, \%aux_params);
}
=head2 get_states
The states from all devices
Parameters
----------
$merge_multi_context=1 : Bool
Default is true (1). In the case when data-parallelism is used, the states
will be collected from multiple devices. A true value indicate that we
should merge the collected results so that they look like from a single
executor.
Returns
-------
If $merge_multi_context is 1, it is like [$out1, $out2]. Otherwise, it
is like [[$out1_dev1, $out1_dev2], [$out2_dev1, $out2_dev2]]. All the output
elements are AI::MXNet::NDArray.
=cut
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
Parameters
----------
$mon : AI::MXNet::Monitor
=cut
method install_monitor(AI::MXNet::Monitor $mon) { confess("NotImplemented") }
=head2 prepare
Prepare the module for processing a data batch.
Usually involves switching a bucket and reshaping.
Parameters
----------
$data_batch : AI::MXNet::DataBatch
=cut
method prepare(AI::MXNet::DataBatch $data_batch){}
################################################################################
# Computations
################################################################################
=head2 forward
Forward computation. It supports data batches with different shapes, such as
different batch sizes or different image sizes.
If reshaping of data batch relates to modification of symbol or module, such as
changing image layout ordering or switching from training to predicting, module
rebinding is required.
Parameters
----------
$data_batch : DataBatch
Could be anything with similar API implemented.
:$is_train= : Bool
Default is undef, which means is_train takes the value of $self->for_training.
=cut
method forward(AI::MXNet::DataBatch $data_batch, Bool :$is_train=) { confess("NotImplemented") }
=head2 backward
Backward computation.
Parameters
----------
$out_grads : Maybe[AI::MXNet::NDArray|ArrayRef[AI::MXNet::NDArray]], optional
Gradient on the outputs to be propagated back.
This parameter is only needed when bind is called
lib/AI/MXNet/Module/Base.pm view on Meta::CPAN
method update() { confess("NotImplemented") }
=head2 update_metric
Evaluate and accumulate evaluation metric on outputs of the last forward computation.
Parameters
----------
$eval_metric : EvalMetric
$labels : ArrayRef[AI::MXNet::NDArray]
Typically $data_batch->label.
=cut
method update_metric(EvalMetric $eval_metric, ArrayRef[AI::MXNet::NDArray] $labels)
{
confess("NotImplemented")
}
################################################################################
# module setup
################################################################################
=head2 bind
Binds the symbols in order to construct the executors. This is necessary
before the computations can be performed.
Parameters
----------
$data_shapes : ArrayRef[AI::MXNet::DataDesc]
Typically is $data_iter->provide_data.
:$label_shapes= : Maybe[ArrayRef[AI::MXNet::DataDesc]]
Typically is $data_iter->provide_label.
:$for_training=1 : Bool
Default is 1. Whether the executors should be bind for training.
:$inputs_need_grad=0 : Bool
Default is 0. Whether the gradients to the input data need to be computed.
Typically this is not needed. But this might be needed when implementing composition
of modules.
:$force_rebind=0 : Bool
Default is 0. This function does nothing if the executors are already
binded. But with this as 1, the executors will be forced to rebind.
:$shared_module= : A subclass of AI::MXNet::Module::Base
Default is undef. This is used in bucketing. When not undef, the shared module
essentially corresponds to a different bucket -- a module with different symbol
but with the same sets of parameters (e.g. unrolled RNNs with different lengths).
:$grad_req='write' : Str|ArrayRef[Str]|HashRef[Str]
Requirement for gradient accumulation. Can be 'write', 'add', or 'null'
(defaults to 'write').
Can be specified globally (str) or for each argument (array ref, hash ref).
=cut
method bind(
ArrayRef[AI::MXNet::DataDesc] $data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc]] :$label_shapes=,
Bool :$for_training=1,
Bool :$inputs_need_grad=0,
Bool :$force_rebind=0,
Maybe[AI::MXNet::BaseModule] :$shared_module=,
Str|ArrayRef[Str]|HashRef[Str] :$grad_req='write'
)
{
confess("NotImplemented")
}
lib/AI/MXNet/Module/Bucketing.pm view on Meta::CPAN
AI::MXNet::Module::Bucketing
=head1 SYNOPSIS
my $buckets = [10, 20, 30, 40, 50, 60];
my $start_label = 1;
my $invalid_label = 0;
my ($train_sentences, $vocabulary) = tokenize_text(
'./data/ptb.train.txt', start_label => $start_label,
invalid_label => $invalid_label
);
my ($validation_sentences) = tokenize_text(
'./data/ptb.test.txt', vocab => $vocabulary,
start_label => $start_label, invalid_label => $invalid_label
);
my $data_train = mx->rnn->BucketSentenceIter(
$train_sentences, $batch_size, buckets => $buckets,
invalid_label => $invalid_label
);
my $data_val = mx->rnn->BucketSentenceIter(
$validation_sentences, $batch_size, buckets => $buckets,
invalid_label => $invalid_label
);
my $stack = mx->rnn->SequentialRNNCell();
for my $i (0..$num_layers-1)
{
$stack->add(mx->rnn->LSTMCell(num_hidden => $num_hidden, prefix => "lstm_l${i}_"));
}
my $sym_gen = sub {
my $seq_len = shift;
my $data = mx->sym->Variable('data');
my $label = mx->sym->Variable('softmax_label');
my $embed = mx->sym->Embedding(
data => $data, input_dim => scalar(keys %$vocabulary),
output_dim => $num_embed, name => 'embed'
);
$stack->reset;
my ($outputs, $states) = $stack->unroll($seq_len, inputs => $embed, merge_outputs => 1);
my $pred = mx->sym->Reshape($outputs, shape => [-1, $num_hidden]);
$pred = mx->sym->FullyConnected(data => $pred, num_hidden => scalar(keys %$vocabulary), name => 'pred');
$label = mx->sym->Reshape($label, shape => [-1]);
$pred = mx->sym->SoftmaxOutput(data => $pred, label => $label, name => 'softmax');
return ($pred, ['data'], ['softmax_label']);
};
my $contexts;
if(defined $gpus)
{
$contexts = [map { mx->gpu($_) } split(/,/, $gpus)];
}
else
{
$contexts = mx->cpu(0);
}
my $model = mx->mod->BucketingModule(
sym_gen => $sym_gen,
default_bucket_key => $data_train->default_bucket_key,
context => $contexts
);
$model->fit(
$data_train,
eval_data => $data_val,
eval_metric => mx->metric->Perplexity($invalid_label),
kvstore => $kv_store,
optimizer => $optimizer,
optimizer_params => {
learning_rate => $lr,
momentum => $mom,
wd => $wd,
},
initializer => mx->init->Xavier(factor_type => "in", magnitude => 2.34),
num_epoch => $num_epoch,
batch_end_callback => mx->callback->Speedometer($batch_size, $disp_batches),
($chkp_epoch ? (epoch_end_callback => mx->rnn->do_rnn_checkpoint($stack, $chkp_prefix, $chkp_epoch)) : ())
);
=head1 DESCRIPTION
Implements the AI::MXNet::Module::Base API, and allows multiple
symbols to be used depending on the `bucket_key` provided by each different
mini-batch of data
=cut
=head2 new
Parameters
----------
$sym_gen : subref or any perl object that overloads &{} op
A sub when called with a bucket key, returns a list with triple
of ($symbol, $data_names, $label_names).
$default_bucket_key : str or anything else
The key for the default bucket.
$logger : Logger
$context : AI::MXNet::Context or array ref of AI::MXNet::Context objects
Default is cpu(0)
$work_load_list : array ref of Num
Default is undef, indicating uniform workload.
$fixed_param_names: arrayref of str
Default is undef, indicating no network parameters are fixed.
$state_names : arrayref of str
states are similar to data and label, but not provided by data iterator.
Instead they are initialized to 0 and can be set by set_states()
=cut
extends 'AI::MXNet::Module::Base';
has '_sym_gen' => (is => 'ro', init_arg => 'sym_gen', required => 1);
has '_default_bucket_key' => (is => 'rw', init_arg => 'default_bucket_key', required => 1);
has '_context' => (
is => 'ro', isa => 'AI::MXNet::Context|ArrayRef[AI::MXNet::Context]',
lazy => 1, default => sub { AI::MXNet::Context->cpu },
init_arg => 'context'
lib/AI/MXNet/Module/Bucketing.pm view on Meta::CPAN
has '_fixed_param_names' => (is => 'rw', isa => 'ArrayRef[Str]', init_arg => 'fixed_param_names');
has '_state_names' => (is => 'rw', isa => 'ArrayRef[Str]', init_arg => 'state_names');
has '_params_dirty' => (is => 'rw', init_arg => undef);
sub BUILD
{
my ($self, $original_params) = @_;
$self->_fixed_param_names([]) unless defined $original_params->{fixed_param_names};
$self->_state_names([]) unless defined $original_params->{state_names};
$self->_params_dirty(0);
my ($symbol, $data_names, $label_names) = &{$self->_sym_gen}($self->_default_bucket_key);
$self->_check_input_names($symbol, $data_names//[], "data", 1);
$self->_check_input_names($symbol, $label_names//[], "label", 0);
$self->_check_input_names($symbol, $self->_state_names, "state", 1);
$self->_check_input_names($symbol, $self->_fixed_param_names, "fixed_param", 1);
}
method _reset_bind()
{
$self->binded(0);
$self->_buckets({});
$self->_curr_module(undef);
$self->_curr_bucket_key(undef);
}
method data_names()
{
if($self->binded)
{
return $self->_curr_module->data_names;
}
else
{
return (&{$self->_sym_gen}($self->_default_bucket_key))[1];
}
}
method output_names()
{
if($self->binded)
{
return $self->_curr_module->ouput_names;
}
else
{
my ($symbol) = &{$self->_sym_gen}($self->_default_bucket_key);
return $symbol->list_ouputs;
}
}
method data_shapes()
{
assert($self->binded);
return $self->_curr_module->data_shapes;
}
method label_shapes()
{
assert($self->binded);
return $self->_curr_module->label_shapes;
}
method output_shapes()
{
lib/AI/MXNet/Module/Bucketing.pm view on Meta::CPAN
}
=head2 bind
Binding for a AI::MXNet::Module::Bucketing means setting up the buckets and bind the
executor for the default bucket key. Executors corresponding to other keys are
binded afterwards with switch_bucket.
Parameters
----------
:$data_shapes : ArrayRef[AI::MXNet::DataDesc|NameShape]
This should correspond to the symbol for the default bucket.
:$label_shapes= : Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]]
This should correspond to the symbol for the default bucket.
:$for_training : Bool
Default is 1.
:$inputs_need_grad : Bool
Default is 0.
:$force_rebind : Bool
Default is 0.
:$shared_module : AI::MXNet::Module::Bucketing
Default is undef. This value is currently not used.
:$grad_req : str, array ref of str, hash ref of str to str
Requirement for gradient accumulation. Can be 'write', 'add', or 'null'
(defaults to 'write').
Can be specified globally (str) or for each argument (array ref, hash ref).
:$bucket_key : str
bucket key for binding. by default is to use the ->default_bucket_key
=cut
method bind(
ArrayRef[AI::MXNet::DataDesc|NameShape] :$data_shapes,
Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]] :$label_shapes=,
Bool :$for_training=1,
Bool :$inputs_need_grad=0,
Bool :$force_rebind=0,
Maybe[AI::MXNet::BaseModule] :$shared_module=,
Str|ArrayRef[Str]|HashRef[Str] :$grad_req='write',
Maybe[Str] :$bucket_key=
)
{
# in case we already initialized params, keep it
lib/AI/MXNet/Module/Bucketing.pm view on Meta::CPAN
$self->logger->warning('Already binded, ignoring bind()');
return;
}
assert((not defined $shared_module), 'shared_module for BucketingModule is not supported');
$self->for_training($for_training);
$self->inputs_need_grad($inputs_need_grad);
$self->binded(1);
my ($symbol, $data_names, $label_names) = &{$self->_sym_gen}($bucket_key//$self->_default_bucket_key);
my $module = AI::MXNet::Module->new(
symbol => $symbol,
data_names => $data_names,
label_names => $label_names,
logger => $self->logger,
context => $self->_context,
work_load_list => $self->_work_load_list,
state_names => $self->_state_names,
fixed_param_names => $self->_fixed_param_names
);
$module->bind(
data_shapes => $data_shapes,
label_shapes => $label_shapes,
for_training => $for_training,
inputs_need_grad => $inputs_need_grad,
force_rebind => 0,
shared_module => undef,
grad_req => $grad_req
);
$self->_curr_module($module);
$self->_curr_bucket_key($self->_default_bucket_key);
$self->_buckets->{ $self->_default_bucket_key } = $module;
lib/AI/MXNet/Module/Bucketing.pm view on Meta::CPAN
}
=head2 switch_bucket
Switch to a different bucket. This will change $self->_curr_module.
Parameters
----------
:$bucket_key : str (or any perl object that overloads "" op)
The key of the target bucket.
:$data_shapes : Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]]
Typically $data_batch->provide_data.
:$label_shapes : Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]]
Typically $data_batch->provide_label.
=cut
method switch_bucket(
Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]] :$data_shapes=,
Maybe[ArrayRef[AI::MXNet::DataDesc|NameShape]] :$label_shapes=,
:$bucket_key
)
{
assert($self->binded, 'call bind before switching bucket');
if(not exists $self->_buckets->{ $bucket_key })
{
my ($symbol, $data_names, $label_names) = &{$self->_sym_gen}($bucket_key);
my $module = AI::MXNet::Module->new(
symbol => $symbol,
data_names => $data_names,
label_names => $label_names,
logger => $self->logger,
context => $self->_context,
work_load_list => $self->_work_load_list
);
$module->bind(
data_shapes => $data_shapes,
label_shapes => $label_shapes,
for_training => $self->_curr_module->for_training,
inputs_need_grad => $self->_curr_module->inputs_need_grad,
force_rebind => 0,
shared_module => $self->_buckets->{ $self->_default_bucket_key },
);
$self->_buckets->{ $bucket_key } = $module;
}
$self->_curr_module($self->_buckets->{ $bucket_key });
$self->_curr_bucket_key($bucket_key);
lib/AI/MXNet/Module/Bucketing.pm view on Meta::CPAN
for my $mod (values %{ $self->_buckets })
{
if($mod ne $self->_curr_module)
{
$mod->borrow_optimizer($self->_curr_module);
}
}
$self->optimizer_initialized(1);
}
method prepare(AI::MXNet::DataBatch $data_batch)
{
assert($self->binded and $self->params_initialized);
## perform bind if have not done so yet
my $original_bucket_key = $self->_curr_bucket_key;
$self->switch_bucket(
bucket_key => $data_batch->bucket_key,
data_shapes => $data_batch->provide_data,
label_shapes => $data_batch->provide_label
);
# switch back
$self->switch_bucket(bucket_key => $original_bucket_key);
}
method forward(
AI::MXNet::DataBatch $data_batch,
Bool :$is_train=
)
{
assert($self->binded and $self->params_initialized);
$self->switch_bucket(
bucket_key => $data_batch->bucket_key,
data_shapes => $data_batch->provide_data,
label_shapes => $data_batch->provide_label
);
$self->_curr_module->forward($data_batch, is_train => $is_train);
}
method backward(Maybe[ArrayRef[AI::MXNet::NDArray]|AI::MXNet::NDArray] $out_grads=)
{
assert($self->binded and $self->params_initialized);
$self->_curr_module->backward($out_grads);
}
method update()
{
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
my $ndary_shape_str = join(',', @{ $self->shape });
if($pdl_shape_str ne $ndary_shape_str)
{
confess("Shape inconsistant: expected $ndary_shape_str vs got $pdl_shape_str")
}
my $perl_pack_type = DTYPE_MX_TO_PERL->{$dtype};
my $buf;
## special handling for float16
if($perl_pack_type eq 'S')
{
$buf = pack("S*", map { AI::MXNetCAPI::_float_to_half($_) } unpack ("f*", ${$source_array->get_dataref}));
}
else
{
$buf = ${$source_array->get_dataref};
}
check_call(AI::MXNetCAPI::NDArraySyncCopyFromCPU($self->handle, $buf, $self->size));
return $self;
}
=head2 aspdl
Returns a copied PDL array of current array.
Returns
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
my $pdl_type = PDL::Type->new(DTYPE_MX_TO_PDL->{ $dtype });
my $pdl = PDL->new_from_specification($pdl_type, reverse @{ $self->shape });
my $perl_pack_type = DTYPE_MX_TO_PERL->{$dtype};
my $buf = pack("$perl_pack_type*", (0)x$self->size);
check_call(AI::MXNetCAPI::NDArraySyncCopyToCPU($self->handle, $buf, $self->size));
## special handling for float16
if($perl_pack_type eq 'S')
{
$buf = pack("f*", map { AI::MXNetCAPI::_half_to_float($_) } unpack("S*", $buf));
}
${$pdl->get_dataref} = $buf;
$pdl->upd_data;
return $pdl;
}
=head2 asmpdl
Returns copied PDL::Matrix objectt of current array.
Requires caller to "use PDL::Matrix" in user space.
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
my $pdl_type = PDL::Type->new(DTYPE_MX_TO_PDL->{ $dtype });
my $pdl = PDL::Matrix->new_from_specification($pdl_type, @{ $self->shape });
my $perl_pack_type = DTYPE_MX_TO_PERL->{$dtype};
my $buf = pack("$perl_pack_type*", (0)x$self->size);
check_call(AI::MXNetCAPI::NDArraySyncCopyToCPU($self->handle, $buf, $self->size));
## special handling for float16
if($perl_pack_type eq 'S')
{
$buf = pack("f*", map { AI::MXNetCAPI::_half_to_float($_) } unpack("S*", $buf));
}
${$pdl->get_dataref} = $buf;
$pdl->upd_data;
return $pdl;
}
=head2 _slice
Returns sliced NDArray that shares memory with the current one.
Parameters
----------
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
AI::MXNetCAPI::NDArrayGetContext($self->handle)
);
return AI::MXNet::Context->new(
device_type => AI::MXNet::Context::devtype2str->{ $dev_type_id },
device_id => $dev_id
);
}
=head2 dtype
The data type of current NDArray.
Returns
-------
a data type string ('float32', 'float64', 'float16', 'uint8', 'int32')
representing the data type of the ndarray.
'float32' is the default dtype for the ndarray class.
=cut
method dtype()
{
my $dtype = check_call(
AI::MXNetCAPI::NDArrayGetDType(
$self->handle
)
);
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
Copy the content of current array to another entity.
When another entity is the NDArray, the content is copied over.
When another entity is AI::MXNet::Context, a new NDArray in the context
will be created.
Parameters
----------
other : NDArray or Context
Target NDArray or context we want to copy data to.
Returns
-------
dst : NDArray
=cut
method copyto(AI::MXNet::Context|AI::MXNet::NDArray $other)
{
if(blessed($other) and $other->isa('AI::MXNet::Context'))
{
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
return __PACKAGE__->_set_value({ src => $val, out => $out ? $out : __PACKAGE__->empty($shape, ctx => $ctx, dtype => $dtype) });
}
=head2 array
Creates a new NDArray that is a copy of the source_array.
Parameters
----------
$source_array : AI::MXNet::NDArray PDL, PDL::Matrix, Array ref in PDL::pdl format
Source data to create NDArray from.
:$ctx : AI::MXNet::Context, optional
The context of the NDArray, defaults to current default context.
:$dtype : Dtype, optional
The dtype of the NDArray, defaults to 'float32'.
Returns
-------
out: Array
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
=head2 concatenate
Concatenates an array ref of NDArrays along the first dimension.
Parameters
----------
$arrays : array ref of NDArrays
Arrays to be concatenate. They must have identical shape except
for the first dimension. They also must have the same data type.
:$axis=0 : int
The axis along which to concatenate.
:$always_copy=1 : bool
Default is 1. When not 1, if the arrays only contain one
NDArray, that element will be returned directly, avoid copying.
Returns
-------
An NDArray in the same context as $arrays->[0]->context.
=cut
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
Start of interval. The interval includes this value. The default start value is 0.
$stop= : number, optional
End of interval. The interval does not include this value.
:$step=1 : number, optional
Spacing between the values
:$repeat=1 : number, optional
The repeating time of all elements.
E.g repeat=3, the element a will be repeated three times --> a, a, a.
:$ctx : Context, optional
The context of the NDArray, defaultw to current default context.
:$dtype : data type, optional
The value type of the NDArray, defaults to float32
Returns
-------
$out : NDArray
The created NDArray
=cut
method arange(Index :$start=0, Index :$stop=, Index :$step=1, Index :$repeat=1,
AI::MXNet::Context :$ctx=AI::MXNet::Context->current_ctx, Dtype :$dtype='float32')
lib/AI/MXNet/NDArray.pm view on Meta::CPAN
Parameters
----------
fname : str
The name of the file.Can be S3 or HDFS address (remember built with S3 support).
Example of fname:
- `s3://my-bucket/path/my-s3-ndarray`
- `hdfs://my-bucket/path/my-hdfs-ndarray`
- `/path-to/my-local-ndarray`
$data : array ref of NDArrays or hash ref of NDArrays
The data to be saved.
=cut
method save(Str $filename, ArrayRef[AI::MXNet::NDArray]|HashRef[AI::MXNet::NDArray] $data)
{
my $handles = [];
my $names = [];
if(ref $data eq 'HASH')
{
for my $name (keys %$data)
{
push @$names, $name;
push @$handles, $data->{ $name }->handle;
}
}
else
{
@$handles = map { $_->handle } @$data;
}
check_call(
AI::MXNetCAPI::NDArraySave(
$filename,
scalar(@$handles),
$handles,
$names
)
);
}
=head2 imdecode
Decode an image from string. Requires OpenCV to work.
Parameters
----------
$str_img : str
binary image data
:$clip_rect : iterable of 4 int
clip decoded image to rectangle (x0, y0, x1, y1)
:$out= : Maybe[NDArray]
output buffer. can be 3 dimensional (c, h, w) or 4 dimensional (n, c, h, w)
:$index : int
output decoded image to i-th slice of 4 dimensional buffer
:$channels=3 : int
number of channels to output. Decode to grey scale when channels = 1.
$mean= : Maybe[NDArray]
subtract mean from decode image before outputting.
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
Str :$input_prefix='',
Str :$layout='NTC',
Maybe[Bool] :$merge_outputs=
)
{
$self->reset;
my $axis = index($layout, 'T');
if(not defined $inputs)
{
$inputs = [
map { AI::MXNet::Symbol->Variable("${input_prefix}t${_}_data") } (0..$length-1)
];
}
elsif(blessed($inputs))
{
assert(
(@{ $inputs->list_outputs() } == 1),
"unroll doesn't allow grouped symbol as input. Please "
."convert to list first or let unroll handle slicing"
);
$inputs = AI::MXNet::Symbol->SliceChannel(
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
method state_info()
{
return [{ shape => [0, $self->_num_hidden], __layout__ => 'NC' }];
}
method call(AI::MXNet::Symbol $inputs, SymbolOrArrayOfSymbols $states)
{
$self->_counter($self->_counter + 1);
my $name = sprintf('%st%d_', $self->_prefix, $self->_counter);
my $i2h = AI::MXNet::Symbol->FullyConnected(
data => $inputs,
weight => $self->_iW,
bias => $self->_iB,
num_hidden => $self->_num_hidden,
name => "${name}i2h"
);
my $h2h = AI::MXNet::Symbol->FullyConnected(
data => @{$states}[0],
weight => $self->_hW,
bias => $self->_hB,
num_hidden => $self->_num_hidden,
name => "${name}h2h"
);
my $output = $self->_get_activation(
$i2h + $h2h,
$self->_activation,
name => "${name}out"
);
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
{
[qw/_i _f _c _o/];
}
method call(AI::MXNet::Symbol $inputs, SymbolOrArrayOfSymbols $states)
{
$self->_counter($self->_counter + 1);
my $name = sprintf('%st%d_', $self->_prefix, $self->_counter);
my @states = @{ $states };
my $i2h = AI::MXNet::Symbol->FullyConnected(
data => $inputs,
weight => $self->_iW,
bias => $self->_iB,
num_hidden => $self->_num_hidden*4,
name => "${name}i2h"
);
my $h2h = AI::MXNet::Symbol->FullyConnected(
data => $states[0],
weight => $self->_hW,
bias => $self->_hB,
num_hidden => $self->_num_hidden*4,
name => "${name}h2h"
);
my $gates = $i2h + $h2h;
my @slice_gates = @{ AI::MXNet::Symbol->SliceChannel(
$gates, num_outputs => 4, name => "${name}slice"
) };
my $in_gate = AI::MXNet::Symbol->Activation(
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
{
[qw/_r _z _o/];
}
method call(AI::MXNet::Symbol $inputs, SymbolOrArrayOfSymbols $states)
{
$self->_counter($self->_counter + 1);
my $name = sprintf('%st%d_', $self->_prefix, $self->_counter);
my $prev_state_h = @{ $states }[0];
my $i2h = AI::MXNet::Symbol->FullyConnected(
data => $inputs,
weight => $self->_iW,
bias => $self->_iB,
num_hidden => $self->_num_hidden*3,
name => "${name}i2h"
);
my $h2h = AI::MXNet::Symbol->FullyConnected(
data => $prev_state_h,
weight => $self->_hW,
bias => $self->_hB,
num_hidden => $self->_num_hidden*3,
name => "${name}h2h"
);
my ($i2h_r, $i2h_z);
($i2h_r, $i2h_z, $i2h) = @{ AI::MXNet::Symbol->SliceChannel(
$i2h, num_outputs => 3, name => "${name}_i2h_slice"
) };
my ($h2h_r, $h2h_z);
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
Int $length,
Maybe[AI::MXNet::Symbol|ArrayRef[AI::MXNet::Symbol]] :$inputs=,
Maybe[AI::MXNet::Symbol|ArrayRef[AI::MXNet::Symbol]] :$begin_state=,
Str :$input_prefix='',
Str :$layout='NTC',
Maybe[Bool] :$merge_outputs=
)
{
$self->reset;
my $axis = index($layout, 'T');
$inputs //= AI::MXNet::Symbol->Variable("${input_prefix}data");
if(blessed($inputs))
{
assert(
(@{ $inputs->list_outputs() } == 1),
"unroll doesn't allow grouped symbol as input. Please "
."convert to list first or let unroll handle slicing"
);
if($axis == 1)
{
AI::MXNet::Logging->warning(
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
my %states;
if($self->_mode eq 'lstm')
{
%states = (state => $states[0], state_cell => $states[1]);
}
else
{
%states = (state => $states[0]);
}
my $rnn = AI::MXNet::Symbol->RNN(
data => $inputs,
parameters => $self->_parameter,
state_size => $self->_num_hidden,
num_layers => $self->_num_layers,
bidirectional => $self->_bidirectional,
p => $self->_dropout,
state_outputs => $self->_get_next_state,
mode => $self->_mode,
name => $self->_prefix.'rnn',
%states
);
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
Str :$input_prefix='',
Str :$layout='NTC',
Maybe[Bool] :$merge_outputs=
)
{
my $axis = index($layout, 'T');
if(not defined $inputs)
{
$inputs = [
map { AI::MXNet::Symbol->Variable("${input_prefix}t${_}_data") } (0..$length-1)
];
}
elsif(blessed($inputs))
{
assert(
(@{ $inputs->list_outputs() } == 1),
"unroll doesn't allow grouped symbol as input. Please "
."convert to list first or let unroll handle slicing"
);
$inputs = [ @{ AI::MXNet::Symbol->SliceChannel(
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
my $self = shift;
assert (
($self->_h2h_kernel->[0] % 2 == 1 and $self->_h2h_kernel->[1] % 2 == 1),
"Only support odd numbers, got h2h_kernel= (@{[ $self->_h2h_kernel ]})"
);
$self->_h2h_pad([
int($self->_h2h_dilate->[0] * ($self->_h2h_kernel->[0] - 1) / 2),
int($self->_h2h_dilate->[1] * ($self->_h2h_kernel->[1] - 1) / 2)
]);
# Infer state shape
my $data = AI::MXNet::Symbol->Variable('data');
my $state_shape = AI::MXNet::Symbol->Convolution(
data => $data,
num_filter => $self->_num_hidden,
kernel => $self->_i2h_kernel,
stride => $self->_i2h_stride,
pad => $self->_i2h_pad,
dilate => $self->_i2h_dilate,
layout => $self->_conv_layout
);
$state_shape = ($state_shape->infer_shape(data=>$self->_input_shape))[1]->[0];
$state_shape->[0] = 0;
$self->_state_shape($state_shape);
}
method state_info()
{
return [
{ shape => $self->_state_shape, __layout__ => $self->_conv_layout },
{ shape => $self->_state_shape, __layout__ => $self->_conv_layout }
];
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
method _gate_names()
{
return ['']
}
method _conv_forward($inputs, $states, $name)
{
my $i2h = AI::MXNet::Symbol->Convolution(
name => "${name}i2h",
data => $inputs,
num_filter => $self->_num_hidden*$self->_num_gates(),
kernel => $self->_i2h_kernel,
stride => $self->_i2h_stride,
pad => $self->_i2h_pad,
dilate => $self->_i2h_dilate,
weight => $self->_iW,
bias => $self->_iB
);
my $h2h = AI::MXNet::Symbol->Convolution(
name => "${name}h2h",
data => @{ $states }[0],
num_filter => $self->_num_hidden*$self->_num_gates(),
kernel => $self->_h2h_kernel,
stride => [1, 1],
pad => $self->_h2h_pad,
dilate => $self->_h2h_dilate,
weight => $self->_hW,
bias => $self->_hB
);
return ($i2h, $h2h);
}
lib/AI/MXNet/RNN/Cell.pm view on Meta::CPAN
=head1 DESCRIPTION
Apply the dropout on base cell
=cut
method call(AI::MXNet::Symbol $inputs, SymbolOrArrayOfSymbols $states)
{
my ($output, $states) = &{$self->base_cell}($inputs, $states);
if($self->dropout_outputs > 0)
{
$output = AI::MXNet::Symbol->Dropout(data => $output, p => $self->dropout_outputs);
}
if($self->dropout_states > 0)
{
$states = [map { AI::MXNet::Symbol->Dropout(data => $_, p => $self->dropout_states) } @{ $states }];
}
return ($output, $states);
}
package AI::MXNet::RNN::ZoneoutCell;
use Mouse;
use AI::MXNet::Base;
extends 'AI::MXNet::RNN::ModifierCell';
has [qw/zoneout_outputs zoneout_states/] => (is => 'ro', isa => 'Num', default => 0);
has 'prev_output' => (is => 'rw', init_arg => undef);
lib/AI/MXNet/RNN/IO.pm view on Meta::CPAN
=encoding UTF-8
=head1 NAME
AI::MXNet::BucketSentenceIter
=cut
=head1 DESCRIPTION
Simple bucketing iterator for language model.
Label for each step is constructed from data of
next step.
=cut
=head2 new
Parameters
----------
sentences : array ref of array refs of int
encoded sentences
batch_size : int
batch_size of data
invalid_label : int, default -1
key for invalid label, e.g. <end-of-sentence>
dtype : str, default 'float32'
data type
buckets : array ref of int
size of data buckets. Automatically generated if undef.
data_name : str, default 'data'
name of data
label_name : str, default 'softmax_label'
name of label
layout : str
format of data and label. 'NT' means (batch_size, length)
and 'TN' means (length, batch_size).
=cut
use Mouse;
use AI::MXNet::Base;
use List::Util qw(shuffle max);
extends 'AI::MXNet::DataIter';
has 'sentences' => (is => 'ro', isa => 'ArrayRef[ArrayRef]', required => 1);
has '+batch_size' => (is => 'ro', isa => 'Int', required => 1);
has 'invalid_label' => (is => 'ro', isa => 'Int', default => -1);
has 'data_name' => (is => 'ro', isa => 'Str', default => 'data');
has 'label_name' => (is => 'ro', isa => 'Str', default => 'softmax_label');
has 'dtype' => (is => 'ro', isa => 'Dtype', default => 'float32');
has 'layout' => (is => 'ro', isa => 'Str', default => 'NT');
has 'buckets' => (is => 'rw', isa => 'Maybe[ArrayRef[Int]]');
has [qw/data nddata ndlabel
major_axis default_bucket_key
provide_data provide_label
idx curr_idx
/] => (is => 'rw', init_arg => undef);
sub BUILD
{
my $self = shift;
if(not defined $self->buckets)
{
my @buckets;
my $p = pdl([map { scalar(@$_) } @{ $self->sentences }]);
lib/AI/MXNet/RNN/IO.pm view on Meta::CPAN
my ($i, $j) = @_;
if($j >= $self->batch_size)
{
push @buckets, $i;
}
}, $p->histogram(1,0,$p->max+1)->unpdl);
$self->buckets(\@buckets);
}
@{ $self->buckets } = sort { $a <=> $b } @{ $self->buckets };
my $ndiscard = 0;
$self->data([map { [] } 0..@{ $self->buckets }-1]);
for my $i (0..@{$self->sentences}-1)
{
my $buck = bisect_left($self->buckets, scalar(@{ $self->sentences->[$i] }));
if($buck == @{ $self->buckets })
{
$ndiscard += 1;
next;
}
my $buff = AI::MXNet::NDArray->full(
[$self->buckets->[$buck]],
$self->invalid_label,
dtype => $self->dtype
)->aspdl;
$buff->slice([0, @{ $self->sentences->[$i] }-1]) .= pdl($self->sentences->[$i]);
push @{ $self->data->[$buck] }, $buff;
}
$self->data([map { pdl(PDL::Type->new(DTYPE_MX_TO_PDL->{$self->dtype}), $_) } @{$self->data}]);
AI::MXNet::Logging->warning("discarded $ndiscard sentences longer than the largest bucket.")
if $ndiscard;
$self->nddata([]);
$self->ndlabel([]);
$self->major_axis(index($self->layout, 'N'));
$self->default_bucket_key(max(@{ $self->buckets }));
my $shape;
if($self->major_axis == 0)
{
$shape = [$self->batch_size, $self->default_bucket_key];
}
elsif($self->major_axis == 1)
{
$shape = [$self->default_bucket_key, $self->batch_size];
}
else
{
confess("Invalid layout ${\ $self->layout }: Must by NT (batch major) or TN (time major)");
}
$self->provide_data([
AI::MXNet::DataDesc->new(
name => $self->data_name,
shape => $shape,
dtype => $self->dtype,
layout => $self->layout
)
]);
$self->provide_label([
AI::MXNet::DataDesc->new(
name => $self->label_name,
shape => $shape,
dtype => $self->dtype,
lib/AI/MXNet/RNN/IO.pm view on Meta::CPAN
enumerate(sub {
my ($i, $buck) = @_;
my $buck_len = $buck->shape->at(-1);
for my $j (0..($buck_len - $self->batch_size))
{
if(not $j%$self->batch_size)
{
push @{ $self->idx }, [$i, $j];
}
}
}, $self->data);
$self->curr_idx(0);
$self->reset;
}
method reset()
{
$self->curr_idx(0);
@{ $self->idx } = shuffle(@{ $self->idx });
$self->nddata([]);
$self->ndlabel([]);
for my $buck (@{ $self->data })
{
$buck = pdl_shuffle($buck);
my $label = $buck->zeros;
$label->slice([0, -2], 'X') .= $buck->slice([1, -1], 'X');
$label->slice([-1, -1], 'X') .= $self->invalid_label;
push @{ $self->nddata }, AI::MXNet::NDArray->array($buck, dtype => $self->dtype);
push @{ $self->ndlabel }, AI::MXNet::NDArray->array($label, dtype => $self->dtype);
}
}
method next()
{
return undef if($self->curr_idx == @{ $self->idx });
my ($i, $j) = @{ $self->idx->[$self->curr_idx] };
$self->curr_idx($self->curr_idx + 1);
my ($data, $label);
if($self->major_axis == 1)
{
$data = $self->nddata->[$i]->slice([$j, $j+$self->batch_size-1])->T;
$label = $self->ndlabel->[$i]->slice([$j, $j+$self->batch_size-1])->T;
}
else
{
$data = $self->nddata->[$i]->slice([$j, $j+$self->batch_size-1]);
$label = $self->ndlabel->[$i]->slice([$j, $j+$self->batch_size-1]);
}
return AI::MXNet::DataBatch->new(
data => [$data],
label => [$label],
bucket_key => $self->buckets->[$i],
pad => 0,
provide_data => [
AI::MXNet::DataDesc->new(
name => $self->data_name,
shape => $data->shape,
dtype => $self->dtype,
layout => $self->layout
)
],
provide_label => [
AI::MXNet::DataDesc->new(
name => $self->label_name,
shape => $label->shape,
dtype => $self->dtype,
layout => $self->layout
lib/AI/MXNet/RecordIO.pm view on Meta::CPAN
package AI::MXNet::RecordIO;
use strict;
use warnings;
use AI::MXNet::Function::Parameters;
use AI::MXNet::Types;
use AI::MXNet::Base;
use Mouse;
=head1 NAME
AI::MXNet::Function::Parameters - Read/write RecordIO format data
=cut
=head2 new
Parameters
----------
uri : Str
uri path to recordIO file.
flag: Str
"r" for reading or "w" writing.
lib/AI/MXNet/RecordIO.pm view on Meta::CPAN
my $h;
my $h_size = 24;
($h, $s) = (substr($s, 0, $h_size), substr($s, $h_size));
my $header = AI::MXNet::IRHeader->new(unpack('IfQQ', $h));
if($header->flag > 0)
{
my $label;
($label, $s) = (substr($s, 0, 4*$header->flag), substr($s, 4*$header->flag));
my $pdl_type = PDL::Type->new(DTYPE_MX_TO_PDL->{float32});
my $pdl = PDL->new_from_specification($pdl_type, $header->flag);
${$pdl->get_dataref} = $label;
$pdl->upd_data;
$header->label($pdl);
}
return ($header, $s)
}
=head2 pack
pack a string into MXImageRecord
Parameters
lib/AI/MXNet/RecordIO.pm view on Meta::CPAN
$header = AI::MXNet::IRHeader->new(@$header) unless blessed $header;
if(not ref $header->label)
{
$header->flag(0);
}
else
{
my $label = AI::MXNet::NDArray->array($header->label, dtype=>'float32')->aspdl;
$header->label(0);
$header->flag($label->nelem);
my $buf = ${$label->get_dataref};
$s = "$buf$s";
}
$s = pack('IfQQ', @{ $header }) . $s;
return $s;
}
package AI::MXNet::IndexedRecordIO;
use Mouse;
use AI::MXNet::Base;
extends 'AI::MXNet::RecordIO';
=head1 NAME
AI::MXNet::IndexedRecordIO - Read/write RecordIO format data supporting random access.
=cut
=head2 new
Parameters
----------
idx_path : str
Path to index file
uri : str
Path to record file. Only support file types that are seekable.
lib/AI/MXNet/Symbol.pm view on Meta::CPAN
The order is in the same order as list_outputs()
aux_types : array ref of Dtype or undef
List of types of outputs.
The order is in the same order as list_auxiliary()
=cut
method infer_type(Str|Undef @args)
{
my ($positional_arguments, $kwargs, $kwargs_order) = _parse_arguments("Dtype", @args);
my $sdata = [];
my $keys = [];
if(@$positional_arguments)
{
@{ $sdata } = map { defined($_) ? DTYPE_STR_TO_MX->{ $_ } : -1 } @{ $positional_arguments };
}
else
{
@{ $keys } = @{ $kwargs_order };
@{ $sdata } = map { DTYPE_STR_TO_MX->{ $_ } } @{ $kwargs }{ @{ $kwargs_order } };
}
my ($arg_type, $out_type, $aux_type, $complete) = check_call(AI::MXNetCAPI::SymbolInferType(
$self->handle,
scalar(@{ $sdata }),
$keys,
$sdata
)
);
if($complete)
{
return (
[ map { DTYPE_MX_TO_STR->{ $_ } } @{ $arg_type }],
[ map { DTYPE_MX_TO_STR->{ $_ } } @{ $out_type }],
[ map { DTYPE_MX_TO_STR->{ $_ } } @{ $aux_type }]
);
}
lib/AI/MXNet/Symbol.pm view on Meta::CPAN
method infer_shape_partial(Maybe[Str|Shape] @args)
{
$self->_infer_shape_impl(1, @args)
}
# The actual implementation for calling shape inference API.
method _infer_shape_impl(Maybe[Str|Shape] @args)
{
my $partial = shift(@args);
my ($positional_arguments, $kwargs, $kwargs_order) = _parse_arguments("Shape", @args);
my $sdata = [];
my $indptr = [0];
my $keys = [];
if(@{ $positional_arguments })
{
for my $shape (grep { defined } @{ $positional_arguments })
{
push @{ $sdata }, @{ $shape };
push @{ $indptr }, scalar(@{ $sdata });
}
}
{
for my $k (@{ $kwargs_order })
{
push @{ $keys }, $k;
push @{ $sdata }, @{ $kwargs->{ $k } };
push @{ $indptr }, scalar(@{ $sdata });
}
}
my $infer_func = $partial ? \&AI::MXNetCAPI::SymbolInferShapePartial : \&AI::MXNetCAPI::SymbolInferShape;
my ($arg_shapes, $out_shapes, $aux_shapes, $complete) = check_call(
$infer_func->(
$self->handle,
scalar(@{ $indptr }) - 1,
$keys,
$indptr,
$sdata,
)
);
if($complete)
{
return $arg_shapes, $out_shapes, $aux_shapes;
}
else
{
return (undef, undef, undef);
}
lib/AI/MXNet/Symbol.pm view on Meta::CPAN
push @$arg_handles, defined($tmp{ $name }) ? $tmp{ $name }->handle : undef;
push @$arg_arrays, defined($tmp{ $name }) ? $tmp{ $name } : undef;
}
}
return ($arg_handles, $arg_arrays);
}
=head2 simple_bind
Bind current symbol to get an executor, allocate all the ndarrays needed.
Allows specifying data types.
This function will ask user to pass in ndarray of position
they like to bind to, and it will automatically allocate the ndarray
for arguments and auxiliary states that user did not specify explicitly.
Parameters
----------
:$ctx : AI::MXNet::Context
The device context the generated executor to run on.
lib/AI/MXNet/Symbol.pm view on Meta::CPAN
Maybe[HashRef[Shape]] :$shapes=,
Maybe[HashRef[Dtype]] :$type_dict=,
Maybe[HashRef[AI::MXNet::Context]] :$group2ctx=,
Maybe[ArrayRef[Str]] :$shared_arg_names=,
Maybe[AI::MXNet::Executor] :$shared_exec=,
Maybe[HashRef[AI::MXNet::NDArray]] :$shared_buffer=
)
{
my $num_provided_arg_types;
my @provided_arg_type_names;
my @provided_arg_type_data;
if(defined $type_dict)
{
while(my ($k, $v) = each %{ $type_dict })
{
push @provided_arg_type_names, $k;
push @provided_arg_type_data, DTYPE_STR_TO_MX->{$v};
}
$num_provided_arg_types = @provided_arg_type_names;
}
my @provided_arg_shape_data;
# argument shape index in sdata,
# e.g. [sdata[indptr[0]], sdata[indptr[1]]) is the shape of the first arg
my @provided_arg_shape_idx = (0);
my @provided_arg_shape_names;
while(my ($k, $v) = each %{ $shapes//{} })
{
push @provided_arg_shape_names, $k;
push @provided_arg_shape_data, @{ $v };
push @provided_arg_shape_idx, scalar(@provided_arg_shape_data);
}
$num_provided_arg_types = @provided_arg_type_names;
my $provided_req_type_list_len = 0;
my @provided_grad_req_types;
my @provided_grad_req_names;
if(defined $grad_req)
{
if(not ref $grad_req)
{
lib/AI/MXNet/Symbol.pm view on Meta::CPAN
push @ctx_map_dev_ids, $v->device_id;
}
$num_ctx_map_keys = @ctx_map_keys;
}
my @shared_arg_name_list;
if(defined $shared_arg_names)
{
@shared_arg_name_list = @{ $shared_arg_names };
}
my %shared_data;
if(defined $shared_buffer)
{
while(my ($k, $v) = each %{ $shared_buffer })
{
$shared_data{$k} = $v->handle;
}
}
my $shared_exec_handle = defined $shared_exec ? $shared_exec->handle : undef;
my (
$updated_shared_data,
$in_arg_handles,
$arg_grad_handles,
$aux_state_handles,
$exe_handle
);
eval {
($updated_shared_data, $in_arg_handles, $arg_grad_handles, $aux_state_handles, $exe_handle)
=
check_call(
AI::MXNetCAPI::ExecutorSimpleBind(
$self->handle,
$ctx->device_type_id,
$ctx->device_id,
$num_ctx_map_keys,
\@ctx_map_keys,
\@ctx_map_dev_types,
\@ctx_map_dev_ids,
$provided_req_type_list_len,
\@provided_grad_req_names,
\@provided_grad_req_types,
scalar(@provided_arg_shape_names),
\@provided_arg_shape_names,
\@provided_arg_shape_data,
\@provided_arg_shape_idx,
$num_provided_arg_types,
\@provided_arg_type_names,
\@provided_arg_type_data,
scalar(@shared_arg_name_list),
\@shared_arg_name_list,
defined $shared_buffer ? \%shared_data : undef,
$shared_exec_handle
)
);
};
if($@)
{
confess(
"simple_bind failed: Error: $@; Arguments: ".
Data::Dumper->new(
[$shapes//{}]
)->Purity(1)->Deepcopy(1)->Terse(1)->Dump
);
}
if(defined $shared_buffer)
{
while(my ($k, $v) = each %{ $updated_shared_data })
{
$shared_buffer->{$k} = AI::MXNet::NDArray->new(handle => $v);
}
}
my @arg_arrays = map { AI::MXNet::NDArray->new(handle => $_) } @{ $in_arg_handles };
my @grad_arrays = map { defined $_ ? AI::MXNet::NDArray->new(handle => $_) : undef } @{ $arg_grad_handles };
my @aux_arrays = map { AI::MXNet::NDArray->new(handle => $_) } @{ $aux_state_handles };
my $executor = AI::MXNet::Executor->new(
handle => $exe_handle,
symbol => $self,
lib/AI/MXNet/Symbol.pm view on Meta::CPAN
- In either case, all arguments must be provided.
Returns
----------
result : an array ref of NDArrays corresponding to the values
taken by each symbol when evaluated on given args.
When called on a single symbol (not a group),
the result will be an array ref with one element.
Examples:
my $result = $symbol->eval(ctx => mx->gpu, args => {data => mx->nd->ones([5,5])});
my $result = $symbol->eval(args => {data => mx->nd->ones([5,5])});
=cut
method eval(:$ctx=AI::MXNet::Context->cpu, HashRef[AI::MXNet::NDArray]|ArrayRef[AI::MXNet::NDArray] :$args)
{
return $self->bind(ctx => $ctx, args => $args)->forward;
}
=head2 grad
lib/AI/MXNet/TestUtils.pm view on Meta::CPAN
func almost_equal(PDL $a, PDL $b, Maybe[Num] $threshold=)
{
$threshold //= default_numerical_threshold;
my $rel = reldiff($a, $b);
return $rel <= $threshold;
}
func GetMNIST_ubyte()
{
if(not -d "data")
{
mkdir "data";
}
if (
not -f 'data/train-images-idx3-ubyte'
or
not -f 'data/train-labels-idx1-ubyte'
or
not -f 'data/t10k-images-idx3-ubyte'
or
not -f 'data/t10k-labels-idx1-ubyte'
)
{
`wget http://data.mxnet.io/mxnet/data/mnist.zip -P data`;
chdir 'data';
`unzip -u mnist.zip`;
chdir '..';
}
}
func GetCifar10()
{
if(not -d "data")
{
mkdir "data";
}
if (not -f 'data/cifar10.zip')
{
`wget http://data.mxnet.io/mxnet/data/cifar10.zip -P data`;
chdir 'data';
`unzip -u cifar10.zip`;
chdir '..';
}
}
func _pdl_compare(PDL $a, PDL|Num $b, Str $criteria)
{
if(not blessed $b)
{
my $tmp = $b;
lib/AI/MXNet/TestUtils.pm view on Meta::CPAN
_pdl_compare($a, $b, 'max');
}
func pdl_minimum(PDL $a, PDL|Num $b)
{
_pdl_compare($a, $b, 'min');
}
func mlp2()
{
my $data = AI::MXNet::Symbol->Variable('data');
my $out = AI::MXNet::Symbol->FullyConnected(data=>$data, name=>'fc1', num_hidden=>1000);
$out = AI::MXNet::Symbol->Activation(data=>$out, act_type=>'relu');
$out = AI::MXNet::Symbol->FullyConnected(data=>$out, name=>'fc2', num_hidden=>10);
return $out;
}
func conv()
{
my $data = AI::MXNet::Symbol->Variable('data');
my $conv1 = AI::MXNet::Symbol->Convolution(data => $data, name=>'conv1', num_filter=>32, kernel=>[3,3], stride=>[2,2]);
my $bn1 = AI::MXNet::Symbol->BatchNorm(data => $conv1, name=>"bn1");
my $act1 = AI::MXNet::Symbol->Activation(data => $bn1, name=>'relu1', act_type=>"relu");
my $mp1 = AI::MXNet::Symbol->Pooling(data => $act1, name => 'mp1', kernel=>[2,2], stride=>[2,2], pool_type=>'max');
my $conv2 = AI::MXNet::Symbol->Convolution(data => $mp1, name=>'conv2', num_filter=>32, kernel=>[3,3], stride=>[2,2]);
my $bn2 = AI::MXNet::Symbol->BatchNorm(data => $conv2, name=>"bn2");
my $act2 = AI::MXNet::Symbol->Activation(data => $bn2, name=>'relu2', act_type=>"relu");
my $mp2 = AI::MXNet::Symbol->Pooling(data => $act2, name => 'mp2', kernel=>[2,2], stride=>[2,2], pool_type=>'max');
my $fl = AI::MXNet::Symbol->Flatten(data => $mp2, name=>"flatten");
my $fc2 = AI::MXNet::Symbol->FullyConnected(data => $fl, name=>'fc2', num_hidden=>10);
my $softmax = AI::MXNet::Symbol->SoftmaxOutput(data => $fc2, name => 'sm');
return $softmax;
}
=head2 check_consistency
Check symbol gives the same output for different running context
Parameters
----------
sym : Symbol or list of Symbols
lib/AI/MXNet/Visualization.pm view on Meta::CPAN
AI::MXNet::Vizualization - Vizualization support for Perl interface to MXNet machine learning library
=head1 SYNOPSIS
use strict;
use warnings;
use AI::MXNet qw(mx);
### model
my $data = mx->symbol->Variable('data');
my $conv1= mx->symbol->Convolution(data => $data, name => 'conv1', num_filter => 32, kernel => [3,3], stride => [2,2]);
my $bn1 = mx->symbol->BatchNorm(data => $conv1, name => "bn1");
my $act1 = mx->symbol->Activation(data => $bn1, name => 'relu1', act_type => "relu");
my $mp1 = mx->symbol->Pooling(data => $act1, name => 'mp1', kernel => [2,2], stride =>[2,2], pool_type=>'max');
my $conv2= mx->symbol->Convolution(data => $mp1, name => 'conv2', num_filter => 32, kernel=>[3,3], stride=>[2,2]);
my $bn2 = mx->symbol->BatchNorm(data => $conv2, name=>"bn2");
my $act2 = mx->symbol->Activation(data => $bn2, name=>'relu2', act_type=>"relu");
my $mp2 = mx->symbol->Pooling(data => $act2, name => 'mp2', kernel=>[2,2], stride=>[2,2], pool_type=>'max');
my $fl = mx->symbol->Flatten(data => $mp2, name=>"flatten");
my $fc1 = mx->symbol->FullyConnected(data => $fl, name=>"fc1", num_hidden=>30);
my $act3 = mx->symbol->Activation(data => $fc1, name=>'relu3', act_type=>"relu");
my $fc2 = mx->symbol->FullyConnected(data => $act3, name=>'fc2', num_hidden=>10);
my $softmax = mx->symbol->SoftmaxOutput(data => $fc2, name => 'softmax');
## creates the image file working directory
mx->viz->plot_network($softmax, save_format => 'png')->render("network.png");
=head1 DESCRIPTION
Vizualization support for Perl interface to MXNet machine learning library
=head1 Class methods
lib/AI/MXNet/Visualization.pm view on Meta::CPAN
my @cm = (
"#8dd3c7", "#fb8072", "#ffffb3", "#bebada", "#80b1d3",
"#fdb462", "#b3de69", "#fccde5"
);
# make nodes
my %hidden_nodes;
for my $node (@{ $nodes })
{
my $op = $node->{op};
my $name = $node->{name};
# input data
my %attr = %node_attr;
my $label = $name;
if($op eq 'null')
{
if($name =~ /(?:_weight|_bias|_beta|_gamma|_moving_var|_moving_mean)$/)
{
if($hide_weights)
{
$hidden_nodes{$name} = 1;
}
t/test_attr.t view on Meta::CPAN
elsif($y->{$k} ne $v)
{
return 0;
}
}
return 1;
}
sub test_attr_basic
{
my ($data, $gdata);
{
local($mx::AttrScope) = mx->AttrScope(group=>'4', data=>'great');
$data = mx->symbol->Variable(
'data',
attr => {
qw/ dtype data
group 1
force_mirroring 1/
},
lr_mult => 1);
$gdata = mx->symbol->Variable('data2');
}
ok($gdata->attr('group') == 4);
ok($data->attr('group') == 1);
ok($data->attr('lr_mult') == 1);
ok($data->attr('__lr_mult__') == 1);
ok($data->attr('force_mirroring') == 1);
ok($data->attr('__force_mirroring__') == 1);
my $data2 = Storable::thaw(Storable::freeze($data));
ok($data->attr('dtype') eq $data2->attr('dtype'));
}
sub test_operator
{
my $data = mx->symbol->Variable('data');
my ($fc1, $fc2);
{
local($mx::AttrScope) = mx->AttrScope(__group__=>'4', __data__=>'great');
$fc1 = mx->symbol->Activation($data, act_type=>'relu');
{
local($mx::AttrScope) = mx->AttrScope(__init_bias__ => 0,
__group__=>'4', __data__=>'great');
$fc2 = mx->symbol->FullyConnected($fc1, num_hidden=>10, name=>'fc2');
}
}
ok($fc1->attr('__data__') eq 'great');
ok($fc2->attr('__data__') eq 'great');
ok($fc2->attr('__init_bias__') == 0);
my $fc2copy = Storable::thaw(Storable::freeze($fc2));
ok($fc2copy->tojson() eq $fc2->tojson());
ok($fc2->get_internals()->slice('fc2_weight'));
}
sub test_list_attr
{
my $data = mx->sym->Variable('data', attr=>{'mood', 'angry'});
my $op = mx->sym->Convolution(
data=>$data, name=>'conv', kernel=>[1, 1],
num_filter=>1, attr => {'__mood__'=> 'so so', 'wd_mult'=> 'x'}
);
ok(contains({'__mood__'=> 'so so', 'wd_mult'=> 'x', '__wd_mult__'=> 'x'}, $op->list_attr()));
}
sub test_attr_dict
{
my $data = mx->sym->Variable('data', attr=>{'mood'=> 'angry'});
my $op = mx->sym->Convolution(
data=>$data, name=>'conv', kernel=>[1, 1],
num_filter=>1, attr=>{'__mood__'=> 'so so'}, lr_mult=>1
);
ok(
contains(
{
'data'=> {'mood'=> 'angry'},
'conv_weight'=> {'__mood__'=> 'so so'},
'conv'=> {
'kernel'=> '(1, 1)', '__mood__'=> 'so so',
'num_filter'=> '1', 'lr_mult'=> '1', '__lr_mult__'=> '1'
},
'conv_bias'=> {'__mood__'=> 'so so'}
},
$op->attr_dict()
)
);
t/test_conv.t view on Meta::CPAN
use AI::MXNet::TestUtils qw(GetMNIST_ubyte);
use Test::More tests => 1;
## speed up the tests when gpu present
my $gpu_present = (`perl -e 'use AI::MXNet qw(mx); print mx->nd->ones([1], ctx => mx->gpu(0))->asscalar' 2>/dev/null` eq '1');
# symbol net
my $batch_size = 100;
### model
my $data = mx->symbol->Variable('data');
my $conv1= mx->symbol->Convolution(data => $data, name => 'conv1', num_filter => 32, kernel => [3,3], stride => [2,2]);
my $bn1 = mx->symbol->BatchNorm(data => $conv1, name => "bn1");
my $act1 = mx->symbol->Activation(data => $bn1, name => 'relu1', act_type => "relu");
my $mp1 = mx->symbol->Pooling(data => $act1, name => 'mp1', kernel => [2,2], stride =>[2,2], pool_type=>'max');
my $conv2= mx->symbol->Convolution(data => $mp1, name => 'conv2', num_filter => 32, kernel=>[3,3], stride=>[2,2]);
my $bn2 = mx->symbol->BatchNorm(data => $conv2, name=>"bn2");
my $act2 = mx->symbol->Activation(data => $bn2, name=>'relu2', act_type=>"relu");
my $mp2 = mx->symbol->Pooling(data => $act2, name => 'mp2', kernel=>[2,2], stride=>[2,2], pool_type=>'max');
my $fl = mx->symbol->Flatten(data => $mp2, name=>"flatten");
my $fc1 = mx->symbol->FullyConnected(data => $fl, name=>"fc1", num_hidden=>30);
my $act3 = mx->symbol->Activation(data => $fc1, name=>'relu3', act_type=>"relu");
my $fc2 = mx->symbol->FullyConnected(data => $act3, name=>'fc2', num_hidden=>10);
my $softmax = mx->symbol->SoftmaxOutput(data => $fc2, name => 'softmax');
# check data
GetMNIST_ubyte();
my $train_dataiter = mx->io->MNISTIter({
image=>"data/train-images-idx3-ubyte",
label=>"data/train-labels-idx1-ubyte",
data_shape=>[1, 28, 28],
batch_size=>$batch_size, shuffle=>1, flat=>0, silent=>0, seed=>10});
my $val_dataiter = mx->io->MNISTIter({
image=>"data/t10k-images-idx3-ubyte",
label=>"data/t10k-labels-idx1-ubyte",
data_shape=>[1, 28, 28],
batch_size=>$batch_size, shuffle=>1, flat=>0, silent=>0});
my $n_epoch = 1;
my $mod = mx->mod->new(symbol => $softmax, ($gpu_present ? (context => mx->gpu(0)) : ()));
$mod->fit(
$train_dataiter,
eval_data => $val_dataiter,
optimizer_params=>{learning_rate=>0.01, momentum=> 0.9},
num_epoch=>$n_epoch
);
my $res = $mod->score($val_dataiter, mx->metric->create('acc'));
ok($res->{accuracy} > 0.8);
t/test_infer_shape.t view on Meta::CPAN
{
is_deeply($arg_shape_dict{$k}, $v);
}
}
sub test_mlp2_infer_shape
{
# Build MLP
my $out = mlp2();
# infer shape
my $data_shape = [100, 100];
my($arg_shapes, $out_shapes, $aux_shapes) = $out->infer_shape(data=>$data_shape);
ok(@$out_shapes == 1);
is_deeply($out_shapes->[0], [100, 10]);
my %true_shapes = (
fc2_bias => [10],
fc2_weight => [10, 1000],
fc1_bias => [1000],
fc1_weight => [1000,100]
);
_test_shapes($out, $arg_shapes, %true_shapes);
}
sub test_mlp2_infer_error
{
# Test shape inconsistent case
my $out = mlp2();
my $weight_shape = [1, 100];
my $data_shape = [100, 100];
eval { $out->infer_shape(data=>$data_shape, fc1_weight=>$weight_shape) };
like($@, qr/Shape inconsistent/);
}
sub test_backward_infer
{
my $w = mx->sym->Variable("weight");
my $wshift = mx->sym->Variable("wshift", shape=>[1]);
my $data = mx->sym->Variable("data");
# broadcast add here, not being able to deduce shape correctly
my $wt = mx->sym->broadcast_add($w, $wshift);
# shape constraint, this is what enables backward shape inference
$wt = mx->sym->_identity_with_attr_like_rhs($wt, $w);
my $net = mx->sym->FullyConnected(data=>$data, weight=>$wt, num_hidden=>11, no_bias=>1);
my $data_shape = [7, 100];
my ($arg_shapes, $out_shapes, $aux_shapes) = $net->infer_shape(data=>$data_shape);
_test_shapes($net, $arg_shapes, weight=>[11,100]);
}
sub test_incomplete_infer_elewise
{
my $a = mx->sym->Variable('a', shape=>[0, 10]);
my $b = mx->sym->Variable('b', shape=>[12, 0]);
my $c = $a + $b;
my ($arg_shapes) = $c->infer_shape();
_test_shapes($c, $arg_shapes, a=>[12,10], b=>[12,10]);
}
sub test_incomplete_infer_mlp
{
my $a = mx->sym->Variable('a', shape=>[0, 10]);
my $b = mx->sym->FullyConnected(data=>$a, num_hidden=>21);
my $c = mx->sym->Variable('c', shape=>[5, 0]);
my $d = $b + $c;
my ($arg_shapes) = $d->infer_shape();
_test_shapes($d, $arg_shapes, a=>[5,10], c=>[5,21]);
}
sub test_incomplete_infer_slicechannel
{
my $a = mx->sym->Variable('a', shape=>[0, 10]);
my $b = mx->sym->SliceChannel(data=>$a, num_outputs=>10, axis=>1, squeeze_axis=>1);
my $c = mx->sym->Variable('c', shape=>[5]);
my $d = @{$b}[1] + $c;
my ($arg_shapes) = $d->infer_shape();
_test_shapes($d, $arg_shapes, a=>[5,10]);
$a = mx->sym->Variable('a', shape=>[0, 15, 0]);
$b = mx->sym->SliceChannel(data=>$a, num_outputs=>3, squeeze_axis=>0);
$c = mx->sym->Variable('c', shape=>[3, 5, 2]);
$d = @{$b}[1] + $c;
($arg_shapes) = $d->infer_shape();
_test_shapes($d, $arg_shapes, a=>[3,15,2]);
}
sub test_incomplete_infer_convolution
{
my $a = mx->sym->Variable('a', shape=>[0, 10, 0, 0]);
my $b = mx->sym->Convolution(data=>$a, num_filter=>21, kernel=>[3, 3], dilate=>[1, 1], pad=>[1, 1]);
my $c = mx->sym->Variable('c', shape=>[5, 21, 32, 32]);
my $d = $b + $c;
my ($arg_shapes) = $d->infer_shape();
_test_shapes($d, $arg_shapes, a=>[5, 10, 32, 32]);
}
sub test_incomplete_infer_concat
{
my $a = mx->sym->Variable('a', shape=>[0, 10]);
my $b = mx->sym->Variable('b', shape=>[0, 5]);
t/test_init.t view on Meta::CPAN
use strict;
use warnings;
use Test::More tests => 4;
use AI::MXNet qw(mx);
sub test_default_init
{
my $data = mx->sym->Variable('data');
my $sym = mx->sym->LeakyReLU(data => $data, act_type => 'prelu');
my $mod = mx->mod->Module($sym);
$mod->bind(data_shapes=>[['data', [10,10]]]);
$mod->init_params;
ok((((values %{ ($mod->get_params)[0] }))[0]->aspdl == 0.25)->all);
}
sub test_variable_init
{
my $data = mx->sym->Variable('data');
my $gamma = mx->sym->Variable('gamma', init => mx->init->One());
my $sym = mx->sym->LeakyReLU(data => $data, gamma => $gamma, act_type => 'prelu');
my $mod = mx->mod->Module($sym);
$mod->bind(data_shapes=>[['data', [10,10]]]);
$mod->init_params();
ok((((values %{ ($mod->get_params)[0] }))[0]->aspdl == 1)->all);
}
sub test_aux_init
{
my $data = mx->sym->Variable('data');
my $sym = mx->sym->BatchNorm(data => $data, name => 'bn');
my $mod = mx->mod->Module($sym);
$mod->bind(data_shapes=>[['data', [10, 10, 3, 3]]]);
$mod->init_params();
ok((($mod->get_params)[1]->{bn_moving_var}->aspdl == 1)->all);
ok((($mod->get_params)[1]->{bn_moving_mean}->aspdl == 0)->all);
}
test_default_init();
test_variable_init();
test_aux_init();
t/test_io.t view on Meta::CPAN
use AI::MXNet::TestUtils qw(same reldiff GetMNIST_ubyte GetCifar10);
use PDL;
use PDL::Types;
use PDL::NiceSlice;
$|++;
sub test_Cifar10Rec()
{
GetCifar10();
my $dataiter = mx->io->ImageRecordIter({
path_imgrec => "data/cifar/train.rec",
mean_img => "data/cifar/cifar10_mean.bin",
rand_crop => 0,
and_mirror => 0,
shuffle => 0,
data_shape => [3,28,28],
batch_size => 100,
preprocess_threads => 4,
prefetch_buffer => 1
});
my @labelcount;
my $batchcount = 0;
while(my $batch = <$dataiter>)
{
my $nplabel = $batch->label->[0];
for my $i (0..$nplabel->shape->[0]-1)
{
$labelcount[int($nplabel->at($i)->asscalar)] += 1;
}
}
for my $i (0..9)
{
ok($labelcount[$i] == 5000);
}
}
sub test_NDArrayIter()
{
my $datas = ones(PDL::Type->new(6), 2, 2, 1000);
my $labels = ones(PDL::Type->new(6), 1, 1000);
for my $i (0..999)
{
$datas(:,:,$i) .= $i / 100;
$labels(:,$i) .= $i / 100;
}
my $dataiter = mx->io->NDArrayIter(
data => $datas,
label => $labels,
batch_size => 128,
shuffle => 1,
last_batch_handle => 'pad'
);
my $batchidx = 0;
while(<$dataiter>)
{
$batchidx += 1;
}
is($batchidx, 8);
$dataiter = mx->io->NDArrayIter(
data => $datas,
label => $labels,
batch_size => 128,
shuffle => 0,
last_batch_handle => 'pad'
);
$batchidx = 0;
my @labelcount;
my $i = 0;
for my $batch (@{ $dataiter })
{
my $label = $batch->label->[0];
my $flabel = $label->aspdl->flat;
ok($batch->data->[0]->aspdl->slice(0,0,'X')->flat->at(0) == $flabel->at(0));
for my $i (0..$label->shape->[0]-1)
{
$labelcount[$flabel->at($i)] += 1;
}
}
for my $i (0..9)
{
if($i == 0)
{
ok($labelcount[$i] == 124);
t/test_io.t view on Meta::CPAN
ok($labelcount[$i] == 100);
}
}
}
sub test_MNISTIter()
{
GetMNIST_ubyte();
my $batch_size = 100;
my $train_dataiter = mx->io->MNISTIter({
image => "data/train-images-idx3-ubyte",
label => "data/train-labels-idx1-ubyte",
data_shape => [784],
batch_size => $batch_size,
shuffle => 1,
flat => 1,
silent => 0,
seed => 10
});
# test_loop
my $nbatch = 60000 / $batch_size;
my $batch_count = 0;
for my $batch (@{ $train_dataiter})
{
$batch_count += 1;
}
ok($nbatch == $batch_count);
# test_reset
$train_dataiter->reset();
$train_dataiter->iter_next();
my $label_0 = $train_dataiter->getlabel->aspdl->flat;
$train_dataiter->iter_next;
$train_dataiter->iter_next;
$train_dataiter->iter_next;
$train_dataiter->reset;
$train_dataiter->iter_next;
my $label_1 = $train_dataiter->getlabel->aspdl->flat;
ok(sum($label_0 - $label_1) == 0);
}
test_NDArrayIter();
test_MNISTIter();
test_Cifar10Rec();