のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

Neural Networkでの失敗経験やアンチパターンを語る

Sponsored Links

皆さんこんにちは
お元気ですか。私は元気です。

今日は珍しくNeural Networkを使っていく上での失敗経験について語ります。
学習の時に案外、失敗するのですが、だいたい原因は決まっています。そう大体は・・・
ということで、今回は失敗の経験、アンチパターンのようなものを書こうと思います。

Trouble1:学習時にNanを叩き出す。

こんな経験よくあるのでは無いでしょうか?
なぜかよくわからないけれども学習していると気がついたらNanになっていることがあります。

原因1 cross-entropy誤差を使っている。

まずは、cross-entropy誤差関数を見てみましょう。
(今回はTheanoのbinary cross entropyを取り上げます)

{ \displaystyle
crossentropy(t,o) = -(t\cdot log(o) + (1 - t) \cdot log(1 - o))
}

実はこの数式、nanを出す要因があります。対数に着目してみます。
対数は0を入力するとnanが返ってきて、計算不可能な値となります。
何らかの原因で、対数に0が入力され、計算がおかしくなっています。

この場合はsigmoid関数やnp.log1pの関数を使い、関数に一工夫が必要になります。
しかし、sigmoid関数についても、nanが出力される場合があります。

原因2 結果が小さすぎて、0と認識される。

原因1の事象はsigmoid関数を使っている場合でも発生します。
sigmoid関数は∞に大きくなれば0や1を計算することが可能です。
Neural Networkの計算において、sigmoid関数はfloat32の場合において、小さすぎて0と算出されます。
これが原因になっていることがあります。

以下のコードを書いて検証してみました。

import numpy as np
def sigmoid(z):
	return 1/(1+np.exp(-z))

sigmoid(-500) #7.1245764067412845e-218
sigmoid(-500).astype(np.float32) #0.0

原因3 重みがあらぬ方向へ学習する。

回帰式を計算した時によくあることなのですが、
重みが凄い方向へ学習してそのままinfを叩き出し、nanになることがあります。

Trouble2:収束しない

作った関数が収束しない、これもよくあることです。
大体原因は決まっている気がします。

原因1 学習率が高すぎる

学習率が高過ぎると重みがぶれ、学習ができません。
あまりにも遅ければ上げてみる、もしくはAdamなど、自動的に学習してくれる
学習法を使ってみると良いと思います。

chainerのMNISTのexample(
chainer/train_mnist.py at master · pfnet/chainer · GitHub)
に対して学習方法をSGDにして、学習率1000000000.0とした場合の標準出力
正当率があがらず、誤差もnanが出続けています。

epoch 1
graph generated
train mean loss=nan, accuracy=0.0988166666403
test  mean loss=nan, accuracy=0.0979999999329
epoch 2
train mean loss=nan, accuracy=0.0987166666767
test  mean loss=nan, accuracy=0.0979999999329
epoch 3
train mean loss=nan, accuracy=0.0987166667202
test  mean loss=nan, accuracy=0.0979999999329

原因2 学習率が低すぎる

学習率が低すぎると、学習が遅くなります。
対策としては原因1と同じです。見なおしてみてください。

chainerのMNISTのexampleに対して学習方法をSGDにして、学習率0.0000001とした場合は以下になります。
縦軸が誤差、横軸がepoch数、で学習が殆ど進んでいないことが見てわかります。

f:id:tereka:20160310001616p:plain

対して学習率を0.1とした場合の誤差は以下のようになります。

f:id:tereka:20160310000439p:plain

個人的にこのパラメータは0.01周りを使っていればトラブルが少ないと考えています。

原因3 適切な誤差関数ではない

誤差関数がよくないといった失敗をやったことがあります。
基本的に誤差関数は、誤っている場合の誤差が大きければ、大きいほど、学習の進みがよくなります。
マルチラベルの場合最小二乗誤差ではなく、cross-entropyを使わなければ、収束がすごく遅いです。

因みにこれを私はcross-entropyと最小二乗誤差でやりました。

原因4 活性化関数を誤った

昔、回帰の問題を解いていました(100,300などを予測)が
盛大な誤差関数を出力するが、誤差落ちなかったことがありました。

モデルを調べた結果、なぜかsigmoid関数を使っていたことがありました。
全然減らないと思った時は最後の出力の活性化関数を見直すのも良いでしょう。

因みに負の値の予測が必要な時にReLuを使うのもよろしくありません。式を見れば一目瞭然ですが・・・

原因5 そもそも入力が誤っている

画像に変な処理をかけたことで、入力が誤っていることがあります。
例えばuint8に対してintの処理をすることで期待と異なる結果が返る場合があります。

2 / 100 #0
2 / 100.0 #0.02

Trouble3:Validation Scoreが低い

Neural Networkを作ったけれども精度が出ない、そんな失敗もあります。

原因1 過学習しているにも関わらず、気づかなかった。

Early Stoppingを実施しなかった時によく起こる問題だと思います。
Early Stoppingを実施しない場合は、一定の間は普通ですが、
あるときから、Validation Scoreが低下していきます。

原因2 与えるデータとラベルの1対1が誤っている

与えるデータとラベルを何らかの処理で取得している場合
1対1が間違っている可能性があります。

例えば、与えるデータとラベルをうっかりValidationの時に各々でシャッフルするなどあります。
あまりにも学習データと乖離しているときは、与えているデータが違っていないか疑ってみるのもよいでしょう。

Trouble4:正しい入力を与えたはずなのに、ライブラリから変なエラーを吐かれる場合

これは、どちらかというとライブラリ周りの話です。

原因1 モデル構築誤り

この場合はだいたい、入力誤りです。
連結している部分のfor文あたりが間違っている事が多いです。

例えば、連結が途切れているなどはこの場合に含まれるでしょう。

原因2 入力データのデータ形式誤り

ライブラリによって異なりますが、regressionとclassificationでは大体入力の方法が異なっています。
これに気づかずにやるとデータの形式にハマります。
時々、ConvolutionやDense Layer(Liner Layer)の対応マップが誤って出ることもあります。

Theano関連のライブラリだと以下の様な結果が出ることでしょう。

ValueError: Input dimension mis-match. (input[0].shape[1] = 2, input[1].shape[1] = 1)

Apply node that caused the error: Elemwise{sub,no_inplace}(Elemwise{Composite{tanh((i0 + i1))}}[(0, 0)].0, <TensorType(float64, matrix)>)
Inputs types: [TensorType(float64, matrix), TensorType(float64, matrix)]
Inputs shapes: [(50, 2), (50, 1)]
Inputs strides: [(16, 8), (8, 8)]

Python: keras shape mismatch error - Stack Overflowより

因みに大体のライブラリで回帰問題とクラス分類問題によって決まっており、
以下の入力をすれば、特段問題はないと思います。

[[1],[3],[4]] #regression
[1,2,3,4] #classification

まとめ

大体上記4つのトラブルに失敗の経験は含まれると思います。(私はそうでした)
皆さんの失敗経験がこれで減りますように。
他に失敗経験があったらぜひ教えて下さい。