Javascriptニューラルネットワーク

画像

この記事を書くアイデアは、昨年の夏、私がニューラルネットワークに関するBigData会議で講演を聞いたときに生まれました。 講師は聴衆に「ニューロン」、「トレーニングセット」、「モデルのトレーニング」という珍しい言葉を「振りかけ」ました...「何も理解できませんでした。今度はマネージャーに行きましょう」と思いました。 しかし最近、ニューラルネットワークのトピックが私の仕事に影響を与えたため、JavaScriptでこのツールを使用する方法を簡単な例で示すことにしました。



0から9までの数字の手動スペルを認識するニューラルネットワークを作成します。実際の例では数行かかります。 このコードは、以前にニューラルネットワークを扱ったことのないプログラマーにとっても明らかです。 どのように機能するかは、ブラウザで直接表示できます。





パーセプトロンとは何かをすでに知っている場合は、次の章を飛ばしてください。



かなりの理論



ニューラルネットワークは、人工知能の分野の研究、つまり、脳の低レベルの構造をシミュレートして、エラーを学習および修正する生物学的神経系の能力を再現する試みから生まれました。 最も単純なケースでは、相互接続されたいくつかのニューロンで構成されています。



数学的ニューロン



入力信号を結果の出力信号に変換する単純なマシン。

数学ニューロン回路

入力に到達する信号x1、x2、x3 ... xnは、線形に変換されます。 ニューロン本体に到達する力:w1x1、w2x2、w3x3 ... wnxn、ここでwiは対応する信号の重みです。 ニューロンはこれらの信号を合計し、関数f(x)を合計に適用して、結果の出力信号yを生成します。

シグモイド関数またはしきい値関数は、関数f(x)として最もよく使用されます。

シグモイド関数としきい値関数



しきい値関数は、0または1の2つの離散値のみを取ることができます。関数の値の変更は、指定されたしきい値Tを通過するときに発生します。



シグモイド-連続関数は、0から1の範囲で無限に多くの値を取ることができます。



UPD:コメントでは、 ReLUおよびMaxOut関数がよりモダンであると述べています



ニューラルネットワークのアーキテクチャは異なる場合があります。ニューラルネットワークの最も単純な実装の1つであるパーセプトロンを検討します。



パーセプトロンアーキテクチャ



パーセプトロンアーキテクチャ



入力ニューロンの層(情報が外部から来る場合)、出力ニューロンの層(結果を得ることができる場合)、およびそれらの間の一連のいわゆる隠れ層があります。 ニューロンは複数の層に配置できます。 ニューロン間の各接続には独自の重みWijがあります



入出力信号



着信ネットワーク層のニューロンに信号を適用する前に、それらを正規化する必要があります。 入力データの正規化は、すべての入力データが「整列」のプロセスを経るプロセスです。 間隔[0,1]または[-1,1]にキャストします。 正規化が実行されない場合、入力データはニューロンに追加の影響を与え、誤った判断につながります。 つまり、異なる注文の値をどのように比較できますか?



出力層のニューロンでは、クリーンな「1」または「0」もありません。これは正常です。 「1」または「0」を受け取ったと仮定する特定のしきい値があります。 結果の解釈については後で説明します。



「スタジオでの例、そうでなければ眠りに落ちる」



便宜上、nodejsとnpmをインストールすることをお勧めします。



Brain.jsライブラリを使用してネットワークを説明します。 記事の最後に、同様の方法で構成できる他のライブラリへのリンクも示します。 Brain.jsは、その速度と訓練されたモデルを維持する能力が好きでした。



ドキュメントからの例を試してみましょう-XOR関数エミュレータ:

var brain = require('brain.js'); var net = new brain.NeuralNetwork(); net.train([{input: [0, 0], output: [0]}, {input: [0, 1], output: [1]}, {input: [1, 0], output: [1]}, {input: [1, 1], output: [0]}]); var output = net.run([1, 0]); // [0.987] console.log(output);
      
      





サンプルが動作するように、すべてをsimple1.jsファイルに書き込み、 ブレインモジュールを配置して実行します

 npm install brain.js node simple1.js # [ 0.9331839217737243 ]
      
      







2つの入力ニューロンと1つの出力ニューロンがあり、brain.jsライブラリ自体が非表示層を構成し、適切と思われる数のニューロン(この例では3つのニューロン)をインストールします。



.trainメソッドに渡したものはトレーニングセットと呼ばれ、その各要素は、入力および出力プロパティを持つオブジェクトの配列(入力および出力パラメーターの配列)で構成されます。 データ自体はすでに必要な形式になっているため、着信データを正規化しませんでした。



注意:出力は[0.987]ではなく[0.9331 ...]です。 意味が少し異なる場合があります。 学習アルゴリズムは乱数を使用して重みを選択するため、これは正常です。



.runメソッド 、引数で指定された着信信号の配列に対するニューラルネットワークの応答を取得するために使用されます。



他の簡単な例は、 脳のドキュメントで見つけることができます。



数字を認識する



最初は、手書きの数字を同じサイズに縮小した画像を取得する必要があります。 この例では、 MNIST数字モジュールを使用します。これは、 0〜9の手書き数字の28x28pxの数千のバイナリイメージのセットです。

nmistトレーニングサンプル



元のMNISTデータベースには、トレーニング用の60,000のサンプルとテスト用の10,000のサンプルが含まれており、 LeCun Webサイトからダウンロードできます。 MNISTディジットの作成者は 、JavaScript言語のこれらの例の一部を利用可能にし、ライブラリは着信信号の正規化をすでに実行しています。 このモジュールを使用すると、トレーニングとテストサンプルを自動的に受け取ることができます。



