NNDL 作业四

代码实现

1、使用numpy实现

import numpy as np
w1, 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 a
# 前向传播
def forward_propagate(x1, x2, w1, w2, w3, w4, w5, w6, w7, w8):
    In_h1 = w1 * x1 + w3 * x2
    h1 = sigmoid(In_h1)
    In_h2 = w2 * x1 + w4 * x2
    h2 = sigmoid(In_h2)
    In_o1 = w5 * h1 + w7 * h2
    o1 = sigmoid(In_o1)
    In_o2 = w6 * h1 + w8 * h2
    o2 = sigmoid(In_o2)
    print("隐藏层h1=", h1, "h2=", h2)
    print("预测值o1=", o1, "o2=", o2)
    error = (1 / 2) * (o1 - y1) ** 2 + (1 / 2) * (o2 - y2) ** 2
    print("损失函数", error)
    return o1, o2, h1, h2
# 反向传播
def back_propagate(o1, o2, h1, h2):
    d_w5 = (o1 - y1) * o1 * (1 - o1) * h1
    d_w6 = (o2 - y2) * o2 * (1 - o2) * h1
    d_w7 = (o1 - y1) * o1 * (1 - o1) * h2
    d_w8 = (o2 - y2) * o2 * (1 - o2) * h2
    d_w1 = ((o1 - y1) * o1 * (1 - o1) * w5 + (o2 - y2) * o2 * (1 - o2) * w6) * h1 * (1 - h1) * x1
    d_w2 = ((o1 - y1) * o1 * (1 - o1) * w7 + (o2 - y2) * o2 * (1 - o2) * w8) * h2 * (1 - h2) * x1
    d_w3 = ((o1 - y1) * o1 * (1 - o1) * w5 + (o2 - y2) * o2 * (1 - o2) * w6) * h1 * (1 - h1) * x2
    d_w4 = ((o1 - y1) * o1 * (1 - o1) * w7 + (o2 - y2) * o2 * (1 - o2) * w8) * h2 * (1 - h2) * x2
    print("w的梯度:d_w1=", d_w1, "d_w2=", d_w2, "d_w3=", d_w3, "d_w4=", d_w4, "d_w5=", d_w5, "d_w6=", d_w6, "d_w7=",
          d_w7, "d_w8=", d_w8)
    return d_w1, d_w2, d_w3, d_w4, d_w5, d_w6, d_w7, d_w8
def update_w(w1, w2, w3, w4, w5, w6, w7, w8):
    # 步长
    step = 1
    w1 = w1 - step * d_w1
    w2 = w2 - step * d_w2
    w3 = w3 - step * d_w3
    w4 = w4 - step * d_w4
    w5 = w5 - step * d_w5
    w6 = w6 - step * d_w6
    w7 = w7 - step * d_w7
    w8 = w8 - step * d_w8
    return w1, w2, w3, w4, w5, w6, w7, w8
if __name__ == "__main__":
    print("权值w1=", w1, "w2=", w2, "w3=", w3, "w4=", w4, "w5=", w5, "w6=", w6, "w7=", w7, "w8=", w8)
    for i in range(5):
        print("=====第" + str(i + 1) + "轮=====")
        o1, o2, h1, h2 = forward_propagate(x1, x2, 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(o1, o2, h1, h2)
        w1, w2, w3, w4, w5, w6, w7, w8 = update_w(w1, w2, w3, w4, w5, w6, w7, w8)
    print("更新后的权值w1=", w1, "w2=", w2, "w3=", w3, "w4=", w4, "w5=", w5, "w6=", w6, "w7=", w7, "w8=", w8)

2、使用pytorch实现

import torch
x = [0.5, 0.3]
y = [0.23, -0.07]
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
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]
    h1 = torch.sigmoid(In_h1)
    In_h2 = w[1] * x[0] + w[3] * x[1]
    h2 = torch.sigmoid(In_h2)
    In_o1 = w[4] * h1 + w[6] * h2
    o1 = torch.sigmoid(In_o1)
    In_o2 = w[5] * h1 + w[7] * h2
    o2 = torch.sigmoid(In_o2)
    print("正向计算,隐藏层h1 ,h2:", end="")
    print(h1.data, h2.data)
    print("正向计算,预测值o1 ,o2:", end="")
    print(o1.data, o2.data)
    return o1, o2
def error(x, y):  # 损失函数
    y_pre = forward_propagate(x)  # 前向传播
    error = (1 / 2) * (y_pre[0] - y[0]) ** 2 + (1 / 2) * (y_pre[1] - y[1]) ** 2  # 考虑 : t.nn.MSELoss()
    print("损失函数:", error.item())
    return error
if __name__ == "__main__":
    for k in range(5):
        print("\n=====第" + str(k + 1) + "轮=====")
        l = error(x, y)  # 前向传播,求 Loss,构建计算图
        l.backward()  # 反向传播,求出计算图中所有梯度存入w中. 自动求梯度,不需要人工编程实现。
        print("w的梯度: \n", 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="  ")

问题

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

  numpy算法与 pytorch算法运行出来的结果相同,但是 PyTorch算法的张量运算很方便,而且 PyTorch通过使用 backward ()函数进行自动求导,能够很容易地求解出梯度值。但是numpy不可以,需要手动计算梯度。

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

