"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

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

ディープラーニングには、活性化関数というのが登場します。

Neural Network Consoleだと「Activationレイヤー」と呼ばれてます。

今回は頭の整理を兼ねて、こいつをざっくり整理してみます。

f:id:arakan_no_boku:20190330090249j:plain

 

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つに分かれます。

ひとつは、「上位の計算結果を整理して、特徴を比較しやすくする」です。

これは同じかどうか?を比較するときに、違う基準の数字って比較しづらいですよね。

片方は「-2、1、10」、片方は「-4、3、7」みたいな数字群があって、同じ特徴をもつものかどうか比較しなければならなくなったりしたら、とりあえず、同じ基準範囲「0~10」の中にマッピングしなおしてみて比較するとかみたいにするはずです。

この「比較しやすいようにマッピングしなおす」

これが、まさしく「Tanh(ハイポリックタンジェント)」と「Sigmoid(シグモイド)」の役目となります。

簡単にかくと。

です。

もうひとつは「特徴を際立たせて、比較しやすくする」です。

ReLuなんかは典型的です。

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

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

 

向き不向きがある

 

上記の特徴がわかったうえで、それぞれが、どういうデータに向くのかを考えます。

ReLuが「画像データ」と相性が良いのは自明です。

何故か。

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

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

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

ただ。

気をつけないといけないのは、たとえ、元がマイナスのないデータでも、ウエイトの初期値にマイナス数値がはいっているもので全結合(Affine)とかを通すと、ノイズでないデータまでマイナス値になって出力されてくる場合があること。

それをReLuで処理すると、必要な部分まで削られてトホホになります。

それを受け取って、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・・・・

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

-255から255まで0.1きざみで増加しているのだから、まあ、当然ですが。

f:id:arakan_no_boku:20181104145544j:plain

 

 これを、まずReLUで処理してみます

 

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

f:id:arakan_no_boku:20181104145647j:plain

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

わかりやすいですね。

 

 次にSigmoidとTanhです

 

処理後のデータのグラフです。

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とかをかましてしまうとか・・)と、知らぬ間に情報が消失して、どうにも学習精度があがらない・・みたいなことになります。

このことは。

知っている人には常識以前の話みたいで、ディープラーニングの本とかでも、書いてあるのを見たことがありません。

でも。

自分がそうだったように、文系人間には意外とそこがわからないんだよ!

ということで、長々整理してみました。

 

ではでは。