前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >刹车与油门:PyTorch Autograd 的赛车之旅

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

作者头像
掘金安东尼
发布2024-02-05 14:42:40
1250
发布2024-02-05 14:42:40
举报
文章被收录于专栏:掘金安东尼掘金安东尼

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

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

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

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

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

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

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

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

具体怎么实践呢?

简单计算

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

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

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

代码语言:javascript
复制
import torch

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

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

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

梯度计算

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

代码语言:javascript
复制
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)
image.png
image.png

调用 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()**): 最终,你冲过了比赛的终点,这就是整个计算过程的结果。

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

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

将上述代码调整一下:

代码语言:javascript
复制
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)

运行结果:

image.png
image.png
代码语言:javascript
复制
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

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

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

微调展示

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

代码语言:javascript
复制
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()

创建张量和跟踪计算:

代码语言:javascript
复制
x = torch.ones(2, 2, requires_grad=True)

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

初始化存储变量:

代码语言:javascript
复制
grad_history = []
out_history = []

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

进行微调并记录变化:

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

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

计算梯度:

代码语言:javascript
复制
    out.backward()

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

记录梯度和结果:

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

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

根据梯度微调参数:

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

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

清零梯度:

代码语言:javascript
复制
    x.grad.zero_()

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

绘制曲线图:

代码语言:javascript
复制
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()
image.png
image.png

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


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

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

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-02-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简单计算
  • 梯度计算
  • 拉力比赛
  • 微调展示
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档