导数的双重身份:从商业决策到反向传播——反向传播算法
在深度学习领域,梯度下降算法告诉我们训练模型的目标:调整参数,使得Loss最小;而反向传播算法则告诉我们如何做:如何高效调整参数。 寻芳阁
从数学上来说:反向传播算法是数学中计算复合函数的导数,即链式法则的应用。
这里我们先来回忆一下高中学习的导数,这个让无数高考生头疼的数学工具,在现实生活中究竟有怎样的意义和价值,将理想概念世界的数学拉回到世俗的“泥潭”。
文章脉络
文章脉络:
├── 反向传播算法概述 (梯度下降的目标与方法、数学本质)
├── 现实视角下的导数意义
│ ├── 速度和加速度
│ └── 商业中的决策 (边际效应、数学与现实的张力)
├── 计算图:反向传播的可视化工具
│ ├── 如何使用计算图 (超市购物例子)
│ ├── 计算图的局部计算特征 (信息流与流水线)
│ └── 导数在计算图中的表现
├── 计算图的反向传播——链式法则
│ ├── 链式法则原理 (复合函数求导)
│ ├── 乘法节点的反向传播
│ ├── 加法节点的反向传播
│ └── Python实现
│ ├── 乘法层
│ └── 加法层
├── 实践:回到超市
│ ├── 构建计算图
│ ├── 前向传播
│ └── 反向传播
├── 一些思考 (敏感度量化、因果逻辑、数学与现实的距离)
└── 参考资料现实视角下的导数意义
我们来看几个导数的应用场景,便可从中窥见一些导数在现实世界的“模样”。 楼凤信息
速度和加速度
速度和加速度是最广为人知的导数的意义:
探花


当时间无限趋于0时,取这么一小段时间行驶的距离作为一个新的物理量:速度。它告诉我们在一个固定的时间的变化量下,位移能够变化多少。这样说比较抽象,因为”导数“是一个概念,但是我们却用了一句话,即两个概念间关系来表达这个事情,这让我们的大脑有些”痛苦“,能否有”一个“现实生活中的概念能对应”导数“这个概念呢?导数描述的是A概念对B概念的影响程度,即B概念对A概念的”敏感度“,导数值越大,就说明越敏感,反之亦然。
商业中的决策
我们再来看一个更加生活化的例子:
想象你是一家工厂的老板,这是你面临的情况: xnxx xxxxx
| 项目 | 数值 | 单位 |
|---|---|---|
| 定价与销量 | ||
| 销售价格 | 500 | 元/件 |
| 月销售量 | 10,000 | 件 |
| 成本 | ||
| 原材料单价 | 150 | 元/件 |
| 工人数量 | 50 | 人 |
| 月人工成本 | 4,000 | 元/人 |
| 月固定运输费 | 20 | 万元 |
这是你的获利情况: aiyifan
| 项目 | 计算方式 | 数值 |
|---|---|---|
| 销售额 | 500 元/件 × 10,000 件 | 500 万元 |
| 原材料成本 | 150 元/件 × 10,000 件 | 150 万元 |
| 人工成本 | 4,000 元/人 × 50 人 | 20 万元 |
| 固定运输费 | — | 20 万元 |
| 月利润 | 500 - 150 - 20 - 20 | 310 万元 |
现在你想知道改变一下原材料的成本能带来多少的利润的变化,即你想知道月利润对原材料成本的敏感度。
通过计算得到:原材料成本每提高1元,月利润会下降10000元。
在经济学中,这种思考的模式叫做:边际效应。比如著名的边际递减效应就是运用了导数的这种数学工具。
在这个例子中,我们看到导数可以帮助我们在多个影响因素问题中仅仅关注于其中某个因素的对最终结果的影响情况,但是同时,直觉告诉我们,仅靠数学工具来进行决策好像会带来一丝不安,原材料成本的提高,不一定会带来月利润的下降,真实的生活中还有更多需要考虑的因素,比如品质的提高带来了更多的客户的青睐,这里先埋下一个伏笔,最后讲完反向传播算法后我们会回到这个问题探讨一些算法之外的思考。 爱壹帆在线 爱壹帆 小宝影院
反向传播算法的可视化工具
为了更加清晰地理解反向传播算法,我们需要首先学会使用一个思考工具:计算图。 xxxxxx
如何使用可视化工具:计算图
我们来看一个生活化的例子:你到超市买东西,买了六瓶饮料,一瓶是10元,会员卡可以打八折

