自动微分模块【掌握】
自动微分就是:给定最终结果后,自动把计算图里每个参数的导数算出来。
先抓住 4 句话:
- 梯度本质上就是导数
- 在深度学习里,通常说的是:损失函数对某个参数的导数(更准确地说是偏导数)
- 梯度表示“往哪边变、变多快”
- 在某一点上,导数越大,说明函数在这一点附近变化越快
- 梯度指向增长最快的方向
- 想让损失变小,走的不是梯度方向,而是负梯度方向
- 反向传播传播的就是梯度
- 它本质上就是利用链式法则,从后往前把各层参数的导数一步步算出来
可以把训练过程理解成下面这条线:
输入 -> 前向计算 -> 得到损失 -> 反向传播求梯度 -> 按负梯度方向更新参数
训练神经网络时,最常用的就是这套流程。为了自动完成“求梯度”这一步,PyTorch 提供了 torch.autograd 模块。它会根据计算图自动完成求导,所以我们不需要手写链式法则:

接下来就围绕这个流程来学自动微分,重点看两个东西:
backward():触发反向传播,自动计算梯度grad:读取某个参数当前保存的梯度
梯度基本计算
- pytorch不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导
- 简单理解就是损失值需要是标量,因为要对权重求导
- 计算梯度:
y.backward(), y是一个标量张量- 获取x点的梯度值:
x.grad, 会累加上一次的梯度值
-
标量张量梯度计算_单轮
1- 初始化w权重值
""" 本代码只实现反向传播 """ import torch if __name__ == '__main__': # 1- 初始化w权重值 """ requires_grad:允许对该数据进行梯度下降计算,必须为True dtype:因为更新梯度的过程中肯定会出现小数的情况,因此dtype必须是小数 """ # 多个权重 # w = torch.tensor([10,20,30],requires_grad=True,dtype=torch.float32) # 一个权重 w = torch.tensor(20,requires_grad=True,dtype=torch.float32) # 2- 定义Loss损失函数 # 下面的公式因为目前没有学到深度学习的损失函数有哪些,因此下面的公式是自定义的,不用关心 loss = 2*w**2 # 3- 进行反向传播->让损失值越来越小 loss.sum().backward() # 4- 更新w权重值 # W1 = W0 - lr*grad """ w.data = w.data - 0.1 * w.grad代码类似如下的过程 a = 20 a = a - 1 w.data是从张量中将值取出来 """ w.data = w.data - 0.1 * w.grad print(f"更新后的权重值是{w}") -
标量张量梯度计算_多轮
1- 初始化权重值
import torch if __name__ == '__main__': # 1- 初始化权重值 x = torch.tensor(20,requires_grad=True,dtype=torch.float32) # 进行多次梯度的更新 epochs = 100 for epoch in range(epochs): # 2- 定义损失函数 y = 2*x**2 # 3- 梯度清零:因为Pytorch会对梯度值进行累加 if x.grad is not None: x.grad.zero_() # 4- 反向传播 # 这里可以用sum()或者mean()。常用的是sum() y.sum().backward() # 5- 更新梯度值 # W1 = W0 - lr*grad old_x = x.data # 原始的权重值 lr = 0.01 # 学习率 grad = x.grad # 自动微分计算得到的梯度值。也就是导数值 x.data = x.data - lr*grad print(f"第{epoch+1}次计算,原始的权重值{old_x},更新后的权重值{x.data}")
梯度计算注意点【熟悉】
-
不能将自动微分的张量转换成numpy数组,会发生报错,可以通过detach()方法实现
查看基础信息
import torch if __name__ == '__main__': t1 = torch.tensor(20,requires_grad=True,dtype=torch.float32) d_2 = t1.data print(d_2) print(d_2.requires_grad) print(d_2.numpy()) # 查看基础信息 # print(t1) # print(type(t1)) # print(t1.requires_grad) # 如果设置requires_grad=True有使用限制。无法直接转成numpy中的ndarray数组 # arr_1 = t1.numpy() # print(arr_1) # 如果还想转成numpy中的ndarray数组。需要使用detach() """ 使用了detach() 1- 能够转成numpy中的ndarray数组 2- 分离后的新张量与原始的张量之间共享内存,但是内存地址不同 扩展: 还可以通过 张量对象.data 进行分离。 """ d_1:torch.Tensor = t1.detach() arr_1 = d_1.numpy() print(d_1) print(d_1.requires_grad) # False print(arr_1) # 分离后的新张量与原始的张量之间共享内存,但是内存地址不同 print(id(t1)) print(id(d_1)) print(t1) print(d_1) print("-"*30) d_1.add_(1000) print(id(t1)) print(id(d_1)) print(t1) print(d_1)
自动微分模块应用【了解】
演示前向传播和反向传播的过程
前向传播
import torch
if __name__ == '__main__':
# --------------- 前向传播 ---------------
# 1- 样本数据准备
# 2条样本,每条样本有5个特征
x = torch.ones(2,5)
# 2- 样本数据的真实值
y = torch.zeros(2,3)
# 3- 初始化w权重和b偏置
w = torch.randn(5,3,requires_grad=True,dtype=torch.float32)
b = torch.randn(3,requires_grad=True,dtype=torch.float32)
# 4- 前向传播计算得到预测值
z = x @ w + b
# 5- 定义损失函数
loss_fn = torch.nn.MSELoss()
# 6- 计算损失值
loss_value = loss_fn(z,y)
print(f"损失值是:{loss_value}")
# --------------- 反向传播 ---------------
# 7- 反向传播
loss_value.sum().backward()
# 8- 更新梯度值,也就是要更新w和b
print(f"w的梯度是{w.grad}")
print(f"b的梯度是{b.grad}")PyTorch模拟线性回归模型【了解】
PyTorch模拟线性回归模型
我们使用 PyTorch 的各个组件来构建线性回归模型。在pytorch中进行模型构建的整个流程一般分为四个步骤:
- 准备训练集数据
- 构建要使用的模型
- 设置损失函数和优化器
- 模型训练

