Python:PyTorch 定义网络 (七十六)

定义网络

关于使用 PyTorch 构建网络的视频。从一个简单的前馈网络开始。显示 nn.Linear、F.relu、nn.dropout。
指导学员如何实现用 MNIST 进行训练的 FF 网络。MNIST 数据集是一对手写数字的图像。

此数据集用于训练网络或其他机器学习模型,使它们能够将图像分类为数字

通过 PyTorch 构建神经网络

下面我们将了解如何使用 PyTorch 构建神经网络。

# Import things like usual

%matplotlib inline
%config InlineBackend.figure_format = 'retina'

import numpy as np
import torch

import helper

import matplotlib.pyplot as plt
from torchvision import datasets, transforms

首先,我们需要获取数据集。这些数据位于 torchvision 软件包中。以下代码将下载 MNIST 数据集,然后为我们创建训练数据集和测试数据集。暂时不用对细节部分太关心,稍后你会详细学习的。

# Define a transform to normalize the data
transform = transforms.Compose([transforms.ToTensor(),
                              transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
                             ])
# Download and load the training data
trainset = datasets.MNIST('MNIST_data/', download=True, train=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

# Download and load the test data
testset = datasets.MNIST('MNIST_data/', download=True, train=False, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=True)
dataiter = iter(trainloader)
images, labels = dataiter.next()

# print(labels)
# print(images[1].numpy().squeeze())

我们将训练数据加载到了 trainloader 中,并使用 iter(trainloader)使其变成迭代器。我们将用它循环访问数据集以进行训练,但是现在我只获取了第一批数据,以便查看数据。从下方可以看出,images 是一个大小为 (64, 1, 28, 28) 的张量。因此,每批有 64 个图像、1 个颜色通道,共有 28x28 个图像。

plt.imshow(images[1].numpy().squeeze(), cmap='Greys_r')
<matplotlib.image.AxesImage at 0x7f15e0bc6ac8>

file

通过 PyTorch 构建神经网络

我将使用 PyTorch 构建一个简单的前馈网络,用于对 MNIST 图像进行分类。即该网络将接收数字图像作为输入,并预测图像中的数字。

file

要通过 PyTorch 构建神经网络,你需要使用 torch.nn 模块。网络本身是继承自 torch.nn.Module 的类。你需要单独定义每个运算,例如针对具有 784 个输入和 128 个单元的全连接层定义为 nn.Linear(784, 128)

该类需要包含对网络实现前向传递的 forward 方法。在此方法中,你将对之前定义的每个运算传递输入张量 xtorch.nn 模块在 torch.nn.functional 中还具有一些对等的功能,例如 ReLU。此模块通常导入为 F。要对某个层(只是一个张量)使用 ReLU 激活函数,你需要使用 F.relu(x)。以下是一些常见的不同激活函数。

file

对于此网络,我将添加三个全连接层,然后添加一个预测类别的 softmax 输出。softmax 函数和 S 型函数相似,都会将输入调整到 0 到 1 之间,但是还会标准化这些输入,以便所有值的和为 1,就像正常的概率分布一样。

from torch import nn
from torch import optim
import torch.nn.functional as F
from torch.autograd import Variable
class Network(nn.Module):
    def __init__(self):
        super().__init__()
        # Defining the layers, 128, 64, 10 units each
        self.fc1 = nn.Linear(784, 128)
        self.fc2 = nn.Linear(128, 64)
        # Output layer, 10 units - one for each digit
        self.fc3 = nn.Linear(64, 10)

    def forward(self, x):
        ''' Forward pass through the network, returns the output logits '''

        x = self.fc1(x)
        x = F.relu(x)
        x = self.fc2(x)
        x = F.relu(x)
        x = self.fc3(x)

        return x

    def predict(self, x):
        ''' This function for predicts classes by calculating the softmax '''
        logits = self.forward(x)
        return F.softmax(logits)

net = Network()
net
Network(
  (fc1): Linear(in_features=784, out_features=128, bias=True)
  (fc2): Linear(in_features=128, out_features=64, bias=True)
  (fc3): Linear(in_features=64, out_features=10, bias=True)
)

初始化权重和偏差

权重等参数是系统自动初始化的,但是你也可以自定义如何初始化这些权重。权重和偏差是附加到你所定义的层的张量,你可以通过 net.fc1.weight 获取它们。

print(net.fc1.weight)
print(net.fc1.bias)
Parameter containing:
tensor([[-3.0241e-02,  4.4066e-03,  1.3974e-02,  ..., -1.9309e-02,
          1.0016e-02, -2.2536e-02],
        [ 1.7180e-02,  4.7228e-03, -2.8895e-02,  ...,  1.5672e-02,
          2.2669e-02, -7.0154e-03],
        [-8.9447e-03, -5.5896e-03, -2.4434e-02,  ..., -3.1379e-02,
         -2.2174e-02,  3.2440e-02],
        ...,
        [ 1.5040e-02, -2.1528e-02, -1.7114e-02,  ..., -1.4072e-04,
         -1.2104e-02,  1.0004e-02],
        [-8.1822e-03,  2.9496e-02, -8.4151e-03,  ..., -2.4345e-02,
          2.6140e-02,  1.6807e-02],
        [-2.6826e-02, -2.1177e-02,  6.5086e-03,  ..., -2.5218e-02,
         -3.2065e-02,  2.8937e-02]])
Parameter containing:
tensor(1.00000e-02 *
       [ 3.1542, -1.7342, -2.1497, -1.0821,  3.1377,  0.9586,  2.8925,
         0.0542,  3.4995,  2.0240,  1.4187,  3.3561, -1.7415, -3.5396,
        -3.5333,  1.6903, -3.4994,  2.2294, -3.5214,  2.7299, -0.8103,
         0.5224, -1.5546,  2.7586,  2.8542,  1.7023,  2.9034, -1.7315,
         1.5604,  2.7831, -3.1129, -0.5623, -2.2574, -3.3509, -2.1946,
         3.4741, -2.1797,  2.3253, -3.3238,  3.4261,  0.3980, -2.6069,
        -3.1360,  1.6052, -2.8879,  1.5004,  0.7647,  2.8695, -2.1236,
         0.5421, -1.5433,  3.3891, -1.9037, -0.2953,  0.0231,  2.6445,
         2.8549,  0.1714,  0.1802, -2.0729, -2.2221, -2.7699, -1.7883,
        -2.1269,  2.9939,  0.3668, -0.9957,  0.0950,  3.0375, -1.4314,
        -0.7270,  3.1325, -0.3636,  0.0608, -2.7781,  0.7890,  2.8682,
        -0.7768, -2.4334,  0.5169, -3.3676,  2.4219,  0.7760,  0.9049,
         1.5173, -3.2969, -2.8419, -0.1239,  3.1865, -2.2526, -3.3603,
         1.1960, -2.9012,  2.5352, -0.0061,  3.4463, -2.6725, -0.6125,
         3.3132,  0.7425,  3.1817,  0.7956, -0.0428, -2.5479, -1.0976,
         3.3106, -2.1201, -1.3958, -2.7474,  0.2148,  2.9428,  3.0631,
         0.0926, -2.1324, -1.2255,  0.7599, -3.3021,  0.9725, -0.9577,
         2.3643,  1.5917, -1.1061, -0.1588,  3.4311,  2.5810, -1.3146,
        -0.6671,  2.2558])

要自定义初始化过程,请原地修改这些张量。实际上存在 autograd 变量,因此我们需要通过 net.fc1.weight.data 获取真正的张量。获得张量后,可以用 0(针对偏差)或随机正常值填充这些张量。

# Set biases to all zeros
net.fc1.bias.data.fill_(0);
# sample from random normal with standard dev = 0.01
net.fc1.weight.data.normal_(std=0.01);

前向传递

我们已经创建好网络,看看传入图像后会发生什么。这一过程称之为前向传递。我们将图像数据转换为张量,然后传递给网络架构定义的运算。

# Grab some data 
dataiter = iter(trainloader)
images, labels = dataiter.next()
images.resize_(64, 1, 784)

# Need to wrap it in a Variable, will explain in next notebook
inputs = Variable(images) 

# Forward pass through the network
img_idx = 0
logits = net.forward(inputs[img_idx,:])

# Predict the class from the network output
ps = F.softmax(logits, dim=1)

img = images[img_idx]
helper.view_classify(img.resize_(1, 28, 28), ps)

file

从上图中可以看出,我们的网络基本上根本不知道这个数字是什么,因为我们还没训练它,所有权重都是随机的!接下来,我们将了解如何训练该网络,使其能学习如何正确地对这些数字进行分类。

为者常成,行者常至