JavaScriptでニューラルネットワーク : synaptic.jsですぐに作れる手書き数字分類器

ニューラルネットワークは複雑な非線形問題を解決する可能性を提供してくれるもので、信号の分類や時系列的な予測、パターン認識など様々な領域で利用できます。ニューラルネットワークは人間の脳から着想を得たモデルで、接続された複数のニューロンから構成されます。ネットワークには入力ニューロンの層(情報が入ってくるところ)と出力ニューロンの層(結果が得られるところ)、そしてその間にある「隠れ層」と呼ばれるいくつかの層があります。

neuronal network scheme

より深い理解のために、Neural Networks and Deep Learningをチェックすることをお勧めします。

ここ数年、ニューラルネットワークを色々な目的のために作成・訓練・利用する助けになるJavaScriptフレームワークがいくつも開発されました。このブログ記事では、画像の分類のためにネットワークをセットアップする方法を学んでいきます。

ニューラルネットワークの入門の一般的な例として、手書きの数字の分類があります。よい結果を得るためにはネットワークを適切に学習させる必要があるため、「訓練データ」と呼ばれるデータセットが必要となります。私たちの例ではMNISTの数字のデータを用います。これは、0から9の手書き数字の28x28pxの二値化画像が大量に集まったデータセットです。

mnist numbers

MNISTデータベースには60,000個の訓練データと10,000個のテストデータがあり、LeCunのウェブサイトからダウンロードできます。データベースをダウンロードして実画像に変換する代わりに、MNIST digitsという便利なライブラリを使って、テストデータと訓練データのデータセットを自動的に生成することができます。

const mnist = require('mnist'); 

const set = mnist.set(700, 20);

const trainingSet = set.training;
const testSet = set.test;

上記のコードでは、700個の画像からなる訓練データセットと、20個の画像からなるテストデータセットを生成しています。データセットを手動で生成する場合は、データセット内に重複がないことを確認することが重要になります。もしMNIST digitsライブラリを使う場合、これは自動的にチェックされます。

訓練とテストのデータを作成したあとは、ネットワークをセットアップします。ここでは、ニューラルネットワークの作成と様々なパラメータの設定を機能として提供してくれるsynaptic.jsというライブラリを用います。まず最初に、必要な入力・出力ニューロンの数を決める必要があります。全ての画像サイズが28x28pxなので、ネットワークが入力として取るべきピクセルの数は28 x 28 = 784となります。数字は10個の分類のうちの1つに当てはめられるので、出力ニューロンの数は10となります。さらに、ネットワークは少なくとも1つの隠れ層を持つ必要があり、この例ではその中に100個のニューロンが含まれています。

以上で説明したネットワークをセットアップするコードが以下です。

const synaptic = require('synaptic');

const Layer = synaptic.Layer;
const Network = synaptic.Network;
const Trainer = synaptic.Trainer;

const inputLayer = new Layer(784);
const hiddenLayer = new Layer(100);
const outputLayer = new Layer(10);

inputLayer.project(hiddenLayer);
hiddenLayer.project(outputLayer);

const myNetwork = new Network({
    input: inputLayer,
    hidden: [hiddenLayer],
    output: outputLayer
});

訓練データセットでネットワークを訓練するために、synaptic.jsが提供するTrainerを利用できます。train()関数の引数は、訓練用データと、Trainerの設定のためのパラメータのリストです。

const trainer = new Trainer(myNetwork);
trainer.train(trainingSet, {
    rate: .2,
    iterations: 20,
    error: .1,
    shuffle: true,
    log: 1,
    cost: Trainer.cost.CROSS_ENTROPY
});

オプションとして、訓練における学習率となる’rate’をセットすることができます。また、’iterations’は、「何回繰り返した後に訓練結果を確定させるか」を定義します。’error’は訓練中に到達しうる最小誤差で、もし誤差がこの値にまで到達した場合には訓練を終了します。’shuffle’オプションをセットすることで、訓練データセットの順番をランダムにするかしないかを決められます。利用可能な全てのオプションに関するさらに詳しい情報はsynaptic.jsのドキュメンテーションからご覧になれます。

一般的な考えを習得するために、今回の例では最大繰り返し回数を20回にして、訓練が終了するまで何時間も待つ必要がないようにします。たった20回の繰り返しではネットワークを十分に訓練できないことに注意してください。よりよい結果を得るためには、この回数を増やして辛抱強く待つ必要があります。

訓練の進行状況を見たい場合は、’log’オプションを利用してネットワークの現在の誤差を書き出すことができます。誤差が小さくなるほどネットワークがよく訓練された状態になります。

error log

誤差は一貫して減少するのではなく、やや変動するということに気づくかもしれません。しかし、大局的には小さくなっていきます。もしそうならない場合は、学習率(‘rate’)を下げてみてください。

訓練が終わったあとは、テストデータセットを利用して、ネットワークによる分類がどれほどうまくいくかをチェックすることができます。これにはネットワークのactivate()関数を用いることができます。この関数のパラメータは分類すべき要素となります。結果を確認するために、結果を出力して期待される結果と比較できます。

console.log(myNetwork.activate(testSet[0].input));
console.log(testSet[0].output);

この例のネットワークではあまりいい結果は得られないことにご注意ください。訓練データセットの要素のうち50%程度しか正しく分類できません。これは、訓練データセットが小さいことと、訓練のための繰り返し回数が少なかったことに起因します。結果を向上させたければこの両者を増加させるべきです。注意:これにより、訓練にかかる時間はずっと長くなります!

一般的なMNISTの例の他にも、ニューラルネットワークが有用になる応用先はたくさんあります。ほかにsynaptic.jsを用いた面白い実験としては、ニューラルネットワークがT-Rexゲームを学習したT-Rex ML Playerがあります。

ニューラルネットワークを作れる他の似たようなJavaScriptライブラリを見てみたい場合、チェックしてみるべきプロジェクトには以下のようなものがあります。