デジタル・デザイン・ラボラトリーな日々

アラフィフプログラマーが数学と物理を基礎からやり直す。https://qiita.com/yaju

ディープラーニング(深層学習)を理解してみる(活性化/損失関数)

はじめに

前回、勾配降下法の計算方法をPythonで実際に組んでみて、TensorFlowで実行した結果と同じになりました。
yaju3d.hatenablog.jp

今回は、活性化関数/損失関数を軽くまとめてみます。 というのも、前回の勾配降下法の計算確認をした際に活性化関数を使ってなかったことに疑問を持ったからです。

2017年に始めたこのシリーズは1ヶ月に1記事のペースになってしまったのですが、来年はペースを上げたいところ。

来年から誤差伝播法、畳込みニューラルネットワーク(CNN)、リカレントニューラルネットワーク(RNN)をやっていきます。

下記5つの本(書籍版とKindle版)を、これまでに参考にさせて頂きました。
    

活性化関数

活性化関数は入力信号の総和を出力信号に変換する関数のことです。どのように活性化するか(どのように発火するか)ということを決定する役割があります。
人工知能の本を読むと、活性化関数としてステップ関数、シグモイド関数、ソフトマックス関数などが出てきます。
前回の勾配降下法の計算確認をした際に、先述した活性化関数は1つも使っていませんでした。
調べて見たところ、参考にした本には「活性化関数は恒等関数を使います」と書かれていました。

ゼロから作るDeepLearning
3.5 出力層の設計
ニューラルネットワークは、分類問題と回帰問題の両方に用いることができます。ただし、分類問題と回帰問題のどちらに用いるかで、出力層の活性化関数を変更する必要があります。一般的には、回帰問題には恒等関数を、分類問題ではソフトマックス関数を使います。
分類問題とは、データがどのクラスに属するか、という問題です。(中略) 一方、回帰問題は、ある入力データから、(連続的な)数値の予測を行う問題です。

今回はリンゴとミカンの値段を予測するということで回帰問題となるため、活性化関数は恒等関数を使ったわけです。

「回帰分析」という言葉の誕生
科学史から最小二乗法 (回帰分析) を説明してみる

活性化関数の種類

活性化関数でも使用する場所(層)があります。
中間層(隠れ層)での活性化関数は、通常はシグモイド関数やReLUなどが用いられ、出力層での活性化関数は、回帰問題なら恒等関数、分類問題の2値分類ならシグモイド関数、分類問題の多値分類ならソフトマックスなどが用いられます。

代表的な活性化関数です。

  • Step (ステップ関数)
  • Linear (線形関数)
  • Identity (恒等関数)
  • Sigmoid (シグモイド関数 ロジスティクス関数)
  • Tanh (双曲線正接関数 hyperbolic function)
  • Hard Sigmoid
  • Hard Tanh
  • ReLu (ランプ関数 正規化線形関数 Rectified Linear Unit)
  • Leaky ReLU
  • PReLU(Parametrized ReLU)
  • RReLU
  • SReLU
  • ELU
  • SELU
  • Probit
  • LeCun Tanh
  • ArcTan
  • SoftSign (ソフトサイン関数)
  • Softplus (ソフトプラス関数)
  • Softmax (ソフトマックス関数)
  • Signum
  • Bent Identity
  • Symmetrical Sigmoid
  • Log Log
  • Gaussian
  • Absolute
  • Sinusoid
  • Cos
  • Sinc
  • Swish
  • Gumbel-Softmax
  • eLU (Exponential Linear Units)
  • Thresholded ReLU
  • Maxout (区分線形凸関数)

次のサイトで8割くらいが可視化されています。
dashee87.github.io

恒等関数(Identity)

恒等関数は言葉こそ難しいですが、単純に入ってきたものに対して何も手を加えずに出力する関数となります。 「恒」の意味が「いつも変わらない。」となっています。ソフトマックス関数を比べると分かりやすい。
f:id:Yaju3D:20180104021747p:plain

def koutou(a):
  return a

ステップ関数(Step)

