刹车与油门:PyTorch Autograd 的赛车之旅

对于大模型的学习与探索不要停止,LLM 就是"未来已来"。

前面通过简单的实操上手 Pytorch:# 轻松上手:PyTorch 预测书店销售趋势,本篇带来 Pytorch 核心引擎:autograd。

那么,autograd 在 Pytorch 中是怎样的定位呢?

一句话:autograd 为 Tensors 上的所有操作提供自动微分。 用白话来作比喻:

神经网络通过不断调整参数(类似于汽车的油门),微分是告诉网络:如果你稍微调整一下参数,预测值会如何变化(汽车速度仪表盘)。

当你加速时,仪表盘速度逐渐增加;而当你减速时,仪表盘速度逐渐减小。这个速度的变化信息,就好比微分提供的导数信息。

autograd像是汽车的智能驾驶系统:它能够自动追踪神经网络中各个参数的变化,同时告诉你,每个参数的微小调整会对最终预测结果产生怎样的影响。

有了autograd,使得我们能够以一种更智能的方式来训练神经网络,让它逐渐学会正确的任务。

具体怎么实践呢?

简单计算

还记得:torch.Tensor张量?通过将属性.requires_grad设置为True,就能实现开始跟踪针对张量的所有操作。

完成计算后,再调用.backward()来自动计算所有梯度,并将梯度累积到.grad属性中。

我们先设置属性、然后打印看看:

ini 复制代码
import torch

# 创建一个张量并设置requires_grad=True,开始跟踪计算
x = torch.ones(2, 2, requires_grad=True)
print(x)

# 对张量进行操作
y = x + 2
print(y)
print(y.grad_fn)

y.grad_fn 显示 <AddBackward0 object at ...>。这表示 y 是通过加法操作得到的;

梯度计算

基于以上,再叠加一个乘法计算,然后来计算梯度:

ini 复制代码
import torch

# 创建一个张量并设置requires_grad=True,开始跟踪计算
x = torch.ones(2, 2, requires_grad=True)
print(x)

# 对张量进行操作
y = x + 2
print(y)
print(y.grad_fn)

# 叠加操作
z = y * y * 3
out = z.mean()

# 计算梯度
out.backward()

# 打印梯度 d(out)/dx
print(x.grad)

调用 out.backward() 表示计算 out 相对于所有具有 requires_grad=True 的张量的梯度;

最后,打印了 x 相对于 out 的梯度,d(out)/d(x);

拉力比赛

且慢,如果觉得上述不太好理解, 我们用"开车"比喻:

你参加了一场拉力赛,目标是尽量快地开车冲过终点(out),整个比赛道路是由一系列拉力赛中的弯道和直道组成。

1、 起点(x): 你的车停在起点,表示比赛的初始状态,就是在比赛开始前的你的位置。

2、第一段直道(y = x + 2): 你沿着第一段直道加速行驶,表示进行了一个加法操作,像是在拉力赛中的一段平稳的直道,你可以提速。

3、弯道(z = y * y * 3): 进入弯道,表示进行平方和乘法操作;弯道可能有些曲折,需要通过巧妙的驾驶技巧来维持速度。

4、 终点(out = z.mean()): 最终,你冲过了比赛的终点,这就是整个计算过程的结果。

比赛的过程中,你会思考一个问题:如果我在这个位置采取稍微不同的策略,我的比赛时间会有多大的变化?

这种变化就是梯度,它告诉你在每个位置上应该如何调整你的驾驶策略,以便更快地冲过终点。

将上述代码调整一下:

ini 复制代码
import torch

# 创建一个张量并设置requires_grad=True,开始跟踪计算
x = torch.ones(2, 2, requires_grad=True)
print("比赛起始位置 x:", x)

# 进入第一段直道
y = x + 2
print("进入第一段直道 y:", y)
print("y 的梯度(直道的加速度):", y.grad_fn.next_functions[0][0].variable.grad)

# 进入弯道
z = y * y * 3
out = z.mean()

# 计算梯度
out.backward()

# 打印梯度 d(out)/dx
print("x 的梯度(终点前的整体策略):", x.grad)

运行结果:

lua 复制代码
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

这表示在整个拉力赛比赛中,如果在每个位置微调 x 的值,对最终结果的影响。

比如,取张量的左上角元素,其值为 4.5。这表示如果在起点位置微调拉力赛车的某个策略,比如调整油门或方向盘,将会使比赛结果 out 增加 4.5。同理,其他位置的元素也表示了在对应位置上微调 x 对最终结果的影响。

微调展示

基于上述理论上的原理,我们则可以开始一定程度的微调了,再借助 Matplotlib 库绘制曲线图;代码如下:

scss 复制代码
import torch
import matplotlib.pyplot as plt

# 创建一个张量并设置requires_grad=True,开始跟踪计算
x = torch.ones(2, 2, requires_grad=True)

# 存储梯度和结果的变化
grad_history = []
out_history = []

