Edited at

Tensorflow + Keras で定義域外の数値が返ってきた件

More than 1 year has passed since last update.


注意

解決はしてません。が、こういう事もあり得るんだという認識の情報共有です。


学習中にどうしても解決できないnanがでた

こんなネットワークを学習してました。(かなり簡略化しています。)

from keras.models import Model

from keras.layers import Dense, Activation, Input
import keras.backend as K

inputs = Input(shape=(5, ))
mid_layer = Dense(2)(inputs)
tanh = Activation('tanh')(mid_layer)
outputs = K.sqrt(0.5 * (1.0 + tanh) + K.epsilon())
model = Model(inputs, outputs)

ミソになるのはoutputsの行です。計算の内容はともかく、-1.0 < tanh(x) < 1.0が満たされるのであれば0.0 < 0.5 * (1.0 + tanh) < 1.0になるのは自明かと思います。K.epsilon() を足しているのは $\sqrt x$ が原点で学習できないため微小量を足して避けていました。( https://qiita.com/Mco7777/items/2bae29e6e0793f9c376b#sqrt

ところがこれでも学習中、しかも学習がかなり進んだ時に時折計算結果にnanが紛れ込み相当悩みました。

結論、nanが出た直前のモデルを保存しておいて1つずつ追った所上記コードのtanhの出力がなんと-1.0000002となっていました。Pythonで小数を扱う時に厳密に一致しない事は知っていますが、それはもう少し小さいオーダー(少なくともK.epsilon() = 1e-7よりは誤差の範囲)での話だと思います。

さらに直前のレイヤーの出力をnumpyのtanhに放り込んでみましたがやはりどれだけ大きくても、極端な話np.tanh(np.inf)でも1.0なのでTensorflowのtanhの計算ロジックに定義域外の数値が入り込むロジックが僅かながらの可能性ながらあるんだと思います。

※ tfdbgを使おうとも思ったのですが、ネットワークが大きくデバッグモードだと1エポックの学習にとんでもなく時間がかかってしまった事と、Tensorの名前が~~~/Adam/Add_2/~~~~~~/Adam/add_2/~~~という大文字小文字が食い違ってNot Foundみたいな謎エラーが発生したので諦めました… 後者についてはtfdbgのソースコードを追ってログ等出して調べてみたのですが、最小の構成での再現方法がどうしても分からなかったので(無駄に大きくなったモデルでは出たが、構成は同じはずのユニット数が小さいモデルだと発生させられなかった)、詳しい方いたら教えてもらえると嬉しいです。


考察

とにもかくにも、こういう可能性があると「知っている」か「知っていない」かで発見までの時間は大きく変わってくると思うので、頭の片隅に置いておくといつか役に立つかもしれません。私は1ヶ月以上悩みました。(本業で書いているコードではないという事もありますが)

原因も調べようと思ったのですが、結果的に調べきれませんでした。Tensorflowのソースコードでは活性化関数の処理が gen_math_ops とかいうgit上には存在しないモジュールに投げられています。ローカルにインストールしたディレクトリの中を探すと定義しているファイルが存在しているので、インストール時に自動生成されるような仕組みになっているんだと思うのですが、それのせいで正直tanhの計算がどういうロジックになっているのかが追いきれませんでした。

多分generateされる元となるソースコードがあると思うのですがもし知っている方いたら教えてください。