上一篇中我们使用到了variable时,创建的类继承的就是nn中的module,torch.nn包提供了很多神经网络中具体功能相关的类,下面我们使用nn来简化之前的代码
刚开始的参数定义变化不大
import torch
from torch.autograd import Variable
batch_n=100
input_data=1000
hidden_layer=100
output_data=10
x=Variable(torch.randn(batch_n,input_data),requires_grad=False)
y=Variable(torch.randn(hidden_layer,output_data),requires_grad=False)
相比之下定义两层网络的代码被删减了,因为后面会自适应权重网络
model=torch.nn.Sequential(torch.nn.Linear(input_data,hidden_layer),torch.nn.ReLU(),torch.nn.Linear(hidden_layer,output_data))
在这里sequential里的内容就是我们搭建的网络结构,首先通过linear完成输入到隐藏层的线性变换,然后使用ReLU作为激活函数,再使用linear完成隐藏层到输出层的线性变换,其中:
torch.nn.Sequential是nn中的一种序列容器,通过在容器中嵌套具体功能的类实现模型网络的搭建,参数会按照我们定义好的序列自动传递下去,如果将容器中的各个部分看作不同的模块的话,这些模块可以自由组合,模块加入有两种方式,一种就是向上面的直接嵌套,另一种就是以orderdict有序字典的方式进行传入,第一种默认用从零开始的数字序列作为代号,而第二种每个模块有我们自己定义的名字
model=torch.nn.Sequential(torch.nn.Linear(input_data,hidden_layer),torch.nn.ReLU(),torch.nn.Linear(hidden_layer,output_data))
print(model)
from collections import OrderedDict
models=torch.nn.Sequential(OrderedDict([("L1",torch.nn.Linear(input_data,hidden_layer)),("Relu",torch.nn.ReLU()),("L2",torch.nn.Linear(hidden_layer,output_data))]))
print(models)
torch.nn.Linear定义模型中的线性层,参数有三个,分别书输入特征数,输出特征数和是否使用偏置,第三个参数是布尔值,默认是true,因此我们只需要传入输入特征数和输出特征数,就会自动生成权重参数和偏置,且自动生成的会比简单随机方法好很多
torch.nn.ReLU属于非线性激活分类,相当于我们之前用的xx.clamp(min=0),剪裁出大于等于零的数据,不需要传入参数,除此之外还有很多激活函数可以使用,常见的比如PReLU,LeakyReLU,Tanh,Sigmoid,Softmax等
接下来进行训练参数优化
epoch_n=10000
learning_rate=1e-4
loss_fn=torch.nn.MSELoss()
前两行没有什么特别的,只不过增加了迭代次数,第三行直接使用MSELoss方法,即使用已经定义好的均方误差函数直接计算损失值,下面计算nn里面常用的损失函数
torch.nn.MSELoss 使用均方误差对损失值进行计算,定义是不用传入任何参数,需要输入两个维度一样的参数进行计算,使用时通过通过定义的变量传入两个维度一样的参数直接调用
torch.nn.L1Loss使用平均绝对误差进行损失值计算,定义时不用传入任何参数,使用时同样需要传入两个参数维度一样
torch.nn.CrossEntropyLoss用于计算交叉熵,定义时不用传入任何参数,使用时需要传入两个满足计算交叉熵条件的参数
在学会了优化函数之后就可以对自己建立的网络进行优化了
for epoch in range(epoch_n):
y_pred=model(x)
loss=loss_fn(y_pred,y)
if epoch%1000==0:
print("Epoch:{},Loss:{:.4f}".format(epoch,loss))
model.zero_grad()
loss.backward()
for param in model.parameters():
param.data -= param.grad.data*learning_rate
因为使用了不同的模型搭建方法,因此访问模型中的全部参数是使用model.parameters()来实现的,因为迭代次数更多,一昵称我们只输出每1000次的结果,输出如下:
同样可见损失函数逐渐递减,说明结果逐渐接近于预期,我们搭建的网络是有效的
因为前文中参数的优化和更新都没有实现自动化,并且学习速率也都是固定值,因此优化函数相对简单。但是如果我们自己实现高级的参数优化方法就会变得非常复杂,下面介绍torch中的optim包,它提供了非常多的自动化参数的类。包括SDG,Ada,AdaGrad,RMSprop等,下面以Adam为例,对参数优化过程尽心优化
import torch
from torch.autograd import Variable
batch_n=100
input_data=1000
hidden_layer=100
output_data=10
x=Variable(torch.randn(batch_n,input_data),requires_grad=False)
y=Variable(torch.randn(hidden_layer,output_data),requires_grad=False)
models=torch.nn.Sequential(torch.nn.Linear(input_data,hidden_layer),torch.nn.ReLU(),torch.nn.Linear(hidden_layer,output_data))
epoch_n=20
learning_rate=1e-4
loss_fn=torch.nn.MSELoss()
optimzer=torch.optim.Adam(models.parameters(),lr=learning_rate) # 定义优化函数
for epoch in range(epoch_n):
pred_y=models(x)
loss=loss_fn(pred_y,y)
print("Epoch:{},Loss:{:.4f}".format(epoch,loss))
optimzer.zero_grad()
loss.backward()
optimzer.step()
首先预准备工作都差不多,核心在于注释的第一行,在这里通过Adam方法指定了要优化的参数(models里面的参数),以及学习速率,随后在循环语句中,因为引入了优化算法,所以直接使用zeros_grad方法对梯度进行归零,最后使用optimzer.step()的方法,使用计算得到的梯度值对各个节点的参数进行梯度更新,可以看到我们只迭代了20次,但是由于优化方法更好,取得了较低的损失值: