目录

一、过程推导——了解BP原理

二、数值计算

三、代码实现- numpy手推 + pytorch自动

1、对比【numpy】和【pytorch】程序,总结并陈述。

(1)使用numpy实现

(2)使用pytorch实现

2、激活函数Sigmoid用PyTorch自带函数torch.sigmoid(),观察、总结并陈述。

3、激活函数Sigmoid改变为Relu,观察、总结并陈述。

4、损失函数MSE用PyTorch自带函数 t.nn.MSELoss()替代,观察、总结并陈述。

5、损失函数MSE改变为交叉熵,观察、总结并陈述。

6、改变步长,训练次数,观察、总结并陈述。

7、权值w1-w8初始值换为随机数,对比“指定权值”的结果,观察、总结并陈述。

8、权值w1-w8初始值换为0,观察、总结并陈述。

9、全面总结反向传播原理和编码实现,认真写心得体会。


分别使用numpy和pytorch实现FNN例题

  1. 过程推导 - 了解BP原理
  2. 数值计算 - 手动计算,掌握细节
  3. 代码实现 - numpy手推 + pytorch自动

代码实现:

一、过程推导——了解BP原理

神经网络是一种运算模型,由大量的节点(也可以说神经元)之间连接构成。每个节点代表一种特定的输出函数,称为激励函数或者激活函数。每两个节点间的连接都代表一个对于通过连接信号的加权值,称之为权重。这相当于人神经的记忆(就是仿生),神经网络的输出则根据网络的连接方式、权重值和激活函数的不同而不同。而网络本身通常都是对自然界某种算法或函数的逼近,也可能是一种逻辑策略的表达。简单来说,人工神经网络搭建利用函数拟合的性质体现某种规律。人工神经网络的每个节点(神经元)不同于生物上的神经元只有激活和抑制两种状态,它通过激活函数传递一种可连续变化的值。在神经网络中,输入层与输出层之间的层称为隐含层或隐层,隐层和输出层的神经元都是具有激活函数的功能神经元。

BP(back propagation)神经网络是1986年由Rumelhart和McClelland为首的科学家提出的概念,是一种按照误差逆向传播算法训练的多层前馈神经网络,是应用最广泛的神经网络。这里的“前馈”指的是网络拓扑结构中不存在环或回路,而不是指该网络只能向前传播而不能向后传播。BP网络采用梯度下降法,通过不断调整神经元的权值和阈值,以使得到的误差的平方和最小。

BP神经网络的特点在于信号向前传播,误差向后传播,依据得到的误差进行调整以求得误差的最小化。

参考链接:https://blog.csdn.net/qq_50208667/article/details/119006291

来自西瓜书的图:

二、数值计算

三、代码实现- numpy手推 + pytorch自动

1、对比【numpy】和【pytorch】程序,总结并陈述。

(1)使用numpy实现

输入值:x1, x2 = 0.5,0.3

输出值:y1, y2 =0.23, -0.07

激活函数:sigmoid

损失函数:MSE

初始权值:0.2 -0.4 0.5 0.6 0.1 -0.5 -0.3 0.8

目标:通过反向传播优化权值

