"BOKU"のITな日常

テクノロジー以外にも、日常には、面白そうな”イット”がつまってるんだな

活性化関数「Tanh」「Sigmoid」「ReLu」の仕組みと使い分けを数式なしで整理する/ディープラーニング

f:id:arakan_no_boku:20190330090249j:plain

活性化関数は、Neural Network Consoleだと「Activationレイヤー」と呼ばれてます。

今回はこいつをざっくり整理してみます。

いっぱい種類があるので、今回はその中の代表的な3つ。

にしぼってとりあげます。

本当は「SoftMax」も重要ですけど、少し用途が違うので、今回はとりあげません。

さて。

Neural Network Consoleのレイヤーリファレンスの「Activationレイヤー」をみます。

まずは、それぞれのレイヤーリファレンスの記述です。

Tanh

入力値のTanhによる処理結果を出力します。

o=tanh(i) (oは出力、iは入力を示す)

Sigmoid

入力値のSigmoidによる処理結果を出力します。

確率など0.0~1.0の出力値を得たい場合に使用します。

o=sigmoid(i) (oは出力、iは入力を示す)

ReLU

入力値のReLU(Rectified Linear Unit)による処理結果を出力します。

o=max(0, i) (oは出力、iは入力を示す)

この時点で「ああ、なるほど。」思った人は、これ以上読む必要はありません。

処理結果を出力すると、どんな良いことがあるのか?さっぱりわからん。

そう思った人だけ続きをどうぞ(笑) 

 

活性化関数の役割とは、ざっくりと言うなら、

特徴の学習をしやすくするために、データを整える。

です。

その整え方のアプローチで、大きく2つに分かれます。

  • 計算結果の単位をそろえて比較しやすくする。
  • 特徴を際立たせることで比較しやすくする。

がそうです。

 

コンピュータのデータって基本数字の塊です。

数字を比較して、これは同じとか似ている・・と判断する一番簡単な方法は、基準をそろえることですが、データとウェイトを掛け合わせて計算した結果が、そんなに都合よく一定の範囲におさまっているわけがない。

だから、活性化関数で一定範囲の中に納まるよう整理し、後工程で特徴を計算しやすくしてあげる=計算結果の単位をそろえて比較しやすくする・・働きです。

この働きをするのが、「Tanh(ハイポリックタンジェント)」と「Sigmoid(シグモイド)」です。

その2つの違いは、マッピングする範囲です。

ということになります。

 

もうひとつのアプローチは、特徴を際立たせるように加工することです。

余分な情報(ノイズ)が多いほど、特徴がつかみずらくなるので、ある程度割り切って基準を決めて、余分なものは捨てていくことで、後工程で特徴をつかむ計算をやりやすくする=特徴を際立たせることで比較しやすくする・・アプローチです。

それが「ReLu」です。

ReLuは「0より大きければそのまま、0より小さければ0に置き換えて」出力します。

つまり、マイナスの数字をノイズとみなして切り捨てることで、より特徴をつかみやすくする効果を期待するものなわけです。

 

何をしているか?がわかると、何となく、どんなデータに、どの活性化関数がむくのかな?というのはイメージできるはずです。

例えば「ReLu」です。

これが「画像データ」と相性が良いのだろうというのは想像しやすいです。

何故か。

画像のデータを読み込んだ時の型は「UINT(Unsigned INT)」。

つまり、マイナスのないデータだからです。

画像を処理してマイナスのデータになったものは「ノイズ」だから切り捨てる・・というのは理にかなってます。

対して、マイナスのデータがはいっているのが当たり前のものもあります。

何かの測定値だったりとかですね。

そういうもののマイナスを切り落とすと無茶苦茶になる可能性があるので、「Sigmoid」とか「Tanh」を使った方がいいかな・・とか。

そういうことが予測できるわけです。

他にも文章データとかいろんなデータがありますし、ひとつのモデルの中で、ReluとSigmoidとTanhをかを混在して使う場合もあります。

それでも、訳もわからず適当に使ってみる・・よりは、どういうことをするのかのイメージをもって、このあたりではこういうデータになっているはずだから、これを使おう・・的に考えられると、ちょっとだけ楽しくはなります。 

 

上記のことを頭にいれて、リファレンスマニュアルのグラフを見ると、よりイメージしやすいです。

Tanh

