• 手把手推导Back Propagation


    f04636ce638cef876577ebff05b43b1f.png

    撰文|月踏

    BP(Back Propagation)是深度学习神经网络的理论核心,本文通过两个例子展示手动推导BP的过程。

    1

    链式法则

    链式法则是BP的核心,分两种情况:

    1. 一元方程

    在一元方程的情况下,链式法则比较简单,假设存在下面两个函数:

    4d23b1459a87cad9eda2d7878016e07a.png

    那么x的变化最终会影响到z的值,用数学符号表示如下: 

    bc6dee9076cf842b5eea1d6a654234a2.png

    z对x的微分可以表示如下: 

    c58e773c60296a819be0b3a625fade5b.png

    2. 多元方程

    在多元方程的情况下,链式法则稍微复杂一些,假设存在下面三个函数: 

    98bc2ef8736ee93822d056e314fba73b.png

    因为s的微小变化会通过g(s)和h(s)两条路径来影响z的结果,这时z对s的微分可以表示如下: 

    2a9cbf9def85cda72b597a8d01f53d96.png

    这就是链式法则的全部内容,后面用实际例子来推导BP的具体过程。

    2

    只有一个weight的简单情况

    做了一个简单的网络,这可以对应到链式法则的第一种情况,如下图所示:

    f713cd2d08001d31896a413c585460f5.png4a876dfcb0155ffa81e45d5bd4faca1d.png

    图1

    其中圆形表示叶子节点,方块表示非叶子节点,每个非叶子节点的定义如下,训练过程中的前向过程会根据这些公式进行计算: 

    b89332d88ad187846d5d16c96188e1f3.png

    这个例子中,我们是想更新w1、b1、w2三个参数值,假如用lr表示learning rate,那么它们的更新公式如下: 

    58f9e03bb5783537a97d9adea380250b.png

    在训练开始之前,b1、w1、w2都会被初始化成某个值,在训练开始之后,参数根据下面两个步骤来进行更新:

    1. 先进行一次前向计算,这样可以得到y1、y2、y3、loss的值

    2. 再进行一次反向计算,得到每个参数的梯度值,进而根据上面的公式(13)、(14)、(15)来更新参数值

    下面看下反向传播时的梯度的计算过程,因为梯度值是从后往前计算的,所以先看w2的梯度计算: 

    a1dd0486dcee52b244d0a7f5e4978ba8.png

    再继续看w1的梯度计算: 

    2710a64b6a9abac86b068582d2d4c58c.png

    最后看b1的梯度计算: 

    6d18a7591b4be5b9af26789f91d09089.png

     把w2、w1、b1的梯度计算出来之后,就可以按照公式(13)、(14)、(15)来更新参数值了,下面用OneFlow按照图1搭建一个对应的网络做实验,代码如下:

    1. import oneflow as of
    2. import oneflow.nn as nn
    3. import oneflow.optim as optim
    4. class Sample(nn.Module):
    5. def __init__(self):
    6. super(Sample, self).__init__()
    7. self.w1 = of.tensor(10.0, dtype=of.float, requires_grad=True)
    8. self.b1 = of.tensor(1.0, dtype=of.float, requires_grad=True)
    9. self.w2 = of.tensor(20.0, dtype=of.float, requires_grad=True)
    10. self.loss = nn.MSELoss()
    11. def parameters(self):
    12. return [self.w1, self.b1, self.w2]
    13. def forward(self, x, label):
    14. y1 = self.w1 * x + self.b1
    15. y2 = y1 * self.w2
    16. y3 = 2 * y2
    17. return self.loss(y3, label)
    18. model = Sample()
    19. optimizer = optim.SGD(model.parameters(), lr=0.005)
    20. data = of.tensor(1.0, dtype=of.float)
    21. label = of.tensor(500.0, dtype=of.float)
    22. loss = model(data, label)
    23. print("------------before backward()---------------")
    24. print("w1 =", model.w1)
    25. print("b1 =", model.b1)
    26. print("w2 =", model.w2)
    27. print("w1.grad =", model.w1.grad)
    28. print("b1.grad =", model.b1.grad)
    29. print("w2.grad =", model.w2.grad)
    30. loss.backward()
    31. print("------------after backward()---------------")
    32. print("w1 =", model.w1)
    33. print("b1 =", model.b1)
    34. print("w2 =", model.w2)
    35. print("w1.grad =", model.w1.grad)
    36. print("b1.grad =", model.b1.grad)
    37. print("w2.grad =", model.w2.grad)
    38. optimizer.step()
    39. print("------------after step()---------------")
    40. print("w1 =", model.w1)
    41. print("b1 =", model.b1)
    42. print("w2 =", model.w2)
    43. print("w1.grad =", model.w1.grad)
    44. print("b1.grad =", model.b1.grad)
    45. print("w2.grad =", model.w2.grad)
    46. optimizer.zero_grad()
    47. print("------------after zero_grad()---------------")
    48. print("w1 =", model.w1)
    49. print("b1 =", model.b1)
    50. print("w2 =", model.w2)
    51. print("w1.grad =", model.w1.grad)
    52. print("b1.grad =", model.b1.grad)
    53. print("w2.grad =", model.w2.grad)

    这段代码只跑了一次forward和一次backward,然后调用step更新了参数信息,最后调用zero_grad来对这一轮backward算出来的梯度信息进行了清零,运行结果如下:

    1. ------------before backward()---------------
    2. w1 = tensor(10., requires_grad=True)
    3. b1 = tensor(1., requires_grad=True)
    4. w2 = tensor(20., requires_grad=True)
    5. w1.grad = None
    6. b1.grad = None
    7. w2.grad = None
    8. ------------after backward()---------------
    9. w1 = tensor(10., requires_grad=True)
    10. b1 = tensor(1., requires_grad=True)
    11. w2 = tensor(20., requires_grad=True)
    12. w1.grad = tensor(-4800.)
    13. b1.grad = tensor(-4800.)
    14. w2.grad = tensor(-2640.)
    15. ------------after step()---------------
    16. w1 = tensor(34., requires_grad=True)
    17. b1 = tensor(25., requires_grad=True)
    18. w2 = tensor(33.2000, requires_grad=True)
    19. w1.grad = tensor(-4800.)
    20. b1.grad = tensor(-4800.)
    21. w2.grad = tensor(-2640.)
    22. ------------after zero_grad()---------------
    23. w1 = tensor(34., requires_grad=True)
    24. b1 = tensor(25., requires_grad=True)
    25. w2 = tensor(33.2000, requires_grad=True)
    26. w1.grad = tensor(0.)
    27. b1.grad = tensor(0.)
    28. w2.grad = tensor(0.)

    3

    以conv为例的含有多个weights的情况

    用一个非常简单的conv来举例,这个conv的各种属性如下:

    9f1c9c8307320f585fb4c96ae51eb96f.png

    如下图所示:

    e0008abb16538b7d8508b74d7de070ce.png

    图2

    假定这个例子中的网络结构如下图:

    03eaae48fd0ff722b0335a51270efda2.png

    图3

    在这个简单的网络中,z节点表示一个avg-pooling的操作,kernel是2x2,loss采用均方误差,下面是对应的公式:

    3c9dd4fa8ecfc54b1561352998171d3b.png

    前传部分同上一节一样,直接看反传过程,目的是为了求w0、w1、w2、w3的梯度,并更新这四个参数值,以下是求w0梯度的过程: 

    36281c220e39f810dd155dc1c9c313af.png

    下面是求w1、w2、w3梯度的过程类似,直接写出结果:

    68c96d62c12ce9982bd873c6f6b7816a.png

    最后再按照下面公式来更新参数即可: 

    c756385df8c3713429d0c62a3c426196.png

    用OneFlow按照图3来搭建一个对应的网络做实验,代码如下:

     
     
    1. import oneflow as of
    2. import oneflow.nn as nn
    3. import oneflow.optim as optim
    4. class Sample(nn.Module):
    5. def __init__(self):
    6. super(Sample, self).__init__()
    7. self.op1 = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(2,2), bias=False)
    8. self.op2 = nn.AvgPool2d(kernel_size=(2,2))
    9. self.loss = nn.MSELoss()
    10. def forward(self, x, label):
    11. y1 = self.op1(x)
    12. y2 = self.op2(y1)
    13. return self.loss(y2, label)
    14. model = Sample()
    15. optimizer = optim.SGD(model.parameters(), lr=0.005)
    16. data = of.randn(1, 1, 3, 3)
    17. label = of.randn(1, 1, 1, 1)
    18. loss = model(data, label)
    19. print("------------before backward()---------------")
    20. param = model.parameters()
    21. print("w =", next(param))
    22. loss.backward()
    23. print("------------after backward()---------------")
    24. param = model.parameters()
    25. print("w =", next(param))
    26. optimizer.step()
    27. print("------------after step()---------------")
    28. param = model.parameters()
    29. print("w =", next(param))
    30. optimizer.zero_grad()
    31. print("------------after zero_grad()---------------")
    32. param = model.parameters()
    33. print("w ="next(param))

    输出如下(里面的input、param、label的值都是随机的,每次运行的结果会不一样):

    1. ------------before backward()---------------
    2. w = tensor([[[[ 0.2621, -0.2583],
    3. [-0.1751, -0.0839]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)
    4. ------------after backward()---------------
    5. w = tensor([[[[ 0.2621, -0.2583],
    6. [-0.1751, -0.0839]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)
    7. ------------after step()---------------
    8. w = tensor([[[[ 0.2587, -0.2642],
    9. [-0.1831, -0.0884]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)
    10. ------------after zero_grad()---------------
    11. w = tensor([[[[ 0.2587, -0.2642],
    12.           [-0.1831-0.0884]]]], dtype=oneflow.float32, grad_fn=<accumulate_grad>)

    参考资料:

    1.http://speech.ee.ntu.edu.tw/~tlkagk/courses.html

    2.https://speech.ee.ntu.edu.tw/~hylee/index.php

    3.https://www.youtube.com/c/HungyiLeeNTU

    其他人都在看

    欢迎下载体验OneFlow v0.7.0:GitHub - Oneflow-Inc/oneflow: OneFlow is a performance-centered and open-source deep learning framework.icon-default.png?t=M4ADhttps://github.com/Oneflow-Inc/oneflow

  • 相关阅读:
    Qt OpenGL(二十四)——Qt OpenGL 核心模式-实现彩色三角形
    盲盒商城系统开发抽奖系统方案大全
    几种简短的数据类型的介绍(主要介绍void)
    基于SSM的超市会员管理系统
    R语言使用mean函数计算样本(观测)数据中指定变量的相对频数:计算dataframe中指定数据列偏离平均值超过两个标准差的观测样本所占总体的比例
    jenkins升级及基于jeecgboot 的nbcio-boot前端演示发布
    [附源码]Python计算机毕业设计Django新能源汽车租赁
    ros2订阅esp32发布的电池电压数据-补充
    半桥BUCK电路—记录篇
    智慧图书馆中一般有哪些设备
  • 原文地址:https://blog.csdn.net/OneFlow_Official/article/details/124763082