誤差関数(損失関数)

入力を適当なニューラルネットワークを順伝播した出力は当然狙ったものと大きな違いがあるだろう
その得られた出力と正解データの差を学習を通して縮めていくのが教師あり学習だ。

つまり、適当なニューラルネットワークから得られる入力 \boldsymbol{x}と出力 \boldsymbol{y}のペアが、入力 \boldsymbol{x}と正解データ \tilde{\boldsymbol{y}}に近づくように、重み \boldsymbol{W}やバイアス \boldsymbol{b}を変化させながら0に近づけていく必要がある。
このニューラルネットワークの出力と、正解データの差は誤差関数もしくは損失関数と表現する。今回は誤差関数で統一しようと思う。

この誤差関数も(その差が縮まるにつれて値が0に近づくならば)自分の好きなように設定していいのだが、一般的に使われる誤差関数というのは限られている。
今回は、分類(データの種類をよそくすること)ではなく、回帰(データの値を予測すること)でよく使われている二乗誤差関数というものを紹介する。

二乗誤差関数:  \displaystyle E(\boldsymbol{y},\tilde{\boldsymbol{y}}) = \frac{1}{2} (\boldsymbol{y} - \tilde{\boldsymbol{y}})^2 =\frac{1}{2} \sum_{j} (y_j - \tilde{y}_j)^2

まあ、見慣れた人にとってはよく見る式だろう。等式の二つ目はベクトルで、三つ目はベクトルの要素で表現した式である。
式の頭の1/2が気になった人もいるだろう。これは E(\boldsymbol{y},\tilde{\boldsymbol{y}})偏微分すると、
 \displaystyle \frac{E(\boldsymbol{y},\tilde{\partial \boldsymbol{y}})}{\partial y_i} = y_i-\tilde{y}_i
と上手く消えてくれるから慣習的につけているだけであって、特になくても問題ない。

なお、この結果から
 \displaystyle \nabla E(\boldsymbol{y},\tilde{\boldsymbol{y}}) = \left[ \begin{matrix} \frac{\partial E}{\partial  y_1} \\ \frac{\partial E}{\partial y_2}  \\ \vdots \\ \frac{\partial E}{\partial y_n} \end{matrix} \right] =\left[ \begin{matrix} y_1-\tilde{y}_1 \\ y_2-\tilde{y}_2  \\ \vdots \\ y_n-\tilde{y}_n \end{matrix} \right] =\boldsymbol{y}-\tilde{\boldsymbol{y}}
ということがわかる。この \nabla E(\boldsymbol{y},\tilde{\boldsymbol{y}})さえあれば、どの[\boldsymbol{y}]の要素で偏微分したとしても、すぐに取り出すことができる。

プログラムは次のようになる。短いのでまとめて書いた。

class ErrorFunction(object):
    def __init__(self, nOutput, errFunc='SqueredError'):
        self.errFunc=errFunc
        self.error=0
        self.diffError=np.array([0 for i in range(nOutput)])

    def calculate(self, outUnit,testData):
        trainData=np.array(trainData)
        if self.errFunc=='SqueredError':
            self.error=0.5*np.sum((outUnit-trainData)**2)

    def calcDiff(self, outUnit,trainData):
        trainData=np.array(trainData)
        if self.errFunc=='SqueredError':
            self.diffError=outUnit-trainData

難しいことはなにもないが、一応説明していこう。
まず、クラスErrorFunctionはnOutput(出力の個数)とerrFunc(誤差関数の種類)を引数に持つ。
今回は二乗誤差の一つしかないが、今後増える可能性があるのでこのように引数を取るよう設定しておこう。
このクラスがオブジェクト生成された時点で、errorとdiffErrorという変数が生成される。
これはそれぞれ、誤差関数の計算結果と、その勾配( \nabla E(\boldsymbol{y},\tilde{\boldsymbol{y}}))の値を格納する変数だ。

次にcalculateは誤差関数を計算する関数で、calcDiffはその勾配を計算する関数だ。
それぞれ引数に出力と正解データを入れれば勝手に計算してくれる。

さて、次はニューラルネットワークに学習させ、重みを少しずつ変えてこの誤差を減少させていこう。
そのためには誤差逆伝播法という、数式が多くなり理解が難しい、ちょっとした壁のような手法を学んでいく。
先に行っておくが、この誤差逆伝播法というのは、ディープラーニングの一つの高級なアルゴリズム、というわけではなく、単なる偏微分を漸化式的に楽に解くための手法と考えていい。逆に言えば、使わなくても理論的には問題ない。そう聞くとちょっと気が楽になるだろう。

もちろん、そのためには偏微分のちょっとした知識が必要なのだが、それはまたそのときに説明しよう。