用Pytorch进行深度学习

实验楼学习链接

Pytorch文档 Torchvision文档

torch.empty(x,y) 返回填充了未初始化数据的张量

torch.rand(x,y) 创建一个随机初始化的矩阵, 范围(0,1)

torch.zeros(x, y, dtype=torch.type) 创建一个 0 填充的矩阵,指定数据类型

x = torch.tensor([[2,3],[3,4]]) 给定数据创建 Tensor,注意多维数组要封装成一个参数

x = x.new_ones(5, 3, dtype=torch.double) new_* 方法来创建对象,这里的意思是创建一个double类型,5×3的值为1.0的矩阵。根据现有张量创建新张量,如果没有设置dtype等属性,将重用输入张量的属性。

x = torch.randn_like(x, dtype=torch.float) 不改变size维度,只变化数值和dtype

x.size() 获取张量大小,返回值是 tuple 类型,所以它支持 tuple 类型的所有操作。

torch.add(x,y,out=result) or x+y 张量加法, 前一种方法指定了输出参数

y.add_(x) y的值将被更新为x+y的结果,任何以下划线结尾的操作都会用结果替换原变量。例如:x.copy_(y), x.t_(), 都会改变 x

z = x.view(-1, 8) torch.view 可以改变张量的维度和大小, size为-1时自动计算出行数?


将 PyTorch 张量转换为 NumPy 数组(反之亦然)是一件轻而易举的事。PyTorch 张量和 NumPy 数组将共享其底层内存位置,改变一个也将改变另一个。

b = a.numpy()

1
tensor([2., 2., 2., 2., 2.]), array([2., 2., 2., 2., 2.], dtype=float32)

b = torch.from_numpy(a)

1
array([2., 2., 2., 2., 2.]), tensor([2., 2., 2., 2., 2.], dtype=torch.float64)

所有的 Tensor 类型默认都是基于 CPU, CharTensor 类型不支持到 NumPy 的转换。

autograd为张量上的所有操作提供了自动求导。如果设置 .requires_grad 为 True,那么将会追踪所有对于该张量的操作。当完成计算后通过调用 .backward()会自动计算所有的梯度,这个张量的所有梯度将会自动积累到 .grad 属性。这也就完成了自动求导的过程。

如果 .requires_grad=True 但是你又不希望进行 Autograd 的计算,那么可以将变量包裹在 with torch.no_grad() 中


nn.Module 中包含了构建神经网络所需的各个层和 forward(input) 方法,该方法返回神经网络的输出。

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

  1. 定义包含可学习参数(权重)的神经网络模型。
  2. 在数据集上迭代。
  3. 通过神经网络处理输入。
  4. 计算损失(输出结果和正确值的差值大小)。
  5. 将梯度反向传播回网络节点。
  6. 更新网络的参数,一般可使用梯度下降等最优化方法。

模型中必须要定义 forward 函数,backward 函数(用来计算梯度)会被 autograd 自动创建。可以在 forward 函数中使用任何针对 Tensor 的操作。

一个损失函数接受一对 (output, target) 作为输入,计算一个值来估计网络的输出和目标值相差多少。

调用 loss.backward() 获得反向传播的误差。但是在调用前需要清除已存在的梯度,否则梯度将被累加到已存在的梯度。

1
2
3
4
5
6
7
8
9
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)

以上代码只能被执行一次。

最简单的权重更新规则是随机梯度下降SGD: weight=weight-learning rate*gradient

当你想使用其他不同的优化方法,如 SGD、Nesterov-SGD、Adam、RMSPROP 等来更新神经网络参数时。可以借助于 PyTorch 中的 torch.optim 快速实现。

1
2
3
4
5
6
7
8
9
10
11
12
import torch.optim as optim

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

# 执行一次训练迭代过程
optimizer.zero_grad() # 梯度置零
output = net(input)
loss = criterion(output, target)
loss.backward()
optimizer.step() # 更新
loss

训练一个图像分类器,基本流程如下:

  1. 使用 torchvision 加载和归一化训练集和测试集。— 文本可以使用原始 Python 和 Cython 来加载,或者使用 NLTK 或 SpaCy 处理。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    import torchvision
    import torchvision.transforms as transforms

    # 图像预处理步骤
    transform = transforms.Compose(
    [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
    # 训练数据加载器
    trainset = torchvision.datasets.CIFAR10(
    root='./data', train=True, download=True, transform=transform)
    trainloader = torch.utils.data.DataLoader(
    trainset, batch_size=4, shuffle=True, num_workers=2)
    # 测试数据加载器
    testset = torchvision.datasets.CIFAR10(
    root='./data', train=False, download=True, transform=transform)
    testloader = torch.utils.data.DataLoader(
    testset, batch_size=4, shuffle=False, num_workers=2)
    # 图像类别
    classes = ('plane', 'car', 'bird', 'cat', 'deer',
    'dog', 'frog', 'horse', 'ship', 'truck')

    trainloader, testloader
  2. 定义一个卷积神经网络。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import torch.nn as nn
    import torch.nn.functional as F


    class Net(nn.Module):
    def __init__(self):
    super(Net, self).__init__()
    self.conv1 = nn.Conv2d(3, 6, 5)
    self.pool = nn.MaxPool2d(2, 2)
    self.conv2 = nn.Conv2d(6, 16, 5)
    self.fc1 = nn.Linear(16 * 5 * 5, 120)
    self.fc2 = nn.Linear(120, 84)
    self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
    x = self.pool(F.relu(self.conv1(x)))
    x = self.pool(F.relu(self.conv2(x)))
    x = x.view(-1, 16 * 5 * 5)
    x = F.relu(self.fc1(x))
    x = F.relu(self.fc2(x))
    x = self.fc3(x)
    return x


    net = Net()
    net
  3. 定义损失函数和优化器。

    1
    2
    3
    criterion = nn.CrossEntropyLoss()
    optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
    optimizer

    这里使用交叉熵作为损失函数,使用带动量的随机梯度下降完成参数优化。

  4. 在训练集上训练网络。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    for epoch in range(1):  # 迭代一次
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
    # 获取输入
    inputs, labels = data
    # 梯度置 0
    optimizer.zero_grad()
    # 正向传播,反向传播,优化
    outputs = net(inputs)
    loss = criterion(outputs, labels)
    loss.backward()
    optimizer.step()
    # 打印状态信息
    running_loss += loss.item()
    if i % 200 == 199: # 每 200 批次打印一次
    print('[%d, %5d] loss: %.3f' %
    (epoch + 1, i + 1, running_loss / 200))
    running_loss = 0.0
    print('Finished Training.')
  5. 在测试集上测试网络。

    一般情况下,可以通过预测神经网络输出的类别标签与实际情况标签进行对比来进行检测。如果预测正确,我们把该样本添加到正确预测列表。

    输出是各个标签的权重。一个类别的权重越大,神经网络越认为它是这个类别。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class_correct = list(0. for i in range(10))
    class_total = list(0. for i in range(10))
    with torch.no_grad():
    for data in testloader:
    images, labels = data
    outputs = net(images)
    _, predicted = torch.max(outputs, 1)
    c = (predicted == labels).squeeze()
    for i in range(4):
    label = labels[i]
    class_correct[label] += c[i].item()
    class_total[label] += 1

    for i in range(10):
    print('Accuracy of %5s : %2d%%' %
    (classes[i], 100 * class_correct[i] / class_total[i]))

    获取单个类别上的预测准确率