SGD的缺点
如果函数的形状非均向,搜索的路径就会非常低效,根本原因是梯度的方向没有指向最小值的方向
Momenrum
新增变量v,对应物理上的速度。
AdaGrad
新增变量h,保存了以前的所有梯度值的平方和
在更新参数时,通过乘以1/根号h,就可以调整学习的尺度
参数的元素中变动较大(被大幅更新)的元素学习率将变小
Adam
融合了Momentum和AdaGrad的方法,也进行超参数的“偏置校正”也是其特点。
设置三个超参数,一个是学习率,另外两个是momentum系数和二次momentum系数
结论:
根据不同的问题选择不同的方法,SGD在mnist数据集中学习的表现比其他三种方法慢
一般而言,与SGD相比,其他三种方法可以学习得更快,有时最终的识别精度也更高
权重的初始值设为0,在误差反向传播法中,所有的权重值都会进行相同的更新,并拥有对称的值(重复的值),为了防止“权重均一化”(为了瓦解权重的对称结构),必须随机生成初始值。
隐藏层的激活值的分布:
向一个五层神经网络(激活函数使用sigmoid函数)传入随机生成的输入数据,用直方图绘制各层激活值的数据分布。
# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def ReLU(x):
return np.maximum(0, x)
def tanh(x):
return np.tanh(x)
input_data = np.random.randn(1000, 100) # 1000个数据
node_num = 100 # 各隐藏层的节点(神经元)数
hidden_layer_size = 5 # 隐藏层有5层
activations = {} # 激活值的结果保存在这里
x = input_data
for i in range(hidden_layer_size):
if i != 0:
x = activations[i-1]
# 改变初始值进行实验!
w = np.random.randn(node_num, node_num) * 1 #权重初始值设为1
# w = np.random.randn(node_num, node_num) * 0.01
# w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)
a = np.dot(x, w)
# 将激活函数的种类也改变,来进行实验!
z = sigmoid(a)
# z = ReLU(a)
# z = tanh(a)
activations[i] = z
# 绘制直方图
for i, a in activations.items(): #item()方法把字典中每对key和value组成一个元组,并把这些元组放在列表中返回。
plt.subplot(1, len(activations), i+1)
'''fig.add_subplot(numrows, numcols, fignum) 三个参数,分别代表子图的行数,列数,图索引号。'''
plt.title(str(i+1) + "-layer")
if i != 0: plt.yticks([], []) #设置y轴的刻度
plt.hist(a.flatten(), 30, range=(0,1)) #hist柱状图,a.flatten():a是个数组,a.flatten()就是把a降到一维,默认是按行的方向降,bin=30:有30个柱
plt.show()
结果:
Xavier初始值
如果前一层的节点数为n,则初始值使用标准差为1/√ ̄n的分布
w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)
如果用tanh函数代替sigmoid函数,会呈吊钟型分布。
用作激活函数的函数最好具有关于原点对称的性质。
ReLu的权重初始值:
当激活函数使用ReLu函数时,一般推荐使用ReLu专用的初始值,也称He初始值
当前一层的节点数为n时,He初始值使用标准差为√ ̄(2/n)的高斯分布
Batch Normalization的算法:
优点:
- 可以使学习快速进行(可以增大学习率)
- 不那么依赖初始值(对于初始值不用那么神经质)
- 抑制过拟合(降低Dropout等的必要性)
思路:
调整各层的激活值分布使其适当的广度,为此,要向神经网络中插入对数据分布进行正规化的层。
以进行学习时的Mini-batch为单位,按Mini-batch进行正规化(使数据分布的均值为0,方差为1)
正则化:
过拟合发生的原因:
- 模型拥有大量参数、表现力强
- 训练数据少
权值衰减:
该方法通过在学习的过程中对大的权值进行惩罚,来抑制过拟合。
对于所有权重,权值衰减方法都会为损失函数加上1/2λW²,因此在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数λW
Dropout:
网络模型复杂时只用权值衰减就很难应对。
在学习过程中随机删除神经元的方法,训练时,随机选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例再输出。
import numpy as np
class Dropout:
def __init__(self, dropout_ratio=0.5):
self.dropout_ratio = dropout_ratio
self.mask = None
def forward(self, x, train_flg=True):
if train_flg:
self.mask = np.random.rand(*x.shape) > self.dropout_ratio #self.mask会随机生成和x形状相同的数组
return x * self.mask
else:
return x * (1.0 - self.dropout_ratio)
def backward(self, dout):
return dout * self.mask #每次正向传播时,self.mask都会以False的形式保存要删除的神经元
正向传播时传递了信号的神经元,反向传播时按原样传递信号,正向传播时没有传递信号的神经元,反向传播时信号将停在那里。
集成学习:
让多个模型单独进行学习,推理时再取多个模型的输出的平均值。这个集成学习与Dropout有密切的关系。这是因为可以将Dropout理解为,通过在学习过程中随机删除神经元,从而每一次都让不同的模型进行学习。并且,推理时,通过对神经元的输出乘以删除比例,可以取得模型的平均值。
超参数:
比如各层的神经元数量、batch大小、参数更新时的学习率或权值衰减等。
调整超参数时,必须使用超参数专用的确认数据。
用于调整超参数的数据一般称为验证数据。
(x_train, t_train),(x_test, t_test) = load_mnist()
#打乱训练数据
x_train, t_train = shuffle_dataset(x_train, t_train)
#分割验证数据
validation_rate = 0.20
validation_num = int(x_train.shape[0] * validation_rate)
x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[:validation_num]
t_train = t_train[:validation_num]
分割训练数据前,先打乱了输入数据和教师标签,这是因为数据集的数据可能存在偏向。
超参数的最优化:
- 设定超参数的范围
- 从设定的超参数范围中随机采样
- 使用步骤2中采样的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置的很小)
- 重复步骤2和步骤3(100次等),根据它们的识别精度的结果,缩小超参数的范围。