HOWTO · Python
Python の ReLU 関数の導関数
ReLU 関数は、深層学習の観点から、機械学習で最も頻繁に使用されます。 この記事では、Python で ReLU 派生を実装する方法と、ReLU 関数を実装する方法について説明します。
このページの内容
ReLUはf(x) = max(0, x)で定義されます。Pythonでは、スカラー値にはmaxを、配列にはnumpy.maximumを使って実装できます。数学的な導関数はx < 0で0、x > 0で1です。x = 0では導関数は定義されません。コードでは劣勾配として0を選ぶことがよくあり、以下の例もこの規約を使います。
PythonでReLU関数を実装する
スカラー入力なら、組み込みのmax関数で定義をそのまま表せます。次の例を実行してください。
このセクションのコードと表示されるラベルや出力は英語のままです。
def relu(x):
return max(0, x)
print(relu(3))
print(relu(0.5))
3
0.5
この関数は、入力が正ならその値を返し、それ以外の場合は0を返します。このスカラー向けの実装は、一度に1つの数値を扱うもので、NumPy配列にはそのまま適用できません。
スカラー値に対するReLUの導関数を実装する
ゼロ以外の入力では、導関数は区分的な直線関数の傾きに従います。ゼロより小さいときは0、ゼロより大きいときは1です。左右で傾きが異なるため、数学的な導関数はゼロでは存在しません。勾配を計算するプログラムではその点にも値が必要なので、この関数では一般的な劣勾配の値である0を明示的に選びます。
このセクションのコードと表示されるラベルや出力は英語のままです。
def relu_derivative(x):
return 1 if x > 0 else 0
for value in (-2, 0, 3):
print(value, relu_derivative(value))
-2 0
0 0
3 1
この規則は、入力が厳密に正のときだけ1を返し、ゼロ以下なら0を返します。これは微分できない点に対する実装上の規約であり、数学的な導関数がその点で0になるという意味ではありません。
NumPy配列にReLUとその導関数を適用する
NumPyでは、これらの演算を要素ごとに適用できます。ReLUの計算にはnp.maximumを使い、各配列要素に対する導関数の値の選択にはnp.whereを使います。導関数の式では、スカラー関数と同じくゼロでの劣勾配に0を使います。
このセクションのコードと表示されるラベルや出力は英語のままです。
import numpy as np
values = np.array([-2.0, 0.0, 3.0])
outputs = np.maximum(values, 0)
gradients = np.where(values > 0, 1, 0)
print(outputs)
print(gradients)
[0. 0. 3.]
[0 0 1]
配列を入力すると、出力される2つの配列はどちらもvaluesの各要素に対応します。選択した規約により、ゼロの要素に対する導関数の値は0です。入力が配列ならnp.maximumとnp.whereを使い、1回の呼び出しで数値を1つ扱う場合は前述のスカラー関数を使います。
スカラー入力にラムダ式を使う
短い関数をインラインで使いたい場合、ラムダ式は簡潔な選択肢です。次の式は、名前付き関数と同じ定義およびゼロでの規約を使います。
このセクションのコードと表示されるラベルや出力は英語のままです。
relu = lambda x: max(0, x)
relu_derivative = lambda x: 1 if x > 0 else 0
print(relu(3), relu_derivative(3))
3 1
これらのラムダ式はスカラー入力を受け取ります。配列に対するNumPyの要素ごとの演算の代わりにはなりません。再利用する処理や、より詳しい説明が必要な場合は、名前付きのdef関数のほうが一般に読みやすく、拡張もしやすくなります。
ReLUとその導関数をプロットして確認する
プロットを使うと、平坦な領域と傾きの変化を目で確認できます。付属のプロットでは、導関数の曲線はゼロで0となっており、実装上の規約に沿っています。ただし、数学的な導関数はその点で定義されないままです。次の自己完結型のコードで、プロット用ライブラリを使わずに代表的な値を確認できます。
このセクションのコードと表示されるラベルや出力は英語のままです。
points = (-2, 0, 2)
for x in points:
relu = max(0, x)
derivative = 1 if x > 0 else 0
print(x, relu, derivative)
-2 0 0
0 0 0
2 2 1
ReLUの曲線は、入力が負の間は0のままで、正になると傾き1で上昇します。数学的な導関数はx = 0で定義されませんが、上の実装ではその点の劣勾配として0を選んでいます。破線の垂直な接続線は0から1への跳びを示すもので、導関数がその間の値を取ることを表してはいません。ニューラルネットワークでは、勾配が繰り返し0になると、負の入力を受ける一部のユニットが更新されなくなることがあります。Leaky ReLUはその代替手法の1つで、負の側にも小さな非ゼロの傾きを使います。