首页 > 其他分享 >深度学习入门(鱼书)学习记录 - 第5章 误差反向传播法

深度学习入门(鱼书)学习记录 - 第5章 误差反向传播法

时间:2024-06-08 22:30:05浏览次数:12  
标签:学习 入门 self 传播 鱼书 反向 计算 price apple

前言:上一章通过数值微分计算神经网络的权重参数的梯度,这种方法比较简单但比较耗时。所以现在介绍另外一种比较高效的方法 -- 误差反向传播法

目录

计算图

举例

为什么用计算图求解

计算图的优点

链式法则

链式求导

反向传播

加法节点的反向传播

乘法节点的反向传播

简单层的实现

乘法层实现 

加法层的实现

激活函数层的实现

ReLU层

Sigmoid层

Affine/Softmax层的实现

Affine层

Softmax-with-Loss 层


计算图

计算图通过节点和箭头表示计算过程。

举例

问题1:太郎在超市买了2个100日元一个的苹果,消费税是10%,请计算支付金额

计算图改进:将苹果和消费税标在圆圈外面

问题2:太郎在超市买了2个苹果、3个橘子。其中,苹果每个100日元, 橘子每个150日元。消费税是10%,请计算支付金额

综上,用计算图解题的情况下,需要按如下流程进行。

1.构建计算图。

2.在计算图上,从左向右进行计算。

第2步 从左往右计算的过程就是正向传播,反之从右往左是反向传播。

前面计算图中 各个节点处的计算都是局部计算,只需进行与自己有关的计算,不用考虑全局。

为什么用计算图求解

使用计算图最大的原因是,可以通过反向传播高效计算导数

上述问题1补充 求“支付金额关于苹果的价格的导数”,这个导数的值表示当苹果的价格稍微上涨时,支付金额会增加多少

图5-5 反向传播使用与正方向相反的箭头(粗线)表示。反向传播传递“局部导数”,将导数的值写在箭头的下方。在这个例子中,反向传播从右向左传递导数的值(1 → 1.1 → 2.2)。从这个结果中可知,“支付金额关于苹果的价格的导数”的值是2.2。这意味着,如果苹果的价格上涨1日元, 最终的支付金额会增加2.2日元(严格地讲,如果苹果的价格增加某个微小值, 则最终的支付金额将增加那个微小值的2.2倍)。

计算图的优点

可以通过正向传播和反向传播高效地计算各个变量的导数

链式法则

假设存在 y = f(x)的计算,这个计算的反向传播如图5-6所示

反向传播的计算顺序是,将信号E乘以节点的局部导数  \frac{\partial y}{\partial x},然后将结果传递给下一个节点。

链式求导

计算图的反向传播是基于链式法则成立的。

复合函数求导一般用链式法则,这里是高数基础知识就不赘述了。比如  z = (x+y){_{}}^{2}可以看作

z = t{_{}}^{2} 和 t = x + y两个式子构成。求z对x的导数 \frac{\partial z}{\partial x} = \frac{\partial z}{\partial t} \frac{\partial t}{\partial x} = 2t \cdot 1 = 2(x+y) 

用计算图表示如下,**2表示平方运算。

反向传播

加法节点的反向传播

以 z = x + y 为例观察其反向传播,\frac{\partial z}{\partial x} = 1,\frac{\partial z}{\partial y} = 1  用计算图表示。

加法节点的反向传播只是将输入信号输出到下一个节点。例如 15 = 10 + 5,反向传播会从上游传来1.3

乘法节点的反向传播

z = xy 对x和y分别求导 \frac{\partial z }{\partial x} = y, \frac{\partial z }{\partial y} = x

乘法的反向传播会将上游的值乘以正向传播时的输入信号的“翻转值” 后传递给下游。翻转值表示一种翻转关系,如图5-12所示,正向传播时信号 是x的话,反向传播时则是y;正向传播时信号是y的话,反向传播时则是x。

其中 6.5 = 1.3 * 5, 13 = 1.3 * 10

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

简单层的实现

python实现上述购买苹果的例子,计算图的乘法节点称为“乘法层”(MulLayer),加法节点称为“加法层” (AddLayer)。

乘法层实现 

例如 z = xy

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

