HBU_神经网络与深度学习 作业3 前馈神经网络的实现

本文深入探讨了前馈神经网络的正向传播与反向传播原理,通过具体实例讲解了激活函数、损失函数的选择及其对神经网络性能的影响,并通过代码实现了神经网络的训练过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面的一些内容

本次习题与 神经网络与深度学习 pdf电子书的第104页(对应纸质版第92页)的图4.7有关,具体内容详见 [1]NNDL 作业3
本实验报告参考了NNDL 作业3 作者:HBU_fangerfang
HBU-NNDL 作业3 作者:不是蒋承翰
的部分内容。
水平有限,难免有误,如有错漏之处敬请指正。

习题

实现例题中的前馈神经网络。[1]

  1. 使用Numpy实现(不使用深度学习框架)
  2. 使用Pytorch实现,并对比Numpy与Pytorch的实现效果
  3. 更改步长、迭代次数,观察效果
  4. 更改损失函数、激活函数,观察效果

具体步骤

过程推导

正向传播(前向传播)过程

下表为前馈神经网络的表示方法。

表示含义
L L L神经网络的层数
M l M_l Ml l l l层神经元的个数
f l ( ⋅ ) f_l(⋅) fl() l l l层神经元的激活函数
W ( l ) ∈ R M l × M l − 1 \boldsymbol{W}^{(l)} \in \mathbb{R}^{M_l \times M_{l-1}} W(l)RMl×Ml1 l − 1 l-1 l1层到第 l l l的权重矩阵
b ( l ) ∈ R M l \boldsymbol{b}^{(l)} \in \mathbb{R}^{M_l} b(l)RMl l − 1 l-1 l1层到第 l l l的偏置
z ( l ) ∈ R M l \boldsymbol{z}^{(l)} \in \mathbb{R}^{M_l} z(l)RMl l l l层神经元的净输入(净活性值)
a ( l ) ∈ R M l \boldsymbol{a}^{(l)} \in \mathbb{R}^{M_l} a(l)RMl l l l层神经元的输出(活性值)

基本思想:从输入层的输入开始,每次将前一层的输出作为后一层的输入,如此循环至输出层输出为止。

设一共有 l l l层神经元。
输入层的输入:
x \boldsymbol{x} x输入层的输出:
a ( 0 ) = x \boldsymbol{a}^{(0)}=\boldsymbol{x} a(0)=x第n个隐藏层的输入:
z ( n ) = W ( n ) a ( n − 1 ) + b ( n ) \boldsymbol{z}^{(n)}=\boldsymbol{W}^{(n)}\boldsymbol{a}^{(n-1)}+\boldsymbol{b}^{(n)} z(n)=W(n)a(n1)+b(n)第n个隐藏层的输出:
a ( n ) = f l ( z ( n ) ) \boldsymbol{a}^{(n)}=f_l(\boldsymbol{z}^{(n)}) a(n)=fl(z(n))输出层的输入:
z ( l ) = W ( l ) a ( l − 1 ) + b ( l ) \boldsymbol{z}^{(l)}=\boldsymbol{W}^{(l)}\boldsymbol{a}^{(l-1)}+\boldsymbol{b}^{(l)} z(l)=W(l)a(l1)+b(l)输入层的输出:
y = f l ( z ( l ) ) \boldsymbol{y}=f_l(\boldsymbol{z}^{(l)}) y=fl(z(l))过程如题中所给的图4.7所示,计算时循环套用即可。

反向传播(后向传播)过程

下表为误差逆传播的表示方法。

表示含义
x i x_i xi i i i个输入神经元的输入
v i h v_{ih} vih输入层第 i i i个神经元与隐层第 h h h个神经元之间的连接权
γ h \gamma_h γh隐藏层第 h h h个神经元的阈值
α h = ∑ i = 1 d v i h x i \alpha_h=\sum_{i=1}^dv_{ih}x_i αh=i=1dvihxi h h h个隐层神经元的输入
b h b_h bh h h h个隐层神经元的输出
w h j w_{hj} whj隐层第 h h h个神经元与输出层第 j j j个神经元之间的连接权
θ j \theta_j θj输出层第 j j j个神经元的阈值
β j = ∑ h = 1 q w h j b h \beta_j=\sum_{h=1}^qw_{hj}b_h βj=h=1qwhjbh j j j个输出神经元的输入
y j y_j yj j j j个输出神经元的输出