import numpy as npw1, w2, w3, w4, w5, w6, w7, w8 = 0.2, -0.4, 0.5, 0.6, 0.1, -0.5, -0.3, 0.8
x1, x2 = 0.5, 0.3
y1, y2 = 0.23, -0.07
print("输入值 x0, x1:", x1, x2)
print("输出值 y0, y1:", y1, y2)def sigmoid(z):a = 1 / (1 + np.exp(-z))return adef forward_propagate(x1, x2, y1, y2, w1, w2, w3, w4, w5, w6, w7, w8):in_h1 = w1 * x1 + w3 * x2out_h1 = sigmoid(in_h1)in_h2 = w2 * x1 + w4 * x2out_h2 = sigmoid(in_h2)in_o1 = w5 * out_h1 + w7 * out_h2out_o1 = sigmoid(in_o1)in_o2 = w6 * out_h1 + w8 * out_h2out_o2 = sigmoid(in_o2)print("正向计算,隐藏层h1 ,h2:", end="")print(round(out_h1, 5), round(out_h2, 5))print("正向计算,预测值o1 ,o2:", end="")print(round(out_o1, 5), round(out_o2, 5))error = (1 / 2) * (out_o1 - y1) ** 2 + (1 / 2) * (out_o2 - y2) ** 2print("损失函数(均方误差):",round(error, 5))return out_o1, out_o2, out_h1, out_h2def back_propagate(out_o1, out_o2, out_h1, out_h2):# 反向传播d_o1 = out_o1 - y1d_o2 = out_o2 - y2d_w5 = d_o1 * out_o1 * (1 - out_o1) * out_h1d_w7 = d_o1 * out_o1 * (1 - out_o1) * out_h2d_w6 = d_o2 * out_o2 * (1 - out_o2) * out_h1d_w8 = d_o2 * out_o2 * (1 - out_o2) * out_h2d_w1 = (d_o1 * out_h1 * (1 - out_h1) * w5 + d_o2 * out_o2 * (1 - out_o2) * w6) * out_h1 * (1 - out_h1) * x1d_w3 = (d_o1 * out_h1 * (1 - out_h1) * w5 + d_o2 * out_o2 * (1 - out_o2) * w6) * out_h1 * (1 - out_h1) * x2d_w2 = (d_o1 * out_h1 * (1 - out_h1) * w7 + d_o2 * out_o2 * (1 - out_o2) * w8) * out_h2 * (1 - out_h2) * x1d_w4 = (d_o1 * out_h1 * (1 - out_h1) * w7 + d_o2 * out_o2 * (1 - out_o2) * w8) * out_h2 * (1 - out_h2) * x2print("w的梯度:",round(d_w1, 2), round(d_w2, 2), round(d_w3, 2), round(d_w4, 2), round(d_w5, 2), round(d_w6, 2),round(d_w7, 2), round(d_w8, 2))return d_w1, d_w2, d_w3, d_w4, d_w5, d_w6, d_w7, d_w8def update_w(w1, w2, w3, w4, w5, w6, w7, w8):# 步长step = 1w1 = w1 - step * d_w1w2 = w2 - step * d_w2w3 = w3 - step * d_w3w4 = w4 - step * d_w4w5 = w5 - step * d_w5w6 = w6 - step * d_w6w7 = w7 - step * d_w7w8 = w8 - step * d_w8return w1, w2, w3, w4, w5, w6, w7, w8if __name__ == "__main__":print("权值w0-w7:",round(w1, 2), round(w2, 2), round(w3, 2), round(w4, 2), round(w5, 2), round(w6, 2), round(w7, 2),round(w8, 2))for i in range(5):print("=====第" + str(i+1) + "轮=====")out_o1, out_o2, out_h1, out_h2 = forward_propagate(x1, x2, y1, y2, w1, w2, w3, w4, w5, w6, w7, w8)d_w1, d_w2, d_w3, d_w4, d_w5, d_w6, d_w7, d_w8 = back_propagate(out_o1, out_o2, out_h1, out_h2)w1, w2, w3, w4, w5, w6, w7, w8 = update_w(w1, w2, w3, w4, w5, w6, w7, w8)print("更新后的权值w:",round(w1, 2), round(w2, 2), round(w3, 2), round(w4, 2), round(w5, 2), round(w6, 2), round(w7, 2),round(w8, 2))

运行结果

(2)使用pytorch实现

使用python实现,主要是为了观察链式求导的过程。

链式法则求导还是比较麻烦的,特别是层数比较深的时候,计算量很大,过程也很复杂,编程实现非常困难。

真正在做深度学习时,不需要编程实现求梯度过程。框架可以通过计算图自动求导,大大提高了效率。