-1.0 ~ 1.0 の範囲の数値に整えます。

f:id:arakan_no_boku:20181104001705j:plain

Sigmoid

0.0 ~ 1.0 の範囲の数値に整えます。

tanhとの違いは、マイナスの数値を残すかどうか?だけ・・ですね。

f:id:arakan_no_boku:20181104001942j:plain

 ReLU

マイナスの数値は0にしてしまう。

プラスの数値はそのまま残す。

ようするに、マイナスの数値はノイズだと判断して捨てている・・ってわけですね。

f:id:arakan_no_boku:20181104002103j:plain

なんとなく、イメージはつきましたので、もう少しだけ深堀します。 

 

もう少しだけ踏み込んで、確かめてみます。

Pythonで各活性化関数を実装してみます。

活性化関数は「numpy」を使うと、1行で書けてしまうくらいのものです。

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

def relu(x):
    return np.maximum(0, x) 

これを試しに「-255~255」まで、0.1きざみで生成した数値を処理させてみます。

こんな感じのデータが255まで続く感じです。

-255.0,-254.5,-254.0,-253.5,-253.0,-252.5,-252.0,-251.5,-251.0,-250.5,-250.0,-249.5,-249.0,-248.5,-248.0,-247.5,-247.0,-246.5,-246.0,-245.5,-245.0,-244.5,-244.0,-243.5,-243.0,-242.5,-242.0,-241.5,-241.0,-240.5,-240.0・・・・

グラフにすると、きれいな右肩あがりになります。

f:id:arakan_no_boku:20181104145544j:plain

 

上記のデータをReLUを通したものは、こんなグラフになります。

f:id:arakan_no_boku:20181104145647j:plain

マイナスの数字の部分が全部0になって、プラスの数字はそのまんまです。

わかりやすいですね。 

 

今度は「Tanh」を通した結果のグラフです。

f:id:arakan_no_boku:20181104153546j:plain

 

続けで「sigmoid」を通した結果のグラフです。

f:id:arakan_no_boku:20181104153617j:plain

-1.0~1.0の範囲(tanh)か、0~1.0の範囲(sigmoid)の違いはありますが、とてもよく似た形になってます。

けど、前にのせたリファレンスからコピーしたグラフのイメージとは形が違います。

なぜかというと、リファレンスマニュアルのグラフは「-6 ~ 6」のごく狭い範囲だけを表示していて、自分のやったほうはそんな制限をかけていないからです。

実は、TanhもSigmoidがきれいなグラフを描くのは、せいぜい-10~10位の幅です。

それをこえてしまう値は全部上限・下限にまとめられてしまって、上記のグラフみたいなカクカクした形になってしまいます。

これが何を意味するか?というと。

大きな範囲の数値データをSigmoidやTanhにかけてはダメだということです。 

ピンとこないかもしれないので、ためしに以下のような配列を処理してみます。

[255,150,100,10,0,-10,-100,-150,-255]

すると。

tahhの場合は

 [ 1. 1. 1. 1. 0. -1. -1. -1. -1.]

sigmoidの場合は。

[1. 1. 1. 0.9999546 0.5 0.0000454 0.0. 0. ] 

 みたいな出力になります。

tanhは、10以上や-10以下しかない場合は、すっぱり、1か-1になってしまってます。

かつ、0は0のまま残るのです。

sigmoidの場合は、0は「0.5」になります。

1.0から0の範囲にまとめるのだから、当然ですね。

Sigmoidだと、10、-10のあたりがまだ少数データにとどまっているのですが、やっぱり大きな数字は1か0になってしまってます。

こうなると、元のデータの特徴なんか消え失せて、ほとんどが同じデータのように加工されてしまいます。

だから。

Neural Network Consoleのルールのように、極力 -1.0~1.0の範囲に収まるように整理されたデータ)を入力としなさいとなっているわけです。 

 

こんな風に、活性化関数には特性があります。

それを考慮せずに、無造作に使う(例えば、画像データを正規化しないで、でいきなりtanhとかをかましてしまうとか・・)と、知らぬ間に情報が消失して、どうにも学習精度があがらない・・みたいなことになります。

こんなことは、知っている人には常識以前の話みたいです。

でも、自分がそうだったように、文系人間には意外とそこがわからないんだよ!ということで、長々整理してみました。

ではでは。