はじめに
シグモイドやソフトプラス等の指数関数を含む関数を自分で実装すると,指数関数部でオーバーフローになり正しく計算できなくなることがあります。
関数によっては簡単な工夫で防げる場合があるので紹介します。
シグモイド関数
シグモイド関数は
f(x) = \frac{1}{1+\exp(-x)}
で表され,$(-1,1)$の値域をとります。
(画像はWikipediaより引用)
$x$がマイナス方向に行きすぎると,理論的には$f(x)\simeq 0$ですが$\exp(-x)$がオーバーフローします。
$x< 0$のときは分母分子に$\exp(x)$を掛けて変形し,
f(x)=
\begin{cases}
\displaystyle\frac{1}{1+\exp(-x)} & \text{if $x\geq 0$} \\
\displaystyle\frac{\exp(x)}{\exp(x)+1} & \text{if $x< 0$}
\end{cases}
と場合分けすれば常に正しく計算できます。
また,場合分けをまとめると
f(x) = \frac{\exp(\min(0,x))}{1+\exp(-|x|)}
とできます。
ソフトプラス関数
ソフトプラス関数は
f(x) = \log \left( 1+\exp(x) \right)
で表されます。
(画像はWikipediaより引用)
この関数も$x$が大きくなりすぎると$\exp(x)$がオーバーフローします。
$x\geq 0$のときは$\log$の中身を$\exp(x)$で括り,$\log\exp(x)=x$であることを利用して$\exp(x)$を消します。
\begin{align}
f(x) &= \log\left\{ \exp(x)(\exp(-x)+1) \right\} \\
&= \log\exp(x) + \log(\exp(-x)+1) \\
&= x+\log(\exp(-x)+1)
\end{align}
つまり,
f(x) =
\begin{cases}
\log(1+\exp(x)) & \text{if $x<0$} \\
x+\log(\exp(-x)+1) & \text{if $x\geq 0$}
\end{cases}
を計算すれば良いことになります。
また,場合分けを上手くまとめると
f(x) = \max (0,x) + \log(1+\exp(-|x|))
と書けます。
ソフトマックス関数
ソフトマックス関数は上記とは異なりベクトルを受け取ってベクトルを返します。
入力を${\bf x} = [x_1, \ldots, x_N]$とすると
{\bf f}({\bf x}) = \left[ \frac{\exp(x_i)}{\sum_j \exp(x_j)} \right]_{i=1}^N
で表されます。
入力${\bf x}$の中で最大の値を持つ要素を
x^{\rm m}=\max(x_1,\ldots,x_N)
とし,分母分子を$\exp(x^{\rm m})$で割った
{\bf f}({\bf x}) = \left[ \frac{\exp(x_i-x^{\rm m})}{\sum_j \exp(x_j - x^{\rm m})} \right]_{i=1}^N
を計算することでオーバーフローを回避できます。
おわりに
いずれも指数関数の引数が0以下になるようにするのがポイントです。