b站刘二大人《Pytorch深度学习实践》最后四讲
第十讲 卷积神经网络(基础)
先看了吴恩达机器学习中的讲解,
卷积是为了边缘检测,或者说是提取特征,一个矩阵与一个kernal(filter)作数乘。用不同的核作卷积可以提取不同的特征,这里的核就类似于线性回归中的w,是一个参数、
padding
为了使得边缘信息丢失的影响降低,可以在图像边缘增加p圈,可以使得卷积过后的结果不会变的太小,比如图像6×6,kernal3×3,可以令p=1,则,图像变为8×8,卷积后又会得到6×6的图像。
由此可将convolution分为两类,vaild and same
valid 意为不进行padding
same 意为输出的尺寸和输出的尺寸相同。n+2*p-f+1=n可得,p=(f-1)/2;
stride
n
×
n
f
×
f
p
a
d
d
i
n
g
p
s
t
r
i
d
e
s
n\times n f\times f \ \ padding\ \ \ p\ \ \ \ \ stride\ \ s
n×nf×f padding p stride s
图像n,核f,padding为p,stide为s,则得到的结果是
(
n
+
2
p
−
f
s
+
1
)
×
(
n
+
2
p
−
f
s
+
1
)
(\frac{n+2p-f}{s}+1)\times (\frac{n+2p-f}{s}+1)
(sn+2p−f+1)×(sn+2p−f+1)向下取整。
吴恩达视频中一层中各中notation的总结
对于一个完整的例子,构建多层神经网络,此处明确一种趋势即,图像的尺寸是逐渐减小而filter的数量在不断增加,如图中绿色核紫色圈出的数值。对于参数的选取(f,p,s)是构建网络的繁琐的点。
典型的卷积神经网络有三层,
卷积层(Conv)、convolution
池化层(Pool)、pooling
全连接层(FC) fully connected
池化层
直观理解:分块计算max,最大化操作的目的是,在过滤器中提取到某个特征时保留其最大值,池化层同样是用一个filter,类似于卷积层的filter,计算输出尺寸的公式依然适用。
池化过程中没有参数需要学习。
全连接层
一个更完整的示例
将一个卷积层和一个池化层作为一个layer,最后得到的三维矩阵转化成一个一维很长的矩阵,然后就是全连接层,变成前面的内容了,通过参数w减小特征的数量,以识别数字为例,最后通过softmax得到10个输出。
回到pytorch的课程,在pytorch实现卷积层
import torch
in_channels, out_channels = 5, 10
#输出通道是kernel的数量
width, height = 100, 100
kernel_size = 3 #卷积核的尺寸
batch_size = 1
input = torch.randn(batch_size, in_channels, width, height)
conv_layer = torch.nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size)
output = conv_layer(input)
print(input.shape)
print(output.shape)
print(conv_layer.weight.shape)
因此MNIST手写识别中的net可以改写为卷积层+池化层+全连接层,只需改变Net的定义,其他不变可直接训练
class Net(torch.nn.Module):
def __init__(self):
super().__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5) #卷积层1
self.conv2 = torch.nn.Conv2d(10, 20, kernel_size=5) #卷积层2
self.pooling = torch.nn.MaxPool2d(2) #池化
self.fc = torch.nn.Linear(320, 10) #全连接
self.Relu = torch.nn.ReLU() #relu
def forward(self, x):
batch_size = x.size(0)
x = self.Relu(self.pooling(self.conv1(x)))
x = self.Relu(self.pooling(self.conv2(x)))
x = x.view(batch_size, -1)
x = self.fc(x)
return x
model = Net()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model.to(device) #放到GPU上计算
第十一讲 卷积神经网络(高级)
卷积神经网络除了串联,也可以分支,如GoogleNet
依然来到吴恩达的机器学习
ResNet残差网络
可跳跃(skip)
完成一个多分支的inception,类代码如下:
class InceptionA(torch.nn.Module):
def __init__(self, in_channels):
super().__init__()
self.branch1x1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1) #实现1x1的kernel
self.branch5x5_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1) #5x5的kernel
self.branch5x5_2 = torch.nn.Conv2d(16, 24, kernel_size=5, padding=2)
self.branch3x3_1 = torch.nn.Conv2d(in_channels, 16, kernel_size=1) #3x3的kernel
self.branch3x3_2 = torch.nn.Conv2d(16, 24, kernel_size=3, padding=1)
self.branch3x3_3 = torch.nn.Conv2d(24, 24, kernel_size=3, padding=1)
self.branch_pool = torch.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)
branch3x3 = self.branch3x3_1(x)
branch3x3 = self.branch3x3_2(branch3x3)
branch3x3 = self.branch3x3_3(branch3x3)
branch_pool = F.avg_pool2d(x, kernel_size=3, stride=1, padding=1)
branch_pool = self.branch_pool(branch_pool)
outputs = [branch1x1, branch5x5, branch3x3, branch_pool]
return torch.cat(outputs, dim=1) #按照第一个维度进行拼接
在Net类里就要增加Inception的操作
class Net(torch.nn.Module):
def __init__(self):
super().__init__()
self.conv1 = torch.nn.Conv2d(1, 10, kernel_size=5)
self.incep1 = InceptionA(in_channels=10) #
self.conv2 = torch.nn.Conv2d(88, 20, kernel_size=5) #这里88是由incep1输出的tensor的长度
self.incep2 = InceptionA(in_channels=20)
self.pooling = torch.nn.MaxPool2d(2)
self.fc = torch.nn.Linear(1408, 10) #1408是最后输出的长度
self.Relu = torch.nn.ReLU()
def forward(self, x):
batch_size = x.size(0)
x = self.Relu(self.pooling(self.conv1(x)))
x = self.incep1(x)
x = self.Relu(self.pooling(self.conv2(x)))
x = self.incep2(x)
x = x.view(batch_size, -1)
x = self.fc(x)
return x
最后优化后准确率达到98%
对于ResNetBlock,可以用来避免梯度消失的问题。
class ResidualBlock(torch.nn.Module):
def __init__(self, channels): #Res需要保证输入输出的通道数相同,可将其作为参数传入
super().__init__()
self.channels = channels
self.conv1 = torch.nn.Conv2d(channels, channels, kernel_size=3, padding=1)
self.conv2 = torch.nn.Conv2d(channels, channels, kernel_size=3, padding=1)
self.Relu = torch.nn.ReLU()
def forward(self, x):
y = self.Relu(self.conv1(x))
y = self.conv2(y)
return self.Relu(x+y) #将x和经过两层之后的结果相加
使用ResNet后可将准确率提升到99%
第十二讲 循环神经网络(RNN)
处理带有序列模式的数据
某个数据依赖于前一个数据
如:自然语言
o
=
1
4
∑
i
=
1
4
z
i
=
1
4
∑
i
=
1
4
3
(
x
i
+
2
)
2
o=\frac14\sum_{i=1}^4z_i=\frac14\sum_{i=1}^43(x_i+2)^2
o=41i=1∑4zi=41i=1∑43(xi+2)2 所以
∂
o
∂
x
i
∣
x
i
=
1
=
9
2
=
4.5
\frac{\partial{o}}{\partial{x_i}}\bigr\rvert_{x_i=1}=\frac{9}{2}=4.5
∂xi∂o
xi=1=29=4.5
数学上,如果有一个函数值和自变量都为向量的函数
, 那么
关于
的梯度就是一个雅可比矩阵(Jacobian matrix):
J
=
(
∂
y
1
∂
x
1
⋯
∂
y
1
∂
x
n
⋮
⋱
⋮
∂
y
m
∂
x
1
⋯
∂
y
m
∂
x
n
)
J=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}}\ \vdots & \ddots & \vdots\ \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)
J=(∂x1∂y1⋯∂xn∂y1 ⋮⋱⋮ ∂x1∂ym⋯∂xn∂ym) 而torch.autograd这个包就是用来计算一些雅克比矩阵的乘积的。例如,如果
是一个标量函数的
的梯度:
v
=
(
∂
l
∂
y
1
⋯
∂
l
∂
y
m
)
v=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)
v=(∂y1∂l⋯∂ym∂l) 那么根据链式法则我们有
关于
的雅克比矩阵就为:
v
J
=
(
∂
l
∂
y
1
⋯
∂
l
∂
y
m
)
(
∂
y
1
∂
x
1
⋯
∂
y
1
∂
x
n
⋮
⋱
⋮
∂
y
m
∂
x
1
⋯
∂
y
m
∂
x
n
)
=
(
∂
l
∂
x
1
⋯
∂
l
∂
x
n
)
v J=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right) \left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}}\ \vdots & \ddots & \vdots\ \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)=\left(\begin{array}{ccc}\frac{\partial l}{\partial x_{1}} & \cdots & \frac{\partial l}{\partial x_{n}}\end{array}\right)
vJ=(∂y1∂l⋯∂ym∂l)(∂x1∂y1⋯∂xn∂y1 ⋮⋱⋮ ∂x1∂ym⋯∂xn∂ym)=(∂x1∂l⋯∂xn∂l)
让我们再次回到本节的房价预测问题。如果我们对训练数据集里的3个房屋样本(索引分别为1、2和3)逐一预测价格,将得到
y
^
(
1
)
=
x
1
(
1
)
w
1
+
x
2
(
1
)
w
2
+
b
,
y
^
(
2
)
=
x
1
(
2
)
w
1
+
x
2
(
2
)
w
2
+
b
,
y
^
(
3
)
=
x
1
(
3
)
w
1
+
x
2
(
3
)
w
2
+
b
.
\begin{aligned} \hat{y}^{(1)} &= x_1^{(1)} w_1 + x_2^{(1)} w_2 + b,\ \hat{y}^{(2)} &= x_1^{(2)} w_1 + x_2^{(2)} w_2 + b,\ \hat{y}^{(3)} &= x_1^{(3)} w_1 + x_2^{(3)} w_2 + b. \end{aligned}
y^(1)=x1(1)w1+x2(1)w2+b, y^(2)=x1(2)w1+x2(2)w2+b, y^(3)=x1(3)w1+x2(3)w2+b.
广义上讲,当数据样本数为 n
,特征数为 d
时,线性回归的矢量计算表达式为
y
^
=
X
w
+
b
\boldsymbol{\hat{y}} = \boldsymbol{X} \boldsymbol{w} + b
y^=Xw+b 其中模型输出
批量数据样本特征
,权重
, 偏差
。相应地,批量数据样本标签
。设模型参数
,我们可以重写损失函数为
ℓ
(
θ
)
=
1
2
n
(
y
^
−
y
)
⊤
(
y
^
−
y
)
\ell(\boldsymbol{\theta})=\frac{1}{2n}(\boldsymbol{\hat{y}}-\boldsymbol{y})^\top(\boldsymbol{\hat{y}}-\boldsymbol{y})
ℓ(θ)=2n1(y^−y)⊤(y^−y)
小批量随机梯度下降的迭代步骤将相应地改写为 θ ← θ − η ∣ B ∣ ∑ i ∈ B ∇ θ ℓ ( i ) ( θ ) , \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \nabla_{\boldsymbol{\theta}} \ell^{(i)}(\boldsymbol{\theta}), θ←θ−∣B∣ηi∈B∑∇θℓ(i)(θ),
其中梯度是损失有关3个为标量的模型参数的偏导数组成的向量: ∇ θ ℓ ( i ) ( θ ) = [ ∂ ℓ ( i ) ( w 1 , w 2 , b ) ∂ w 1 ∂ ℓ ( i ) ( w 1 , w 2 , b ) ∂ w 2 ∂ ℓ ( i ) ( w 1 , w 2 , b ) ∂ b ] = [ x 1 ( i ) ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) x 2 ( i ) ( x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ) x 1 ( i ) w 1 + x 2 ( i ) w 2 + b − y ( i ) ] = [ x 1 ( i ) x 2 ( i ) 1 ] ( y ^ ( i ) − y ( i ) ) \nabla_{\boldsymbol{\theta}} \ell^{(i)}(\boldsymbol{\theta})= \begin{bmatrix} \frac{ \partial \ell^{(i)}(w_1, w_2, b) }{\partial w_1} \ \frac{ \partial \ell^{(i)}(w_1, w_2, b) }{\partial w_2} \ \frac{ \partial \ell^{(i)}(w_1, w_2, b) }{\partial b} \end{bmatrix} = \begin{bmatrix} x_1^{(i)} (x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}) \ x_2^{(i)} (x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)}) \ x_1^{(i)} w_1 + x_2^{(i)} w_2 + b - y^{(i)} \end{bmatrix}= \begin{bmatrix} x_1^{(i)} \ x_2^{(i)} \ 1 \end{bmatrix} (\hat{y}^{(i)} - y^{(i)}) ∇θℓ(i)(θ)=[∂w1∂ℓ(i)(w1,w2,b) ∂w2∂ℓ(i)(w1,w2,b) ∂b∂ℓ(i)(w1,w2,b)]=[x1(i)(x1(i)w1+x2(i)w2+b−y(i)) x2(i)(x1(i)w1+x2(i)w2+b−y(i)) x1(i)w1+x2(i)w2+b−y(i)]=[x1(i) x2(i) 1](y^(i)−y(i))
为了进一步提升计算效率,我们通常对小批量数据做矢量计算。广义上讲,给定一个小批量样本,其批量大小为 n n n,输入个数(特征数)为 d d d,输出个数(类别数)为 q q q。设批量特征为 X ∈ R n × d \boldsymbol{X} \in \mathbb{R}^{n \times d} X∈Rn×d。假设softmax回归的权重和偏差参数分别为 W ∈ R d × q \boldsymbol{W} \in \mathbb{R}^{d \times q} W∈Rd×q和 b ∈ R 1 × q \boldsymbol{b} \in \mathbb{R}^{1 \times q} b∈R1×q。softmax回归的矢量计算表达式为