import torchx = [0.5, 0.3]  # x0, x1 = 0.5, 0.3
y = [0.23, -0.07]  # y0, y1 = 0.23, -0.07
print("输入值 x0, x1:", x[0], x[1])
print("输出值 y0, y1:", y[0], y[1])
w = [torch.Tensor([0.2]), torch.Tensor([-0.4]), torch.Tensor([0.5]), torch.Tensor([0.6]), torch.Tensor([0.1]), torch.Tensor([-0.5]), torch.Tensor([-0.3]), torch.Tensor([0.8])]  # 权重初始值
for i in range(0, 8):w[i].requires_grad = True
print("权值w0-w7:")
for i in range(0, 8):print(w[i].data, end="  ")def forward_propagate(x):  # 计算图in_h1 = w[0] * x[0] + w[2] * x[1]out_h1 = torch.sigmoid(in_h1)in_h2 = w[1] * x[0] + w[3] * x[1]out_h2 = torch.sigmoid(in_h2)in_o1 = w[4] * out_h1 + w[6] * out_h2out_o1 = torch.sigmoid(in_o1)in_o2 = w[5] * out_h1 + w[7] * out_h2out_o2 = torch.sigmoid(in_o2)print("正向计算,隐藏层h1 ,h2:", end="")print(out_h1.data, out_h2.data)print("正向计算,预测值o1 ,o2:", end="")print(out_o1.data, out_o2.data)return out_o1, out_o2def loss(x, y):  # 损失函数y_pre = forward_propagate(x)  # 前向传播loss_mse = (1 / 2) * (y_pre[0] - y[0]) ** 2 + (1 / 2) * (y_pre[1] - y[1]) ** 2  # 考虑 : t.nn.MSELoss()print("损失函数(均方误差):", loss_mse.item())return loss_mseif __name__ == "__main__":for k in range(5):print("\n=====第" + str(k+1) + "轮=====")l = loss(x, y)  # 前向传播,求 Loss,构建计算图l.backward()  # 反向传播,求出计算图中所有梯度存入w中. 自动求梯度,不需要人工编程实现。print("w的梯度: ", end="  ")for i in range(0, 8):print(round(w[i].grad.item(), 2), end="  ")  # 查看梯度step = 1  # 步长for i in range(0, 8):w[i].data = w[i].data - step * w[i].grad.data  # 更新权值w[i].grad.data.zero_()  # 注意:将w中所有梯度清零print("\n更新后的权值w:")for i in range(0, 8):print(w[i].data, end="  ")

运行结果:

总结:

通过观察两种代码的运行结果,发现当训练轮数为5轮时,两次实验的结果差不太多,w的梯度都相同,结果相差不多,所以两种不同实现方法的效果基本相同。

2、激活函数Sigmoid用PyTorch自带函数torch.sigmoid(),观察、总结并陈述。

sigmoid函数图:

使用Sigmoid函数和使用Pytorch自带函数torch.sigmoid()运行出的结果相差不多。

替换的代码:

def forward_propagate(x1, x2):in_h1 = w1 * x1 + w3 * x2out_h1 = torch.sigmoid(in_h1)in_h2 = w2 * x1 + w4 * x2out_h2 = torch.sigmoid(in_h2)in_o1 = w5 * out_h1 + w7 * out_h2out_o1 = torch.sigmoid(in_o1)in_o2 = w6 * out_h1 + w8 * out_h2out_o2 = torch.sigmoid(in_o2)print("正向计算,隐藏层h1 ,h2:", end="")print(out_h1.data, out_h2.data)print("正向计算,预测值o1 ,o2:", end="")print(out_o1.data, out_o2.data)return out_o1, out_o2

3、激活函数Sigmoid改变为Relu,观察、总结并陈述。

Relu函数图:

将激活函数换为ReLU函数:

def relu(z):return np.maximum(0, z)

ReLU函数的收敛速度比Sigmoid函数更快。

4、损失函数MSE用PyTorch自带函数 t.nn.MSELoss()替代,观察、总结并陈述。

def loss_fuction(x1, x2, y1, y2):  # 损失函数y1_pred, y2_pred = forward_propagate(x1, x2)  # 前向传播# loss = (1 / 2) * (y1_pred - y1) ** 2 + (1 / 2) * (y2_pred - y2) ** 2  # 考虑 : t.nn.MSELoss()loss_func = torch.nn.MSELoss() # 创建损失函数y_pred = torch.cat((y1_pred, y2_pred), dim=0) # 将y1_pred, y2_pred合并成一个向量y = torch.cat((y1, y2), dim=0) # 将y1, y2合并成一个向量loss = loss_func(y_pred, y) # 计算损失print("损失函数(均方误差):", loss.item())return loss

从结果得出,当训练轮数多了之后,原代码的收敛结果比自带函数torch.nn.MSELoss()好一些。

5、损失函数MSE改变为交叉熵,观察、总结并陈述。

