自动微分模块
1. 自动微分模块=对损失函数求导,结合反向传播,更新权重参数w,b
pytorch不支持向量张量对向量张量的求导,只支持标量张量对向量张量的求导
import torch# 定义参数,requires_grad默认为false
w = torch.tensor(10, requires_grad=True, dtype=torch.float32)# 定义损失函数
loss = w ** 2 + 20
print(f'梯度函数类型:{type(loss.grad_fn)}')
print(f'权重初始值w:{w}, loss:{loss}')for i in range(0, 10):# 计算损失loss = w ** 2 + 20# 梯度清零 w.grad.zero_(), 默认梯度会累加# 至此(第一次的时候),还没有计算梯度, 所以w.grad = Node,要做非空判断if w.grad is not None:w.grad.zero_()# 反向传播,计算梯度,梯度 = 损失函数的倒数,计算完毕后,会记录到w.grad属性中# loss是一个标量,若非标量可通过loss.sum().backward()来保证loss是1个标量loss.backward() # 更新参数# .data 返回一个与原始张量共享相同数据存储的新张量,但不共享计算历史和梯度信息。# w.data = w.data - w.grad * 0.01# 不推荐使用data,推荐使用以下# 注意:w = w - 0.01 * w.grad 错误,因为当你执行 w = w - 0.01 * w.grad 时,# 实际上创建了一个新的张量,这个新张量不再需要梯度(因为在 torch.no_grad() 上下文中创建),所以后续的循环迭代中无法计算梯度。with torch.no_grad():w -= 0.01 * w.grad # 使用 -= 进行原地更新print(f'第{i}次,更新后的权重w:{w}, loss:{loss}')
2.detach()
若张量允许自动微分则不能转换为numpy,需要使用detach拷贝一份
另外,detach后与原变量共享同一块空间
import torch
import numpy as npt1 = torch.tensor([10, 20], requires_grad=True, dtype=torch.float)
print(f't1:{t1}, type:{type(t1)}') # t1:tensor([10., 20.], requires_grad=True), type:<class 'torch.Tensor'># 通过 detach(),拷贝一份张量,然后转换
# t2 = t1.detach().numpy()
# 注意: t1 和 t2 和 t3 共享同一块空间
t2 = t1.detach()
t3 = t2.numpy()
# 测试 共享同一块空间
t1.data[0] = 1
print(f't1:{t1}, type:{type(t1)}') # t1:tensor([ 1., 20.], requires_grad=True), type:<class 'torch.Tensor'>
print(f't2:{t2}, type:{type(t2)}') # t2:tensor([ 1., 20.]), type:<class 'torch.Tensor'>
print(f't3:{t3}, type:{type(t3)}') # t3:[ 1. 20.], type:<class 'numpy.ndarray'>
#测试 detach
print(f't1:{t1.requires_grad}') # True
print(f't2:{t2.requires_grad}') # False
3. PyTorch构建线性回归模型
import torch
from torch.utils.data import TensorDataset # 创建x和y张量数据集对象
from torch.utils.data import DataLoader # 创建数据集加载器
import torch.nn as nn # 损失函数和回归函数
from torch.optim import SGD # 随机梯度下降函数, 取一个训练样本算梯度值
from sklearn.datasets import make_regression # 创建随机样本, 工作中不使用
import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号# 1-创建线性回归样本 x y coef(w) b
# numpy对象 ->张量Tensor ->数据集对象TensorDataset ->数据加载器DataLoader
def create_datasets():x, y, coef = make_regression(n_samples=100, # 样本数n_features=1, # 特征数noise=10, # 标准差, 噪声, 样本离散程度coef=True, # 返回系数, wbias=14.5, # 截距 brandom_state=0)# 将数组转换成张量x = torch.tensor(data=x)y = torch.tensor(data=y)# print('x->', x)# print('y->', y)# print('coef->', coef)return x, y, coef# 2-模型训练
def train(x, y, coef):# 创建张量数据集对象datasets = TensorDataset(x, y)print('datasets->', datasets)# 创建数据加载器对象# dataset: 张量数据集对象# batch_size: 每个batch的样本数# shuffle: 是否打乱样本dataloader = DataLoader(dataset=datasets, batch_size=16, shuffle=True)print('dataloader->', dataloader)# for batch in dataloader: # 每次遍历取每个batch样本# print('batch->', batch) # [x张量对象, y张量对象]# break# 创建初始回归模型对象, 随机生成w和b, 元素类型为float32# in_features: 输入特征数 1个# out_features: 输出特征数 1个model = nn.Linear(in_features=1, out_features=1)print('model->', model)# 获取模型对象的w和b参数print('model.weight->', model.weight)print('model.bias->', model.bias)print('model.parameters()->', list(model.parameters()))# 创建损失函数对象, 计算损失值criterion = nn.MSELoss()# 创建SGD优化器对象, 更新w和boptimizer = SGD(params=model.parameters(), lr=0.01)# 定义变量, 接收训练次数, 损失值, 训练样本数epochs = 100loss_list = [] # 存储每次训练的平均损失值total_loss = 0.0train_samples = 0for epoch in range(epochs): # 训练100次# 借助循环实现 mini-batch SGD 模型训练for train_x, train_y in dataloader:# 模型预测# train_x->float64# w->float32y_pred = model(train_x.type(dtype=torch.float32)) # y=w*x+bprint('y_pred->', y_pred)# 计算损失值, 调用损失函数对象# print('train_y->', train_y)# y_pred: 二维张量# train_y: 一维张量, 修改成二维张量, n行1列# 可能发生报错, 修改形状# 修改train_y元素类型, 和y_pred类型一致, 否则发生报错loss = criterion(y_pred, train_y.reshape(shape=(-1, 1)).type(dtype=torch.float32))print('loss->', loss)# 获取loss标量张量的数值 item()# 统计n次batch的总MSE值total_loss += loss.item()# 统计batch次数train_samples += 1# 梯度清零optimizer.zero_grad()# 计算梯度值loss.backward()# 梯度更新 w和b更新# step()等同 w=w-lr*gradoptimizer.step()# 每次训练的平均损失值保存到loss列表中loss_list.append(total_loss / train_samples)print('每次训练的平均损失值->', total_loss / train_samples)print('loss_list->', loss_list)print('w->', model.weight)print('b->', model.bias)# 绘制每次训练损失值曲线变化图plt.plot(range(epochs), loss_list)plt.title('损失值曲线变化图')plt.grid()plt.show()# 绘制预测值和真实值对比图# 绘制样本点分布plt.scatter(x, y)# 获取1000个样本点# x = torch.linspace(start=x.min(), end=x.max(), steps=1000)# 计算训练模型的预测值y1 = torch.tensor(data=[v * model.weight + model.bias for v in x])# 计算真实值y2 = torch.tensor(data=[v * coef + 14.5 for v in x])plt.plot(x, y1, label='训练')plt.plot(x, y2, label='真实')plt.legend()plt.grid()plt.show()x, y, coef = create_datasets()
train(x, y, coef)