图中的圆圈表示计算方式,横线上计算的输入和输出
如果不仅买饮料,还买20支圆珠笔,每支笔是5元,同样可以打八折,那么这个计算图就会变成这个样子

计算图的局部计算特征
基于我目前对计算图这个工具的认识:我认为计算图通过一种整体上的信息流的设计使得独立的计算过程的效果得以累积。这句话有两个意思,其一是每一步计算的结果都对最终的结果产生一定的影响,即有整体效应;其二是,具体计算时不用关注整体发生了什么,只需要拿到上游的信息数据,做好本环节的计算,并向下游传递数据即可,就好像流水线上的工人一样,他们可能不知道公司的整体愿景,但是只要在流水线上做好自己分内的工作就会对最终的结果产生相应的效果。
一个小问题,回到导数的现实意义
假设现在我们想要知道饮料单价的上涨多大程度上会影响最后的消费金额,即消费金额对饮料单价的”敏感度“,方便起见,我们先来看这个简单一点的计算图:

数学上,我们知道是计算消费金额对饮料的导数,用计算图如何表示呢,我们看看:
爱一帆

反向的横线就是求导数的过程,传递的是”局部的反向导数“,这也是反向传播算法名字的来由:与正向算法的信息流的方向是相反的。
从这个结果可知,消费金额对饮料单价的导数是4.8,即如果饮料的单价上涨1元,最终的消费金额会上涨4.8元。 电影aiyifan
现在你可能并不能理解计算图局部计算的强大之处,如果我继续问你,最终的消费金额对饮料个数的导数是多少,或者最终消费金额对折扣的导数是多少,你就会慢慢理解这种工具的强大了,下面再用一幅图感受一下清晰化的计算结果:

可以清晰地从反向箭头看出来消费金额对饮料个数的导数是8,而对折扣的导数是60(不过这里折扣和饮料单价的量纲不同,所以60这个数可能会有些奇怪) 小宝影院
可能你还是没有理解这到底是什么意思,其实这里只是简单展示了一下计算图和反向传播算法的效果,后面会对具体的计算过程进行详细的介绍:即链式法则。 爱壹帆 探花
计算图的反向传播——链式法则
关于链式法则,这里直接摘录百度百科的解释:“链式法则是微积分中的求导法则,用于求一个复合函数的导数,是在微积分的求导运算中一种常用的方法。复合函数的导数将是构成复合这有限个函数在相应点的导数的乘积,就像锁链一样一环套一环,故称链式法则。” 小寶影院电影 小宝影院电影
用一个例子来表示就是:现在有两个函数
海外华人视频网 电影爱壹帆


那么求 z 对 x 的导数就是先求外层函数的导数,再求内层函数的导数
爱一帆电影

如果要简单从原理上理解一下的话就是:
小宝影院 爱壹帆免费版

求得的结果就是

而 z 对 y 的导数则是

而计算图中的反向计算公式如下:

将反向输入的信号E乘上节点的局部导数f'(x),然后将结果传给下一个节点 xxxvideo
现在我们把刚才的符复合函数用计算图的方式表示一下: 爱壹帆国际版