backward()将从上游传来的导数(dout)乘以正向传播的翻转值,然后传给下游。

买苹果例子的正向和反向传播代码实现如下

apple = 100
apple_num = 2
tax = 1.1

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)

# backward
dprice = 1
dapple_price, dtax = mul_tax_layer.backward(dprice)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)

print("price:", int(price))
print("dApple:", dapple)
print("dApple_num:", int(dapple_num))
print("dTax:", dtax)

执行结果和上图黑色加粗箭头表示的数字一致 

 price: 220
dApple: 2.2
dApple_num: 110
dTax: 200

加法层的实现

以 z = x + y 为例,backward()将上游传来的导数(dout)原封不动地传递给下游

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

python实现上面5-17的计算图,这里有3个乘法1个加法对应3个乘法层和1个加法层的实例。

虽然代码比较长,但是逻辑简单,按顺序传入参数。

apple = 100
apple_num = 2
orange = 150
orange_num = 3
tax = 1.1

# layer
mul_apple_layer = MulLayer()
mul_orange_layer = MulLayer()
add_apple_orange_layer = AddLayer()
mul_tax_layer = MulLayer()

# forward
apple_price = mul_apple_layer.forward(apple, apple_num)  # (1)
orange_price = mul_orange_layer.forward(orange, orange_num)  # (2)
all_price = add_apple_orange_layer.forward(apple_price, orange_price)  # (3)
price = mul_tax_layer.forward(all_price, tax)  # (4)

# backward
dprice = 1
dall_price, dtax = mul_tax_layer.backward(dprice)  # (4)
dapple_price, dorange_price = add_apple_orange_layer.backward(dall_price)  # (3)
dorange, dorange_num = mul_orange_layer.backward(dorange_price)  # (2)
dapple, dapple_num = mul_apple_layer.backward(dapple_price)  # (1)

print("price:", int(price))
print("dApple:", dapple)
print("dApple_num:", int(dapple_num))
print("dOrange:", dorange)
print("dOrange_num:", int(dorange_num))
print("dTax:", dtax)

计算的结果如下,和上图黑色加粗箭头表示的数字一致 

price: 715
dApple: 2.2
dApple_num: 110
dOrange: 3.3000000000000003
dOrange_num: 165
dTax: 650

激活函数层的实现

ReLU层

y是分段函数,这里根据导数定义 求x = 0处的导数 ,因为x = 0处左右两边的导数值不同,所以x = 0处应该不可导。这里不知道原作者是如何考虑的,有同样疑问的小伙伴欢迎留言。

图5.8 如果正向传播时的输入x大于0,则反向传播会将上游的值原封不动地传给下游;如果正向传播时的x小于等于0,则反向传播中传给下游的信号将停在此处。计算图如下

python实现Relu层

class Relu:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        self.mask = (x <= 0)
        out = x.copy()
        out[self.mask] = 0 

        return out

    def backward(self, dout):
        dout[self.mask] = 0
        dx = dout

        return dx

mask是由True/False构成的NumPy数组,它会把正向传播时的输入x的元素中小于等于0的地方保存为True,其他地方(大于0的元素)保存为False。

out = x.copy()
out[mask] = 0
print(out)

out数组将mask数组中值为True的(x <= 0)元素设置为0 

[[1. 0.]
 [0. 3.]]

Sigmoid层

图5-19的计算图的反向传播

 

Sigmoid层的计算图进一步简化为下图

Sigmoid层代码实现: 

class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        out = sigmoid(x)
        self.out = out
        return out

    def backward(self, dout):
        dx = dout * (1.0 - self.out) * self.out

        return dx

Affine/Softmax层的实现

前情回顾:使用Numpy的np.dot()实现矩阵的乘法

这里,X、W、B 分别是形状为(2,)、(2, 3)、(3,)的多维数组。这样一 来,神经元的加权和可以用Y = np.dot(X, W) + B计算出来。然后,Y 经过激活函数转换后,传递给下一层。这就是神经网络正向传播的流程。

注意:X和W 的乘积必须使对应维度的元素个数一致

Affine层

神经网络的正向传播中进行的矩阵的乘积运算在几何学领域被称为“仿射变换”。因此,这里将进行仿射变换的处理实现为“Affine层”

