鈍足ランナーのIT日記

走るのが好きな5流のITエンジニアのブログ。

趣味の範囲は広いけど、どれも中途半端なクソブロガー楽しめるWebアプリを作ってあっと言わせたい。サーバーサイドPerl(Mojolicious)、クライアントサイドVue.js。Arduinoにも触手を伸ばす予定。

PerlでMNISTを画像ファイルでやりたい

AI::MXNetを最近使って機械学習を勉強中。
0-9の手書き文字の認識するサンプルMNISTはサンプルコードもあるのですが
MNISTデータは28*28次元のバイナリデータでして、応用しようとしても
そのままだと使いづらい、画像データ(pngなど)でやりたいのです。

Githubを調べた結果MNISTのバイナリデータをpngに変換するツールがありました。
github.com

そこで、pngファイルになったものから、コントロールファイル形式(パス一覧のようなもの)のファイルをperlで作り。
そのデータを元に学習されました。

pngファイルはtraning/[0-9]/index.png, testing/[0-9]/index.pngというようなフォルダ構成
で作られていますので、正規表現で処理してファイルに書き出してあげています。

use strict;
use warnings;

open my $train, ">./training.ctl";
open my $test, ">./testing.ctl";
my @png = glob "./*/*/*.png";
for my $png(@png){
  $png =~ /(training|testing)\/(\d)\/(\d+)\.png/;
  my $file;
  if ($1 eq "training" ){
      $file = $train;
  }
  else{
      $file = $test;
  }
  printf $file "%s\t%s\t./%s/%s/%s.png\n", $3 , $2, $1 , $2, $3;
}

close $train;
close $test;

コントロールファイルの内容はこんな感じです。

index    label    pngファイルのパス

次に、コントロールファイル形式のデータをImageIterで読み込み、機械学習をさせるようにperlでコーディングしました。(ほぼコピペ)
MNISTは白黒のデータなので1次元ですが、pngへ変換すると3次元(RGB)に変わっておりますのでdata_schapeの第1引数が3になっています。

use strict;
use warnings;
use AI::MXNet qw('mx');
use Data::Dumper;
use Test::More tests => 1;

 my $training_ite = mx->img()->ImageIter(
 {  batch_size => 10, data_shape=> [3,28,28],label_width =>1, path_imglist => "training.ctl", path_root => '.' });

 my $testing_ite = mx->img()->ImageIter(
 {  batch_size => 10, data_shape=> [3,28,28],label_width =>1, path_imglist => "testing.ctl", path_root => '.' });

# Create a place holder variable for the input data
my $data = mx->sym->Variable('data');

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');

my $model = mx->mod->Module(
    symbol => $softmax,       # network structure
);
$model->fit(
   $training_ite,
   eval_data => $testing_ite,
   optimizer_params=>{learning_rate=>0.01, momentum=> 0.9},
   num_epoch=>1
);
my $res = $model->score($testing_ite, mx->metric->create('acc'));
ok($res->{accuracy} > 0.8);

が、しかし、テストが通らない。。何かがマズイようです。
もしや、グレースケールのモデルをそのまま使っているから、
RGBに対応したモデルにしないといけないのかも??

mxnetの畳み込み層は3Dに対応したものらしい。

stackoverflow.com

RGBの畳み込み

s0sem0y.hatenablog.com

畳み込みの例

deeplearning4j.org

グレースケールで処理しよう

画像をグレースケールで処理するのが良いのではないか。
用意した画像ファイルを元に、recというファイル形式に変えるim2recコマンドが
mxnetをインストールすると付いてくるコマンド。
これのオプションにグレースケールで処理を可能にする--colorというオプションがあるので
そいつを使うのが最もポピュラーな方法かも。

im2rec training.ctl  ./ training.rec encoding=.png color=0