文章目录

  • pytorch
    • 张量
  • 更多torch的api
  • 桥接 NumPy
  • CUDA上的张量
  • Autograd:自动求导
  • 阻止跟踪
  • 梯度
  • autograd 和 Function 的文档
  • torch.nn
    • 1.定义网络
    • 2. 注意:
    • 3. 损失函数
    • 4. 反向传播
    • 5. 各种模块和损失函数
    • 6. 更新权重
    • 7. 数据
    • 8. CIFAR10数据集
  • 训练一个图片分类器
    • 1.加载并标准化CIFAR10
    • 2.定义一个卷积神经网络
    • 3.定义损失函数和优化器
    • 4.训练网络
      • saving a model
    • 5.使用测试数据测试网络
  • 在GPU上训练
  • 在多GPU上训练
    • 1. 导入和参数
  • 2. 虚拟数据集
    • 3. 简单模型
    • 4. 创建一个模型和数据并行
    • 5. 运行模型
    • 6.总结
  • 编写自定义数据集,数据加载器和转换
    • 步骤
    • 1.Transforms 变换
    • 后记:torchvision
  • 使用 TensorBoard 可视化模型,数据和训练
    • 1. 样板代码开始
    • 1. TensorBoard 设置
    • 2.写入 TensorBoard
    • 3.使用 TensorBoard 检查模型
    • 4.在 TensorBoard 中添加一个“投影仪”
    • 5.使用 TensorBoard 跟踪模型训练
    • 6.使用 TensorBoard 评估经过训练的模型

pytorch

  • 作为NumPy的替代品,可以利用GPU的性能进行计算
  • 作为一个高灵活性、速度快的深度学习平台

张量

Tensor(张量)类似于NumPy的ndarray,但还可以在GPU上使用来加速计算。

from __future__ import print_function
import torchx = torch.empty(5, 3)
print(x)x = torch.rand(5, 3)
print(x)x = torch.zeros(5, 3, dtype=torch.long)
print(x)x = torch.tensor([5.5, 3])# 直接从数据构造张量
print(x)x = x.new_ones(5, 3, dtype=torch.double)      # new_* methods take in sizes
print(x)x = torch.randn_like(x, dtype=torch.float)    # 重载 dtype!
print(x)                                      # 结果size一致print(x.size())

加法:

形式一
y = torch.rand(5, 3)
print(x + y)形式二:
print(torch.add(x, y))形式三:给定一个输出张量作为参数
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)形式四:原位/原地操作(in-place)
#adds x to y
y.add_(x)
print(y)
#任何一个in-place改变张量的操作后面都固定一个_。例如x.copy_(y)、x.t_()将更改x

也可以使用像标准的NumPy一样的各种索引操作:

print(x[:, 1])#改变形状:如果想改变形状,可以使用torch.view
x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)  # the size -1 is inferred from other dimensions
print(x.size(), y.size(), z.size())

输出:

torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])

如果是仅包含一个元素的tensor,可以使用.item()来得到对应的python数值

x = torch.randn(1)
print(x)
print(x.item())

更多torch的api

https://pytorch.org/docs/stable/torch.html

桥接 NumPy

将一个Torch张量转换为一个NumPy数组是轻而易举的事情,反之亦然。

Torch张量和NumPy数组将共享它们的底层内存位置,因此当一个改变时,另外也会改变。

a = torch.ones(5)
print(a)
b = a.numpy()
print(b)

看NumPy数组是如何改变里面的值的:

a.add_(1)
print(a)
print(b)
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)

CPU上的所有张量(CharTensor除外)都支持与Numpy的相互转换。

CUDA上的张量

张量可以使用.to方法移动到任何设备(device)上:

# 当GPU可用时,我们可以运行以下代码
# 我们将使用`torch.device`来将tensor移入和移出GPU
if torch.cuda.is_available():device = torch.device("cuda")          # a CUDA device objecty = torch.ones_like(x, device=device)  # 直接在GPU上创建tensorx = x.to(device)                       # 或者使用`.to("cuda")`方法z = x + yprint(z)print(z.to("cpu", torch.double))       # `.to`也能在移动时改变dtype

Autograd:自动求导

PyTorch中,所有神经网络的核心是 autograd 包。先简单介绍一下这个包

autograd 包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义(define-by-run)的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的.

张量
torch.Tensor 是这个包的核心类。如果设置它的属性 .requires_grad 为 True,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 .backward(),来自动计算所有的梯度。这个张量的所有梯度将会自动累加到.grad属性.

import torch
#创建一个张量并设置requires_grad=True用来追踪其计算历史
x = torch.ones(2, 2, requires_grad=True)
print(x)

输出

tensor([[1., 1.],[1., 1.]], requires_grad=True)

对这个张量做一次运算:

y = x + 2
print(y)
tensor([[3., 3.],[3., 3.]], grad_fn=<AddBackward0>)

