深度学习平台
平台简介
库名 | 发布者 | 支持语言 |
---|---|---|
TensorFlow | Python/C++/Java/Go | |
Caffe | UC Berkeley | Python/C++/Matlab |
JAX | Python | |
MXNet | Amazon/DMLC | Python/C++/Matlab/Julia/Go/R/SCaia |
Torch/PyTorch | C/Python | |
PaddlePaddle | 百度 | Python |
MMdetection | 商汤/港中文 | Python |
PyTorch
PyTorch是在Torch基础上用python语言重新打造的一款深度学习框架。Torch是采用Lua语言为接口的机器学习框架,因为Lua语言较为小众,导致Torch学习成本高,因此知名度不高。
PyTorch发展历程
- 2017 年 1 月正式发布 PyTorch;
- 2018 年 4 月更新 0.4.0 版,支持 Windows 系统,caffe2 正式并入 PyTorch;
- 2018 年 11 月更新 1.0 稳定版,已成为 Github 上增长第二快的开源项目;
- 2019 年 5 月更新 1.1.0 版,支持 TensorBoard,增强可视化功能;
- 2019 年 8 月更新 1.2.0 版,更新 Torchvision,torchaudio 和 torchtext,支持更多功能。
PyTorch 和 TensorFlow2 的对比
PyTorch | TensorFlow2 |
---|---|
上手简单、学习速度快 | 上手难 |
部署能力稍弱 | 部署方便、稳定 |
支持动态图 | 支持动态图 |
函数简洁、运用灵活 | 函数封装复杂 |
PyTorch基本使用
- 使用tensor表示数据
- 使用Dataset 、DataLoader读取样本数据和标签
- 使用变量(Variable)存储神经网络权值等参数
- 使用计算图(computational graph)来表示计算任务
- 在代码运行过程中同时执行计算图
tensor的使用和numpy中的多维数组类似
import torch
x_const = torch.tensor([1.0 , 2.0 , 3.0])
y = torch.tensor([3.0 , 4.0 , 5.0])
output = x_const + y
print(x_const, '\n', y, '\n',output)
import torch
import torch.nn as nn
import matplotlib.pyplot as plt
from torch.autograd import Variable
class LinearRegression(nn.Module):
def __init__(self):
super(LinearRegression,self).__init__()
self.linear=nn.Linear(1,1)
def forward(self,x):
out = self.linear(x)
return out
if __name__ == '__main__':
model = LinearRegression()
params = list(model.named_parameters())
(_, w) = params[0]
(_, b) = params[1]
criterion = nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(),lr=0.3)
x = torch.unsqueeze(torch.linspace(-1, 1, 100), dim=1)
y = 2 * x + 10 + torch.rand(x.size())
for epoch in range(20):
inputs = Variable(x)
target = Variable(y)
out = model(inputs)
loss = criterion(out, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
print('Epoch:{},w:{:.4f},b:{:.4f}'.format(epoch+1,float(w.data ),float(b.data)))
model.eval()
predict = model(Variable(x))
predict = predict.data.numpy()
plt.plot(x.numpy(), y.numpy(), 'ro', label='Original Data')
plt.plot(x.numpy(), predict, label='Fitting Line')
plt.legend()
plt.show()
卷积神经网络
基本概念
互相关运算:在卷积层中,输入张量和核张量通过互相关运算产生输出张量。在二维互相关运算中,卷积窗口从输入张量的左上角开始,从左到右、从上到下滑动。 当卷积窗口滑动到新一个位置时,包含在该窗口中的部分张量与卷积核张量进行按元素相乘,得到的张量再求和得到一个单一的标量值,由此得出这一位置的输出张量值。
全连接网络:链接权过多,难算难收敛,同时可能进入局部极小值,也容易产生过拟合问题
e.g.输入为 96x96 图像,要学习 100 个特征
局部连接网络:顾名思义,只有一部分权值连接。部分输入和权值卷积。
填充(Padding):在矩阵的边界上填充一些值,以增加矩阵的大小,通常用 0 或者复制边界像素来进行填充。这样在应用多层卷积时,以弥补丢失的边缘像素。
步长 (Stride):滑动卷积核时,会先从输入的左上角开始,每次往左滑动一列或者往下滑动一行逐一计算输出,有时候为了高效计算或是缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素。将每次滑动的行数和列数称为步长(Stride)。
多通道卷积:如 RGB,以彩色图像为例,包含三个通道,分别表示RGB三原色的像素值,卷积过程为每一个通道的像素值与对应的卷积核通道的数值进行卷积,因此每一个通道会对应一个输出卷积结果,三个卷积结果对应位置累加求和,得到最终的卷积结果,卷积结果是原始图像各个通道上的综合信息结果。
池化 (pooling):图像中的相邻像素倾向于具有相似的值,因此通常卷积层相邻的输出像素也具有相似的值。这意味着,卷积层输出中包含的大部分信息都是冗余的。池化层所做的就是通过减小输入的大小降低输出值的数量,使用局部统计特征,如均值或最大值。解决特征过多问题。
卷积神经网络结构
- 构成:由多个卷积层和下采样层构成,后面可连接全连接网络
- 卷积层:k 个滤波器
- 下采样层:采用 mean 或 max
- 后面:连着全连接网络
递推:
前向传播定义为:
z
[
l
]
(
x
,
y
)
=
∑
u
=
0
p
∑
v
=
0
q
a
[
l
−
1
]
(
x
+
u
,
y
+
v
)
w
r
o
t
[
l
]
,
k
(
u
,
v
)
{{z}^{[l]}}\left( x,y \right)=\sum\limits_{u=0}^{p}{\sum\limits_{v=0}^{q}{{{a}^{\left[ l-1 \right]}}}}\left( x+u,y+v \right)w_{rot}^{\left[ l \right],k}\left( u,v \right)
z[l](x,y)=u=0∑pv=0∑qa[l−1](x+u,y+v)wrot[l],k(u,v)
a
[
l
]
(
x
,
y
)
=
f
(
z
[
l
]
(
x
,
y
)
)
{{a}^{[l]}}\left( x,y \right)=f\left( {{z}^{[l]}}\left( x,y \right) \right)
a[l](x,y)=f(z[l](x,y))
如果第𝑙层是卷积池化层,则:
a
[
l
]
(
x
,
y
)
=
d
o
w
n
s
a
m
p
l
e
(
∑
u
=
0
p
∑
v
=
0
q
a
[
l
−
1
]
(
x
+
u
,
y
+
v
)
w
s
(
u
,
v
)
)
{{a}^{[l]}}\left( x,y \right)=downsample\left( \sum\limits_{u=0}^{p}{\sum\limits_{v=0}^{q}{{{a}^{[l-1]}}\left( x+u,y+v \right){{w}_{s}}\left( u,v \right)}} \right)
a[l](x,y)=downsample(u=0∑pv=0∑qa[l−1](x+u,y+v)ws(u,v))
LeNet-5
网络介绍
网络提出
LeNet是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),目的是识别图像 (LeCun et al., 1998)中的手写数字。当时,Yann LeCun发表了第一篇通过反向传播成功训练卷积神经网络的研究,这项工作代表了十多年来神经网络研究开发的成果。当时,LeNet取得了与支持向量机(support vector machines)性能相媲美的成果,成为监督学习的主流方法。
结构详解
网络结构
LeNet(LeNet-5)由两个部分组成:
卷积编码器:由两个卷积层组成;
全连接层密集块:由三个全连接层组成。
C1层
- 6个Feature map构成
- 每个神经元对输入进行5*5卷积
- 每个神经元对应 5*5+1个参数,共6个feature map,28乘28个神经元,因此共有122,304连接
S2层
下采样层,使用最大池化进行下采样,池化的size,选择(2,2),相当于对C1层28乘28的图片,进行分块,每个块的大小为22,可以得到14乘14个块,统计每个块中,最大的值作为下采样的新像素,因此可以得到S1结果为14乘14大小的图片,共有6个这样的图片。
C3层
卷积层,这一层卷积核大小为(5,5),新的图片大小为14-5+1=10,S2包含:6张14*14大小的图片,这一层得到的结果是:16张10乘10的图片,这每一张是通过S2的6张图片进行加权组合得到的。组合方式如图所示:
S4层
下采样层,与S2层工作相同,对C3层的16张10乘10的图片进行最大池化,池化块的大小为22。因此最后S4层为16张大小为5乘5的图片。至此神经元个数已经减少为400。
C5层
用5乘5的卷积核进行卷积,C5层图片的大小为5-5+1=1,1个神经元,120个特征图,因此只有120个神经元,后面就直接利用全连接神经网络,进行这120个神经元的后续处理。
F6层
84个神经元,与C5全连接,总连接数(120+1)*84=10164
输出层
由欧式径向基函数单元构成;每类一个单元;输出 RBF 单元计算输入向量和参数向量之间的欧式距离
网络说明
与现在网络的区别
- 卷积时不进行填充(padding)
- 池化层选用平均池化而非最大池化
- 选用Sigmoid或tanh而非ReLU作为非线性环节激活函数
- 层数较浅,参数数量小(约为6万)
普遍规律:随网络深入,宽、高衰减,通道数增加
误差反向传播
经典BP算法
如果当前是输出层:
δ
i
[
L
]
=
a
i
(
1
−
a
i
)
e
i
\delta _{i}^{\left[ L \right]}={{a}_{i}}\left( 1-{{a}_{i}} \right){{e}_{i}}
δi[L]=ai(1−ai)ei
隐含层(按从后向前顺序更新):
δ
i
[
l
]
=
[
∑
j
=
1
m
w
j
i
[
l
+
1
]
δ
j
[
l
+
1
]
]
(
a
i
[
l
]
)
\delta _{i}^{\left[ l \right]}=\left[ \sum\limits_{j=1}^{m}{w_{ji}^{\left[ l+1 \right]}\delta _{j}^{\left[ l+1 \right]}} \right]\left( a_{i}^{\left[ l \right]} \right)
δi[l]=[j=1∑mwji[l+1]δj[l+1]](ai[l])
然后更新:
Δ
w
i
j
[
l
]
(
k
)
=
α
⋅
δ
i
[
l
]
⋅
a
j
[
l
−
1
]
\Delta w_{ij}^{\left[ l \right]}\left( k \right)=\alpha \cdot \delta _{i}^{\left[ l \right]}\cdot a_{j}^{\left[ l-1 \right]}
Δwij[l](k)=α⋅δi[l]⋅aj[l−1]
a
j
[
0
]
=
x
j
a_{j}^{\left[ 0 \right]}={{x}_{j}}
aj[0]=xj
卷积NN的BP算法:下采样层
如果当前是卷积层,下一层为下采样层,误差如何从下采样层回传;
如果当前是下采样层,下一层为卷积层,误差如何从卷积回传。
LeNet5代码实现
import torch
from torch import nn
from d2l import torch as d2l
net = nn.Sequential(
nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
nn.AvgPool2d(kernel_size=2, stride=2),
nn.Flatten(),
nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
nn.Linear(120, 84), nn.Sigmoid(),
nn.Linear(84, 10))
X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
X = layer(X)
print(layer.__class__.__name__,'output shape: \t',X.shape)
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)
def evaluate_accuracy_gpu(net, data_iter, device=None): #@save
"""使用GPU计算模型在数据集上的精度"""
if isinstance(net, nn.Module):
net.eval() # 设置为评估模式
if not device:
device = next(iter(net.parameters())).device
# 正确预测的数量,总预测的数量
metric = d2l.Accumulator(2)
with torch.no_grad():
for X, y in data_iter:
if isinstance(X, list):
# BERT微调所需的(之后将介绍)
X = [x.to(device) for x in X]
else:
X = X.to(device)
y = y.to(device)
metric.add(d2l.accuracy(net(X), y), y.numel())
return metric[0] / metric[1]
#@save
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
"""用GPU训练模型(在第六章定义)"""
def init_weights(m):
if type(m) == nn.Linear or type(m) == nn.Conv2d:
nn.init.xavier_uniform_(m.weight)
net.apply(init_weights)
print('training on', device)
net.to(device)
optimizer = torch.optim.SGD(net.parameters(), lr=lr)
loss = nn.CrossEntropyLoss()
animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
legend=['train loss', 'train acc', 'test acc'])
timer, num_batches = d2l.Timer(), len(train_iter)
for epoch in range(num_epochs):
# 训练损失之和,训练准确率之和,样本数
metric = d2l.Accumulator(3)
net.train()
for i, (X, y) in enumerate(train_iter):
timer.start()
optimizer.zero_grad()
X, y = X.to(device), y.to(device)
y_hat = net(X)
l = loss(y_hat, y)
l.backward()
optimizer.step()
with torch.no_grad():
metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
timer.stop()
train_l = metric[0] / metric[2]
train_acc = metric[1] / metric[2]
if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
animator.add(epoch + (i + 1) / num_batches,
(train_l, train_acc, None))
test_acc = evaluate_accuracy_gpu(net, test_iter)
animator.add(epoch + 1, (None, None, test_acc))
print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
f'test acc {test_acc:.3f}')
print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '
f'on {str(device)}')
lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())
代码来源:《动手学深度学习》6.6
没有安装cuda,用cpu进行training