交叉熵(Cross Entry)是用来评估当前训练得到的概率分布与真实分布的差异情况,也就是交叉熵的值越小,两个概率分布就越接近。减少交叉熵损失就是在提高模型的预测准确率。其离散函数形式

替换代码:

def loss_fuction(x1, x2, y1, y2):y1_pred, y2_pred = forward_propagate(x1, x2)loss_func = torch.nn.CrossEntropyLoss() # 创建交叉熵损失函数y_pred = torch.stack([y1_pred, y2_pred], dim=1)y = torch.stack([y1, y2], dim=1)loss = loss_func(y_pred, y) # 计算print("损失函数(交叉熵损失):", loss.item())return loss

结果:

当训练次数增加以后,损失函数变成了负值。

6、改变步长,训练次数,观察、总结并陈述。

(1)改变步长

步长为1,训练次数为50:

步长为20,训练次数为50:

改变步长,随着步长增加,均方误差下降越快,收敛越快。

(2)改变训练次数

步长为1,训练次数为10:

步长为1,训练次数为500:

改变训练次数,随着训练次数增大,均方误差下降速度也在逐渐降低,收敛速度下降 。

7、权值w1-w8初始值换为随机数,对比“指定权值”的结果,观察、总结并陈述。

初始化权值:

w = [torch.randn(1, 1), torch.randn(1, 1), torch.randn(1, 1), torch.randn(1, 1), torch.randn(1, 1), torch.randn(1, 1), torch.randn(1, 1), torch.randn(1, 1) ]# 权重初始值

改变以后没影响收敛的结果,均方误差的下降速度与指定权值时相比有所降低。

8、权值w1-w8初始值换为0,观察、总结并陈述。

权值初始化为0:

w1, w2, w3, w4, w5, w6, w7, w8 = torch.Tensor([0]), torch.Tensor([0]), torch.Tensor([0]), torch.Tensor([0]), torch.Tensor([0]), torch.Tensor([0]), torch.Tensor([0]), torch.Tensor([0])

改变了权值以后,对收敛速度基本没有影响。

9、全面总结反向传播原理和编码实现,认真写心得体会。

BP神经网络是一种多层前馈神经网络,特点是:信号向前传播,误差反向传播。通俗理解就是,BP神经网络通过层与层向前传播,得到最终实际输出后,与期望输出做对比,通过“梯度下降”策略,逐层调节权重和阈值,最终得到与期望输出在误差允许范围内的神经网络模型。

反向传播算法是目前用来训练人工神经网络的最常用且最有效的算法。

反向传播工作原理就是:

(1)前向传播:将训练集数据输入到ANN的输入层,经过隐藏层,最后到达输出层并输出结果。【输入层—隐藏层–输出层】
(2)反向传播:由于ANN的输入结果与输出结果有误差,则计算估计值与实际值之间的误差,并将该误差从输出层向隐藏层反向传播,直至传播到输入层。【输出层–隐藏层–输入层】
(3)权重更新:在反向传播的过程中,根据误差调整各种参数的值;不断迭代上述过程,直至收敛。

本次作业,先是自己动手推导了公式,也是复习上学期学习的知识,动手写一遍后,留下了更深的印象。然后又在老师给出的问题的引导下,动手修改部分代码后,观察实现效果的不同之处,收获很多。
参考链接:http://t.csdn.cn/Y2N2R

https://blog.csdn.net/weixin_55775980/article/details/119795266