y是计算的结果,所以它有grad_fn属性。

print(y.grad_fn)
# 输出
<AddBackward0 object at 0x7f1b248453c8>

.requires_grad_(…) 原地改变了现有张量的 requires_grad 标志。如果没有指定的话,默认输入的这个标志是 False。

a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x7f1b24845f98>

阻止跟踪

要阻止一个张量被跟踪历史,可以调用 .detach() 方法将其与计算历史分离,并阻止它未来的计算记录被跟踪。

为了防止跟踪历史记录(和使用内存),可以将代码块包装在 with torch.no_grad(): 中。在评估模型时特别有用,因为模型可能具有 requires_grad = True 的可训练的参数,但是我们不需要在此过程中对他们进行梯度计算

还有一个类对于autograd的实现非常重要:Function。

Tensor 和 Function 互相连接生成了一个无圈图(acyclic graph),它编码了完整的计算历史。每个张量都有一个 .grad_fn 属性,该属性引用了创建 Tensor 自身的Function(除非这个张量是用户手动创建的,即这个张量的 grad_fn 是 None )。

如果需要计算导数,可以在 Tensor 上调用 .backward()。如果 Tensor 是一个标量(即它包含一个元素的数据),则不需要为 backward() 指定任何参数,但是如果它有更多的元素,则需要指定一个 gradient 参数,该参数是形状匹配的张量。

梯度

现在开始进行反向传播,因为 out 是一个标量,因此 out.backward() 和 out.backward(torch.tensor(1.)) 等价。

import torch
#创建一个张量并设置requires_grad=True用来追踪其计算历史
x = torch.ones(2, 2, requires_grad=True)
print(x)y = x + 2
print(y)print(y.grad_fn)z = y * y * 3
out = z.mean()print(z,out)out.backward()print(x.grad)
tensor([[4.5000, 4.5000],[4.5000, 4.5000]])

我们的得到的是一个数取值全部为4.5的矩阵。

我们来验证这个的准确性

o=14∑izi,zi=3(xi+2)2和zi∣xi=1=27因此,∂o∂xi=32(xi+2)​,因而∂o∂xi∣xi=1=92=4.5​o = \frac{1}{4}\sum_i z_i ,z_i = 3(x_i+2)^2和z_i\bigr\rvert_{x_i=1} = 27 因此, \frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2)​,因而 \frac{\partial o}{\partial x_i}\bigr\rvert_{x_i=1} = \frac{9}{2} = 4.5​o=41izizi=3(xi+2)2zixi=1=27,xio=23(xi+2)xioxi=1=29=4.5

数学上,若有向量值函数 y⃗=f(x⃗),那么y⃗相对于x⃗\vec{y}=f(\vec{x}) ,那么 \vec{y}相对于 \vec{x}y

=f(x

)y

x

​​ 的梯度是一个雅可比矩阵:

通常来说,torch.autograd 是计算雅可比向量积的一个“引擎”。

给定任意向量 v=(v1v2⋯vm)Tv=\left(\begin{array}{cccc} v_{1} & v_{2} & \cdots & v_{m}\end{array}\right)^{T}v=(v1v2vm)T

​​计算乘积 vT⋅Jv^{T}\cdot JvTJ
(注意:行向量的vT⋅Jv^{T}\cdot JvTJ也可以被视作列向量的JT⋅vJ^{T}\cdot vJTv

如果 vvv恰好是一个标量函数 l=g(y⃗)l=g\left(\vec{y}\right)l=g(y

)的导数,即 v=(∂l∂y1⋯∂l∂ym)Tv=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T}v=(y1lyml)T

那么根据链式法则,雅可比向量积应该是lllx⃗\vec{x}x

的导数:

雅可比向量积的这一特性使得将外部梯度输入到具有非标量输出的模型中变得非常方便。

x = torch.randn(3, requires_grad=True)y = x * 2
while y.data.norm() < 1000:y = y * 2print(y)

在这种情况下,y 不再是标量。torch.autograd 不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 backward:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)print(x.grad)
tensor([4.0960e+02, 4.0960e+03, 4.0960e-01])

也可以通过将代码块包装在 with torch.no_grad(): 中,来阻止autograd跟踪设置了 .requires_grad=True 的张量的历史记录。

print(x.requires_grad)
print((x ** 2).requires_grad)with torch.no_grad():print((x ** 2).requires_grad)

autograd 和 Function 的文档

https://pytorch.org/docs/autograd

torch.nn

可以使用torch.nn包来构建神经网络.

我们已经介绍了autograd包,nn包则依赖于autograd包来定义模型并对它们求导。一个nn.Module包含各个层和一个forward(input)方法,该方法返回output。

例如,下面这个神经网络可以对数字进行分类:

