目录
一、建立线性模型过程
<1> Prepare dataset
构造一个简单的神经网络,准备Tensor类型的数据集。写成矩阵的形式,有利于后面forward()
函数时用广播机制对数据的维度进行扩充。此处共有三条数据,每条数据只有一个特征,[1.0]表示第一行,[2.0]表示第二行,[3.0]表示第三行。
x_data = torch.Tensor([[1.0],[2.0],[3.0]])
y_data = torch.Tensor([[2.0],[4.0],[6.0]])
<2> Design model
-
使用nn.Module类来自定义模型,PyTorch里面的自定义操作基本上都是继承nn.Modulel这个父类来实现的。自定义的时候必须重新实现
__init__
以构造函数并重写forward()
函数。class Module(object): # 1.构造函数 def __init__(self): ... # 2.前向传播函数,计算y_hat(预测值) def forward(self, *input): ···
-
通常会在
__init__
函数里定义各个层,而在forward()
函数里定义各层之间的连接关系。例如下面的单层神经网络和三层神经网络:单层:
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) #中间到输出(outputs)的关系 return y_pred
三层:
class Model(torch.nn.Module): def __init__(self): super(Model, self).__init__() self.linear1 = torch.nn.Linear(9, 6) #三层神经网络,逐渐空间降维 self.linear2 = torch.nn.Linear(6, 4) self.linear3 = torch.nn.Linear(4, 1) self.sigmoid = torch.nn.Sigmoid() #构造非线性变换函数 def forward(self, x): #为了避免传错参数,统一用x来覆盖计算出来的值 x = self.sigmoid(self.linear1(x)) #输入(inputs)与一层的关系 x = self.sigmoid(self.linear2(x)) #一层与二层的关系 x = self.sigmoid(self.linear3(x)) #二层与三层(outputs)的关系 return x
<3> Construct loss and optimizer
-
在PyTorch中,经常使用nn.MSELoss来作为损失函数,通常使用步骤如下:
#1.构造损失函数 criterion = torch.nn.MSELoss(size_average = True, reduce = True, reduction = 'sum'/'mean'/'None') #MSE类,会构造计算图,继承Module类 #2.调用损失函数,传入参数为(y的预测值, y的原始值) loss = criterion(y_pred, y_data) #求损失loss #3.输出loss print(loss.item()) #如果reduction = 'None',输出的是向量 print(loss) #如果reduction = 'sum'/'mean',输出的是标量 #4.反向传播 loss.backward() #因为MSELoss继承自nn.Module,所以会构造计算图
其他损失函数参考文章: MSE、BCE、BCEWithLogits、NLLLoss、CrossEntropyLoss的用法
-
torch.optim是一个实现了各种优化算法的库,为了使用优化器,必须先构造一个optimizer对象,此处使用的是梯度下降算法,调用的PyTorch API是torch.optim.SGD,如下所示:
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01, momentum = 0.9) #不会构造计算图 #model.parameters()会检查构建的神经网络中所有待更新的参数,如w,b
<4> Training cycle
- 每一次epoch训练过程包括:
1、求出y_pred,y_pred = model(x_data)
2、根据y_pred
和y_data
求出损失loss
;1,2两步相当于前向传播,并且构造计算图,存储子节点梯度
3、每轮的反向传播前应该将梯度清零,避免梯度的累积,optimizer.zero_grad()
4、反向传播,求出所需梯度,loss.backward()
5、参数更新,optimizer.step()
for epoch in range(1000): y_pred = model(x_data) #使用了实例model,调用了前馈forward loss = criterion(y_pred, y_data) #求损失loss print(epoch, loss.item()) optimizer.zero_grad() #梯度归零 loss.backward() #反向传播 optimizer.step() #更新
- 关于魔法函数
__call__()
的作用
1、如果在类中实现了__call__
方法,那么实例对象也将成为一个可调用对象(callable),例如本文中PyTorch在nn.Module中,实现了__call__
方法,并且在__call__
中调用了forward函数,所以实例model可以直接被调用:model(x_data)
。
2、 例子:对象初始化为10后再通过调用函数a(5)给对象加5class A(): def __init__(self, init_num): #构造函数,类实例化的时候要赋予一些基本的参数值 super().__init__() print('----old number is ', init_num) self.num = init_num #赋值 def __call__(self, add_num): #call函数里面调用forward() print('2> call函数被调用!') return self.forward(add_num) def forward(self, input): print('3> forward函数被调用!') print('4> input is:', input) return input + self.num a = A(10) #此时对类A进行实例化 print('1> 初始化结束!') output = a(5) #此时会直接调用call函数,call函数调用 print("----new number is ", output)
参考文章:python class 中 的__call__方法In [1]: ----old number is 10 1> 初始化结束! 2> call函数被调用! 3> forward函数被调用! 4> input is: 5 ----new number is 15
二、代码运行测试
完整代码:
import torch
import matplotlib.pyplot as plt
#1.Prepare training set
x_data = torch.Tensor([[1.0],[2.0],[3.0]]) #数据集是tensor,且写成矩阵的形式
y_data = torch.Tensor([[2.0],[4.0],[6.0]])
#2.Design model 构建计算图(使用线性模型)
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
self.linear = torch.nn.Linear(1, 1) #(in_features, out_features, bias = True)
def forward(self, x):
y_pred = self.linear(x) #self.linear是callable的,是可调用的对象
return y_pred
model = LinearModel() #创建类的实例
#3.Construct Loss and OPtimizer
criterion = torch.nn.MSELoss(size_average = False) #MSE损失类,会构造计算图,继承Module类
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01) #优化器,不会构造计算图,检查module的所有参数
#4.Training Cycle 训练并更新
for epoch in range(1000):
y_pred = model(x_data) #使用了实例model,调用了前馈forward
loss = criterion(y_pred, y_data) #求损失loss
print(epoch, loss.item())
optimizer.zero_grad() #梯度归零??
loss.backward() #反向传播
optimizer.step() #更新
#5.Output weight and bias
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
#6.Test Model
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data) # 取tensor数值
运行结果:
各种优化器的迭代结果对比:
optimizer1 = torch.optim.SGD(model.parameters(), lr = 0.01)
optimizer2 = torch.optim.Adam(model.parameters(), lr = 0.01)
optimizer3 = torch.optim.Adamax(model.parameters(), lr = 0.01)
optimizer4 = torch.optim.ASGD(model.parameters(), lr = 0.01)
optimizer5 = torch.optim.RMSprop(model.parameters(), lr = 0.01)
optimizer6 = torch.optim.Adagrad(model.parameters(), lr = 0.01)
optimizer7 = torch.optim.Rprop(model.parameters(), lr = 0.01)