DNNを実装してみた ( 2 )
前回の続きで、転移学習を扱っていこうと思います。
ちなみに↓が前回の記事です。
前回の記事をみてもらえればわかると思いますが、前回はMNISTデータの0〜4までの数字を使ってDNNを作成しました。今回は、そのモデルを再利用して、残りの5〜9の数字についての学習をしたいと思います。
前問の全ての訓練済み隠れ層(5層)を再利用
隠れ層を全て凍結し、新しいソフトマックス出力層を追加
プレトレーニング済み層の再利用
reset_graph() restore_saver = tf.train.import_meta_graph("./my_best_mnist_model_0_to_4.meta") X = tf.get_default_graph().get_tensor_by_name("X:0") y = tf.get_default_graph().get_tensor_by_name("y:0") loss = tf.get_default_graph().get_tensor_by_name("loss:0") Y_proba = tf.get_default_graph().get_tensor_by_name("Y_proba:0") logits = Y_proba.op.inputs[0] accuracy = tf.get_default_graph().get_tensor_by_name("accuracy:0")
(./my_best_mnist_model_0_to_4.metaに前回作成したモデルが保存されています。)
隠れ層の凍結
重みを凍結するとは、下位層の重みが固定することであり、無駄な訓練をしなくて済むため上位層の重みの訓練が簡単になる。
tf.get_collection()
関数- 下位層の変数を除外して、オプティマイザに訓練する変数のリストを渡す
learning_rate = 0.01 output_layer_vars = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES, scope="logits") # logitsの下位層は除外 optimizer = tf.train.AdamOptimizer(learning_rate, name="Adam2") training_op = optimizer.minimize(loss, var_list=output_layer_vars) correct = tf.nn.in_top_k(logits, y, 1) accuracy = tf.reduce_mean(tf.cast(correct, tf.float32), name="accuracy") init = tf.global_variables_initializer() five_frozen_saver = tf.train.Saver()
数字1つあたり100個の画像で5から9までの数字について新しいDNNを訓練
学習
n_epochs = 1000 batch_size = 20 max_checks_without_progress = 20 checks_without_progress = 0 best_loss = np.infty with tf.Session() as sess: init.run() restore_saver.restore(sess, "./my_best_mnist_model_0_to_4") for epoch in range(n_epochs): rnd_idx = np.random.permutation(len(X_train2)) for rnd_indices in np.array_split(rnd_idx, len(X_train2) // batch_size): X_batch, y_batch = X_train2[rnd_indices], y_train2[rnd_indices] sess.run(training_op, feed_dict={X: X_batch, y: y_batch}) loss_val, acc_val = sess.run([loss, accuracy], feed_dict={X: X_valid2, y: y_valid2}) if loss_val < best_loss: save_path = five_frozen_saver.save(sess, "./my_mnist_model_5_to_9_five_frozen") best_loss = loss_val checks_without_progress = 0 else: checks_without_progress += 1 if checks_without_progress > max_checks_without_progress: print("Early stopping!") break print("{}\tValidation loss: {:.6f}\tBest loss: {:.6f}\tAccuracy: {:.2f}%".format( epoch, loss_val, best_loss, acc_val * 100)) with tf.Session() as sess: five_frozen_saver.restore(sess, "./my_mnist_model_5_to_9_five_frozen") acc_test = accuracy.eval(feed_dict={X: X_test2, y: y_test2}) print("Final test accuracy: {:.2f}%".format(acc_test * 100))
結果
Total training time: 2.2s Final test accuracy: 74.04%
重みの計算の部分を全て凍結しているから、学習速度はめちゃくちゃはやくなっている。各画像100枚ずつの学習だからなんとも言えないが、それにしても74%は低い。まあ、前回作成したモデルの隠れ層をそのまま使ってるから当たり前なんだけど...
再利用する隠れ層の数を変更(5 -> 4)
これ以降は上記のコードを少しいじればできるので、コードは省略します。前回と同じようにGitHubに置いてあるので、興味のある方は見てください。
Final test accuracy: 75.77%
まあ、さっきよりはほんの少し良くなったくらい。
上位2層の凍結を解除(隠れ層1,2のみを凍結)
Final test accuracy: 81.92%
少ない学習データに関わらず、そこそこの精度は得られてるけど前回ほど高い数値はでないなあ
最後に
今回はそこまで良い結果が得られなかったが、転移学習は深層学習に必須の知識なので、ぜひ使えるようになりたい。転移学習ができるようになれば、わざわざ下位層の重みの計算をしなくて済み、実行時間が大幅に短縮できるので非常に便利だと思った。
scikit-learnとTensorFlowによる実践機械学習
- 作者: Aurélien Géron,下田倫大,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/04/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
DNNを実装してみた ( 1 )
さて今回は、Google Colaboratory(ちなみにGoogle Colaboratoryは無料で深層学習に必須なGPUやTPUを使用できるので超便利)を使って、前回記事で紹介した実践的なガイドに沿ったDNNを構築してみました。
また今回はデフォルト通り、MNISTデータに対してモデルを構築しました。
今回使用したコードはgithubに置いてあります。
基本的なDNNモデル
訓練するのは0から4までの数字とする (5から9は後でやる転移学習を使って訓練する)
- モデル
- 実行
- エポック数 : 1000
- バッチサイズ : 20
結果
0 Validation loss: 0.111523 Best loss: 0.111523 Accuracy: 97.85% 1 Validation loss: 0.259246 Best loss: 0.111523 Accuracy: 95.00% 2 Validation loss: 0.116553 Best loss: 0.111523 Accuracy: 97.46% 3 Validation loss: 0.439698 Best loss: 0.111523 Accuracy: 96.72% 4 Validation loss: 0.124247 Best loss: 0.111523 Accuracy: 96.48% 5 Validation loss: 0.556761 Best loss: 0.111523 Accuracy: 78.93% 6 Validation loss: 0.488352 Best loss: 0.111523 Accuracy: 87.88% 7 Validation loss: 0.327603 Best loss: 0.111523 Accuracy: 96.56% 8 Validation loss: 0.153663 Best loss: 0.111523 Accuracy: 97.97% 9 Validation loss: 0.481955 Best loss: 0.111523 Accuracy: 84.17% 10 Validation loss: 0.657136 Best loss: 0.111523 Accuracy: 77.95% 11 Validation loss: 0.583860 Best loss: 0.111523 Accuracy: 80.02% 12 Validation loss: 0.384622 Best loss: 0.111523 Accuracy: 91.67% 13 Validation loss: 200.980087 Best loss: 0.111523 Accuracy: 96.56% 14 Validation loss: 0.147616 Best loss: 0.111523 Accuracy: 97.62% 15 Validation loss: 0.134488 Best loss: 0.111523 Accuracy: 97.62% 16 Validation loss: 0.470304 Best loss: 0.111523 Accuracy: 84.64% 17 Validation loss: 0.204020 Best loss: 0.111523 Accuracy: 97.26% 18 Validation loss: 0.774719 Best loss: 0.111523 Accuracy: 95.35% 19 Validation loss: 0.299993 Best loss: 0.111523 Accuracy: 92.57% 20 Validation loss: 0.147901 Best loss: 0.111523 Accuracy: 96.56% Early stopping! Total training time: 49.6s INFO:tensorflow:Restoring parameters from ./my_mnist_model_0_to_4.ckpt Final test accuracy: 97.94%
以上より、最終的な正確度としては
97.94%
しかも1分もかからずにここまでの精度を出せるってことに感動(やっぱGPU最強)
以下を追加したバージョン
- 交差検証
- バッチ正規化
- ドロップアウト
ここからは途中経過も載せると長くなりすぎてしまうので、最終的な結果のみにします。
交差検証のみ
- 交差検証したパラメータ
- "n_neurons": [10, 30, 50, 70, 90, 100, 120, 140, 160],
- "batch_size": [10, 50, 100, 500],
- "learning_rate": [0.01, 0.02, 0.05, 0.1],
- "activation": [tf.nn.relu, tf.nn.elu, leaky_relu(alpha=0.01), leaky_relu(alpha=0.1)]
- 実行
- イテレーション数 : 50
- エポック数 : 1000
結果
交差検証結果
{'activation': <function tensorflow.python.ops.gen_nn_ops.relu(features, name=None)>, 'batch_size': 100, 'dropout_rate': 0.2, 'learning_rate': 0.01, 'n_neurons': 160}
正確度
0.9904650710254913
交差検証すると時間が結構かかるけど、その分正確度も相当上がっている。モデル自体は変わっていないのに、パラメータを変えただけでここまで精度が上がるって考えると交差検証はやっぱり重要。
以下、基本的に交差検証したパラメータと実行の回数は同じとする。
バッチ正規化 + 交差検証
- +α
- "batch_norm_momentum": [0.9, 0.95, 0.98, 0.99, 0.999]
結果
交差検証結果
{'activation': <function tensorflow.python.ops.gen_nn_ops.relu(features, name=None)>, 'batch_norm_momentum': 0.99, 'batch_size': 50, 'learning_rate': 0.01, 'n_neurons': 70}
正確度
0.9937731076084841
過学習を抑制しているにも関わらず、正確度が非常に高いなあ。 しかも次のドロップアウトと比べて実行時間がはやい。
ドロップアウト + 交差検証
- +α
- "dropout_rate": [0.2, 0.3, 0.4, 0.5, 0.6]
結果
交差検証結果
{'activation': <function tensorflow.python.ops.gen_nn_ops.relu(features, name=None)>, 'batch_size': 100, 'dropout_rate': 0.2, 'learning_rate': 0.01, 'n_neurons': 160}
正確度
0.9926055652850749
今回だとドロップアウトの方がバッチ正規化と比べて正確度は少しだけ落ちてる... (だからと言ってドロップアウトが劣っていると決めつけるのは早いけど)
次回
今回はMNISTデータの0から4までの数字を使ってDNNを構築したが、次回は今回構築したDNNを再利用して5から9までの数字についてのDNNを構築する転移学習を扱います。
DNNの基礎知識についてのまとめ
深層ニューラルネットの訓練
今回は、深層ニューラルネットワーク(以下DNN)の問題点を挙げ、その解決法を示します。
勾配消失/爆発問題
勾配消失
勾配降下法による更新では下位層の接続の重みがほとんど変わらず、訓練が良い解に収束しなくなること
勾配爆発
逆に勾配がどんどん急になり、多くの層の重みが更新によって膨大になり、アルゴリズムが発散してしまうこと → 層によって学習速度が大幅に変わってしまう
- 理由
Xavierの初期値とHeの初期値
信号を適切に流すために、各層の入出力の分散を等しくする必要があり、層を通過する前後の勾配の分散も等しい必要がある
活性化関数
- ReLU(ランプ)関数
深層ニューラルネットで主に使用される
バッチ正規化
訓練中に前の層のパラメータの変化に伴い、各層の入力分布が変化する問題(内部共変量シフト)に対処するためのテクニック
各層の活性化関数を実行する直前に、入力の0を中心とするセンタリングと正規化を行い、層ごとに二つの新しいパラメータをそれぞれ使ってスケーリングとシフトを行う → 各層の入力の最適なスケールと平均をモデルに学習させる
勾配クリッピング
バックプロパゲーションステップでは勾配をクリッピングし、一定の閾値を超えないようにするテクニック ただ、一般にバッチ正規化の方が主流
プレトレーニング済み層の再利用
転移学習
既存のNNを探し、そのネットワークの下位層を再利用すること → 時間の大幅短縮、訓練データを減らせる
- 事前に訓練したモデルを復元するためのSaver
- 新しいモデルを保存するための別のSaver
model zoo
多くの人が様々なタスクのために機械学習モデルを訓練し、プレトレーニング済みのモデルを一般開放している
オプティマイザの高速化
Momentum最適化
それまでの勾配を重視
- ハイパーパラメータ β 0~1の値をとる(一般に0.9を用いる)
NAG(Nesterov Accelerated Gradient)
Momentum最適化の変種 元の位置の勾配ではなく、慣性の方向に少し進んだところで測定された勾配を使うことで、正確性が増した
AdaGrad
最も急な次元に沿って勾配ベクトルをスケールダウンする → パラメータの要素ごとに適応的に学習係数を調整しながら学習を行う手法 - 適応学習率(adaptive learning rate) 傾斜が急な次元では傾斜が緩やかな次元よりも早く学習率を下げる
ただし、学習率が大きく下がるため、全体の最適値に到達する前にアルゴリズムが止まってしまう
RMSProp
- Adaの問題点を解決 過去の全ての勾配を均一に加算していくのではなく、過去の勾配を徐々に忘れて、新しい勾配の情報が反映されるように加算 → 指数平均移動 : 指数関数的に過去の勾配のスケールを減少させる
Adam(adaptive momentum estimation)
- MomentumとRMSPropのアイデアの融合
- Momentum 過去の勾配の指数関数的減衰平均を管理
- RMSProp 過去の勾配の二乗の指数関数的減衰平均を管理
- ハイパーパラメータ
- 慣性減衰ハイパーパラメータ β1 : 0.9
- スケーリング減衰ハイパーパラメータ β2 : 0.999
学習スケジュール
高い学習率でスタートし、コスト低減のペースが下がったら学習率を下げる
- 部分ごとに一定の学習率をあらかじめ決めておく
- 性能によるスケジューリング
- 指数関数的スケジューリング
- 累乗スケジューリング
正則化による過学習の防止
早期打ち切り
検証セットに対する性能が落ち始めたところで訓練を中止する 通常、他の正則化手法と組み合わせる
l1, l2正則化
重みに制限を加える → コスト関数に正則化項を追加
ドロップアウト
DNNで最もよく使われている正則化テクニック(ただし、収束は大幅に遅くなる)
ニューロンをランダムに消去しながら学習する手法
入出力層と任意の隠れ層にtf.layers.dropout()
関数を適用
データ拡張
既存の訓練インスタンスから新しい訓練インスタンスを生成して人工的に訓練セットを膨らませる
実践的なガイドライン
今回は以下の書籍のDNNについてまとめました。次回はこの実践的なガイドラインに沿って実際にプログラムを動かしてみようと思います。
scikit-learnとTensorFlowによる実践機械学習
- 作者: Aurélien Géron,下田倫大,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/04/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
今更ながらブログ始めました
大学三年生がほぼ終わり、今更ながら自分のアウトプットの意味もかねてブログを始めました。
今までさんざんアウトプットは大切だと周りの人に言われたにも関わらず、なかなか実行するに至りませんでした… (ただめんどくさがってただけです)
主に扱う内容としては、最近流行りまくっている深層学習(deep learning)とたまに機械学習や画像処理系もやれたらいいなあと思っています。
ちなみにここ最近オライリー・ジャパンから出版されている以下の二つの書籍を読み始めたところなので、これらについて取り扱うことが多くなると思います。
- scikit-learnとTensorFlowによる実践機械学習
- ゼロから作るDeepLearning
scikit-learnとTensorFlowによる実践機械学習
- 作者: Aurélien Géron,下田倫大,長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2018/04/26
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- 作者: 斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/09/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (18件) を見る