ステップ関数は入力に対してある閾値を境に階段(ステップ)のように出力が1か0か決まる関数となります。
f:id:Yaju3D:20170603024904p:plain

入力した値が0以下のとき0になり、0より大きいとき1になるステップ関数の実装例

def step_function(x):
  if x>0:
    return 1
  else:
    return 0

ニューラルネットワークは学習をする際に微分を含む計算を行うため、微分するとゼロになるステップ関数は都合が悪いことからシグモイド曲線(sigmoid)などが使用されている。

線形関数(Linear)

線形関数は単純パーセプトロンには使えますが、多層パーセプトロン(ニューラルネットワーク)には使えません。
線形関数で層を増やしても多層化には貢献しないのです。例えば任意の関数 y = f(x) で構成される層に、線形関数 g = cx の層を重ねたとしても、それは y = f(cx) で構成される単層パーセプトロンとやってることは同じになるためです。

def linear_function(x, w, b):
    return w * x + b

シグモイド関数(Sigmoid)

シグモイド曲線は入力した値を0から1の間に収めてくれる関数の1つです。多くの自然界に存在する事柄は、このようなS字曲線を取ります。欠点として1に近づくほど1そのものにならない性質があります。
f:id:Yaju3D:20170602002203p:plain

\displaystyle h(x) = \frac{1}{1+e^{-x}}

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

双曲線正接関数(Tanh)

双曲線正接関数は、シグモイド関数の線形変換であり、正にも負にも値を取り、原点を通ることから後に採用されるようになった。
f:id:Yaju3D:20180104203510p:plain

def tanh(x):
    return numpy.tanh(x)

ランプ関数(ReLU)

ランプ関数は、入力した値が0以下のとき0になり、1より大きいとき入力をそのまま出力します。
シグモイド関数や双曲線正接関数は、原点から遠ざかる(値の絶対値が大きい)ほど、勾配が無くなるため、一度ユニットが大きな値を持ってしまったら学習が停滞する問題がある。ランプ関数では勾配消失問題が経験的に解消されることが知られている。
ReLUの派生として、Leaky ReLU、PReLU、RReLU、SReLU がある。
f:id:Yaju3D:20180104202547p:plain

def relu(x):
  return np.maximum(0, x)

区分線形凸関数(Maxout)

他のディープラーニングの手法とは異なり、活性化関数自体を学習するという、少し特殊な手法です。実験による精度はReLU より表現力が高いです。多数の線形関数のmax(任意の閾値関数を近似)するため、計算時間が増えます。
数式で書き下す Maxout Networks

f:id:Yaju3D:20180106130542p:plain

ソフトマックス関数(Softmax)

ソフトマックス関数は、中間層(隠れ層)ではなく出力層にて分類問題の多値分類で使用される。

ソフトマックス関数の特徴は、すべての出力の総和が1に調整されることだ。これにより、各出力を確率として考えることができる。例えば入力画像が①犬なのか、②猫なのか、③パンダなのかを判定するニューラルネットワークを設計したとする。ソフトマックス関数を介する前①0.3、②2.9、③4.0という「真である可能性の高さ」を持っていた場合、単に「画像はパンダである」と答えることができる。しかし、ソフトマックス関数を介すると出力は①0.018、②0.245、③0.737というように出て、「画像は73.7%の確率でパンダである。」と言える。
#4 ソフトマックス関数による出力層の設計 | | casaLiz, Ltd ->[リスの家] .

\displaystyle y_k = \frac{exp(a_k)}{\sum_{i=1}^n exp(a_i)}

def softmax(a):
  exp_a = np.exp(a)
  sum_exp_a = np.sum(exp_a)
  y = exp_a / sum_exp_a
  return y
オーバーフロー対策

上記コードの実装は理論上は正確だが、実際に動かした場合に入力信号の値が大きいと指数関数の値が大き過ぎてオーバーフローしてしまう。 e^{10} は、20,000を超え、 e^{100} は 0 が40 個以上も並ぶ大きな値になり、e^{1000} では桁が大きすぎてコンピュータが無限大(inf)と解釈してしまう。このオーバーフローを避けるには、計算結果の理論値を変えずに計算途中の数字の絶対値を小さくする工夫が要る。

