【读书笔记】深度学习入门(二)

误差的反向传播

误差的反向传播是正向传播的逆向推导,将误差的结果分摊到每个参数,作为其修改的依据。

计算图

请添加图片描述
计算图的优点:

  • 局部计算:无论全局设计多么复杂,每个步骤所要做的都是对象节点的局部计算
  • 计算图可以保存每个节点的计算结果
  • 计算图可以方向传播高效计算导数

每个节点的运算看作一个函数,最后结果是所有函数运算的复合结果

链式法则

如果某个函数由复合函数表示,则该复合函数的导数可以用复合函数的各个函数的导数的乘积表示

请添加图片描述

加法的反向传播

请添加图片描述

#加法层的实现
class AddLayer:
	def __init__(self):
		pass
	def forward(self, x, y):
		out = x + y
		return out
	def backward(self, dout):
		dx = dout * 1
		dy = dout * 1
		return dx, dy

乘法的反向传播

请添加图片描述

#乘法层的实现
class MulLayer:
	def __init__(self):
		self.x = None
		self.y = None
	def forward(self, x, y):
		self.x = x
		self.y = y
		out = x * y
		return out
	def backward(self, dout):
		dx = dout * self.y
		dy = dout * self.x
		return dx, dy

加法的反向传播与乘法的反向传播的不同之处

加法的反向传播只是将上游的值传给下游,并不需要正向传播输入信号。但是乘法的反向传播需要正向传播时的输入信号。所以实现乘法节点的反向传播时,需要保存正向传播的输入信号。

ReLU层函数的实现

请添加图片描述

def Relu:
	def __init__(self):
		self.mask = None
	def forward(self, x):
		self.mask = (x <= 0) #存的是bool值 x<=0为true,否则为false
		out = x.copy()
		out[self.mask] = 0 #将小于0的置为0
		return out
	def backward(self, y):
		dout[self.mask] = 0
		dx = dout
		return dx

sigmoid层函数的实现

请添加图片描述
下面的推导过程来源于百度
请添加图片描述

def Sigmoid:
	def __init__(self):
		self.out = None
	def forward(self, x):
		out = 1 / (1 + np.sxp(-x))
		self.out = out
		return out
	def backward(self, dout):
		dx = dout * (1.0 - self.out) * self.out
		return dx

Affine层

Affine层:计算加权信号的总和加偏置,计算仿射变换(包括一次线性变换和一次平移,对应神经网络的加权和偏置运算)的层

推导式
请添加图片描述
Affine层计算图
请添加图片描述

批处理版本的Affine层

输入的x为多维

请添加图片描述

#Affine层的实现
def Affine:
	def __init__(self, w, b):
		self.w = w
		self.b = b
		self.x = None
		self.dw = None
		self.db = None
	def forward(self, x):
		self.x = x
		out = np.dot(x, self.w) + self.b
		return out
	def backward(self, dout):
		dx = np.dot(dout, self.w.T)
		self.dw = np.dot(self.x.T, dout)
		self.db = np.sum(dout, axis = 0) #对第0轴方向上的数据求和
		return dx

Softmax-with-Loss层

Softmax函数和交叉熵误差层

神经网络的处理过程有两种:学习和推理,推理层不需要softmax层,因为softmax是对Affine层结果的处理,推理在只有一个输出的情况下只需要知道所有输出结果中的最大值,Affine层已经足够,而学习阶段则需要softmax层。

请添加图片描述

图中最后反向传播的结果是{y1-t1, y2- t2, y3-t3},这个结果直接显示出了神经网络的输出和监督标签的误差,能得到这样的结果并不是偶然,而是为了得到这样的结果而设计的交叉熵误差函数。

class SoftmaxWithLoss:
	def __init(self):
		self.loss = None
		self.y = None
		self.t = None
	def forward(self, x, t):
		self.t = t
		self.y = 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

与学习有关的技巧

参数的更新

SGD的缺点

如果输入的函数的形状非均向,搜索路径会变得非常低效,因为梯度没有指向最小值的方向

