零基础深度学习Pytorch教程,一个小时快速入门(三)

Pytorch官网有非常优秀的教程,其中有几篇小短文属于名为DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ这个小专栏的内容,考虑到大家阅读英文文献有点困难,笔者打算花些时间做一下翻译,同时结合自己的理解做一些内容调整,原文链接贴在这里点此跳转。承接之前的内容,点此跳转到第二部分。好的我们开始第三部分。

这一部分的内容是神经网络,这一次就开始对整个网络进行介绍,为下一部分的实战做一个铺垫。

神经网络可以使用torch.nn的包来构建。

如今你已经了解过一些autograd的内容,nn依赖于autograd来定义和区分模型。一个nn.Module包含很多层,包括前向从输出开始到返回输出。

举个栗子,看一下下面这一张通过网络分类图片的图:

零基础深度学习Pytorch教程,一个小时快速入门(三)_第1张图片

它是一个简单的前馈神经网络,它通过获取输入,喂给一层又一层的网络,然后最后输出结果。

一个经典的网络训练过程如下图:

(1)定义一个神经网络然后有一些可以学习的参数或者权重

(2)遍历输入数据集

(3)通过神经网络输入数据

(4)计算loss值(预测值和真实值的误差)

(5)将梯度回传到网络参数中

(6)更新网络权重,通常使用一个简单的更新规则:weight = weight - learning_rate * gradient

下面就让我们一步步的实现: 

 

定义一个网络

让我们定义这个网络:

# 引入对于的库
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个图片输入通道, 6个输出通道, 5x5 面积的卷积核
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        # an affine operation: y = Wx + b
        self.fc1 = nn.Linear(16 * 5 * 5, 120)  # 5*5来源于图片维度中
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # 最大池化层通过了一个2*2的窗口
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 如果大小是正方形,则可以用单个数字指定
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = torch.flatten(x, 1) # 除batch(批量)使用的维度外的所有尺寸都要打平,即把高维降成一维
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()
print(net)

结果输出:

Net(
  (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, 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)
)

你必须定义前向传播,然后反向传播函数(用来计算梯度) 被使用autograd自动地定义。你可以使用任意一个tensor操作在前向传播函数中。

一个模型的学习率参数通过net.parameters()来学习。

params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1的权重

输出结果为:

10
torch.Size([6, 1, 5, 5])

让我们试着随机化32 × 32的输入,这个网络(LeNet)的输入尺寸是32 × 32。为了在MNIST上使用 数据集,需要改变数据集图片的尺寸为32 × 32.

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)

输出结果为:

tensor([[-0.0794,  0.0241,  0.0712, -0.0940,  0.0481, -0.0220,  0.0628,  0.0115,
         -0.0880, -0.0059]], grad_fn=)

用零初始化所有参数的梯度,然后用随机数初始化反向传播的参数。

net.zero_grad()
out.backward(torch.randn(1, 10))

torch.nn仅仅支持mini-batch(小批量的,就是把大的数据集分成一批一批的) ,整个torch.nn包紧急支持输出一个mini-batch的样本,而不支持以后单独的样本。

举个例子,nn.Conv2d将采用一个4个维度的tensor,nSample × nChannels × Height × Width

如果你有一个单独的样本,使用input.unsqueeze(0)去伪装成一批数据。

 在继续学习其他知识之前,让我们重新回顾已经学习的知识。

回顾

(1)torch.Tensor一个支持自动求梯度操作的多维度的数组就像backward()。也会保存相关梯度的参数。

(2)nn.Module神经网络模型,方便参数封装,可以移动到GPU中,读取和保存。

(3)nn.Parameter一种tensor,能够自动化注册参数然后分配给对应的一个模块上。

(4)autograd.Function实现前向或者反向创博使用自动化执行的方式,创建一个tensor然后对它的历史过程进行编码。

loss(损失函数)函数

一个损失函数获取(输出结果,目标结果)输入对,然后计算一个数值,预估输出结果和目标结果相差多少。

有许多不同的损失函数都在nn这个包中。一个简单的loss是:nn.MSELoss这个可以计算两个数据直接的均方差。

output = net(input)
target = torch.randn(10)  # 一个假设的目标
target = target.view(1, -1)  # 使用同样的形状作为输出
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)

输出结果为:

tensor(0.8815, grad_fn=)

如今,二u给你向在反向传播的方向获取一个loss值,使用它的.grad_fn就可以看到图表化的计算结果:

input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
      -> flatten -> linear -> relu -> linear -> relu -> linear
      -> MSELoss
      -> loss

所以,当我们调用loss.backward()的时候,整个图会区分神经网络的各个参数,所有的tensor带有requires_grad=True的结果都会有他们自己.grad tensor结果。

 为了表述清楚,来进行一步反向传播:

print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU

输出结果:



 

反向传播 

为了反向传播误差值我们需要去使用loss.backward()。你需要去清除现在已经存在的梯度值的梯度,否则梯度会不断累积。

现在我们应该调用loss.backward(),然后找到conv1的偏置梯度然后反向传播。

net.zero_grad()     # 用零初始化所有参数的地图

print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)

loss.backward()

print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

输出结果为:

conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0029, -0.0122,  0.0044,  0.0115,  0.0076,  0.0122])

现在,我们需要制度如何让使用损失函数。

更新权重 

 最简单的更新规则就是私用随机梯度下降法(Stochastic Gradient Descent,简称SGD):

weight = weight - learning_rate * gradient

我们能够实现这种简单的Python代码,PS(说实话似乎也没那么简单╮(╯▽╰)╭):

learning_rate = 0.01
for f in net.parameters():
    f.data.sub_(f.grad.data * learning_rate)

但是如果你需要使用一个神经网络,你想去适应各种各样的更新规则比如SGD,Nestrov-SGD,Adam,RMSProp等等,可以使用这个touch.optim这个包。

import torch.optim as optim

# 创建你的优化器
optimizer = optim.SGD(net.parameters(), lr=0.01)

# in your training loop:
optimizer.zero_grad()   # zero the gradient buffers
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()    # Does the update

这第三部分也就结束了,接下来就等着最后一部分了,我尽快。 

你可能感兴趣的