【作业4】 BP算法

点击查看需要完成的任务

目录

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

二、数值计算 - 手动计算,掌握细

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

1.对比【numpy】和【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,观察、总结并陈述。

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


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


 一个数据点的所有输出误差平方和:

整个数据集D的累积误差:

链式法则计算梯度

  


二、数值计算 - 手动计算,掌握细节



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


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

【numpy】版:

import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt

'''
模型结构及参数定义:一个简单的两层神经网络,包含一个隐藏层和一个输出层
    ·输入层:两个输入节点,分别为 x1 和 x2,表示输入的数据
    ·隐藏层:两个神经元,分别为 h1 和 h2。
        输入层与隐藏层之间的权重 :w[0],w[1],w[2],w[3]
        输入:权重 w[0],w[1],w[2],w[3]和输入值 x1, x2 进行加权线性组合得到 in_h1,in_h2
        输出:经过激活函数处理,得到隐藏层输出out_h1 ,out_h2 
    .输出层:两个神经元,分别为 o1 和 o2,用于预测输出
        隐藏层与输出层之间的权重 w[4],w[5],w[6],w[7]
        输入:权重  w[4],w[5],w[6],w[7]和隐藏层输出值out_h1 ,out_h2进行加权线性组合得到 in_o1,in_o2
        输出:经过激活函数处理,得到输出层输出out_o1 ,out_o2 
---------------------------------------------------------------
均方误差(MSE)计算预测值 out_o1, out_o2 与真实值 y1, y2 之间的误差,
根据误差计算每个权重 w[0] 至 w[7] 的梯度,然后通过梯度下降法调整权重。   
---------------------------------------------------------------
'''

# 激活函数 - Sigmoid函数
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

# 前向传播过程 - 计算隐藏层和输出层的值
def forward_propagate(x1, x2, y1, y2, w):
    # 计算隐藏层的输入和输出
    in_h1 = w[0] * x1 + w[2] * x2  # 线性组合
    out_h1 = sigmoid(in_h1)  # 激活
    in_h2 = w[1] * x1 + w[3] * x2
    out_h2 = sigmoid(in_h2)

    # 计算输出层的输入和输出
    in_o1 = w[4] * out_h1 + w[6] * out_h2
    out_o1 = sigmoid(in_o1)
    in_o2 = w[5] * out_h1 + w[7] * out_h2
    out_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 + (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, y1, y2, w):
    # 输出层的误差
    d_o1 = out_o1 - y1
    d_o2 = out_o2 - y2
    # 计算输出层到隐藏层的权重梯度
    d_w = np.zeros(8)
    d_w[4] = d_o1 * out_o1 * (1 - out_o1) * out_h1
    d_w[6] = d_o1 * out_o1 * (1 - out_o1) * out_h2
    d_w[5] = d_o2 * out_o2 * (1 - out_o2) * out_h1
    d_w[7] = d_o2 * out_o2 * (1 - out_o2) * out_h2
    # 计算隐藏层到输入层的权重梯度
    d_h1 = (d_o1 * w[4] + d_o2 * w[5]) * out_h1 * (1 - out_h1)
    d_h2 = (d_o1 * w[6] + d_o2 * w[7]) * out_h2 * (1 - out_h2)
    d_w[0] = d_h1 * x1
    d_w[2] = d_h1 * x2
    d_w[1] = d_h2 * x1
    d_w[3] = d_h2 * x2
    print('更新后的权值:',d_w)
    return d_w

# 更新权值
def update_weight(w, d_w, step):
    return w - step * d_w

if __name__ == "__main__":
    # 定义初始权值参数 w
    w = np.array([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.2, 0.7  # 真实输出
    print("输入值 x1, x2:", x1, x2)
    print("目标输出值 y1, y2:", y1, y2)
    print("初始权值:", w.round(2))
    Error = []
    epoh = 10  # 训练次数
    step = float(input('请输入步长:'))

    for i in range(epoh):
        print("=====第{}轮=====".format(i + 1))
        out_o1, out_o2, out_h1, out_h2, error = forward_propagate(x1, x2, y1, y2, w)
        Error.append(error)
        d_w = back_propagate(out_o1, out_o2, out_h1, out_h2, y1, y2, w)
        w = update_weight(w, d_w, step)

    # 绘图
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
    plt.plot(range(epoh), Error)
    plt.xlabel('迭代轮次')
    plt.ylabel('均方误差')
    plt.title('步长为:{}'.format(step))
    plt.show()

运行结果(部分)

输入值 x1, x2: 0.5 0.3
目标输出值 y1, y2: 0.2 0.7
初始权值: [ 0.2 -0.4  0.5  0.6  0.1 -0.5 -0.3  0.8]
请输入步长:1
=====第50轮=====
正向计算,隐藏层h1 ,h2的输出:0.58239 0.65033
正向计算,输出层的最终预测值o1 ,o2:0.24533 0.68559
更新后的权值: [-0.0037651  -0.00771668 -0.00225906 -0.00463001  0.00488786 -0.00180847
  0.00545811 -0.00201946]

=====第184轮=====
正向计算,隐藏层h1 ,h2的输出:0.6046 0.68295
正向计算,输出层的最终预测值o1 ,o2:0.20093 0.70024
更新后的权值: [-9.50787880e-05 -9.38699376e-05 -5.70472728e-05 -5.63219625e-05
  9.00648707e-05  3.10786630e-05  1.01737549e-04  3.51065513e-05]

=====第379轮=====
正向计算,隐藏层h1 ,h2的输出:0.60518 0.68345
正向计算,输出层的最终预测值o1 ,o2:0.2 0.7
更新后的权值: [-5.11867704e-07 -4.56782455e-07 -3.07120623e-07 -2.74069473e-07
  4.80587731e-07  2.10193766e-07  5.42748790e-07  2.37381033e-07]


=====第500轮=====
正向计算,隐藏层h1 ,h2的输出:0.60518 0.68345
正向计算,输出层的最终预测值o1 ,o2:0.2 0.7
更新后的权值: [-2.01684800e-08 -1.79465574e-08 -1.21010880e-08 -1.07679344e-08
  1.89340387e-08  8.32786456e-09  2.13830093e-08  9.40500904e-09]

 

【分析】步长设置为1时,由图可看出模型在第80轮左右达到均方误差基本为0,达到收敛

【pytorch版】

import torch
import matplotlib

matplotlib.use('TkAgg')
import matplotlib.pyplot as plt


def sigmoid(x):
    return 1 / (1 + torch.exp(-x))


def forward(x, w, y):
    h1 = sigmoid(w[0] * x[0] + w[2] * x[1])
    h2 = sigmoid(w[1] * x[0] + w[3] * x[1])
    o1 = sigmoid(w[4] * h1 + w[6] * h2)
    o2 = sigmoid(w[5] * h1 + w[7] * h2)
    error = ((o1 - y[0]) ** 2 + (o2 - y[1]) ** 2) / 2
    return o1, o2, error


def loss(x, y, w):
    y_pre, _, error = forward(x, w, y)
    return error


def train(x, y, w, epoch, step):
    errors = []
    for i in range(epoch):
        print("\n=====第" + str(i + 1) + "轮=====")
        w.grad = None  # 清零梯度
        error = loss(x, y, w)  # 计算损失
        errors.append(error.item())

        error.backward()  # 反向传播
        with torch.no_grad():
            w -= step * w.grad  # 更新权重

        print(f'均方误差={error.item():.5f},\n 更新后的权值={w.tolist()}')
        print("正向计算,预测值:", round(forward(x, w, y)[0].item(), 5), round(forward(x, w, y)[1].item(), 5))
        print("w的梯度:", [round(grad.item(), 5) for grad in w.grad])

    return errors


if __name__ == "__main__":
    x = torch.tensor([0.5, 0.3])  # 输入
    y = torch.tensor([0.2, 0.7])  # 真实标签
    print("输入值",x)
    print("真实输出值",y)
    w = torch.tensor([0.2, -0.4, 0.5, 0.6, 0.1, -0.5, -0.3, 0.8], requires_grad=True)  # 权重
    epoch = 500  # 训练轮次
    step = float(input('请输入步长:'))  # 步长

    errors = train(x, y, w, epoch, step)

    # 画图
    plt.plot(range(epoch), errors)
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False  # 显示负号
    plt.xlabel('迭代轮次')
    plt.ylabel('均方误差')
    plt.title(f'步长为:{step}')
    plt.show()

运行结果(调整训练次数为500)

输入值 tensor([0.5000, 0.3000])
真实输出值 tensor([0.2000, 0.7000])
请输入步长:1
=====第10轮=====
均方误差=0.02785
正向计算,预测值: 0.39485 0.58177
w的梯度: [-3e-05, -0.00704, -2e-05, -0.00423, 0.02712, -0.01675, 0.02451, -0.01514]

=====第50轮=====
均方误差=0.00237
正向计算,预测值: 0.26073 0.67164
w的梯度: [-0.00117, -0.00269, -0.0007, -0.00161, 0.00684, -0.00368, 0.00649, -0.00349]  

=====第500轮=====
均方误差=0.00000
正向计算,预测值: 0.20001 0.7
w的梯度: [-0.0, -0.0, -0.0, -0.0, 0.0, 0.0, 0.0, 0.0] 

【分析】步长设置为1时,由图可看出模型在第80轮左右达到均方误差基本为0,达到收敛,与刚刚numpy实现效果没什么差别。

总结:

  • 两种形式的程序基本效果更新到最后loss都相同,梯度小到0,达到一个最优解。
  • pytorch可以直接使用backward函数反向传播,不需要再手动计算梯度,比较方便;而numpy还需要一个一个算,这里注意每次计算完各个权重的梯度之后要记得将梯度清零,否则再次计算时,梯度会累加。

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

通过刚刚pytorch程序与numpy程序的对比,可以看出使用自带的sigmod与自己手写的结果没有什么区别(这里我修改了迭代次数,可视化结果进行对比)

 前者为pytorch版本,后者为numpy版本,没有什么太大差别,最终都会收敛。

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

def forward(x, w, y):
    h1 = torch.relu(w[0] * x[0] + w[2] * x[1])
    h2 = torch.relu(w[1] * x[0] + w[3] * x[1])
    o1 = torch.relu(w[4] * h1 + w[6] * h2)
    o2 = torch.relu(w[5] * h1 + w[7] * h2)
    error = ((o1 - y[0]) ** 2 + (o2 - y[1]) ** 2) / 2
    return o1, o2, error

【分析】Relu函数比Sigmoid函数的效果要好,步长为1时,使用Relu函数,迭代10次左右就可以使均方误差达到最小,而使用Sigmoid函数要迭代100次左右才能使均方误差达到最小,Relu函数的效率更高。

why??

梯度传递:ReLU 函数在正区间的梯度恒为 1,这意味着在正值输入时可以有效地传递梯度,避免了梯度消失的问题。Sigmoid 函数在输入过大或过小时,梯度会趋近于 0,从而使得更新权重变得缓慢。

计算效率:ReLU 的计算比 Sigmoid 简单,因为它仅需进行一个线性阈值判断,而 Sigmoid 需要计算指数函数。

更好地适应稀疏输入:ReLU将所有负数映射为0,这意味着只有一部分神经元对输出产生贡献,其余神经元的输出都是0,相当于对输入的某些特征进行了稀疏的选择和压缩,有助于减少过拟合问题。


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

def loss(x, y, w):
    y_pre = forward(x, w)  # 预测输出
    y_pre_tensor = torch.stack(y_pre)  # 使用stack将预测值组合成一个张量
    mse_loss = nn.MSELoss()  # 创建MSELoss对象
    return mse_loss(y_pre_tensor, y)  # 使用MSELoss计算损失

运行结果(部分)

=====第10轮=====
均方误差=0.02785,
 更新后的权值=[0.18523848056793213, -0.32900500297546387, 0.4911430776119232, 0.6425970792770386, -0.22692103683948517, -0.2981908619403839, -0.5915791988372803, 0.9799925684928894]
正向计算,预测值: 0.39485 0.58177
w的梯度: [-3e-05, -0.00704, -2e-05, -0.00423, 0.02712, -0.01675, 0.02451, -0.01514]
=====第71轮=====
均方误差=0.00081,
 更新后的权值=[0.2557661831378937, -0.09925784915685654, 0.5334596037864685, 0.7804454565048218, -0.8914641737937927, 0.0896066352725029, -1.2102655172348022, 1.3406552076339722]
正向计算,预测值: 0.23681 0.68637
w的梯度: [-0.00077, -0.00153, -0.00046, -0.00092, 0.0039, -0.00174, 0.00372, -0.00166]
=====第200轮=====
均方误差=0.00000,
 更新后的权值=[0.2906154990196228, -0.04064858704805374, 0.5543695092201233, 0.8156110644340515, -1.0546848773956299, 0.1350463181734085, -1.3668859004974365, 1.3842142820358276]
正向计算,预测值: 0.20284 0.70002
w的梯度: [-6e-05, -8e-05, -4e-05, -5e-05, 0.00027, 0.0, 0.00026, 0.0]

【分析】运行结果与手动计算损失的结果一样,但是在这里遇到了几个问题,改了还挺久的,主要还是对orch.nn.MSELoss()不够熟悉导致出现的报错(学长的代码中也有类似的报错,给我提供了参考,)这里主要注意

使用stack将预测值组合成一个张量(torch.nn.MSELoss() 期望输入和目标是同样的张量类型。 forward函数返回了两个值需要将它们合并为一个张量);否则报错

 要先创建MSELoss对象,再使用MSELoss计算损失,不能一起使用否则报错显示,因为MSELoss中没有backward函数

 参考链接

nn.MSELoss损失函数返回值介绍

MSELoss中没有backward


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


def loss(x, y, w):
    y_pre = forward(x, w)  # 获取预测值
    Cro_loss = torch.nn.CrossEntropyLoss()  # 创建交叉熵损失函数
    # 计算损失
    loss_value = Cro_loss(torch.stack(y_pre), y)  # 增加一个维度
    print("损失函数(交叉熵损失):", loss_value.item())
    return loss_value
输入值 tensor([0.5000, 0.3000])
真实输出值 tensor([0.2000, 0.7000])
请输入步长:1

 根据下图我发现均方误差无法达到0

查阅了很多资料,主要是给出的数据和激活函数使用不当的问题 ,就像如果给出y为[0.2 -0.7]时误差会是负数。(下面是学姐对此的解释)

交叉熵的应用场景及一些注意事项: 

  • 分类任务:交叉熵损失函数广泛用于分类问题,尤其是多类分类和二分类任务。它衡量的是模型预测的概率分布与真实标签分布之间的差异。

  • 概率输出:交叉熵损失函数通常与 softmax 激活函数结合使用,以获得每个类别的概率分布。在输出层,softmax 确保输出为合法的概率(即所有输出之和为1)。

  • 输入格式:确保输入给交叉熵损失函数的预测值(未经过激活的输出)和目标值(真实标签)具有正确的维度

  • PyTorch 提供的 nn.CrossEntropyLoss(),该函数内部已经处理了 softmax。

于是我修改了输入输出,改为

 # 创建输入数据和对应的真实输出
    x = [
        torch.tensor([0.5, 0.3]),
        torch.tensor([0.1, 0.2]),
    ]

    # 真实输出,0表示类别0,1表示类别1
    y = torch.tensor([1, 0])  # 直接使用类别索引

最终样本输出了两个值,代表两个类别的概率 ,直接看图,可以发现误差逐渐接近于0。

完整代码(取消了输出层的sigmod激活并添加了softmax函数)

import torch
import matplotlib

matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
import torch.nn as nn  # 导入nn模块


def softmax(x):
    exp_x = torch.exp(x - torch.max(x))  # 防止溢出
    return exp_x / exp_x.sum(dim=0)


def forward(x, w):
    h1 = torch.sigmoid(w[0] * x[0] + w[2] * x[1])
    h2 = torch.sigmoid(w[1] * x[0] + w[3] * x[1])
    o1 = w[4] * h1 + w[6] * h2
    o2 = w[5] * h1 + w[7] * h2
    return torch.stack([o1, o2])  # 返回未经过激活的输出


def loss(y_pre, y):
    Cro_loss = nn.CrossEntropyLoss()  # 创建交叉熵损失函数
    return Cro_loss(y_pre, y)  # 不需要额外的维度


def train(x, y, w, epoch, step):
    errors = []
    for i in range(epoch):
        print("\n=====第" + str(i + 1) + "轮=====")
        w.grad = None  # 清零梯度

        # 批量训练
        y_pre = torch.stack([forward(data, w) for data in x])
        error = loss(y_pre, y)  # 计算损失
        errors.append(error.item())

        error.backward()  # 反向传播
        with torch.no_grad():
            w -= step * w.grad  # 更新权重

        print(f'损失={error.item():.5f},\n 更新后的权值={w.tolist()}')
        print("正向计算,预测值:", softmax(y_pre).tolist())
        print("w的梯度:", [round(grad.item(), 5) for grad in w.grad])

    return errors


if __name__ == "__main__":
    # 创建输入数据和对应的真实输出
    x = [
        torch.tensor([0.5, 0.3]),
        torch.tensor([0.1, 0.2]),
    ]

    # 真实输出,0表示类别0,1表示类别1
    y = torch.tensor([1, 0])  # 直接使用类别索引

    print("输入值", x)
    print("真实输出值", y)

    w = torch.tensor([0.2, -0.4, 0.5, 0.6, 0.1, -0.5, -0.3, 0.8], requires_grad=True)  # 权重
    epoch = 1000  # 训练轮次
    step = float(input('请输入步长:'))  # 步长

    errors = train(x, y, w, epoch, step)

    # 画图
    plt.plot(range(epoch), errors)
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False  # 显示负号
    plt.xlabel('迭代轮次')
    plt.ylabel('交叉熵损失')
    plt.title(f'步长为:{step}')
    plt.show()


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

改变步长,训练次数不变:

     

 

 

观察步长为0.1、0.5、1、2、10、150、200、500时的运行结果可以发现:

  • 步长特别小:误差下降得特别慢。步长为0.01时,迭代了500次,均方误差由0.05302降到0.00235。
  • 步长较合适:误差下降得较快。如步长为2时,迭代100次内误差就能降为0.000,步长为150时,迭代大概2、3次误差就能降为0。
  • 当步长特别大:参数更新的幅度过大,使得模型无法逐渐接近最优解,而是不断跳过最优解点。没有收敛到0或者错误收敛。

【步长(学习率)是影响模型训练的重要超参数。适当的步长能够提高收敛速度和训练效果,而过小或过大的步长则会导致训练效率低下或模型不稳定。(可以采用动态学习率策略来进一步优化训练过程)】

参考调参连接

步长不变,训练次数变:

 

        训练轮数达到一定次数时,准确率或者误差会不变,之后再训练就没啥必要了,所有训练次数应该找一个比较合适的值,此时让模型停止训练可以提高效率


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

设定步长为1,迭代次数为200(比较合理)

w = torch.rand(8, requires_grad=True)  # 权重初始值

  对比初始权值为随机数与初始权值为设定值的结果可以看出,一开始的误差不一样,但是最终误差都能收敛到0,只是迭代次数会变大或变小,对模型没有太大影响。

=====第20轮=====
均方误差=0.02664,
 更新后的权值=[0.26934725046157837, 0.48926082253456116, 0.6278168559074402, 0.1668519228696823, -0.473112016916275, 0.7220044136047363, -0.09025576710700989, 0.4833618998527527]
正向计算,预测值: 0.41917 0.66727
w的梯度: [-0.00366, -0.00084, -0.0022, -0.0005, 0.03239, -0.00438, 0.03203, -0.00433]
=====第100轮=====
均方误差=0.00025,
 更新后的权值=[0.42069563269615173, 0.5744732618331909, 0.7186256051063538, 0.21797925233840942, -1.2496750354766846, 0.8253331780433655, -0.8507203459739685, 0.5845967531204224]
正向计算,预测值: 0.22175 0.699
w的梯度: [-0.0006, -0.00041, -0.00036, -0.00025, 0.00233, -0.00013, 0.00226, -0.00013]

=====第200轮=====
均方误差=0.00000,
 更新后的权值=[0.4439411759376526, 0.5907717943191528, 0.7325732707977295, 0.2277582883834839, -1.34062922000885, 0.8259215354919434, -0.9389474391937256, 0.5851688981056213]
正向计算,预测值: 0.20263 0.70013
w的梯度: [-7e-05, -5e-05, -4e-05, -3e-05, 0.00026, 2e-05, 0.00026, 2e-05]


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

 w = torch.zeros(8, requires_grad=True)  # 权重初始值


=====第10轮=====
均方误差=0.03683,
 更新后的权值=[0.01710500754415989, 0.01710500754415989, 0.010263004340231419, 0.010263004340231419, -0.3246518075466156, 0.21721895039081573, -0.3246518075466156, 0.21721895039081573]
正向计算,预测值: 0.41908 0.5544
w的梯度: [-0.00297, -0.00297, -0.00178, -0.00178, 0.02776, -0.01869, 0.02776, -0.01869]
=====第100轮=====
均方误差=0.00036,
 更新后的权值=[0.223085418343544, 0.223085418343544, 0.13385125994682312, 0.13385125994682312, -1.1511119604110718, 0.7496736645698547, -1.1511119604110718, 0.7496736645698547]
正向计算,预测值: 0.22474 0.69135
w的梯度: [-0.00081, -0.00081, -0.00048, -0.00048, 0.00237, -0.00103, 0.00237, -0.00103]
=====第200轮=====
均方误差=0.00001,
 更新后的权值=[0.25630050897598267, 0.25630050897598267, 0.15378034114837646, 0.15378034114837646, -1.2530189752578735, 0.7787061333656311, -1.2530189752578735, 0.7787061333656311]
正向计算,预测值: 0.20393 0.69981
w的梯度: [-0.0001, -0.0001, -6e-05, -6e-05, 0.00035, -2e-05, 0.00035, -2e-05]

对比上一道题权重随机值来看,初始化为0对模型也没什么太大影响,都会收敛到0,只是收敛速度会变大或变小。

  • 好的初始化:可以加快模型的收敛速度,使损失函数在较少的迭代中迅速下降。(一般设为0或随机初始化)
  • 不良初始化:可能导致收敛缓慢,甚至不收敛,

不良的初始化会使模型梯度消失或爆炸。参考以下链接。

权重初始化问题


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

        反向传播BP在上学期机器学习的时候学的还是很认真的,原理比较简单,就是通过计算损失函数关于模型参数的梯度来优化模型。【首先在前向传播过程中,将输入数据传入网络,计算隐藏层的激活值传入到输出层,最终得到一个输出结果。并通过合理的损失函数计算预测输出与实际目标之间的误差。接着反向传播优化参数,利用链式法则,从输出层开始逐层向后计算每个参数的梯度,对于每一层的参数,计算损失函数关于该参数的偏导数,并将其传递回前一层,结合当前层的激活值,更新前一层的权重和偏置的梯度,更新网络中的权重和偏置,知道输出结果不断接近真实值】当时就觉得计算梯度真的好麻烦,要有什么函数能自动实现这一步就好了,于是在这学期就了解到了backward函数!

        训练过程中还需要注意学习率的选择,以避免过大导致不收敛或过小使收敛速度过慢,同时应防止梯度消失或爆炸问题。迭代次数以及权重的初始化也应该合理,迭代次数不要太大,因为模型迭代一定次数后误差为0不会再发生变化。

        应该根据不同的问题设置合适的激活函数和损失函数,不然无法收敛或者收敛到一个不正常的值。(这里就需要掌握常见激活函数以及损失函数的作用和适用场景,下面有其他博主总结的链接)

        Pytorch框架确实好用,向损失函数的计算、反向传播只需要调用就可以了,但是!!!!啊啊啊因为是别人写好封装的代码,真正使用的时候并不能按照自己的理解去调用,需要去查函数需要输入的数据类型以及具体使用方法,否则会碰到一堆报错,就像那个均方误差函数这里卡了好几处。

过程中碰到问题对应的知识点

1、 不同的激活函数有不同的输出范围。有些激活函数的输出范围在0,1之间,适用于二元分类问题,比如本次用的sigmoid函数;(任务是回归,Sigmoid函数不适合,因为输出值被限制在0到1之间,而真实值可能超出这个范围);有些输出范围在0, +\infty之间,如ReLU函数,适用于回归问题。

        本次实验使用的sigmod函数,于是我将真实值定位(0.2,0.7)使其在0-1之间,便于分析预测值与真实值差距。【将这个模型可以视为一个二分类模型的输出,每个输出值(通过Sigmoid计算得到的)可以被解释为该样本属于某个类别的概率。例如,输出0.2表示该样本属于类别1(值为0.5)的概率为20%,输出0.7表示属于类别2(值为0.3)的概率为70%。;也可以视为数值归一化的回归任务】

激活函数对比·

损失函数对比

2、对item()函数用法做出总结:  item()函数的作用是从包含单个元素的张量中取出该元素值,即把张量转为Python数值,而不是直接取对应的元素x[i,j]。

pytorch中item函数的使用

3、在 PyTorch 中,requires_grad=True 的作用是告诉框架需要对该张量进行梯度计算,在模型的参数上设置 requires_grad=True,使得在调用 backward() 方法时可以自动计算这些参数的梯度。

requires_grad=True的解释

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值