KDOG Notebook

どうも、古くからの友人です。

Batch Normalizationによる学習の効率化

Introduction

Batch Normalizationは各層でバッチごとの入力を正規化する手法である。これにより

  • 学習速度があがる(学習係数の値を大きく設定可能)

  • 初期値に依存しにくくなる

などの効果がある。今回は、MNISTを用いてその効果を確認した。 また、活性化関数についてSigmoidおよびReLu関数を用いた場合を比較した。

Implementation

Algorithm

$N$個のデータからなるミニバッチ$B = { x_{1}, \cdots ,x_{N} }$に対して、 平均$\mu_{B}$および分散 \sigma_{B}^2を求めて正規化を行う。 さらに、パラメータを用いて正規化したデータのスケールとシフトを実行する。

 \begin{eqnarray}
\mu_{B} \leftarrow \frac{1}{N} \sum_{i=1}^N x_i
\end{eqnarray}
 \begin{eqnarray}
\sigma_{B}^2 \leftarrow \frac{1}{N} \sum_{i=1}^N (x_i - \mu_B)^2
\end{eqnarray}
 \begin{eqnarray}
\hat{x}_i \leftarrow \frac{x_i - \mu_B}{\sqrt{\sigma_{B}^2 + \epsilon}}
\end{eqnarray}
 \begin{eqnarray}
y_i \leftarrow \gamma\hat{x}_i + \beta
\end{eqnarray}

Backpropagation

BatchNormの誤差逆伝搬は計算グラフを用いて導出できる。 詳細は文献[1]にゆずる。

f:id:kdog08:20170705234948p:plain:w600

順伝搬および逆伝搬からなるBatchNormクラスは以下の通り。

class BatchNorm:
    def __init__(self, dim):
        self.gamma = np.ones(np.prod(dim)).astype('float32')
        self.beta = np.zeros(np.prod(dim)).astype('float32')
        
        self.batch_size = None
        self.dgamma = None
        self.dbeta = None
    
    def forward(self, x):
        self.x_shape = x.shape
        if x.ndim != 2:
            x = x.reshape(x.shape[0], -1)
        
        mu = np.mean(x, 0)
        xc = x- mu
        var = np.mean(xc**2, 0)
        std = np.sqrt(var + 1e-6)
        xn = xc / std
        
        self.xc = xc
        self.xn = xn
        self.std = std
        self.batch_size = x.shape[0]
        
        out = self.gamma * xn + self.beta
        
        return out.reshape(self.x_shape)
    
    def backward(self, delta):
        if delta.ndim != 2:
            delta = delta.reshape(delta.shape[0], -1)
        
        dbeta = np.sum(delta, 0)
        dgamma = np.sum(self.xn * delta, 0)
        dxn = self.gamma * delta
        dxc1 = dxn / self.std
        dstd = -np.sum((self.xc * dxn) / (self.std * self.std), axis=0)
        dvar = 0.5 * dstd / self.std
        dxc2 = 2.0 * dvar / self.batch_size
        dx1 = dxc1 + dxc2
        dx2 = -np.sum(dx1, axis=0) / self.batch_size
        dx = dx1 + dx2
        
        self.dgamma = dgamma
        self.dbeta = dbeta
        
        return dx.reshape(self.x_shape)

Results

今回実験したモデルを以下の表にまとめた。いずれのモデルにおいても、入力層のユニット数は784個、隠れ層は300個で構成した。表のlrは学習係数(learning rate)を示している。

Name Construction Parameters
S3 Input->Dense->Dense->Softmax->Output Sigmoid, lr = 0.01
S4 Input->Dense->Dense->Dense->Softmax->Output Sigmoid, lr = 0.01
R6 Input->Dense->Dense->Dense->Dense->Dense->Softmax->Output ReLu, lr = 0.01
R6_lr Input->Dense->Dense->Dense->Dense->Dense->Softmax->Output Relu, lr = 0.1
S6_BN_lr Input->Dense->Dense->Dense->Dense->Dense->Softmax->Output Sigmoid, lr = 0.01
S6_BN Input->Dense->Dense->Dense->Dense->Dense->Softmax->Output Sigmoid, BatchNorm, lr = 0.5
R6_BN Input->Dense->Dense->Dense->Dense->Dense->Softmax->Output ReLu, BatchNorm, lr = 0.1

Batch normalization

BatchNormの有無による損失関数の変化を下の図に示す。まず、S3およびS4モデルより層を深くすることで学習が進まなくなっていることがわかった。S6_BN_lrより、層を深くしてもBatchNormを実行することで学習効率が大きく改善していることがわかった。さらに学習係数(lr)を0.01から0.5に大きくしても学習が進んでいることも分かった。

f:id:kdog08:20170705235019p:plain:w400

Activation function

ReLu関数を活性化関数として用いた場合の損失関数の変化を下図に示す。活性化関数にReLu関数を用いることで層を深くしても学習が進んでいることが確認できた(R6モデル)。活性化関数(ReLu)および学習係数(lr=0.1)を揃えた2つのモデル(R6_lr, R6_BN)を比較するとBatchNormを実行することでわずかながら学習効率の改善が見られた。

f:id:kdog08:20170705235042p:plain:w400

メモ

Batch Normalization

$(\gamma , \beta)=(1, 0)$とすると通常の正規化を実行することになる。したがって、$(\gamma , \beta)=(1, 0)$を初期値として与えるのが一般的であろう。学習パラメータとすることで、より学習が進むようなデータの分布を探すようになる。パラメータが増えることで過学習のリスクも増す?

Activation function

Sigmoid関数の傾きは最大で0.25である。したがって、層が深くなるにつれて浅い層に伝搬される誤差は非常に小さい値になる。これによって学習が進まなくなる。ReLu関数は$x>$0の領域で必ず傾きが1になるためSigmoid関数がもつ問題は解消される。

Unit value distribution per layer

各層(Affine, BatchNorm, Activation)のユニットが取る値の分布をS4およびS6_BN_lrモデルについて調べた。下図に順伝搬における出力の分布を示す。 S4モデルでは深い層ほどユニットが取る値が狭まってきている。一方、S6_BN_lrモデルでは層が深くなってもActivationのユニットが取る値は広がりを持っている。正規化によって関数の表現力が落ちていないことが確認できた。

  • S4モデル(上:Affine, 下:Activation)

f:id:kdog08:20170706031538p:plain:w300

  • S6_BN_lrモデル(上:Affine, 中央:BatchNorm, 下:Activation)

f:id:kdog08:20170706031613p:plain:w500

逆伝搬における出力の分布についても同様に調べた(下図)。S4モデルでは層が浅くなるにつれ伝搬される誤差が小さくなっていき、学習が進まない様子が確認できた。一方、S6_BN_lrモデルでは浅い層まで誤差が伝搬されていることがわかった。

  • S4モデル(上:Activation, 下:Affine)

f:id:kdog08:20170706040008p:plain:w300

  • S6_BN_lrモデル(上:Activation, 中央:BatchNorm, 下:Affine)

f:id:kdog08:20170706040040p:plain:w500

参考文献

[1] Frederik Kratzert “Understanding the backward pass through Batch Normalization Layer”

[2] 斎藤 康毅 「ゼロから作るDeep Learning」第6章, O'Reilly Japan, 2016.