要使用的API:
- 使用 PyTorch 的 nn.MSELoss() 代替平方损失函数
- 使用 PyTorch 的 data.DataLoader 代替数据加载器
- 使用 PyTorch 的 optim.SGD 代替优化器
- 使用 PyTorch 的 nn.Linear 代替假设函数
为什么这里能够返回样本真实的斜率coef?
import torch
from torch import nn
from torch import optim # 优化器
from torch.utils.data import TensorDataset # 张量数据集
from torch.utils.data import DataLoader # 数据加载器
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():
# 为什么这里能够返回样本真实的斜率coef?
# 因为这些样本是程序自动给我们生成的,那么程序它本身肯定是知道斜率是多少
x,y,coef = make_regression(
n_samples=100, # 生成的样本数据条数
n_features=1, # 每条样本的特征个数
n_targets=1, # 每条样本的目标值个数
bias=10.14, # 线性方程中的截距,也就是b
coef=True, # 是否要返回斜率,也就是w
noise=10, # 噪声,让数据稍微分散一些
shuffle=True, # 样本数据是否打散
random_state=1014# 随机数种子
)
# print(f"x-->{x},shape-->{x.shape},type-->{type(x)}")
# print(f"y-->{y},shape-->{y.shape},type-->{type(y)}")
# print(f"coef-->{coef},type-->{type(coef)}")
# 步骤:不管是什么数据类型 -> 张量
x = torch.tensor(x,dtype=torch.float32)
y = torch.tensor(y,dtype=torch.float32)
return x,y,coef
def train_model(x,y,coef):
# 1- 构造得到数据加载器
# 1.1- 将特征和目标值进行合并
dataset = TensorDataset(x,y)
# 1.2- 得到数据加载器
"""
作用:为了防止内存溢出,也就是避免数据量过大导致内存不够
batch_size:每次给到模型训练的数据条数
shuffle:是否要对数据进行打散,防止样本不均衡
"""
dataloader = DataLoader(dataset,batch_size=16,shuffle=True)
# 2- 定义对象
# 2.1- 创建线性模型
"""
in_features:输入的特征个数,也就是输入层的神经元个数
out_features:输出的特征个数,也就是隐藏层的神经元个数
bias:是否要对偏置进行计算
"""
model = nn.Linear(in_features=1,out_features=1,bias=True)
# 2.2- 损失函数对象
criterion = nn.MSELoss()
# 2.3- 优化器
"""
作用:优化器负责自动进行梯度更新计算 W1 = W0 - lr*grad
params:优化器对哪些参数进行梯度更新,实际就是w和b
lr:学习率
"""
optimizer = optim.SGD(params=model.parameters(),lr=0.01)
# 3- 模型训练
epochs = 100 # 总共对全量样本训练多少轮次
loss_list = [] # 用来记录每个轮次得到的损失值
for epoch in range(epochs):
# 每个轮次得到的损失值
total_loss_value = 0.0
# 每个轮次训练的样本数据条数
total_sample_cnt = 0
for x_train,y_train in dataloader:
# 预测
y_predict = model(x_train)
# print(f"y_train-->{y_train.shape}")
# print(f"y_predict-->{y_predict.shape}")
# 计算损失值
loss_value = criterion(y_predict,y_train.reshape(-1,1))
# 记录下损失值和样本数据条数
total_loss_value += loss_value.item() * len(x_train)
total_sample_cnt += len(x_train)
# 反向传播(下面是固定代码)
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss_value.sum().backward()
# 更新权重和偏置:也就是自动计算W1 = W0 - lr*grad
optimizer.step()
avg_loss_value = total_loss_value/total_sample_cnt
loss_list.append(avg_loss_value)
print(f"第{epoch+1}次训练,平均损失{avg_loss_value}")
# 深度学习中不用将整个算法模型进行保存,只需要保存关键参数即可
print("训练好的模型参数信息",model.state_dict())
# 4- 可视化展示
# 4.1- 循环轮次epochs与损失值的关系
plt.plot(range(epochs), loss_list)
plt.xlabel("循环轮次epochs")
plt.ylabel("损失值")
plt.title("循环轮次epochs与损失值的关系")
plt.grid()
plt.show()
# 4.2- 预测和真实结果对比
"""
1- 展示100条样本的散点图:横轴特征x,纵轴目标y
2- 真实线性回归曲线图
3- 预测线性回归曲线图
"""
plt.scatter(x, y)
axis_x = torch.linspace(x.min(), x.max(), 1000)
# 真实线性回归曲线图
true_fn_torch = torch.tensor([tmp_x * coef + 10.14 for tmp_x in axis_x])
# 预测线性回归曲线图
pred_fn_torch = torch.tensor([tmp_x * model.weight.detach() + model.bias.detach() for tmp_x in axis_x])
plt.plot(axis_x, true_fn_torch, label="真实值", color="red")
plt.plot(axis_x, pred_fn_torch, label="预测值", color="blue")
plt.grid()
plt.legend()
plt.title("真实和预测的对比")
plt.show()
if __name__ == '__main__':
# 1- 准备训练集数据
x,y,coef = create_dataset()
# 2- 模型训练
train_model(x,y,coef)
