史上最简单、实际、通俗易懂的PyTorch实战系列教程!(新手友好、小白请进、建议收藏)
一、Autograd(自动求导/微分)机制
PyTorch框架干的最厉害的一件事就是帮我们把反向传播全部计算好了
我们来看一下这个机制的简单实现过程
先随机生成一个 3*4 的矩阵,后面的参数就是自动求导=True:
x = torch.randn(3,4,requires_grad=True)
x
再定义一个随机的 3*4 矩阵:
b = torch.randn(3,4,requires_grad=True)
定义一个简单函数让它们相加起来:
t = x + b
再定义 y 是 t 的求和:
y = t.sum()
y
把 y 当成损失函数开始反向传播求导:
y.backward()
看一下 b 的梯度(求导)等于多少:
b.grad
虽然没有指定t的requires_grad,但是需要用到它,也会默认的。
x.requires_grad, b.requires_grad, t.requires_grad
接下来举个例子看一下:
图一:y = w·x ,z = y+b 图二:z对b求偏导,z对y求偏导;y对w求偏导,y对b求偏导;z对x求偏导,z对w求偏导
#计算流程
x = torch.rand(1)
b = torch.rand(1, requires_grad = True)
w = torch.rand(1, requires_grad = True)
y = w * x
z = y + b
x.requires_grad, b.requires_grad, w.requires_grad, y.requires_grad#注意y也是需要的
x.is_leaf, w.is_leaf, b.is_leaf, y.is_leaf, z.is_leaf
反向传播计算:(如果这里不清零,梯度每次会默认累加,会影响到当前梯度的计算)
z.backward(retain_graph=True)#如果不清空会累加起来
w.grad
b.grad
二、举个简单例子,做一个线性回归试试水
一个典型的神经网络的训练过程如下:
- 定义具有学习参数(或权重)的神经网络
- 迭代输入数据集
- 根据神经网络对输入数据集进行运算
- 计算损失(输出与真实结果的距离,损失越小说明模型越准确)
- 将梯度反向传播给神经网络的参数
- 更新网络的权重(或参数)。通常使用一种简单的更新规则:权重 = 权重 - 学习率 * 梯度
构造一组输入数据 x 和其对应的标签 y ,让模型学习拟合出 w 和 b。(我们这里要模拟出的线性回归函数:y = 2x + 1 ,我们 x 给的是从 0 - 10 一共11个数,对应的y就是 1 - 21 也是11个数,也就是11行1列的矩阵)
构造 x:
import numpy as np
x_values = [i for i in range(11)]
x_train = np.array(x_values, dtype=np.float32)
x_train = x_train.reshape(-1, 1)
x_train.shape
构造 y:
y_values = [2*i + 1 for i in x_values] # y = 2x + 1
y_train = np.array(y_values, dtype=np.float32)
y_train = y_train.reshape(-1, 1) #把数据reshape转成矩阵的格式
y_train.shape
定义一个线性回归模型:
(其实线性回归就是一个不加激活函数的全连接层)
import torch
import torch.nn as nn
class LinearRegressionModel(nn.Module):
def __init__(self, input_dim, output_dim):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(input_dim, output_dim)
def forward(self, x):
out = self.linear(x)
return out
torch.nn 只支持对批量数据的处理,不支持单个样本。
input_dim = 1 #输入数据维度 = 1
output_dim = 1 #输出数据维度 = 1
model = LinearRegressionModel(input_dim, output_dim)
model
指定好参数和损失函数:
epochs = 1000 #训练1000轮
learning_rate = 0.01 #学习率=0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate) #用SGD的优化器
criterion = nn.MSELoss() # 用均方误差(MSE)的损失函数
一个损失函数包括两个输入(预测值和实际值),然后返回这两个值的距离。
在 nn 的 package 里面有不同的损失函数。均方误差(MSE)损失函数,它返回的是均方差,即每一个分量相减的平方累计最后除分量的个数,常被用作线性回归的损失函数。
训练模型:
for epoch in range(epochs):
epoch += 1
# 注意转行成tensor
inputs = torch.from_numpy(x_train)
labels = torch.from_numpy(y_train)
# 梯度要清零每一次迭代
optimizer.zero_grad()
# 前向传播
outputs = model(inputs)
# 计算损失
loss = criterion(outputs, labels)
# 返向传播
loss.backward()
# 更新权重参数
optimizer.step()
if epoch % 50 == 0:
print('epoch {}, loss {}'.format(epoch, loss.item()))
测试模型预测结果:
predicted = model(torch.from_numpy(x_train).requires_grad_()).data.numpy()
predicted
结果还是很准的,我们预定的是:y = 2x+1,让模型学习模拟 w 和 b 也就是 w = 2,b = 1,现在代入0-10这11个数字去算,和我们上面预测得到的结果基本一样。
模型的保存与读取
保存:
torch.save(model.state_dict(), 'model.pkl')
读取:
model.load_state_dict(torch.load('model.pkl'))
使用GPU进行训练
(只需要把数据和模型传入到cuda里面就可以了)
class LinearRegressionModel(nn.Module):
def __init__(self, input_dim, output_dim):
super(LinearRegressionModel, self).__init__()
self.linear = nn.Linear(input_dim, output_dim)
def forward(self, x):
out = self.linear(x)
return out
input_dim = 1
output_dim = 1
model = LinearRegressionModel(input_dim, output_dim)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device)
criterion = nn.MSELoss()
learning_rate = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
epochs = 1000
for epoch in range(epochs):
epoch += 1
inputs = torch.from_numpy(x_train).to(device)
labels = torch.from_numpy(y_train).to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
if epoch % 50 == 0:
print('epoch {}, loss {}'.format(epoch, loss.item()))