反向求导,多层次对应一个神经,单个神经元场景

学习这一篇的前提是,已经学会了梯度算法和线性结构算法,不明白的可以去看我之前的文章。

前面看不懂的,直接跳转到 “ 反向传播的流程 ”

底层的数学算法

z 是中间变量 u 的函数,u 是自变量 w(权重)的函数,因此 z 通过 u 间接依赖 w。
公式核心逻辑为:单路相乘

  • 对权重 w 求偏导时,仅存在 z→u→w 一条路径(如果嵌套的跟多,那就继续导),将路径上的偏导数直接相乘即可,无其他分支,无需相加。
  • 在这里插入图片描述

注意:

这一篇文章只考虑单个神经元场景,多个场景在下一篇完成。第一张图片是多个神经元的场景,第二章是本文中对应的

底层的运用逻辑

  我们知道对一个函数的求导,就是求这个函数的最值。大模型的运用就是面对海量的训练数据,找到一个最贴近输入一个x,输出一个正确的y值。这样我们的大模型在没有对应的y值时,也能通过之前我们训练的函数计算出预算出。所以训练的过程中,我们要不断的求导,直到找到最合适的 w 和 b 。

  类比人类的大脑,如图(看不懂的直接看下一张图):

在这里插入图片描述

  1. 树突就是我们的影响因数 w 和 b ,但是树突不可能只有一个,是有很多个的。

  2. 轴突,他输出信息的一个过程,在我们的反向传播可以类比成激活函数(他的作用就是放在我们的函数模型太过单一,这里的意思就是嵌套另外一个函数)

 上面的类比图只类比了一层,一组 w 和 b(树突)对应 激活函数(轴突)。现在来看多层对应单个神经元的图:
在这里插入图片描述

反向传播的流程

好的现在就根据这个来解说反向传播的流程:

  1. 我们先假设值 w1 b1 w2 b2 w3 b3,激活函数是在每一个算完 y = wx + b ,对 f(y) 再进行嵌套的一个函数。

  2. 正向运算,一步一步的计算,得到一个 y_forward 值。

  3. 将 y_forward 与真正的 y_true 进行相减去得到损失值, loss = 1/2*( y_forward - y_true )^2。(为什么要*1/2,这是因为我们方便计算倒数自己规定的,因为导一下就没有常数了,这个不影响,因为反向传播会自己寻找最合适的 w 和 b,进行平方是为了保证非负数)。

在这里插入图片描述

经理过这一步了,我们就得到了 w1 b1 w2 b2 w3 b3 y_forward loss这几个值,接下来我们就要进行反向求导,一步一步的得到最佳 w 和 b

  1. 分别对w1 b1 w2 b2 w3 b3进行求导,注意了我们求导的函数是 loss = 1/2*( y_forward - y_true )^2。求导公式如图:

在这里插入图片描述

  1. 求导之后,我们得到了w1 b1 w2 b2 w3 b3 他们的导数,然后用跟新公式对他们进行跟新。

  2. 在这里插入图片描述

  3. 如此反复,我们就能得到一个最佳的 w1 b1 w2 b2 w3 b3值。

代码展示(三层,四层就是多两个参数,思路代码是一样的)
# 这个库里面有自动帮我们计算的求导的方法,就不用自己去手动计算了
import torch

# 定义数据
x_data = [0.0, 1.0, 2.0, 3.0]
y_data = [1.0, 3.0, 5.0, 7.0]

# 第1层参数
w1 = torch.tensor([0.1], requires_grad=True)   # 第1层权重
b1 = torch.tensor([0.0], requires_grad=True)   # 第1层偏置

# 第2层参数
w2 = torch.tensor([0.1], requires_grad=True)   # 第2层权重
b2 = torch.tensor([0.0], requires_grad=True)   # 第2层偏置

# 第3层参数(输出层)
w3 = torch.tensor([0.1], requires_grad=True)   # 第3层权重
b3 = torch.tensor([0.0], requires_grad=True)   # 第3层偏置

# 定义学习率
lr = 0.1

# 定义遍历次数
epochs = 2000

# 定义期望函数
def forward(x):
    # 第一层
    z1 = w1 * x + b1
    # a1 = torch.sigmoid(z1) # 激活函数 这里知道了是线性结构就不用激活函数了,如果要的话,就嵌套进去就好了,注意几个传参就是了

    # 第二层
    z2 = w2 * z1 + b2
    # a2 = torch.sigmoid(z2) # 激活函数这里知道了是线性结构就不用激活函数了,如果要的话,就嵌套进去就好了,注意几个传参就是了

    # 第三层输出
    y_pred = w3 * z2 + b3
    return z1,z2,y_pred

# 定义损失函数
def loss_n(y_pred,y_true):
    return 0.5*( y_pred - y_true )**2

# 定义变量,方便找到最小的损失值
best_loss = float('inf')   # 先设成无穷大,方便后面比较
best_w1 = 0
best_b1 = 0
best_w2 = 0
best_b2 = 0
best_w3 = 0
best_b3 = 0

# 开始遍历计算
for epoch in range(epochs):
    total_loss = 0
    for x,y in zip(x_data,y_data):
        # 先将原始数据转化成可以计算的形式
        # 因为 PyTorch 的运算和自动求导主要针对 tensor
        x_tensor = torch.tensor([x])
        y_tensor = torch.tensor([y])

        # 拿到原始数据
        z1, z2, y_pred = forward(x_tensor)

        # 开始计算求导,用链式法则把每一层参数的梯度都求出来
        loss = loss_n(y_pred, y_tensor)
        loss.backward()

        # 梯度跟新公式
        with torch.no_grad():
            w1 -= lr * w1.grad
            b1 -= lr * b1.grad

            w2 -= lr * w2.grad
            b2 -= lr * b2.grad

            w3 -= lr * w3.grad
            b3 -= lr * b3.grad

        # 记录损失,.item()是取里面的值,因为他是tensor对象的数据
        total_loss += loss.item()

        # 清零
        w1.grad.zero_()
        b1.grad.zero_()
        w2.grad.zero_()
        b2.grad.zero_()
        w3.grad.zero_()
        b3.grad.zero_()

    # 平均损失值
    total_loss = total_loss / len(x_data)
    # 找最佳的参数
    if total_loss < best_loss:
        best_loss = total_loss
        # 找到最小的参数
        best_w1 = w1.item()
        best_b1 = b1.item()
        best_w2 = w2.item()
        best_b2 = b2.item()
        best_w3 = w3.item()
        best_b3 = b3.item()

    if epoch % 100 == 0:
        # 打印平均损失值
        print(f'epoch: {epoch} | loss: {total_loss}')
        print(f'w1: {w1.item()} | b1: {b1.item()} | w2: {w2.item()} | b2: {b2.item()} | w3: {w3.item()} | b3: {b3.item()}')

# 打印最终值
print("最终参数:==================================================")
print("w1 =", w1.item(), "b1 =", b1.item())
print("w2 =", w2.item(), "b2 =", b2.item())
print("w3 =", w3.item(), "b3 =", b3.item())
# 打印最佳值
print("最佳参数:==================================================")
print("w1 =", best_w1, "b1 =", best_b1)
print("w2 =", best_w2, "b2 =", best_b2)
print("w3 =", best_w3, "b3 =", best_b3)
w_total = w1 * w2 * w3
b_total = w3 * w2 * b1 + w3 * b2 + b3
print("最终结果============================")
print("w =", w_total.item(), "b =", b_total.item())
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