790 views
【1】やりたいこと
PyTorchや TensorFlowなどの機械学習ライブラリを使わない場合、ライブラリが提供してくれている機能を自力実装する必要がある。
「逆伝播で使う微分」シリーズの投稿は以下の通り。
(56) 逆伝播で使う tanh(x)の微分
(57) 逆伝播で使う sigmoid(x)の微分
(58) 逆伝播で使う ReLU(x)の微分
(59) 逆伝播で使う MSE(平均二乗誤差)の微分 ←今回
(60) 他クラス分類で使う Softmax
(61) 機械学習で多用されるネイピア数とは?
今回は、損失関数として使われる MSE(Mean Squared Wrror, 平均二乗誤差)の微分について見てみる。
【2】やってみる
1) MSEとは?
MSEは、予測のズレを数字で表す誤差の指標 として使われる。
機械学習では、出力層の出力値と正解値(=教師データ)の誤差 を評価するための 損失関数 として MSEを用いる。
MSEは、回帰問題でよく使われる。
MSEが小さいほど、予測が上手くいっているということ。
学習中はこの MSEを最小にするように重みを調整(=訓練、学習)していく。
2) MSE’の計算式
(1) 順伝播と逆伝播
■順伝播
ここでは、話を分かりやすくするために、出力層のニューロン数を 3個として説明する。(n個でも考え方は同じ)
下図のように、出力層の各ニューロンの出力値と期待値の偏差の二乗を積算し、出力層のニューロン数で平均したエラー値が MSEだ。

■逆伝播
下図のように、損失関数(ここでは MSE)で算出したエラー値を、各ニューロンについてそれぞれ偏微分した値(=勾配の初期値)を算出し、これを入力層に向って逆伝播する。

MSEとその導関数を一般化すると、以下のように書ける。

実際の実装時には、MSE x 1/2 した値を損失関数の値として使う。
これにより、導関数の分子の 2が消えてシンプルになる。
なぜこれが許されるのか?
どうせ勾配値に学習率を掛けてパラメータ調整するのだから、勾配の初期値が 1/2されようが 1/100されようが関係ない。
(2) MSEの導関数の証明
過去三回の投稿と同様に、しつこいほどに愚直に証明 してみる。
ここでは、上図の通り、出力層のニューロン数が 3個の場合を例に証明する。(n個でも考え方は同じ)
まず、誤差関数の Σ を展開すると以下の通り。

ここでは出力値が y1 のニューロンの勾配の初期値 g1 について考える。
このニューロンに着目する場合、MSEの計算式を ŷ1 について偏微分することになる。
すなわち、ŷ1 が存在しない項は定数項として無視できる。
・・・(1)
ここで、

として、式(1)を f(x) と g(x) の合成関数で表す。
・・・(2)
ここで毎度おなじみの 合成関数の連鎖律(=chain rule) の出番だ。

これに当てはめると、式(2)は以下のように書ける。

ここで関数 g(x) の導関数は下記の通りなので、勾配の初期値 g1 は以下のように書ける。
・・・(3)
関数 f(x) の導関数は以下の通り。

これを式(3)に代入すると、

OKだ! スッキリ
3) MSEの微分を Pythonで実装する。
def mse(y_true, y_pred):
"""
(1/2)付き平均二乗誤差を計算する
y_true: 正解値(numpy配列)
y_pred: 予測値(numpy配列)
"""
return 0.5 * np.mean((y_true - y_pred) ** 2)
def mse_derivative(y_true, y_pred):
"""
MSEの導関数((1/2)付きバージョンに対応)
y_true: 正解値(numpy配列)
y_pred: 予測値(numpy配列)
"""
n = y_true.shape[0]
return (1 / n) * (y_pred - y_true)
シンプルな実装に見えるが、ここには Python + Numpyの底力が隠れている。
numpyのベクトル化関数を使っているので、入力値 xは多次元配列(ndarray)を使える。
→ ミニバッチデータ等、複数データをまとめて処理できる。
※実際のデータ並列処理はライブラリ内部に隠蔽されている。
【3】微分記号の使い分け
常微分: 関数が 1つの変数だけに依存しているときは、以下のように書く。

偏微分(partial derivative): 多変数関数のうち、ある1つの変数に関する微分では ∂ (pertial) 記号を使って書く。

ニューラルネットワークでの 誤差の逆伝播など、微小な変化の場合には ∂ の代わりに δ (delta)を使う。
意味の区別で使い分けている。