假设我们的基础模型就是y = wx+b
,其中w和b均为参数,我们使用y = 3x+0.8来构造数据x、y,所以最后通过模型应该能够得出w和b应该分别接近3和0.8。
tensor.grad
中保存的是梯度,更新参数import torch
learning_rate = 0.01
# 1 准备数据
# y = 3x+0.8
x = torch.rand([500, 1]) # 500行1列的随机数,范围是0-1
y_true = x*3 + 0.8
# 初始化两个要训练的参数
w = torch.rand([1, 1], requires_grad=True) # 1行1列
b = torch.zeros(1, requires_grad=True)
# 或者写成这样: b = torch.tensor(0, requires_grad=True, dtype=torch.float32)
print(w, b)
# 4 通过循环,反向传播,更新参数
for i in range(2500):
# 2 通过模型计算y_pred
# 3 计算loss
y_predict = torch.matmul(x, w) + b
loss = (y_true - y_predict).pow(2).mean() # 均方误差
if w.grad is not None:
w.grad.data.zero_() # 归零 (就地修改)
if b.grad is not None:
b.grad.data.zero_()
loss.backward() # 反向传播
w.data = w.data - learning_rate * w.grad
b.data = b.data - learning_rate * b.grad
print("w, b, loss", w.item(), b.item(), loss.item())
以上代码就是一个模型训练的过程,训练模型的本质就是训练了参数w和b。
输出如下:(因为输出有2500行+,所以只截取最后的结果)
可以看到,最后w收敛到了2.95517635345459
, b最后收敛到了0.823337733745575
,非常接近w=3
和 b=0.8
。
nn.Model 是torch.nn 提供的一个类,是Pytorch中 微我们自定义网络的一个基类,在这个类中定义了很多有用的方法,让我们在继承这个类 定义网路的时候非常简单。
__init__
需要 调用super方法,继承附列的属性和方法。forward
方法必须实现,用来定义我们的网络的前向计算的过程。import torch
from torch import nn
class Lr(nn.Module):
def __init__(self):
super(lr, self).__init__() # 继承父类的init的参数
self.linear = nn.Linear(1, 1)
def forward(self, x):
out = self.linear(x)
return out
其中:这一行代码是固定的。
super(lr, self).__init__() # 继承父类的init的参数
nn.Linear
为 torch 预定好的线性模型,也被称为全连接层,传入的参数为输入的数量和输出的数量(in_features, out_features
),是不算(batch_size)
的列数的。nn.Module
定义了 __call__
方法,即类Lr
的实例,实现的就是调用forward
方法,能够直接被传入参数调用,实际上调用的是forward方法并传入参数。示例:
model = Lr() # 实例化模型
pred = model(x) # 传入数据,计算结果
优化器(optimizer),可以理解为pytorch中封装好了的用来更新参数的方法,比如常见的随机下降(SGD)和Adam。
torch.optim.SGD(参数,学习率)
torch.optim.Adam(参数,学习率)
model.parameters()
来获取,获取模型中所有 requires_grad=True
的参数。optimizer = optim.SGD(model.parameters(), lr=1e-3) # 1. 实例化
optimizer.zero_grad() # 2. 梯度置零
loss.backward() # 3. 计算梯度
optimizer.step() # 4. 更新参数的值
import torch
import torch.nn as nn
from torch.optim import SGD
# 0. 准备数据
x = torch.rand([500, 1])
y_true = 3*x + 0.8
# 1. 定义模型
class MyLinear(nn.Module):
def __init__(self):
super(MyLinear, self).__init__()
self.linear = nn.Linear(1, 1)
def forward(self, x):
out = self.linear(x)
return out
# 2. 实例化模型,优化器实例化,loss实例化
my_linear = MyLinear()
optimizer = SGD(my_linear.parameters(), 0.001)
loss_fn = nn.MSELoss()
# 3. 循环,进行梯度下降,参数的更新
for i in range(5000):
# 得到预测值
y_predict = my_linear(x)
loss = loss_fn(y_predict, y_true)
# 梯度置零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 参数更新
optimizer.step()
if i%200 == 0:
print(loss.item(), list(my_linear.parameters()))
可以看到,用API来实现线性回归的收敛效果没有手动实现的好。(不知道为啥)
model.eval() # 表示设置模型为评估模式,即预测模式
model.train(mode=True) # 表示设置模型为训练模式
在目前的线性模型中上述没有什么区别,但是在一些训练和预测时参数不同的模型中,比如说是Dropout
, BatchNorm
等存在时,就需要告诉模型是训练还是在预测。
torch.cuda.is_available()
torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
>> device(type='cuda', index=0) # 使用GPU
>> device(type='cpu') # 使用CPU
model.to(device)
x_true.to(device)
predict = predict.cpu().detach().numpy()
detach()
的作用相当于 data,但是detach()是深拷贝,data是取值是浅拷贝。
在GPU上执行程序:
(1)自定义的参数和数据,需要转化为cuda支持的tensor
(2)model需要转化为cuda支持的model
(3)执行的结果需要和cpu的tensor进行计算的时候:
a. tensor.cpu() 把cuda的tensor转化为CPU的tensor
.data
返回的是一个tensor
而.item()
返回的是一个具体的数值。
注意:对于元素不止一个的tensor列表,使用item()会报错
list(my_linear.parameters()