zoukankan      html  css  js  c++  java
  • PyTorch教程之Neural Networks

    我们可以通过torch.nn package构建神经网络。

    现在我们已经了解了autograd,nn基于autograd来定义模型并对他们有所区分。

    一个 nn.Module模块由如下部分构成:若干层,以及返回output的forward(input)方法。

    例如,这张图描述了进行数字图像分类的神经网络:

    这是一个简单的前馈( feed-forward)网络,读入input内容,每层接受前一级的输入,并输出到下一级,直到给出outpu结果。

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

    1.定义具有可学习参数(或权重)的神经网络

    2.遍历input数据集

    3.通过神经网络对input进行处理得到output结果

    4.计算损失(ouput离正确值有多远)

    5.将梯度返回到神经网络的参数中

    6.更新神经网络的权重,通常使用一个简单的更新规则:

    weight = weight - learning_rate * gradient

     一、如何在pytorch中定义神经网络

    定义神经网络:

    import torch
    from torch.autograd import Variable
    import torch.nn as nn
    import torch.nn.functional as F
    
    class Net(nn.Module):
        # 定义Net的初始化函数,本函数定义了神经网络的基本结构
        def __init__(self):
            # 继承父类的初始化方法,即先运行nn.Module的初始化函数
            super(Net,self).__init__()
            # 定义卷积层:输入1通道(灰度图)的图片,输出6张特征图,卷积核5x5
            self.conv1 = nn.Conv2d(1,6,(5,5))
            # 定义卷积层:输入6张特征图,输出16张特征图,卷积核5x5
            self.conv2 = nn.Conv2d(6,16,5)
            # 定义全连接层:线性连接(y = Wx + b),16*5*5个节点连接到120个节点上
            self.fc1 = nn.Linear(16*5*5,120)
            # 定义全连接层:线性连接(y = Wx + b),120个节点连接到84个节点上
            self.fc2 = nn.Linear(120,84)
            # 定义全连接层:线性连接(y = Wx + b),84个节点连接到10个节点上
            self.fc3 = nn.Linear(84,10)
        # 定义向前传播函数,并自动生成向后传播函数(autograd)
        def forward(self,x):
            # 输入x->conv1->relu->2x2窗口的最大池化->更新到x
            x = F.max_pool2d(F.relu(self.conv1(x)),(2,2))
            # 如果大小是一个正方形,可以只指定一个数字
            x = F.max_pool2d(F.relu(self.conv2(x)),2)
            # view函数将张量x变形成一维向量形式,总特征数不变,为全连接层做准备
            x = x.view(-1,self.num_flat_features(x))
            # 输入x->fc1->relu,更新到x
            x = F.relu(self.fc1(x))
            # 输入x->fc2->relu,更新到x
            x = F.relu(self.fc2(x))
            # 输入x->fc3,更新到x
            x = self.fc3(x)
            return x
        # 计算张量x的总特征量
        def num_flat_features(selfself,x):
            # 由于默认批量输入,第零维度的batch剔除
            size = x.size()[1:]
            num_features = 1
            for s in size:
                num_features *= s
            return num_features
    
    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 (400 -> 120)
      (fc2): Linear (120 -> 84)
      (fc3): Linear (84 -> 10)
    )

     通过net.parameters()可以得到可学习的参数:

    params = list(net.parameters())
    print(len(params))
    print(params[0].size())  # conv1's .weight

    输出结果:

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

    我们模拟一下单向传播,其中input和output均为autograd.Variable:

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

    输出结果:

    Variable containing:
    -0.0618 -0.0648 -0.0350  0.0443  0.0633 -0.0414  0.0317 -0.1100 -0.0569 -0.0636
    [torch.FloatTensor of size 1x10]

    对所有参数的梯度缓冲区归零并设置随机梯度反向传播:

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

    整个torch.nn包只接受那种小批量样本的数据,而无法接受单个样本。 例如,nn.Conv2d能够构建一个四维的Tensor:nSamples x nChannels x Height x Width。

    如果需要对单个样本进行操作,使用input.unsqueeze(0)来加一个假维度就可以了。

    我们回顾一下目前出现过的概念:

    torch.Tensor - 一个多维数组
    autograd.Variable - 改变Tensor并且记录下来历史操作过程。和Tensor拥有相同的API,以及backward()的一些API。同时包含着和Tensor 相关的梯度。
    nn.Module - 神经网络模块。便捷的数据封装,能够将运算移往GPU,还包括一些输入输出的东西。
    nn.Parameter - 一种变量(Variable),当将任何值赋予Module时自动注册为一个参数。
    autograd.Function - 实现了使用自动求导方法的前馈和后馈的定义。每个Variable的操作都会生成至少一个独立的Function节点,与生成了Variable的函数相连之后记录下历史操作过程。

    二、Loss Function

    Loss function根据(output, target) 输入对计算一个值,该值估计输出离目标有多远。

     nn包下有几种不同的损失函数。一个简单的损失是:nn.MSELoss,它计算输入和目标之间的均方误差。

     

    output = net(input)
    target = Variable(torch.arange(1, 11))  # a dummy target, for example
    criterion = nn.MSELoss()
    
    loss = criterion(output, target)
    print(loss)

    输出结果:

    Variable containing:
     38.4929
    [torch.FloatTensor of size 1]

    现在如果你沿着loss函数反方向走,用.grad_fn属性,你会看到一个像这样的计算图:

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

    三、Backprop

    我们只需要进行loss.backward()即可向前传播误差。

    你先对现有的梯度进行清零,否则其他梯度将被累积到现有的梯度上。

    我们可以看一看 conv1’s bias在loss.backward()前后的梯度:

    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)

    输出结果:

    Variable containing:
     0
     0
     0
     0
     0
     0
    [torch.FloatTensor of size 6]
    Variable containing:
    -0.0929
     0.0419
    -0.0218
     0.0941
     0.2251
     0.1809
    [torch.FloatTensor of size 6]

    四、Update the weights

    在实践中使用的最简单的更新规则是随机梯度下降法(SGD):weight weight learning_rate gradient。

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

    然而,在使用神经网络时,用户希望使用各种不同的更新规则,如SGD、nesterov - SGD、Adam、RMSProp等,为了实现这一点,pytorch构建了一个小的包:torch.optim实现了所有这些方法。

    SGD使用方法如下:

    import torch.optim as optim
    
    # create your optimizer lr为learning_rate
    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()     #得到梯度,i.e. 给Variable.grad赋值
    optimizer.step() # Does the update

      

  • 相关阅读:
    建站随手记:about server stack
    【个人申明】主要发表平台迁至简书和公众号
    每日一问:Android 消息机制,我有必要再讲一次!
    每日一问:View.getContext() 的返回一定是 Activity 么?
    每日一问:Android 滑动冲突,你们都是怎样处理的
    每日一问:谈谈 SharedPreferences 的 apply() 和 commit()
    每日一问:浅谈 onAttachedToWindow 和 onDetachedFromWindow
    每日一问:到底为什么属性动画后 View 在新位置还能响应事件
    百万级日活 App 的屏幕录制功能是如何实现的
    每日一问:不一样的角度吐槽下 DataBinding
  • 原文地址:https://www.cnblogs.com/xueqiuqiu/p/7514674.html
Copyright © 2011-2022 走看看