【笔记】深度学习入门:基于Python的理论与实现(三)

误差反向传播法

一 个能够高效计算权重参数的梯度的方法

计算图

正向传播

太郎在超市买了 2 个 100 日元一个的苹果,消费税是 10%,请计 算支付金额。
在这里插入图片描述

反向传播(导数)

如果苹果的价格增加某个微小值, 则最终的支付金额将增加那个微小值的 2.2 倍
在这里插入图片描述

链式法则(可以了解下复合函数的链式求导法则)

反向传播的计算顺序是,将信号 E 乘以节点的局部导数 ( ),然后将结果传递给下一个节点
在这里插入图片描述

复合函数链式求导法则,这里略。

链式法则和计算图

在这里插入图片描述

反向传播

加法节点的反向传播将上游的值原封不动地输出到下游
在这里插入图片描述
乘法的反向传播会乘以输入信号的翻转值
在这里插入图片描述
简单例子:
在这里插入图片描述

简单层的实现

乘法层的实现

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 # 翻转x和y 
		dy = dout * self.x
		return dx, dy

例子:
在这里插入图片描述

# 正向传播
apple = 100 
apple_num = 2 
tax = 1.1
# layer
mul_apple_layer = MulLayer() 
mul_tax_layer = MulLayer()
# forward
apple_price = mul_apple_layer.forward(apple, apple_num) 
price = mul_tax_layer.forward(apple_price, tax)
print(price) # 220

# 反向传播
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price) 
print(dapple, dapple_num, dtax) # 2.2 110 200

加法层的实现

激活函数层的实现

ReLU 层

ReLU表达式、导数式、计算图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

python实现:

class Relu:
	def __init__(self):
		# 由 True/False 构成的 NumPy 数组
		self.mask = None
	def forward(self, x): 
		# 根据是否x<=0将x数组转化成True/False数组
		self.mask = (x <= 0) 
		out = x.copy() 
		# 是True的也就是x<=0的就输出0,其他的不变
		out[self.mask] = 0
		return out
	def backward(self, dout): 
		# 若为true则表示x<=0此时输出0 其他的不变
		dout[self.mask] = 0 
		dx = dout
		return dx

Sigmoid 层

表达式、计算图
在这里插入图片描述
在这里插入图片描述

其中一些注意的地方

  • “/”节点表示 y = 1/x,它的导数表达式为: 在这里插入图片描述
  • “exp”节点表示y = exp(x),导数仍为exp(x)
  • 反向的输出结果可以进行简化推导:在这里插入图片描述

可以得到完整的计算图为:
在这里插入图片描述
简化为:
在这里插入图片描述
Python 实现 Sigmoid 层

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

Affine/Softmax 层的实现

Affine 层

神经网络的正向传播中进行的矩阵的乘积运算在几何学领域被称为“仿射变换”(几何中,仿射变换包括一次线性变换和一次平移,分别对应神经网络的加权和运算与加偏置运算)。因此,这里将进行仿射变换的处理实现为“Affine 层”。
复习一下内积
在这里插入图片描述

Affine层计算图(3,) = (1,3):
在这里插入图片描述

批版本的 Affine 层

N批:
在这里插入图片描述
正向传播时,偏置会被加到每一个数据(第 1 个、第 2 个…)上。因此, 反向传播时,各个数据的反向传播的值需要汇总为偏置的元素

>>> dY = np.array([[1, 2, 3,], [4, 5, 6]]) 
>>> dY
array([[1, 2, 3],
		[4, 5, 6]])
>>> dB = np.sum(dY, axis=0) 
>>> dB
array([5, 7, 9])

python实现:

class 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):
		# .T转置
		dx = np.dot(dout, self.W.T) 
		self.dW = np.dot(self.x.T, dout) 
		self.db = np.sum(dout, axis=0)
		return dx

Softmax-with-Loss 层

Softmax 层。考虑到这里也包含作为损失函数的交叉熵误差(cross entropy error),所以称为“Softmax-with-Loss层”

计算图:
在这里插入图片描述
简化版:
在这里插入图片描述

由于(y1, y2, y3)是 Softmax 层的输出,(t1, t2, t3)是监督数据,所以(y1 − t1, y2 − t2, y3 − t3)是 Softmax 层的输 出和教师标签的差分

