感谢学习视频:https://www.bilibili.com/video/BV1Y7411d7Ys?p=5&vd_source=2314316d319741d0a2bc13b4ca76fae6
前面学到:
第一步: 确定我们的模型;
第二步:优化目标:定义损失函数(最终必须是一个标量值,这样才能去找怎样让它变得更小);
第三步:然后在 pytorch 进行优化时,需要用到前面学的SGD(随机梯度下降)【核心就是求出每一个权重损失关于权重的梯度】,然后用SGD对权重进行更新。
之前3节课实现了线性模型如何去训练、更新权重。本节主要内容是用 pytorch 提供的工具帮我们去实现一个简单的线性模型的过程。
介绍 module 【如何构造自己的神经网络】;
loss 【如何构造损失函数】;
以及如何构造随机梯度下降的优化器。
之前也用到了pytorch的一些功能: Tensor 、 forward : 前馈,计算这组样本带来的损失、backward:反馈,求出最终的梯度。更新权重、权重梯度清零。。。
用pytorch框架写的神经网络代码看起来复杂,但它有很强的弹性,可通过扩展构造出更复杂的神经网络。
线性回归是最简单的只有一个神经元的神经网络。
pytorch写神经网络:
第一步:准备/构造数据集;
第二步:设计模型用来计算 y hat(y_pred);
第三步:构造损失函数和优化器(loss、optimizer),使用pytorch的API【封装功能】构造用来计算损失的对象、用来优化的对象;
第四步:训练的周期。
准备数据
模型是 y_hat = w * x + b
import torch
# 之前用两个列表保存数据
x_data = [1.0,2.0,3.0]
y_data = [2.0,4.0,6.0]
# 现在用 torch的 Tensor,用mini-batch的风格
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])
mini-batch 就是一次性把三个样本结果都求出来。
numpy有一个广播机制【就是一个矩阵+一个列向量,本身两者是无法相加的,但广播机制会自动将1;2;3 扩充成与另一个矩阵相同的大小,这样就可以完成运算】
w和b自动广播成可运算的矩阵大小。
第二步定义模型
彷射模型实际上就是pytorch中的线性单元,一个线性单元就是w*x+b,需要确定权重形状【维度】是什么?偏置形状是什么?想知道w的大小,就要先知道x和y_hat的维度,然后将y_hat扔到loss函数中计算,最后得到loss。
换句话说,就是构造这样一个计算图,从x开始,经过权重参与的运算,最终经过损失函数到达loss,然后在 loss 调用 backward 对计算图进行反向传播。
这就是模型的设计,这里是比较简单的模型,就一个线性单元,x和y都是一维【列数就是维度】,注意loss 一定是一个标量,前面我们知道loss算出来有三个,所以我们要求平均。
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
self.linear = torch.nn.Linear(1,1)
def forward(self, x):
y_pred = self.linear(x)
return y_pred
model = LinearModel() # 用定义的模型时,实例化即可 model是可调用的
首先,把模型定义成一个类【模板】,所有模型都要继承自 Module 【因为Module 这个父类里有很多方法,它是将来模型训练过程中需要用到的】,继承下来后,这个类里需要至少两个函数:
- 构造函数 init【初始化对象时默认调用函数】
- forward 【前馈时执行的计算;必须叫这个名字】
这里没有backward,这是因为Module 构造的对象会自动地根据你的计算图帮你实现backward过程【这是Module里自动完成的】
注:有时,要用的模块 pytorch 里没有定义,即没办法对它求导数,没有这种计算。这时候有两种方法:
1.如果你的模块可以由基本的pytorch支持的运算来构成,可以把它封装成一个model、然后实例化model、然后调用它,它可以自动反向传播。
2。 如果你觉得pytorch的计算图计算效率不高,可能在计算导数时,你有更高效的方法,你可以从Functions 里继承, Functions时pytorch 的一个类,这个类里是需要实现反向传播的,你可以自己构造自己的计算块,但你的计算快都可以用pytorch的计算模块构成。
用Module是最简单的,因为不用人工设计反向传播的导数怎么去求。
(1)构造函数: super就是父类,supper(LinearModel, self).init()是调用父类的构造。必须要有super(你定义类的名称,self)固定格式。
self.linear = torch.nn.Linear(1,1)中torch.nn.Linear是pytorch 的一个类;Linear 也是继承Module的,所以它能自动反向传播;类后加括号【实际就是构造对象】;Linear这个的对象就包含了权重和偏置这两个Tensor,所以,就可直接用Linear来完成 权重*x+b 计算;linear是对象,这对象的类型是torch.nn模块中的Linear这个类, nn(Neural Network神经网络)
y_pred = self.linear(x)——做的就是w乘x+b的计算,self.linear是前面建立的对象;对象后加括号:意味着实现了可调用的对象。
model = LinearModel() 实例化,model是可调用的(collable);model(x):就是把x送到forward中返回y_hat。
linear是由Linear 实例化的,里面有权重和偏置。
class Foobar: # 定义一个可调用的类
def __init__(self):
pass
def __call__(self, *args, **kwargs):# 想让对象可调用,必须定义call这个函数;默认形式
print("Hello" + str(args[0]))
foobar = Foobar() # 定义变量foobar 让其实例化
foobar(1, 2, 3)
第三步构造损失函数
criterion = torch.nn.MSELoss(size_average =False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
pytorch里有一个现成的类MSELoss【也是继承自Module】,实例化后,将来调用时就会计算(y_hat-y)^2。
构造 criterion 对象,需要的对象是 y_hat 和 y 求出loss。第一个参数是:是否求均值(可求可不求);第二个参数reduce:降维,是否要求和。
optimizer :优化器,不会构建计算图,optim 模块里有个类SGD,做实例化等一个参数:权重,model 有个成员函数 parameters 会检查 model 里的所有成员,如果成员里有相应的权重,它就把这些都加到最后训练的结果(参数集合)上。 lr 是设置的学习率。opimizer 知道要对哪些权重做优化,也知道学习率,用来对模型进行优化。
第四步
for epoch in range(100):
y_pred = model(x_data)
loss = criterion(y_pred, y_data)
print(epoch, loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
训练过程,:
(1)先在前馈算 y_hat
(2)用 criterion 算损失
(3)打印【注意 用 print 时loss自动用__str__()所以不会产生计算图】
(4)梯度归零
(5)反向传播
(6)更新 step()根据所有参数里所包含的梯度以及预先设置的学习率自动进行更新
test
# output weight and bias
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
# test model
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
打印 w 、b weight是一个矩阵,虽然只有一个值,需用item()
这是 optim 的不同优化器。
import torch
# 现在用 torch的 Tensor,用mini-batch的风格
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
self.linear = torch.nn.Linear(1,1)
def forward(self, x):
y_pred = self.linear(x)
return y_pred
model = LinearModel()
criterion = torch.nn.MSELoss(size_average =False)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
for epoch in range(100):
y_pred = model(x_data)
loss = criterion(y_pred, y_data)
print(epoch, loss)
optimizer.zero_grad()
loss.backward()
optimizer.step()
# output weight and bias
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
# test model
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
可以看到不同的优化器,效果不同。多尝试