这是一个简单的前馈神经网络(feed-forward network)。它接受一个输入,然后将它送入下一层,一层接一层的传递,最后给出输出。

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

  • 定义包含一些可学习参数(或者叫权重)的神经网络
  • 在输入数据集上迭代
  • 通过网络处理输入
  • 计算loss(输出和正确答案的距离)
  • 将梯度反向传播给网络的参数
  • 更新网络的权重,一般使用一个简单的规则:weight = weight - learning_rate * gradient

1.定义网络

import torch
import torch.nn as nn
import torch.nn.functional as Fclass Net(nn.Module):def __init__(self):super(Net, self).__init__()# 输入图像channel:1;输出channel:6;5x5卷积核self.conv1 = nn.Conv2d(1, 6, 5)self.conv2 = nn.Conv2d(6, 16, 5)# an affine operation: y = Wx + bself.fc1 = nn.Linear(16 * 5 * 5, 120)self.fc2 = nn.Linear(120, 84)self.fc3 = nn.Linear(84, 10)def forward(self, x):# 2x2 Max poolingx = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))# 如果是方阵,则可以只使用一个数字进行定义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 xdef num_flat_features(self, x):size = x.size()[1:]  # 除去批处理维度的其他所有维度num_features = 1for s in size:num_features *= sreturn num_featuresnet = Net()
print(net)

我们只需要定义 forward 函数,backward函数会在使用autograd时自动定义,backward函数用来计算导数。我们可以在 forward 函数中使用任何针对张量的操作和计算。

一个模型的可学习参数可以通过net.parameters()返回

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

让我们尝试一个随机的32x32的输入。注意:这个网络(LeNet)的期待输入是32x32的张量。如果使用MNIST数据集来训练这个网络,要把图片大小重新调整到32x32。

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

清零所有参数的梯度缓存,然后进行随机梯度的反向传播:

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

2. 注意:

torch.nn只支持小批量处理(mini-batches)。整个torch.nn包只支持小批量样本的输入,不支持单个样本的输入。

比如,nn.Conv2d 接受一个4维的张量,即nSamples x nChannels x Height x Width

如果是一个单独的样本,只需要使用input.unsqueeze(0)来添加一个“假的”批大小维度。

torch.Tensor - 一个多维数组,支持诸如backward()等的自动求导操作,同时也保存了张量的梯度。
nn.Module - 神经网络模块。是一种方便封装参数的方式,具有将参数移动到GPU、导出、加载等功能。
nn.Parameter - 张量的一种,当它作为一个属性分配给一个Module时,它会被自动注册为一个参数。
autograd.Function - 实现了自动求导前向和反向传播的定义,每个Tensor至少创建一个Function节点,该节点连接到创建Tensor的函数并对其历史进行编码。

3. 损失函数

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

nn包中有很多不同的损失函数。nn.MSELoss是比较简单的一种,它计算输出和目标的均方误差(mean-squared error)。

output = net(input)
target = torch.randn(10)  # 本例子中使用模拟数据
target = target.view(1, -1)  # 使目标值与数据值尺寸一致
criterion = nn.MSELoss()loss = criterion(output, target)
print(loss)

现在,如果使用loss的.grad_fn属性跟踪反向传播过程,会看到计算图如下:

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

4. 反向传播

我们只需要调用loss.backward()来反向传播误差。我们需要清零现有的梯度,否则梯度将会与已有的梯度累加。

现在,我们将调用loss.backward(),并查看conv1层的偏置(bias)在反向传播前后的梯度。

net.zero_grad()     # 清零所有参数(parameter)的梯度缓存print('conv1.bias.grad before backward')
print(net.conv1.bias.grad)loss.backward()print('conv1.bias.grad after backward')
print(net.conv1.bias.grad)

5. 各种模块和损失函数

神经网络包包含了各种模块和损失函数,这些模块和损失函数构成了深度神经网络的构建模块。完整的文档列表见这里。https://pytorch.org/docs/stable/nn.html

6. 更新权重

最简单的更新规则是随机梯度下降法(SGD):

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、RMSProp等。为此,我们构建了一个较小的包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()    # 更新参数

观察梯度缓存区是如何使用optimizer.zero_grad()手动清零的。这是因为梯度是累加的,正如前面反向传播章节叙述的那样。

7. 数据

通常来说,当必须处理图像、文本、音频或视频数据时,可以使用python标准库将数据加载到numpy数组里。然后将这个数组转化成torch.*Tensor。

  • 对于图片,有Pillow,OpenCV等包可以使用
  • 对于音频,有scipy和librosa等包可以使用
  • 对于文本,不管是原生python的或者是基于Cython的文本,可以使用NLTK和SpaCy

特别对于视觉方面,我们创建了一个包,名字叫torchvision,其中包含了针对Imagenet、CIFAR10、MNIST等常用数据集的数据加载器(data loaders),还有对图像数据转换的操作,即torchvision.datasets和torch.utils.data.DataLoader。

8. CIFAR10数据集

