からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

3.2.7:ReLU関数の実装【ゼロつく1のノート(実装)】

はじめに

 「プログラミング」初学者のための『ゼロから作るDeep Learning』攻略ノートです。『ゼロつくシリーズ』学習の補助となるように適宜解説を加えています。本と一緒に読んでください。

 関数やクラスとして実装される処理の塊を細かく分解して、1つずつ実行結果を見ながら処理の意図を確認していきます。

 この記事は、3.2.7項「ReLU関数」の内容です。ReLU関数をPythonで実装します。

【前節の内容】

www.anarchive-beta.com

【他の節の内容】

www.anarchive-beta.com

【この節の内容】

3.2.7 ReLU関数の実装

 ニューラルネットワークの活性化関数として用いられるReLU関数を実装します。

 利用するライブラリを読み込みます。

# 3.2.7項で利用するライブラリ
import numpy as np
import matplotlib.pyplot as plt


・数式の確認

 まずは、ReLU関数の定義式を確認します。

 ReLU関数は、次の式で定義されます。

$$ h(x) = \begin{cases} x \quad (x > 0) \\ 0 \quad (x \leq 0) \end{cases} \tag{3.7} $$

 ReLU関数$h(x)$の入力$x$が、0より大きければそのまま$x$の値を、0以下であれば0を出力する関数です。

・処理の確認

 次に、ReLU関数で行う処理を確認します。

 0または0以上のxの値を出力するのは、np.maximum()を使うと簡単に行えます。

 np.maximum()は、第1引数と第2引数の要素を比較して大きい方の値を返します。np.maximum()を確認しましょう。

 どちらの引数にもスカラを渡してみます。

# スカラ同士で比較
res = np.maximum(6, 10)
print(res)
10

 大きい方の値を返します。

 第1引数にスカラ、第2引数にリストを渡してみます。(NumPy配列を使って確認すべきですが、同じ結果になるので、np.array()を省略してリストで試しています。)

# スカラとリスト(配列)で比較
res = np.maximum(2, [0, 1, 2, 3, 4, 5])
print(res)
[2 2 2 3 4 5]

 リストの各要素とスカラを比較して、大きい方の値を返します。

 リストの0番目と1番目の要素0, 1よりも、スカラ2の方が大きいので、出力の0番目と1番目の要素が2, 2になりました。
 リストの2番目の要素2は、スカラ2と等しいので、出力の2番目の要素も2になりました。
 リストの3から5番目の要素3, 4, 5の方が、スカラ2のよりも大きいので、出力の3から5番目の要素が3, 4, 5になりました。

 どちらの引数にもリストを渡してみます。

# リスト(配列)同士で比較
res = np.maximum([-5, 2, 10], [5, 0, 10])
print(res)
[ 5  2 10]

 同じインデックスの要素を比較して、大きい方の値を返します。要素数が異なる場合はエラーになります。

 では、np.maximum()を使ってReLU関数の処理を確認します。

 入力$\mathbf{x} = (x_0, x_1, \cdots, x_n)$をNumPy配列として作成します。Pythonのインデックスに合わせて、$\mathbf{x}$の添字を0からにしています。

# 仮の入力を指定
x = np.array([-2, -1, 0, 1, 2, 3])

 負の値・0・正の値を持つように配列を作成しておくと、うまく処理できているかを確認しやすくなります。

 np.maximum()に、0とNumPy配列xを渡します。

# 0以上の値を出力:式(3.7)
y = np.maximum(0, x)
print(y)
[0 0 0 1 2 3]

 xの各要素と0を比較して大きい方の値を出力します。つまり、0以下のxの要素については0が、0以上のxの要素についてはそのままの値が出力されます。

 0以上の入力xの値を出力できました。

 以上がReLU関数で行う処理です。

・実装

 処理の確認ができたので、ReLU関数を関数として実装します。

# RaLU関数の実装
def relu(x):
    # 0以上の値を出力:式(3.7)
    return np.maximum(0, x)


 実装した関数を試してみましょう。

# ReLU関数による活性化
print(relu(-4.5))
print(relu(0.0))
print(relu(10))
print(relu(np.array([-2, -1, 0, 1, 2, 3])))
0.0
0.0
10
[0 0 0 1 2 3]

 この関数で、2次元以上の多次元配列も処理できます。

・グラフの確認

 最後に、ReLU関数の入力に対する出力の変化をグラフで確認します。

 入力$x$としてグラフに表示する範囲を指定します。np.arange()の第1引数に最小値、第2引数に最大値、第3引数に分割する間隔を指定します。

# 入力を指定(x軸の値を作成)
x = np.arange(-5, 5, 0.1)
print(np.round(x, 1))
[-5.  -4.9 -4.8 -4.7 -4.6 -4.5 -4.4 -4.3 -4.2 -4.1 -4.  -3.9 -3.8 -3.7
 -3.6 -3.5 -3.4 -3.3 -3.2 -3.1 -3.  -2.9 -2.8 -2.7 -2.6 -2.5 -2.4 -2.3
 -2.2 -2.1 -2.  -1.9 -1.8 -1.7 -1.6 -1.5 -1.4 -1.3 -1.2 -1.1 -1.  -0.9
 -0.8 -0.7 -0.6 -0.5 -0.4 -0.3 -0.2 -0.1 -0.   0.1  0.2  0.3  0.4  0.5
  0.6  0.7  0.8  0.9  1.   1.1  1.2  1.3  1.4  1.5  1.6  1.7  1.8  1.9
  2.   2.1  2.2  2.3  2.4  2.5  2.6  2.7  2.8  2.9  3.   3.1  3.2  3.3
  3.4  3.5  3.6  3.7  3.8  3.9  4.   4.1  4.2  4.3  4.4  4.5  4.6  4.7
  4.8  4.9]

 この例では、-5から5まで(正確には未満)を範囲として、0.1間隔で要素を作成します。

 xrelu()に入力して、出力をyとします。

# ReLU関数による活性化(y軸の値を計算)
y = relu(x)
print(y)
[0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.
 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.  0.1 0.2 0.3
 0.4 0.5 0.6 0.7 0.8 0.9 1.  1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.  2.1
 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.  3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9
 4.  4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9]

 xの値が0以下の場合はyの値が0になり、xの値が0以上の場合はxの値がそのままyの値になっています。

 matplotlibライブラリのpyplotモジュールを利用して、ReLU関数のグラフを作成します。

# ReLU関数を作図
plt.figure(figsize=(8, 6)) # 図の設定:(サイズを指定)
plt.plot(x, y) # 折れ線グラフ
plt.title('ReLU Function', fontsize=20) # タイトル
plt.xlabel('x') # x軸ラベル
plt.ylabel('y') # y軸ラベル
plt.grid() # グリッド線
plt.show() # グラフを表示

ReLU関数

 $x \leq 0$の範囲では$y = 0$で一定で、$x \geq 0$の範囲では$y = x$の直線になっています。ただし、全体では非線形のグラフになっているのが分かります。

 以上で、ReLU関数を実装できました。次項では、これまでに実装した3つの活性化関数を簡単に比較します。この本では、終盤以降で活性化関数として使います。

参考文献

github.com

おわりに

 加筆修正の際に記事を分割しました。

【次節の内容】

www.anarchive-beta.com