注意X、W、B是矩阵(多维数组),各个结点之间传播的是矩阵

推导后得到下面两个式子

标签:学习,入门,self,传播,鱼书,反向,计算,price,apple
From: https://blog.csdn.net/chinn_1234/article/details/139528282

相关文章

  • Wireshark分析两台设备在不同子网进行内ping包——学习篇
    最近在读一本关于网络抓本和协议分析的一本书——《Wireshark网络分析就这么简单》书中有一篇章节为——从一道面试题开始说起问题:两台服务器A和B的网络配置如下,B的子网掩码本应该是255.255.255.0,却不小心配置成了255.255.255.224.它们还能正常通信吗?Aip地址:192.168.26.129......
  • MathType7.8永久破解版下载 让数学学习变得简单有趣!
    大家好,我是科技评论家。今天给大家推荐一款非常实用的数学公式编辑器——MathType7.8!......
  • 0004python金融量化初入门
    >Date:2024.04.24>Keywords:在量化投资(证券和比特币)开源项目里,全球star数排名前10位里面,有7个是Python实现的。从数据获取到策略回测再到交易,覆盖了整个业务链。而全球注册用户数最多的商业量化平台Uqer优矿,也同样是基于Python实现和提供服务的。国内后来的其他量化平台,例如ricequ......
  • 搭建verilog/systemverilog学习环境
    目录仿真软件选择使用iverilog的基本步骤仿真软件选择学习verilog或者systemverilog过程中,使用那种仿真软件?当然最好是使用synopsys的vcs+verdi的组合,功能强大,而且大部分公司也使用synopsys的eda软件,如果熟练掌握vcs+verdi对以后工作中使用它们也是有很大帮助。但是这两个软件......
  • 机器学习--损失函数
    损失函数(LossFunction),也称为代价函数(CostFunction)或误差函数(ErrorFunction),是机器学习和统计学中的一个重要概念。它用于量化模型预测值与真实值之间的差异。损失函数的值越小,表示模型的预测越准确。损失函数的定义损失函数根据具体的任务和目标会有所不同。常见的损失......
  • kettle从入门到精通 第六十六课 ETL之kettle kettle阻塞教程,轻松获取最后一行数据,so e
    场景:ETL沟通交流群内有小伙伴反馈,如何在同步一批数据完成之后记录下同步结果呢?或者是调用后续步骤、存储过程、三方接口等。解决:使用步骤Blockingstep进行阻塞处理即可。1、下面的demo演示从表t1同步数据至表t2(t1表中有三条数据,t2为空表,两个表表结构相同),然后数据同步完毕之后进......
  • 替罪羊树学习笔记
    替罪羊树学习笔记史!思想众所周知,替罪羊树是一种平衡二叉搜索树,其拥有虽然我不理解为什么,但是很牛的复杂度。其思想在于通过一个系数进行定期重构,使得维护所有信息的树是一棵接近平衡树的伪平衡树,那么他依然拥有\(O(\logn)\)级别的层高,因此对于跳转查询依旧具有优异的复杂度......
  • Python学习日记Day1
    目录一、Python的安装二、输出print()1,输出单变量后换行——print(*)2,连续输出多个变量不换行——print(*,*,*,*,*)用英文逗号分离3,使用ASCII码进行输出——借助chr()函数4,使用Unicode码进行输出——借助ord()函数 5,ASCII码与Unicode码的相互转换6,使用print()函数将内......
  • 后缀数组学习笔记
    1.前置知识:基数排序1.1.思想现有如下序列:3,44,38,5,47,15,36,32,50,现在要用基数排序算法排序,要怎么做?基数排序的初始状态如下:按照个位将原序列中的数分组,放入对应的集合将分好的数按照个位的顺序取出,得到:将序列中的数重新按照十位分组,放入对应集合:将每一位上......
  • 第一篇 Markdown学习
    第一篇Markdown语法归纳Markdown官方文档Typora安装教程(来自CSDN大佬)标题一级标题二级标题三级标题四级标题五级标题六级标题标题一标题二字体样式加粗文本加粗文本删除线斜体斜体斜体加粗斜体加粗引用引用图片分割线超链接我的博客列表AB......