它有如下的分类:“飞机”,“汽车”,“鸟”,“猫”,“鹿”,“狗”,“青蛙”,“马”,“船”,“卡车”等。在CIFAR-10里面的图片数据大小是3x32x32,即:三通道彩色图像,图像大小是32x32像素。

训练一个图片分类器

我们将按顺序做以下步骤:

  • 通过torchvision加载CIFAR10里面的训练和测试数据集,并对数据进行标准化
  • 定义卷积神经网络
  • 定义损失函数
  • 利用训练数据训练网络
  • 利用测试数据测试网络

1.加载并标准化CIFAR10

使用torchvision加载CIFAR10超级简单。

import torch
import torchvision
import torchvision.transforms as transforms

torchvision数据集加载完后的输出是范围在[0, 1]之间的PILImage。我们将其标准化为范围在[-1, 1]之间的张量。

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')

现在让我们可视化部分训练数据

import matplotlib.pyplot as plt
import numpy as np# 输出图像的函数
def imshow(img):img = img / 2 + 0.5     # unnormalizenpimg = img.numpy()plt.imshow(np.transpose(npimg, (1, 2, 0)))plt.show()# 随机获取训练图片
dataiter = iter(trainloader)
images, labels = dataiter.next()# 显示图片
imshow(torchvision.utils.make_grid(images))
# 打印图片标签
print(' '.join('%5s' % classes[labels[j]] for j in range(4)))

2.定义一个卷积神经网络

将之前神经网络章节定义的神经网络拿过来,并将其修改成输入为3通道图像(替代原来定义的单通道图像)。

import torch.nn as nn
import torch.nn.functional as Fclass 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 xnet = Net()

3.定义损失函数和优化器

import torch.optim as optimcriterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

4.训练网络

遍历我们的数据迭代器,并将输入“喂”给网络和优化函数。

for epoch in range(2):  # loop over the dataset multiple timesrunning_loss = 0.0for i, data in enumerate(trainloader, 0):# get the inputsinputs, labels = data# zero the parameter gradientsoptimizer.zero_grad()# forward + backward + optimizeoutputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()# print statisticsrunning_loss += loss.item()if i % 2000 == 1999:    # print every 2000 mini-batchesprint('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))running_loss = 0.0print('Finished Training')

saving a model

https://pytorch.org/docs/stable/notes/serialization.html

Recommended approach for saving a model
让我们赶紧保存已训练得到的模型:

There are two main approaches for serializing and restoring a model.

1、The first (recommended) saves and loads only the model parameters:


PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)
#torch.save(the_model.state_dict(), PATH)

Then later:

the_model = TheModelClass(*args, **kwargs)
the_model.load_state_dict(torch.load(PATH))

2、 The second saves and loads the entire model:

torch.save(the_model, PATH)

Then later:

the_model = torch.load(PATH)

However in this case, the serialized data is bound to the specific classes and the exact directory structure used, so it can break in various ways when used in other projects, or after some serious refactors.序列化的数据将绑定到所使用的特定类和确切的目录结构,因此在其他项目中使用时或经过一些严重的重构后,它可能以各种方式中断。

5.使用测试数据测试网络

我们已经在训练集上训练了2遍网络。但是我们需要检查网络是否学到了一些东西。

我们将通过预测神经网络输出的标签来检查这个问题,并和正确样本进行(ground-truth)对比。如果预测是正确的,我们将样本添加到正确预测的列表中。

ok,第一步。让我们展示测试集中的图像来熟悉一下。

dataiter = iter(testloader)
images, labels = dataiter.next()# 输出图片
imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

下一步,让我们加载保存的模型(注意:在这里保存和加载模型不是必要的,我们只是为了解释如何去做这件事)

net = Net()
net.load_state_dict(torch.load(PATH))

ok,现在让我们看看神经网络认为上面的例子是:

outputs = net(images)

输出是10个类别的量值。一个类的值越高,网络就越认为这个图像属于这个特定的类。让我们得到最高量值的下标/索引;

_, predicted = torch.max(outputs, 1)print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))

让我们看看网络在整个数据集上表现的怎么样。


correct = 0
total = 0
with torch.no_grad():for data in testloader:images, labels = dataoutputs = net(images)_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item()print('Accuracy of the network on the 10000 test images: %d %%' % (100 * correct / total))

那么哪些是表现好的类呢?哪些是表现的差的类呢?

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 = dataoutputs = 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] += 1for i in range(10):print('Accuracy of %5s : %2d %%' % (classes[i], 100 * class_correct[i] / class_total[i]))

在GPU上训练

与将一个张量传递给GPU一样,可以这样将神经网络转移到GPU上。

如果我们有cuda可用的话,让我们首先定义第一个设备为可见cuda设备:

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")# Assuming that we are on a CUDA machine, this should print a CUDA device:print(device)

本节的其余部分假设device是CUDA。

