end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

Affine層とSoftmax-with-Loss層の計算グラフとnumpy for python実装

github.com

「ゼロから作るDeep Learning ① (Pythonで学ぶディープラーニングの理論と実装)」 p.147~154の写経として、以下の朱書き部分を計算グラフ化します。

目次

Affine ReLU Affine Softmax input CrossEntropyError L

Affineレイヤ

Affine層とは「X・W + B = O」らしく、この計算グラフは以下の通りです。

朱書き部分は、微分による誤差逆伝播です。

W = ( ) w w w 11 12 13 w w w 21 22 23 T Y + L Y dot X W X W B (2,) (3,) (3,) (3,) (2,3) (3,) L Y (3,) L Y (3,) L W L Y (1,3) =X T (2,3) (2,1) L X L Y (3,2) W T (2,) (3,) = X・W + B = O (2,) (2,3) (3,) 対応する要素数を一致 W = ( ) w w w 11 12 13 w w w 21 22 23 (3,) ただし

さらに、これをミニバッチに対応さたたものが以下。

<sodipodi:namedview id="namedview2563" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:showpageshadow="2" inkscape:pageopacity="0.0" inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:document-units="mm" showgrid="false" inkscape:zoom="0.9888468" inkscape:cx="453.55863" inkscape:cy="521.31432" inkscape:window-width="2542" inkscape:window-height="1332" inkscape:window-x="2585" inkscape:window-y="473" inkscape:window-maximized="0" inkscape:current-layer="layer1" /> <inkscape:path-effect effect="bspline" id="path-effect4338" is_visible="true" lpeversion="1" weight="33.333333" steps="2" helper_size="0" apply_no_weight="true" apply_with_weight="true" only_selected="false" /> <inkscape:path-effect effect="bspline" id="path-effect4444" is_visible="true" lpeversion="1" weight="33.333333" steps="2" helper_size="0" apply_no_weight="true" apply_with_weight="true" only_selected="false" /> <inkscape:path-effect effect="bspline" id="path-effect6891" is_visible="true" lpeversion="1" weight="33.333333" steps="2" helper_size="0" apply_no_weight="true" apply_with_weight="true" only_selected="false" /> Y + L Y dot X W X W B (N,2) (N,3) (N,3) (3,) (2,3) (N,3) L Y (N,3) L Y (N,3) L W L Y (N,3) =X T (2,3) (2,N) L X L Y (3,2) W T (N,2) (N,3) =

以下は、Affine layerのpython実装

class Affine:
    def __init__(self, W, b):
        self.W =W
        self.b = b
        self.x = None
        self.original_x_shape = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.original_x_shape = x.shape
        x = x.reshape(x.shape[0], -1)
        self.x = x

        out = np.dot(self.x, self.W) + self.b
        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T) # W.Tはnumpyによる転置行列
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)
        # 変数前のアスタリスクは、入力値の分割
        dx = dx.reshape(*self.original_x_shape)
        return dx

Softmax-with-Lossレイヤ

画面幅の都合から、Softmax部分と、Cross Entropy Error部分を分けて記載します。

e a 1 y - t 1 1 + e a 2 y - t 2 2 e a 3 y - t 3 3 e e e ÷ a 1 e a 2 e a 3 e S 1/S 1/S -t1S -t2S -t3S -t1/e a 1 -t2/e a 2 -t3/e a 3 y 1 y 2 y 3 -t1/y1 -t2/y2 -t3/y3 Soft max

ln × ln y1 -t1 -t2 -t3 t1 ln y1 Cross Entropy Error y 1 y 2 y 3 -t1/y1 -t2/y2 -t3/y3 ln ln ln y2 ln y3 × × t1 t2 t3 + t2 ln y2 t3 ln y3 -1 × t1 ln y1+ t2 ln y2+ t3 ln y3 -1 1 L

以下は、python実装

import numpy as np

class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None # 損失
        self.y    = None # softmax の出力
        self.t    = None # 教師データ(one-hot vector)
    def forward(self, x, t):
        self.t = t
        self.y = self.softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)
        return self.loss
    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size
        return dx

    def softmax(self,x):
        x = x - np.max(x, axis=-1, keepdims=True)   # オーバーフロー対策
        return np.exp(x) / np.sum(np.exp(x), axis=-1, keepdims=True)

    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の場合、正解ラベルのindexへ
        if t.size == y.size:
            t = t.argmax(axis=1)
             
        batch_size = y.shape[0]
        return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size