# 进行微调并记录结果变化
for i in range(50):
    # 进入第一段直道
    y = x + 2

    # 进入弯道
    z = y * y * 3
    out = z.mean()

    # 记录梯度和结果
    if x.grad is not None:
        grad_history.append(x.grad.view(-1).numpy().copy())
    out_history.append(out.item())

    # 计算梯度
    out.backward()

    # 根据梯度微调 x,模拟优化过程
    with torch.no_grad():
        x -= 0.1 * x.grad if x.grad is not None else 0  # 这里使用一个简单的学习率

    # 清零梯度,以便下一次计算
    x.grad.zero_()

# 绘制曲线图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
ax1.plot(grad_history)
ax1.set_title('Gradient Changes')
ax1.set_xlabel('Iteration')
ax1.set_ylabel('Gradient Value')

ax2.plot(out_history)
ax2.set_title('Result Changes')
ax2.set_xlabel('Iteration')
ax2.set_ylabel('Result Value')

plt.show()
  1. 创建张量和跟踪计算:

    python 复制代码
    x = torch.ones(2, 2, requires_grad=True)

    先创建了一个2x2的张量 x,并告诉PyTorch我们希望追踪它的计算历史,因为我们将在优化过程中对它进行微调。

  2. 初始化存储变量:

    python 复制代码
    grad_history = []
    out_history = []

    初始化两个空列表,用于存储梯度和结果的变化。

  3. 进行微调并记录变化:

    python 复制代码
    for i in range(50):
        y = x + 2
        z = y * y * 3
        out = z.mean()

    在这个循环中,模拟一个简单的优化过程。首先,我们进入了一个"直道"(y = x + 2),然后进入了一个"弯道"(z = y * y * 3)。out是结果的均值。

  4. 计算梯度:

    python 复制代码
        out.backward()

    通过调用 backward() 方法,PyTorch 自动计算了关于 x 的梯度。这个梯度告诉我们在当前参数设置下,损失函数关于参数的变化方向和强度。

  5. 记录梯度和结果:

    python 复制代码
        if x.grad is not None:
            grad_history.append(x.grad.view(-1).numpy().copy())
    out_history.append(out.item())

    我们将梯度(经过一些处理以方便绘图)和结果的值记录到相应的列表中。

  6. 根据梯度微调参数:

    python 复制代码
        with torch.no_grad():
            x -= 0.1 * x.grad if x.grad is not None else 0

    这里使用一个简单的学习率(0.1)根据梯度微调张量 x,模拟优化算法的一次迭代。

  7. 清零梯度:

    python 复制代码
        x.grad.zero_()

    为了确保下一次计算的梯度是基于新的微调后的参数,我们在每次迭代之后都将梯度清零。

  8. 绘制曲线图:

    python 复制代码
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
    ax1.plot(grad_history[1:])  # 忽略第一次迭代
    ax1.set_title('Gradient Changes')
    ax1.set_xlabel('Iteration')
    ax1.set_ylabel('Gradient Value')
    
    ax2.plot(out_history)
    ax2.set_title('Result Changes')
    ax2.set_xlabel('Iteration')
    ax2.set_ylabel('Result Value')
    
    plt.show()

从左图我们可以观察到,刚开始梯度值较大,表示在微调的初期,模型参数需要较大的调整。随着微调的进行,梯度值逐渐减小,说明模型参数逐渐接近最优点。当梯度值接近零时,说明模型参数已经趋于稳定,微调的幅度逐渐减小。


OK,以上,通过使用一个简单的学习率来微调模型参数,演示了学习率对优化过程的影响。

使用 PyTorch 的自动微分功能 Autograd,就可以轻松地计算模型参数的梯度,而不需要手动推导梯度公式啦~~

OK,以上便是本次分享,希望各位喜欢~ 欢迎点赞、收藏、评论 🤟 我是安东尼 🤠 人气技术博主 💥 坚持千日更文 ✍ 关注我,安东尼陪你一起度过漫长编程岁月

相关推荐
今夕资源网13 分钟前
github开源 网页在线音频转文本工具 node.js+html源码
github·音频转文本·音频转字幕·音频转srt·音频转srt字幕文件·音频转lrc·音频转txt
AI语宙漫游指南23 分钟前
从 CV 扩散到 NLP:详解 Google DiffusionGemma 架构、推理机制与优劣
深度学习·llm
牛油果子哥q38 分钟前
AVL平衡树与红黑树深度精讲对比,平衡因子、四大旋转原理、着色规则、平衡策略、性能差异与面试手撕全解
数据结构·c++·面试
程序员cxuan1 小时前
瑞幸出 CLI 了,这会是迈向 AGI 的第一步吗?
ai·llm·agi
得要找到一束光2 小时前
git详细命令
git·github
智泊AI2 小时前
为什么现在大家都在扎堆转 Agent 流程架构师?
llm
LeahDizon2 小时前
AI Coding 协作实践方案
程序员·github·代码规范
星哥的编程之路2 小时前
别再调 API 就说自己会 RAG 了,看看真正的企业级 AI 智能体长什么样
后端·面试
2601_961875243 小时前
花生十三公考课程|网课|视频
数据库·windows·git·svn·eclipse·github