对于大模型的学习与探索不要停止,LLM 就是“未来已来”。
前面通过简单的实操上手 Pytorch:# 轻松上手:PyTorch 预测书店销售趋势,本篇带来 Pytorch 核心引擎:autograd。
那么,autograd 在 Pytorch 中是怎样的定位呢?
一句话:autograd 为 Tensors 上的所有操作提供自动微分。 用白话来作比喻:
神经网络通过不断调整参数(类似于汽车的油门),微分是告诉网络:如果你稍微调整一下参数,预测值会如何变化(汽车速度仪表盘)。
当你加速时,仪表盘速度逐渐增加;而当你减速时,仪表盘速度逐渐减小。这个速度的变化信息,就好比微分提供的导数信息。
autograd
像是汽车的智能驾驶系统:它能够自动追踪神经网络中各个参数的变化,同时告诉你,每个参数的微小调整会对最终预测结果产生怎样的影响。
有了autograd,使得我们能够以一种更智能的方式来训练神经网络,让它逐渐学会正确的任务。
具体怎么实践呢?
简单计算
还记得:torch.Tensor
张量?通过将属性.requires_grad
设置为True,就能实现开始跟踪针对张量的所有操作。
完成计算后,再调用.backward()
来自动计算所有梯度,并将梯度累积到.grad
属性中。
我们先设置属性、然后打印看看:
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
是通过加法操作得到的;
梯度计算
基于以上,再叠加一个乘法计算,然后来计算梯度:
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()
): 最终,你冲过了比赛的终点,这就是整个计算过程的结果。
比赛的过程中,你会思考一个问题:如果我在这个位置采取稍微不同的策略,我的比赛时间会有多大的变化?
这种变化就是梯度,它告诉你在每个位置上应该如何调整你的驾驶策略,以便更快地冲过终点。
将上述代码调整一下:
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)
运行结果:
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
这表示在整个拉力赛比赛中,如果在每个位置微调 x
的值,对最终结果的影响。
比如,取张量的左上角元素,其值为 4.5。这表示如果在起点位置微调拉力赛车的某个策略,比如调整油门或方向盘,将会使比赛结果 out
增加 4.5。同理,其他位置的元素也表示了在对应位置上微调 x
对最终结果的影响。
微调展示
基于上述理论上的原理,我们则可以开始一定程度的微调了,再借助 Matplotlib 库绘制曲线图;代码如下:
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()
-
创建张量和跟踪计算:
x = torch.ones(2, 2, requires_grad=True)
先创建了一个2x2的张量
x
,并告诉PyTorch我们希望追踪它的计算历史,因为我们将在优化过程中对它进行微调。 -
初始化存储变量:
grad_history = [] out_history = []
初始化两个空列表,用于存储梯度和结果的变化。
-
进行微调并记录变化:
for i in range(50): y = x + 2 z = y * y * 3 out = z.mean()
在这个循环中,模拟一个简单的优化过程。首先,我们进入了一个“直道”(
y = x + 2
),然后进入了一个“弯道”(z = y * y * 3
)。out
是结果的均值。 -
计算梯度:
out.backward()
通过调用
backward()
方法,PyTorch 自动计算了关于x
的梯度。这个梯度告诉我们在当前参数设置下,损失函数关于参数的变化方向和强度。 -
记录梯度和结果:
if x.grad is not None: grad_history.append(x.grad.view(-1).numpy().copy()) out_history.append(out.item())
我们将梯度(经过一些处理以方便绘图)和结果的值记录到相应的列表中。
-
根据梯度微调参数:
with torch.no_grad(): x -= 0.1 * x.grad if x.grad is not None else 0
这里使用一个简单的学习率(0.1)根据梯度微调张量
x
,模拟优化算法的一次迭代。 -
清零梯度:
x.grad.zero_()
为了确保下一次计算的梯度是基于新的微调后的参数,我们在每次迭代之后都将梯度清零。
-
绘制曲线图:
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,以上便是本次分享,希望各位喜欢~ 欢迎点赞、收藏、评论 🤟 我是安东尼 🤠 人气技术博主 💥 坚持千日更文 ✍ 关注我,安东尼陪你一起度过漫长编程岁月