神经网络框架-Pytorch使用介绍
2018年06月04日 21:23:28 zzulp 阅读数 35888更多
个人分类: ML
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zzulp/article/details/80573331
Pytorch上手使用
近期学习了另一个深度学习框架库Pytorch,对学习进行一些总结,方便自己回顾。
Pytorch是torch的python版本,是由Facebook开源的神经网络框架。与Tensorflow的静态计算图不同,pytorch的计算图是动态的,可以根据计算需要实时改变计算图。
1 安装
如果已经安装了cuda8,则使用pip来安装pytorch会十分简单。若使用其他版本的cuda,则需要下载官方释放出来对应的安装包。具体安装地址参见官网的首页。
目前最新稳定版本为0.4.0。上个版本0.3.0的文档有中文版,见中文文档。
pip install torch torchvision # for python2.7
pip3 install torch torchvision # for python3
- 1
- 2
2 概述
理解pytorch的基础主要从以下三个方面
- Numpy风格的Tensor操作。pytorch中tensor提供的API参考了Numpy的设计,因此熟悉Numpy的用户基本上可以无缝理解,并创建和操作tensor,同时torch中的数组和Numpy数组对象可以无缝的对接。
- 变量自动求导。在一序列计算过程形成的计算图中,参与的变量可以方便的计算自己对目标函数的梯度。这样就可以方便的实现神经网络的后向传播过程。
- 神经网络层与损失函数优化等高层封装。网络层的封装存在于torch.nn模块,损失函数由torch.nn.functional模块提供,优化函数由torch.optim模块提供。
因此下面的内容也主要围绕这三个方面来介绍。第3节介绍张量的操作,第4节介绍自动求导,第5节介绍神经网络层等的封装,第6,7节简单介绍损失函数与优化方法。这三部分相对重要。后续的第8节介绍介绍数据集及torchvision,第9节介绍训练过程可视的工具,第10节通过相对完整的示例代码展示pytorch中如何解决MNIST与CIFAR10的分类。
3 Tensor(张量)
Tensor是神经网络框架中重要的基础数据类型,可以简单理解为N维数组的容器对象。tensor之间的通过运算进行连接,从而形成计算图。
3.1 Tensor类型
Torch 定义了七种 CPU tensor 类型和八种 GPU tensor 类型:
Data type | CPU tensor | GPU tensor |
---|---|---|
32-bit floating point | torch.FloatTensor | torch.cuda.FloatTensor |
64-bit floating point | torch.DoubleTensor | torch.cuda.DoubleTensor |
16-bit floating point | torch.HalfTensor | torch.cuda.HalfTensor |
8-bit integer (unsigned) | torch.ByteTensor | torch.cuda.ByteTensor |
8-bit integer (signed) | torch.CharTensor | torch.cuda.CharTensor |
16-bit integer (signed) | torch.ShortTensor | torch.cuda.ShortTensor |
32-bit integer (signed) | torch.IntTensor | torch.cuda.IntTensor |
64-bit integer (signed) | torch.LongTensor | torch.cuda.LongTensor |
通常情况下使用Tensor类的构造函数返回的是FloatTensor类型对象,可通过在对象上调用cuda()返回一个新的cuda.FloatTensor类型的对象。
torch模块内提供了操作tensor的接口,而Tensor类型的对象上也设计了对应了接口。例如torch.add()与tensor.add()等价。需要注意的是这些接口都采用创建一个新对象返回的形式。如果想就地修改一个tensor对象,需要使用加后缀下划线的方法。例如x.add_(y),将修改x。Tensor类的构建函数支持从列表或ndarray等类型进行构建。默认tensor为FloatTensor。
下面的几节简单的描述重要的操作tensor的方法。
3.1 tensor的常见创建接口
方法名 | 说明 |
---|---|
Tensor() | 直接从参数构造一个的张量,参数支持list,numpy数组 |
eye(row, column) | 创建指定行数,列数的二维单位tensor |
linspace(start,end,count) | 在区间[s,e]上创建c个tensor |
logspace(s,e,c) | 在区间[10^s, 10^e]上创建c个tensor |
ones(*size) | 返回指定shape的张量,元素初始为1 |
zeros(*size) | 返回指定shape的张量,元素初始为0 |
ones_like(t) | 返回与t的shape相同的张量,且元素初始为1 |
zeros_like(t) | 返回与t的shape相同的张量,且元素初始为0 |
arange(s,e,sep) | 在区间[s,e)上以间隔sep生成一个序列张量 |
3.2 随机采样
方法名 | 说明 |
---|---|
rand(*size) | 在区间[0,1)返回一个均匀分布的随机数张量 |
uniform(s,e) | 在指定区间[s,e]上生成一个均匀分布的张量 |
randn(*size) | 返回正态分布N(0,1)取样的随机数张量 |
normal(means, std) | 返回一个正态分布N(means, std) |
3.3 序列化
方法名 | 说明 |
---|---|
save(obj, path) | 张量对象的保存,通过pickle进行 |
load(path) | 从文件中反序列化一个张量对象 |
3.4 数学操作
这些方法均为逐元素处理方法
方法名 | 说明 |
---|---|
abs | 绝对值 |
add | 加法 |
addcdiv(t, v, t1, t2) | t1与t2的按元素除后,乘v加t |
addcmul(t, v, t1, t2) | t1与t2的按元素乘后,乘v加t |
ceil | 向上取整 |
floor | 向下取整 |
clamp(t, min, max) | 将张量元素限制在指定区间 |
exp | 指数 |
log | 对数 |
pow | 幂 |
mul | 逐元素乘法 |
neg | 取反 |
sigmoid | |
sign | 取符号 |
sqrt | 开根号 |
tanh |
注:这些操作均创建新的tensor,如果需要就地操作,可以使用这些方法的下划线版本,例如abs_。
3.5 归约方法
方法名 | 说明 |
---|---|
cumprod(t, axis) | 在指定维度对t进行累积 |
cumsum | 在指定维度对t进行累加 |
dist(a,b,p=2) | 返回a,b之间的p阶范数 |
mean | 均值 |
median | 中位数 |
std | 标准差 |
var | 方差 |
norm(t,p=2) | 返回t的p阶范数 |
prod(t) | 返回t所有元素的积 |
sum(t) | 返回t所有元素的和 |
3.6 比较方法
方法名 | 说明 |
---|---|
eq | 比较tensor是否相等,支持broadcast |
equal | 比较tensor是否有相同的shape与值 |
ge/le | 大于/小于比较 |
gt/lt | 大于等于/小于等于比较 |
max/min(t,axis) | 返回最值,若指定axis,则额外返回下标 |
topk(t,k,axis) | 在指定的axis维上取最高的K个值 |
3.7 其他操作
方法名 | 说明 |
---|---|
cat(iterable, axis) | 在指定的维度上拼接序列 |
chunk(tensor, c, axis) | 在指定的维度上分割tensor |
squeeze(input,dim) | 将张量维度为1的dim进行压缩,不指定dim则压缩所有维度为1的维 |
unsqueeze(dim) | squeeze操作的逆操作 |
transpose(t) | 计算矩阵的转置换 |
cross(a, b, axis) | 在指定维度上计算向量积 |
diag | 返回对角线元素 |
hist(t, bins) | 计算直方图 |
trace | 返回迹 |
3.8 矩阵操作
方法名 | 说明 |
---|---|
dot(t1, t2) | 计算张量的内积 |
mm(t1, t2) | 计算矩阵乘法 |
mv(t1, v1) | 计算矩阵与向量乘法 |
qr(t) | 计算t的QR分解 |
svd(t) | 计算t的SVD分解 |
3.9 tensor对象的方法
方法名 | 作用 |
---|---|
size() | 返回张量的shape属性值 |
numel(input) | 计算tensor的元素个数 |
view(*shape) | 修改tensor的shape,与np.reshape类似,view返回的对象共享内存 |
resize | 类似于view,但在size超出时会重新分配内存空间 |
item | 若为单元素tensor,则返回pyton的scalar |
from_numpy | 从numpy数据填充 |
numpy | 返回ndarray类型 |
3.10 tensor内部
tensor对象由两部分组成,tensor的信息与存储,storage封装了真正的data,可以由多个tensor共享。大多数操作只是修改tensor的信息,而不修改storage部分。这样达到效率与性能的提升。
3.11 使用pytorch进行线性回归
import torch
import torch.optim as optim
import matplotlib.pyplot as plt
def get_fake_data(batch_size=32):
''' y=x*2+3 '''
x = torch.randn(batch_size, 1) * 20
y = x * 2 + 3 + torch.randn(batch_size, 1)
return x, y
x, y = get_fake_data()
class LinerRegress(torch.nn.Module):
def __init__(self):
super(LinerRegress, self).__init__()
self.fc1 = torch.nn.Linear(1, 1)
def forward(self, x):
return self.fc1(x)
net = LinerRegress()
loss_func = torch.nn.MSELoss()
optimzer = optim.SGD(net.parameters())
for i in range(40000):
optimzer.zero_grad()
out = net(x)
loss = loss_func(out, y)
loss.backward()
optimzer.step()
w, b = [param.item() for param in net.parameters()]
print w, b # 2.01146, 3.184525
# 显示原始点与拟合直线
plt.scatter(x.squeeze().numpy(), y.squeeze().numpy())
plt.plot(x.squeeze().numpy(), (x*w + b).squeeze().numpy())
plt.show()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
从这里的代码可以发现,pytorch需要我们自己实现各轮更新,并手动调用反向传播以及更新参数,此外也没有提供评估及预测功能。相对于Keras这种高层的封装,pytorch需要我们了解更多的低层细节。
4 自动求导
tensor对象通过一系列的运算可以组成动态图,对于每个tensor对象,有下面几个变量控制求导的属性。
变量 | 作用 |
---|---|
requirs_grad | 默认为False,表示变量是否需要计算导数 |
grad_fn | 变量的梯度函数 |
grad | 变量对应的梯度 |
在0.3.0版本中,自动求导还需要借助于Variable类来完成,在0.4.0版本中,Variable已经被废除了,tensor自身即可完成这一过程。
import torch
x = torch.randn((4,4), requires_grad=True)
y = 2*x
z = y.sum()
print z.requires_grad # True
z.backward()
print x.grad
'''
tensor([[ 2., 2., 2., 2.],
[ 2., 2., 2., 2.],
[ 2., 2., 2., 2.],
[ 2., 2., 2., 2.]])
'''
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
5 创建神经网络
5.1 神经网络层
torch.nn模块提供了创建神经网络的基础构件,这些层都继承自Module类。下面我们简单看下如何实现Liner层。
class Liner(torch.nn.Module):
def __init__(self,in_features, out_features, bias=True):
super(Liner, self).__init__()
self.weight = torch.nn.Parameter(torch.randn(out_features, in_features))
if bias:
self.bias = torch.nn.Parameter(torch.randn(out_features))
def forward(self, x):
x = x.mm(self.weight)
if self.bias:
x = x + self.bias.expand_as(x)
return x
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
下面表格中列出了比较重要的神经网络层组件。对应的在nn.functional模块中,提供这些层对应的函数实现。通常对于可训练参数的层使用module,而对于不需要训练参数的层如softmax这些,可以使用functional中的函数。
Layer对应的类 | 功能说明 |
---|---|
Linear(in_dim, out_dim, bias=True) | 提供了进行线性变换操作的功能 |
Dropout(p) | Dropout层,有2D,3D的类型 |
Conv2d(in_c, out_c, filter_size, stride, padding) | 二维卷积层,类似的有Conv1d,Conv3d |
ConvTranspose2d() | |
MaxPool2d(filter_size, stride, padding) | 二维最大池化层 |
MaxUnpool2d(filter, stride, padding) | 逆过程 |
AvgPool2d(filter_size, stride, padding) | 二维平均池化层 |
FractionalMaxPool2d | 分数最大池化 |
AdaptiveMaxPool2d([h,w]) | 自适应最大池化 |
AdaptiveAvgPool2d([h,w]) | 自自应平均池化 |
ZeroPad2d(padding_size) | 零填充边界 |
ConstantPad2d(padding_size,const) | 常量填充边界 |
ReplicationPad2d(ps) | 复制填充边界 |
BatchNorm1d() | 对2维或3维小批量数据进行标准化操作 |
RNN(in_dim, hidden_dim, num_layers, activation, dropout, bidi, bias) | 构建RNN层 |
RNNCell(in_dim, hidden_dim, bias, activation) | RNN单元 |
LSTM(in_dim, hidden_dim, num_layers, activation, dropout, bidi, bias) | 构建LSTM层 |
LSTMCell(in_dim, hidden_dim, bias, activation) | LSTM单元 |
GRU(in_dim, hidden_dim, num_layers, activation, dropout, bidi, bias) | 构建GRU层 |
GRUCell(in_dim, hidden_dim, bias, activation) | GRU单元 |
5.2 非线性激活层
激活层类名 | 作用 |
---|---|
ReLU(inplace=False) | Relu激活层 |
Sigmoid | Sigmoid激活层 |
Tanh | Tanh激活层 |
Softmax | Softmax激活层 |
Softmax2d | |
LogSoftmax | LogSoftmax激活层 |
5.3 容器类型
容器类型 | 功能 |
---|---|
Module | 神经网络模块的基类 |
Sequential | 序列模型,类似于keras,用于构建序列型神经网络 |
ModuleList | 用于存储层,不接受输入 |
Parameters(t) | 模块的属性,用于保存其训练参数 |
ParameterList | 参数列表 |
下面的代码演示了使用容器型模块的方式。
# 方法1
model1 = nn.Sequential()
model.add_module('fc1', nn.Linear(3,4))
model.add_module('fc2', nn.Linear(4,2))
model.add_module('output', nn.Softmax(2))
# 方法2
model2 = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
# 方法3
model3 = nn.ModuleList([nn.Linear(3,4), nn.ReLU(), nn.Linear(4,2)])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
5.4 其他层
容器类型 | 功能 |
---|---|
Embedding(vocab_size, feature_dim) | 词向量层 |
Embeddingbag |
5.5 模型的保存
前面我们知道tensor可以通过save与load方法实现序列化与反序列化。由tensor组成的网络同样也可以方便的保存。不过通常没有必要完全保存网络模块对象,只需要保存各层的权重数据即可,这些数据保存在模块的state_dict字典中,因此只需要序列化这个词典。
# 模型的保存
torch.save(model.state_dict, 'path')
# 模型的加载
model.load_state_dict('path)
- 1
- 2
- 3
- 4
5.6 实现LeNet神经网络
torch.nn.Module提供了神经网络的基类,当实现神经网络时需要继承自此模块,并在初始化函数中创建网络需要包含的层,并实现forward函数完成前向计算,网络的反向计算会由自动求导机制处理。
下面的示例代码创建了LeNet的卷积神经网络。通常将需要训练的层写在init函数中,将参数不需要训练的层在forward方法里调用对应的函数来实现相应的层。
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
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 = F.max_pool2d(F.relu(self.conv1(x)), 2)
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
6 损失函数与优化方法
6.1 损失函数
torch.nn模块中提供了许多损失函数类,这里列出几种相对常见的。
类名 | 功能 |
---|---|
MSELoss | 均方差损失 |
CrossEntropyLoss | 交叉熵损失 |
NLLLoss | 负对数似然损失 |
PoissonNLLLoss | 带泊松分布的负对数似然损失 |
6.2 优化方法
由torch.optim模块提供支持
类名 | 功能 |
---|---|
SGD(params, lr=0.1, momentum=0, dampening=0, weight_decay=0, nesterov=False) | 随机梯度下降法 |
Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False) | Adam |
RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False) | RMSprop |
Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0) | Adadelta |
Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0) | Adagrad |
lr_scheduler.ReduceLROnPlateau(optimizer, mode=’min’, factor=0.1, patience=10, verbose=False, threshold=0.0001, threshold_mode=’rel’, cooldown=0, min_lr=0, eps=1e-08) | 学习率的控制 |
在神经网络的性能调优中,常见的作法是对不对层的网络设置不同的学习率。
class model(nn.Module):
def __init__():
super(model,self).__init__()
self.base = Sequencial()
# code for base sub module
self.classifier = Sequencial()
# code for classifier sub module
optim.SGD([
{'params': model.base.parameters()},
{'params': model.classifier.parameters(), 'lr': 1e-3}
], lr=1e-2, momentum=0.9)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
6.3 参数初始化
良好的初始化可以让模型快速收敛,有时甚至可以决定模型是否能训练成功。Pytorch中的参数通常有默认的初始化策略,不需要我们自己指定,但框架仍然留有相应的接口供我们来调整初始化方法。
初始化方法 | 说明 |
---|---|
xavier_uniform_ | |
xavier_normal_ | |
kaiming_uniform_ |
from torch.nn import init
# net的类定义
...
# 初始化各层权重
for name, params in net.named_parameters():
init.xavier_normal(param[0])
init.xavier_normal(param[1])
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
7 数据集与数据加载器
7.1 DataSet与DataLoader
torch.util.data模块提供了DataSet类用于描述一个数据集。定义自己的数据集需要继承自DataSet类,且实现__getitem__()与__len__()方法。__getitem__方法返回指定索引处的tensor与其对应的label。
为了支持数据的批量及随机化操作,可以使用data模块下的DataLoader类型来返回一个加载器:
DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0)
7.2 torchvision简介
torchvision是配合pytorch的独立计算机视觉数据集的工具库,下面介绍其中常用的数据集类型。
torchvision.datasets.ImageFolder(dir, transform, label_map,loader)
提供了从一个目录初始化出来一个图片数据集的便捷方法。
要求目录下的图片分类存放,每一类的图片存储在以类名为目录名的目录下,方法会将每个类名映射到唯一的数字上,如果你对数字有要求,可以用label_map来定义目录名到数字的映射。
torchvision.datasets.DatasetFolder(dir,transform, label_map, loader, extensions)
提供了从一个目录初始化一般数据集的便捷方法。目录下的数据分类存放,每类数据存储在class_xxx命名的目录下。
此外torchvision.datasets下实现了常用的数据集,如CIFAR-10/100, ImageNet, COCO, MNIST, LSUN等。
除了数据集,torchvision的model模块提供了常见的模型实现,如Alex-Net, VGG,Inception, Resnet等。
7.3 torchvision提供的图像变换工具
torchvision的transforms模块提供了对PIL.Image对象和Tensor对象的常见操作。如果需要连续应用多个变换,可以使用Compose对象组装多个变换。
转换操作 | 说明 |
---|---|
Scale | PIL图片进行缩放 |
CenterCrop | PIL图片从中心位置剪切 |
Pad | PIL图片填充 |
ToTensor | PIL图片转换为Tensor且归一化到[0,1] |
Normalize | Tensor标准化 |
ToPILImage | 将Tensor转为PIL表示 |
import torchvision.tranforms as Trans
tranform = Trans.Compose([
T.Scale(28*28),
T.ToTensor(),
T.Normalize([0.5],[0.5])
])
- 1
- 2
- 3
- 4
- 5
- 6
8 训练过程可视化
8.1 使用Tensorboard
通过使用第三方库tensorboard_logger,将训练过程中的数据保存为日志,然后便可以通过Tensorboard来查看这些数据了。其功能支持相对有限,这里不做过多介绍。
8.2 使用visdom
visdom是facebook开源的一个可视工具,可以用来完成pytorch训练过程的可视化。
安装可以使用pip install visdom
启动类似于tb,在命令行上执行:python -m visdom.server
服务启动后可以使用浏览器打开http://127.0.0.1:8097/即可看到主面板。
visdom的绘图API类似于plot,通过API将绘图数据发送到基于tornado的web服务器上并显示在浏览器中。更详细内容参见visdom的github主页
9 GPU及并行支持
为了能在GPU上运行,Tensor与Module都需要转换到cuda模式下。
import torch
import torchvision
t = torch.Tensor(3,4)
print t.is_cuda #False
t = t.cuda(0)
print t.is_cuda #True
net = torchvision.model.AlexNet()
net.cuda(0)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如果有多块显卡,可以通过cuda(device_id)来将tensor分到不同的GPU上以达到负载的均衡。
另一种比较省事的做法是调用torch.set_default_tensor_type使程序默认使用某种cuda的tensor。或者使用torch.cuda.set_device(id)指定使用某个GPU。
10 示例:Pytorch实现CIFAR10与MNIST分类
关于cifar10与mnist数据集不再进行解释了。这里的Model类实现的二者的共同的任务,借鉴了keras的接口方式,Model类提供了train与evaluat方法,并没有实现序列模型的添加方法以及predict方法。此外设定损失函数与优化函数时,也只是简单的全部实例化出来,根据参数选择其中的一个,这里完全可以根据参数动态创建。
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
class Model:
def __init__(self, net, cost, optimist):
self.net = net
self.cost = self.create_cost(cost)
self.optimizer = self.create_optimizer(optimist)
pass
def create_cost(self, cost):
support_cost = {
'CROSS_ENTROPY': nn.CrossEntropyLoss(),
'MSE': nn.MSELoss()
}
return support_cost[cost]
def create_optimizer(self, optimist, **rests):
support_optim = {
'SGD': optim.SGD(self.net.parameters(), lr=0.1, **rests),
'ADAM': optim.Adam(self.net.parameters(), lr=0.01, **rests),
'RMSP':optim.RMSprop(self.net.parameters(), lr=0.001, **rest)
}
return support_optim[optimist]
def train(self, train_loader, epoches=3):
for epoch in range(epoches):
running_loss = 0.0
for i, data in enumerate(train_loader, 0):
inputs, labels = data
self.optimizer.zero_grad()
# forward + backward + optimize
outputs = self.net(inputs)
loss = self.cost(outputs, labels)
loss.backward()
self.optimizer.step()
running_loss += loss.item()
if i % 100 == 0:
print('[epoch %d, %.2f%%] loss: %.3f' %
(epoch + 1, (i + 1)*1./len(train_loader), running_loss / 100))
running_loss = 0.0
print('Finished Training')
def evaluate(self, test_loader):
print('Evaluating ...')
correct = 0
total = 0
with torch.no_grad(): # no grad when test and predict
for data in test_loader:
images, labels = data
outputs = self.net(images)
predicted = torch.argmax(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the test images: %d %%' % (100 * correct / total))
classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
def cifar_load_data():
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)
return trainloader, testloader
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
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 = F.max_pool2d(F.relu(self.conv1(x)), 2)
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
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
def mnist_load_data():
transform = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize([0,], [1,])])
trainset = torchvision.datasets.MNIST(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=32,
shuffle=True, num_workers=2)
testset = torchvision.datasets.MNIST(root='./data', train=False,
download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=32,shuffle=True, num_workers=2)
return trainloader, testloader
class MnistNet(torch.nn.Module):
def __init__(self):
super(MnistNet, self).__init__()
self.fc1 = torch.nn.Linear(28*28, 512)
self.fc2 = torch.nn.Linear(512, 512)
self.fc3 = torch.nn.Linear(512, 10)
def forward(self, x):
x = x.view(-1, 28*28)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = F.softmax(self.fc3(x), dim=1)
return x
if __name__ == '__main__':
# train for mnist
net = MnistNet()
model = Model(net, 'CROSS_ENTROPY', 'RMSP')
train_loader, test_loader = mnist_load_data()
model.train(train_loader)
model.evaluate(test_loader)
# train for cifar
net = LeNet()
model = Model(net, 'CROSS_ENTROPY', 'RMSP')
train_loader, test_loader = cifar_load_data()
model.train(train_loader)
model.evaluate(test_loader)
==================================================================================================================================================================
【深度学习】Pytorch 学习笔记
目录
- Pytorch Leture 05: Linear Rregression in the Pytorch Way
- Logistic Regression 逻辑回归 - 二分类
- Lecture07: How to make netural network wide and deep ?
- Lecture 08: Pytorch DataLoader
- Lecture 09: softmax Classifier
- Lecture 10 : basic CNN
- Lecture 11 Advanced CNN
- Lecture 12: RNN
学习网址:https://www.youtube.com/watch?v=ogZi5oIo4fI
有道云笔记:http://note.youdao.com/noteshare?id=d86bd8fc60cb4fe87005a2d2e2d5b70d&sub=6911732F9FA44C68AD53A09072155ED3
Pytorch Leture 05: Linear Rregression in the Pytorch Way
第一部分,使用一个类来构建你的模型,需要写forward函数
【【【【【【
instantiate例式
最近主要看了一下pytorch nn中一些函数的具体形式
一般定义一个linear层的时候,写法为nn.Linear(in_features,out_features)
具体形式为:
y = wx+b
weight = Parameter(torch.Tensor(out_features, in_features))
bias = Parameter(torch.Tensor(out_features))
(arbitrary任意的)
】】】】】】
import torch
from torch.autograd import Variable
import matplotlib.pyplot as plt
x_data = Variable(torch.Tensor([[1.0], [2.0], [3.0]]))
y_data = Variable(torch.Tensor([[2.0], [4.0], [6.0]]))
class Model(torch.nn.Module):
def __init__(self):
"""
In the constructor we instantiate two nn.Linear module
"""
super(Model, self).__init__()
self.linear = torch.nn.Linear(1, 1) # One in and one out
def forward(self, x):
"""
In the forward function we accept a Variable of input data and we must return
a Variable of output data. We can use Modules defined in the constructor as
well as arbitrary operators on Variables.
"""
y_pred = self.linear(x)
return y_pred
# our model
model = Model()
第二部分,构建loss和优化器来进行参数计算
【【【【【【
SGD
MSELoss
】】】】】】
# Construct our loss function and an Optimizer. The call to model.parameters()
# in the SGD constructor will contain the learnable parameters of the two
# nn.Linear modules which are members of the model.
# criterion 标准准则 主要用来计算loss
criterion = torch.nn.MSELoss(size_average=False)
# 优化器
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
第三部分,进行训练,forward -> backward -> update parameters
# Training loop
for epoch in range(1000):
# Forward pass: Compute predicted y by passing x to the model
y_pred = model(x_data)
# Compute and print loss
loss = criterion(y_pred, y_data)
print(epoch, loss.data[0])
# Zero gradients, perform a backward pass, and update the weights.
# initialize the gradients
optimizer.zero_grad()
# 反向传递
loss.backward()
# 更新优化器中的权重,即model.parrameters
optimizer.step()
第四部分,测试
# After training
hour_var = Variable(torch.Tensor([[4.0]]))
y_pred = model(hour_var)
print("predict (after training)", 4, model(hour_var).data[0][0])
总结一下基本的训练框架:
- 通过写一个类,来构造你的模型
- 构建loss和优化器
- 开始训练 Forward -> compute loss -> backward -> update
- Forward: y_pred = model(x_data)
- Compute loss: loss = criterion(y_pred,y_data)
- Backward: optimizer.zero_grad() && loss.backward()
- Update: optimizer.step()
作业测试其他optimizers:
- torch.optim.Adagrad
- torch.optim.Adam
- torch.optim.Adamax
- torch.optim.ASGD
- torch.optim.LBFGS
- torch.optim.RRRMSprop
- torch.optim.Rprop
- torch.optim.SGD
Logistic Regression 逻辑回归 - 二分类
原来的:
graph LR
x-->Linear
Linear-->y
\hat{y} = x * w + b
loss = \frac{1}{N}\sum_{n=1}^{N}(\hat{y_n}-y_n)^2
激活函数:
using sigmoid functions:
graph LR
x --> Linear
Linear --> Sigmoid
Sigmoid --> y
Y 介于 [0,1] 之间, 这样做可以用来压缩计算量,让计算更加容易
\sigma(z) = \frac{1}{1+e^{-z}}
\hat{y} = \sigma(x*w+b)
loss=-\frac{1}{N}\sum_{n=1}^{N}y_nlog\hat{y_n} + (1-y_n)log(1-\hat{y_n})
代码:
import torch
from torch.autograd import Variable
import torch.nn.functional as F
x_data = Variable(torch.Tensor([[1.0], [2.0], [3.0], [4.0],[5.0]]))
y_data = Variable(torch.Tensor([[0.], [0.], [1.], [1.],[1.]]))
class Model(torch.nn.Module):
def __init__(self):
"""
In the constructor we instantiate nn.Linear module
"""
super(Model, self).__init__()
self.linear = torch.nn.Linear(1, 1) # One in and one out
def forward(self, x):
"""
In the forward function we accept a Variable of input data and we must return
a Variable of output data.
"""
y_pred = F.sigmoid(self.linear(x))
return y_pred
# our model
model = Model()
# Construct our loss function and an Optimizer. The call to model.parameters()
# in the SGD constructor will contain the learnable parameters of the two
# nn.Linear modules which are members of the model.
criterion = torch.nn.BCELoss(size_average=True)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# Training loop
for epoch in range(400):
# Forward pass: Compute predicted y by passing x to the model
y_pred = model(x_data)
# Compute and print loss
loss = criterion(y_pred, y_data)
print(epoch, loss.data[0])
# Zero gradients, perform a backward pass, and update the weights.
optimizer.zero_grad()
loss.backward()
optimizer.step()
# After training
hour_var = Variable(torch.Tensor([[0.0]]))
print("predict 1 hour ", 0.0, model(hour_var).data[0][0] > 0.5)
hour_var = Variable(torch.Tensor([[7.0]]))
print("predict 7 hours", 7.0, model(hour_var).data[0][0] > 0.5)
新增激活函数:
- Design your model using class
y_Pred = F.sigmoid(self.linear(x))
- Construct loss and optimizer
change loss into:
criterion = torch.nn.BCELoss(size_average=True)
- Training cycle (forward,backward,update)
作业:尝试其他激活函数:
- ReLu
ReLU是修正线性单元(The Rectified Linear Unit)的简称,近些年来在深度学习中使用得很多,可以解决梯度弥散问题,因为它的导数等于1或者就是0。相对于sigmoid和tanh激励函数,对ReLU求梯度非常简单,计算也很简单,可以非常大程度地提升随机梯度下降的收敛速度。(因为ReLU是线性的,而sigmoid和tanh是非线性的)。但ReLU的缺点是比较脆弱,随着训练的进行,可能会出现神经元死亡的情况,例如有一个很大的梯度流经ReLU单元后,那权重的更新结果可能是,在此之后任何的数据点都没有办法再激活它了。如果发生这种情况,那么流经神经元的梯度从这一点开始将永远是0。也就是说,ReLU神经元在训练中不可逆地死亡了。
- ReLu6
- ELU
ELU在正值区间的值为x本身,这样减轻了梯度弥散问题(x>0区间导数处处为1),这点跟ReLU、Leaky ReLU相似。而在负值区间,ELU在输入取较小值时具有软饱和的特性,提升了对噪声的鲁棒性
- SELU
- PReLU
- LeakyReLu
Leaky ReLU主要是为了避免梯度消失,当神经元处于非激活状态时,允许一个非0的梯度存在,这样不会出现梯度消失,收敛速度快。它的优缺点跟ReLU类似。
- Threshold
- Hardtanh
tanh函数将输入值压缩至-1到1之间。该函数与Sigmoid类似,也存在着梯度弥散或梯度饱和的缺点。
- Sigmoid
这应该是神经网络中使用最频繁的激励函数了,它把一个实数压缩至0到1之间,当输入的数字非常大的时候,结果会接近1,当输入非常大的负数时,则会得到接近0的结果。在早期的神经网络中使用得非常多,因为它很好地解释了神经元受到刺激后是否被激活和向后传递的场景(0:几乎没有被激活,1:完全被激活),不过近几年在深度学习的应用中比较少见到它的身影,因为使用sigmoid函数容易出现梯度弥散或者梯度饱和。当神经网络的层数很多时,如果每一层的激励函数都采用sigmoid函数的话,就会产生梯度弥散的问题,因为利用反向传播更新参数时,会乘以它的导数,所以会一直减小。如果输入的是比较大或者比较小的数(例如输入100,经Sigmoid函数后结果接近于1,梯度接近于0),会产生饱和效应,导致神经元类似于死亡状态。
- Tanh
Lecture07: How to make netural network wide and deep ?
graph LR
a-->Linear
b-->Linear
Linear-->Sigmoid
Sigmoid-->y
多维度,更层次的网络,主要在Design your model using class 中进行的改变
import torch
from torch.autograd import Variable
import numpy as np
xy = np.loadtxt('./data/diabetes.csv.gz', delimiter=',', dtype=np.float32)
x_data = Variable(torch.from_numpy(xy[:, 0:-1]))
y_data = Variable(torch.from_numpy(xy[:, [-1]]))
print(x_data.data.shape)
print(y_data.data.shape)
class Model(torch.nn.Module):
def __init__(self):
"""
In the constructor we instantiate two nn.Linear module
"""
super(Model, self).__init__()
self.l1 = torch.nn.Linear(8, 6)
self.l2 = torch.nn.Linear(6, 4)
self.l3 = torch.nn.Linear(4, 1)
self.sigmoid = torch.nn.Sigmoid()
def forward(self, x):
"""
In the forward function we accept a Variable of input data and we must return
a Variable of output data. We can use Modules defined in the constructor as
well as arbitrary operators on Variables.
"""
out1 = self.sigmoid(self.l1(x))
out2 = self.sigmoid(self.l2(out1))
y_pred = self.sigmoid(self.l3(out2))
return y_pred
# our model
model = Model()
# Construct our loss function and an Optimizer. The call to model.parameters()
# in the SGD constructor will contain the learnable parameters of the two
# nn.Linear modules which are members of the model.
#criterion = torch.nn.BCELoss(size_average=True)
criterion = torch.nn.BCELoss(reduction='elementwise_mean')
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
# Training loop
for epoch in range(1200000):
# Forward pass: Compute predicted y by passing x to the model
y_pred = model(x_data)
# Compute and print loss
loss = criterion(y_pred, y_data)
print(epoch, loss.item())
# Zero gradients, perform a backward pass, and update the weights.
optimizer.zero_grad()
loss.backward()
optimizer.step()
作业:
- 10层以上的更深层测的网络进行训练
发现并没有因为更深,效果变好 - 更改激励函数
Lecture 08: Pytorch DataLoader
构造Datasets主要分为三个过程:
继承自Dataset
- download, rerad data etc
- return one item on the index
- return the data length
实例化一个dataset,在Dataloader中使用:
train_loader = DataLoader(dataset=dataset,
batch_size=1,
shuffle=True,
num_workers=1)
Code:
# References
# https://github.com/yunjey/pytorch-tutorial/blob/master/tutorials/01-basics/pytorch_basics/main.py
# http://pytorch.org/tutorials/beginner/data_loading_tutorial.html#dataset-class
import torch
import numpy as np
from torch.autograd import Variable
from torch.utils.data import Dataset, DataLoader
class DiabetesDataset(Dataset):
""" Diabetes dataset."""
# Initialize your data, download, etc.
def __init__(self):
xy = np.loadtxt('./data/diabetes.csv.gz',
delimiter=',', dtype=np.float32)
self.len = xy.shape[0]
self.x_data = torch.from_numpy(xy[:, 0:-1])
self.y_data = torch.from_numpy(xy[:, [-1]])
def __getitem__(self, index):
return self.x_data[index], self.y_data[index]
def __len__(self):
return self.len
dataset = DiabetesDataset()
train_loader = DataLoader(dataset=dataset,
batch_size=1,
shuffle=True,
num_workers=1)
for epoch in range(2):
for i, data in enumerate(train_loader, 0):
# get the inputs
inputs, labels = data
# wrap them in Variable
inputs, labels = Variable(inputs), Variable(labels)
# Run your training process
print(epoch, i, "inputs", inputs.data, "labels", labels.data)
课后作业:
使用其他数据集,MNIST,参考了官网的代码:
总结一下训练的思路:
- 构造继承自Dataset的自己的datasets类
- [ ] 读取数据集,np.loadtxt("datas.csv") , 构建trainset testset
- [ ] 构建DataLoader: 得到trainLoader , testLoader
- [ ] 从DataLoader中获取数据: dataiter = iter(trainloader) images, labels = dataiter.next()
- [ ] 训练
- [ ] 测试
Lecture 09: softmax Classifier
part one
MNist softmax
before:
graph LR
x{x} --> Linear
Linear --> Activation
Activation --> ...
... --> Linear2
Linear2-->Activation2
Activation2-->h{y}
now:
graph LR
x{x} --> Linear
Linear --> Activation
Activation --> ...
... --> Linear2
Linear2-->Activation2
Activation2-->P_y=0
Activation2-->P_y=1
Activation2-->....
Activation2-->P_y=10
what is softmax?
\sigma(z)_j = \frac{e^{z_j}}{\sum_{k=1}^{K}e^{z_k}} for j=1,2,...,k
using softmax to get probabilities.
what is corss entropy?
loss = \frac{1}{N}\sum_i D(Softmax(wx_i+b),Y_i)
D(\hat{Y},Y) = -Ylog\hat{Y}
整个过程:
graph LR
x--LinearModel-->Z
Z--Softmax-->y'
y'--Cross_Entropy-->Y
Pytorch中的实现:
loss = torch.nn.CrossEntropyLoss()
这个中既包括了Softmax也包括了Cross_Entropy
graph LR
X--Softmax-->y'
y'--Cross_Entropy-->Y
Code:
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
# Cross entropy example
import numpy as np
# One hot
# 0: 1 0 0
# 1: 0 1 0
# 2: 0 0 1
Y = np.array([1, 0, 0])
Y_pred1 = np.array([0.7, 0.2, 0.1])
Y_pred2 = np.array([0.1, 0.3, 0.6])
print("loss1 = ", np.sum(-Y * np.log(Y_pred1)))
print("loss2 = ", np.sum(-Y * np.log(Y_pred2)))
################################################################################
# Softmax + CrossEntropy (logSoftmax + NLLLoss)
loss = nn.CrossEntropyLoss()
# target is of size nBatch
# each element in target has to have 0 <= value < nClasses (0-2)
# Input is class, not one-hot
Y = Variable(torch.LongTensor([0]), requires_grad=False)
# input is of size nBatch x nClasses = 1 x 4
# Y_pred are logits (not softmax)
Y_pred1 = Variable(torch.Tensor([[2.0, 1.0, 0.1]]))
Y_pred2 = Variable(torch.Tensor([[0.5, 2.0, 0.3]]))
l1 = loss(Y_pred1, Y)
l2 = loss(Y_pred2, Y)
print("PyTorch Loss1 = ", l1.data, "\nPyTorch Loss2=", l2.data)
print("Y_pred1=", torch.max(Y_pred1.data, 1)[1])
print("Y_pred2=", torch.max(Y_pred2.data, 1)[1])
################################################################################
"""Batch loss"""
# target is of size nBatch
# each element in target has to have 0 <= value < nClasses (0-2)
# Input is class, not one-hot
Y = Variable(torch.LongTensor([2, 0, 1]), requires_grad=False)
# input is of size nBatch x nClasses = 2 x 4
# Y_pred are logits (not softmax)
Y_pred1 = Variable(torch.Tensor([[0.1, 0.2, 0.9],
[1.1, 0.1, 0.2],
[0.2, 2.1, 0.1]]))
Y_pred2 = Variable(torch.Tensor([[0.8, 0.2, 0.3],
[0.2, 0.3, 0.5],
[0.2, 0.2, 0.5]]))
l1 = loss(Y_pred1, Y)
l2 = loss(Y_pred2, Y)
print("Batch Loss1 = ", l1.data, "\nBatch Loss2=", l2.data)
作业:CrossEntropyLoss VS NLLLoss ?
part two : real problem - MNIST input
MNIST Network
graph LR
inputLayer -.-> HiddenLayer
HiddenLayer -.-> OutputLayer
Code:
# https://github.com/pytorch/examples/blob/master/mnist/main.py
from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
# Training settings
batch_size = 16
# MNIST Dataset
train_dataset = datasets.MNIST(root='./mnist_data/',
train=True,
transform=transforms.ToTensor(),
download=True)
test_dataset = datasets.MNIST(root='./mnist_data/',
train=False,
transform=transforms.ToTensor())
# Data Loader (Input Pipeline)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.l1 = nn.Linear(784, 520)
self.l2 = nn.Linear(520, 320)
self.l3 = nn.Linear(320, 240)
self.l4 = nn.Linear(240, 120)
self.l5 = nn.Linear(120, 10)
def forward(self, x):
x = x.view(-1, 784) # Flatten the data (n, 1, 28, 28)-> (n, 784)
x = F.relu(self.l1(x))
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x)
model = Net()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
def train(epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = Variable(data), Variable(target)
optimizer.zero_grad()
output = model(data)
loss = criterion(output, target)
loss.backward()
optimizer.step()
if batch_idx % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.data[0]))
def test():
model.eval()
test_loss = 0
correct = 0
for data, target in test_loader:
data, target = Variable(data, volatile=True), Variable(target)
output = model(data)
# sum up batch loss
test_loss += criterion(output, target).data[0]
# get the index of the max
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target.data.view_as(pred)).cpu().sum()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
for epoch in range(1, 10):
train(epoch)
test()
作业:
Use DataLoader
Lecture 10 : basic CNN
Simple convolution layer
for Example:
graph LR
3*3*1_image-->2*2*1_filter_W
3*3*1_image-->1*1_Stride
3*3*1_image-->NoPadding
NoPadding-->2*2_featureMap
2*2*1_filter_W-->2*2_featureMap
1*1_Stride-->2*2_featureMap
How to compute multi-dimension pictures ?
- 32 * 32 * 3 image
- 5 * 5 * 3 filter W
w^T + b
Get: 28 * 28 * 1 feature map * N (how many filters you used)
计算公式
OutputSize = \frac{(InputSize+PaddingSize*2-FilterSize)}{Stride} + 1
几个需要解释的参数:
CONV
卷积层,需要配合激活函数使用
filter and padding and filterSize using function above to calculate
torch.nn.Conv2d(in_channels,out_channels,kernel_size)
self.conv1=nn.Conv2d(1,10,kernel_size=5)
激活函数
activate functions
Max Pooling
选取一个n*m的Filter中最大的值作为pooling的结果
还有类似的avg Pooling
nn.MaxPool2d(kernel_size)
self.mp = nn.MaxPool2d(2)
全连接层
self.fc = nn.Linear(320,10)
CNN & Fully Connected network 区别
CNN中的神经元不是跟每个像素都相连
Fully Connected network中的神经元是跟每个像素都相连。
implement of Simple CNN
graph TB
ConvolutionalLayer1 --> PoolingLayer1
PoolingLayer1 --> ConvolutionalLayer2
ConvolutionalLayer2 --> PoolingLayer2
PoolingLayer2 --> Fully-ConnectedLayer
Model:
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.conv1 = nn.Conv2d(1,10,kernel_size=5)
self.conv2 = nn.Conv2d(10,20,kernel_size=5)
self.mp = nn.MaxPool2d(2)
self.fc = nn.Linear(???,10)
def forward(self,x):
in_size = x.size(0)
x = F.relu(self.mp(self.conv1(x)))
x = F.relu(self.mp(self.conv2(x)))
x = x.view(in_size,-1) # flatten the tensor
x = self.fc(x)
return F.log_softmax(x)
???
处如何填写
???
处可以随意先填一个数值,然后通过程序的报错来填写- 还可以在forward函数中print(x.size())得到tensor的维度
作业:
尝试更深层次的网络,更深的全连接层
Lecture 11 Advanced CNN
Why 1*1 convolution ?
using 32 1*1 filters to turn 64-dimension pic into 32-dimension pic.
using 1*1 filters can significantly save our computations.
Inception Module
graph LR
Filter_concat_in --> 1*1Conv0_16
Filter_concat_in --> 1*1Conv1_16
Filter_concat_in --> 1*1Conv2_16
Filter_concat_in --> AvgPooling
AvgPooling --> 1*1Conv3_16
1*1Conv0_16 --> 3*3Conv0_24
3*3Conv0_24 --> 3*3Conv1_24
3*3Conv1_24 --> Filter_Concat_out
1*1Conv1_16 --> 5*5Conv_24
5*5Conv_24 --> Filter_Concat_out
1*1Conv3_16 --> Filter_Concat_out
1*1Conv2_16 --> Filter_Concat_out
Implement
- 最下边的实现(第四道)
self.brach1x1 = nn.Conv2d(in_channels,16,kernel_size=1)
branch1x1 = self.branch1x1(x)
- 倒数第二道
self.branch_pool = nn.Conv2d(in_channels,24,kernel_size=1)
branch_pool = F.avg_pool2d(x,kernel_size=3,stride=1,padding=1)
branch_pool = self.branch_pool(branch_pool)
- 正数第二道
self.branch5x5_1 = nn.Conv2d(in_channels,16,kernel_size=1)
self.branch5x5_2 = nn.Conv2d(16,24,kernel_size=1,padding=2)
branch5x5 = self.branch5x5_1(x)
branch5x5 = self.branch5x5_2(branch5x5)
- 第一道
self.branch3x3_1=nn.Conv2d(in_channels,16,kernel_size=1)
self.branch3x3_2=nn.Conv2d(16,24,kernel_size=3,padding=1)
self.branch3x3_3=nn.Conv2d(24,24,kernel_size=3,padding=1)
branch3x3 = self.branch3x3_1(x)
branch3x3 = self.branch3x3_2(branch3x3)
branch3x3 = self.branch3x3_3(branch3x3)
- output
outputs = [branch1x1,branch_pool,branch5x5,branch3x3]
ALL CODE:
# https://github.com/pytorch/examples/blob/master/mnist/main.py
from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
# Training settings
batch_size = 64
# MNIST Dataset
train_dataset = datasets.MNIST(root='./data/',
train=True,
transform=transforms.ToTensor(),
download=True)
test_dataset = datasets.MNIST(root='./data/',
train=False,
transform=transforms.ToTensor())
# Data Loader (Input Pipeline)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
batch_size=batch_size,
shuffle=True)
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
batch_size=batch_size,
shuffle=False)
class InceptionA(nn.Module):
def __init__(self, in_channels):
super(InceptionA, self).__init__()
self.branch1x1 = nn.Conv2d(in_channels, 16, kernel_size=1)
self.branch5x5_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
self.branch5x5_2 = nn.Conv2d(16, 24, kernel_size=5, padding=2)
self.branch3x3dbl_1 = nn.Conv2d(in_channels, 16, kernel_size=1)
self.branch3x3dbl_2 = nn.Conv2d(16, 24, kernel_size=3, padding=1)
self.branch3x3dbl_3 = nn.Conv2d(24, 24, kernel_size=3, padding=1)
self.branch_pool = nn.Conv2d(in_channels, 24, kernel_size=1)
def forward(self, x):
branch1x1 = self.branch1x1(x)
branch5x5 = self.branch5x5_1(x)
branch5x5 = self.branch5x5_2(branch5x5)
branch3x3dbl = self.branch3x3dbl_1(x)
branch3x3dbl = self.branch3x3dbl_2(branch3x3dbl)
branch3x3dbl = self.branch3x3dbl_3(branch3x3dbl)
branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
branch_pool = self.branch_pool(branch_pool)
outputs = [branch1x1, branch5x5, branch3x3dbl, branch_pool]
return torch.cat(outputs, 1)
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(88, 20, kernel_size=5)
self.incept1 = InceptionA(in_channels=10)
self.incept2 = InceptionA(in_channels=20)
self.mp = nn.MaxPool2d(2)
self.fc = nn.Linear(1408, 10)
def forward(self, x):
in_size = x.size(0)
x = F.relu(self.mp(self.conv1(x)))
x = self.incept1(x)
x = F.relu(self.mp(self.conv2(x)))
x = self.incept2(x)
x = x.view(in_size, -1) # flatten the tensor
x = self.fc(x)
return F.log_softmax(x)
model = Net()
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5)
def train(epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = Variable(data), Variable(target)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.data[0]))
def test():
model.eval()
test_loss = 0
correct = 0
for data, target in test_loader:
data, target = Variable(data, volatile=True), Variable(target)
output = model(data)
# sum up batch loss
test_loss += F.nll_loss(output, target, size_average=False).data[0]
# get the index of the max log-probability
pred = output.data.max(1, keepdim=True)[1]
correct += pred.eq(target.data.view_as(pred)).cpu().sum()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
for epoch in range(1, 10):
train(epoch)
test()
Lecture 12: RNN
Recurrrent NN
graph LR
X1 --> A1
A1 --> h1
X2 --> A2
A2 --> h2
X3 --> A3
A3 --> h3
X4 --> A4
A4 --> h4
A1 --> A2
A2 --> A3
A3 --> A4
Pytorch提供了RNN函数,可以直接使用
different RNN implementations
cell = nn.RNN(input_size=4,hidden_size=2,batch_first=True)
cell = nn.GRU(input_size=4,hidden_size=2,batch_first=True)
cell = nn.LSTM(input_size=4,hidden_size=2,batch_first=True)
How to use RNN?
cell = nn.RNN(input_size=4,hidden_size=2,batch_first=True)
inputs = ... # batch_size, seq_len,inputSize
hidden = (...) # numLayers,batch_size, hidden_size
out, hidden = cell(inputs,hidden)
有两个输出,一个是output, 一个是hidden layer的output
# Lab 12 RNN
import sys
import torch
import torch.nn as nn
from torch.autograd import Variable
torch.manual_seed(777) # reproducibility
# 0 1 2 3 4
idx2char = ['h', 'i', 'e', 'l', 'o']
# Teach hihell -> ihello
x_data = [0, 1, 0, 2, 3, 3] # hihell
one_hot_lookup = [[1, 0, 0, 0, 0], # 0
[0, 1, 0, 0, 0], # 1
[0, 0, 1, 0, 0], # 2
[0, 0, 0, 1, 0], # 3
[0, 0, 0, 0, 1]] # 4
y_data = [1, 0, 2, 3, 3, 4] # ihello
x_one_hot = [one_hot_lookup[x] for x in x_data]
# As we have one batch of samples, we will change them to variables only once
inputs = Variable(torch.Tensor(x_one_hot))
labels = Variable(torch.LongTensor(y_data))
num_classes = 5
input_size = 5 # one-hot size
hidden_size = 5 # output from the RNN. 5 to directly predict one-hot
batch_size = 1 # one sentence
sequence_length = 1 # One by one
num_layers = 1 # one-layer rnn
class Model(nn.Module):
def __init__(self):
super(Model, self).__init__()
self.rnn = nn.RNN(input_size=input_size,
hidden_size=hidden_size, batch_first=True)
def forward(self, hidden, x):
# Reshape input (batch first)
x = x.view(batch_size, sequence_length, input_size)
# Propagate input through RNN
# Input: (batch, seq_len, input_size)
# hidden: (num_layers * num_directions, batch, hidden_size)
out, hidden = self.rnn(x, hidden)
return hidden, out.view(-1, num_classes)
def init_hidden(self):
# Initialize hidden and cell states
# (num_layers * num_directions, batch, hidden_size)
return Variable(torch.zeros(num_layers, batch_size, hidden_size))
# Instantiate RNN model
model = Model()
print(model)
# Set loss and optimizer function
# CrossEntropyLoss = LogSoftmax + NLLLoss
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.1)
# Train the model
for epoch in range(100):
optimizer.zero_grad()
loss = 0
hidden = model.init_hidden()
sys.stdout.write("predicted string: ")
for input, label in zip(inputs, labels):
# print(input.size(), label.size())
hidden, output = model(hidden, input)
val, idx = output.max(1)
sys.stdout.write(idx2char[idx.data[0]])
loss += criterion(output, label)
print(", epoch: %d, loss: %1.3f" % (epoch + 1, loss.data[0]))
loss.backward()
optimizer.step()
print("Learning finished!")
代码改变世界
====================================================================================================================================================================================================================================================================================================================================================================
【【【【【【【【
torch.autograd提供实现任意标量值功能的自动区分的类和功能。
matplotlib.pyplot
torch.autograd方法类Variable
torch.autograd提供实现任意标量值功能的自动区分的类和功能。 它需要对现有代码进行最小的更改,只需将所有张量包装在Variable对象中。
Variable API与常规Tensor API几乎相同(除了几个需要对梯度计算的输入进行重写的in-place方法外)。在大多数情况下,Tensors可以安全地替换为Variable,代码将保持工作正常。所以,在这里只介绍有别于Tensors的一些操作。
Variable的in-place(原地操作):在autograd中支持就地操作是一件很困难的事情,大多数情况下都不鼓励使用它们。 Autograd的积极缓冲区释放和重用使in-place变得非常高效,极少数场合就地操作能够实质上地降低内存使用量。 除非您在内存压力很大的情况下运行,否则您可能永远不需要使用它们。
in-place准确性检查:所有的Variable应用并跟踪in-place操作,如果在实现过程中发现一个变量在一个函数中保存为后向,并在in-place前向操作时被修改,那么一但反向传播开始,会产生一个错误。这可以确保如果您使用就地操作并且没有看到任何错误,则可以确定计算出的梯度是正确的。
torch.autograd.Variable用来包裹张量并记录应用的操作。
Variable可以看作是对Tensor对象周围的一个薄包装,也包含了和张量相关的梯度,以及对创建它的函数的引用。 此引用允许对创建数据的整个操作链进行回溯。需要BP的网络都是通过Variable来计算的。如果Variable是由用户创建的,则其grad_fn将为None,我们将这些对象称为叶子Variable。
由于自动求导仅支持标量值函数微分,因此grad大小始终与数据大小匹配。 此外,grad通常仅分配给叶子Variable,否则将始终为零。
class Variable(_C._VariableBase):
"""
Attributes:
data: 任意类型的封装好的张量。
grad: 保存与data类型和位置相匹配的梯度,此属性难以分配并且不能重新分配。
requires_grad: 标记变量是否已经由一个需要调用到此变量的子图创建的bool值。只能在叶子变量上进行修改。
volatile: 标记变量是否能在推理模式下应用(如不保存历史记录)的bool值。只能在叶变量上更改。
is_leaf: 标记变量是否是图叶子(如由用户创建的变量)的bool值.
grad_fn: Gradient function graph trace.
Parameters:
data (any tensor class): 要包装的张量.
requires_grad (bool): bool型的标记值. **Keyword only.**
volatile (bool): bool型的标记值. **Keyword only.**
"""
def backward(self, gradient=None, retain_graph=None, create_graph=None, retain_variables=None):
"""计算关于当前图叶子变量的梯度,图使用链式法则导致分化
如果Variable是一个标量(例如它包含一个单元素数据),你无需对backward()指定任何参数
如果变量不是标量(包含多个元素数据的矢量)且需要梯度,函数需要额外的梯度;
需要指定一个和tensor的形状匹配的grad_output参数(y在指定方向投影对x的导数);
可以是一个类型和位置相匹配且包含与自身相关的不同函数梯度的张量。
函数在叶子上累积梯度,调用前需要对该叶子进行清零。
Arguments:
grad_variables (Tensor, Variable or None):
变量的梯度,如果是一个张量,除非“create_graph”是True,否则会自动转换成volatile型的变量。
可以为标量变量或不需要grad的值指定None值。如果None值可接受,则此参数可选。
retain_graph (bool, optional): 如果为False,用来计算梯度的图将被释放。
在几乎所有情况下,将此选项设置为True不是必需的,通常可以以更有效的方式解决。
默认值为create_graph的值。
create_graph (bool, optional): 为True时,会构造一个导数的图,用来计算出更高阶导数结果。
默认为False,除非``gradient``是一个volatile变量。
"""
torch.autograd.backward(self, gradient, retain_graph, create_graph, retain_variables)
def register_hook(self, hook):
"""Registers a backward hook.
每当与variable相关的梯度被计算时调用hook,hook的申明:hook(grad)->Variable or None
不能对hook的参数进行修改,但可以选择性地返回一个新的梯度以用在`grad`的相应位置。
函数返回一个handle,其``handle.remove()``方法用于将hook从模块中移除。
Example:
>>> v = Variable(torch.Tensor([0, 0, 0]), requires_grad=True)
>>> h = v.register_hook(lambda grad: grad * 2) # double the gradient
>>> v.backward(torch.Tensor([1, 1, 1]))
>>> v.grad.data
2
2
2
[torch.FloatTensor of size 3]
>>> h.remove() # removes the hook
"""
if self.volatile:
raise RuntimeError("cannot register a hook on a volatile variable")
if not self.requires_grad:
raise RuntimeError("cannot register a hook on a variable that "
"doesn't require gradient")
if self._backward_hooks is None:
self._backward_hooks = OrderedDict()
if self.grad_fn is not None:
self.grad_fn._register_hook_dict(self)
handle = hooks.RemovableHandle(self._backward_hooks)
self._backward_hooks[handle.id] = hook
return handle
def reinforce(self, reward):
"""Registers a reward obtained as a result of a stochastic process.
区分随机节点需要为他们提供reward值。如果图表中包含任何的随机操作,都应该在其输出上调用此函数,否则会出现错误。
Parameters:
reward(Tensor): 带有每个元素奖赏的张量,必须与Variable数据的设备位置和形状相匹配。
"""
if not isinstance(self.grad_fn, StochasticFunction):
raise RuntimeError("reinforce() can be only called on outputs "
"of stochastic functions")
self.grad_fn._reinforce(reward)
def detach(self):
"""返回一个从当前图分离出来的心变量。
结果不需要梯度,如果输入是volatile,则输出也是volatile。
.. 注意::
返回变量使用与原始变量相同的数据张量,并且可以看到其中任何一个的就地修改,并且可能会触发正确性检查中的错误。
"""
result = NoGrad()(self) # this is needed, because it merges version counters
result._grad_fn = None
return result
def detach_(self):
"""从创建它的图中分离出变量并作为该图的一个叶子"""
self._grad_fn = None
self.requires_grad = False
def retain_grad(self):
"""Enables .grad attribute for non-leaf Variables."""
if self.grad_fn is None: # no-op for leaves
return
if not self.requires_grad:
raise RuntimeError("can't retain_grad on Variable that has requires_grad=False")
if hasattr(self, 'retains_grad'):
return
weak_self = weakref.ref(self)
def retain_grad_hook(grad):
var = weak_self()
if var is None:
return
if var._grad is None:
var._grad = grad.clone()
else:
var._grad = var._grad + grad
self.register_hook(retain_grad_hook)
self.retains_grad = True
】】】】】】】】