【deep learning with pytorch】运用pytorch写自己的神经网络
之前学习了pytorch中的tensor与建立自己的数据集。这次要开始写自己的神经网络。pytorch一般的神经网络运算如卷积池化等都是打包在了torch.nn的库里面。
在pytorch之中对tensor的所有操作都是在autograd库里面的,包括反向传播,运算等等。而torch.nn又是依赖于autograd库的来定义模型架构与区分模型。其中autograd.Function是一个用来前向传播与反向传播的类,包含了forward()与backward()函数。
nn.Module包含网络层,和前向传播函数,并返回输出结果。
一个典型的神经网络训练过程可以归纳为:
1、定义神经网络。
2、构建数据集,并将数据集迭代输入
3、用神经网络处理输入的数据
4、计算loss
5、反向传播神经网络的参数
6、更新网络参数
定义神经网络
例如定义一个神经网络:
import torch
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
# 1 input image channel, 6 output channels, 3x3 square convolution
# kernel
self.conv1 = nn.Conv2d(1, 6, 3)
self.conv2 = nn.Conv2d(6, 16, 3)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 6 * 6, 120) # 6*6 from image dimension
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# Max pooling over a (2, 2) window
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# If the size is a square you can only specify a single number
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # all dimensions except the batch dimension
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
输出为:
(conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1))
(conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1))
(fc1): Linear(in_features=576, out_features=120, bias=True)
(fc2): Linear(in_features=120, out_features=84, bias=True)
(fc3): Linear(in_features=84, out_features=10, bias=True)
)
这里只需要定义forward函数(只能用Tensor变量的类型输入到forward之中),让计算机知道整个网络的计算方式是什么样子的,而反向传播的计算(backward()自动生成)。但这里要注意这只是针对于普通的神经网络,如果要重新定义神经网络卷积的方式,就需要重新定义反向传播的方式。
学习得到
的网络参数会返回到 net.parametes() 之中
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
#out:10
#torch.Size([6, 1, 3, 3])
定义神经网络这个部分还想要多扩充一点关于源代码的知识:
因为写网络很多时候不会简单的只写一个网络,会出现对网络架构的修改或者在前面网络的基础上进行延伸的问题,所以不能只看forward()与backward(),需要考虑nn.Module 这个库中的其它的函数:这里建议去看https://pytorch.org/docs/stable/nn.html
其中有几个可以列举一下:
add_module(name,module):这里为模块增加一个子模块,通过父模块可以直接调用子模块
apply(fn):这里面fn是一个函数,将整个模块变为参数传入到这个函数之中。
cpu() 将模块中所有的参数和节点转为cpu模式
cuda() 将模块的所有参数与节点转为GPU模式
eval() 将模块调整为evaluation的模式
train(mode = True) 将这个模型设置在训练的模式
load_state_dict(state_dict,strict = True) :将state_dict之中所有的参数与节点都复制到模型和模型的孩子之中。
state_dict(destination=None, prefix=’’, keep_vars=False) 返回一个字典,包含了模型的整个状态,像参数,节点等都包括,字典的key是它们相应的名称。
输入数据
根据上面写好的神经网络的例子,可以尝试随机输入一个32*32的数据(仿真一个图像),得到的结果是:
input = torch.randn(1,1,32,32)#1 数据的类型是torch ,2 数据输入为(batch,channel,height,width)
out = net(input)
print(out)
#out:tensor([[-0.0491, -0.0338, -0.0398, -0.0413, 0.2044, -0.1106, 0.0700,0.1368,
#0.0359, 0.0736]], grad_fn=<AddmmBackward>)
net.zero_grad()#将网络中反向传播的梯度设置为0
out.backward(torch.randn(1,10))#这里不能out.backward()的原因是out不是一个scalar
计算loss
计算损失函数需要(output, target)为输入,计算出来具体的值并预估输出值与目标值相差多少。
有很多不同的计算方式在nn的库之中。一个较为简单的计算损失的Loss为nn.MSELoss,可以用来计算误差与目标值之间平均平方误差,还有其它的误差函数如CrossEntropyLoss函数等,可以在这里找到https://pytorch.org/docs/stable/nn.html
output = net (input)
target = torch.randn(10)
target = target.view(1,-1)#将gt变为和输出相同的维度
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
#out:tensor(0.8907, grad_fn=<MseLossBackward>)
反向传播和更新权重
上面我们得到的loss为一个scalar,故而可以直接采用loss.backwawrd()进行反向传播
对于权重的简单更新为:
weight = weight - learning_rate*gradient
learning_rate = 0.01
for f in net.parameters():
f.data.sub_(f.grad.data * learning_rate)
但是神经网络中权重的更新方式一般没有上面的那么简单,一般会采用如SGD,Adam.RMSProp等。这些更新方式都被存储到 torch.optim 之中,可以直接调用:
import torch.optim as optim
#创建自己的优化器
optimizer = optim.SGD(net.parameters(),lr = 0.01)#这里参数为,网络参数和学习率
#在训练的循环之中可以调用optim
optimizer.zero_grad()#先将网络所有反向梯度归0,这里如果不手动归0,参数上的梯度会被累乘
output = net(input)
loss = criterion(output,target)
loss.backward()
optimizer.step()#进行更新