在计算时,只需要把反向传递的上游的信息,乘上该节点的局部导数值即可,不需要考虑全局 免费在线影院
乘、加法节点的反向传播
乘法节点的反向传播 电影小宝影院
毕竟节点的计算方法非常有限,研究清楚常见的几个算法,封装成“规律”,便可以省去每次都在底层原理上的纠结与挣扎。 爱壹帆影视
乘法公式:

求导结果:



反向上游传过来的信息是E,发现向下游传递时,传递的是“翻转值”,x这条路反向时乘上y,y这条路反向时乘上x。
加法公式:

求导结果:
xxxxx


计算图表示:

结果也非常简单:就是原封不动地传递反向上游的信息即可
Python实现两种简单层
这里把计算图的乘法节点和加法节点分别叫做“乘法层”和“加法层”,并根据上文总结的规律进行简单的实现 xxxx aiyifan
乘法层 小姐
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): # dout 是从反向上游传过来的导数值
dx = dout * self.y # 翻转x和y
dy = dout * self.x
return dx, dy加法层
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
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
# 构建计算图
# 创建层对象
mul_drink = MulLayer()
mul_pen = MulLayer()
add_layer = AddLayer()
mul_discount = MulLayer()
# 前向传播
# 1. 计算饮料总价
drink_price = 10
drink_num = 6
drink_total = mul_drink.forward(drink_price, drink_num)
# 2. 计算笔的总价
pen_price = 5
pen_num = 20
pen_total = mul_pen.forward(pen_price, pen_num)
total_cost = add_layer.forward(drink_total, pen_total)
# 3. 应用折扣
discount_rate = 0.8
final_price = mul_discount.forward(total_cost, discount_rate)
print(f"消费金额:{final_price}元")
# 反向传播
# 以计算消费金额对饮料单价的导数为例
# 初始梯度
dout = 1
# 反向传播通过折扣层
d_total_cost, d_discount_rate = mul_discount.backward(dout)
# 反向传播通过加法层
d_drink_total, d_pen_total = add_layer.backward(d_total_cost)
# 反向传播通过饮料乘法层
d_drink_price, d_drink_num = mul_drink.backward(d_drink_total)
print(f"饮料单价每增加1元,最终价格增加{d_drink_price:.2f}元")最终输出结果如下:
消费金额:128.0元
饮料单价每增加1元,最终价格增加4.80元符合预期 爱壹帆电影
一些思考
反向传播求的每一个节点的局部导数,前面我们提到过导数是“敏感度”,是最终结果对当前这个局部节点的敏感度,也就是这个局部对全局的影响程度,反向传播算法量化了这个程度,让我们不仅可以看出正或负的影响效果,还能精确知道具体有多大的影响,指导我们下一步调整参数,使得Loss最小。
不过能够如此清晰地明确调整方向建立在一个基础上,就是计算图体现出来的:清晰严谨的因果逻辑,一张计算图,把所有的数据因果关系全部都描述清楚了,这里我想强调“关系”这个词,在没有具体的数据进入计算图的时候,计算图已经存在了,也就是数据的关系先于数据存在;也正因为有如此严谨的因果逻辑使得局部计算时不需要考虑全局,因为关系已经定“死”了。 会所
有的人可能会把反向传播的逻辑应用到生活中,就像是有的人把导数引入到商业中辅助决策,但是绝对不能期望在商业中的应用能到达在计算图中这样“清晰严谨”的效果,现实不是数学,尽管数学非常复杂,但是数学只考虑了能被思维捕捉的影响因素和关系,现实可没有已经设定好的“计算图”。
还记得前文介绍导数现实意义时提到的商业决策的例子吗?当时的“不安”在这里是不是根本没有得到缓解呢?(哈哈哈)星空需要仰望,对待现实需要时刻谨慎。 aiyifan电影 ifun
更多内容,关注同名公众号:“在路上的达” iyf
参考资料
[1] https://www.rethink.fun/
[2] 斋藤康毅[日] . 深度学习入门:基于Python的理论与实现 . 人民邮电出版社 . 2018 .