如果為了使各層擁有適當(dāng)?shù)膹V度,“強制性”地調(diào)整激活值的分布會怎樣呢?實際上,Batch Normalization 方法就是基于這個想法而產(chǎn)生的
為什么Batch Norm這么惹人注目呢?因為Batch Norm有以下優(yōu)點:
- 可以使學(xué)習(xí)快速進行(可以增大學(xué)習(xí)率)。
- 不那么依賴初始值(對于初始值不用那么神經(jīng)質(zhì)) 。
- 抑制過擬合(降低Dropout等的必要性)。
Batch Norm的思路是調(diào)整各層的激活值分布使其擁有適當(dāng)?shù)膹V度。為此,要向神經(jīng)網(wǎng)絡(luò)中插入對數(shù)據(jù)分布進行正規(guī)化的層,即Batch Normalization層(下文簡稱Batch Norm層)
Batch Norm,顧名思義,以進行學(xué)習(xí)時的mini-batch為單位,按mini-batch進行正規(guī)化。具體而言,就是進行使數(shù)據(jù)分布的均值為0、方差為1的正規(guī)化。用數(shù)學(xué)式表示的話,如下:
這里對mini-batch的m個輸人數(shù)據(jù)的集合B求均值和方差。然后,對輸人數(shù)據(jù)進行均值為0、方差為1(合適的分布)的正規(guī)化。
這個式子所做的是將mini-batch的輸人數(shù)據(jù)變換為均值為0,方差為1的數(shù)據(jù)。通過將這個處理插入到激活函數(shù)的前面(或者后面),可以減少數(shù)據(jù)分布的偏向。
接著,Batch Norm層會對正規(guī)化后的數(shù)據(jù)進行縮放和平移的變換用數(shù)學(xué)式可以如下表示。
這里,γ和β是參數(shù)。一開始γ=1,β=0,然后再通過學(xué)習(xí)調(diào)整到合適的值。
上面就是Batch Norm的算法。這個算法是神經(jīng)網(wǎng)絡(luò)上的正向傳播。
用計算圖表示如下:
Batch Norm的反向傳播
Batch Norm實現(xiàn)類
class BatchNormalization:
"""
http://arxiv.org/abs/1502.03167
"""
def __init__(self, gamma, beta, momentum=0.9, running_mean=None, running_var=None):
self.gamma = gamma
self.beta = beta
self.momentum = momentum
self.input_shape = None # Conv層的情況下為4維,全連接層的情況下為2維
# 測試時使用的平均值和方差
self.running_mean = running_mean
self.running_var = running_var
# backward時使用的中間數(shù)據(jù)
self.batch_size = None
self.xc = None
self.std = None
self.dgamma = None
self.dbeta = None
def forward(self, x, train_flg=True):
self.input_shape = x.shape
if x.ndim != 2:
N, C, H, W = x.shape
x = x.reshape(N, -1)
out = self.__forward(x, train_flg)
return out.reshape(*self.input_shape)
def __forward(self, x, train_flg):
if self.running_mean is None:
N, D = x.shape
self.running_mean = np.zeros(D)
self.running_var = np.zeros(D)
if train_flg:
mu = x.mean(axis=0)
xc = x - mu
var = np.mean(xc**2, axis=0)
std = np.sqrt(var + 10e-7)
xn = xc / std
self.batch_size = x.shape[0]
self.xc = xc
self.xn = xn
self.std = std
self.running_mean = self.momentum * self.running_mean + (1-self.momentum) * mu
self.running_var = self.momentum * self.running_var + (1-self.momentum) * var
else:
# 算法實現(xiàn)
xc = x - self.running_mean
xn = xc / ((np.sqrt(self.running_var + 10e-7)))
out = self.gamma * xn + self.beta
return out
def backward(self, dout):
if dout.ndim != 2:
N, C, H, W = dout.shape
dout = dout.reshape(N, -1)
dx = self.__backward(dout)
dx = dx.reshape(*self.input_shape)
return dx
# 反向傳播
def __backward(self, dout):
dbeta = dout.sum(axis=0)
dgamma = np.sum(self.xn * dout, axis=0)
dxn = self.gamma * dout
dxc = dxn / self.std
dstd = -np.sum((dxn * self.xc) / (self.std * self.std), axis=0)
dvar = 0.5 * dstd / self.std
dxc += (2.0 / self.batch_size) * self.xc * dvar
dmu = np.sum(dxc, axis=0)
dx = dxc - dmu / self.batch_size
self.dgamma = dgamma
self.dbeta = dbeta
return dx
Batch Normalization的評估
現(xiàn)在我們使用Batch Norm層進行實驗。首先,使用MNIST數(shù)據(jù)集,觀察使用Batch Norm層和不使用Batch Norm層時學(xué)習(xí)的過程會如何變化,
代碼如下:
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 為了導(dǎo)入父目錄的文件而進行的設(shè)定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net_extend import MultiLayerNetExtend
from common.optimizer import SGD, Adam
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
# 減少學(xué)習(xí)數(shù)據(jù)
x_train = x_train[:1000]
t_train = t_train[:1000]
max_epochs = 20
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.01
def __train(weight_init_std):
bn_network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100], output_size=10,
weight_init_std=weight_init_std, use_batchnorm=True)
network = MultiLayerNetExtend(input_size=784, hidden_size_list=[100, 100, 100, 100, 100], output_size=10,
weight_init_std=weight_init_std)
optimizer = SGD(lr=learning_rate)
train_acc_list = []
bn_train_acc_list = []
iter_per_epoch = max(train_size / batch_size, 1)
epoch_cnt = 0
for i in range(1000000000):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
for _network in (bn_network, network):
grads = _network.gradient(x_batch, t_batch)
optimizer.update(_network.params, grads)
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
bn_train_acc = bn_network.accuracy(x_train, t_train)
train_acc_list.append(train_acc)
bn_train_acc_list.append(bn_train_acc)
print("epoch:" + str(epoch_cnt) + " | " + str(train_acc) + " - " + str(bn_train_acc))
epoch_cnt += 1
if epoch_cnt >= max_epochs:
break
return train_acc_list, bn_train_acc_list
# 3.繪制圖形==========
weight_scale_list = np.logspace(0, -4, num=16)
x = np.arange(max_epochs)
for i, w in enumerate(weight_scale_list):
print( "============== " + str(i+1) + "/16" + " ==============")
train_acc_list, bn_train_acc_list = __train(w)
plt.subplot(4,4,i+1)
plt.title("W:" + str(w))
if i == 15:
plt.plot(x, bn_train_acc_list, label='Batch Normalization', markevery=2)
plt.plot(x, train_acc_list, linestyle = "--", label='Normal(without BatchNorm)', markevery=2)
else:
plt.plot(x, bn_train_acc_list, markevery=2)
plt.plot(x, train_acc_list, linestyle="--", markevery=2)
plt.ylim(0, 1.0)
if i % 4:
plt.yticks([])
else:
plt.ylabel("accuracy")
if i < 12:
plt.xticks([])
else:
plt.xlabel("epochs")
plt.legend(loc='lower right')
plt.show()
運行結(jié)果如下:文章來源:http://www.zghlxwxcb.cn/news/detail-495608.html
從運行結(jié)果可以看到使用Batch Norm后,學(xué)習(xí)進行得更快了。
綜上,通過使用Batch Norm,可以推動學(xué)習(xí)的進行。并且,對權(quán)重初始值變得健壯(表示不那么依初始值) Batch Norm具備如此優(yōu)良的性質(zhì),一定能應(yīng)用在更多場合中。文章來源地址http://www.zghlxwxcb.cn/news/detail-495608.html
到了這里,關(guān)于【深度學(xué)習(xí)】5-3 與學(xué)習(xí)相關(guān)的技巧 - Batch Normalization的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!