TensorFlow.js : チュートリアル : 訓練の最初の一歩 : 合成データに曲線をフィットさせる (翻訳/解説)
作成 : (株)クラスキャット セールスインフォメーション
日時 : 04/03/2018
* 本ページは、TensorFlow.js サイトの Tutorials – Training First Steps: Fitting a Curve to Synthetic Data を翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
入力データ
私達の合成データセットは x- と y-座標から成り、これはデカルト平面上でプロットした時に次のように見えます :
このデータは形式 y = ax3 + bx2 + cx + d の3次関数を使用して生成されました。
私達のタスクはこの関数の係数を学習することです : データに最善にフィットする a, b, c, そして d の値です。TensorFlow.js 演算を使用してそれらの値をどのように学習するのかを見てみましょう。
Step 1 : Variable をセットアップする
最初に、モデル訓練の各ステップにおけるこれらの値の現在の最善の推定を保持するために幾つか variable を作成しましょう。手始めに、これらの varilable の各々にランダム数を割り当てましょう :
const a = tf.variable(tf.scalar(Math.random())); const b = tf.variable(tf.scalar(Math.random())); const c = tf.variable(tf.scalar(Math.random())); const d = tf.variable(tf.scalar(Math.random()));
Step 2: モデルを構築する
TensorFlow.js で私達の多項式関数 y = ax3 + bx2 + cx + d を数学的演算のシリーズを連鎖することにより表わすことができます : 加算 (add), 乗算 (mul), そしてべき乗 (pow と square) です。
次のコードは入力として x を取り y を返す predict 関数を構築します :
function predict(x) { // y = a * x ^ 3 + b * x ^ 2 + c * x + d return tf.tidy(() => { return a.mul(x.pow(tf.scalar(3))) // a * x^3 .add(b.mul(x.square())) // + b * x ^ 2 .add(c.mul(x)) // + c .add(d); }); }
先に進んで私達の多項式関数を Step 1 で設定した a, b, c と d のためのランダム値を使用してプロットしましょう。プロットは多分このようなものに見えるでしょう :
ランダム値で開始しましたので、私達の関数はデータセットに対して非常に劣ったフィットになりがちです。モデルはまだ係数についてより良い値を学習しなければなりません。
Step 3: モデルを訓練する
私達の最後のステップは係数のために良い値を学習するためにモデルを訓練することです。私達のモデルを訓練するために、3つのことを定義する必要があります :
- 損失関数、これは与えられた多項式がデータにどのくらい上手くフィットするかを測定します。損失値が低くなればなるほど、多項式はデータにより良くフィットします。
- optimizer、これは損失関数の出力に基づいて係数値を更新するためのアルゴリズムを実装します。optimizer の目的は損失関数の出力値を最小化することです。
- 訓練ループ、これは損失を最小化するために optimizer を反復的に実行するでしょう。
損失関数を定義する
このチュートリアルのためには、私達の損失関数として平均二乗誤差 (MSE, mean squared error) を使用します。MSE は、データセットの各 x 値に対する実際の y 値と予測された y 値の間の差を二乗し、そして総ての結果の項の平均を取ることで計算されます。
TensorFlow.js では MSE 損失関数は次のように定義できます :
function loss(predictions, labels) { // Subtract our labels (actual values) from predictions, square the results, // and take the mean. const meanSquareError = predictions.sub(labels).square().mean(); return meanSquareError; }
Optimizer を定義する
私達の optimizer のためには、確率的勾配降下法 (SGD, Stochastic Gradient Descent) を使用します。SGD は、データセットのランダムポイントの 勾配 を取りそしてその値をモデル係数の値を増加させるか減少させるかを伝えるために使用することで動作します。
TensorFlow.js は SGD を遂行するために便利な関数を提供します、そのためこれら総ての数学的演算を貴方自身で遂行する心配する必要はありません。tf.train.sdg は入力として望ましい学習率を取り、そして SGDOptimizer オブジェクトを返します、これは損失関数の値を最適化するために呼び起こされます。
学習率は (モデルの) 予測を改良するときモデルの調整がどのくらい大きいかを制御します。
低い学習率は学習プロセスをよりゆっくりにします (良い係数を学習するためにはより多くの訓練反復が必要とされます)、その一方で高い学習率は学習をスピードアップしますがモデルが正しい値のまわりで振動する、常に過剰修正 (= overcorrecting) する結果になるかもしれません。
次のコードは SGD optimizer を 0.5 の学習率で構築します :
const learningRate = 0.5; const optimizer = tf.train.sgd(learningRate);
訓練ループを定義する
損失関数と optimizer を定義した今、私達は訓練ループを構築できます、これは損失 (MSE) を最小化して私達のモデル係数を洗練するために反復的に SGD を遂行します。私達のループがどのように見えるかがここにあります :
function train(xs, ys, numIterations = 75) { const learningRate = 0.5; const optimizer = tf.train.sgd(learningRate); for (let iter = 0; iter < numIterations; iter++) { optimizer.minimize(() => { const predsYs = predict(xs); return loss(predsYs, ys); }); } }
コードをより密接に見てみましょう、一歩ずつです。最初に、入力としてデータセットの x と y 値、そして指定された反復数を取る訓練関数を定義します :
function train(xs, ys, numIterations) { ... }
次に、前のセクションで議論したように学習率と SGD optimizer を定義します :
const learningRate = 0.5; const optimizer = tf.train.sgd(learningRate);
最後に、numIterations 訓練反復を実行する for ループをセットアップします。各反復では、optimizer 上の minimize を呼び起こし、ここが魔法が起こるところです :
for (let iter = 0; iter < numIterations; iter++) { optimizer.minimize(() => { const predsYs = predict(xs); return loss(predsYs, ys); }); }
minimize は2つのことを行なう関数を取ります :
- それは Step 2 で先に定義した predict モデル関数を使用して総ての x 値のために y 値 (predYs) を予測します。
- それは「損失関数を定義する」で先に定義した損失関数を使用してそれらの予測に対する平均二乗誤差損失を返します。
それから minimize はこの関数で使用される任意の変数 (ここでは、係数 a, b, c, と d) を戻り値 (損失) を最小化するために自動的に調整します。
訓練ループの実行後、a, b, c, と d は SGD の 75 反復後のモデルにより学習された係数値を含みます。
結果を見ます!
プログラムの実行がひとたび終了すれば、変数 a, b, c, と d の最終値を取り曲線をプロットするためにそれらを使用できます :
結果は係数のために元々はランダム値を使用してプロットした曲線よりも遥かに良い結果です。
以上