Sigmoid函数:               

                                                   f(x)=\frac{1}{1+e^{-x}}

   在PyTorch中,我们可以使用torch.sigmoid()函数来计算任意张量的Sigmoid函数值。这个函数接受一个张量作为输入,并返回相同形状的张量,其中每个元素的值是对应位置元素经  过Sigmoid函数后的结果,输入张量经过Sigmoid函数处理后得到了介于0和1之间的输出值。

   输出结果可以看出,两者区别不大

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

 定义Relu函数:

def relu(z):
    return torch.maximum(z, torch.tensor(0.))

运行结果

tensor([0.2000])  tensor([-0.4000])  tensor([0.5000])  tensor([0.6000])  tensor([0.1000])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
=====第1轮=====
正向计算,隐藏层h1 ,h2:tensor([0.2500]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.0250]) tensor([0.])
损失函数: 0.023462500423192978
w的梯度: 
  -0.01  0.0  -0.01  0.0  -0.05  0.0  -0.0  0.0  
更新后的权值w:
tensor([0.2103])  tensor([-0.4000])  tensor([0.5062])  tensor([0.6000])  tensor([0.1513])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
=====第2轮=====
正向计算,隐藏层h1 ,h2:tensor([0.2570]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.0389]) tensor([0.])
损失函数: 0.020715968683362007
w的梯度: 
  -0.01  0.0  -0.01  0.0  -0.05  0.0  0.0  0.0  
更新后的权值w:
tensor([0.2247])  tensor([-0.4000])  tensor([0.5148])  tensor([0.6000])  tensor([0.2004])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
=====第3轮=====
正向计算,隐藏层h1 ,h2:tensor([0.2668]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.0535]) tensor([0.])
损失函数: 0.01803365722298622
w的梯度: 
  -0.02  0.0  -0.01  0.0  -0.05  0.0  0.0  0.0  
更新后的权值w:
tensor([0.2424])  tensor([-0.4000])  tensor([0.5254])  tensor([0.6000])  tensor([0.2475])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
=====第4轮=====
正向计算,隐藏层h1 ,h2:tensor([0.2788]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.0690]) tensor([0.])
损失函数: 0.015410471707582474
w的梯度: 
  -0.02  0.0  -0.01  0.0  -0.04  0.0  0.0  0.0  
更新后的权值w:
tensor([0.2623])  tensor([-0.4000])  tensor([0.5374])  tensor([0.6000])  tensor([0.2924])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
=====第5轮=====
正向计算,隐藏层h1 ,h2:tensor([0.2924]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.0855]) tensor([0.])
损失函数: 0.012893404811620712
w的梯度: 
  -0.02  0.0  -0.01  0.0  -0.04  0.0  0.0  0.0  
更新后的权值w:
tensor([0.2834])  tensor([-0.4000])  tensor([0.5501])  tensor([0.6000])  tensor([0.3346])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
进程已结束,退出代码0

揆诸当下:Relu激活函数收敛速度要优于使用sigmoid激活函数 (更新后的损失函数明显低于之前)

与Sigmoid不同,ReLU是一个非线性函数。它在负输入值上返回0,在正输入值上返回该值本身。这种非线性特性使得神经网络具备更强的学习能力,可以更好地逼近复杂函数。

Sigmoid函数在输入值较大或较小时具有饱和性,导致梯度接近于零,从而出现梯度消失的问题。而ReLU函数没有饱和区域,只要输入值大于零,梯度始终为1。这样可以避免梯度消失问题,有助于更快地收敛和训练深层神经网络。

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

诚载斯言,揆诸当下:  当训练轮数多了之后,原代码的收敛结果比自带函数torch.nn.MSELoss()好一些。

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

MSE用于回归问题,旨在最小化预测值与真实值之间的平方差。而交叉熵常用于分类问题,旨在最小化预测概率分布与真实标签之间的差异。交叉熵对异常值更加敏感,因为它使用了对数运算。相比之下,MSE平方了误差,因此对异常值具有较强的鲁棒性。

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

改为交叉熵后,结果出现了更多的负值。

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

 step=1

step=25

step=50

step=75

step=100

step从1到50,50以后可以看到发现不仅收敛速度变快,收敛结果变化也会变小

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,观察、总结并陈述。

w1, w2, w3, w4, w5, w6, w7, w8 = torch.Tensor([0.0]), torch.Tensor([-0.0]), torch.Tensor([0.0]), torch.Tensor(
    [0.0]), torch.Tensor([0.0]), torch.Tensor([-0.0]), torch.Tensor([-0.0]), torch.Tensor([0.0])  # 权重初始值

权重初始值只绘影响网络收敛速度,对网络的收敛结果影响很小可以忽略不计。

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

1、这次作业当头第一棒就是求梯度时候和ppt上的答案一样,又重新复习了求梯度的知识,重新算了几遍,求梯度时候一定要好好梳理每一个变量的关系,不要漏。编程一般使用梯度下降的优化方法,在每一步中,计算每个神经元的误差项,然后用这些误差项来更新权重,使用学习率来控制权重的更新步长。

2、步长编程一般使用梯度下降法,使用学习率来控制权重的更新步长。合适的步长可以加快模型的收敛速度。较大的步长可以使模型更快地接近最优解,而较小的步长可能导致收敛速度较慢。通过选择适当的步长,可以在有限的训练时间内达到满意的结果。

3、w的初始值和结果关系不大我感觉就是已知x1,x2和结果y1,y2,肯定是想让预测结果和最终结果尽可能的接近,那么最终w更新后的值应该是很类似的,但是w的初始值确实很影响收敛速度,好的初始值肯能几下就出来了,不好的就不好说了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值