自动微分模块
自动微分模块
自动微分就是自动计算梯度值,也就是计算导数。
- 什么是梯度
- 对函数求导的值就是梯度
- 什么是梯度下降法
- 是一种求最优梯度值的方法,使得损失函数的值最小
- 梯度经典语录
- 对函数求导得到的值就是梯度
(在数值上的理解)
(在方向上理解)
训练神经网络时,最常用的算法就是反向传播。在该算法中,参数(模型权重)会根据损失函数关于对应参数的梯度进行调整。为了计算这些梯度,PyTorch内置了名为 torch.autograd 的微分模块。它支持任意计算图的自动梯度计算:

接下来我们使用这个结构进行自动微分模块的介绍,我们使用 backward 方法、grad 属性来实现梯度的计算和访问。
梯度基本计算
- pytorch不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导
- x如果是张量,y必须是标量(一个值)才可以进行求导
- 计算梯度: y.backward(), y是一个标量
- 获取x点的梯度值: x.grad, 会累加上一次的梯度值
- 标量张量梯度计算
# 定义一个标量张量(点)
# requires_grad=:默认为False,不会自动计算梯度;为True的话是将自动计算的梯度值保存到grad中
x = torch.tensor(10, requires_grad=True, dtype=torch.float32)
print("x-->", x)
# 定义一个曲线
y = 2 * x ** 2
print("y-->", y)
# 查看梯度函数类型,即曲线函数类型
print(y.grad_fn)
# 计算x点的梯度
# 此时y是一个标量,可以不用使用y.sum()转换成标量
print("y.sum()-->", y.sum())
# y'|(x=10) = (2*x**2)'|(x=10) = 4x|(x=10) = 40
# 因为y是一个标量,所以不需要调用y.sum().backward()方法会自动计算y关于x的梯度,并将结果存储在x.grad中。
#y.sum().backward()
y.backward()
# 打印x的梯度值
print("x的梯度值是:", x.grad)
"""
x--> tensor(10., requires_grad=True)
y--> tensor(200., grad_fn=<MulBackward0>)
<MulBackward0 object at 0x000002042A749420>
y.sum()--> tensor(200., grad_fn=<SumBackward0>)
x的梯度值是: tensor(40.)
"""
y.sum()函数用于计算张量y中所有元素的总和。这个函数非常有用,特别是在处理多维张量时,可以将多维张量缩减为一个标量值。这在许多深度学习任务中非常常见,例如在计算损失函数时,通常需要将模型的输出张量缩减为一个标量损失值,以便进行反向传播。
- 向量张量梯度计算
# 定义一个向量张量(点)
x = torch.tensor([10, 20], requires_grad=True, dtype=torch.float32)
print("x-->", x)
# 定义一个曲线
y = 2 * x ** 2
print("y-->", y)
# 计算梯度
# x和y都是向量张量,不能进行求导,需要将y转换成标量张量-->y.sum()
# y'|(x=10) = (2*x**2)'|(x=10) = 4x|(x=10) = 40
# y'|(x=20) = (2*x**2)'|(x=20) = 4x|(x=20) = 80
y.sum().backward()
# 打印x的梯度
print("x.grad-->", x.grad)
"""
x--> tensor([10., 20.], requires_grad=True)
y--> tensor([200., 800.], grad_fn=<MulBackward0>)
x.grad--> tensor([40., 80.])
"""
梯度下降法求最优解
- 梯度下降法公式: w = w - r * grad (r是学习率, grad是梯度值)
- 清空上一次的梯度值: x.grad.zero_()
# 求 y = x**2 + 20 的极小值点 并打印y是最小值时 w的值(梯度)
# 1 定义点 x=10 requires_grad=True dtype=torch.float32
# 2 定义函数 y = x**2 + 20
# 3 利用梯度下降法 循环迭代1000 求最优解
# 3-1 正向计算(前向传播)
# 3-2 梯度清零 x.grad.zero_()
# 3-3 反向传播
# 3-4 梯度更新 x.data = x.data - 0.01 * x.grad
# 1 定义点x=10 requires_grad=True dtype=torch.float32
x = torch.tensor(10, requires_grad=True, dtype=torch.float32)
# 2 定义函数 y = x ** 2 + 20
y = x ** 2 + 20
print('开始 权重x初始值:%.6f (0.01 * x.grad):无 y:%.6f' % (x, y))
# 3 利用梯度下降法 循环迭代1000 求最优解
for i in range(1, 1001):
# 3-1 正向计算(前向传播)
y = x ** 2 + 20
# 3-2 梯度清零 x.grad.zero_()
# 默认张量的 grad 属性会累加历史梯度值 需手工清零上一次的提取
# 一开始梯度不存在, 需要做判断
if x.grad is not None:
x.grad.zero_()
# 3-3 反向传播
y.sum().backward()
# 3-4 梯度更新 x.data = x.data - 0.01 * x.grad
# x.data是修改原始x内存中的数据,前后x的内存空间一样;如果使用x,此时修改前后x的内存空间不同
x.data = x.data - 0.01 * x.grad # 注:不能 x = x - 0.01 * x.grad 这样写
print('次数:%d 权重x: %.6f, (0.01 * x.grad):%.6f y:%.6f' % (i, x, 0.01 * x.grad, y))
print('x:', x, x.grad, 'y最小值', y)
梯度计算注意点
- 不能将自动微分的张量转换成numpy数组,会发生报错,可以通过detach()方法实现
# 定义一个张量
x1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float64)
# 将x张量转换成numpy数组
# 发生报错,RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
# 不能将自动微分的张量转换成numpy数组
# print(x1.numpy())
# 通过detach()方法产生一个新的张量,作为叶子结点
x2 = x1.detach()
# x1和x2张量共享数据,但是x2不会自动微分
print(x1.requires_grad)
print(x2.requires_grad)
# x1和x2张量的值一样,共用一份内存空间的数据
print(x1.data)
print(x2.data)
print(id(x1.data))
print(id(x2.data))
# 将x2张量转换成numpy数组
print(x2.numpy())
9.4 自动微分模块应用
import torch
# 输入张量 2*5
x = torch.ones(2, 5)
# 目标值是 2*3
y = torch.zeros(2, 3)
# 设置要更新的权重和偏置的初始值
w = torch.randn(5, 3, requires_grad=True)
b = torch.randn(3, requires_grad=True)
# 设置网络的输出值
z = torch.matmul(x, w) + b # 矩阵乘法
# 设置损失函数,并进行损失的计算
loss = torch.nn.MSELoss()
loss = loss(z, y)
# 自动微分
loss.backward()
# 打印 w,b 变量的梯度
# backward 函数计算的梯度值会存储在张量的 grad 变量中
print("W的梯度:", w.grad)
print("b的梯度", b.grad)
"""
W的梯度: tensor([[-1.7421, -3.0443, 0.4371],
[-1.7421, -3.0443, 0.4371],
[-1.7421, -3.0443, 0.4371],
[-1.7421, -3.0443, 0.4371],
[-1.7421, -3.0443, 0.4371]])
b的梯度 tensor([-1.7421, -3.0443, 0.4371])
"""
PyTorch构建线性回归模型
我们使用 PyTorch 的各个组件来构建线性回归模型。在pytorch中进行模型构建的整个流程一般分为四个步骤:
- 准备训练集数据
- 构建要使用的模型
- 设置损失函数和优化器
- 模型训练

