1. Pytorch中的Tensor
1.1数据类型定义方式:
(1)torch.FloatTensor:用于生成浮点型Tensor,传递给torch.FloatTensor的参数可以是一个列表,也可以是一个维度值。(2)torch.IntTensor: 用于生成整型的Tensor(3)torch.rand:用于生成浮点型且维度指定的随机Tensor,满足0-1的均匀分布(4)torch.randn:满足均值0方差1的正态分布(5)torch.range:生成浮点型且自定义起始范围和结束范围的Tensor,需要传入三个参数,起始值,结束值和步长(6)torch.zeros:生成浮点型且维度指定的Tensor,其中元素全为0。
1.2 Tensor运算:
(1)torch.abs:
(2)torch.add:
(3)torch.clamp:裁剪,对输入参数按照自定义范围进行裁剪。
(4)torch.div:返回输入参数的求商结果。
(5)torch.mul:返回输入参数求积结果。
(6)torch.pow:返回输入参数求幂结果
(7)torch.mm:返回输入参数求积结果。(矩阵运算)
(8)torch.mv:返回输入参数求积结果。(矩阵和向量运算)
1.3 搭建简易神经网络:
import torch
batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10
##batch_n--一个批次输入数据量 input_data--每个数据包含的数据特征 hidden_layer--经过隐藏层后保留的数据特征个数 output_data 输出的数据
##
x = torch.randn(batch_n,input_data)
y = torch.randn(batch_n,output_data)
w1 = torch.randn(input_data,hidden_layer)
w2 = torch.randn(hidden_layer,output_data)
##定义从输入层到隐藏层到输出层权重参数,注意保证参数维度一致。保证后续矩阵连续乘法运算。
epoch_n = 20
learning_rate = 1e-6
##定义训练总次数和学习率
#是用梯度下降法优化神经网络参数
for i in range(epoch_n):
h1 = x.mm(w1)
h1 = h1.clamp(min = 0)#类似relu激活函数
y_pred = h1.mm(w2)
loss = (y_pred-y).pow(2).sum()#MSE误差
print("Epoch:{},Loss:{:.4f}".format(i,loss))
##反向传播
grad_y_pred = 2*(y_pred - y)
grad_w2 = h1.t().mm(grad_y_pred)
grad_h = grad_y_pred.clone()
grad_h = grad_h.mm(w2.t())
grad_h.clamp(min = 0)
grad_w1 = x.t().mm(grad_h)
w1 -= learning_rate*grad_w1
w2 -= learning_rate*grad_w2
2. 自动梯度
通过使用torch.autograd包,可以使模型参数自动计算在优化过程中需要用到的梯度值,在很大程度上帮助降低了实现后向传播代码的复杂度。
2.1 torch.autograd和variable
torch.autograd包的主要功能是完成神经网络后向传播中的链式求导,手动实现链式求导代码较复杂。实现自动梯度的过程:先通过输入的Tensor数据类型的变量在神经网络前向传播中生成一张计算图,然后根据这个计算图和输出结果准确计算出每个参数需要更新的梯度,并通过完成后向传播完成对参数梯度的更新。
实现时需要使用variable类对定义的tensor进行封装,封装后,计算图中的各个节点就是一个variable对象。用X代表选中的节点,则X.data代表Tensor数据类型的变量,X.grad也是一个variable对象,表示X的梯度,想要访问梯度值时需要使用X.grad.data。
import torch
from torch.autograd import Variable
batch_n = 64
hidden_layer = 100
input_data = 1000
output_data = 10
##定义前向传播forward函数和后向传播backward函数
class Model(torch.nn.Module):
def __init__(self):
super(Model,self).__init__()
def forward(self,input,w1,w2):
x = torch.mm(input,w1)
x = torch.clamp(x,min = 0)
x = torch.mm(x,w2)
return x
def backward(self):
pass
##以上代码为常用python类构造方式:首先通过class完成类继承操作,之后分别是类的初始化,以及函数定义,forward完成前向传播中的矩阵运算,backward完成后向传播中自动梯度计算。定义好之后可以对其进行调用
model = Model()
##以上完成了神经网络的搭建,接下来进行训练和优化部分
x = Variable(torch.randn(batch_n,input_data),requires_grad = False)
y = Variable(torch.randn(batch_n,output_data),requires_grad = False)
w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad = True)
w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad = True)
epoch_n = 30
learning_rate = 1e-6
for epoch in range(epoch_n):
y_pred = model(x,w1,w2)
loss = (y_pred-y).pow(2).sum()
print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data))
loss.backward()
w1.data -= learning_rate*w1.grad.data
w2.data -= learning_rate*w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
3. 模型搭建和参数优化
3.1 torch.nn
torch.nn包提供了很多神经网络相关类,如卷积层,池化层,全连接层等层次构造方法,防止过拟合的参数归一化方法,dropout方法还有激活函数部分的线性激活函数,非线性激活函数等。
之前的简单神经网络可以使用torch.nn实现
import torch
from torch.autograd import Variable
bach_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10
x = Variable(torch.randn(bach_n,input_data),requires_grad = False)
y = Variable(torch.randn(bach_n,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 = 10000
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()
for epoch in range(epoch_n):
y_pred = models(x)
loss = loss_fn(y_pred,y)
if epoch%1000 == 0:
print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data))
models.zero_grad()
loss.backward()
for param in models.parameters():
param.data -= param.grad.data*learning_rate
torch.nn.Sequential括号中的内容就是我们搭建的神经网络模型的具体结构,这里首先通过torch.nn.Linear(input_data,hidden_layer)完成从输入层到隐藏层的线性变换,然后经过激活函数及torch.nn.Linear(hidden_layer,output_data)完成从隐藏层到输出层的线性变换。接下来对Sequential,Linear,ReLU三个类进行详细介绍。
(1)torch.nn.Sequential:
是torch.nn中的一种序列容器,通过在容器中嵌套各种实现神经网络中具体功能相关的类,来完成对神经网络模型的搭建,最主要的是,参数会按照我们定义好的序列自动传递下去。我们可以将嵌套在容器中的各个部分看做各种不同的模块,这些模块可以自由组合。模块的加入一般有两种方式,一种是在以上代码中使用的直接嵌套,另一种是以orderdict有序字典的方式进行传入,这两种方式的唯一区别是,使用后者搭建的模型的每个模块都有我们自定义的名字,而前者默认使用从零开始的数字序列作为每个模块的名字。接下来对两种方式搭建模型做展示:
使用直接嵌套搭建的模型代码如下:
hidden_layer = 100
input_data = 1000
output_data = 10
models = torch.nn.Sequential(
torch.nn.Linear(input_data,hidden_layer),
torch.nn.ReLU(),
torch.nn.Linear(hidden_layer,output_data)
)
print(models)
使用orderdict有序字典进行传入来搭建的模型代码如下:
hidden_layer = 100
input_data = 1000
output_data = 10
from collections import OrderedDict
models = torch.nn.Sequential(OrderedDict([
("Line1",torch.nn.Linear(input_data,hidden_layer)),
("ReLU1",torch.nn.ReLU()),
("Line2",torch.nn.Linear(hidden_layer,output_data))])
)
print(models)
可以发现,使用第二种方法对模块使用自定义名称可以让我们更便捷地找到模型中相应的模块。
(2)torch.nn.Linear:
用于定义模型的线性层,即完成前面提到的不同层之间的线性变换。torch.nn.Linear类接受的参数有三个,分别是输入特征数,输出特征数和是否使用偏置,设置是否使用偏置的参数是一个布尔值,默认为True,即使用偏置。在实际使用的过程中,我们只需要将输入的特征数和输出的特征数传递给Linear类,就会自动生成对应维度的权重参数和偏置,对于生成的权重参数和偏置,我们的模型默认使用一种比简单随机方式更好的参数初始化方法。
可以在代码中看到Linear类代替了之前矩阵相乘的方式实现线性变换。
(3)torch.nn.ReLU:
属于非线性类激活函数,在定义时默认不需要传入参数。除此之外torch.nn包中还有很多非线性激活函数类可供选择,比如PReLU,LeakyReLU,Tanh,Sigmoid,Softmax等。
在掌握Sequential,Linear,ReLU三个类的使用方法后,快速搭建复杂的多层神经网络模型变为可能,且不需要对模型中使用的权重参数和偏置进行任何意义的定义和初始化说明,因为参数已经完成了自动生成。
接下来对已经搭建好的模型进行训练并对参数进行优化,代码如下:
epoch_n = 10000
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()
可以看到计算损失函数的代码发生了改变,现在使用nn包中定义好的均方误差函数类MSELoss来计算损失函数,而之前代码是根据损失函数计算公式写的。
接下来简单介绍nn包中常用损失函数用法。
(1)torch.nn.MSELoss:
使用均方误差函数对损失值进行计算,在定义类的对象时不传入任何参数,但在使用实例时需要输入两个维度一样的参数才能进行运算。
import torch
from torch.autograd import Variable
loss_f = torch.nn.MSELoss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
print(loss.data)
(2)torch.nn.L1Loss:
使用平均绝对误差函数对损失值进行计算。
import torch
from torch.autograd import Variable
loss_f = torch.nn.L1Loss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
print(loss.data)
(3)torch.nn.CrossEntropyLoss:
用于计算交叉熵,在定义类的对象时不用穿入任何参数,在使用实例时需要输入两个满足交叉熵的计算条件的参数。
import torch
from torch.autograd import Variable
loss_f = torch.nn.CrossEntropyLoss()
x = Variable(torch.randn(3,5))
y = Variable(torch.LongTensor(3).random_(5))
#这里生成的第一组参数是一个随机参数,维度为(3,5);第二组参数是三个范围为0-4的随机数字。
loss = loss_f(x,y)
print(loss.data)
完成损失函数定义后对网络模型进行训练并对参数进行优化:
for epoch in range(epoch_n):
y_pred = models(x)
loss = loss_fn(y_pred,y)
if epoch%1000 == 0:
print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data))
models.zero_grad()
loss.backward()
for param in models.parameters():
param.data -= param.grad.data*learning_rate
因为使用了不同的模型搭建方法,所以访问模型中的全部参数是通过对“models.parameters()”进行遍历完成的,然后才对每个遍历的参数进行梯度更新。
3.2 torch.optim
到目前为止,代码中的神经网络权重的参数优化和更新还没有实现自动化,并且目前使用的优化方法都有固定的学习速率,所以优化函数相对简单,如果我们自己实现一些高级的参数优化算法,则优化函数部分的代码会变得较为复杂。在Pytorch的torch.optim包中提供了非常多的可实现参数自动优化的类,比如SGD,AdaGrad,RMSProp,Adam等,这些类都可以被直接调用,使用起来也非常方便。我们使用自动化的优化函数实现方法对之前的代码进行替换,新的代码如下。
import torch
from torch.autograd import Variable
batch_n = 100
hidden_layer = 100
input_data = 1000
output_data = 10
x = Variable(torch.randn(batch_n,input_data),requires_grad = False)
y = Variable(torch.randn(batch_n,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 = 100
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()
optimzer = torch.optim.Adam(models.parameters(),lr = learning_rate)
这里使用了torch.optim包中的torch.optim.Adam类作为我们的模型参数的优化函数,在torch.optim.Adam类中输入的是被优化的参数和学习速率的初始值,如果没有输入学习速率的初始值,那么默认使用0.001这个值。因为我们需要优化的是模型中的全部参数,所以传递给Adam类的参数是models.parameters。另外,Adam优化函数还有一个强大的功能,就是可以对梯度更新使用到的学习速率进行自适应调节,所以最后得到的效果会比较理想。进行模型训练的代码如下:
for epoch in range(epoch_n):
y_pred = models(x)
loss = loss_fn(y_pred,y)
print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data))
optimzer.zero_grad()
loss.backward()
optimzer.step()
由于引入优化算法,可以直接调用optimzer.zero_grad()来完成对模型参数梯度的归零;并且增加了optimzer.step(),主要功能是使用计算得到的梯度值对各个节点的参数进行梯度更新。