指数関数のオーバーフロー対策
最近見た実装で賢いなぁと思った実装があったのでご紹介します。
それが,タイトルにもある通りシグモイド関数のオーバーフロー対策です。
pythonは割と数値型(int, float,...)に関しては優秀で,あまり意識せずとも大きな数値を取ることが可能です。
しかし,流石に限度があるようで。
指数関数ぐらい発散速度が大きいものは簡単にオーバーフローします。
例)
>>> import numpy as np
>>> np.exp(100) #100くらいなら平気
2.6881171418161356e+43 #ただしかなり大きい桁数
>>> np.exp(709)
8.218407461554972e+307
>>> np.exp(710)
<stdin>:1: RuntimeWarning: overflow encountered in exp
inf
>>> np.exp(-1000)
0.0 #マイナス方向なら大丈夫
というわけで,$e^{710}$ という数値になるとオーバーフローしました。
肩の数字がマイナス方向に大きければ問題ないのですが,そうでなければ気をつけなければなりません。
これが効いてくるのが,シグモイド関数の実装時です。
シグモイド関数とは
$$
f(x) = \frac{1}{1+e^{-x}}
$$
で表される関数で,ロジスティック回帰とかニューラルネットワークの活性化関数とかでよく見られるやつです。
これ,一応定義域が実数全体をとるのですが,xがマイナス方向に大きい場合はどうなるでしょうか。
例えば,
$$
f(-1000) = \frac{1}{1+e^{-(-1000)}} = \frac{1}{1+e^{1000}}
$$
なので,$e^{1000}$がオーバーフローすることを考えると計算できないことになります。
そこで,実装上この関数を次のように場合分けをします。
f(x) = \left\{
\begin{array}{ll}
\frac{1}{1+e^{-x}} & (x \geq 0) \\
\frac{e^x}{1+e^{x}} & (x \lt 0)
\end{array}
\right.
このようにすると,$x\geq0$の時は$e^{-x}$を計算して,$x\lt0$の場合は$e^x$を計算してくれるため,指数関数の肩の数字がマイナスの値になります。
よって,オーバーフロー対策になるわけです。
なお、if文
で書いてもいいですが次のようにやると一行で書けます。
import numpy as np
def sigmoid(x):
return np.exp(np.minimum(x, 0)) / (1 + np.exp(- np.abs(x)))