• 【深度学习2】反向传播BP算法


     

    🍊本文从梯度下降算法开始介绍BP反向传播算法背景,并使用一个实际案例来模拟BP过程来讲解其原理。最后做了三个实验进行BP实战

    🍊实验一手撸一个y=ω*x模拟反向传播过程

    🍊实验二将BP应用到线性回归模型中进行参数拟合

    🍊实验三使用Pytorch重现实验二过程

    一、Introductiaing

    以下是一个常见的神经网络图

    直观的讲,经典神经网络的训练就是求解各个权重w,从正向传播的角度看,想要求解各个参数需要使用链式求导法则和梯度下降法,一个权重w 需要嵌套很多层,求解的时间代价很高😕,咋普通人手里的家伙是扛不住的

    那么是否有一种更加快捷的方式呢?我们将网络逆向的看,随后标注其公式,我们可以惊奇的发现它就是一个反向传播的神经网络

     那么我们从输出值出发,逆向的进行网络的传播,这样求解的效率比较高

    二、Principle

    2.1 偏导数

    首先简单的回顾一下偏导数的相关的知识,来看看以下这个题目

    已知J(a,b,c)=3(a+bc),令u=a+v,v=bc,求a,b,c各自的偏导数

    2.2 BP推导

    求解了偏导数之后,我们就可以进行BP推导了,假设这里有一个最简单的神经元,y= ω*x

    神经网络本身也是做预测功能的,预测结果使用损失函数来判断好坏,所以我们的目标是使得损失函数最小,如何计算呢?回到梯度下降算法中,求一个函数的最值就是要不断的求梯度,此时这里的自变量可以看做是ω,那么最终目标自然是求解

    损失函数如下图中所示

    我们将该求解Loss的过程转化为计算图(下图中的有向无环图),假设初始化参数x和ω均为1,随后进行反向的传播

     求解出了之后,就可以更新权重了

    重复的更新参数就完成了整个反向传播的过程

    三、Experiment

    3.1 伪代码

    思路

    1 初始化数据集

    2 正向传播计算预测值,计算损失

    3 反向传播 

    4 更新参数

    3.2 实验一:y=ω*x模拟反向传播过程

    题目:使用y=ω*x(没有偏置b,也没有激活函数)来模拟反向传播的过程。此外,日志化输出Loss来观察模型的好坏

    1. import torch
    2. x_data = [1.0, 2.0, 3.0]
    3. y_data = [2.0, 4.0, 6.0]
    4. w = torch.tensor([1.0])
    5. w.requires_grad = True # Pytorch中默认Tensor是不需要求梯度的
    6. def forward(x):
    7. return x * w # w为Tensor,这里的*其实已经重载了,两个Tensor进行点乘
    8. def loss(x, y):
    9. y_pred = forward(x)
    10. return (y_pred - y) ** 2
    11. for epoch in range(100):
    12. for x, y in zip(x_data, y_data): # SGD随机梯度下降
    13. l = loss(x, y) # 正向传播,计算损失。
    14. l.backward() # 反向传播,每次进行backward时,loss的计算图就会被自动释放掉
    15. print('\tgrad:', x, y, w.grad.item())
    16. w.data = w.data - 0.01 * w.grad.data # 权重更新时,grad也是一个tensor
    17. # 这里为什么要使用data呢?因为使用data只是进行数值修改,并不会影响建立一个新计算图
    18. w.grad.data.zero_()
    19. # 这里梯度一定要清零,因为如果不清零,比如第一次是Loss1的梯度,第二次就是Loss1的梯度+Loss2的梯度。
    20. # 实际上我们只要Loss2的梯度
    21. print('progress:', epoch, l.item()) # 取出loss使用l.item,不要直接使用l(l是tensor会构建计算图)
    22. print("predict (after training)", 4, forward(4).item())

      Result

    训练结果可以有两个角度。第一个角度,截取最后5个Epoch的训练结果,我们可以明显的发现Loss已经完成收敛。第二个角度, ω实际值为2,输入为4时预测输出的7.99999与实际值8非常相近了,说明预测效果不错

    3.3 实验二:线性回归拟合(非Pytorch版)

    题目:使用BP算法来进行线性回归,假设我们使用的是一个全连接层,即模型为y=wx+b。实例化使用y=4x+0.6来构造数据集x,y,最后通过模型拟合出的w和x是否接近4和0.6?

    1. # Linear propagation via BP
    2. import torch
    3. import numpy
    4. from matplotlib import pyplot as plt
    5. # Prepare for the dataset
    6. x=torch.rand([100])
    7. y=4*x+0.6
    8. w=torch.rand(1,requires_grad=True)
    9. b=torch.rand(1,requires_grad=True)
    10. # Define the loss function
    11. def loss_fn(y,y_predict):
    12. loss=(y_predict-y).pow(2).mean()
    13. # Set the grad as zero every BP
    14. for i in [w,b]:
    15. if i.grad is not None:
    16. i.grad.data.zero_()
    17. loss.backward()
    18. return loss.data
    19. # Define the optimizer
    20. def optimize(learning_rate):
    21. w.data-=learning_rate*w.grad.data
    22. b.data-=learning_rate*b.grad.data
    23. for i in range(3000):
    24. y_predict=x*w+b
    25. loss=loss_fn(y,y_predict)
    26. if i%500==0:
    27. print(i,loss)
    28. optimize(0.01)
    29. # Draw the image
    30. predict=x*w+b
    31. plt.scatter(x.data.numpy(),y.data.numpy(),c="r")
    32. plt.plot(x.data.numpy(),predict.data.numpy())
    33. plt.show()
    34. print("w",w)
    35. print("b",b)

     Result

    可以看到,使用BP反向传播之后,w和b的值已经非常的接近4和0.6了

    3.4 实验三:线性回归拟合(Pytorch版)

    我们再使用Pytorch+GPU来重写上述内容

    1. # 使用Pytorch内置函数
    2. import torch
    3. from torch import nn
    4. from torch import optim
    5. from matplotlib import pyplot as plt
    6. # Define the dataset
    7. x = torch.rand([50, 1])
    8. y = x * 4 + 0.6
    9. # Define the module
    10. class Lr(nn.Module):
    11. def __init__(self):
    12. super(Lr, self).__init__()
    13. self.linear = nn.Linear(1, 1)
    14. def forward(self, x):
    15. out = self.linear(x)
    16. return out
    17. # Instantiate the model, loss and optimizer
    18. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    19. x, y = x.to(device), y.to(device)
    20. model = Lr().to(device)
    21. criterion = nn.MSELoss()
    22. optimizer = optim.SGD(model.parameters(), lr=1e-3)
    23. # Train the module
    24. for i in range(30000):
    25. out = model(x) # Get the prediction
    26. loss = criterion(y, out) # Calculate loss
    27. optimizer.zero_grad() # Gradient zeroing
    28. loss.backward() # Calculate Gradient
    29. optimizer.step() # Update the Gradient
    30. if (i + 1) % 20 == 0:
    31. print('Epoch[{}/{}],loss:{:.6f}'.format(i, 30000, loss.data))
    32. # Module evaluation
    33. model.eval() # Set module as evaluation mode
    34. predict = model(x)
    35. predict = predict.cpu().detach().numpy()
    36. plt.scatter(x.cpu().data.numpy(), y.cpu().data.numpy(), c="r")
    37. plt.plot(x.cpu().data.numpy(), predict, )
    38. plt.show()

    参考资料

    《机器学习》周志华

    《深度学习与机器学习》吴恩达

    《神经网络与与深度学习》邱锡鹏

    《Pytorch深度学习实战》刘二大人

  • 相关阅读:
    muduo库剖析(1)
    Java IO 字节和字符数组的相关简介说明
    PromQL基础语法(上)-数据类型、时序选择器、二元操作符【prometheus查询语句】
    重温Python基础——字符串
    【Java并发编程】之线程安全
    Shiro学习笔记(1)——shiro入门
    【经典总线协议】IIC
    jQuery
    java 实用的时间日期工具类
    ACU-01B 3HNA024871-001/03 机器人将如何改变世界
  • 原文地址:https://blog.csdn.net/ccaoshangfei/article/details/126585000