(追記)
コードの解説を追記しました。
かつて初心者だった自分。独学ながらもなんとか理解してきました。
始めたばかりの頃に、こうやって聞きたかったなぁと思った解説を追記しました。この記事はおそらく初級、中級の方に読んでいただけるのかと思います。独学だと最初は難しくて、理解が進まず、挫折しそうなこともありました。そんな時に少しでも助けになれるようにと書いてみました。
1. はじめに
深層学習において活性化関数が提案され、今も研究が進められています。それぞれの並べて表示してみました。専らReLUを使うことが多いのですが、そういえばと思った時に意外と正確に覚えられていないと思い、一覧表示してみました。
2. 並べてみる
Jupyter Notebookを使って、実装しました。各活性化関数を並べて表示することができます。
%matplotlib inline
# モジュールのインポート
import numpy as np
import matplotlib.pyplot as plt
# グラフの設定
n_cols = 4
n_rows = 2
plt.figure(figsize=(20,8))
# xの値を生成
x = np.arange(-10, 10, 0.01)
# 表示する各関数のリスト
activation_function_list = [ 'Indentity', 'Step', 'Sigmoid', 'tanh', 'ReLU', 'Leaky ReLU','Swish', 'Mish']
# グラフ表示
def graph(x,y):
plt.subplot(n_rows, n_cols, i+1)
plt.grid()
plt.title(f)
plt.ylim(-1, 1)
plt.xlim(-5, 5)
plt.plot(x,y)
# 各関数の計算
for i, f in enumerate(activation_function_list):
# 恒等関数
if f == 'Indentity':
y = x
# Step関数
elif f == 'Step':
y = np.where(x<=0, 0, 1)
# Sigmoid関数
elif f == 'Sigmoid':
y = 1./(1. + np.exp(-x))
# tanh関数
elif f == 'tanh':
y = (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
# ReLU関数
elif f == 'ReLU':
y = np.maximum(0,x)
# Leaky ReLU関数
elif f == 'Leaky ReLU':
y = np.where(x > 0, x , 0.01 * x)
# Swish関数
elif f == 'Swish':
y = x*1./(1. + np.exp(-x))
# mish関数
elif f == 'Mish':
x_sp = np.log(1+np.exp(x)) # Softplus関数
y = x * ((np.exp(x_sp ) - np.exp(-x_sp)) / (np.exp(x_sp) + np.exp(-x_sp)))
# グラフの作成
graph(x,y)
# グラフの描画
plt.show()
# グラフの保存
fig.savefig('activation_function_list.png')
3. 出力された関数
コードの最後にmatplotlipにて作成したグラフをpngファイルとして保存しています。
このファイルは、上記の場合は、ノートブックのファイルがある同じディレクトリに保存されます。
SwishやMishは新しく登場した活性化関数です。
今後は、これらの活性化関数での性能の違いなどを検証したいと思います。
ここまででいったん終了。
4. 上記のコードを解説してみる。
ここから上記で書いたコードについて解説をしてみようと思います。
もし自分が初心者だった時に聞きたかった解説を書いてみました。中級者、上級者の方には物足りないと思いますが、興味のある人はみてみてください。
4-1. グラフ表示
%matplotlib inline
%matplotlib inline
と書くことによって、Notebook内に描画されます。
4-2. モジュールのインポート
以下のコードでは、NumPyとMatplotlibの2つのモジュールをインポートしてきます。
# モジュールのインポート
import numpy as np
import matplotlib.pyplot as plt
このモジュールはいったいどこからimportしているかというと、確認してみましょう。
ノートブック上で以下のコードを実行すると
import numpy as np
print(np.__file__)
出力
/Users/username/.pyenv/versions/3.6.2/lib/python3.6/site-packages/numpy/__init__.py
こちらの出力はそれぞれの環境によりますが、この出力結果からわかるのは、ローカルPCにインストールされているnumpyが参照されていることがわかります。
__init__.py
については、@PYTHONISTAさんの記事にて詳しく記載されています。
Python init.pyの書き方
インポートする時に、asをつけているので、このあとnumpyを参照するにはnpで参照できるようになります。
import numpy
print(numpy.__file__)
としても、同じことができます。
4-3. グラフの設定
ここでは、活性化関数を並べて表示するグラフの設定を行っています。
このあとに記載するsubplotにてグラフの並べ方を横に4つ、縦に2つ並べる設定に使っています。
figsizeで指定するサイズにて、figureを作っています。
コードの最後でpngファイルとして一覧を保存しますが、figureに配置しているグラフが一つのファイルとなります。
# グラフの設定
n_cols = 4
n_rows = 2
plt.figure(figsize=(20,8))
4-4. xの値生成
# x軸の値を生成
x = np.arange(-10, 10, 0.01)
このように()の中にカーソルがある状態で、[shift]+[tab]キーを押すと、↓のように詳細が表示されます。
ここからarangeが引数とするパラメータが何かがわかります。
x = np.arange(-10, 10, 0.1) の場合は、startが-10、stopが10、stepが0.1となり、
0.1刻みで-10から10までの数のリストが帰ってきます。では確認してみましょう。
print('xのリスト:',x)
print('xの長さ:',len(x))
print('xの型', type(x))
xのリスト: [-10. -9.99 -9.98 ... 9.97 9.98 9.99] xの長さ: 2000 xの型 <class 'numpy.ndarray'>
2000の数が生成されています。stopに指定された10は含まれていないことがわかります。
4-5. 表示する各関数のリスト
ここでは、一覧に並べたい関数のリストを作っています。
activation_function_list = [ 'Indentity', 'Step', 'Sigmoid', 'tanh', 'ReLU', 'Leaky ReLU','Swish', 'Mish']
4-6. グラフ表示
この部分でグラフ描画の関数を定義しています。4-7の処理の中でこの関数を呼び出して、グラフを作っています。
この中での i の部分ですが、ここは4x2で配置する場合のグラフの場所を示しています。
左上から右にむかって
1 2 3 4
5 6 7 8
となります。plt.subplotの引数の3番目にiを入れています。このことによって、グラフ関数を呼び出した時のiがグラフそのものの場所を指定していることになります。
4-7でリストからインデックスと活性化関数の名前を取り出しています。
# グラフ表示
def graph(x, y):
# インデックスには0から入りますが、グラフの場所は1から指定されるので、
# ここで1を足して、4-6で定義したgraph関数を呼んでいます。
plt.subplot(n_rows, n_cols, i+1)
plt.grid() # グラフのグリッド線を有効化
plt.title(f) # 関数名をグラフのタイトルに設定
plt.ylim(-1, 1) # 表示するy軸の範囲を指定
plt.xlim(-5, 5) # 表示するx軸の範囲を指定
plt.plot(x, y) # xとyの値をプロット
4-7 各関数の計算
ちょっと長いのでコード内にコメントを追記します。
各関数の詳細については、ここで誌面を割くにはあまりにも量が多くなってしまうので、この記事では割愛します。
# 各関数の計算
# enumerate関数を使って、activation_function_listからインデックスとリスト値を取り出しています。
# iにはインデックス、fには関数名が入ります。
# iは0から順に入ります。
for i, f in enumerate(activation_function_list):
# 取り出した関数名によって、yの値を計算しています。
# xの値が2,000個あったので、yの値も2,000個生成されています。
if f == 'Indentity': # 恒等関数
y = x
elif f == 'Step': # ステップ関数
y = np.where(x<=0, 0, 1)
elif f == 'Sigmoid': # シグモイド関数
y = 1./(1. + np.exp(-x))
elif f == 'tanh': # ハイパボリックタンジェント関数
y = (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))
elif f == 'ReLU': # ReLU関数
y = np.maximum(0,x)
elif f == 'Leaky ReLU': # Leaky ReLU関数
y = np.where(x > 0, x , 0.01 * x)
elif f == 'Swish': # Swish関数
y = x*1./(1. + np.exp(-x))
elif f == 'Mish': # Swish関数
x_sp = np.log(1+np.exp(x)) # softplus関数
y = x * ((np.exp(x_sp ) - np.exp(-x_sp )) / (np.exp(x_sp ) + np.exp(-x_sp )))
# グラフの描画
# xを使って計算したyをgraph関数の引数に渡します。
graph(x,y)
enumerate
関数を使って、リストからインデックスと値を取得しているのですが、
どんな値が取得されているかは、以下のコードを実行すると
for i, f in enumerate(activation_function_list):
print('index:', i , 'value:', f)
出力
index: 0 value: Indentity index: 1 value: Step index: 2 value: Sigmoid index: 3 value: tanh index: 4 value: ReLU index: 5 value: Leaky ReLU index: 6 value: Swish index: 7 value: Mish
となって、順番にインデックスと値が取り出せていることがわかります。
4-8 グラフの描画と保存
以下の二行でグラフの描画と保存を行っています。
保存するpngファイルは、使っているNotebookのファイルがあるフォルダに保存されます。
# グラフの描画
plt.show()
# グラフの保存
fig.savefig('activation_function_list.png')
もし間違っている部分や誤りなどあれば、ご教示いただけると幸いです。