然后这些方法将递归遍历所有模块,并将它们的参数和缓冲区转换为CUDA张量:

net.to(device)

请记住,我们必须也将输入和目标在每一步都送入GPU
所有的张量复制到GPU上:

mytensor = my_tensor.to(device)

请注意,调用my_tensor.to(device)返回一个GPU上的my_tensor副本,而不是重写my_tensor。你需要把它赋值给一个新的张量并在GPU上使用这个张量。

inputs, labels = inputs.to(device), labels.to(device)

为什么我们感受不到与CPU相比的巨大加速?因为我们的网络实在是太小了。

尝试一下:加宽你的网络(注意第一个nn.Conv2d的第二个参数和第二个nn.Conv2d的第一个参数要相同),看看能获得多少加速。

已实现的目标:

  • 在更高层次上理解PyTorch的Tensor库和神经网络
  • 训练一个小的神经网络做图片分类

在多GPU上训练

https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html

在多GPU上执行正向和反向传播是自然而然的事。然而,PyTorch默认将只是用一个GPU。你可以使用DataParallel让模型并行运行来轻易的在多个GPU上运行你的操作。

model = nn.DataParallel(model)

1. 导入和参数

导入PyTorch模块和定义参数。

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader# Parameters 和 DataLoaders
input_size = 5
output_size = 2batch_size = 30
data_size = 100

设备(Device):

device = torch.device("cuda: 0" if torch.cuda.is_available() else "cpu")

2. 虚拟数据集

要制作一个虚拟(随机)数据集,你只需实现__getitem__。

class RandomDataset(Dataset):def __init__(self, size, length):self.len = lengthself.data = torch.randn(length, size)def __getitem__(self, index):return self.data[index]def __len__(self):return self.lenrand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),batch_size=batch_size, shuffle=True)

3. 简单模型

作为演示,我们的模型只接受一个输入,执行一个线性操作,然后得到结果。然而,你能在任何模型(CNN,RNN,Capsule Net等)上使用DataParallel。

我们在模型内部放置了一条打印语句来检测输入和输出向量的大小。请注意批等级为0时打印的内容。

class Model(nn.Module):# Our modeldef __init__(self, input_size, output_size):super(Model, self).__init__()self.fc = nn.Linear(input_size, output_size)def forward(self, input):output = self.fc(input)print("\tIn Model: input size", input.size(),"output size", output.size())return output

4. 创建一个模型和数据并行

这是本教程的核心部分。首先,我们需要创建一个模型实例和检测我们是否有多个GPU。如果我们有多个GPU,我们使用nn.DataParallel来包装我们的模型。然后通过model.to(device)把模型放到GPU上。

model = Model(input_size, output_size)
if torch.cuda.device_count() > 1: print("Let's use", torch.cuda.device_count(), "GPUs!")# dim = 0 [30, xxx] -> [10, ...], [10, ...], [10, ...] on 3 GPUsmodel = nn.DataParallel(model)model.to(device)

5. 运行模型

现在我们可以看输入和输出张量的大小。

for data in rand_loader: input = data.to(device)output = model(input)print("Outside: input size", input.size(),"output_size", output.size())

6.总结

DataParallel自动的划分数据,并将作业顺序发送到多个GPU上的多个模型。DataParallel会在每个模型完成作业后,收集与合并结果然后返回给你。

更多信息,请参考: https://pytorch.org/tutorials/beginner/former_torchies/parallelism_tutorial.html

编写自定义数据集,数据加载器和转换

在本教程中,我们将了解如何从非空的数据集中加载和预处理/增强数据。

要运行本教程,请确保已安装以下软件包:

scikit-image:用于图像 io 和变换
pandas:用于更轻松的 csv 解析

步骤

1、如何编写
2、查看数据集,
3、转换
4、数据加载器。

1.Transforms 变换

从上面可以看到的一个问题是样本的大小不同。 大多数神经网络期望图像的大小固定。 因此,我们将需要编写一些预处理代码。 让我们创建三个转换:

Rescale:缩放图像
RandomCrop:从图像中随机裁剪。 这是数据增强。
ToTensor:将 numpy 图像转换为 torch 图像(我们需要交换轴)。

后记:torchvision

torchvision包提供了一些常见的数据集和转换。 您甚至不必编写自定义类。 Torchvision 中可用的更通用的数据集之一是ImageFolder。

您可以使用以下代码编写数据加载器,如下所示:

import torch
from torchvision import transforms, datasetsdata_transform = transforms.Compose([transforms.RandomSizedCrop(224),transforms.RandomHorizontalFlip(),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])
hymenoptera_dataset = datasets.ImageFolder(root='hymenoptera_data/train',transform=data_transform)
dataset_loader = torch.utils.data.DataLoader(hymenoptera_dataset,batch_size=4, shuffle=True,num_workers=4)

使用 TensorBoard 可视化模型,数据和训练

