zoukankan      html  css  js  c++  java
  • 神经网络

    神经网络

    可以使用 torch.nn 包构建一个神经网络。

    现在,我们已经大致了解了autogradnn 是依赖在 autograd 定义的模型和区分它们。一个 nn.Module 包含了层、 forward(input) 方法返回 output

    举个例子,看下面的神经网络用来分类数字图片:

    图片

    这是一个简单的前馈神经网络。它接受一个输入,通过几层一个接一个的前馈,最终得到输出。

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

    • 定义神经网络包含一些可学习的参数(或权重)
    • 遍历数据集的所有样本给输入
    • 通过网络处理输入
    • 计算损失度(和正确的输出相差多少)
    • 反馈梯度给网络的参数
    • 更新网络的权重,典型的更新规则是: 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 个输入 image channel,6 个输出 channel,3x3 square convolution
            # kernel
            self.conv1 = nn.Conv2d(1, 6, 3)
            self.conv2 = nn.Conv2d(6, 16, 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):
            # 最大池化在 (2, 2) 窗口上
            x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
            # 如果尺寸是方(square)的,你可以仅指定一个值
            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)
    
    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 函数, backward 函数(计算梯度)是自动地定义在你的 autograd 。你可以在 forward 使用任何的张量运算符。

    一个模型的可学习参数通过 net.parameters() 得到。

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

    尝试 (32 imes 32) 的输入。注意,这个网络(LeNet)期待的输入大小是 (32 imes 32) 。为了将此网络使用在 MNIST 数据集上,将数据集的图像大小调整到 (32 imes 32)

    input = torch.randn(1, 1, 32, 32)
    out = net(input)
    print(out)
    
    tensor([[ 0.0760, -0.0790,  0.0214,  0.1041,  0.0785,  0.0156,  0.0075,  0.0907,
              0.0538, -0.0357]], grad_fn=<AddmmBackward>)
    

    将所有参数的梯度缓冲区清零和随机化反向传播的梯度:

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

    torch.nn 仅仅支持小批量。整个 torch.nn 包仅支持训练样本的小批量作为输入,而且不能是一个样本。
    举个例子, nn,.Conv2d 将接受一个 4D 的张量:nSamples * nChannels * Height * Width
    如果你只有一个样本,那就使用 input.unsqueeze(0) 添加一个伪批量维度。

    在进一步处理之前,让我们回顾一下。

    回顾

    • torch.Tensor 一个支持例如 backward() 这样的操作的自动求梯度运算的多维数组。而且还有关于张量的梯度。
    • nn.Module 神经网络模块。封装参数的便捷方法,并将参数移入 GPU、导出、加载等等。
    • nn.Parameter 一种张量,当分配为 Module 的属性时,可以自动地注册为参数。
    • autograd.Function 实现自动求梯度运算的前向和后向的定义。每一个 Tensor 运算至少创建一个 Function 节点,连接创建 Tensor 的函数并对历史进行编码。

    至此,我们介绍了

    • 一个神经网络的定义
    • 处理输入和调用 backward

    还剩

    • 计算损失
    • 更新网络的权重

    损失函数(loss function)

    损失函数接受一对输入(output,target),并计算输出的值和目标值相差多少。

    有几个不同的损失函数定义在 nn 包下。一个简单的损失函数:nn.MSELoss 计算输入和目标的均方误差。

    output = net(input)
    target = torch.randn(10)  # 本例的一个伪目标值
    target = target.view(1, -1)  # 将其改为和输出相同的 shape
    criterion = nn.MSELoss()
    
    loss = criterion(output, target)
    print(loss)
    
    tensor(1.4172, grad_fn=<MseLossBackward>)
    

    现在,如果跟着 loss 的反向传播方向,使用它的 .grad_fn 属性,你将会看到如下所示的计算图:

    print('''input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
          -> view -> linear -> relu -> linear -> relu -> linear
          -> MSELoss
          -> loss''')
    
    input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
          -> view -> linear -> relu -> linear -> relu -> linear
          -> MSELoss
          -> loss
    

    因此,当我们调用 loss.backward() 整个图是求关于 loss 的微分,并且图中的所有有 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 0x0000021A309C3040>
    <AddmmBackward object at 0x0000021A309C30D0>
    <AccumulateGrad object at 0x0000021A309C3040>
    

    反向传播

    为了反向传播误差,我们要做的就是 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.0189,  0.0098,  0.0226, -0.0010,  0.0068,  0.0108])
    

    至此,我们已经看到了如何使用损失函数。

    神经网络的包包含了各种各样的模块和损失函数,这些模块和损失函数形成深度神经网络的组成要素。
    可以去官方文档查看完整的列表。

    现在,只剩下一件事:更新网络权重。

    更新权重

    实践中,最简单的更新规则就是随机梯度下降(SGD:Stochastic Gradient Descent):

    weight = weight - learning_rate * gradient

    我们可以非常容易的使用 Python 代码去实现它:

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

    但是,作为你的神经网络,你想使用各种不同的更新规则,比如 SGD、Nesterov-SGD、Adam、BMSPrp 等等。为了使用它们,我们构建了一个小包: torch.optim 它实现了这些所有方法。使用它们非常简单:

    import torch.optim as optim
    
    # 创建你的优化控制器(optimizer)
    optimizer = optim.SGD(net.parameters(), lr=0.01)
    
    # 在训练的循环中:
    optimizer.zero_grad()  # 清零梯度缓冲区
    output = net(input)
    loss = criterion(output, target)
    loss.backward()
    optimizer.step()  # 更新
    
  • 相关阅读:
    启动nginx出错:open() "/var/run/nginx/nginx.pid" failed (2: No such file or directory)
    linux vsftpd 550 create directory operation failed解决方法
    如何配置vsftpd
    配置<welcome-file>直接访问请求
    搭建solr集群的时候出现 ./zkcli.sh:行13: unzip: 未找到命令
    使用redis集群中遇到的错误
    存储过程
    查询
    sqlHelper
    一款批量修改AE模板的小工具
  • 原文地址:https://www.cnblogs.com/geekfx/p/13911810.html
Copyright © 2011-2022 走看看