NNDL 作业3:分别使用numpy和pytorch实现FNN例题相关推荐

  1. 神经网络与深度学习 作业3:分别使用numpy和pytorch实现FNN例题

    目录 一.过程推导 - 了解BP原理 二.数值计算 - 手动计算,掌握细节 三.代码实现 - numpy手推 + pytorch自动 (1)使用numpy实现 (2)使用pytorch实现 (3)思考 ...

  2. NNDL 作业9:分别使用numpy和pytorch实现BPTT

    目录 6-1:推导RNN反向传播算法BPTT. 6-2P:设计简单RNN模型,分别用Numpy.Pytorch实现反向传播算子,并代入数值测试. 6-1:推导RNN反向传播算法BPTT. 在RNN中, ...

  3. NNDL 作业6:基于CNN的XO识别

    实现卷积-池化-激活 Numpy版本:手工实现 卷积-池化-激活 自定义卷积算子.池化算子实现,源码如下: import numpy as npx = np.array([[-1, -1, -1, - ...

  4. NNDL 作业8:RNN-简单循环网络

    简单循环网络(Simple Recurrent Network,SRN)是只有一个隐藏层的神经网络. 目录 1.使用Numpy实现SRN 2.在1的基础上,增加激活函数tanh 3.分别使用nn.RN ...

  5. NNDL 作业5:卷积

    目录 作业1 1. 图1使用卷积核​编辑,输出特征图 2. 图1使用卷积核​编辑,输出特征图 3. 图2使用卷积核​编辑,输出特征图 4. 图2使用卷积核​编辑,输出特征图 5. 图3使用卷积核​编辑 ...

  6. NNDL 作业7:第五章课后题

    目录 习题5-2 证明宽卷积具有交换性,即公式(5.13) 习题5-3 分析卷积神经网络中用1×1的卷积核的作用 习题5-4 对于一个输入为100×100×256的特征映射组,使用3×3的卷积核,输出 ...

  7. 深度盘点Python11个主流框架:Pandas、Django、Matplotlib、Numpy、PyTorch......

    六月份TIOBE编程语言排行榜,位居第二名的Python与第一名C语言之间的差距正在逐渐缩小.Python如此受欢迎一方面得益于它崇尚简洁的编程哲学,另一方面是因为强大的第三方库生态. 要说杀手级的库 ...

  8. 深度盘点 Python11 个主流框架:Pandas、Django、Matplotlib、Numpy、PyTorch......

    六月份TIOBE编程语言排行榜,位居第二名的Python与第一名C语言之间的差距正在逐渐缩小.Python如此受欢迎一方面得益于它崇尚简洁的编程哲学,另一方面是因为强大的第三方库生态. 要说杀手级的库 ...

  9. python numpy.arry, pytorch.Tensor及原生list相互转换

    文章目录 python numpy.arry, pytorch.Tensor及原生list相互转换 1 原生list转numpy list 2 numpy.array 转原生list 3 numpy. ...

最新文章

  1. 阿里敏捷实践| 4个迭代,从批量交付向持续交付转型
  2. 启用Gzip压缩(IIS)提高客户端网站访问速度
  3. 剑指Offer Ⅱ 003.二进制加法(力扣剑指Offer专项突击版——整数_3)
  4. 文献学习(part24)--Splitting Methods for Convex Clustering
  5. 根据对象的属性去重,获取新数组
  6. 菜鸟教程工具(三)——Maven自己主动部署Tomcat
  7. ROS入门-10.话题消息的定义与使用
  8. conn.setAutoCommit(true) and conn.close() 关系
  9. mysql导入超大sql文件方法
  10. Java 语言结构【转】
  11. Python3+Scrapy实现网页爬虫
  12. 解决报错Duplicate keys detected
  13. phpWord 读取word模板,替换相应变量
  14. Vue快速实现通用表单验证
  15. excel计数连续负数(正数)
  16. matlab高斯白噪声功率谱密度,matlab-正弦波-高斯白噪声-均匀白噪声-功率谱密度-自相关函数.doc...
  17. 谷歌浏览器f12功能修改服务器代码,Chrome(谷歌)控制台,console实用教程
  18. python基于SVM的疫情评论情感数据分析
  19. cudnn.benchmark = True什么意思
  20. win10系统如何连接宽带连接服务器,win10宽带连接在哪_win10设置宽带连接的方法...

热门文章

  1. 在C语言中为什么c1出现错误,我在用C语言编程时,编译后总出现Error:spawning C1.exe的错误,看不懂,是什么意思啊??快快快,谢谢!...
  2. Cocos2d-JS中ctor和Cocos2d-x中init的作用
  3. FS100N03替代NCE3080K新洁能30V 80A MOS场效应管
  4. height和line-height的区别(简单易懂)
  5. GIS开发:QGIS编辑矢量数据
  6. 自动化冒烟测试:Unittest vs Pytest框架
  7. SPA(单页面)和MPA(多页面)的区别与优缺点
  8. 2017,我的面试复盘
  9. 机器学习之模拟退火算法
  10. excel锁定单元格不能修改_EXCEL/WPS如何保护特定的数据不被更改?