Perlのディープラーニングのライブラリ - AI::MXNet

Perlのディープラーニングライブラリには、AI::MXNetがあります。C++で書かれたディープラーニングのライブラリをPerlでバインディングして利用できるようにしたものです。

まずは、実際にディープラーニングを試してみたいという方は、ライブラリを使うと簡単に(?)できます。ディープラーニングは深層学習とも呼ばれます。深層学習のPerlのライブラリを探している方も、AI::MXNetがそれです。

MXNetのAmazon AWSにおける公式サポート

MXNetはAmazon AWSにおける公式サポートがあります。

柔軟性と選択肢

MXNetでは、C++、JavaScript、Python、R、Matlab、Julia、Scala、Clojure、Perl といったプログラミング言語が幅広くサポートされているため、自分のすでに知っている言語で開始することができます。ただし、バックエンドではすべてのコードが C++ にコンパイルされるため、モデル構築に使用された言語にかかわりなく最大限のパフォーマンスを発揮できます。

AWS での Apache MXNet

AI::MXNetは、数少ないAmazon Perlサポートのひとつです...。

ディープラーニングを使った画像生成のサンプル

AI::MXNetの作者のSergey V. Kolychevさんの英語のブログによると、ディープラーニングを使ったイメージ生成などもできるようです。作者の方自身は、自然言語処理に関連するディープラーニングを業務で行っているようです。

この例を楽しんで、素敵な写真をたくさん作り出してほしいですね。以下は、キュウビの写真と異なる古典的な絵画から作られたサンプルスクリプトによって生成された画像です。

Machine learning in Perl: Kyuubi goes to a (Model)Zoo during The Starry Night.

元の画像

ディープラーニングで生成された画像

ゴッホ風画風みたいなのを学習させて、オリジナル画像から生成したものでしょうか。

AI::MXNetの使い方

使い方をサンプルから紹介です。

## 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 L<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);
 
## Gluon MNIST example
 
my $net = nn->Sequential();
$net->name_scope(sub {
    $net->add(nn->Dense(128, activation=>'relu'));
    $net->add(nn->Dense(64, activation=>'relu'));
    $net->add(nn->Dense(10));
});
$net->hybridize;
 
# data
sub transformer
{
    my ($data, $label) = @_;
    $data = $data->reshape([-1])->astype('float32')/255;
    return ($data, $label);
}
my $train_data = gluon->data->DataLoader(
    gluon->data->vision->MNIST('./data', train=>1, transform => \&transformer),
    batch_size=>$batch_size, shuffle=>1, last_batch=>'discard'
);
 
## training
sub train
{
    my ($epochs, $ctx) = @_;
    # Collect all parameters from net and its children, then initialize them.
    $net->initialize(mx->init->Xavier(magnitude=>2.24), ctx=>$ctx);
    # Trainer is for updating parameters with gradient.
    my $trainer = gluon->Trainer($net->collect_params(), 'sgd', { learning_rate => $lr, momentum => $momentum });
    my $metric = mx->metric->Accuracy();
    my $loss = gluon->loss->SoftmaxCrossEntropyLoss();
 
    for my $epoch (0..$epochs-1)
    {
        # reset data iterator and metric at begining of epoch.
        $metric->reset();
        enumerate(sub {
            my ($i, $d) = @_;
            my ($data, $label) = @$d;
            $data = $data->as_in_context($ctx);
            $label = $label->as_in_context($ctx);
            # Start recording computation graph with record() section.
            # Recorded graphs can then be differentiated with backward.
            my $output;
            autograd->record(sub {
                $output = $net->($data);
                my $L = $loss->($output, $label);
                $L->backward;
            });
            # take a gradient step with batch_size equal to data.shape[0]
            $trainer->step($data->shape->[0]);
            # update metric at last.
            $metric->update([$label], [$output]);
 
            if($i % $log_interval == 0 and $i > 0)
            {
                my ($name, $acc) = $metric->get();
                print "[Epoch $epoch Batch $i] Training: $name=$acc\n";
            }
        }, \@{ $train_data });
 
        my ($name, $acc) = $metric->get();
        print "[Epoch $epoch] Training: $name=$acc\n";
 
        my ($val_name, $val_acc) = test($ctx);
        print "[Epoch $epoch] Validation: $val_name=$val_acc\n"
    }
    $net->save_parameters('mnist.params');
}
 
train($epochs, $cuda ? mx->gpu(0) : mx->cpu);

関連情報