Momentum
class Momentum:
	def __init__(self, lr = 0.01, momentum = 0.9):
		self.lr = lr
		self.momentum = momentum #相当于物理中的阻力,承担减小速度的任务
		self.v = None
	def update(self, params, grads):
		if self.v is None:
			self.v = {}
			for key, val in params.items():
				self.v[key] = np.zeros_like(val)
			
		for key in params.keys():
			self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
			params[key] += self.v[key] 
		

x轴上的受力非常的小,但是一直在同一个方向上受力,反过来y虽然受的力很大,但是方向却会发生改变,它们会相互抵消,所以y轴上的速度不稳定。这样可以减弱方向不均匀带来的影响。

AdaGrad

基于学习率衰减的思想,参数的元素中变动较大的元素的学习率将变小,也就是说,可以调整学习的幅度。

class AdaGrad:
	def __init__(self, lr = 0.01):
		self.lr = lr
		self.h = None
	def update(self, params, grads):
		if self.h is None:
			self.h = {}
			for key, val in params.items():
				self.h[key] = np.zeros_like(val)
		for key in params.keys():
			self.h[key] += grads[key] * grads[key]
			params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
		
Adam
  1. 组合了前俩个方法的优点
  2. 偏置校正

Adam的3个超参数:

  1. 学习率α
  2. 一次momentum系数 β1,标准设定值为0.9
  3. 二次momentum系数 β2,标准设定值为0.999
比较

以上3个方法各有特点,但是没有可以在所有问题中都表表现良好的方法,因此,应该根据具体的场景和实验结果选择合适的方法。
请添加图片描述

权重的初始化

权重的初始值非常重要。

随着层次加深,梯度消失的问题可能会更加严重。

  • 对于sigmoid和tanh等S型函数,初始值使用Xavier初始值。Xavier是标准差为
    ( 1 / n ) \sqrt (1/n) ( 1/n)
    的高斯分布。
  • 对于Relu,权重初始值使用He初始值。He是
  • ( 2 / n ) \sqrt (2/n) ( 2/n)
    的高斯分布。
权重的初始值可以设置为0吗

答案是不可以,因为如果将权重的初始值设置为0,那么在误差的反向传播法中,所有的权重值都会进行相同的更新。因为输入层的权重为0,所以第2层的神经元中全部输入相同的值,这意味着反向传播的过程中,第二层的权重全部会进行相同的更新,因此,权重会被更新为相同的值,并拥有的对称的值,这使得神经网络拥有许多不同权重的意义丧失了

Batch Normalization

为了使各层拥有适当的广度,“强制性地”调整激活值地分布,具体而言,就是使数据地均值为0,方差为1 地正规化。

优点:

  • 可以使学习快速进行
  • 不那么依赖初始值
  • 抑制过拟合
    请添加图片描述

请添加图片描述
batch norm的计算图
请添加图片描述

正则化

过拟合

只能拟合训练数据,不能很好地拟合不包含在训练数据中的其他数据。

产生原因:

  • 模型拥有大量参数,表现力强
  • 训练数据少
权值衰减

对大的权重进行惩罚.

例如为损失函数加上权重的平方函数(L2范数)。

L2范数相当于各个元素的平方和。L1是各个元素的绝对值之和,L∞相当于各个元素中绝对值最大的那个。

Dropout

随机删除神经元,Dropout将集成学习的效果(模拟地)通过一个网络实现了。

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
			return x * self.mask
		else:
			return x * (1.0 - self.dropout_ratio)
	def backward(self, dout):
		return dout * self.mask

超参数的验证

比如各层的神经元的数量,batch的大小,参数更新时的学习率和权值衰减。如果这些超参数没有设置合适的值,模型的性能就会很差。

验证数据

用于调整超参数的数据。

超参数的最优化

逐渐缩小超参数的“好值”的存在范围。
步骤:

  1. 设置超参数的范围
  2. 从设定的超参数范围中随机取样
  3. 使用步骤1中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置得很小)
  4. 重复步骤1和步骤2,根据它们识别精度的结果,缩小超参数的范围

除上述方法之外,还可以使用贝叶斯最优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值