python实现:

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 = 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]
		# 批的大小(batch_size)
		dx = (self.y - self.t) / batch_size
		return dx

误差反向传播法的实现

在步骤2(计算损失函数关于各个权重参数的梯度)计算梯度中,数值微分虽然实现简单,但是计算要耗费较多的时间。和需要花费较多时间的数值微分不同,误差反向传播法可以快速高效地计算梯度。

对应误差反向传播法的神经网络的实现

主要在于这里使用了层。通过使用层,获得识别结果的处理(predict())和计算梯度的处理(gradient())只需通过层之间的传递就能完成

import sys, os
sys.path.append(os.pardir)
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient from collections import OrderedDict
class TwoLayerNet:
	def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
  		# 初始化权重 
  		self.params = {} 
  		self.params['W1'] = weight_init_std * \ np.random.randn(input_size, hidden_size)
		self.params['b1'] = np.zeros(hidden_size)
		self.params['W2'] = weight_init_std * \ np.random.randn(hidden_size, output_size)
		self.params['b2'] = np.zeros(output_size)
		# 生成层
		# OrderedDict 是有序字典,“有序”是指它可以记住向字典里添加元素的顺序
		self.layers = OrderedDict() 
		self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1']) 
		self.layers['Relu1'] = Relu() 
		self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2']) 
		self.lastLayer = SoftmaxWithLoss()
	def predict(self, x):
		for layer in self.layers.values(): 
			x = layer.forward(x)
		return x
	# x:输入数据, t:监督数据 
	def loss(self, x, t):
		y = self.predict(x)
		return self.lastLayer.forward(y, t)
	def accuracy(self, x, t):
		y = self.predict(x)
		y = np.argmax(y, axis=1)
		if t.ndim != 1 : t = np.argmax(t, axis=1)
			accuracy = np.sum(y == t) / float(x.shape[0])
			return accuracy
	# 通过数值微分计算关于权重参数的梯度 x:输入数据, t:监督数据
	def numerical_gradient(self, x, t):
		loss_W = lambda W: self.loss(x, t)
		grads = {}
		grads['W1'] = numerical_gradient(loss_W, self.params['W1']) 
		grads['b1'] = numerical_gradient(loss_W, self.params['b1']) 
		grads['W2'] = numerical_gradient(loss_W, self.params['W2']) 
		grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
		return grads
	# 通过误差反向传播法计算关于权重参数的梯度
	def gradient(self, x, t):
		# forward 
		self.loss(x, t)
		# backward
		dout = 1
		dout = self.lastLayer.backward(dout)
		layers = list(self.layers.values()) layers.reverse()
		for layer in layers:
			dout = layer.backward(dout)
		# 设定
		grads = {}
		grads['W1'] = self.layers['Affine1'].dW 
		grads['b1'] = self.layers['Affine1'].db 
		grads['W2'] = self.layers['Affine2'].dW 
		grads['b2'] = self.layers['Affine2'].db
		return grads

误差反向传播法的梯度确认

确认数值 微分求出的梯度结果和误差反向传播法求出的结果是否一致(严格地讲,是 非常相近)的操作称为梯度确认(gradient check)

python代码实现:

import sys, os sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist from two_layer_net import TwoLayerNet
# 读入数据
(x_train, t_train), (x_test, t_test) = \ load_mnist(normalize=True, one_ hot_label = True)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10) 
x_batch = x_train[:3]
t_batch = t_train[:3]
grad_numerical = network.numerical_gradient(x_batch, t_batch)
grad_backprop = network.gradient(x_batch, t_batch)
# 求各个权重的绝对误差的平均值
for key in grad_numerical.keys():
	diff = np.average( np.abs(grad_backprop[key] - grad_numerical[key]) ) 
	print(key + ":" + str(diff))

使用误差反向传播法的学习

# 通过误差反向传播法求梯度(在实现基础上的替换)
grad = network.gradient(x_batch, t_batch)

小结

• 通过使用计算图,可以直观地把握计算过程。
• 计算图的节点是由局部计算构成的。局部计算构成全局计算。
• 计算图的正向传播进行一般的计算。通过计算图的反向传播,可以计算各个节点的导数。
• 通过将神经网络的组成元素实现为层,可以高效地计算梯度(反向传播法)。
• 通过比较数值微分和误差反向传播法的结果,可以确认误差反向传播法的实现是否正确(梯度确认)。

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值