活性化関数の紹介

本チュートリアルでは、代表的な活性関数とその特徴について記述します。

活性関数とはニューロンに集まった入力信号をどのように活性化するかを決定する関数です。活性化関数は主に非線形な関数を使うが、その理由として線形分離不可能な問題や線形近似できない問題等を解くことができるからです。

1個のニューロンの入出力関係は以下のように表せます。

\begin{split}y=f(z)\\ z=\sum_{i} w_i x_i +b\end{split}

x_i はニューロンへの入力変数、 w_i は重み変数、 b はバイアス変数、 f(z) は活性化関数を表します。本チュートリアルでは f(z) に注目します。

ニューラルネットワークが登場してから様々な活性化関数が紹介されましたが、本チュートリアルではSigmoid, Tanh, ReLu関数を中心に紹介します。また、それぞれの違いについてさらに理解を深めるために、バックプロパゲーションの観点からそれぞれの活性化関数を違いについて記述します。(バックプロパゲーションについては別チュートリアルを参照いただけると、幸いです。)

必要なライブラリ

In [2]:
import renom as rm
import numpy as np
import matplotlib.pyplot as plt

Sigmoid関数

f(x) = \frac{1}{1 + \exp(-x)}

出力範囲が0、1の間の関数です。ReLuが普及するまではよく活用されていた活性化関数です。Sigmoid が登場するまではIFルールで0, 1を出力していたが、Sigmoidの登場により間の値を出力することができました。入力範囲が小さい状態でSigmoidを見ると大きな曲線になるが、入力範囲が大きい範囲で見るとIFルールに近似した連続的な関数になります。

下記は入力範囲-10,10のSigmoid関数です。

In [3]:
x=np.array([i/100 for i in range(-1000,1000)])
y=rm.sigmoid(x) # Sequential ->rm.Sigmoid()
plt.grid()
plt.plot(x,y)
plt.xlabel('input')
plt.ylabel('output')
plt.show()
../../../_images/notebooks_beginners_guide_activation_intro_notebook_4_0.png

また、もう一つ注目して欲しい特徴は微分式です。 f(x) をSigmoid関数とすると、以下のようになります。

\frac {\partial f(x)}{\partial x}=f(x)(1-f(x))

で表現できます。つまり、微分の計算が簡単にでき、バックプロパゲーションも簡単にできます。

しかし、Sigmoid関数 では以下の2点が指摘されています。

  1. 出力の 中心点が0.5付近であるため、入力が0点付近だと更新量が大きい

\rightarrow 学習の頭で重みが大きく変化する

  1. 入力が極限に大きくなると、勾配が消える

\rightarrow 入力が極限に大きくなると、勾配が消える

現在は上記を克服した活性化関数がいくつかあるため、Sigmoidはあまり使われなくなりました。

Tanh関数

f(x)=\tanh(x)

出力範囲が—1、1の間の関数です。Sigmoid関数と同様に連続関数であり、微分式は出力値を用いて算出する点は同じだが、大きな違いとして出力の中心点が0であることです。

下記は入力範囲-10,10のTanh関数です。

In [4]:
y=rm.tanh(x) # Sequential ->rm.Tanh()
plt.grid()
plt.plot(x,y)
plt.xlabel('input')
plt.ylabel('output')
plt.show()
../../../_images/notebooks_beginners_guide_activation_intro_notebook_7_0.png

f(x) がtanh関数とすると、微分式は以下のようになります。

\frac {\partial f(x)}{\partial x}=1-f(x)^2

上記の関数を活性化関数として使用することにより、sigmoid関数の問題は解決できるが、問題2は解決することはできません。

ReLu関数

f(x)=\max(x, 0)

入力が正の時に入力と同じ値を、入力が負の時に0を出力する関数です。不連続な関数ではあるが、バックプロパゲーションの計算時は定数を使用するため、簡単に計算できます。

下記は入力範囲-10,10のRelu関数です。

In [5]:
y=rm.relu(x) # Sequential ->rm.Relu()
plt.grid()
plt.plot(x,y)
plt.xlabel('input')
plt.ylabel('output')
plt.show()
../../../_images/notebooks_beginners_guide_activation_intro_notebook_10_0.png

上記の関数を活性化関数として使用することにより、Sigmoid関数の問題1,2を解消することができます。NairとHintonらにより有効性が示されたと言われ、以降Reluを活用することが一般的となりました。また、Reluには入力が負になった時に勾配が0になり、更新を停止するという特徴があるため、これを改善する施策として、Leaky Relu 、Elu、Seluなどが提案されました。

まとめ

上記ではSigmoid, Tanh, ReLuの特徴と、バックプロパゲーション時に及ぼす影響について記述しました。上記の内容をまとめると以下のようになります。

また、近年では様々な活性化関数が提案されています。

本チュートリアルを基準にどのような活性化関数を使用するか、またなぜ使用するか、の参考指標として参考にしていただければ幸いです。