改善したのが次の数式となります。
\displaystyle y_k = \frac{Cexp(a_k)}{C\sum_{i=1}^n exp(a_i)}

\displaystyle \quad = \frac{exp(a_k + logC)}{\sum_{i=1}^n exp(a_i + logC)}

\displaystyle \quad = \frac{exp(a_k + C')}{\sum_{i=1}^n exp(a_i + C')}

任意の定数 C を分子と分母の両方に掛けています(分母と分子の両方に同じ定数を乗算しているため、同じ計算を行っていることになります)。そして、指数関数(exp)の中に移動させ、logC とします。最後にlogCC’ という別の記号に置き換えます。
ソフトマックスの指数関数の計算時には何らかの定数を足し算(もしくは、引き算)しても結果は変わらない、ということです。
定数の値 C としては入力信号の中の最大の値を用いることが一般的。

def softmax(a):
  c = np.max(a)
  exp_a = np.exp(a – c)
  sum_exp_a = np.sum(exp_a)
  y = exp_a / sum_exp_a
  return y

改善前はnanでオーバーフローになるが、配列の中の最大値 1010 を引いた値で計算することでオーバーフローなく計算ができるようになる。

>>> a = np.array([1010, 1000, 990])
>>> np.exp(a) / np.sum(np.exp(a)) # ソフトマックス関数の計算
>>> array([  nan,  nan,  nan])
>>> c = np.max(a) # 1010
>>> a - c
array([  0,  -10,  -20])
>>> 
>>> np.exp(a - c) / np.sum(np.exp(a - c))
array([   9.99954600e-01,   4.53978686e-05,   2.06106005e-09])

損失関数

機械学習では学習時に、いかに答えに近い値になるように重みパラメータを調整(必要な補正量を定量的に示す)させます。その部分を担うのが損失関数となります。そして損失関数の最小値を探すことが学習のゴールとなる。
損失関数を設定する理由は、認識精度を指標するとパラメータの微分がほとんどの場所で 0 (動かなくなる)になるから。

二乗誤差(mean squared error)

\displaystyle E = \frac{1}{2}\sum_{k=1} (y_k - t_k)^2
def mean_squared_error(y,t):
    return 0.5 * np.sum((y-t)**2)

線形回帰で二乗和を2で割る理由について - デジタル・デザイン・ラボラトリーな日々

交差エントロピー誤差(cross entropy error)

交差エントロピー誤差は、正解の選択肢に対してのみ、出力と理想的な値との距離を計る。
計算式には全ての値が含まれるが、正解ラベルがone-hot表現されていれば不正解となる選択肢の項は自動的に全て消える。

\displaystyle E=\sum_{k=1} - t_k \log y_k
def cross_entropy_error(y, t) :
    delta = le-7
    return -np.sum(t*np.log(y+delta))

例えば出力 y はクラスが5つの場合次のような列になります。
[0.1, 0.3, 0.8, 0.2, 0,1]   正解のクラスが、1つ目は3(0.8)で2つ目は2(0.3)であるとき、教師データはそれぞれ次のようになります。
one-hot [0, 0, 1, 0, 0] ラベル [3]

出力 y と one-hot表現を掛けた場合
[log(0.1), log(0.3), log(0.8), log(0.2), log(0,1)] と one-hot [0, 0, 1, 0, 0]
3つ目の値以外は 0 を掛けるために 0 になってしまい、log (0.8) だけが出てきて、計算結果は -log(0.8) になります。

損失関数なので、例えばニューラルネットワークA(0.8)とニューラルネットワークB(0.05)のとき、同じ正解でも結果が大きい値(0.8)になった場合、重みパラメータを補正する必要があることを示している。

最後に

前回、勾配降下法の計算確認にて損失関数を求めた、正解に近づくにつれ値が小さくなること分かったが、その計算結果を使って何かしたわけではない。次回の誤差伝播法では損失関数の計算結果を使用するんだと思う。