令神经网络的输出为 y ^ = ( y 1 k ^ , y 2 k ^ , . . . , y l k ^ ) \hat{\boldsymbol{y}}=(\hat{\boldsymbol{y}^k_1},\hat{\boldsymbol{y}^k_2},...,\hat{\boldsymbol{y}^k_l}) y^=(y1k^,y2k^,...,ylk^),即
y ^ j k = f ( β j − θ j ) ,( 1 ) \hat{y}^k_j=f(\beta_j-\theta_j),(1) y^jk=f(βjθj),(1其中 β j \beta_j βj时第j个神经元的输入。

该神经网络在 ( x k , y k ) (\boldsymbol{x}_k,\boldsymbol{y}_k) (xk,yk)上的均方误差为
E k = 1 2 ∑ j = 1 l ( y ^ j k − y j k ) ,( 2 ) E_k=\frac{1}{2}\sum_{j=1}^l(\hat{y}^k_j-y^k_j),(2) Ek=21j=1l(y^jkyjk),(2任意参数的更新公式为
v ← v + Δ v ,( 3 ) v\leftarrow v+\Delta v,(3) vv+Δv,(3BP算法基于梯度下降的策略,以目标的负梯度方向对参数进行调整。给定学习率 η \eta η,有
Δ w h j = − η ∂ E k ∂ w h j ,( 4 ) \Delta w_{hj}=-\eta \frac{\partial E_k}{\partial w_{hj}},(4) Δwhj=ηwhjEk,(4 w h j w_{hj} whj先影响到 β j \beta_j βj,再到 y ^ j k \hat{y}^k_j y^jk,再到 E k E_k Ek,有
∂ E k ∂ w h j = ∂ E k ∂ y ^ j k ⋅ ∂ y ^ j k ∂ β j ⋅ ∂ β j ∂ w h j ,( 5 ) \frac{\partial E_k}{\partial w_{hj}}=\frac{\partial E_k}{\partial \hat{y}^k_j} \cdot \frac{\partial \hat{y}^k_j}{\partial \beta_j} \cdot \frac{\partial \beta_j}{\partial w_{hj}},(5) whjEk=y^jkEkβjy^jkwhjβj,(5 β j \beta_j βj定义可知
∂ β j ∂ w h j = b h ,( 6 ) \frac{\partial \beta_j}{\partial w_{hj}}=b_h,(6) whjβj=bh,(6由Sigmoid函数的性质
f ′ ( x ) = f ( x ) ( 1 − f ( x ) ) ,( 7 ) f'(x)=f(x)(1-f(x)),(7) f(x)=f(x)(1f(x)),(7
根据(1)式和(2)式,有
g j = ∂ E k ∂ y ^ j k ⋅ ∂ y ^ j k ∂ β j = ( y ^ j k − y j k ) f ′ ( β j − θ j ) = y ^ j k ( 1 − y ^ j k ) ( y j k − y ^ j k ) ,( 8 ) \begin{aligned} g_j &=\frac{\partial E_k}{\partial \hat{y}^k_j} \cdot \frac{\partial \hat{y}^k_j}{\partial \beta_j} \\ &=(\hat{y}^k_j-y^k_j)f'(\beta_j-\theta_j) \\ &=\hat{y}^k_j(1-\hat{y}^k_j)(y^k_j-\hat{y}^k_j),(8) \end{aligned} gj=y^jkEkβjy^jk=(y^jkyjk)f(βjθj)=y^jk(1y^jk)(yjky^jk),(8将(6)和(8)代入(5),再代入(4),可得 w h j w_{hj} whj的更新公式
Δ w h j = η g j b h ,( 9 ) \Delta w_{hj}=\eta g_jb_h,(9) Δwhj=ηgjbh,(9类似可得
Δ θ j = − η g j ,( 10 ) Δ v i h = η e h x i ,( 11 ) Δ γ h = − η e h ,( 12 ) \Delta \theta_j=-\eta g_j,(10)\\ \Delta v_{ih}=\eta e_hx_i,(11)\\ \Delta \gamma_h=-\eta e_h,(12)\\ Δθj=ηgj,(10Δvih=ηehxi,(11Δγh=ηeh,(12其中
e h = ∂ E k ∂ b h ⋅ ∂ b h ∂ α h = ∑ j = 1 l ∂ E k ∂ β j ⋅ ∂ β j ∂ b h f ′ ( α h − γ h ) = ∑ j = 1 l w h j g j f ′ ( α h − γ h ) = ∑ j = 1 l w h j g j ⋅ b h ( 1 − b h ) \begin{aligned} e_h&=\frac{\partial E_k}{\partial b_h} \cdot \frac{\partial b_h}{\partial \alpha_h}\\ &=\sum_{j=1}^l\frac{\partial E_k}{\partial \beta_j} \cdot \frac{\partial \beta_j}{\partial b_h}f'(\alpha_h-\gamma_h)\\ &=\sum_{j=1}^lw_{hj}g_jf'(\alpha_h-\gamma_h)\\ &=\sum_{j=1}^lw_{hj}g_j \cdot b_h(1-b_h) \end{aligned} eh=bhEkαhbh=j=1lβjEkbhβjf(αhγh)=j=1lwhjgjf(αhγh)=j=1lwhjgjbh(1bh)学习率 η ∈ ( 0 , 1 ) \eta\in (0,1) η(0,1)控制着算法每一轮迭代中的更新步长。

数值计算


输入值: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
目标:通过反向传播优化权值

h 1 = f ( w 1 x 1 + w 3 x 2 ) = f ( 0.2 × 0.5 + 0.5 × 0.3 ) ≈ 0.56 h 2 = f ( w 2 x 1 + w 4 x 2 ) = f ( − 0.4 × 0.5 + 0.6 × 0.3 ) ≈ 0.5 o 1 = f ( w 5 h 1 + w 7 h 2 ) = f ( 0.1 × 0.56 − 0.3 × 0.5 ) ≈ 0.48 o 2 = f ( w 6 h 1 + w 8 h 2 ) = f ( − 0.5 × 0.56 + 0.8 × 0.3 ) ≈ 0.53 h_1=f(w_1x_1+w_3x_2)=f(0.2 \times 0.5+0.5 \times 0.3) \approx 0.56 \\ h_2=f(w_2x_1+w_4x_2)=f(-0.4 \times 0.5+0.6 \times 0.3) \approx 0.5 \\ o_1=f(w_5h_1+w_7h_2)=f(0.1 \times 0.56-0.3 \times 0.5) \approx 0.48 \\ o_2=f(w_6h_1+w_8h_2)=f(-0.5 \times 0.56+0.8 \times 0.3) \approx 0.53 \\ h1=f(w1x1+w3x2)=f(0.2×0.5+0.5×0.3)0.56h2=f(w2x1+w4x2)=f(0.4×0.5+0.6×0.3)0.5o1=f(w5h1+w7h2)=f(0.1×0.560.3×0.5)0.48o2=f(w6h1+w8h2)=f(0.5×0.56+0.8×0.3)0.53由上可得MSE损失
E = 1 2 [ ( o 1 − y 1 ) 2 + ( o 2 − y 2 ) 2 ] ≈ 1 2 [ ( 0.48 − 0.23 ) 2 + ( 0.53 + 0.07 ) 2 ] ≈ 0.21 \begin{aligned} E&=\frac{1}{2}[(o_1-y_1)^2+(o_2-y_2)^2] \\ &\approx \frac{1}{2}[(0.48-0.23)^2+(0.53+0.07)^2] \\ &\approx 0.21 \end{aligned} E=21[(o1y1)2+(o2y2)2]21[(0.480.23)2+(0.53+0.07)2]0.21取学习率 η = 1 \eta=1 η=1,反向传播更新 w 5 , 6 , 7 , 8 w_{5,6,7,8} w5,6,7,8
w 5 ′ = w 5 − η × ∂ E w 5 = 0.1 − ∂ E k ∂ o 1 ⋅ ∂ o 1 ∂ I n o 1 ⋅ ∂ I n o 1 ∂ w 5 ≈ 0.25 × 0.48 × ( 1 − 0.48 ) × 0.56 ≈ 0.03 w 6 ′ = w 5 − η × ∂ E w 6 = − 0.5 − ∂ E k ∂ o 1 ⋅ ∂ o 2 ∂ I n o 2 ⋅ ∂ I n o 2 ∂ w 6 ≈ 0.6 × 0.53 × ( 1 − 0.53 ) × 0.56 ≈ 0.08 w'_5=w_5-\eta \times \frac{\partial E}{w_5}=0.1-\frac{\partial E_k}{\partial o_1} \cdot \frac{\partial o_1}{\partial Ino_1} \cdot \frac{\partial Ino_1}{\partial w_5} \approx 0.25 \times 0.48 \times (1-0.48) \times 0.56 \approx 0.03 \\ w'_6=w_5-\eta \times \frac{\partial E}{w_6}=-0.5-\frac{\partial E_k}{\partial o_1} \cdot \frac{\partial o_2}{\partial Ino_2} \cdot \frac{\partial Ino_2}{\partial w_6} \approx 0.6 \times 0.53 \times (1-0.53) \times 0.56 \approx 0.08 \\ w5=w5η×w5E=0.1o1EkIno1o1w5Ino10.25×0.48×(10.48)×0.560.03w6=w5η×w6E=0.5o1EkIno2o2w6Ino20.6×0.53×(10.53)×0.560.08同理可得
w 7 ′ ≈ 0.03 w 8 ′ ≈ 0.07 w'_7 \approx 0.03\\ w'_8 \approx 0.07 w70.03w80.07继续反向传播
w 1 ′ = w 1 − η × ∂ E w 1 = 0.2 − ∂ E k ∂ o 1 ⋅ ∂ h 1 ∂ I n h 1 ⋅ ∂ I n h 1 ∂ w 1 ≈ 0.21 w'_1=w_1-\eta \times \frac{\partial E}{w_1}=0.2-\frac{\partial E_k}{\partial o_1} \cdot \frac{\partial h_1}{\partial Inh_1} \cdot \frac{\partial Inh_1}{\partial w_1} \approx 0.21 w1=w1η×w1E=0.2o1EkInh1h1w1Inh10.21同理
w 2 ′ ≈ − 0.41 w 3 ′ ≈ 0.51 w 4 ′ ≈ 0.59 w'_2 \approx -0.41\\ w'_3 \approx 0.51\\ w'_4 \approx 0.59\\ w20.41w30.51w40.59

代码实现

Numpy实现

代码实现如下:

# coding=gbk
import numpy as np
import matplotlib.pyplot as plt

def sigmoid(z):
    a = 1 / (1 + np.exp(-z))
    return a

def forward_propagate(x1, x2, y1, y2, w1, w2, w3, w4, w5, w6, w7, w8): # 正向传播
    in_h1 = w1 * x1 + w3 * x2
    out_h1 = sigmoid(in_h1)
    in_h2 = w2 * x1 + w4 * x2
    out_h2 = sigmoid(in_h2)

    in_o1 = w5 * out_h1 + w7 * out_h2
    out_o1 = sigmoid(in_o1)
    in_o2 = w6 * out_h1 + w8 * out_h2
    out_o2 = sigmoid(in_o2)

    error = (1 / 2) * (out_o1 - y1) ** 2 + (1 / 2) * (out_o2 - y2) ** 2

    return out_o1, out_o2, out_h1, out_h2, error

def back_propagate(out_o1, out_o2, out_h1, out_h2):    # 反向传播
    d_o1 = out_o1 - y1
    d_o2 = out_o2 - y2

    d_w5 = d_o1 * out_o1 * (1 - out_o1) * out_h1
    d_w7 = d_o1 * out_o1 * (1 - out_o1) * out_h2
    d_w6 = d_o2 * out_o2 * (1 - out_o2) * out_h1
    d_w8 = d_o2 * out_o2 * (1 - out_o2) * out_h2

    d_w1 = (d_w5 + d_w6) * out_h1 * (1 - out_h1) * x1
    d_w3 = (d_w5 + d_w6) * out_h1 * (1 - out_h1) * x2
    d_w2 = (d_w7 + d_w8) * out_h2 * (1 - out_h2) * x1
    d_w4 = (d_w7 + d_w8) * out_h2 * (1 - out_h2) * x2

    return d_w1, d_w2, d_w3, d_w4, d_w5, d_w6, d_w7, d_w8

def update_w(step,w1, w2, w3, w4, w5, w6, w7, w8):    #梯度下降,更新权值
    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__":
    w1, w2, w3, w4, w5, w6, w7, w8 = 0.2, -0.4, 0.5, 0.6, 0.1, -0.5, -0.3, 0.8 # 可以给随机值,为配合PPT,给的指定值
    x1, x2 = 0.5, 0.3   # 输入值
    y1, y2 = 0.23, -0.07 # 正数可以准确收敛;负数不行。why? 因为用sigmoid输出,y1, y2 在 (0,1)范围内。
    N = 10             # 迭代次数
    step = 10           # 步长
    print("输入值 x0, x1:", x1, x2)
    print("输出值 y0, y1:", y1, y2)

    print("输入值:x1, x2;",x1, x2, "输出值:y1, y2:", y1, y2)
    eli = []
    lli = []
    for i in range(N):
        print("=====第" + str(i) + "轮=====")
        # 正向传播
        out_o1, out_o2, out_h1, out_h2, error = forward_propagate(x1, x2, y1, y2, w1, w2, w3, w4, w5, w6, w7, w8)
        print("正向传播:", round(out_o1, 5), round(out_o2, 5))
        print("损失函数:", round(error, 5))
        # 反向传播
        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(step,w1, w2, w3, w4, w5, w6, w7, w8)
        eli.append(i)
        lli.append(error)

    plt.plot(eli, lli)
    plt.ylabel('Loss')
    plt.xlabel('w')
    plt.show()

代码执行结果:

=====999=====
正向传播: 0.23025 0.00605
损失函数: 0.00289

Pytorch实现

代码实现如下:

import torch

x1, x2 = torch.Tensor([0.5]), torch.Tensor([0.3])
y1, y2 = torch.Tensor([0.23]), torch.Tensor([-0.07])
print("=====输入值:x1, x2;真实输出值:y1, y2=====")
print(x1, x2, y1, y2)
w1, w2, w3, w4, w5, w6, w7, w8 = 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])  # 权重初始值
w1.requires_grad = True
w2.requires_grad = True
w3.requires_grad = True
w4.requires_grad = True
w5.requires_grad = True
w6.requires_grad = True
w7.requires_grad = True
w8.requires_grad = True

def sigmoid(z):
    a = 1 / (1 + torch.exp(-z))
    return a

def forward_propagate(x1, x2):
    in_h1 = w1 * x1 + w3 * x2
    out_h1 = sigmoid(in_h1)  # out_h1 = torch.sigmoid(in_h1)
    in_h2 = w2 * x1 + w4 * x2
    out_h2 = sigmoid(in_h2)  # out_h2 = torch.sigmoid(in_h2)

    in_o1 = w5 * out_h1 + w7 * out_h2
    out_o1 = sigmoid(in_o1)  # out_o1 = torch.sigmoid(in_o1)
    in_o2 = w6 * out_h1 + w8 * out_h2
    out_o2 = sigmoid(in_o2)  # out_o2 = torch.sigmoid(in_o2)

    print("正向计算:o1 ,o2")
    print(out_o1.data, out_o2.data)

    return out_o1, out_o2

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()
    print("损失函数(均方误差):", loss.item())
    return loss

def update_w(w1, w2, w3, w4, w5, w6, w7, w8):
    # 步长
    step = 1
    w1.data = w1.data - step * w1.grad.data
    w2.data = w2.data - step * w2.grad.data
    w3.data = w3.data - step * w3.grad.data
    w4.data = w4.data - step * w4.grad.data
    w5.data = w5.data - step * w5.grad.data
    w6.data = w6.data - step * w6.grad.data
    w7.data = w7.data - step * w7.grad.data
    w8.data = w8.data - step * w8.grad.data
    w1.grad.data.zero_()  # 注意:将w中所有梯度清零
    w2.grad.data.zero_()
    w3.grad.data.zero_()
    w4.grad.data.zero_()
    w5.grad.data.zero_()
    w6.grad.data.zero_()
    w7.grad.data.zero_()
    w8.grad.data.zero_()
    return w1, w2, w3, w4, w5, w6, w7, w8

if __name__ == "__main__":

    print("=====更新前的权值=====")
    print(w1.data, w2.data, w3.data, w4.data, w5.data, w6.data, w7.data, w8.data)

    for i in range(1000):
        print("=====第" + str(i) + "轮=====")
        L = loss_fuction(x1, x2, y1, y2)  # 前向传播,求 Loss,构建计算图
        L.backward()  # 自动求梯度,不需要人工编程实现。反向传播,求出计算图中所有梯度存入w中
        print("\tgrad W: ", round(w1.grad.item(), 2), round(w2.grad.item(), 2), round(w3.grad.item(), 2),
              round(w4.grad.item(), 2), round(w5.grad.item(), 2), round(w6.grad.item(), 2), round(w7.grad.item(), 2),
              round(w8.grad.item(), 2))
        w1, w2, w3, w4, w5, w6, w7, w8 = update_w(w1, w2, w3, w4, w5, w6, w7, w8)

    print("更新后的权值")
    print(w1.data, w2.data, w3.data, w4.data, w5.data, w6.data, w7.data, w8.data)

代码执行结果:

=====1000=====
正向计算,隐藏层h1 ,h2:tensor([0.7750]) tensor([0.5920])
正向计算,预测值o1 ,o2:tensor([0.2296]) tensor([0.0098])
损失函数(均方误差)0.003185197012498975
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([1.6515])  tensor([0.1770])  tensor([1.3709])  tensor([0.9462])  tensor([-0.7798])  tensor([-4.2741])  tensor([-1.0236])  tensor([-2.1999])  

损失函数相差0.00029,可以认为损失差别不大,且极其接近最优解。

更改步长及迭代次数

步长为1 训练1000轮

=====1000=====
正向计算,隐藏层h1 ,h2:tensor([0.7750]) tensor([0.5920])
正向计算,预测值o1 ,o2:tensor([0.2296]) tensor([0.0098])
损失函数(均方误差)0.003185197012498975
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([1.6515])  tensor([0.1770])  tensor([1.3709])  tensor([0.9462])  tensor([-0.7798])  tensor([-4.2741])  tensor([-1.0236])  tensor([-2.1999])  

步长为1 训练5000轮

正向计算,隐藏层h1 ,h2:tensor([0.8237]) tensor([0.6482])
正向计算,预测值o1 ,o2:tensor([0.2299]) tensor([0.0020])
损失函数(均方误差)0.0025912360288202763
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([2.0996])  tensor([0.5281])  tensor([1.6398])  tensor([1.1568])  tensor([-0.7065])  tensor([-5.2335])  tensor([-0.9668])  tensor([-2.9425])  

步长为1 训练10000轮

=====10000=====
正向计算,隐藏层h1 ,h2:tensor([0.8410]) tensor([0.6734])
正向计算,预测值o1 ,o2:tensor([0.2300]) tensor([0.0010])
损失函数(均方误差)0.0025182669050991535
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([2.2820])  tensor([0.6934])  tensor([1.7492])  tensor([1.2560])  tensor([-0.6798])  tensor([-5.6378])  tensor([-0.9456])  tensor([-3.2633])  

步长为5 训练1000轮

=====1000=====
正向计算,隐藏层h1 ,h2:tensor([0.8225]) tensor([0.6443])
正向计算,预测值o1 ,o2:tensor([0.2299]) tensor([0.0020])
损失函数(均方误差)0.002591301454231143
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([2.0871])  tensor([0.5032])  tensor([1.6323])  tensor([1.1419])  tensor([-0.7079])  tensor([-5.2478])  tensor([-0.9722])  tensor([-2.9524])  

步长为5 训练5000轮

=====5000=====
正向计算,隐藏层h1 ,h2:tensor([0.8598]) tensor([0.7028])
正向计算,预测值o1 ,o2:tensor([0.2300]) tensor([0.0004])
损失函数(均方误差)0.0024759655352681875
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([2.4998])  tensor([0.8953])  tensor([1.8799])  tensor([1.3772])  tensor([-0.6491])  tensor([-6.1724])  tensor([-0.9252])  tensor([-3.6919])  

步长为5 训练10000轮

=====10000=====
正向计算,隐藏层h1 ,h2:tensor([0.8726]) tensor([0.7265])
正向计算,预测值o1 ,o2:tensor([0.2300]) tensor([0.0002])
损失函数(均方误差)0.002462496282532811
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([2.6617])  tensor([1.0664])  tensor([1.9770])  tensor([1.4799])  tensor([-0.6286])  tensor([-6.5554])  tensor([-0.9082])  tensor([-4.0079])  

步长为0.1 训练1000轮

=====1000=====
正向计算,隐藏层h1 ,h2:tensor([0.6871]) tensor([0.5292])
正向计算,预测值o1 ,o2:tensor([0.2379]) tensor([0.0736])
损失函数(均方误差)0.010344177484512329
w的梯度:   -0.0  -0.0  -0.0  -0.0  0.0  0.01  0.0  0.01  
更新后的权值w:
tensor([0.9891])  tensor([-0.1984])  tensor([0.9735])  tensor([0.7210])  tensor([-0.8598])  tensor([-2.8373])  tensor([-1.0837])  tensor([-1.1030])  

步长为0.1 训练5000轮

=====5000=====
正向计算,隐藏层h1 ,h2:tensor([0.7512]) tensor([0.5715])
正向计算,预测值o1 ,o2:tensor([0.2292]) tensor([0.0186])
损失函数(均方误差)0.003924463409930468
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([1.4572])  tensor([0.0529])  tensor([1.2543])  tensor([0.8717])  tensor([-0.8159])  tensor([-3.8519])  tensor([-1.0503])  tensor([-1.8777])  

步长为0.1 训练10000轮

=====10000=====
正向计算,隐藏层h1 ,h2:tensor([0.7754]) tensor([0.5929])
正向计算,预测值o1 ,o2:tensor([0.2296]) tensor([0.0098])
损失函数(均方误差)0.003185402136296034
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([1.6546])  tensor([0.1826])  tensor([1.3728])  tensor([0.9496])  tensor([-0.7792])  tensor([-4.2705])  tensor([-1.0223])  tensor([-2.1968])  

从运行结果来看,训练轮次越多步长越大,损失函数越小。

更改损失函数和激活函数

Sigmoid换为PyTorch自带函数torch.sigmoid()

代码实现如下:

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_h2
    out_o1 = torch.sigmoid(in_o1)
    in_o2 = w[5] * out_h1 + w[7] * out_h2
    out_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

代码执行结果:

=====1000=====
正向计算,隐藏层h1 ,h2:tensor([0.7750]) tensor([0.5920])
正向计算,预测值o1 ,o2:tensor([0.2296]) tensor([0.0098])
损失函数(均方误差)0.003185197012498975
w的梯度:   -0.0  -0.0  -0.0  -0.0  -0.0  0.0  -0.0  0.0  
更新后的权值w:
tensor([1.6515])  tensor([0.1770])  tensor([1.3709])  tensor([0.9462])  tensor([-0.7798])  tensor([-4.2741])  tensor([-1.0236])  tensor([-2.1999])  
进程已结束,退出代码0

Sigmoid换为ReLU

代码实现如下:

def forward_propagate(x):  # 计算图
    in_h1 = w[0] * x[0] + w[2] * x[1]
    out_h1 = torch.relu(in_h1)
    in_h2 = w[1] * x[0] + w[3] * x[1]
    out_h2 = torch.relu(in_h2)
 
    in_o1 = w[4] * out_h1 + w[6] * out_h2
    out_o1 = torch.relu(in_o1)
    in_o2 = w[5] * out_h1 + w[7] * out_h2
    out_o2 = torch.relu(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

代码执行结果:

=====1000=====
正向计算,隐藏层h1 ,h2:tensor([0.4055]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.2300]) tensor([0.])
损失函数(均方误差)0.0024500000290572643
w的梯度:   -0.0  0.0  -0.0  0.0  -0.0  0.0  0.0  0.0  
更新后的权值w:
tensor([0.4287])  tensor([-0.4000])  tensor([0.6372])  tensor([0.6000])  tensor([0.5672])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  

代码向上翻一翻

=====12=====
正向计算,隐藏层h1 ,h2:tensor([0.3777]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.1916]) tensor([0.])
损失函数(均方误差)0.0031862861942499876
w的梯度:   -0.01  0.0  -0.01  0.0  -0.01  0.0  0.0  0.0  
更新后的权值w:
tensor([0.3975])  tensor([-0.4000])  tensor([0.6185])  tensor([0.6000])  tensor([0.5219])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  
=====13=====
正向计算,隐藏层h1 ,h2:tensor([0.3843]) tensor([0.])
正向计算,预测值o1 ,o2:tensor([0.2006]) tensor([0.])
损失函数(均方误差)0.0028835302218794823
w的梯度:   -0.01  0.0  -0.0  0.0  -0.01  0.0  0.0  0.0  
更新后的权值w:
tensor([0.4052])  tensor([-0.4000])  tensor([0.6231])  tensor([0.6000])  tensor([0.5332])  tensor([-0.5000])  tensor([-0.3000])  tensor([0.8000])  

很明显,损失函数的值下降很快,仅13轮时就已经比使用Sigmoid函数训练1000轮时的值低。

为什么?

第一,采用Sigmoid等函数,算激活函数是(指数运算),计算量大;反向传播求误差梯度时,求导涉及除法,计算量相对大。而采用Relu激活函数,整个过程的计算量节省很多。
第二,对于深层网络,Sigmoid函数反向传播时,很容易就会出现梯度消失的情况(在sigmoid接近饱和区时,变换太缓慢,导数趋于0),这种情况会造成信息丢失,梯度消失在网络层数多的时候尤其明显,从而无法完成深层网络的训练。
第三,ReLU会使一部分神经元的输出为0,这样就造成了网络的稀疏性,并且减少了参数的相互依存关系,缓解了过拟合问题的发生。

MSE换为PyTorch自带函数t.nn.MSELoss()

def loss_fuction(x1, x2, y1, y2):  # 损失函数
    y1_pred, y2_pred = forward_propagate(x1, x2)  # 前向传播
    mse = torch.nn.MSELoss()
    loss =mse(y1_pred,y1) + mse(y2_pred,y2)  # 考虑 : t.nn.MSELoss()
    print("损失函数(均方误差):", loss.item())
    return loss

代码执行结果:

=====999=====
正向计算:o1 ,o2
tensor([0.2298]) tensor([0.0050])
损失函数(均方误差): 0.005628134589642286
	grad W:  -0.0 -0.0 -0.0 -0.0 -0.0 0.0 -0.0 0.0
更新后的权值
tensor([1.8441]) tensor([0.3147]) tensor([1.4865]) tensor([1.0288]) tensor([-0.7469]) tensor([-4.6932]) tensor([-0.9992]) tensor([-2.5217])

MSE换为交叉熵

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

代码执行结果:

=====999=====
正向计算:o1 ,o2
tensor([0.9929]) tensor([0.0072])
损失函数(交叉熵损失): -0.018253758549690247
	grad W:  -0.0 -0.0 -0.0 -0.0 -0.0 0.0 -0.0 0.0
更新后的权值
tensor([2.2809]) tensor([0.6580]) tensor([1.7485]) tensor([1.2348]) tensor([3.8104]) tensor([-4.2013]) tensor([2.5933]) tensor([-2.0866])

注意:需要将输入通过Logsoftmax函数得到输出概率后再进行负对数似然损失函数计算。

权值初始值替换

替换为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])  # 权重初始值

代码执行结果:

=====更新前的权值=====
tensor([0.]) tensor([0.]) tensor([0.]) tensor([0.]) tensor([0.]) tensor([0.]) tensor([0.]) tensor([0.])
=====0=====
正向计算:o1 ,o2
tensor([0.5000]) tensor([0.5000])
损失函数(均方误差): 0.1988999992609024
	grad W:  0.0 0.0 0.0 0.0 0.03 0.07 0.03 0.07
=====118=====
正向计算:o1 ,o2
tensor([0.2336]) tensor([0.0654])
损失函数(均方误差): 0.009175034239888191
	grad W:  -0.0 -0.0 -0.0 -0.0 0.0 0.01 0.0 0.01
=====119=====
正向计算:o1 ,o2
tensor([0.2334]) tensor([0.0649])
损失函数(均方误差): 0.009111026301980019
	grad W:  -0.0 -0.0 -0.0 -0.0 0.0 0.0 0.0 0.0
=====999=====
正向计算:o1 ,o2
tensor([0.2296]) tensor([0.0096])
损失函数(均方误差): 0.003165625501424074
	grad W:  -0.0 -0.0 -0.0 -0.0 -0.0 0.0 -0.0 0.0
更新后的权值
tensor([1.1809]) tensor([1.1809]) tensor([0.7085]) tensor([0.7085]) tensor([-0.8765]) tensor([-3.3598]) tensor([-0.8765]) tensor([-3.3598])

替换为随机数

w1, w2, w3, w4, w5, w6, w7, w8 = 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)  # 权重初始值

代码执行结果:

=====更新前的权值=====
tensor([[0.1843]]) tensor([[1.4344]]) tensor([[-0.1303]]) tensor([[-0.1704]]) tensor([[-1.9632]]) tensor([[0.5791]]) tensor([[-0.4025]]) tensor([[-0.2650]])
=====0=====
正向计算:o1 ,o2
tensor([[0.2187]]) tensor([[0.5305]])
损失函数(均方误差): 0.1803714483976364
	grad W:  0.01 -0.0 0.01 -0.0 -0.0 0.08 -0.0 0.1
=====129=====
正向计算:o1 ,o2
tensor([[0.2261]]) tensor([[0.0566]])
损失函数(均方误差): 0.008026348426938057
	grad W:  -0.0 -0.0 -0.0 -0.0 -0.0 0.0 -0.0 0.01
=====130=====
正向计算:o1 ,o2
tensor([[0.2261]]) tensor([[0.0563]])
损失函数(均方误差): 0.007982097566127777
	grad W:  -0.0 -0.0 -0.0 -0.0 -0.0 0.0 -0.0 0.0
=====999=====
正向计算:o1 ,o2
tensor([[0.2296]]) tensor([[0.0097]])
损失函数(均方误差): 0.0031767673790454865
	grad W:  -0.0 -0.0 -0.0 -0.0 -0.0 0.0 -0.0 0.0
更新后的权值
tensor([[0.6406]]) tensor([[2.5083]]) tensor([[0.1434]]) tensor([[0.4740]]) tensor([[-1.8034]]) tensor([[-2.2541]]) tensor([[-0.1830]]) tensor([[-4.1123]])

显然,最后的结果跟初始权重无关,初始权重只影响下降速度。

作业总结

本次主要是对前馈神经网络的部分内容进行了深入学习,包括激活函数和损失函数、权值/训练轮次/训练步长对神经网络输出结果的影响。一个合适的方法,可以大幅减少训练轮次,提升训练速度和训练质量。所以我们应当大量应用ReLU函数,因为这真的好节省时间啊确信

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值