PyTorch 与 TensorBoard 集成,该工具旨在可视化神经网络训练运行的结果。 本教程使用 Fashion-MNIST 数据集说明了其某些功能,可以使用 torchvision.datasets 将其读取到 PyTorch 中。

在本教程中,我们将学习如何:

  • 读取数据并进行适当的转换(与先前的教程几乎相同)。
  • 设置 TensorBoard。
  • 写入 TensorBoard。
  • 使用 TensorBoard 检查模型架构。
  • 使用 TensorBoard 来创建我们在上一个教程中创建的可视化的替代版本,代码量更少。

具体来说,在第 5 点,我们将看到:

  • 有两种检查训练数据的方法
  • 在训练模型时如何追踪其性能
  • 在训练完成后如何评估模型的性能。

1. 样板代码开始

# imports
import matplotlib.pyplot as plt
import numpy as npimport torch
import torchvision
import torchvision.transforms as transformsimport torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim# transforms
transform = transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,), (0.5,))])# datasets
trainset = torchvision.datasets.FashionMNIST('./data',download=True,train=True,transform=transform)
testset = torchvision.datasets.FashionMNIST('./data',download=True,train=False,transform=transform)# dataloaders
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4,shuffle=True, num_workers=2)testloader = torch.utils.data.DataLoader(testset, batch_size=4,shuffle=False, num_workers=2)# constant for classes
classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat','Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle Boot')# helper function to show an image
# (used in the `plot_classes_preds` function below)
def matplotlib_imshow(img, one_channel=False):if one_channel:img = img.mean(dim=0)img = img / 2 + 0.5     # unnormalizenpimg = img.numpy()if one_channel:plt.imshow(npimg, cmap="Greys")else:plt.imshow(np.transpose(npimg, (1, 2, 0)))

我们将在该教程中定义一个类似的模型架构,仅需进行少量修改即可说明以下事实:图像现在是一个通道而不是三个通道,是 28x28 而不是 32x32:

class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.conv1 = nn.Conv2d(1, 6, 5)self.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(6, 16, 5)self.fc1 = nn.Linear(16 * 4 * 4, 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 * 4 * 4)x = F.relu(self.fc1(x))x = F.relu(self.fc2(x))x = self.fc3(x)return xnet = Net()

我们将在之前定义相同的optimizer和criterion:

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

1. TensorBoard 设置

现在,我们将设置 TensorBoard,从torch.utils导入tensorboard并定义SummaryWriter,这是将信息写入 TensorBoard 的关键对象。

from torch.utils.tensorboard import SummaryWriter# default `log_dir` is "runs" - we'll be more specific here
writer = SummaryWriter('runs/fashion_mnist_experiment_1')

请注意,仅此行会创建一个runs/fashion_mnist_experiment_1文件夹。

2.写入 TensorBoard

现在,使用 make_grid 将图像写入到 TensorBoard 中,具体来说就是网格。

# get some random training images
dataiter = iter(trainloader)
images, labels = dataiter.next()# create grid of images
img_grid = torchvision.utils.make_grid(images)# show images
matplotlib_imshow(img_grid, one_channel=True)# write to tensorboard
writer.add_image('four_fashion_mnist_images', img_grid)

现在运行

tensorboard --logdir=runs

从命令行,然后导航到 https:// localhost:6006 应该显示以下内容。

现在您知道如何使用 TensorBoard 了! 但是,此示例可以在 Jupyter Notebook 中完成-TensorBoard 真正擅长的地方是创建交互式可视化。 接下来,我们将介绍其中之一

3.使用 TensorBoard 检查模型

TensorBoard 的优势之一是其可视化复杂模型结构的能力。 让我们可视化我们构建的模型

writer.add_graph(net, images)
writer.close()

现在刷新 TensorBoard 后,您应该会看到一个“ Graphs”标签,如下所示:


继续并双击“ Net”以展开它,查看组成模型的各个操作的详细视图。

TensorBoard 具有非常方便的功能,可在低维空间中可视化高维数据,例如图像数据; 接下来我们将介绍。

4.在 TensorBoard 中添加一个“投影仪”

我们可以通过 add_embedding 方法可视化高维数据的低维表示

# helper function
def select_n_random(data, labels, n=100):'''Selects n random datapoints and their corresponding labels from a dataset'''assert len(data) == len(labels)perm = torch.randperm(len(data))return data[perm][:n], labels[perm][:n]# select random images and their target indices
images, labels = select_n_random(trainset.data, trainset.targets)# get the class labels for each image
class_labels = [classes[lab] for lab in labels]# log embeddings
features = images.view(-1, 28 * 28)
writer.add_embedding(features,metadata=class_labels,label_img=images.unsqueeze(1))
writer.close()

