Pytorch学习教程(三)---------神经网络

%matplotlib inline

神经网络

可以使用torch.nn package构造进行构造神经网络。

刚刚我们简单介绍了autograd,nn依赖于autograd来定义模型并区分它们。一个nn.Module包括你定义的网络层和一个forward(input)方法,这个方法返回output。

我们看下这个数字图片分类的网络的例子:

它是一个简单的前馈网络。它接受输入,并且每层的输入都是上一层的输出,最后给出输出。

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

  • 定义具有学习参数(或权重)的神经网络
  • 迭代输入数据集
  • 根据神经网络对输入数据集进行运算
  • 计算损失(输出与真实结果的距离。损失越小说明模型越准确。)
  • 将梯度反向传播给神经网络的参数。
  • 更新网络的权重(或参数)。通常使用一种简单的更新规则:权重=权重-学习率*梯度。

定义一个神经网络

import torch
import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        # 定义2个卷积层
        # kernel
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)
        # 定义了3个线性层,即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):
        # 在第一层卷积网络和第二层之间增加了relu激活函数和池化层
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        # 如果池化层是方块形的可以用一个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:]  # 切片0里面放的是当前训练batch的number,不是特征信息
        num_features = 1
        for s in size:
            num_features *= s
        return num_features


net = Net()
print(net)
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方法,后向函数(自动计算微分的函数)就会自动定义,当然你需要设置autograd参数为True。在forward方法中你可以使用张量的任何运算。

模型的学习参数由net.parameters()返回。

params = list(net.parameters())
print(len(params))
print(params[0].size())  # conv1's .weight
10
torch.Size([6, 1, 3, 3])

这个网络适合3232的数据集(13232经过一个核心为5的卷积层,大小变为62828;经过一个核心为2的池化层,大小变为61414;再经过一个核心为5的卷积层,大小变为161010;池化层,165*5,正好是下一个线性层的输入特征数),我们随机一个输入数据集,运行一下我们的模型吧:

input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
tensor([[ 0.0219,  0.0002,  0.0533, -0.0077, -0.0179, -0.0888, -0.0075,  0.0175,
          0.0084,  0.0613]], grad_fn=<AddmmBackward>)

将梯度清0并使用随机梯度进行反向传播:

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

注意:

torch.nn 只支持对批量数据的处理,不支持单个样本。

例如,nn.Conv2d需要一个4维的张量作为输入:[ nSamples,nChannels,Height,Width. ]

如果你只有一个样本,你可以使用input.unsqueeze(0)来增加一个假的batch维。

在继续进行之前,我们回顾下刚学到的几个类。

  • torch.Tensor:一个支持自动微分(或梯度)操作(例如backward())的多维数组。

  • nn.Module: 神经网络模型,帮助我们封装参数,移植到GPU,导出,加载等的便捷方式。

  • nn.Parameter: 张量,当你给Module定义属性时,参数会自动生成。

  • autograd.Function:实现自动梯度运算的前向后向定义。每一个张量运算,产生至少一个Function节点,连接到创建张量的函数并对其历史进行编码。

在这个基础上,我们已经做了:

  • 定义神经网络

  • 处理输入和调用backward方法

还剩下:

  • 计算损失

  • 更新网络的权重

损失函数

一个损失函数包括两个输入(预测值和实际值),然后返回这两个值的距离。

在nn package里面有不同的损失函数。一个简单的损失函数是:nn.MSELoss,它返回的是均方差,即每一个分量相减的平方累计最后除分量的个数。

output = net(input)
target = torch.randn(10)  # a dummy target, for example
target = target.view(1, -1)  # make it the same shape as output
criterion = nn.MSELoss()

loss = criterion(output, target)
print(loss)
tensor(1.1062, grad_fn=<MseLossBackward>)

所以,当你调用loss.backward()时,在整个计算图都会进行微分,所有requires_grad=True的张量的.grad属性都会累加。

为了更好的理解它,列出很少的几步:

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
<MseLossBackward object at 0x000001AF9D9C56D8>
<AddmmBackward object at 0x000001AF9D9C5710>
<AccumulateGrad object at 0x000001AF9D9C56D8>

反向传播

为了反向传播误差我们所要做的就只是调用loss.backward()。也需要清除已经存在的梯度,不然梯度会和已经存在的梯度进行累计。

我们调用loss.backward(),看下conv1‘s的bias的梯度在反向传播前后的变化。

net.zero_grad()     # zeroes the gradient buffers of all parameters

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.0098,  0.0003, -0.0068,  0.0056, -0.0279, -0.0121])

现在我们已经知道如何使用损失函数了。

稍后阅读:

nn package包含各种深度神经网络的模块和损失函数。这里有一个完整的列表 http://pytorch.org/docs/nn

我们要做的最后一步是:

更新网络模型的权重(参数)

更新权重

实践中常用的最简单的更新规则是Stochastic Gradient Descent(SGD):

weight = weight - learning_rate * gradient

上式用python代码实现:

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

然而,在你使用神经网络时,你想要使用各种不同的更新规则例如SGD(随机梯度下降法),Nesterov-SGD,Adam,RMSProp(均方根传播)等。我们实现了所有的这些方法,在库torch.optim中。使用起来也很简单:

import torch.optim as optim

# create your optimizer
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

注意:

观察到我们必须手动的把梯度缓存设置为0,即optimizer.zero_grad(),这是因为我们在反向传播那一节也提到过的,梯度会被累计。

全部评论

相关推荐

ProMonkey2024:5个oc?厉害! 但是有一个小问题:谁问你了?😡我的意思是,谁在意?我告诉你,根本没人问你,在我们之中0人问了你,我把所有问你的人都请来 party 了,到场人数是0个人,誰问你了?WHO ASKED?谁问汝矣?誰があなたに聞きましたか?누가 물어봤어?我爬上了珠穆朗玛峰也没找到谁问你了,我刚刚潜入了世界上最大的射电望远镜也没开到那个问你的人的盒,在找到谁问你之前我连癌症的解药都发明了出来,我开了最大距离渲染也没找到谁问你了我活在这个被辐射蹂躏了多年的破碎世界的坟墓里目睹全球核战争把人类文明毁灭也没见到谁问你了(别的帖子偷来的,现学现卖😋)
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务