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

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

超文系向け/「Activationレイヤー(活性化関数)」について、わかりやすく解説してみる/Neural Network Console

Neural Network Consoleは、レイヤーをGUIでペタペタと貼り付けるだけで、それなりのニューラルネットワークが組めます。

f:id:arakan_no_boku:20181030211624j:plain

でも。

レイヤーリファレンスの説明は読んでも、全然ピンとこない。

そんな人は一定数いるみたいなので、試みとして、ひとつのレイヤーにしぼった「超文系向け解説」にチャレンジしてみます。

 

今回はActivationレイヤー(活性化関数)です

 

レイヤーリファレンスの「Activationレイヤー」をテーマにします。

日本語だと「活性化関数」です。

いっぱい種類があるので、今回はその中でも非常によく使う3つ。

をとりあげます。

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

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は入力を示す)

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

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

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

 

例によって簡単な画像のサンプルです

 

前回、以下のような画像を例にしました。

f:id:arakan_no_boku:20181028144011j:plain

f:id:arakan_no_boku:20181028144027j:plain

人間が見ると微妙に位置はずれている。

だけど、この2つは同じとみなすのが「正解」だ。

そう言われたら、なんとかして「同じとみなす為の共通の特徴」を抽出しないといけないくなる。

でも、こんな感じで微妙に位置関係とかがずれているものを同じとみなすための共通の特徴を把握するには、そのままより、なんらかの加工をして処理しやすくしたほうがやりやすいのは当然だ。

その一つの方法として、「Affine(全結合層)」で処理したら、位置関係のズレとかを、ある程度吸収してくれるみたいです。

などなど。

前回は、そんな話でした。

arakan-pgm-ai.hatenablog.com

でも。

確かに「Affine(全結合層)」で位置のズレは吸収できても、画像データを数値としてみたときに数字自体が違っていたりもします。

同じ白に人間には見えても、数値としては「255」と「180」で全く違うとかですね。

そこもそのままでは、ちょっと扱いづらい。

それに、画像の中には、識別を邪魔する余分な情報(ノイズ)とかも含まれている場合もあります。

人の目から見たら白一色でも、実は、ぽつぽつと余分な数値が紛れ込んでたりですね。

これも共通の特徴を整理する上では邪魔です。

なので・・当然。

数字の差異一定の範囲内に整理しておいた方がいいよな・・とか。

ノイズになるような数字は省いておきたいよな・・とか。

そう思うわけです。

その役割をするのが、「Activationレイヤー(活性化関数)」。

そう考えるとイメージしやすいです。

 

Activationレイヤー(活性化関数)それぞれの特性

 

イメージはしやすい。

でも、最初のころの自分にとっては、Activationレイヤー(活性化関数)は、どこで何を使うのが適切か?がピンとこない代物でした。

サンプルを見れば、何となく・・はわかります。

けど、何故か?という理由が腹にはまってないので、なんか気持ち悪い。

そういう感じでした。

それぞれの特性は、リファレンスにもグラフでわかりやすく書いてあります。

だから、これを見ればわかる・・はずなんですけどね。

リファレンスマニュアルに書いてあるグラフは、こんな感じです。

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になって、プラスの数字はそのまんまです。

データを整理する考え方から言うと。

マイナスの数字部分はノイズであって、プラス部分だけが共通の特徴をあらわすものだという考えです。

こうすると、どういうデータを処理するのに都合がよいのか?・・というと。

ズバリ、画像データです。

何故か。

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

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

元がマイナスのないデータでも、例えばAffineとかを通すと、W(ウエイト)の値にマイナスの値があったりするので、結果としてマイナスの値も出力されてきます。

それを受け取って、ReLUで処理して、マイナスの値をカットして共通の特徴を明確にするのは、なんとなく、理にかなってますね。

 

次に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位の幅でしかありません。

だから、-255~255のような広い範囲のデータを、TanhやSigmoidに処理させると、上記のような形になってしまうわけです。

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

[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になってしまってます。

だから。

ReLUと違って、生の画像データとは相性が悪いのは、すぐわかりますよね。

だって。

255~0の数値で表現される画像データを、そのままTanhとかsigmoidにかけてしまうと、ほとんどのデータが-1か1、もしくは 0か1に置き換えられてしまうわけです。

比較すべき情報自体ががなくなってしまいます。

でも、TanhやSigmoidが有効な範囲(例えば、Neural Network Consoleのルールのように、極力 -1.0~1.0の範囲に収まるように整理されたデータ)であれば、リファレンスのグラフにあるような曲線上にマッピングしてくれます。

データの凸凹をならして、共通の特徴をより抽出しやすく整理する感じですかね。

このように。

活性化関数には特性があります。

だから、効果的に処理できるデータと、そうでないデータが存在します。

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

このことは。

知っている人には常識以前の話みたいです。

だから、ディープラーニングの本とかにも、あえて書いたりしないのでしょうね。

でも。

自分がそうだったように、文系人間には意外とそこがわからないもんなんだってのも、本当のことなんですけどね。

ということで。

今回のActivationレイヤー(活性化関数)の「そもそも」は終わりです。

ではでは。