现在,在 TensorBoard 的“投影仪”选项卡中,您可以看到这 100 张图像-每个图像 784 维-向下投影到三维空间中。 此外,这是交互式的:您可以单击并拖动以旋转三维投影。 最后,有两个技巧可以使可视化效果更容易看到:在左上方选择“颜色:标签”,并启用“夜间模式”,这将使图像更容易看到,因为它们的背景是白色的:

现在我们已经彻底检查了我们的数据,接下来让我们展示TensorBoard 如何从训练开始就可以使跟踪模型的训练和评估更加清晰。

5.使用 TensorBoard 跟踪模型训练

在前面的示例中,我们仅每 2000 次迭代打印该模型的运行损失。 现在,我们将运行损失记录到 TensorBoard 中,并通过plot_classes_preds函数查看模型所做的预测。

# helper functionsdef images_to_probs(net, images):'''Generates predictions and corresponding probabilities from a trainednetwork and a list of images'''output = net(images)# convert output probabilities to predicted class_, preds_tensor = torch.max(output, 1)preds = np.squeeze(preds_tensor.numpy())return preds, [F.softmax(el, dim=0)[i].item() for i, el in zip(preds, output)]def plot_classes_preds(net, images, labels):'''Generates matplotlib Figure using a trained network, along with imagesand labels from a batch, that shows the network's top prediction alongwith its probability, alongside the actual label, coloring thisinformation based on whether the prediction was correct or not.Uses the "images_to_probs" function.'''preds, probs = images_to_probs(net, images)# plot the images in the batch, along with predicted and true labelsfig = plt.figure(figsize=(12, 48))for idx in np.arange(4):ax = fig.add_subplot(1, 4, idx+1, xticks=[], yticks=[])matplotlib_imshow(images[idx], one_channel=True)ax.set_title("{0}, {1:.1f}%\n(label: {2})".format(classes[preds[idx]],probs[idx] * 100.0,classes[labels[idx]]),color=("green" if preds[idx]==labels[idx].item() else "red"))return fig

最后,让我们使用与之前教程相同的模型训练代码来训练模型,但是每 1000 批将结果写入 TensorBoard,而不是打印到控制台。 这是通过 add_scalar 函数完成的。

此外,在训练过程中,我们将生成一幅图像,显示该批次中包含的四幅图像的模型预测与实际结果。

running_loss = 0.0
for epoch in range(1):  # loop over the dataset multiple timesfor i, data in enumerate(trainloader, 0):# get the inputs; data is a list of [inputs, labels]inputs, labels = data# zero the parameter gradientsoptimizer.zero_grad()# forward + backward + optimizeoutputs = net(inputs)loss = criterion(outputs, labels)loss.backward()optimizer.step()running_loss += loss.item()if i % 1000 == 999:    # every 1000 mini-batches...# ...log the running losswriter.add_scalar('training loss',running_loss / 1000,epoch * len(trainloader) + i)# ...log a Matplotlib Figure showing the model's predictions on a# random mini-batchwriter.add_figure('predictions vs. actuals',plot_classes_preds(net, inputs, labels),global_step=epoch * len(trainloader) + i)running_loss = 0.0
print('Finished Training')

现在,您可以查看“标量”选项卡,以查看在 15,000 次训练迭代中绘制的运行损失:

6.使用 TensorBoard 评估经过训练的模型

# 1\. gets the probability predictions in a test_size x num_classes Tensor
# 2\. gets the preds in a test_size Tensor
# takes ~10 seconds to run
class_probs = []
class_preds = []
with torch.no_grad():for data in testloader:images, labels = dataoutput = net(images)class_probs_batch = [F.softmax(el, dim=0) for el in output]_, class_preds_batch = torch.max(output, 1)class_probs.append(class_probs_batch)class_preds.append(class_preds_batch)test_probs = torch.cat([torch.stack(batch) for batch in class_probs])
test_preds = torch.cat(class_preds)# helper function
def add_pr_curve_tensorboard(class_index, test_probs, test_preds, global_step=0):'''Takes in a "class_index" from 0 to 9 and plots the correspondingprecision-recall curve'''tensorboard_preds = test_preds == class_indextensorboard_probs = test_probs[:, class_index]writer.add_pr_curve(classes[class_index],tensorboard_preds,tensorboard_probs,global_step=global_step)writer.close()# plot all the pr curves
for i in range(len(classes)):add_pr_curve_tensorboard(i, test_probs, test_preds)

现在,您将看到一个“ PR Curves”选项卡,其中包含每个类别的精度回召曲线。 继续戳一下; 您会发现在某些类别中,模型的“曲线下面积”接近 100%,而在另一些类别中,该面积更低:

这是 TensorBoard 和 PyTorch 与之集成的介绍。 当然,您可以在 Jupyter Notebook 中完成 TensorBoard 所做的所有操作,但是使用 TensorBoard,默认情况下,您还可以获得交互式视觉效果。

