提交 be18b6cc 编写于 作者: W wizardforcel

2020-10-19 21:45:53

上级 e4af6c14
# 莫烦 PyTorch 系列教程
来源:[https://www.pytorchtutorial.com/mofan-pytorch-tutorials-list/](https://www.pytorchtutorial.com/mofan-pytorch-tutorials-list/)
\ No newline at end of file
# PyTorch 简介
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 1.1 – Why PyTorch?
## 为什么用 PyTorch
[PyTorch](https://www.pytorchtutorial.com/goto/http://pytorch.org/) 是 [Torch](https://www.pytorchtutorial.com/goto/http://torch.ch/) 在 Python 上的衍生. 因为 [Torch](https://www.pytorchtutorial.com/goto/http://torch.ch/) 是一个使用 [Lua](https://www.pytorchtutorial.com/goto/https://www.lua.org/) 语言的神经网络库, Torch 很好用, 但是 Lua 又不是特别流行, 所有开发团队将 Lua 的 Torch 移植到了更流行的语言 Python 上. 是的 PyTorch 一出生就引来了剧烈的反响. 为什么呢?
很简单, 我们就看看有谁在用 PyTorch 吧.
![](img/20e2ebdf112e4aa3202e951e072c2dc2.png)
可见, 著名的 Facebook, twitter 等都在使用它, 这就说明 PyTorch 的确是好用的, 而且是值得推广.
而且如果你知道 [Numpy](https://www.pytorchtutorial.com/goto/http://www.numpy.org/), PyTorch 说他就是在神经网络领域可以用来替换 numpy 的模块.
## 神经网络在做什么
神经网络在学习拟合线条(回归):
![](img/c8011979fa20046cbfa36e46cf508689.png)
神经网络在学习区分数据(分类):
![](img/94268b7d9687d039d872da203453c97b.png)
## PyTorch 和 Tensorflow
据 PyTorch 自己介绍, 他们家的最大优点就是建立的神经网络是动态的, 对比静态的 Tensorflow, 他能更有效地处理一些问题, 比如说 RNN 变化时间长度的输出. 而我认为, 各家有各家的优势和劣势, 所以我们要以中立的态度. 两者都是大公司, Tensorflow 自己说自己在分布式训练上下了很大的功夫, 那我就默认 Tensorflow 在这一点上要超出 PyTorch, 但是 Tensorflow 的静态计算图使得他在 RNN 上有一点点被动 (虽然它用其他途径解决了), 不过用 PyTorch 的时候, 你会对这种动态的 RNN 有更好的理解.
而且 Tensorflow 的高度工业化, 它的底层代码… 你是看不懂的. PyTorch 好那么一点点, 如果你深入 API, 你至少能比看 Tensorflow 多看懂一点点 PyTorch 的底层在干嘛.
最后我的建议就是:
* 如果你是学生, 随便选一个学, 或者稍稍偏向 PyTorch, 因为写代码的时候应该更好理解. 懂了一个模块, 转换 Tensorflow 或者其他的模块都好说.
* 如果是上班了, 跟着你公司来, 公司用什么, 你就用什么, 不要脱群.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 1.2 – 安装 PyTorch
## 支持的系统
PyTorch 暂时只支持 MacOS, Linux. 暂不支持 Windows! (可怜的 Windows 同学们.. 又被抛弃了). 不过说不定像 Tensorflow 一样, 因为 Windows 用户的强烈要求, 他们在某天就突然支持了.
## 安装
PyTorch 安装起来很简单, [它自家网页](https://www.pytorchtutorial.com/goto/http://pytorch.org/)上就有很方便的选择方式 (网页升级改版后可能和下图有点不同):
![](img/b708f231f544bd7bcefa1d55c82653dd.png)
所以根据你的情况选择适合你的安装方法, 我已自己为例, 我使用的是 MacOS, 想用 pip 安装, 我的 Python 是 3.5 版的, 我没有 GPU 加速, 那我就按上面的选:
然后根据上面的提示, 我只需要在我的 Terminal 当中输入以下指令就好了:
```
$ pip install http://download.pytorch.org/whl/torch-0.1.11.post5-cp35-cp35m-macosx_10_7_x86_64.whl
$ pip install torchvision
```
注意, 我安装的是0.1.11版本的 torch, 你需要去他们网站上看是否有新版本的. 安装 PyTorch 会安装两个模块, 一个是 torch, 一个 torchvision, torch 是主模块, 用来搭建神经网络的, torchvision 是辅模块, 有数据库, 还有一些已经训练好的神经网络等着你直接用, 比如 ([VGG, AlexNet, ResNet](https://www.pytorchtutorial.com/goto/http://pytorch.org/docs/torchvision/models.html)).
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# PyTorch 神经网络基础
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 2.1 – Torch vs Numpy
## 用 Numpy 还是 Torch
Torch 自称为神经网络界的 Numpy, 因为他能将 torch 产生的 tensor 放在 GPU 中加速运算 (前提是你有合适的 GPU), 就像 Numpy 会把 array 放在 CPU 中加速运算. 所以神经网络的话, 当然是用 Torch 的 tensor 形式数据最好咯. 就像 Tensorflow 当中的 tensor 一样.
当然, 我们对 Numpy 还是爱不释手的, 因为我们太习惯 numpy 的形式了. 不过 torch 看出来我们的喜爱, 他把 torch 做的和 numpy 能很好的兼容. 比如这样就能自由地转换 numpy array 和 torch tensor 了:
```
import torch
import numpy as np
np_data = np.arange(6).reshape((2, 3))
torch_data = torch.from_numpy(np_data)
tensor2array = torch_data.numpy()
print(
\'\nnumpy array:\', np_data, # [[0 1 2], [3 4 5]]
\'\ntorch tensor:\', torch_data, # 0 1 2 \n 3 4 5 [torch.LongTensor of size 2x3]
\'\ntensor to array:\', tensor2array, # [[0 1 2], [3 4 5]]
)
```
## Torch 中的数学运算
其实 torch 中 tensor 的运算和 numpy array 的如出一辙, 我们就以对比的形式来看. 如果想了解 torch 中其它更多有用的运算符, [API就是你要去的地方](https://www.pytorchtutorial.com/goto/http://pytorch.org/docs/torch.html#math-operations).
```
# abs 绝对值计算
data = [-1, -2, 1, 2]
tensor = torch.FloatTensor(data) # 转换成32位浮点 tensor
print(
\'\nabs\',
\'\nnumpy: \', np.abs(data), # [1 2 1 2]
\'\ntorch: \', torch.abs(tensor) # [1 2 1 2]
)
# sin 三角函数 sin
print(
\'\nsin\',
\'\nnumpy: \', np.sin(data), # [-0.84147098 -0.90929743 0.84147098 0.90929743]
\'\ntorch: \', torch.sin(tensor) # [-0.8415 -0.9093 0.8415 0.9093]
)
# mean 均值
print(
\'\nmean\',
\'\nnumpy: \', np.mean(data), # 0.0
\'\ntorch: \', torch.mean(tensor) # 0.0
)
```
除了简单的计算, 矩阵运算才是神经网络中最重要的部分. 所以我们展示下矩阵的乘法. 注意一下包含了一个 numpy 中可行, 但是 torch 中不可行的方式.
```
# matrix multiplication 矩阵点乘
data = [[1,2], [3,4]]
tensor = torch.FloatTensor(data) # 转换成32位浮点 tensor
# correct method
print(
\'\nmatrix multiplication (matmul)\',
\'\nnumpy: \', np.matmul(data, data), # [[7, 10], [15, 22]]
\'\ntorch: \', torch.mm(tensor, tensor) # [[7, 10], [15, 22]]
)
# !!!! 下面是错误的方法 !!!!
data = np.array(data)
print(
\'\nmatrix multiplication (dot)\',
\'\nnumpy: \', data.dot(data), # [[7, 10], [15, 22]] 在numpy 中可行
\'\ntorch: \', tensor.dot(tensor) # torch 会转换成 [1,2,3,4].dot([1,2,3,4) = 30.0
)
```
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 2.2 – 变量 (Variable)
## 什么是 Variable
在 Torch 中的 Variable 就是一个存放会变化的值的地理位置. 里面的值会不停的变化. 就像一个裝鸡蛋的篮子, 鸡蛋数会不停变动. 那谁是里面的鸡蛋呢, 自然就是 Torch 的 Tensor 咯. **如果用一个 Variable 进行计算, 那返回的也是一个同类型的 Variable.**
我们定义一个 Variable:
```
import torch
from torch.autograd import Variable # torch 中 Variable 模块
# 先生鸡蛋
tensor = torch.FloatTensor([[1,2],[3,4]])
# 把鸡蛋放到篮子里, requires_grad是参不参与误差反向传播, 要不要计算梯度
variable = Variable(tensor, requires_grad=True)
print(tensor)
"""
1 2
3 4
[torch.FloatTensor of size 2x2]
"""
print(variable)
"""
Variable containing:
1 2
3 4
[torch.FloatTensor of size 2x2]
"""
```
## Variable 计算, 梯度
我们再对比一下 tensor 的计算和 variable 的计算.\’
```
t_out = torch.mean(tensor*tensor) # x^2
v_out = torch.mean(variable*variable) # x^2
print(t_out)
print(v_out) # 7.5
```
到目前为止, 我们看不出什么不同, **但是时刻记住, Variable 计算时, 它在背景幕布后面一步步默默地搭建着一个庞大的系统, 叫做计算图, computational graph. 这个图是用来干嘛的? 原来是将所有的计算步骤 (节点) 都连接起来, 最后进行误差反向传递的时候, 一次性将所有 variable 里面的修改幅度 (梯度) 都计算出来, 而 tensor 就没有这个能力啦.**
v_out = torch.mean(variable*variable) 就是在计算图中添加的一个计算步骤, 计算误差反向传递的时候有他一份功劳, 我们就来举个例子:
```
v_out.backward() # 模拟 v_out 的误差反向传递
# 下面两步看不懂没关系, 只要知道 Variable 是计算图的一部分, 可以用来传递误差就好.
# v_out = 1/4 * sum(variable*variable) 这是计算图中的 v_out 计算步骤
# 针对于 v_out 的梯度就是, d(v_out)/d(variable) = 1/4*2*variable = variable/2
print(variable.grad) # 初始 Variable 的梯度
\'\'\'
0.5000 1.0000
1.5000 2.0000
\'\'\'
```
#### 获取 Variable 里面的数据
直接 print(variable) 只会输出 Variable 形式的数据, 在很多时候是用不了的(比如想要用 plt 画图), 所以我们要转换一下, 将它变成 tensor 形式.
```
print(variable) # Variable 形式
"""
Variable containing:
1 2
3 4
[torch.FloatTensor of size 2x2]
"""
print(variable.data) # tensor 形式
"""
1 2
3 4
[torch.FloatTensor of size 2x2]
"""
print(variable.data.numpy()) # numpy 形式
"""
[[ 1\. 2.]
[ 3\. 4.]]
"""
```
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 2.3 – 激励函数 (Activation)
## 什么是 Activation
一句话概括 Activation: 就是让神经网络可以描述非线性问题的步骤, 是神经网络变得更强大. 如果还不是特别了解, 我有制作一个动画短片(如下), 浅显易懂的阐述了激励函数的作用. 包懂.
## Torch 中的激励函数
Torch 中的激励函数有很多, 不过我们平时要用到的就这几个.  relu, sigmoid, tanh, softplus . 那我们就看看他们各自长什么样啦.
```
import torch
import torch.nn.functional as F # 激励函数都在这
from torch.autograd import Variable
# 做一些假数据来观看图像
x = torch.linspace(-5, 5, 200) # x data (tensor), shape=(100, 1)
x = Variable(x)
```
接着就是做生成不同的激励函数数据:
```
x_np = x.data.numpy() # 换成 numpy array, 出图时用
# 几种常用的 激励函数
y_relu = F.relu(x).data.numpy()
y_sigmoid = F.sigmoid(x).data.numpy()
y_tanh = F.tanh(x).data.numpy()
y_softplus = F.softplus(x).data.numpy()
# y_softmax = F.softmax(x) softmax 比较特殊, 不能直接显示, 不过他是关于概率的, 用于分类
```
接着我们开始画图, 画图的代码也在下面:
![](img/f1108a1b6941305fa7a39e488c023fe9.png)
```
import matplotlib.pyplot as plt # python 的可视化模块, 我有教程 (https://morvanzhou.github.io/tutorials/data-manipulation/plt/)
plt.figure(1, figsize=(8, 6))
plt.subplot(221)
plt.plot(x_np, y_relu, c=\'red\', label=\'relu\')
plt.ylim((-1, 5))
plt.legend(loc=\'best\')
plt.subplot(222)
plt.plot(x_np, y_sigmoid, c=\'red\', label=\'sigmoid\')
plt.ylim((-0.2, 1.2))
plt.legend(loc=\'best\')
plt.subplot(223)
plt.plot(x_np, y_tanh, c=\'red\', label=\'tanh\')
plt.ylim((-1.2, 1.2))
plt.legend(loc=\'best\')
plt.subplot(224)
plt.plot(x_np, y_softplus, c=\'red\', label=\'softplus\')
plt.ylim((-0.2, 6))
plt.legend(loc=\'best\')
plt.show()
```
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# 建造第一个神经网络
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 3.1 – 关系拟合 (回归 Regression)
我会这次会来见证神经网络是如何通过简单的形式将一群数据用一条线条来表示. 或者说, 是如何在数据当中找到他们的关系, 然后用神经网络模型来建立一个可以代表他们关系的线条.
![](img/1f0b990d5a8d78692d3730d855fe44ea.png)
## 建立数据集
我们创建一些假数据来模拟真实的情况. 比如一个一元二次函数:  y = a * x^2 b , 我们给  y  数据加上一点噪声来更加真实的展示它.
```
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # x data (tensor), shape=(100, 1)
y = x.pow(2) 0.2*torch.rand(x.size()) # noisy y data (tensor), shape=(100, 1)
# 用 Variable 来修饰这些数据 tensor
x, y = torch.autograd.Variable(x), Variable(y)
# 画图
plt.scatter(x.data.numpy(), y.data.numpy())
plt.show()
```
## 建立神经网络
建立一个神经网络我们可以直接运用 torch 中的体系. 先定义所有的层属性( __init__() ), 然后再一层层搭建( forward(x) )层于层的关系链接. 建立关系的时候, 我们会用到激励函数, 如果还不清楚激励函数用途的同学, 这里有非常好的一篇动画教程 (如下).
```
import torch
import torch.nn.functional as F # 激励函数都在这
class Net(torch.nn.Module): # 继承 torch 的 Module
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__() # 继承 __init__ 功能
# 定义每层用什么样的形式
self.hidden = torch.nn.Linear(n_feature, n_hidden) # 隐藏层线性输出
self.predict = torch.nn.Linear(n_hidden, n_output) # 输出层线性输出
def forward(self, x): # 这同时也是 Module 中的 forward 功能
# 正向传播输入值, 神经网络分析出输出值
x = F.relu(self.hidden(x)) # 激励函数(隐藏层的线性值)
x = self.predict(x) # 输出值
return x
net = Net(n_feature=1, n_hidden=10, n_output=1)
print(net) # net 的结构
"""
Net (
(hidden): Linear (1 -> 10)
(predict): Linear (10 -> 1)
)
"""
```
## 训练网络
训练的步骤很简单, 如下:
```
# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.5) # 传入 net 的所有参数, 学习率
loss_func = torch.nn.MSELoss() # 预测值和真实值的误差计算公式 (均方差)
for t in range(100):
prediction = net(x) # 喂给 net 训练数据 x, 输出预测值
loss = loss_func(prediction, y) # 计算两者的误差
optimizer.zero_grad() # 清空上一步的残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
optimizer.step() # 将参数更新值施加到 net 的 parameters 上
```
## 可视化训练过程
为了可视化整个训练的过程, 更好的理解是如何训练, 我们如下操作:
```
import matplotlib.pyplot as plt
plt.ion() # 画图
plt.show()
for t in range(100):
...
loss.backward()
optimizer.step()
# 接着上面来
if t % 5 == 0:
# plot and show learning process
plt.cla()
plt.scatter(x.data.numpy(), y.data.numpy())
plt.plot(x.data.numpy(), prediction.data.numpy(), \'r-\', lw=5)
plt.text(0.5, 0, \'Loss=%.4f\' % loss.data[0], fontdict={\'size\': 20, \'color\': \'red\'})
plt.pause(0.1)
```
![](img/13e0473ef73a9de2569a81c62e30d054.png)
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/301_regression.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 3.2 – 区分类型 (分类 Classification)
这次我们也是用最简单的途径来看看神经网络是怎么进行事物的分类.
![](img/5a415b795ebbb116db6d4a2394d93b27.png)
## 建立数据集
我们创建一些假数据来模拟真实的情况. 比如两个二次分布的数据, 不过他们的均值都不一样.
```
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
# 假数据
n_data = torch.ones(100, 2) # 数据的基本形态
x0 = torch.normal(2*n_data, 1) # 类型0 x data (tensor), shape=(100, 2)
y0 = torch.zeros(100) # 类型0 y data (tensor), shape=(100, 1)
x1 = torch.normal(-2*n_data, 1) # 类型1 x data (tensor), shape=(100, 1)
y1 = torch.ones(100) # 类型1 y data (tensor), shape=(100, 1)
# 注意 x, y 数据的数据形式是一定要像下面一样 (torch.cat 是在合并数据)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor) # FloatTensor = 32-bit floating
y = torch.cat((y0, y1), ).type(torch.LongTensor) # LongTensor = 64-bit integer
# torch 只能在 Variable 上训练, 所以把它们变成 Variable
x, y = Variable(x), Variable(y)
# plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap=\'RdYlGn\')
# plt.show()
# 画图
plt.scatter(x.data.numpy(), y.data.numpy())
plt.show()
```
## 建立神经网络
建立一个神经网络我们可以直接运用 torch 中的体系. 先定义所有的层属性( __init__() ), 然后再一层层搭建( forward(x) )层于层的关系链接. 这个和我们在前面 regression 的时候的神经网络基本没差. 建立关系的时候, 我们会用到激励函数.
```
import torch
import torch.nn.functional as F # 激励函数都在这
class Net(torch.nn.Module): # 继承 torch 的 Module
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__() # 继承 __init__ 功能
self.hidden = torch.nn.Linear(n_feature, n_hidden) # 隐藏层线性输出
self.out = torch.nn.Linear(n_hidden, n_output) # 输出层线性输出
def forward(self, x):
# 正向传播输入值, 神经网络分析出输出值
x = F.relu(self.hidden(x)) # 激励函数(隐藏层的线性值)
x = self.out(x) # 输出值, 但是这个不是预测值, 预测值还需要再另外计算
return x
net = Net(n_feature=2, n_hidden=10, n_output=2) # 几个类别就几个 output
print(net) # net 的结构
"""
Net (
(hidden): Linear (2 -> 10)
(out): Linear (10 -> 2)
)
"""
```
## 训练网络
训练的步骤很简单, 如下:
```
# optimizer 是训练的工具
optimizer = torch.optim.SGD(net.parameters(), lr=0.02) # 传入 net 的所有参数, 学习率
# 算误差的时候, 注意真实值!不是! one-hot 形式的, 而是1D Tensor, (batch,)
# 但是预测值是2D tensor (batch, n_classes)
loss_func = torch.nn.CrossEntropyLoss()
for t in range(100):
out = net(x) # 喂给 net 训练数据 x, 输出分析值
loss = loss_func(out, y) # 计算两者的误差
optimizer.zero_grad() # 清空上一步的残余更新参数值
loss.backward() # 误差反向传播, 计算参数更新值
optimizer.step() # 将参数更新值施加到 net 的 parameters 上
```
## 可视化训练过程
为了可视化整个训练的过程, 更好的理解是如何训练, 我们如下操作:
```
import matplotlib.pyplot as plt
plt.ion() # 画图
plt.show()
for t in range(100):
...
loss.backward()
optimizer.step()
# 接着上面来
if t % 2 == 0:
plt.cla()
# 过了一道 softmax 的激励函数后的最大概率才是预测值
prediction = torch.max(F.softmax(out), 1)[1]
pred_y = prediction.data.numpy().squeeze()
target_y = y.data.numpy()
plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=pred_y, s=100, lw=0, cmap=\'RdYlGn\')
accuracy = sum(pred_y == target_y)/200 # 预测中有多少和真实值一样
plt.text(1.5, -4, \'Accuracy=%.2f\' % accuracy, fontdict={\'size\': 20, \'color\': \'red\'})
plt.pause(0.1)
plt.ioff() # 停止画图
plt.show()
```
![](img/bce7313d5ac6f2600b62a4962a6daf3a.png)
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/302_classification.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 3.3 – 快速搭建回归神经网络
Torch 中提供了很多方便的途径, 同样是神经网络, 能快则快, 我们看看如何用更简单的方式搭建同样的回归神经网络.
## 快速搭建
我们先看看之前写神经网络时用到的步骤. 我们用 net1  代表这种方式搭建的神经网络.
```
class Net(torch.nn.Module):
def __init__(self, n_feature, n_hidden, n_output):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(n_feature, n_hidden)
self.predict = torch.nn.Linear(n_hidden, n_output)
def forward(self, x):
x = F.relu(self.hidden(x))
x = self.predict(x)
return x
net1 = Net(1, 10, 1) # 这是我们用这种方式搭建的 net1
```
我们用 class 继承了一个 torch 中的神经网络结构, 然后对其进行了修改, 不过还有更快的一招, 用一句话就概括了上面所有的内容!
```
net2 = torch.nn.Sequential(
torch.nn.Linear(1, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
```
我们再对比一下两者的结构:
```
print(net1)
"""
Net (
(hidden): Linear (1 -> 10)
(predict): Linear (10 -> 1)
)
"""
print(net2)
"""
Sequential (
(0): Linear (1 -> 10)
(1): ReLU ()
(2): Linear (10 -> 1)
)
"""
```
我们会发现 net2  多显示了一些内容, 这是为什么呢? 原来他把激励函数也一同纳入进去了, 但是 net1  中, 激励函数实际上是在 forward()  功能中才被调用的. 这也就说明了, 相比 net2 , net1  的好处就是, 你可以根据你的个人需要更加个性化你自己的前向传播过程, 比如(RNN). 不过如果你不需要七七八八的过程, 相信 net2  这种形式更适合你.
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/303_build_nn_quickly.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 3.4 – 保存和恢复模型
训练好了一个模型, 我们当然想要保存它, 留到下次要用的时候直接提取直接用, 这就是这节的内容啦. 我们用回归的神经网络举例实现保存提取.
## 保存
我们快速地建造数据, 搭建网络:
```
torch.manual_seed(1) # reproducible
# 假数据
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1) # x data (tensor), shape=(100, 1)
y = x.pow(2) 0.2*torch.rand(x.size()) # noisy y data (tensor), shape=(100, 1)
x, y = Variable(x, requires_grad=False), Variable(y, requires_grad=False)
def save():
# 建网络
net1 = torch.nn.Sequential(
torch.nn.Linear(1, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
optimizer = torch.optim.SGD(net1.parameters(), lr=0.5)
loss_func = torch.nn.MSELoss()
# 训练
for t in range(100):
prediction = net1(x)
loss = loss_func(prediction, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
```
接下来我们有两种途径来保存
```
torch.save(net1, \'net.pkl\') # 保存整个网络
torch.save(net1.state_dict(), \'net_params.pkl\') # 只保存网络中的参数 (速度快, 占内存少)
```
## 提取网络
这种方式将会提取整个神经网络, 网络大的时候可能会比较慢.
```
def restore_net():
# restore entire net1 to net2
net2 = torch.load(\'net.pkl\')
prediction = net2(x)
```
## 只提取网络参数
这种方式将会提取所有的参数, 然后再放到你的新建网络中.
```
def restore_params():
# 新建 net3
net3 = torch.nn.Sequential(
torch.nn.Linear(1, 10),
torch.nn.ReLU(),
torch.nn.Linear(10, 1)
)
# 将保存的参数复制到 net3
net3.load_state_dict(torch.load(\'net_params.pkl\'))
prediction = net3(x)
```
## 显示结果
调用上面建立的几个功能, 然后出图.
![](img/68f39521fc6853acdf26440e7d5a2861.png)
这样我们就能看出三个网络完全一模一样啦.
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/304_save_reload.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 3.5 – 数据读取 (Data Loader)
DataLoader  是 torch 给你用来包装你的数据的工具. 所以你要讲自己的 (numpy array 或其他) 数据形式装换成 Tensor, 然后再放进这个包装器中. 使用 DataLoader  有什么好处呢? 就是他们帮你有效地迭代数据, 举例:
```
import torch
import torch.utils.data as Data
torch.manual_seed(1) # reproducible
BATCH_SIZE = 5 # 批训练的数据个数
x = torch.linspace(1, 10, 10) # x data (torch tensor)
y = torch.linspace(10, 1, 10) # y data (torch tensor)
# 先转换成 torch 能识别的 Dataset
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)
# 把 dataset 放入 DataLoader
loader = Data.DataLoader(
dataset=torch_dataset, # torch TensorDataset format
batch_size=BATCH_SIZE, # mini batch size
shuffle=True, # 要不要打乱数据 (打乱比较好)
num_workers=2, # 多线程来读数据
)
for epoch in range(3): # 训练所有!整套!数据 3 次
for step, (batch_x, batch_y) in enumerate(loader): # 每一步 loader 释放一小批数据用来学习
# 假设这里就是你训练的地方...
# 打出来一些数据
print(\'Epoch: \', epoch, \'| Step: \', step, \'| batch x: \',
batch_x.numpy(), \'| batch y: \', batch_y.numpy())
"""
Epoch: 0 | Step: 0 | batch x: [ 6\. 7\. 2\. 3\. 1.] | batch y: [ 5\. 4\. 9\. 8\. 10.]
Epoch: 0 | Step: 1 | batch x: [ 9\. 10\. 4\. 8\. 5.] | batch y: [ 2\. 1\. 7\. 3\. 6.]
Epoch: 1 | Step: 0 | batch x: [ 3\. 4\. 2\. 9\. 10.] | batch y: [ 8\. 7\. 9\. 2\. 1.]
Epoch: 1 | Step: 1 | batch x: [ 1\. 7\. 8\. 5\. 6.] | batch y: [ 10\. 4\. 3\. 6\. 5.]
Epoch: 2 | Step: 0 | batch x: [ 3\. 9\. 2\. 6\. 7.] | batch y: [ 8\. 2\. 9\. 5\. 4.]
Epoch: 2 | Step: 1 | batch x: [ 10\. 4\. 8\. 1\. 5.] | batch y: [ 1\. 7\. 3\. 10\. 6.]
"""
```
可以看出, 每步都导出了5个数据进行学习. 然后每个 epoch 的导出数据都是先打乱了以后再导出.
真正方便的还不是这点. 如果我们改变一下 BATCH_SIZE = 8 , 这样我们就知道, step=0  会导出8个数据, 但是, step=1  时数据库中的数据不够 8个, 这时怎么办呢:
```
BATCH_SIZE = 8 # 批训练的数据个数
...
for ...:
for ...:
...
print(\'Epoch: \', epoch, \'| Step: \', step, \'| batch x: \',
batch_x.numpy(), \'| batch y: \', batch_y.numpy())
"""
Epoch: 0 | Step: 0 | batch x: [ 6\. 7\. 2\. 3\. 1\. 9\. 10\. 4.] | batch y: [ 5\. 4\. 9\. 8\. 10\. 2\. 1\. 7.]
Epoch: 0 | Step: 1 | batch x: [ 8\. 5.] | batch y: [ 3\. 6.]
Epoch: 1 | Step: 0 | batch x: [ 3\. 4\. 2\. 9\. 10\. 1\. 7\. 8.] | batch y: [ 8\. 7\. 9\. 2\. 1\. 10\. 4\. 3.]
Epoch: 1 | Step: 1 | batch x: [ 5\. 6.] | batch y: [ 6\. 5.]
Epoch: 2 | Step: 0 | batch x: [ 3\. 9\. 2\. 6\. 7\. 10\. 4\. 8.] | batch y: [ 8\. 2\. 9\. 5\. 4\. 1\. 7\. 3.]
Epoch: 2 | Step: 1 | batch x: [ 1\. 5.] | batch y: [ 10\. 6.]
"""
```
这时, 在 step=1  就只给你返回这个 epoch 中剩下的数据就好了.
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/305_batch_train.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 3.6 – 优化器 (Optimizer)
这节内容主要是用 Torch 实践几种优化器, 这几种优化器具体的优势不会在这个节内容中说了, 所以想快速了解的话, 上面的那个动画链接是很好的去处.
下图就是这节内容对比各种优化器的效果:
![](img/7a8716c377832b032ee24276b7ddcc31.png)
## 伪数据
为了对比各种优化器的效果, 我们需要有一些数据, 今天我们还是自己编一些伪数据, 这批数据是这样的:
![](img/2fc34594dcb247d4a3414467eed4a109.png)
```
import torch
import torch.utils.data as Data
import torch.nn.functional as F
from torch.autograd import Variable
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
LR = 0.01
BATCH_SIZE = 32
EPOCH = 12
# fake dataset
x = torch.unsqueeze(torch.linspace(-1, 1, 1000), dim=1)
y = x.pow(2) 0.1*torch.normal(torch.zeros(*x.size()))
# plot dataset
plt.scatter(x.numpy(), y.numpy())
plt.show()
# 使用上节内容提到的 data loader
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)
loader = Data.DataLoader(dataset=torch_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)
```
## 每个优化器优化一个神经网络
为了对比每一种优化器, 我们给他们各自创建一个神经网络, 但这个神经网络都来自同一个 Net 形式.
```
# 默认的 network 形式
class Net(torch.nn.Module):
def __init__(self):
super(Net, self).__init__()
self.hidden = torch.nn.Linear(1, 20) # hidden layer
self.predict = torch.nn.Linear(20, 1) # output layer
def forward(self, x):
x = F.relu(self.hidden(x)) # activation function for hidden layer
x = self.predict(x) # linear output
return x
# 为每个优化器创建一个 net
net_SGD = Net()
net_Momentum = Net()
net_RMSprop = Net()
net_Adam = Net()
nets = [net_SGD, net_Momentum, net_RMSprop, net_Adam]
```
## 优化器 Optimizer
接下来在创建不同的优化器, 用来训练不同的网络. 并创建一个 loss_func  用来计算误差. 我们用几种常见的优化器, SGD , Momentum , RMSprop , Adam .
```
# different optimizers
opt_SGD = torch.optim.SGD(net_SGD.parameters(), lr=LR)
opt_Momentum = torch.optim.SGD(net_Momentum.parameters(), lr=LR, momentum=0.8)
opt_RMSprop = torch.optim.RMSprop(net_RMSprop.parameters(), lr=LR, alpha=0.9)
opt_Adam = torch.optim.Adam(net_Adam.parameters(), lr=LR, betas=(0.9, 0.99))
optimizers = [opt_SGD, opt_Momentum, opt_RMSprop, opt_Adam]
loss_func = torch.nn.MSELoss()
losses_his = [[], [], [], []] # 记录 training 时不同神经网络的 loss
```
## 训练/出图
接下来训练和 loss 画图.
```
for epoch in range(EPOCH):
print(\\'Epoch: \\', epoch)
for step, (batch_x, batch_y) in enumerate(loader):
b_x = Variable(batch_x) # 务必要用 Variable 包一下
b_y = Variable(batch_y)
# 对每个优化器, 优化属于他的神经网络
for net, opt, l_his in zip(nets, optimizers, losses_his):
output = net(b_x) # get output for every net
loss = loss_func(output, b_y) # compute loss for every net
opt.zero_grad() # clear gradients for next train
loss.backward() # backpropagation, compute gradients
opt.step() # apply gradients
l_his.append(loss.data[0]) # loss recoder
```
![](img/7a8716c377832b032ee24276b7ddcc31.png)
SGD  是最普通的优化器, 也可以说没有加速效果, 而 Momentum  是 SGD  的改良版, 它加入了动量原则. 后面的 RMSprop  又是 Momentum  的升级版. 而 Adam  又是 RMSprop  的升级版. 不过从这个结果中我们看到, Adam  的效果似乎比 RMSprop  要差一点. 所以说并不是越先进的优化器, 结果越佳. 我们在自己的试验中可以尝试不同的优化器, 找到那个最适合你数据/网络的优化器.
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/306_optimizer.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# 高级神经网络结构
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 4.1 – CNN 卷积神经网络
卷积神经网络目前被广泛地用在图片识别上, 已经有层出不穷的应用, 如果你对卷积神经网络还没有特别了解, 我制作的 卷积神经网络 动画简介 (如下) 能让你花几分钟就了解什么是卷积神经网络. 接着我们就一步一步做一个分析手写数字的 CNN 吧.
下面是一个 CNN 最后一层的学习过程, 我们先可视化看看:
![](img/388ca39bf710c8f053f533ad10872cd7.png)
## MNIST手写数据
```
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision # 数据库模块
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
# Hyper Parameters
EPOCH = 1 # 训练整批数据多少次, 为了节约时间, 我们只训练一次
BATCH_SIZE = 50
LR = 0.001 # 学习率
DOWNLOAD_MNIST = True # 如果你已经下载好了mnist数据就写上 Fasle
# Mnist 手写数字
train_data = torchvision.datasets.MNIST(
root=\\'./mnist/\\', # 保存或者提取位置
train=True, # this is training data
transform=torchvision.transforms.ToTensor(), # 转换 PIL.Image or numpy.ndarray 成
# torch.FloatTensor (C x H x W), 训练的时候 normalize 成 [0.0, 1.0] 区间
download=DOWNLOAD_MNIST, # 没下载就下载, 下载了就不用再下了
)
```
![](img/25ed82d9ef8a8b1c9c60445c7c08c732.png)
黑色的地方的值都是0, 白色的地方值大于0.
同样, 我们除了训练数据, 还给一些测试数据, 测试看看它有没有训练好.
```
test_data = torchvision.datasets.MNIST(root=\\'./mnist/\\', train=False)
# 批训练 50samples, 1 channel, 28x28 (50, 1, 28, 28)
train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
# 为了节约时间, 我们测试时只测试前2000个
test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1), volatile=True).type(torch.FloatTensor)[:2000]/255\. # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
test_y = test_data.test_labels[:2000]
```
## CNN模型
和以前一样, 我们用一个 class 来建立 CNN 模型. 这个 CNN 整体流程是 卷积( Conv2d ) -> 激励函数( ReLU ) -> 池化, 向下采样 ( MaxPooling ) -> 再来一遍 -> 展平多维的卷积成的特征图 -> 接入全连接层 ( Linear ) -> 输出
```
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Sequential( # input shape (1, 28, 28)
nn.Conv2d(
in_channels=1, # input height
out_channels=16, # n_filters
kernel_size=5, # filter size
stride=1, # filter movement/step
padding=2, # 如果想要 con2d 出来的图片长宽没有变化, padding=(kernel_size-1)/2 当 stride=1
), # output shape (16, 28, 28)
nn.ReLU(), # activation
nn.MaxPool2d(kernel_size=2), # 在 2x2 空间里向下采样, output shape (16, 14, 14)
)
self.conv2 = nn.Sequential( # input shape (1, 28, 28)
nn.Conv2d(16, 32, 5, 1, 2), # output shape (32, 14, 14)
nn.ReLU(), # activation
nn.MaxPool2d(2), # output shape (32, 7, 7)
)
self.out = nn.Linear(32 * 7 * 7, 10) # fully connected layer, output 10 classes
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = x.view(x.size(0), -1) # 展平多维的卷积图成 (batch_size, 32 * 7 * 7)
output = self.out(x)
return output
cnn = CNN()
print(cnn) # net architecture
"""
CNN (
(conv1): Sequential (
(0): Conv2d(1, 16, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU ()
(2): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))
)
(conv2): Sequential (
(0): Conv2d(16, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU ()
(2): MaxPool2d (size=(2, 2), stride=(2, 2), dilation=(1, 1))
)
(out): Linear (1568 -> 10)
)
"""
```
## 训练
下面我们开始训练, 将  y 都用 Variable 包起来, 然后放入 cnn 中计算 output, 最后再计算误差. 下面代码省略了计算精确度 accuracy 的部分, 如果想细看 accuracy 代码的同学, 请去往我的 github 看全部代码.
```
optimizer = torch.optim.Adam(cnn.parameters(), lr=LR) # optimize all cnn parameters
loss_func = nn.CrossEntropyLoss() # the target label is not one-hotted
# training and testing
for epoch in range(EPOCH):
for step, (x, y) in enumerate(train_loader): # 分配 batch data, normalize x when iterate train_loader
b_x = Variable(x) # batch x
b_y = Variable(y) # batch y
output = cnn(b_x) # cnn output
loss = loss_func(output, b_y) # cross entropy loss
optimizer.zero_grad() # clear gradients for this training step
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
"""
...
Epoch: 0 | train loss: 0.0306 | test accuracy: 0.97
Epoch: 0 | train loss: 0.0147 | test accuracy: 0.98
Epoch: 0 | train loss: 0.0427 | test accuracy: 0.98
Epoch: 0 | train loss: 0.0078 | test accuracy: 0.98
"""
```
最后我们再来取10个数据, 看看预测的值到底对不对:
```
test_output = cnn(test_x[:10])
pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
print(pred_y, \\'prediction number\\')
print(test_y[:10].numpy(), \\'real number\\')
"""
[7 2 1 0 4 1 4 9 5 9] prediction number
[7 2 1 0 4 1 4 9 5 9] real number
"""
```
#### 可视化训练(视频中没有)
这是做完视频后突然想要补充的内容, 因为可视化可以帮助理解, 所以还是有必要提一下. 可视化的代码主要是用 matplotlib  和 sklearn  来完成的, 因为其中我们用到了 T-SNE  的降维手段, 将高维的 CNN 最后一层输出结果可视化, 也就是 CNN forward 代码中的 x = x.view(x.size(0), -1)  这一个结果.
可视化的代码不是重点, 我们就直接展示可视化的结果吧.
![](img/388ca39bf710c8f053f533ad10872cd7.png)
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/401_CNN.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 4.2 – RNN 循环神经网络 (分类 Classification)
循环神经网络让神经网络有了记忆, 对于序列话的数据,循环神经网络能达到更好的效果. 如果你对循环神经网络还没有特别了解, 请观看几分钟的短动画, RNN 动画简介(如下) 和 LSTM(如下) 动画简介 能让你生动理解 RNN. 接着我们就一步一步做一个分析手写数字的 RNN 吧.
## RNN 简介
## LSTM 简介
## MNIST手写数据
```
import torch
from torch import nn
from torch.autograd import Variable
import torchvision.datasets as dsets
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
# Hyper Parameters
EPOCH = 1 # 训练整批数据多少次, 为了节约时间, 我们只训练一次
BATCH_SIZE = 64
TIME_STEP = 28 # rnn 时间步数 / 图片高度
INPUT_SIZE = 28 # rnn 每步输入值 / 图片每行像素
LR = 0.01 # learning rate
DOWNLOAD_MNIST = True # 如果你已经下载好了mnist数据就写上 Fasle
# Mnist 手写数字
train_data = torchvision.datasets.MNIST(
root=\\'./mnist/\\', # 保存或者提取位置
train=True, # this is training data
transform=torchvision.transforms.ToTensor(), # 转换 PIL.Image or numpy.ndarray 成
# torch.FloatTensor (C x H x W), 训练的时候 normalize 成 [0.0, 1.0] 区间
download=DOWNLOAD_MNIST, # 没下载就下载, 下载了就不用再下了
)
```
![](img/99c72d57612c137b62599837526f0e0e.png)
黑色的地方的值都是0, 白色的地方值大于0.
同样, 我们除了训练数据, 还给一些测试数据, 测试看看它有没有训练好.
```
test_data = torchvision.datasets.MNIST(root=\\'./mnist/\\', train=False)
# 批训练 50samples, 1 channel, 28x28 (50, 1, 28, 28)
train_loader = Data.DataLoader(dataset=train_data, batch_size=BATCH_SIZE, shuffle=True)
# 为了节约时间, 我们测试时只测试前2000个
test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1), volatile=True).type(torch.FloatTensor)[:2000]/255\. # shape from (2000, 28, 28) to (2000, 1, 28, 28), value in range(0,1)
test_y = test_data.test_labels[:2000]
```
#### RNN模型
和以前一样, 我们用一个 class 来建立 RNN 模型. 这个 RNN 整体流程是
1. (input0, state0) -> LSTM -> (output0, state1) ;
2. (input1, state1) -> LSTM -> (output1, state2) ;
3.
4. (inputN, stateN)-> LSTM -> (outputN, stateN 1) ;
5. outputN -> Linear -> prediction . 通过LSTM分析每一时刻的值, 并且将这一时刻和前面时刻的理解合并在一起, 生成当前时刻对前面数据的理解或记忆. 传递这种理解给下一时刻分析.
```
class RNN(nn.Module):
def __init__(self):
super(RNN, self).__init__()
self.rnn = nn.LSTM( # LSTM 效果要比 nn.RNN() 好多了
input_size=28, # 图片每行的数据像素点
hidden_size=64, # rnn hidden unit
num_layers=1, # 有几层 RNN layers
batch_first=True, # input & output 会是以 batch size 为第一维度的特征集 e.g. (batch, time_step, input_size)
)
self.out = nn.Linear(64, 10) # 输出层
def forward(self, x):
# x shape (batch, time_step, input_size)
# r_out shape (batch, time_step, output_size)
# h_n shape (n_layers, batch, hidden_size) LSTM 有两个 hidden states, h_n 是分线, h_c 是主线
# h_c shape (n_layers, batch, hidden_size)
r_out, (h_n, h_c) = self.rnn(x, None) # None 表示 hidden state 会用全0的 state
# 选取最后一个时间点的 r_out 输出
# 这里 r_out[:, -1, :] 的值也是 h_n 的值
out = self.out(r_out[:, -1, :])
return out
rnn = RNN()
print(rnn)
"""
RNN (
(rnn): LSTM(28, 64, batch_first=True)
(out): Linear (64 -> 10)
)
"""
```
#### 训练
我们将图片数据看成一个时间上的连续数据, 每一行的像素点都是这个时刻的输入, 读完整张图片就是从上而下的读完了每行的像素点. 然后我们就可以拿出 RNN 在最后一步的分析值判断图片是哪一类了. 下面的代码省略了计算 accuracy 的部分, 你可以在我的 github 中看到全部代码.
```
optimizer = torch.optim.Adam(rnn.parameters(), lr=LR) # optimize all parameters
loss_func = nn.CrossEntropyLoss() # the target label is not one-hotted
# training and testing
for epoch in range(EPOCH):
for step, (x, y) in enumerate(train_loader): # gives batch data
b_x = Variable(x.view(-1, 28, 28)) # reshape x to (batch, time_step, input_size)
b_y = Variable(y) # batch y
output = rnn(b_x) # rnn output
loss = loss_func(output, b_y) # cross entropy loss
optimizer.zero_grad() # clear gradients for this training step
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
"""
...
Epoch: 0 | train loss: 0.0945 | test accuracy: 0.94
Epoch: 0 | train loss: 0.0984 | test accuracy: 0.94
Epoch: 0 | train loss: 0.0332 | test accuracy: 0.95
Epoch: 0 | train loss: 0.1868 | test accuracy: 0.96
"""
```
最后我们再来取10个数据, 看看预测的值到底对不对:
```
test_output = rnn(test_x[:10].view(-1, 28, 28))
pred_y = torch.max(test_output, 1)[1].data.numpy().squeeze()
print(pred_y, \\'prediction number\\')
print(test_y[:10], \\'real number\\')
"""
[7 2 1 0 4 1 4 9 5 9] prediction number
[7 2 1 0 4 1 4 9 5 9] real number
"""
```
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/402_RNN_classifier.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 4.3 – RNN 循环神经网络 (回归 Regression)
循环神经网络让神经网络有了记忆, 对于序列话的数据,循环神经网络能达到更好的效果. 如果你对循环神经网络还没有特别了解, 请观看几分钟的短动画,RNN 动画简介(如下) 和 LSTM(如下)动画简介 能让你生动理解 RNN. 上次我们提到了用 RNN 的最后一个时间点输出来判断之前看到的图片属于哪一类, 这次我们来真的了, 用 RNN 来及时预测时间序列.
![](img/f38868821469cadc36810cfd827511d1.png)
## RNN 简介
## LSTM 简介
## 训练数据
我们要用到的数据就是这样的一些数据, 我们想要用 sin  的曲线预测出 cos  的曲线.
![](img/22309cd02ee52b3a65e1f0022e8b964e.png)
```
import torch
from torch import nn
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
# Hyper Parameters
TIME_STEP = 10 # rnn time step / image height
INPUT_SIZE = 1 # rnn input size / image width
LR = 0.02 # learning rate
DOWNLOAD_MNIST = False # set to True if haven\'t download the data
```
## RNN模型
这一次的 RNN, 我们对每一个 r_out  都得放到 Linear  中去计算出预测的 output , 所以我们能用一个 for loop 来循环计算. **这点是 Tensorflow 望尘莫及的!** 除了这点, 还有一些动态的过程都可以在这个教程中查看, 看看我们的 PyTorch 和 Tensorflow 到底哪家强.
```
class RNN(nn.Module):
def __init__(self):
super(RNN, self).__init__()
self.rnn = nn.RNN( # 这回一个普通的 RNN 就能胜任
input_size=1,
hidden_size=32, # rnn hidden unit
num_layers=1, # 有几层 RNN layers
batch_first=True, # input & output 会是以 batch size 为第一维度的特征集 e.g. (batch, time_step, input_size)
)
self.out = nn.Linear(32, 1)
def forward(self, x, h_state): # 因为 hidden state 是连续的, 所以我们要一直传递这一个 state
# x (batch, time_step, input_size)
# h_state (n_layers, batch, hidden_size)
# r_out (batch, time_step, output_size)
r_out, h_state = self.rnn(x, h_state) # h_state 也要作为 RNN 的一个输入
outs = [] # 保存所有时间点的预测值
for time_step in range(r_out.size(1)): # 对每一个时间点计算 output
outs.append(self.out(r_out[:, time_step, :]))
return torch.stack(outs, dim=1), h_state
rnn = RNN()
print(rnn)
"""
RNN (
(rnn): RNN(1, 32, batch_first=True)
(out): Linear (32 -> 1)
)
"""
```
其实熟悉 RNN 的朋友应该知道, forward  过程中的对每个时间点求输出还有一招使得计算量比较小的. 不过上面的内容主要是为了呈现 PyTorch 在动态构图上的优势, 所以我用了一个 for loop  来搭建那套输出系统. 下面介绍一个替换方式. 使用 reshape 的方式整批计算.
```
def forward(self, x, h_state):
r_out, h_state = self.rnn(x, h_state)
r_out_reshaped = r_out.view(-1, HIDDEN_SIZE) # to 2D data
outs = self.linear_layer(r_out_reshaped)
outs = outs.view(-1, TIME_STEP, INPUT_SIZE) # to 3D data
```
## 训练
下面的代码就能实现动图的效果啦~开心, 可以看出, 我们使用 x 作为输入的 sin 值, 然后 y作为想要拟合的输出, cos 值. 因为他们两条曲线是存在某种关系的, 所以我们就能用 sin 来预测 cos. rnn 会理解他们的关系, 并用里面的参数分析出来这个时刻 sin 曲线上的点如何对应上 cos 曲线上的点.
![](img/f38868821469cadc36810cfd827511d1.png)
```
optimizer = torch.optim.Adam(rnn.parameters(), lr=LR) # optimize all rnn parameters
loss_func = nn.MSELoss()
h_state = None # 要使用初始 hidden state, 可以设成 None
for step in range(60):
start, end = step * np.pi, (step 1)*np.pi # time steps
# sin 预测 cos
steps = np.linspace(start, end, 10, dtype=np.float32)
x_np = np.sin(steps) # float32 for converting torch FloatTensor
y_np = np.cos(steps)
x = Variable(torch.from_numpy(x_np[np.newaxis, :, np.newaxis])) # shape (batch, time_step, input_size)
y = Variable(torch.from_numpy(y_np[np.newaxis, :, np.newaxis]))
prediction, h_state = rnn(x, h_state) # rnn 对于每个 step 的 prediction, 还有最后一个 step 的 h_state
# !! 下一步十分重要 !!
h_state = Variable(h_state.data) # 要把 h_state 重新包装一下才能放入下一个 iteration, 不然会报错
loss = loss_func(prediction, y) # cross entropy loss
optimizer.zero_grad() # clear gradients for this training step
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
```
![](img/04a6040ad4f06a69a92f440ea17dde44.png)
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/403_RNN_regressor.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 4.4 – AutoEncoder (自编码/非监督学习)
神经网络也能进行非监督学习, 只需要训练数据, 不需要标签数据. 自编码就是这样一种形式. 自编码能自动分类数据, 而且也能嵌套在半监督学习的上面, 用少量的有标签样本和大量的无标签样本学习.
这次我们还用 MNIST 手写数字数据来压缩再解压图片.
![](img/c429fb827df769a542339e200e2ea20c.png)
然后用压缩的特征进行非监督分类.
![](img/f790e22ee4be05f818e52467c2f13b37.png)
## 训练数据
自编码只用训练集就好了, 而且只需要训练 training data 的 image, 不用训练 labels.
```
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.utils.data as Data
import torchvision
# 超参数
EPOCH = 10
BATCH_SIZE = 64
LR = 0.005
DOWNLOAD_MNIST = True # 下过数据的话, 就可以设置成 False
N_TEST_IMG = 5 # 到时候显示 5张图片看效果, 如上图一
# Mnist digits dataset
train_data = torchvision.datasets.MNIST(
root=\'./mnist/\',
train=True, # this is training data
transform=torchvision.transforms.ToTensor(), # Converts a PIL.Image or numpy.ndarray to
# torch.FloatTensor of shape (C x H x W) and normalize in the range [0.0, 1.0]
download=DOWNLOAD_MNIST, # download it if you don\'t have it
)
```
![](img/ffeedc89cc695e61aa6e941c1c696a39.png)
这就是一张我们要训练的手写数字 4.
## AutoEncoder
AutoEncoder 形式很简单, 分别是 encoder  和 decoder , 压缩和解压, 压缩后得到压缩的特征值, 再从压缩的特征值解压成原图片.
```
class AutoEncoder(nn.Module):
def __init__(self):
super(AutoEncoder, self).__init__()
# 压缩
self.encoder = nn.Sequential(
nn.Linear(28*28, 128),
nn.Tanh(),
nn.Linear(128, 64),
nn.Tanh(),
nn.Linear(64, 12),
nn.Tanh(),
nn.Linear(12, 3), # 压缩成3个特征, 进行 3D 图像可视化
)
# 解压
self.decoder = nn.Sequential(
nn.Linear(3, 12),
nn.Tanh(),
nn.Linear(12, 64),
nn.Tanh(),
nn.Linear(64, 128),
nn.Tanh(),
nn.Linear(128, 28*28),
nn.Sigmoid(), # 激励函数让输出值在 (0, 1)
)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return encoded, decoded
autoencoder = AutoEncoder()
```
#### 训练
训练, 并可视化训练的过程. 我们可以有效的利用 encoder 和 decoder 来做很多事, 比如这里我们用 decoder 的信息输出看和原图片的对比, 还能用 encoder 来看经过压缩后, 神经网络对原图片的理解. encoder 能将不同图片数据大概的分离开来. 这样就是一个无监督学习的过程.
![](img/c429fb827df769a542339e200e2ea20c.png)
```
optimizer = torch.optim.Adam(autoencoder.parameters(), lr=LR)
loss_func = nn.MSELoss()
for epoch in range(EPOCH):
for step, (x, y) in enumerate(train_loader):
b_x = Variable(x.view(-1, 28*28)) # batch x, shape (batch, 28*28)
b_y = Variable(x.view(-1, 28*28)) # batch y, shape (batch, 28*28)
b_label = Variable(y) # batch label
encoded, decoded = autoencoder(b_x)
loss = loss_func(decoded, b_y) # mean square error
optimizer.zero_grad() # clear gradients for this training step
loss.backward() # backpropagation, compute gradients
optimizer.step() # apply gradients
```
![](img/9e1df524980c8f42ab4353070c2a1b74.png)
## 画3D图
![](img/f790e22ee4be05f818e52467c2f13b37.png)
3D 的可视化图挺有趣的, 还能挪动观看, 更加直观, 好理解.
```
# 要观看的数据
view_data = Variable(train_data.train_data[:200].view(-1, 28*28).type(torch.FloatTensor)/255.)
encoded_data, _ = autoencoder(view_data) # 提取压缩的特征值
fig = plt.figure(2)
ax = Axes3D(fig) # 3D 图
# x, y, z 的数据值
X = encoded_data.data[:, 0].numpy()
Y = encoded_data.data[:, 1].numpy()
Z = encoded_data.data[:, 2].numpy()
values = train_data.train_labels[:200].numpy() # 标签值
for x, y, z, s in zip(X, Y, Z, values):
c = cm.rainbow(int(255*s/9)) # 上色
ax.text(x, y, z, s, backgroundcolor=c) # 标位子
ax.set_xlim(X.min(), X.max())
ax.set_ylim(Y.min(), Y.max())
ax.set_zlim(Z.min(), Z.max())
plt.show()
```
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/404_autoencoder.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 4.5 – DQN 强化学习 (Reinforcement Learning)
Torch 是神经网络库, 那么也可以拿来做强化学习, 之前我用另一个强大神经网络库 Tensorflow来制作了这一个 从浅入深强化学习教程, 你同样也可以用 PyTorch 来实现, 这次我们就举 DQN 的例子, 我对比了我的 Tensorflow DQN 的代码, 发现 PyTorch 写的要简单很多. 如果对 DQN 或者强化学习还没有太多概念, 强烈推荐我的这个DQN动画短片(如下), 让你秒懂DQN. 还有强推这套花了我几个月来制作的[强化学习教程](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/tutorials/machine-learning/reinforcement-learning/)!
<video class="wp-video-shortcode" id="video-135-1" width="760" height="427" preload="metadata" controls="controls"><source type="video/mp4" src="https://www.pytorchtutorial.com/wp-content/uploads/2017/08/cartpole-dqn.mp4?_=1">[https://www.pytorchtutorial.com/wp-content/uploads/2017/08/cartpole-dqn.mp4](https://www.pytorchtutorial.com/wp-content/uploads/2017/08/cartpole-dqn.mp4)</video>
## 模块导入和参数设置
这次除了 Torch 自家模块, 我们还要导入 Gym 环境库模块.
```
import torch
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F
import numpy as np
import gym
# 超参数
BATCH_SIZE = 32
LR = 0.01 # learning rate
EPSILON = 0.9 # 最优选择动作百分比
GAMMA = 0.9 # 奖励递减参数
TARGET_REPLACE_ITER = 100 # Q 现实网络的更新频率
MEMORY_CAPACITY = 2000 # 记忆库大小
env = gym.make(\'CartPole-v0\') # 立杆子游戏
env = env.unwrapped
N_ACTIONS = env.action_space.n # 杆子能做的动作
N_STATES = env.observation_space.shape[0] # 杆子能获取的环境信息数
```
## 神经网络
DQN 当中的神经网络模式, 我们将依据这个模式建立两个神经网络, 一个是现实网络 (Target Net), 一个是估计网络 (Eval Net).
```
class Net(nn.Module):
def __init__(self, ):
super(Net, self).__init__()
self.fc1 = nn.Linear(N_STATES, 10)
self.fc1.weight.data.normal_(0, 0.1) # initialization
self.out = nn.Linear(10, N_ACTIONS)
self.out.weight.data.normal_(0, 0.1) # initialization
def forward(self, x):
x = self.fc1(x)
x = F.relu(x)
actions_value = self.out(x)
return actions_value
```
## DQN体系
简化的 DQN 体系是这样, 我们有两个 net, 有选动作机制, 有存经历机制, 有学习机制.
```
class DQN(object):
def __init__(self):
# 建立 target net 和 eval net 还有 memory
def choose_action(self, x):
# 根据环境观测值选择动作的机制
return action
def store_transition(self, s, a, r, s_):
# 存储记忆
def learn(self):
# target 网络更新
# 学习记忆库中的记忆
```
接下来就是具体的啦, 在 DQN 中每个功能都是怎么做的.
```
class DQN(object):
def __init__(self):
self.eval_net, self.target_net = Net(), Net()
self.learn_step_counter = 0 # 用于 target 更新计时
self.memory_counter = 0 # 记忆库记数
self.memory = np.zeros((MEMORY_CAPACITY, N_STATES * 2 2)) # 初始化记忆库
self.optimizer = torch.optim.Adam(self.eval_net.parameters(), lr=LR) # torch 的优化器
self.loss_func = nn.MSELoss() # 误差公式
def choose_action(self, x):
x = Variable(torch.unsqueeze(torch.FloatTensor(x), 0))
# 这里只输入一个 sample
if np.random.uniform() < EPSILON: # 选最优动作
actions_value = self.eval_net.forward(x)
action = torch.max(actions_value, 1)[1].data.numpy()[0, 0] # return the argmax
else: # 选随机动作
action = np.random.randint(0, N_ACTIONS)
return action
def store_transition(self, s, a, r, s_):
transition = np.hstack((s, [a, r], s_))
# 如果记忆库满了, 就覆盖老数据
index = self.memory_counter % MEMORY_CAPACITY
self.memory[index, :] = transition
self.memory_counter = 1
def learn(self):
# target net 参数更新
if self.learn_step_counter % TARGET_REPLACE_ITER == 0:
self.target_net.load_state_dict(self.eval_net.state_dict())
self.learn_step_counter = 1
# 抽取记忆库中的批数据
sample_index = np.random.choice(MEMORY_CAPACITY, BATCH_SIZE)
b_memory = self.memory[sample_index, :]
b_s = Variable(torch.FloatTensor(b_memory[:, :N_STATES]))
b_a = Variable(torch.LongTensor(b_memory[:, N_STATES:N_STATES 1].astype(int)))
b_r = Variable(torch.FloatTensor(b_memory[:, N_STATES 1:N_STATES 2]))
b_s_ = Variable(torch.FloatTensor(b_memory[:, -N_STATES:]))
# 针对做过的动作b_a, 来选 q_eval 的值, (q_eval 原本有所有动作的值)
q_eval = self.eval_net(b_s).gather(1, b_a) # shape (batch, 1)
q_next = self.target_net(b_s_).detach() # q_next 不进行反向传递误差, 所以 detach
q_target = b_r GAMMA * q_next.max(1)[0] # shape (batch, 1)
loss = self.loss_func(q_eval, q_target)
# 计算, 更新 eval net
self.optimizer.zero_grad()
loss.backward()
self.optimizer.step()
```
## 训练
按照 Qlearning 的形式进行 off-policy 的更新. 我们进行回合制更行, 一个回合完了, 进入下一回合. 一直到他们将杆子立起来很久.
```
dqn = DQN() # 定义 DQN 系统
for i_episode in range(400):
s = env.reset()
while True:
env.render() # 显示实验动画
a = dqn.choose_action(s)
# 选动作, 得到环境反馈
s_, r, done, info = env.step(a)
# 修改 reward, 使 DQN 快速学习
x, x_dot, theta, theta_dot = s_
r1 = (env.x_threshold - abs(x)) / env.x_threshold - 0.8
r2 = (env.theta_threshold_radians - abs(theta)) / env.theta_threshold_radians - 0.5
r = r1 r2
# 存记忆
dqn.store_transition(s, a, r, s_)
if dqn.memory_counter > MEMORY_CAPACITY:
dqn.learn() # 记忆库满了就进行学习
if done: # 如果回合结束, 进入下回合
break
s = s_
```
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/405_DQN_Reinforcement_learning.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 4.6 – GAN (Generative Adversarial Nets 生成对抗网络)
GAN 是一个近几年比较流行的生成网络形式. 对比起传统的生成模型, 他减少了模型限制和生成器限制, 他具有有更好的生成能力. 人们常用假钞鉴定者和假钞制造者来打比喻, 但是我不喜欢这个比喻, 觉得没有真实反映出 GAN 里面的机理.
所以我的一句话介绍 GAN 就是: Generator 是新手画家, Discriminator 是新手鉴赏家, 你是高级鉴赏家. 你将著名画家的品和新手画家的作品都给新手鉴赏家评定, 并告诉新手鉴赏家哪些是新手画家画的, 哪些是著名画家画的, 新手鉴赏家就慢慢学习怎么区分新手画家和著名画家的画, 但是新手画家和新手鉴赏家是好朋友, 新手鉴赏家会告诉新手画家要怎么样画得更像著名画家, 新手画家就能将自己的突然来的灵感 (random noise) 画得更像著名画家. 我用一个短动画形式来诠释了整个过程 (GAN 动画简介) (如下).
下面是本节内容的效果, 绿线的变化是新手画家慢慢学习如何踏上画家之路的过程. 而能被认定为著名的画作在 upper bound  和 lower bound  之间.
![](img/febe7e5dc5d5b9a5004d15c50d3228c1.png)
## 超参数设置
新手画家 (Generator) 在作画的时候需要有一些灵感 (random noise), 我们这些灵感的个数定义为 N_IDEAS . 而一幅画需要有一些规格, 我们将这幅画的画笔数定义一下, N_COMPONENTS  就是一条一元二次曲线(这幅画画)上的点个数. 为了进行批训练, 我们将一整批话的点都规定一下( PAINT_POINTS ).
```
import torch
import torch.nn as nn
from torch.autograd import Variable
import numpy as np
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
np.random.seed(1)
# 超参数
BATCH_SIZE = 64
LR_G = 0.0001 # learning rate for generator
LR_D = 0.0001 # learning rate for discriminator
N_IDEAS = 5 # think of this as number of ideas for generating an art work (Generator)
ART_COMPONENTS = 15 # it could be total point G can draw in the canvas
PAINT_POINTS = np.vstack([np.linspace(-1, 1, ART_COMPONENTS) for _ in range(BATCH_SIZE)])
```
## 著名画家的画
我们需要有很多画是来自著名画家的(real data), 将这些著名画家的画, 和新手画家的画都传给新手鉴赏家, 让鉴赏家来区分哪些是著名画家, 哪些是新手画家的画. 如何区分我们在后面呈现. 这里我们生成一些著名画家的画 (batch 条不同的一元二次方程曲线).
```
def artist_works(): # painting from the famous artist (real target)
a = np.random.uniform(1, 2, size=BATCH_SIZE)[:, np.newaxis]
paintings = a * np.power(PAINT_POINTS, 2) (a-1)
paintings = torch.from_numpy(paintings).float()
return Variable(paintings)
```
下面就是会产生曲线的一个上限和下限.
![](img/a577eb2dc81a64cfc4f6d04ff9a25873.png)
## 神经网络
这里会创建两个神经网络, 分别是 Generator (新手画家), Discriminator(新手鉴赏家). G 会拿着自己的一些灵感当做输入, 输出一元二次曲线上的点 (G 的画).
D 会接收一幅画作 (一元二次曲线), 输出这幅画作到底是不是著名画家的画(是著名画家的画的概率).
```
G = nn.Sequential( # Generator
nn.Linear(N_IDEAS, 128), # random ideas (could from normal distribution)
nn.ReLU(),
nn.Linear(128, ART_COMPONENTS), # making a painting from these random ideas
)
D = nn.Sequential( # Discriminator
nn.Linear(ART_COMPONENTS, 128), # receive art work either from the famous artist or a newbie like G
nn.ReLU(),
nn.Linear(128, 1),
nn.Sigmoid(), # tell the probability that the art work is made by artist
)
```
## 训练
接着我们来同时训练 D 和 G. 训练之前, 我们来看看G作画的原理. G 首先会有些灵感, G_ideas 就会拿到这些随机灵感 (可以是正态分布的随机数), 然后 G 会根据这些灵感画画. 接着我们拿着著名画家的画和 G 的画, 让 D 来判定这两批画作是著名画家画的概率.
```
for step in range(10000):
artist_paintings = artist_works() # real painting from artist
G_ideas = Variable(torch.randn(BATCH_SIZE, N_IDEAS)) # random ideas
G_paintings = G(G_ideas()) # fake painting from G (random ideas)
prob_artist0 = D(artist_paintings) # D try to increase this prob
prob_artist1 = D(G_paintings) # D try to reduce this prob
```
然后计算有多少来之画家的画猜对了, 有多少来自 G 的画猜对了, 我们想最大化这些猜对的次数. 这也就是 log(D(x)) log(1-D(G(z))  在[论文](https://www.pytorchtutorial.com/goto/https://arxiv.org/abs/1406.2661)中的形式. 而因为 torch 中提升参数的形式是最小化误差, 那我们把最大化 score 转换成最小化 loss, 在两个 score 的合的地方加一个符号就好. 而 G 的提升就是要减小 D 猜测 G 生成数据的正确率, 也就是减小 D_score1.
```
D_loss = - torch.mean(torch.log(prob_artist0) torch.log(1\. - prob_artist1))
G_loss = torch.mean(torch.log(1\. - prob_artist1))
```
最后我们在根据 loss  提升神经网络就好了.
```
opt_D.zero_grad()
D_loss.backward(retain_variables=True) # retain_variables 这个参数是为了再次使用计算图纸
opt_D.step()
opt_G.zero_grad()
G_loss.backward()
opt_G.step()
```
上面的全部代码内容在我的 [github](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/406_GAN.py).
## 可视化训练过程
可视化的代码很简单, 在这里就不会意义叙说了, 大家直接看[代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/406_GAN.py) 吧. 在本节的最上面就是这次的动图效果, 最后达到收敛时, 下过如下, G 能成功的根据自己的”灵感”, 产生出一条很像 artist画出的曲线, 而 D 再也没有能力猜出这到底是 G 的画作还是 artist 的画作, 他只能一半时间猜是 G 的, 一半时间猜是 artist的.
![](img/7eca2f8318f254b17ca0bc215ec4f5a0.png)
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# 高阶内容
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 5.1 – 为什么 Torch 是动态的
听说过 Torch 的人都听说了 torch 是动态的, 那他的动态到底是什么呢? 我们用一个 RNN 的例子来展示一下动态计算到底长什么样.
## 动态?静态?
对比静态动态, 我们就得知道谁是静态的. 在流行的神经网络模块中, Tensorflow 就是最典型的静态计算模块. 下图是一种我在强化学习教程中的 Tensorflow 计算图. 也就是说, 大部分时候, 用 Tensorflow 是先搭建好这样一个计算系统, 一旦搭建好了, 就不能改动了 (也有例外, 比如 dynamic_rnn() , 但是总体来说他还是运用了一个静态思维), 所有的计算都会在这种图中流动, 当然很多情况, 这样就够了, 我们不需要改动什么结构. 不动结构当然可以提高效率. 但是一旦计算流程不是静态的, 计算图要变动. 最典型的例子就是 RNN, 有时候 RNN 的 time step 不会一样, 或者在 training 和 testing 的时候, batch_size  和 time_step  也不一样, 这时, Tensorflow 就头疼了, Tensorflow 的人也头疼了. 哈哈, 如果用一个动态计算图的 Torch, 我们就好理解多了, 写起来也简单多了.
![](img/1b292936f4a0c3be1d04e43a994fd48c.png)
## 动态RNN
我们拿 [这一节内容的 RNN](https://www.pytorchtutorial.com/4-3-rnn-for-regression/) 来解释动态计算图. 那节内容的[代码在这](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/11_RNN_regressor.py).
```
..
######################## 前面代码都一样, 下面开始不同 #########################
################ 那节内容的代码结构 (静态 time step) ##########
for step in range(60):
start, end = step * np.pi, (step 1)*np.pi # time steps 都是一样长的
# use sin predicts cos
steps = np.linspace(start, end, 10, dtype=np.float32)
...
################ 这节内容修改代码 (动态 time step) #########
step = 0
for i in range(60):
dynamic_steps = np.random.randint(1, 4) # 随机 time step 长度
start, end = step * np.pi, (step dynamic_steps) * np.pi # different time steps length
step = dynamic_steps
# use sin predicts cos
steps = np.linspace(start, end, 10 * dynamic_steps, dtype=np.float32)
####################### 这下面又一样了 ###########################
print(len(steps)) # print how many time step feed to RNN
x_np = np.sin(steps) # float32 for converting torch FloatTensor
y_np = np.cos(steps)
...
"""
输出的动态time step 长
30
30
10
30
20
30
"""
```
有人会说了, Tensorflow 也有类似的功能呀, 比如说 dynamic_rnn(). 对的, 没错, 不过大家是否想过, 如果我在 Tensorflow 当中定义一个 input 的 placeholder, 这个 placeholder 将会有 (batch, time step, input size) 这几个维度, batch 好说, 随便什么大小都可以, 可是 time step 可是固定的呀, 这可不好改, 或者说改起来很麻烦. 那 PyTorch 中又可以变 batch 又可以变 time step, 这不是很方便吗. 这就体现了动态神经网络的好处.
经过这样的折腾, torch 还能 handle 住, 已经很不容易啦. 所以当你想要处理这些动态计算图的时候, Torch 还是你首选的神经网络模块.
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/501_why_torch_dynamic_graph.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 5.2 – GPU 加速运算
在 GPU 训练可以大幅提升运算速度. 而且 Torch 也有一套很好的 GPU 运算体系. 但是要强调的是:
* 你的电脑里有合适的 GPU 显卡(NVIDIA), 且支持 CUDA 模块. [请在NVIDIA官网查询](https://www.pytorchtutorial.com/goto/https://developer.nvidia.com/cuda-gpus)
* 必须安装 GPU 版的 Torch, [点击这里查看如何安装](https://www.pytorchtutorial.com/1-2-install-pytorch/)
## 用 GPU 训练 CNN
这份 GPU 的代码是依据[之前这份CNN](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/401_CNN.py)的代码修改的. 大概修改的地方包括将数据的形式变成 GPU 能读的形式, 然后将 CNN 也变成 GPU 能读的形式. 做法就是在后面加上 .cuda() , 很简单.
```
...
test_data = torchvision.datasets.MNIST(root=\'./mnist/\', train=False)
# !!!!!!!! 修改 test data 形式 !!!!!!!!! #
test_x = Variable(torch.unsqueeze(test_data.test_data, dim=1)).type(torch.FloatTensor)[:2000].cuda()/255\. # Tensor on GPU
test_y = test_data.test_labels[:2000].cuda()
```
再来把我们的 CNN 参数也变成 GPU 兼容形式.
```
class CNN(nn.Module):
...
cnn = CNN()
# !!!!!!!! 转换 cnn 去 CUDA !!!!!!!!! #
cnn.cuda() # Moves all model parameters and buffers to the GPU.
```
然后就是在 train 的时候, 将每次的training data 变成 GPU 形式. .cuda()
```
for epoch ..:
for step, ...:
# !!!!!!!! 这里有修改 !!!!!!!!! #
b_x = Variable(x).cuda() # Tensor on GPU
b_y = Variable(y).cuda() # Tensor on GPU
...
if step % 50 == 0:
test_output = cnn(test_x)
# !!!!!!!! 这里有修改 !!!!!!!!! #
pred_y = torch.max(test_output, 1)[1].cuda().data.squeeze() # 将操作放去 GPU
accuracy = torch.sum(pred_y == test_y) / test_y.size(0)
...
test_output = cnn(test_x[:10])
# !!!!!!!! 这里有修改 !!!!!!!!! #
pred_y = torch.max(test_output, 1)[1].cuda().data.squeeze() # 将操作放去 GPU
...
print(test_y[:10], \'real number\')
```
大功告成~
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/502_GPU.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 5.3 – Dropout 防止过拟合
过拟合让人头疼, 明明训练时误差已经降得足够低, 可是测试的时候误差突然飙升. 这很有可能就是出现了过拟合现象. 强烈推荐通过(下面)这个动画的形式短时间了解什么是过拟合, 怎么解决过拟合. 下面动图就显示了我们成功缓解了过拟合现象.
![](img/a545e4a49909bd7a80e042fd6d8267cb.png)
## 做点数据
自己做一些伪数据, 用来模拟真实情况. 数据少, 才能凸显过拟合问题, 所以我们就做10个数据点.
![](img/761c210ceb0fdd69c7e0f8bd85e39698.png)
```
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
torch.manual_seed(1) # reproducible
N_SAMPLES = 20
N_HIDDEN = 300
# training data
x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES), 1)
y = x 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))
x, y = Variable(x, requires_grad=False), Variable(y, requires_grad=False)
# test data
test_x = torch.unsqueeze(torch.linspace(-1, 1, N_SAMPLES), 1)
test_y = test_x 0.3*torch.normal(torch.zeros(N_SAMPLES, 1), torch.ones(N_SAMPLES, 1))
test_x, test_y = Variable(test_x, requires_grad=False), Variable(test_y, requires_grad=False)
# show data
plt.scatter(x.data.numpy(), y.data.numpy(), c=\'magenta\', s=50, alpha=0.5, label=\'train\')
plt.scatter(test_x.data.numpy(), test_y.data.numpy(), c=\'cyan\', s=50, alpha=0.5, label=\'test\')
plt.legend(loc=\'upper left\')
plt.ylim((-2.5, 2.5))
plt.show()
```
## 搭建神经网络
我们在这里搭建两个神经网络, 一个没有 dropout, 一个有 dropout. 没有 dropout 的容易出现 过拟合, 那我们就命名为 net_overfitting, 另一个就是 net_dropped.  torch.nn.Dropout(0.5)  这里的 0.5 指的是随机有 50% 的神经元会被关闭/丢弃.
```
net_overfitting = torch.nn.Sequential(
torch.nn.Linear(1, N_HIDDEN),
torch.nn.ReLU(),
torch.nn.Linear(N_HIDDEN, N_HIDDEN),
torch.nn.ReLU(),
torch.nn.Linear(N_HIDDEN, 1),
)
net_dropped = torch.nn.Sequential(
torch.nn.Linear(1, N_HIDDEN),
torch.nn.Dropout(0.5), # drop 50% of the neuron
torch.nn.ReLU(),
torch.nn.Linear(N_HIDDEN, N_HIDDEN),
torch.nn.Dropout(0.5), # drop 50% of the neuron
torch.nn.ReLU(),
torch.nn.Linear(N_HIDDEN, 1),
)
```
## 训练
训练的时候, 这两个神经网络分开训练. 训练的环境都一样.
```
optimizer_ofit = torch.optim.Adam(net_overfitting.parameters(), lr=0.01)
optimizer_drop = torch.optim.Adam(net_dropped.parameters(), lr=0.01)
loss_func = torch.nn.MSELoss()
for t in range(500):
pred_ofit = net_overfitting(x)
pred_drop = net_dropped(x)
loss_ofit = loss_func(pred_ofit, y)
loss_drop = loss_func(pred_drop, y)
optimizer_ofit.zero_grad()
optimizer_drop.zero_grad()
loss_ofit.backward()
loss_drop.backward()
optimizer_ofit.step()
optimizer_drop.step()
```
## 对比测试结果
在这个 for  循环里, 我们加上画测试图的部分. 注意在测试时, 要将网络改成 eval()  形式, 特别是 net_dropped , net_overfitting  改不改其实无所谓. 画好图再改回 train()  模式.
![](img/a545e4a49909bd7a80e042fd6d8267cb.png)
```
...
optimizer_ofit.step()
optimizer_drop.step()
# 接着上面来
if t % 10 == 0: # 每 10 步画一次图
# 将神经网络转换成测试形式, 画好图之后改回 训练形式
net_overfitting.eval()
net_dropped.eval() # 因为 drop 网络在 train 的时候和 test 的时候参数不一样.
...
test_pred_ofit = net_overfitting(test_x)
test_pred_drop = net_dropped(test_x)
...
# 将两个网络改回 训练形式
net_overfitting.train()
net_dropped.train()
```
![](img/c2914d88b6f17b84982e162cf6930a88.png)
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/503_dropout.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
# [莫烦 PyTorch 系列教程] 5.4 – Batch Normalization 批标准化
批标准化通俗来说就是对每一层神经网络进行标准化 (normalize) 处理, 我们知道对输入数据进行标准化能让机器学习有效率地学习. 如果把每一层后看成这种接受输入数据的模式, 那我们何不 “批标准化” 所有的层呢? 具体而且清楚的解释请看到 我(原作者)制作的 什么批标准化 动画简介(推荐)(如下).
那我们就看看下面的两个动图, 这就是在每层神经网络有无 batch normalization 的区别啦.
![](img/6730e1145d2a40e8ced1fda4d453d9c6.png)
![](img/cb2138c3f800c7ca4b5ae38076d09429.png)
## 做点数据
自己做一些伪数据, 用来模拟真实情况. 而且 Batch Normalization (之后都简称BN) 还能有效的控制坏的参数初始化 (initialization), 比如说 ReLU  这种激励函数最怕所有的值都落在附属区间, 那我们就将所有的参数都水平移动一个 -0.2 ( bias_initialization = -0.2 , 来看看 BN 的实力.
![](img/fedaa24e2fcad876c77a2038c2d8d14d.png)
```
import torch
from torch.autograd import Variable
from torch import nn
from torch.nn import init
import torch.utils.data as Data
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
# 超参数
N_SAMPLES = 2000
BATCH_SIZE = 64
EPOCH = 12
LR = 0.03
N_HIDDEN = 8
ACTIVATION = F.tanh # 你可以换 relu 试试
B_INIT = -0.2 # 模拟不好的 参数初始化
# training data
x = np.linspace(-7, 10, N_SAMPLES)[:, np.newaxis]
noise = np.random.normal(0, 2, x.shape)
y = np.square(x) - 5 noise
# test data
test_x = np.linspace(-7, 10, 200)[:, np.newaxis]
noise = np.random.normal(0, 2, test_x.shape)
test_y = np.square(test_x) - 5 noise
train_x, train_y = torch.from_numpy(x).float(), torch.from_numpy(y).float()
test_x = Variable(torch.from_numpy(test_x).float(), volatile=True) # volatile=True 不进行梯度计算
test_y = Variable(torch.from_numpy(test_y).float(), volatile=True)
train_dataset = Data.TensorDataset(data_tensor=train_x, target_tensor=train_y)
train_loader = Data.DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)
# show data
plt.scatter(train_x.numpy(), train_y.numpy(), c=\'#FF9359\', s=50, alpha=0.2, label=\'train\')
plt.legend(loc=\'upper left\')
plt.show()
```
## 搭建神经网络
这里就教你如何构建带有 BN 的神经网络的. BN 其实可以看做是一个 layer ( BN layer ). 我们就像平时加层一样加 BN layer  就好了. 注意, 我还对输入数据进行了一个 BN 处理, 因为如果你把输入数据看出是 从前面一层来的输出数据, 我们同样也能对她进行 BN.
```
class Net(nn.Module):
def __init__(self, batch_normalization=False):
super(Net, self).__init__()
self.do_bn = batch_normalization
self.fcs = [] # 太多层了, 我们用 for loop 建立
self.bns = []
self.bn_input = nn.BatchNorm1d(1, momentum=0.5) # 给 input 的 BN
for i in range(N_HIDDEN): # 建层
input_size = 1 if i == 0 else 10
fc = nn.Linear(input_size, 10)
setattr(self, \'fc%i\' % i, fc) # 注意! pytorch 一定要你将层信息变成 class 的属性! 我在这里花了2天时间发现了这个 bug
self._set_init(fc) # 参数初始化
self.fcs.append(fc)
if self.do_bn:
bn = nn.BatchNorm1d(10, momentum=0.5)
setattr(self, \'bn%i\' % i, bn) # 注意! pytorch 一定要你将层信息变成 class 的属性! 我在这里花了2天时间发现了这个 bug
self.bns.append(bn)
self.predict = nn.Linear(10, 1) # output layer
self._set_init(self.predict) # 参数初始化
def _set_init(self, layer): # 参数初始化
init.normal(layer.weight, mean=0., std=.1)
init.constant(layer.bias, B_INIT)
def forward(self, x):
pre_activation = [x]
if self.do_bn: x = self.bn_input(x) # 判断是否要加 BN
layer_input = [x]
for i in range(N_HIDDEN):
x = self.fcs[i](x)
pre_activation.append(x) # 为之后出图
if self.do_bn: x = self.bns[i](x) # 判断是否要加 BN
x = ACTIVATION(x)
layer_input.append(x) # 为之后出图
out = self.predict(x)
return out, layer_input, pre_activation
# 建立两个 net, 一个有 BN, 一个没有
nets = [Net(batch_normalization=False), Net(batch_normalization=True)]
```
## 训练
训练的时候, 这两个神经网络分开训练. 训练的环境都一样.
```
opts = [torch.optim.Adam(net.parameters(), lr=LR) for net in nets]
loss_func = torch.nn.MSELoss()
losses = [[], []] # 每个网络一个 list 来记录误差
for epoch in range(EPOCH):
print(\'Epoch: \', epoch)
for step, (b_x, b_y) in enumerate(train_loader):
b_x, b_y = Variable(b_x), Variable(b_y)
for net, opt in zip(nets, opts): # 训练两个网络
pred, _, _ = net(b_x)
loss = loss_func(pred, b_y)
opt.zero_grad()
loss.backward()
opt.step() # 这也会训练 BN 里面的参数
```
## 画图
这个教程有几张图要画, 首先我们画训练时的动态图. 我单独定义了一个画动图的功能 plot_histogram() , 因为不是重点, 所以代码的具体细节请看我的 [github](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/504_batch_normalization.py),
![](img/cb2138c3f800c7ca4b5ae38076d09429.png)
```
f, axs = plt.subplots(4, N_HIDDEN 1, figsize=(10, 5))
def plot_histogram(l_in, l_in_bn, pre_ac, pre_ac_bn):
...
for epoch in range(EPOCH):
layer_inputs, pre_acts = [], []
for net, l in zip(nets, losses):
# 一定要把 net 的设置成 eval 模式, eval下的 BN 参数会被固定
net.eval()
pred, layer_input, pre_act = net(test_x)
l.append(loss_func(pred, test_y).data[0])
layer_inputs.append(layer_input)
pre_acts.append(pre_act)
# 收集好信息后将 net 设置成 train 模式, 继续训练
net.train()
plot_histogram(*layer_inputs, *pre_acts) # plot histogram
# 后面接着之前 for loop 中的代码来
for step, (b_x, b_y) in enumerate(train_loader):
...
```
后面还有两张图, 一张是预测曲线, 一张是误差变化曲线, 具体代码不在这里呈现, 想知道如何画图的朋友, 请参考我的 [github](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/504_batch_normalization.py)
## 对比结果
首先来看看这次对比的两个激励函数是长什么样:
![](img/7345448d48d8d6c6c1b03fda91334e3e.png)
然后我们来对比使用不同激励函数的结果.
![](img/4d69c0a49be83a66f774caf12e64c3a7.png)
![](img/25959870b2b1e7d6fd61229cb20bed29.png)
![](img/03f4667f9aae4918338a56b74865a389.png)
上面是使用 relu  激励函数的结果, 我们可以看到, 没有使用 BN 的误差要高, 线条不能拟合数据, 原因是我们有一个 “Bad initialization”, 初始 bias = -0.2 , 这一招, 让 relu  无法捕捉到在负数区间的输入值. 而有了 BN, 这就不成问题了.
![](img/bbd3ae66e0235fac8745c37306e74a52.png)
![](img/90e1ab65f286c889d94c9f6c57d596c9.png)
![](img/c42f3cec435a83431d5a1737e99b8b8c.png)
上面结果是使用 tanh  作为激励函数的结果, 可以看出, 不好的初始化, 让输入数据在激活前分散得非常离散, 而有了 BN, 数据都被收拢了. 收拢的数据再放入激励函数就能很好地利用激励函数的非线性. 而且可以看出没有 BN 的数据让激活后的结果都分布在 tanh  的两端, 而这两端的梯度又非常的小, 是的后面的误差都不能往前传, 导致神经网络死掉了.
所以这也就是在我 [github 代码](https://www.pytorchtutorial.com/goto/https://github.com/MorvanZhou/PyTorch-Tutorial/blob/master/tutorial-contents/504_batch_normalization.py) 中的每一步的意义啦.
文章来源:[莫烦](https://www.pytorchtutorial.com/goto/https://morvanzhou.github.io/)
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册