対数関数

本ページは底の明記がないときは自然対数の底として進めます。

\[y = \log{x} \quad 0 < x < \infty\]

微分:

\[\frac{d}{dx}\log x = \frac{1}{x} \quad (x > 0)\]

対数関数

特徴

  1. $x > 1 \implies y > 0$ ($x$ が $1$ より大きければ、$y$ はプラスになる)
  2. $x = 1 \implies y = 0$
  3. $0 < x < 1 \implies y < 0$
  4. $\lim_{x \to 0^+} \log x = -\infty$

微分の特徴:

$x$ が大きくなるほど傾きが緩やかになります(小さくなる)。

  • $x > 0 \implies y’ > 0$($x$ が正の範囲では、傾き $y’$ は常にプラス。つまり、グラフは常に右上がりの状態が続く)
  • $\lim_{x \to 0^+} y’ = \infty$($x$ が $0$ に近づくほど、傾き $y’$ は無限大に大きくなる。垂直に近いレベルで急激に立ち上がる)
  • $\lim_{x \to \infty} y’ = 0$($x$ が大きくなればなるほど、傾き $y’$ は $0$ に近づく。グラフの伸びはどんどん横ばいに緩やかになる)

対数関数の合成関数の微分

交差エントロピー誤差の計算などでは、ただの $\log x$ ではなく、$\log f(x)$ (例えば $\log q(x)$ )の形で微分することがほとんどです。 合成関数の微分公式(チェインルール)が適用されます。