09月23日 pytorch与resnet(一)相关推荐

  1. 2015年09月23日

    织梦怎么修改交叉副栏目的数量?默认是可以选择9个副栏目. 2013-12-02 11:52 lastdanceo  | 浏览 222 次   悬赏: 30 织梦怎么修改交叉副栏目的数量?默认是可以选择 ...

  2. 2017年09月23日普级组 树塔狂想曲

    Description 相信大家都在长训班学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和.走的规则是:(i,j)号点只能走向(i+1,j)或者(i+1,j+1).如下图是一个数塔,映 ...

  3. 2017年09月23日普级组 数列

    Description 小S今天给你出了一道找规律题,题目如下: 有如下的数列1,11,21,1211,111221,312211,-- 小S问你这个数列的第N项是多少,而你一头雾水根本找不出规律. ...

  4. 2017年09月23日普级组 环

    Description 有一个圆,共个数在圆环上顺时针排列着. 现在给你三个数,问的顺序是顺时针还是逆时针. Input 第一行包括四个正整数. Output 一行一个整数,0或1,0表示逆时针,1表 ...

  5. 最新Win7 +Python3.6.0(Anaconda3-4.3.21)+Tensorflow的安装与配置(不用切换python3.5) 原创 2017年09月23日 15:14:58 标签:pyt

    最新Win7 +Python3.6.0(Anaconda3-4.3.21)+Tensorflow的安装与配置(不用切换python3.5) 一.首先进入Anaconda官网下载  https://ww ...

  6. AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛——09:55-10:40刘兵教授《Open-World AI and Continual Learning》

    AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛--09:55-10:40刘兵教授<Open-World AI and Continual Learning> 导 ...

  7. AI:2020年6月23日北京智源大会演讲分享之机器学习专题论坛——09:45-10:25邢波教授《A Blueprint of Standardized and Composable ML》

    AI:2020年6月23日北京智源大会演讲分享之机器学习专题论坛--09:45-10:25邢波教授<A Blueprint of Standardized and Composable ML&g ...

  8. AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛——09:55-10:40刘欢教授《Challenges in Combating Disinformation》

    AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛--09:55-10:40刘欢教授<Challenges in Combating Disinformation> ...

  9. AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛——09:10-09:55裴健教授《智能搜索:从工具到思维方式和心智》

    AI:2020年6月23日北京智源大会演讲分享之智能信息检索与挖掘专题论坛--09:10-09:55裴健教授<智能搜索:从工具到思维方式和心智> 导读:首先感谢北京智源大会进行主题演讲的各 ...

  10. AI:2020年6月23日北京智源大会演讲分享之AI创业专题论坛——09:10-10:00 李开复教授《AI赋能时代的创业》

    AI:2020年6月23日北京智源大会演讲分享之AI创业专题论坛--09:10-10:00 李开复教授<AI赋能时代的创业> 导读:首先感谢北京智源大会进行主题演讲的各领域顶级教授,博主受 ...

最新文章

  1. 基于跨国人口流动数据的新冠病毒传播途径分析
  2. c 多语言切换dll,【图片】老C教学之——给你的程序添加多语言支持【dll】【vb吧】_百度贴吧...
  3. php要字符串的后四位,php如何截取字符串后四位
  4. 数据结构:静态查找动态查找
  5. JMeter入门(3):录制JMeter脚本
  6. 查看修改swap空间大小
  7. Android中1像素Activity进程保活
  8. 诗与远方:无题(八十五)- 无字天书
  9. Linux故障之grub
  10. 《数据结构教程》(李春葆 主编)课后习题【练习题6】
  11. GraphQL | 一种配得上凡尔赛的API框架
  12. 文档加密图纸加密方案之沙盒
  13. 传智播客8月C/C++基础班开班
  14. 网页鼠标点击特效代码
  15. 删除wmspdmv监控进程-- 背后的眼睛
  16. PWM(脉冲宽度调制)信号原理
  17. 二、SpringBoot配置-简述配置加载原理
  18. Python批量复制一个文件夹中的全部excel数据并粘贴至一个excel文件中-openpyxl模块
  19. Leaving...Hard resetting via RTS pin...
  20. [redis]Connection failure occurred. Restarting subscription task after 5000 ms

热门文章

  1. 字节跳动李本超:一年成为 Committer,我与 Flink 社区的故事
  2. Kotlin实战【二】Kotlin基本要素
  3. 关于安装TOMCAT解压版环境配置流程
  4. 无线路由服务器蹭网,让别人知道wifi密码也无法蹭网的办法
  5. java的基本语法(一)
  6. 数据库sqlite3怎么排顺序_【数据库02】MySQL数据库面试题
  7. linux的系统监视器图片_Linux中一个高效的资源监控器Bpytop
  8. php 爬取一个人的网易云评论,爬取网易云音乐某一个人的评论
  9. php微信关键词回复,php微信开发之关键词回复功能
  10. 带有参数的输出存储过程