要使用的API:
- 使用 PyTorch 的 nn.MSELoss() 代替平方损失函数
- 使用 PyTorch 的 data.DataLoader 代替数据加载器
- 使用 PyTorch 的 optim.SGD 代替优化器
- 使用 PyTorch 的 nn.Linear 代替假设函数
例子
import torch
from torch.utils.data import TensorDataset # 构造数据集对象
from torch.utils.data import DataLoader # 数据加载器
from torch import nn # nn模块中有平方损失函数和假设函数
from torch import optim # optim模块中有优化器函数
from sklearn.datasets import make_regression # 创建线性回归模型数据集
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 构造数据集
def create_dataset():
# 使用 make_regression 生成回归数据集
x, y, coef = make_regression(n_samples=100, # 样本数量
n_features=1, # 特征数量
noise=10, # 噪声水平
coef=True, # 返回系数
bias=14.5, # y轴截距
random_state=0) # 随机种子,确保结果可复现
# 将构建数据转换为张量类型
x = torch.tensor(x, dtype=torch.float32) # 确保数据类型为 float32
y = torch.tensor(y, dtype=torch.float32) # 确保数据类型为 float32
return x, y, coef
# 训练模型
def train():
# 构造数据集
x, y, coef = create_dataset()
# 构造数据集对象
dataset = TensorDataset(x, y)
# 构造数据加载器
# dataset=:数据集对象
# batch_size=:批量训练样本数据
# shuffle=:样本数据是否进行乱序
dataloader = DataLoader(dataset=dataset, batch_size=16, shuffle=True)
# 构造模型
# in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size
# out_features指的是输出的二维张量的大小,即输出的[batch_size,size]中的size
model = nn.Linear(in_features=1, out_features=1)
# 构造平方损失函数
criterion = nn.MSELoss()
# 构造优化函数
# params=model.parameters():训练的参数,w和b
# lr=1e-2:学习率, 1e-2为10的负二次方
print("w和b-->", list(model.parameters()))
print("w-->", model.weight)
print("b-->", model.bias)
optimizer = optim.SGD(params=model.parameters(), lr=1e-2)
# 初始化训练次数
epochs = 100
# 损失的变化
epoch_loss = []
total_loss=0.0
train_sample=0.0
for _ in range(epochs):
for train_x, train_y in dataloader:
# 将一个batch的训练数据送入模型
y_pred = model(train_x.type(torch.float32))
# 计算损失值
loss = criterion(y_pred, train_y.reshape(-1, 1).type(torch.float32))
total_loss += loss.item()
train_sample += len(train_y)
# 梯度清零
optimizer.zero_grad()
# 自动微分(反向传播)
loss.backward()
# 更新参数
optimizer.step()
# 获取每个batch的损失
epoch_loss.append(total_loss/train_sample)
# 打印回归模型的w
print(model.weight)
# 打印回归模型的b
print(model.bias)
# 绘制损失变化曲线
plt.plot(range(epochs), epoch_loss)
plt.title('损失变化曲线')
plt.grid()
plt.show()
# 绘制拟合直线
plt.scatter(x, y)
x = torch.linspace(x.min(), x.max(), 1000)
y1 = torch.tensor([v * model.weight + model.bias for v in x])
y2 = torch.tensor([v * coef + 14.5 for v in x])
plt.plot(x, y1, label='训练')
plt.plot(x, y2, label='真实')
plt.grid()
plt.legend()
plt.show()
if __name__ == '__main__':
train()