MNISTディジットライブラリのクローンを作成する必要がありました。そこにはデータの混乱が少しあります。 元のデータベースから10,000個の例をリロードしたため、リポジトリからMNIST数字を使用する必要があります



ネットワーク構成



入力層では、10個のニューロンの出力で28x28 = 784個のニューロンが必要です。 非表示のbrain.jsレイヤーはそれ自体を構成します。 今後、明確にしましょう。392個のニューロンが存在します。 トレーニングサンプルはmnistモジュールによって生成されます



モデルを訓練します



mnistをインストールする

 npm install https://github.com/ApelSYN/mnist
      
      







すべての準備が整ったので、ネットワークをトレーニングします

 const brain = require('brain.js'); var net = new brain.NeuralNetwork(); const fs = require('fs'); const mnist = require('mnist'); const set = mnist.set(1000, 0); const trainingSet = set.training; net.train(trainingSet, { errorThresh: 0.005, // error threshold to reach iterations: 20000, // maximum training iterations log: true, // console.log() progress periodically logPeriod: 1, // number of iterations between logging learningRate: 0.3 // learning rate } ); let wstream = fs.createWriteStream('./data/mnistTrain.json'); wstream.write(JSON.stringify(net.toJSON(),null,2)); wstream.end(); console.log('MNIST dataset with Brain.js train done.')
      
      





ネットワークを作成し、トレーニングセットの要素1000個を取得し、ネットワークトレーニングを実行する.trainメソッドを呼び出します-すべてをファイル './data/mnistTrain.json'に保存します(フォルダー "./data"を作成することを忘れないでください)。



すべてが正しく行われた場合、およそ次の結果が得られます。

 [root@HomeWebServer nn]# node train.js iterations: 0 training error: 0.060402555338691676 iterations: 1 training error: 0.02802436102035996 iterations: 2 training error: 0.020358600820106914 iterations: 3 training error: 0.0159533285799183 iterations: 4 training error: 0.012557029942873513 iterations: 5 training error: 0.010245175822114688 iterations: 6 training error: 0.008218147206099617 iterations: 7 training error: 0.006798613211310184 iterations: 8 training error: 0.005629051609641436 iterations: 9 training error: 0.004910207736789503 MNIST dataset with Brain.js train done.
      
      







すべて認識できる



かなりの量のコードを記述する必要があります-そして、認識システムは準備ができています!

 const brain = require('brain.js'), mnist = require('mnist'); var net = new brain.NeuralNetwork(); const set = mnist.set(0, 1); const testSet = set.test; net.fromJSON(require('./data/mnistTrain')); var output = net.run(testSet[0].input); console.log(testSet[0].output); console.log(output);
      
      







10,000レコードのサンプルからランダムなテスト例を1つ取得し、以前にトレーニングしたモデルをロードし、テストレコードをネットワーク入力に転送して、正しく認識されたかどうかを確認します。



ここに実行の例があります

 [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ] [ 0.0002863506627761867, 0.00002389940760904011, 0.00039954062883041345, 0.9910109896013567, 7.562879202664903e-7, 0.0038756598319246837, 0.000016752919557362786, 0.0007205981595354964, 0.13699517762991756, 0.0011053963693377692 ]
      
      





この例では、デジタル化された3つが着信ニューロンのネットワークに到着し(最初の配列が完全な答えです)、ネットワーク出力で要素の配列を得ました。そのうちの1つは1つ(0.9910109896013567)に近くこれも3番目のビットです。 7.56 ... -7度までの4番目のビットに注意してください。これは、JavaScriptの浮動小数点数の表記法のような形式です。



まあ、認識は正しかった。 おめでとうございます、私たちのネットワークは獲得しました!



私たちは、結果をsoftmax関数と「組み合わせ」ました。これは、ある機械学習の例から取ったものです。

 function softmax(output) { var maximum = output.reduce(function(p,c) { return p>c ? p : c; }); var nominators = output.map(function(e) { return Math.exp(e - maximum); }); var denominator = nominators.reduce(function (p, c) { return p + c; }); var softmax = nominators.map(function(e) { return e / denominator; }); var maxIndex = 0; softmax.reduce(function(p,c,i){if(p<c) {maxIndex=i; return c;} else return p;}); var result = []; for (var i=0; i<output.length; i++) { if (i==maxIndex) result.push(1); else result.push(0); } return result; }
      
      







関数をコードの先頭に配置し、最後の行を次のように置き換えることができます

 console.log(softmax(output));
      
      







すべての友人-すべてが美しく機能するようになりました:

 [root@HomeWebServer nn]# node simpleRecognize.js [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ] [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ] [root@HomeWebServer nn]# node simpleRecognize.js [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ] [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ] [root@HomeWebServer nn]# node simpleRecognize.js [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ] [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ]
      
      







ネットワークが間違った結果を返す場合があります(小さなサンプルを取得し、それほど厳しくないエラーを設定しました)。



そして、あなたが書いた番号を認識する方法は?



もちろん、詐欺はありませんが、それでも何が起こったのか自分で強さをチェックしたいです。



HTML5 Canvasと同じbrain.jsを保存されたモデルで使用して、ブラウザーに認識を実装することに成功し、レンダリング用のコードの一部とインターネット上のインターフェイスデザインを借りました。 ライブで試すことができます。 モバイルデバイスに指で描くことができます。



関連リンク







UPD:コメントおよび個人的な通信からのJavaScriptのライブ例1、2の代替実装。



All Articles