\[\frac{d}{dx}\log f(x) = \frac{f'(x)}{f(x)}\]

例:$f(x) = 1 - x$ であれば、その微分は $-1$ なので、以下のように展開されます。

\[\frac{d}{dx}\log(1 - x) = \frac{-1}{1 - x}\]

負の対数関数

\[y = -\log{x} \quad 0 < x < \infty\]

負の対数関数

情報量としての負の対数関数

情報量(自己情報量)は $-\log_2(x)$ で定義されます。 ただし、多くの機械学習ライブラリの実装では、計算の効率化のために自然対数の底($e$)を使用した $y = -\log x$ が使われます。

特徴

  • $x \to 0^+$ のとき $y \to \infty$(めったに起こらないこと=情報量が非常に大きくなる)
  • $x = 1$ のとき $y = 0$(確実に起こることの情報量は 0)
  • $x$ が $1$ に近づくほど $y$ は $0$ に近づく(確率が高くなるほど情報量が小さくなる)

交差エントロピー誤差

ディープラーニング(分類問題)では、モデルの予測の「悪さ」を負の対数関数を使って交差エントロピー誤差(損失)として表します。

  • $p(x)$(現実の分布):教師データ(正解ラベル)の分布 ※通常はOne-Hotベクトル
  • $q(x)$(モデルの分布):モデルが出力した予測(Softmax等を通した確率)の分布
\[H(p,q) = - \sum_{x} p(x) \log q(x)\]

特徴(正解クラスの予測確率 $q(x)$ に対する損失の挙動)

  • 正解クラスの予測確率 $q(x) \to 0^+$ のとき、損失は $\infty$ に発散する(大ハズレの予測には非常に大きなペナルティ)
  • 正解クラスの予測確率 $q(x) = 1$ のとき、損失は $0$ になる(完全正解、損失なし)
  • $q(x)$ が $1$ に近づくほど、損失は $0$ に近づく(予測の自信が高まるほど損失が小さくなる)

シグモイド関数( Sigmoid function )

\[y = \frac{1}{1 + \exp(-x)}\]

シグモイド関数

シグモイド関数の定義は以下のとおり。 特徴は定義域 $-\infty$ <= x <= $\infty$ に対して y は 0 < y < 1 で $x = 0 のとき y = 0.5$

Python コード

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

シグモイド関数の微分($\frac{dy}{dx} = y(1 - y)$)

\[\frac{dy}{dx} = y(1 - y)\]

勾配消失問題

$x$ の値 シグモイド関数の値 $y$ 勾配(微分値) $y’$
0 0.5 0.25(最大)
±1 約 0.73 / 0.27 約 0.20
±3 約 0.95 / 0.05 約 0.045

層が深くなるほど勾配が 0 に収束して学習が止まります。

ハイパボリックタンジェント/双曲線正接( tanh function )

\[tanh(x) = \frac{e^x-e^{-x}}{e^{x} + e^{-x}}\]

tanh

  • tanh 関数の出力範囲は ( −1, 1 )で Sigmoid 関数( 0 - 1 )と異なり負の値を取れるため出力の平均がゼロに近く(zero-centered)なり次の層への入力が偏りにくく学習が安定しやすい
  • 最大勾配が 1( x=0 付近)で Sigmoid 関数の最大勾配 0.25 の 4 倍で勾配消失が起きにくい(ただし、 x が大きくなると勾配はほぼ 0 になるため深いネットワークでは依然として勾配消失問題が発生する)
    • ReLU は勾配消失がさらに起きにくい

上記の勾配の性質より現在の深いネットワークの Affine レイヤの活性化関数は ReLU が主流だが RNN 、LSTM の内部ゲートの活性化関数には tanh を使用する場合が多い。

LeLU 関数

ReLU 関数

勾配消失

$x > 0$ のとき x( $x \le 0$ のとき 0 )なので勾配消失を抑えることができます。

Softmax 関数

Softmax 関数は入力を確率に変換して出力します。
出力の各要素は 0 以上 1 以下の実数であり、すべての要素の総和は 1 になります。
確率化される前のデータをニューラルネットワークの文脈でスコアと呼びます。

  • 多値分類に Softmax を使う理由:全クラスの確率の和を 1 に正規化しつつ各クラスに独立したスコアを与えられるためです

スコアを $(s_1, s_2, \ldots, s_k, \ldots, s_n)$ とすると、 Softmax 関数は以下のように定義されます:

\[y_k = \frac{\exp(s_k)}{\displaystyle\sum_{i=1}^{n} \exp(s_i)}\]

Softmax 関数

Python コード

def softmax(x):
    # 特徴
    # 入力と出力のシェイプ(形)は完全に一致する
    # Softmax は各要素の値を「確率」に変換するだけで、データの構造(次元数や各次元の要素数)を壊したり、統合したりすることはありません。
    #
    # x - np.max(x) の意味
    # np.max(x) は x の最も大きい値(最大値) を取得する
    # 最大値を引く( x = x-xmax(axis=1, keepdims=True) )意味は 「オーバーフロー(数値の溢れ)対策」 
    # Softmaxは指数関数の入力値が少し大きくなるだけで計算結果が膨大な数値になる
    # 解決策: 全ての要素から最大値を引いても、Softmaxの出力結果は変わらない
    # 最大値を引くことで、指数関数の入力が 「0以下の値」 になり、$e^x$ の結果が必ず 0 から 1 の間に収まる
    if x.ndim == 2:
        x = x - x.max(axis=1, keepdims=True)
        x = np.exp(x)
        x /= x.sum(axis=1, keepdims=True)
    elif x.ndim == 1:
        x = x - np.max(x)
        x = np.exp(x) / np.sum(np.exp(x))

    return x

ステップ関数

\[h(x) = \begin{cases} 0 & (x \leq 0) \\ 1 & (x > 0) \end{cases}\]

[!NOTE] ステップ関数はほぼ全域で微分値が0になり不連続点では微分が定義されません。 これは誤差逆伝播法(バックプロパゲーション)が機能しない、つまり学習ができないことを意味します。

ステップ関数の問題を解消した活性化関数が Sigmoid であり、さらにその限界(勾配消失)を克服したのが ReLU です。

損失関数

ニューラルネットワークは損失(Loss)を最小にするように学習します。
損失はスカラーです

ニューラルネットワークの損失は交差エントロピー誤差( Cross Entropy Error )を使用します:

\[L = -\sum_{k=1}^{n} t_k \log y_k\]

Python コード

def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        
    # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
    if t.size == y.size:
        t = t.argmax(axis=1)
             
    batch_size = y.shape[0]

    # np.log(0) はマイナス無限大($-\infty$)になり、計算が止まってしまいます。それを防ぐために、ごく小さな値($10^{-7}$)を足して、**「絶対に 0 にならないように」**安全策をとっています。

    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size