アラカン"BOKU"のITな日常

文系システムエンジニアの”BOKU”が勉強したこと、経験したこと、日々思うことを書いてます。

全結合層(Affine)と活性化関数(Sigmoid、Tanh等)の加工結果をモニタする/使い方40:Neural Network Console

SONYのNeural Network Consoleの少し裏技っぽい使い方。

レイヤーリファレンスに以下のような記述があります。

ImageAugmentationの効果の手軽な確認方法として、画像Input→ImageAugmentation→SquaredErrorだけのネットワークを用いた上でMax Epoch 0として学習し、評価実行ボタンにより加工結果をモニタするという方法があります。

 つまり、SquaredError を使い、Max Epoch 0 で学習・評価実行すると、その直前のレイヤーまでの加工結果をモニタすることができるということです。

f:id:arakan_no_boku:20180819142726j:plain

f:id:arakan_no_boku:20180819142814j:plain

 

これは、別にImageAugmentationだけの話ではなく、他のレイヤーでも使えます。

これは実に面白くて。

自分もよく、数式や言葉の説明だけでイメージがつかみづらい時に「勉強を兼ねて」これで遊んでます。

今回は、基本的なレイヤーでやってみたいと思います。

 

Inputは画像データではなくテキストデータにします

 

Inputデータは、1.0~0.0 の間でランダムに発生させた実数を256列並べたものにします。

それをExcelのグラフで表示して視覚的に確認できるようにしてます。

元データはこれです。

f:id:arakan_no_boku:20180819143229j:plain

あと、学習・評価実行した結果は、プロジェクトファイル(sdcprojファイル)を保存したフォルダのプロジェクト名.filesフォルダの下の評価実行日時から生成された名前のフォルダ(以下図の赤枠のような)の下にできる「output.csv」に出力されてます。

f:id:arakan_no_boku:20180819143827j:plain

それをEXCELにはりつけて、グラフにしてます。

 

まずはAffine(全結合層)を通したらどんな感じか

 

Affine層だけを通した加工結果を見てみたいと思います。

f:id:arakan_no_boku:20180819152625j:plain

初期値はデフォルトの「NormalAffineGlorot」です。

初期値については以下の記事で書いてますので、繰り返しませんので、興味のある方は後で見てもらえると嬉しいです。

arakan-pgm-ai.hatenablog.com

ちなみに「Kaiming He提案の」と書いてある方(Heの初期値)は、以下の標準偏差をつかいます。

$$ \sqrt  \frac  2 n  $$

 

もうひとつの「Xavier Glorot提案の」と書いてある方(Xavierの初期値)は、以下の標準偏差を使います。

$$ \sqrt  \frac  1 n  $$

一応、ご参考までに。

どちらにせよポイントは初期値の元には「平均0.0 分散1.0であるガウス関数」なので、Affineの中で掛け合わされる重みには「マイナスの実数」もあるということです。

なので、元データに負の数がなくても、Affineを通した途端に、マイナスを含んだ値に加工される・・ということなんですね。

結果はこうなります。

f:id:arakan_no_boku:20180819152703j:plain

 

こいつに活性化関数を通した結果を見てみる

 

活性化関数には色々と種類があります。

代表的なのが「Tanh」「Sigmoid」「ReLU」です。

ネットワークを組む時、このどれを使うと適切なんだろうと、慣れないうちは結構悩みました。

これも、加工結果を視覚化すると特徴がはっきりして、判断の助けになります。

 

まずはTanh(ハイポリックタンジェント

 

こいつは「-1.0 から 1.0の範囲に収める」活性化関数です。

f:id:arakan_no_boku:20180819153637j:plain

レイヤーリファレンスの図だとこんな感じ。

f:id:arakan_no_boku:20180819153410j:plain

だから、今回のようにAffineを通した結果がすでに-1.0~1.0に収まっているようなデータだと、それほど変化しません。

f:id:arakan_no_boku:20180819153541j:plain

 

今度はSigmoidに入れ替えてみます

 

Sigmoidは「0.0~1.0の間に収める」ように計算します。

f:id:arakan_no_boku:20180819153858j:plain

レイヤーリファレンスだとこんな感じ。

f:id:arakan_no_boku:20180819154001j:plain

マイナスの数字がなくなるわけですから、Affineの変換結果からは劇的に変わります。

f:id:arakan_no_boku:20180819154111j:plain

なんか、Inputの状態(非負)に戻して、なだらかにした感じです。

 

次はReLUでやってみよう

 

ReLUは、「マイナスなら出力しない=0、プラスはそのまま出力する」です。

f:id:arakan_no_boku:20180819154905j:plain

レイヤーリファレンスだとこう。

f:id:arakan_no_boku:20180819154537j:plain

予想してみると。

Affineの出力でマイナスの部分が間引かれた感じになるだけになりそうです。

実際がこう。

f:id:arakan_no_boku:20180819154927j:plain

ほぼ予想通りのグラフですね(笑)

 

やっぱり、視覚で確認できるとわかりやすい

 

この方法は、加工結果を視覚でつかめるのが面白いところです。

ネットワークを考える時、次の層につながる出力がこんな感じの形になっているはずだから・・とイメージしながら考えるのと、何のイメージもなく、ただ闇雲にレイヤーを重ねていくのとでは雲泥の差があります。

自分の作成した元データがどのような形なのか?

次の層に伝える活性化関数のアウトプットはどのような形で渡すと、速く収束して、精度をあげられそうか・・とか、形をイメージして仮説をたてながらやってみると、結果を見た時の面白さが違います。

おお!予想通り!なのか。

予想と全然違う!なのか。

どっちにしても、同じ勉強でやるにしても、楽しいほうがいいですから。

途中まで、SquaredError とMax Epoch 0 を使って、Outputの形を確認しながら、ネットワークの検討をするというのは、たまにやってみたらいいんじゃないのかな?

そう自分は思ってます。

ではでは。