卷积神经网络
卷积
从全连接到卷积
-
分类猫和狗的图片
- 使用一个还不错的相机采集图片(12M像素)
- RGB图片有36个元素
- 使用100大小的单隐藏层MLP,模型有3.6B个元素
- 远多于世界上所有的猫和狗的总和(900M狗,600M猫)
- 远多于世界上所有的猫和狗的总和(900M狗,600M猫)
-
我们在图片中找Waldo在哪里,这时候我们有两个原则
- 平移不变性。也就是说在图片任何地方,我们识别一个东西,这个识别器不需要改变
- 局部性。我们要找一个东西我们只需要看局部的信息就可以了。
重新考察全连接层
- 在前面全连接层我们识别一张图片的时候,我们是将图片展成一个一维的张量了。现在我们将输入和输出变形为矩阵(宽度,和高度),因为需要存放一些空间信息
- 将权重变形为4-D张亮(h,w) 到 ( h ′ , w ′ ) (h', w') (h′,w′)。记为: h i , j = ∑ k , l w i , j , k , l x k , l = ∑ a , b v i , j , a , b x i + a , j + b h_{i,j} = \sum_{k,l}w_{i,j,k,l}x_{k,l}=\sum_{a,b}v_{i,j,a,b}x_{i+a,j+b} hi,j=∑k,lwi,j,k,lxk,l=∑a,bvi,j,a,bxi+a,j+b.这里h是我们的输出,x是我们的输入,w是我们的权重,
- V是W的重新索引使得 v i , j , a , b = w i , j , i + a , j + b v_{i,j,a,b}=w_{i,j,i+a,j+b} vi,j,a,b=wi,j,i+a,j+b
原则一:平移不变性
- x的平移导致h的平移 h i , j = ∑ a , b v i , j , a , b x i + a , j + b h_{i,j}=\sum_{a,b}v_{i,j,a,b}x_{i+a,j+b} hi,j=∑a,bvi,j,a,bxi+a,j+b
- v不应该依赖于(i, j)
- 解决方案: v i , j , a , b = v a , b v_{i,j,a,b}=v_{a,b} vi,j,a,b=va,b。则 h i , j = ∑ a , b v a , b x i + a , j + b h_{i,j}=\sum_{a,b}v_{a,b}x_{i+a,j+b} hi,j=∑a,bva,bxi+a,j+b
- 这就是2维卷积,在数学上来说叫做2维的较叉相关
原则二:局部性
h i , j = ∑ a , b v a , b x i + a , j + b h_{i,j}=\sum_{a,b}v_{a,b}x_{i+a,j+b} hi,j=∑a,bva,bxi+a,j+b
- 当评估 h i , j h_{i,j} hi,j时,我们不应该用远离 x i , j x_{i,j} xi,j的参数
- 解决方案:当$\left | a \right |,\left | b\right | > \Delta , 使 得 ,使得 ,使得v_{a,b}=0 , , ,h_{i,j}=\sum_{a=-\Delta }^{\Delta }\sum_{b=-\Delta }^{\Delta }v_{a,b}x_{i+a,j+b}$
总结
卷积层
二维交叉相关
- 这里我们的输入是一个33的矩阵,这里的卷积核kernel是一个22的,所有这里的delta是1,这里的输出是由,kernel乘以输入的元素得到的,这里是元素的相乘,然后每次移动delta个位置
二维卷积层
-
二维卷积层,从神经网络的角度来说,就是输入是一个 n h × n w n_h\times n_w nh×nw,卷积核是 k h × k w k_h\times k_w kh×kw,偏差b是一个实数,输出是 Y = X ∗ W + b Y=X*W+b Y=X∗W+b,这里的Y中的+1是因为每一步大小为1
-
下面的图片我们可以看到不同的卷积核可以带来不同的效果,卷积核是我们学到的
较叉相关vs卷积
一维和三维交叉相关
总结
图像卷积代码实现
# 互相关运算
import torch
from torch import nn
from d2l import torch as d2l
# 这里的输入是我们的输入x和卷积核K
def corr2d(X, K):
"""算二维互相关运算"""
h, w = K.shape # 这里就是拿出卷积核的行和列
Y = torch.zeros(X.shape[0] -h + 1, X.shape[1] - w + 1) # 根据上面理论我们可以得到我们输出的矩阵的大小,也就是输入矩阵的行减去卷积核的行+1,列同理
# 下面的两个for循环的作用是给,输出矩阵的每个元素赋值,这里就是拿卷积核和对应的区的元素做乘法运算
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
return Y
# 验证上述二维互相关运算的输出
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
corr2d(X, K)
tensor([[19., 25.],
[37., 43.]])
# 实现二维卷积层
class Conv2D(nn.Module):
def __init__(self, kernel_size): # 这里kernel_size就是卷积核的大小是一个超参数,可以定义为3*3或2*2,都行
super().__init__()
self.weight = nn.Parameter(torch.rand(kernel_size)) # 这个是我们的参数要学的
self.bias = nn.Parameter(torch.zeros(1))
def forward(self, x):
return corr2d(x, self.weight) + self.bias
# 卷积层的一个简单应用:检测图像中不同颜色的边缘
# 我们首先构造一个输入X,这是一个特殊的矩阵它的前两列是1,中间4列是0,最后2列是1,这样我们从0变到1或者从1变到0要么是1要么是-1
X = torch.ones((6, 8))
X[:, 2:6] = 0
X
tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.],
[1., 1., 0., 0., 0., 0., 1., 1.]])
# 这边是构造一个卷积核
K = torch.tensor([[1.0, -1.0]])
Y = corr2d(X, K)
Y
tensor([[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.],
[ 0., 1., 0., 0., 0., -1., 0.]])
# 卷积核K只能检测垂直边缘
corr2d(X.t(), K)
tensor([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
# 学习由X生成Y的卷积核
# 这里我们直接调用了pytorch中定义的一个二维的卷积层,第一个参数的意思是:输入的通道为1,第二个参数的意思是输出的通道为1,kernel_size指定核的大小
conv2d = nn.Conv2d(1, 1, kernel_size=(1, 2), bias=False)
X = X.reshape((1, 1, 6, 8)) # 这里reshape的原因是因为,第一个维度是通道维,第二个维度是批量大小维,后面的才是值
Y = Y.reshape((1, 1, 6, 7))
for i in range(10):
Y_hat = conv2d(X) # 这里是将X传入到网络,计算出输出
l = (Y_hat - Y) ** 2 # 这里采用了一个均方误差
conv2d.zero_grad() # 将梯度置为0
l.sum().backward() # 反向传播求梯度
conv2d.weight.data[:] -= 3e-2 * conv2d.weight.grad # 这里手写了一个梯度下降,3e-1是学习率
if (i + 1) % 2 == 0:
print(f'batch {i+1}, loss {l.sum():.3f}')
batch 2, loss 11.542
batch 4, loss 2.754
batch 6, loss 0.797
batch 8, loss 0.271
batch 10, loss 0.102
# 学习到的卷积核的权重张量
conv2d.weight.data.reshape((1, 2))
tensor([[ 1.0170, -0.9533]])