未验证 提交 b62bd47f 编写于 作者: 片刻小哥哥's avatar 片刻小哥哥 提交者: GitHub

Merge pull request #499 from jiangzhonglian/master

更新 遗漏的内容
......@@ -125,4 +125,5 @@ docs/0.3/book.json
docs/0.4/book.json
docs/1.0/book.json
docs/1.2/book.json
docs/1.4/book.json
docs/LatestChanges/book.json
......@@ -19,20 +19,20 @@ _本教程假定您对 numpy 有基本的了解。_
![../_images/tensor_illustration_flat.png](img/0c7a402331744a44f5e17575b1607904.jpg)
[什么是 PyTorch?](blitz/tensor_tutorial.html#sphx-glr-beginner-blitz-tensor-tutorial-py)
[什么是 PyTorch?](blitz/tensor_tutorial.html)
![../_images/autodiff.png](img/0a7a97c39d6dfc0e08d2701eb7a49231.jpg)
[Autograd:自动分化](blitz/autograd_tutorial.html#sphx-glr-beginner-blitz-autograd-tutorial-py)
[Autograd:自动分化](blitz/autograd_tutorial.html)
![../_images/mnist1.png](img/be60e8e1f4baa0de87cf9d37c5325525.jpg)
[神经网络](blitz/neural_networks_tutorial.html#sphx-glr-beginner-blitz-neural-networks-tutorial-py)
[神经网络](blitz/neural_networks_tutorial.html)
![../_images/cifar101.png](img/7a28f697e6bab9f3d9b1e8da4a5a5249.jpg)
[训练分类器](blitz/cifar10_tutorial.html#sphx-glr-beginner-blitz-cifar10-tutorial-py)
[训练分类器](blitz/cifar10_tutorial.html)
![../_images/data_parallel.png](img/c699a36b37c0fd5aec258278788c1216.jpg)
[可选:数据并行](blitz/data_parallel_tutorial.html#sphx-glr-beginner-blitz-data-parallel-tutorial-py)
\ No newline at end of file
[可选:数据并行](blitz/data_parallel_tutorial.html)
......@@ -3,6 +3,11 @@
* [PyTorch 1.4 教程&文档](README.md)
* 入门
* [使用 PyTorch 进行深度学习:60 分钟的闪电战](4.md)
* [什么是PyTorch?](blitz/tensor_tutorial.md)
* [Autograd:自动求导](blitz/autograd_tutorial.md)
* [神经网络](blitz/neural_networks_tutorial.md)
* [训练分类器](blitz/cifar10_tutorial.md)
* [可选:数据并行](blitz/data_parallel_tutorial.md)
* [编写自定义数据集,数据加载器和转换](5.md)
* [使用 TensorBoard 可视化模型,数据和训练](6.md)
* 图片
......
# Autograd:自动求导
> 原文: [https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html](https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html)
> 译者:[bat67](https://github.com/bat67)
>
> 校验者:[FontTian](https://github.com/fonttian)
PyTorch中,所有神经网络的核心是 `autograd` 包。先简单介绍一下这个包,然后训练我们的第一个的神经网络。
`autograd` 包为张量上的所有操作提供了自动求导机制。它是一个在运行时定义(define-by-run)的框架,这意味着反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的.
让我们用一些简单的例子来看看吧。
## 张量
`torch.Tensor` 是这个包的核心类。如果设置它的属性 `.requires_grad``True`,那么它将会追踪对于该张量的所有操作。当完成计算后可以通过调用 `.backward()`,来自动计算所有的梯度。这个张量的所有梯度将会自动累加到`.grad`属性.
要阻止一个张量被跟踪历史,可以调用 `.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` 参数,该参数是形状匹配的张量。
```python
import torch
```
创建一个张量并设置`requires_grad=True`用来追踪其计算历史
```python
x = torch.ones(2, 2, requires_grad=True)
print(x)
```
输出:
```python
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
```
对这个张量做一次运算:
```python
y = x + 2
print(y)
```
输出:
```python
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
```
`y`是计算的结果,所以它有`grad_fn`属性。
```python
print(y.grad_fn)
```
输出:
```python
<AddBackward0 object at 0x7f1b248453c8>
```
对y进行更多操作
```python
z = y * y * 3
out = z.mean()
print(z, out)
```
输出:
```python
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
```
`.requires_grad_(...)` 原地改变了现有张量的 `requires_grad` 标志。如果没有指定的话,默认输入的这个标志是 `False`
```python
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)
```
输出:
```python
False
True
<SumBackward0 object at 0x7f1b24845f98>
```
## 梯度
现在开始进行反向传播,因为 `out` 是一个标量,因此 `out.backward()``out.backward(torch.tensor(1.))` 等价。
```python
out.backward()
```
输出导数 `d(out)/dx`
```python
print(x.grad)
```
输出:
```python
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
```
我们的得到的是一个数取值全部为`4.5`的矩阵。
让我们来调用 `out` 张量 $$“o”$$。
就可以得到 $$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$$。
数学上,若有向量值函数 $$\vec{y}=f(\vec{x})$$,那么 $$\vec{y}$$ 相对于 $$\vec{x}$$ 的梯度是一个雅可比矩阵:
$$
J=\left(\begin{array}{ccc}
\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\
\vdots & \ddots & \vdots\\
\frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right)
$$
通常来说,`torch.autograd` 是计算雅可比向量积的一个“引擎”。也就是说,给定任意向量 $$v=\left(\begin{array}{cccc} v_{1} & v_{2} & \cdots & v_{m}\end{array}\right)^{T}$$,计算乘积 $$v^{T}\cdot J$$。如果 $$v$$ 恰好是一个标量函数 $$l=g\left(\vec{y}\right)$$ 的导数,即 $$v=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T}$$,那么根据链式法则,雅可比向量积应该是 $$l$$ 对 $$\vec{x}$$ 的导数:
$$
J^{T}\cdot v=\left(\begin{array}{ccc}
\frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\
\vdots & \ddots & \vdots\\
\frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
\end{array}\right)\left(\begin{array}{c}
\frac{\partial l}{\partial y_{1}}\\
\vdots\\
\frac{\partial l}{\partial y_{m}}
\end{array}\right)=\left(\begin{array}{c}
\frac{\partial l}{\partial x_{1}}\\
\vdots\\
\frac{\partial l}{\partial x_{n}}
\end{array}\right)
$$
(注意:行向量的$$ v^{T}\cdot J$$也可以被视作列向量的$$J^{T}\cdot v$$)
雅可比向量积的这一特性使得将外部梯度输入到具有非标量输出的模型中变得非常方便。
现在我们来看一个雅可比向量积的例子:
```python
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
```
输出:
```python
tensor([-278.6740, 935.4016, 439.6572], grad_fn=<MulBackward0>)
```
在这种情况下,`y` 不再是标量。`torch.autograd` 不能直接计算完整的雅可比矩阵,但是如果我们只想要雅可比向量积,只需将这个向量作为参数传给 `backward`
```python
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)
```
输出:
```python
tensor([4.0960e+02, 4.0960e+03, 4.0960e-01])
```
也可以通过将代码块包装在 `with torch.no_grad():` 中,来阻止autograd跟踪设置了 `.requires_grad=True` 的张量的历史记录。
```python
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad():
print((x ** 2).requires_grad)
```
输出:
```python
True
True
False
```
> 后续阅读:
>
> `autograd` 和 `Function` 的文档见:https://pytorch.org/docs/autograd
# 训练分类器
> 原文: [https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html](https://pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html)
> 译者:[bat67](https://github.com/bat67)
>
> 校验者:[FontTian](https://github.com/fonttian)
目前为止,我们以及看到了如何定义网络,计算损失,并更新网络的权重。所以你现在可能会想,
## 数据应该怎么办呢?
通常来说,当必须处理图像、文本、音频或视频数据时,可以使用python标准库将数据加载到numpy数组里。然后将这个数组转化成`torch.*Tensor`
* 对于图片,有Pillow,OpenCV等包可以使用
* 对于音频,有scipy和librosa等包可以使用
* 对于文本,不管是原生python的或者是基于Cython的文本,可以使用NLTK和SpaCy
特别对于视觉方面,我们创建了一个包,名字叫`torchvision`,其中包含了针对Imagenet、CIFAR10、MNIST等常用数据集的数据加载器(data loaders),还有对图片数据变形的操作,即`torchvision.datasets``torch.utils.data.DataLoader`
这提供了极大的便利,可以避免编写样板代码。
在这个教程中,我们将使用CIFAR10数据集,它有如下的分类:“飞机”,“汽车”,“鸟”,“猫”,“鹿”,“狗”,“青蛙”,“马”,“船”,“卡车”等。在CIFAR-10里面的图片数据大小是3x32x32,即三通道彩色图,图片大小是32x32像素。
![cifar10](img/ae800707f2489607d51d67499071db16.jpg)
## 训练一个图片分类器
我们将按顺序做以下步骤:
1. 通过`torchvision`加载CIFAR10里面的训练和测试数据集,并对数据进行标准化
2. 定义卷积神经网络
3. 定义损失函数
4. 利用训练数据训练网络
5. 利用测试数据测试网络
### 1.加载并标准化CIFAR10
使用`torchvision`加载CIFAR10超级简单。
```python
import torch
import torchvision
import torchvision.transforms as transforms
```
torchvision数据集加载完后的输出是范围在[0, 1]之间的PILImage。我们将其标准化为范围在[-1, 1]之间的张量。
```python
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')
```
输出:
```python
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./data/cifar-10-python.tar.gz
Files already downloaded and verified
```
乐趣所致,现在让我们可视化部分训练数据。
```python
import matplotlib.pyplot as plt
import numpy as np
# 输出图像的函数
def imshow(img):
img = img / 2 + 0.5 # unnormalize
npimg = 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)))
```
![https://pytorch.org/tutorials/_images/sphx_glr_cifar10_tutorial_001.png](img/aaf8c905effc5044cb9691420e5261fa.jpg)
输出:
```python
horse horse horse car
```
### 2.定义卷积神经网络
将之前神经网络章节定义的神经网络拿过来,并将其修改成输入为3通道图像(替代原来定义的单通道图像)。
```python
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()
```
### 3.定义损失函数和优化器
我们使用分类的交叉熵损失和随机梯度下降(使用momentum)。
```python
import torch.optim as optim
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
```
### 4.训练网络
事情开始变得有趣了。我们只需要遍历我们的数据迭代器,并将输入“喂”给网络和优化函数。
```python
for epoch in range(2): # loop over the dataset multiple times
running_loss = 0.0
for i, data in enumerate(trainloader, 0):
# get the inputs
inputs, labels = data
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# print statistics
running_loss += loss.item()
if i % 2000 == 1999: # print every 2000 mini-batches
print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
running_loss = 0.0
print('Finished Training')
```
输出:
```python
[1, 2000] loss: 2.182
[1, 4000] loss: 1.819
[1, 6000] loss: 1.648
[1, 8000] loss: 1.569
[1, 10000] loss: 1.511
[1, 12000] loss: 1.473
[2, 2000] loss: 1.414
[2, 4000] loss: 1.365
[2, 6000] loss: 1.358
[2, 8000] loss: 1.322
[2, 10000] loss: 1.298
[2, 12000] loss: 1.282
Finished Training
```
### 5.使用测试数据测试网络
我们已经在训练集上训练了2遍网络。但是我们需要检查网络是否学到了一些东西。
我们将通过预测神经网络输出的标签来检查这个问题,并和正确样本进行(ground-truth)对比。如果预测是正确的,我们将样本添加到正确预测的列表中。
ok,第一步。让我们显示测试集中的图像来熟悉一下。
```python
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)))
```
![https://pytorch.org/tutorials/_images/sphx_glr_cifar10_tutorial_002.png](img/d148a5bd51a3278e9698bba522cbc34a.jpg)
```python
GroundTruth: cat ship ship plane
```
ok,现在让我们看看神经网络认为上面的例子是:
```python
outputs = net(images)
```
输出是10个类别的量值。一个类的值越高,网络就越认为这个图像属于这个特定的类。让我们得到最高量值的下标/索引;
```python
_, predicted = torch.max(outputs, 1)
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
```
输出:
```python
Predicted: dog ship ship plane
```
结果还不错。
让我们看看网络在整个数据集上表现的怎么样。
```python
correct = 0
total = 0
with torch.no_grad():
for data in testloader:
images, labels = data
outputs = 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))
```
输出:
```python
Accuracy of the network on the 10000 test images: 55 %
```
这比随机选取(即从10个类中随机选择一个类,正确率是10%)要好很多。看来网络确实学到了一些东西。
那么哪些是表现好的类呢?哪些是表现的差的类呢?
```python
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]))
```
输出:
```python
Accuracy of plane : 70 %
Accuracy of car : 70 %
Accuracy of bird : 28 %
Accuracy of cat : 25 %
Accuracy of deer : 37 %
Accuracy of dog : 60 %
Accuracy of frog : 66 %
Accuracy of horse : 62 %
Accuracy of ship : 69 %
Accuracy of truck : 61 %
```
ok,接下来呢?
怎么在GPU上运行神经网络呢?
## 在GPU上训练
与将一个张量传递给GPU一样,可以这样将神经网络转移到GPU上。
如果我们有cuda可用的话,让我们首先定义第一个设备为可见cuda设备:
```python
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)
```
输出:
```python
cuda:0
```
本节的其余部分假设`device`是CUDA。
然后这些方法将递归遍历所有模块,并将它们的参数和缓冲区转换为CUDA张量:
```python
net.to(device)
```
请记住,我们不得不将输入和目标在每一步都送入GPU:
```python
inputs, labels = inputs.to(device), labels.to(device)
```
为什么我们感受不到与CPU相比的巨大加速?因为我们的网络实在是太小了。
尝试一下:加宽你的网络(注意第一个`nn.Conv2d`的第二个参数和第二个`nn.Conv2d`的第一个参数要相同),看看能获得多少加速。
已实现的目标:
* 在更高层次上理解PyTorch的Tensor库和神经网络
* 训练一个小的神经网络做图片分类
## 在多GPU上训练
如果希望使用您所有GPU获得**更大的**加速,请查看[Optional: Data Parallelism](https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html)
## 接下来要做什么?
* [Train neural nets to play video games](https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html)
* [Train a state-of-the-art ResNet network on imagenet](https://github.com/pytorch/examples/tree/master/imagenet)
* [Train a face generator using Generative Adversarial Networks](https://github.com/pytorch/examples/tree/master/dcgan)
* [Train a word-level language model using Recurrent LSTM networks](https://github.com/pytorch/examples/tree/master/word_language_model)
* [More examples](https://github.com/pytorch/examples)
* [More tutorials](https://github.com/pytorch/tutorials)
* [Discuss PyTorch on the Forums](https://discuss.pytorch.org/)
* [Chat with other users on Slack](https://pytorch.slack.com/messages/beginner/)
# 可选: 数据并行处理
> 原文: [https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html](https://pytorch.org/tutorials/beginner/blitz/data_parallel_tutorial.html)
> **作者**: [Sung Kim](https://github.com/hunkim) [Jenny Kang](https://github.com/jennykang)
>
> 译者: [bat67](https://github.com/bat67)
>
> 校验者: [FontTian](https://github.com/fonttian) [片刻](https://github.com/jiangzhonglian)
在这个教程里,我们将学习如何使用数据并行(`DataParallel`)来使用多GPU。
PyTorch非常容易的就可以使用GPU,可以用如下方式把一个模型放到GPU上:
```python
device = torch.device("cuda: 0")
model.to(device)
```
然后可以复制所有的张量到GPU上:
```python
mytensor = my_tensor.to(device)
```
请注意,调用`my_tensor.to(device)`返回一个GPU上的`my_tensor`副本,而不是重写`my_tensor`。我们需要把它赋值给一个新的张量并在GPU上使用这个张量。
在多GPU上执行前向和反向传播是自然而然的事。然而,PyTorch默认将只是用一个GPU。你可以使用`DataParallel`让模型并行运行来轻易的让你的操作在多个GPU上运行。
```python
model = nn.DataParallel(model)
```
这是这篇教程背后的核心,我们接下来将更详细的介绍它。
## 导入和参数
导入PyTorch模块和定义参数。
```python
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
# Parameters 和 DataLoaders
input_size = 5
output_size = 2
batch_size = 30
data_size = 100
```
设备(Device):
```python
device = torch.device("cuda: 0" if torch.cuda.is_available() else "cpu")
```
## 虚拟数据集
要制作一个虚拟(随机)数据集,只需实现`__getitem__`
```python
class RandomDataset(Dataset):
def __init__(self, size, length):
self.len = length
self.data = torch.randn(length, size)
def __getitem__(self, index):
return self.data[index]
def __len__(self):
return self.len
rand_loader = DataLoader(dataset=RandomDataset(input_size, data_size),
batch_size=batch_size, shuffle=True)
```
## 简单模型
作为演示,我们的模型只接受一个输入,执行一个线性操作,然后得到结果。然而,你能在任何模型(CNN,RNN,Capsule Net等)上使用`DataParallel`
我们在模型内部放置了一条打印语句来检测输入和输出向量的大小。请注意批等级为0时打印的内容。
```python
class Model(nn.Module):
# Our model
def __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
```
## 创建一个模型和数据并行
这是本教程的核心部分。首先,我们需要创建一个模型实例和检测我们是否有多个GPU。如果我们有多个GPU,我们使用`nn.DataParallel`来包装我们的模型。然后通过`model.to(device)`把模型放到GPU上。
```python
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 GPUs
model = nn.DataParallel(model)
model.to(device)
```
输出:
```python
Let's use 2 GPUs!
```
## 运行模型
现在我们可以看输入和输出张量的大小。
```python
for data in rand_loader:
input = data.to(device)
output = model(input)
print("Outside: input size", input.size(),
"output_size", output.size())
```
输出:
```python
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
```
## 结果
当我们对30个输入和输出进行批处理时,我们和期望的一样得到30个输入和30个输出,但是若有多个GPU,会得到如下的结果。
### 2个GPU
若有2个GPU,将看到:
```python
Let's use 2 GPUs!
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
In Model: input size torch.Size([15, 5]) output size torch.Size([15, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
In Model: input size torch.Size([5, 5]) output size torch.Size([5, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
```
### 3个GPU
若有3个GPU,将看到:
```python
Let's use 3 GPUs!
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
In Model: input size torch.Size([10, 5]) output size torch.Size([10, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
```
### 8个GPU
若有8个GPU,将看到:
```python
Let's use 8 GPUs!
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([4, 5]) output size torch.Size([4, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([30, 5]) output_size torch.Size([30, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
In Model: input size torch.Size([2, 5]) output size torch.Size([2, 2])
Outside: input size torch.Size([10, 5]) output_size torch.Size([10, 2])
```
## 总结
`DataParallel`自动的划分数据,并将作业发送到多个GPU上的多个模型。`DataParallel`会在每个模型完成作业后,收集与合并结果然后返回给你。
更多信息,请参考: https://pytorch.org/tutorials/beginner/former_torchies/parallelism_tutorial.html
# 神经网络
> 原文: [https://pytorch.org/tutorials/beginner/blitz/neural_networks_tutorial.html](https://pytorch.org/tutorials/beginner/blitz/neural_networks_tutorial.html)
> 译者:[bat67](https://github.com/bat67)
>
> 校验者:[FontTian](https://github.com/fonttian)
可以使用`torch.nn`包来构建神经网络.
我们已经介绍了`autograd``nn`包则依赖于`autograd`包来定义模型并对它们求导。一个`nn.Module`包含各个层和一个`forward(input)`方法,该方法返回`output`
例如,下面这个神经网络可以对数字进行分类:
![convnet](https://pytorch.org/tutorials/_images/mnist.png)
这是一个简单的前馈神经网络(feed-forward network)。它接受一个输入,然后将它送入下一层,一层接一层的传递,最后给出输出。
一个神经网络的典型训练过程如下:
* 定义包含一些可学习参数(或者叫权重)的神经网络
* 在输入数据集上迭代
* 通过网络处理输入
* 计算损失(输出和正确答案的距离)
* 将梯度反向传播给网络的参数
* 更新网络的权重,一般使用一个简单的规则:`weight = weight - learning_rate * gradient`
## 定义网络
让我们定义这样一个网络:
```python
import torch
import torch.nn as nn
import torch.nn.functional as F
class 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 + b
self.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 pooling
x = 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 x
def num_flat_features(self, x):
size = x.size()[1:] # 除去批处理维度的其他所有维度
num_features = 1
for s in size:
num_features *= s
return num_features
net = Net()
print(net)
```
输出:
```python
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(in_features=400, 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`时自动定义,`backward`函数用来计算导数。可以在 `forward` 函数中使用任何针对张量的操作和计算。
一个模型的可学习参数可以通过`net.parameters()`返回
```python
params = list(net.parameters())
print(len(params))
print(params[0].size()) # conv1's .weight
```
输出:
```python
10
torch.Size([6, 1, 5, 5])
```
让我们尝试一个随机的32x32的输入。注意:这个网络(LeNet)的期待输入是32x32。如果使用MNIST数据集来训练这个网络,要把图片大小重新调整到32x32。
```python
input = torch.randn(1, 1, 32, 32)
out = net(input)
print(out)
```
输出:
```python
tensor([[ 0.0399, -0.0856, 0.0668, 0.0915, 0.0453, -0.0680, -0.1024, 0.0493,
-0.1043, -0.1267]], grad_fn=<AddmmBackward>)
```
清零所有参数的梯度缓存,然后进行随机梯度的反向传播:
```python
net.zero_grad()
out.backward(torch.randn(1, 10))
```
> 注意:
>
> `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`的函数并对其历史进行编码。
目前为止,我们讨论了:
* 定义一个神经网络
* 处理输入调用`backward`
还剩下:
* 计算损失
* 更新网络权重
## 损失函数
一个损失函数接受一对(output, target)作为输入,计算一个值来估计网络的输出和目标值相差多少。
nn包中有很多不同的[损失函数](https://pytorch.org/docs/stable/nn.html)`nn.MSELoss`是比较简单的一种,它计算输出和目标的均方误差(mean-squared error)。
例如:
```python
output = net(input)
target = torch.randn(10) # 本例子中使用模拟数据
target = target.view(1, -1) # 使目标值与数据值形状一致
criterion = nn.MSELoss()
loss = criterion(output, target)
print(loss)
```
输出:
```python
tensor(1.0263, grad_fn=<MseLossBackward>)
```
现在,如果使用`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`属性累积着梯度张量。
为了说明这一点,让我们向后跟踪几步:
```python
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
```
输出:
```python
<MseLossBackward object at 0x7f94c821fdd8>
<AddmmBackward object at 0x7f94c821f6a0>
<AccumulateGrad object at 0x7f94c821f6a0>
```
## 反向传播
我们只需要调用`loss.backward()`来反向传播权重。我们需要清零现有的梯度,否则梯度将会与已有的梯度累加。
现在,我们将调用`loss.backward()`,并查看conv1层的偏置(bias)在反向传播前后的梯度。
```python
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)
```
输出:
```python
conv1.bias.grad before backward
tensor([0., 0., 0., 0., 0., 0.])
conv1.bias.grad after backward
tensor([ 0.0084, 0.0019, -0.0179, -0.0212, 0.0067, -0.0096])
```
现在,我们已经见到了如何使用损失函数。
> 稍后阅读
>
> 神经网络包包含了各种模块和损失函数,这些模块和损失函数构成了深度神经网络的构建模块。完整的文档列表见[这里](https://pytorch.org/docs/stable/nn.html)。
>
> 现在唯一要学习的是:
>
> * 更新网络的权重
## 更新权重
最简单的更新规则是随机梯度下降法(SGD):
`weight = weight - learning_rate * gradient`
我们可以使用简单的python代码来实现:
```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`,它实现了所有的这些方法。使用它很简单:
```python
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()`手动清零的。这是因为梯度是累加的,正如前面[反向传播章节](#反向传播)叙述的那样。
# 什么是PyTorch?
> 原文: [https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html](https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html)
> 译者:[bat67](https://github.com/bat67)
>
> 校验者:[FontTian](https://github.com/fonttian)
**作者**[Soumith Chintala](http://soumith.ch/)
PyTorch是一个基于python的科学计算包,主要针对两类人群:
* 作为NumPy的替代品,可以利用GPU的性能进行计算
* 作为一个高灵活性、速度快的深度学习平台
## 入门
### 张量
`Tensor`(张量)类似于`NumPy``ndarray`,但还可以在GPU上使用来加速计算。
```python
from __future__ import print_function
import torch
```
创建一个没有初始化的5*3矩阵:
```python
x = torch.empty(5, 3)
print(x)
```
输出:
```python
tensor([[2.2391e-19, 4.5869e-41, 1.4191e-17],
[4.5869e-41, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00],
[0.0000e+00, 0.0000e+00, 0.0000e+00]])
```
创建一个随机初始化矩阵:
```python
x = torch.rand(5, 3)
print(x)
```
输出:
```python
tensor([[0.5307, 0.9752, 0.5376],
[0.2789, 0.7219, 0.1254],
[0.6700, 0.6100, 0.3484],
[0.0922, 0.0779, 0.2446],
[0.2967, 0.9481, 0.1311]])
```
构造一个填满`0`且数据类型为`long`的矩阵:
```python
x = torch.zeros(5, 3, dtype=torch.long)
print(x)
```
输出:
```python
tensor([[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]])
```
直接从数据构造张量:
```python
x = torch.tensor([5.5, 3])
print(x)
```
输出:
```python
tensor([5.5000, 3.0000])
```
或者根据已有的tensor建立新的tensor。除非用户提供新的值,否则这些方法将重用输入张量的属性,例如dtype等:
```python
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一致
```
输出:
```python
tensor([[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.],
[1., 1., 1.]], dtype=torch.float64)
tensor([[ 1.6040, -0.6769, 0.0555],
[ 0.6273, 0.7683, -0.2838],
[-0.7159, -0.5566, -0.2020],
[ 0.6266, 0.3566, 1.4497],
[-0.8092, -0.6741, 0.0406]])
```
获取张量的形状:
```python
print(x.size())
```
输出:
```python
torch.Size([5, 3])
```
> **注意**:
>
> `torch.Size`本质上还是`tuple`,所以支持tuple的一切操作。
### 运算
一种运算有多种语法。在下面的示例中,我们将研究加法运算。
加法:形式一
```python
y = torch.rand(5, 3)
print(x + y)
```
输出:
```python
tensor([[ 2.5541, 0.0943, 0.9835],
[ 1.4911, 1.3117, 0.5220],
[-0.0078, -0.1161, 0.6687],
[ 0.8176, 1.1179, 1.9194],
[-0.3251, -0.2236, 0.7653]])
```
加法:形式二
```python
print(torch.add(x, y))
```
输出:
```python
tensor([[ 2.5541, 0.0943, 0.9835],
[ 1.4911, 1.3117, 0.5220],
[-0.0078, -0.1161, 0.6687],
[ 0.8176, 1.1179, 1.9194],
[-0.3251, -0.2236, 0.7653]])
```
加法:给定一个输出张量作为参数
```python
result = torch.empty(5, 3)
torch.add(x, y, out=result)
print(result)
```
输出:
```python
tensor([[ 2.5541, 0.0943, 0.9835],
[ 1.4911, 1.3117, 0.5220],
[-0.0078, -0.1161, 0.6687],
[ 0.8176, 1.1179, 1.9194],
[-0.3251, -0.2236, 0.7653]])
```
加法:原位/原地操作(in-place)
```python
# adds x to y
y.add_(x)
print(y)
```
输出:
```python
tensor([[ 2.5541, 0.0943, 0.9835],
[ 1.4911, 1.3117, 0.5220],
[-0.0078, -0.1161, 0.6687],
[ 0.8176, 1.1179, 1.9194],
[-0.3251, -0.2236, 0.7653]])
```
>注意:
>
>任何一个in-place改变张量的操作后面都固定一个`_`。例如`x.copy_(y)`、`x.t_()`将更改x
也可以使用像标准的NumPy一样的各种索引操作:
```python
print(x[:, 1])
```
输出:
```python
tensor([-0.6769, 0.7683, -0.5566, 0.3566, -0.6741])
```
改变形状:如果想改变形状,可以使用`torch.view`
```python
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())
```
输出:
```python
torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
```
如果是仅包含一个元素的tensor,可以使用`.item()`来得到对应的python数值
```python
x = torch.randn(1)
print(x)
print(x.item())
```
输出:
```python
tensor([0.0445])
0.0445479191839695
```
>后续阅读:
>
>超过100种tensor的运算操作,包括转置,索引,切片,数学运算,
线性代数,随机数等,具体访问[这里](https://pytorch.org/docs/stable/torch.html)
## 桥接 NumPy
将一个Torch张量转换为一个NumPy数组是轻而易举的事情,反之亦然。
Torch张量和NumPy数组将共享它们的底层内存位置,因此当一个改变时,另外也会改变。
### 将torch的Tensor转化为NumPy数组
输入:
```python
a = torch.ones(5)
print(a)
```
输出:
```python
tensor([1., 1., 1., 1., 1.])
```
输入:
```python
b = a.numpy()
print(b)
```
输出:
```python
[1. 1. 1. 1. 1.]
```
看NumPy数组是如何改变里面的值的:
```python
a.add_(1)
print(a)
print(b)
```
输出:
```python
tensor([2., 2., 2., 2., 2.])
[2. 2. 2. 2. 2.]
```
### 将NumPy数组转化为Torch张量
看改变NumPy数组是如何自动改变Torch张量的:
```python
import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
```
输出:
```python
[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
```
CPU上的所有张量(CharTensor除外)都支持与Numpy的相互转换。
## CUDA上的张量
张量可以使用`.to`方法移动到任何设备(device)上:
```python
# 当GPU可用时,我们可以运行以下代码
# 我们将使用`torch.device`来将tensor移入和移出GPU
if torch.cuda.is_available():
device = torch.device("cuda") # a CUDA device object
y = torch.ones_like(x, device=device) # 直接在GPU上创建tensor
x = x.to(device) # 或者使用`.to("cuda")`方法
z = x + y
print(z)
print(z.to("cpu", torch.double)) # `.to`也能在移动时改变dtype
```
输出:
```python
tensor([1.0445], device='cuda:0')
tensor([1.0445], dtype=torch.float64)
```
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册