TensorDataset
在 PyTorch 中,TensorDataset 是一个非常方便的类,用于将多个张量组合成一个数据集对象。这个数据集对象可以与 DataLoader 一起使用,以便在训练过程中批量加载数据并进行乱序等操作。
TensorDataset 的使用假设你有两个张量 x 和 y,分别表示特征和目标值,你可以使用 TensorDataset 将它们组合成一个数据集对象。
以下是一个完整的示例,展示了如何使用 TensorDataset 和 DataLoader:
import torch
from torch.utils.data import TensorDataset, DataLoader
from sklearn.datasets import make_regression
# 生成回归数据集
x, y, coef = make_regression(n_samples=100, n_features=1, noise=10, coef=True, bias=14.5, random_state=0)
# 将数据转换为 PyTorch 张量
x = torch.tensor(x, dtype=torch.float32)
y = torch.tensor(y, dtype=torch.float32)
# 创建 TensorDataset 对象
dataset = TensorDataset(x, y)
# 创建 DataLoader 对象
dataloader = DataLoader(dataset=dataset, batch_size=16, shuffle=True)
# 使用 DataLoader 加载数据
for batch_idx, (batch_x, batch_y) in enumerate(dataloader):
print(f"Batch {batch_idx + 1}:")
print(f" x: {batch_x}")
print(f" y: {batch_y}")
print()
代码解释
- 生成回归数据集:使用 make_regression 生成回归数据集,生成100个样本,每个样本有一个特征,噪声水平为10,y轴截距为14.5,随机种子为0。
- 将数据转换为 PyTorch 张量:x 和 y 被转换为浮点型张量。
- 创建 TensorDataset 对象:TensorDataset(x, y) 将特征 x 和目标值 y 组合成一个数据集对象 dataset。
- 创建 DataLoader 对象:DataLoader(dataset=dataset, batch_size=16, shuffle=True) 创建一个 DataLoader 对象,批量大小为16,数据在每个轮次开始时会进行乱序。
- 使用 DataLoader 加载数据:使用 for 循环遍历 DataLoader,每次迭代获取一个批次的数据 batch_x 和 batch_y,并打印出来。