2017年4月10日月曜日

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

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

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

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

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

crossentropy(t,o)=−(tlog(o)+(1t)log(1o))crossentropy(t,o)=−(tlog(o)+(1−t)log(1−o))

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

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

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

原因1の事象はsigmoid関数を使っている場合でも発生します。
sigmoid
関数はに大きくなれば01を計算することが可能です。
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など、自動的に学習してくれる
学習法を使ってみると良いと思います。

chainerMNISTexample(
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と同じです。見なおしてみてください。

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

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

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

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

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

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

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

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

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

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

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

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

2 / 100 #0
2 / 100.0 #0.02

Trouble3Validation Scoreが低い

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

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

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

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

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

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

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

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

原因1 モデル構築誤り

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

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

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

ライブラリによって異なりますが、regressionclassificationでは大体入力の方法が異なっています。
これに気づかずにやるとデータの形式にハマります。
時々、ConvolutionDense 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つのトラブルに失敗の経験は含まれると思います。
皆さんの失敗経験がこれで減りますように。
他に失敗経験があったらぜひ教えて下さい。

 

50年後の人口8808万人 厚労省、出生率は上方修正

 厚生労働省の国立社会保障・人口問題研究所は10日、長期的な日本の人口を予測する「将来推計人口」を公表した。2065年の人口は15年比3割減の8808万人と試算した。近年の30〜40歳代の出生率の改善を受け、5年前の前回推計から合計特殊出生率(1人の女性が生涯に産むとされる子どもの人数)の仮定を上方修正。人口減少のペースが緩やかになる見通しを示した。

 厚労省が同日の社会保障審議会人口部会で報告した。将来推計人口は国勢調査の結果を基に5年に1度改定している。

 15年の総人口は1億2709万人で、53年には1億人を割り込み9924万人に減る。15〜64歳の生産年齢人口の割合は足元の60.8%(7728万人)から50年後には51.4%(4529万人)に低下。逆に65歳以上の高齢者の割合は26.6%(3387万人)から38.4%(3381万人)に上昇する。

 一方で近年の30〜40歳代の出生率の実績が前回推計より上昇していることを踏まえ、長期の出生率を1.35から1.44に修正した。65年の人口は前回推計より672万人増え、1億人を下回る時期も5年遅れるとした。

 それでも少子高齢化の傾向は変わらない。50年後に現役世代1.2人で高齢者1人を支える構図は前回推計と同じ。社会保障制度の持続可能性が問われることになりそうだ。

Open sourcing Sonnet - a new library for constructing neural networks

It's now nearly a year since DeepMind made the decision to switch the entire research organisation to using TensorFlow (TF). It's proven to be a good choice - many of our models learn significantly faster, and the built-in features for distributed training have hugely simplified our code. Along the way, we found that the flexibility and adaptiveness of TF lends itself to building higher level frameworks for specific purposes, and we've written one for quickly building neural network modules with TF. We are actively developing this codebase, but what we have so far fits our research needs well, and we're excited to announce that today we are open sourcing it. We call this framework Sonnet.

Since its initial launch in November 2015, a diverse ecosystem of higher level libraries has sprung up around TensorFlow enabling common tasks to be accomplished quicker. Sonnet shares many similarities with some of these existing neural network libraries, but has some features specifically designed around our research requirements. The code releaseaccompanying our Learning to learn paper included a preliminary version of Sonnet, and other forthcoming code releases will be built on top of the full library we are releasing today.

Making Sonnet public allows other models created within DeepMind to be easily shared with the community, and we also hope that the community will use Sonnet to take their own research forwards. In recent months we've also open-sourced our flagship platform DeepMind Lab, and are currently working with Blizzard to develop an open source API that supports AI research in StarCraft II. There are many more releases to come, and they'll all be shared on our new Open Source page.

The library uses an object-oriented approach, similar to Torch/NN, allowing modules to be created which define the forward pass of some computation. Modules are 'called' with some input Tensors, which adds ops to the Graph and returns output Tensors. One of the design choices was to make sure the variable sharing is handled transparently by automatically reusing variables on subsequent calls to the same module.

Many models in the literature can naturally be considered as a hierarchy - e.g. a Differentiable Neural Computer contains a controller which might be an LSTM, which can be implemented as containing a standard Linear layer. We've found that writing code which explicitly represents submodules allows easy code reuse and quick experimentation - Sonnet promotes writing modules which declare other submodules internally, or are passed other modules at construction time.

A final technique we've found very useful is to allow certain modules to operate on arbitrarily nested groups of Tensors. Recurrent Neural Network states are often best represented as a collection of heterogeneous Tensors, and representing these as a flat list can be error prone. Sonnet provides utilities to deal with these arbitrary hierarchies, so that changing your experiment to use a different kind of RNN does not require tedious code changes. We've made changes to core TF as well to better support this use case.

Sonnet is designed specifically to work with TensorFlow, and as such does not prevent you from accessing the underlying details such as Tensors and variable_scopes. Models written in Sonnet can be freely mixed with raw TF code, and that in other high level libraries.

This is not a one-time release - we will regularly update the Github repository to match our in-house version. We've got lots of ideas for new features in the works, which will be made available when ready. We are very excited about contributions from the community. To find out more about Sonnet, please see our GitHub repository.