卷积层
平移不变性和局部性
W 是学习来的一个东西,所以正反顺序并无太大关系。
核矩阵的大小就是局部性,核矩阵大一点就是遍历的范围大一点。
卷积核大、网络层数可能不如卷积核小,网络深的效果好
抖动厉害的可能是数据多样性比较大,或者是批量大小过小,抖动对实验没有影响,关键在于模型效果要越来越好。 解决抖动问题可以调高学习率或者批量变大
填充和步幅(超参)
每一次卷积计算得到的特征图维度都会小于原来的图像。解决这个问题的方法就是填充原图像
填充方法:
填充一行,特征图就增加一行,填充一列,特征图就增加一列。
通常填充核的大小减一的值,这样做使得特征图不会变小。
这里是需要55层
总结:
QA:
- 填充通常使得输入和输出的大小不变,通常设为核大小减1的情况。通常来说步幅为1比较好,单如果计算量过大则可以调大步幅。
- 卷积核的尺度一般选奇数,便于填充。
- 卷积核比较小的情况下,需要使用一个深的神经网络来观察到更多的信息。
- 使用网格搜索可以找到更合适的超参。
- autoglun可以实现自动调参。
- 同等维度变化下,卷积核越大的代价越大。
实现
互相关运算
import torch
from torch import nn
from d2l import torch as d2l
def corr2d(X,K):
"""二维互相关,输入矩阵X, 核矩阵K"""
h,w = K.shape
Y = torch.zeros((X.shape[0] - h + 1,X.shape[1] - w + 1))
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)
实现卷积层
class Conv2D(nn.Module):
def __init__(self,kernel_size):
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 = torch.ones((6,8))
x[:,2:6] = 0
x
k = torch.tensor([[1.0,-1.0]])
1代表白变黑,-1代表黑变白
y = corr2d(x,k)
y
卷积核k只能检测垂直边缘
corr2d(x.t(),k)
学习有x生成y的卷积核
# 黑白图片输入通道为1,彩色图片输入通道为3,第一个参数是输出通道,第二个参数是输入通道,kernel_size是1*2大小
conv2d = nn.Conv2d(1,1,kernel_size=(1,2),bias=False)
x = x.reshape((1,1,6,8))
y = y.reshape((1,1,6,7))
for i in range(10):
y_hat = conv2d(x)
# 均方误差
l = (y_hat - y)**2
# 梯度置0
conv2d.zero_grad()
# 反向传播计算梯度
l.sum().backward()
conv2d.weight.data[:] -= 3e-2*conv2d.weight.grad
if (i+1) % 2 == 0:
print(f'bath{i+1},loss {l.sum():.3f}')
所学卷积核的权重张量
conv2d.weight.data.reshape((1,2))
填充与步幅
在所有侧边填充1各像素
import torch
from torch import nn
def comp_conv2d(conv2d,X):
# 在输入维度前面添加一个通道数和批量大小数,这里是将X提升到四维(1,1,8,8)
X = X.reshape((1,1)+ X.shape)
Y = conv2d(X)
# Y是四维,返回的时候去掉前面两维看起来更直观
return Y.reshape(Y.shape[2:])
# padding 上下左右各填充一行
conv2d = nn.Conv2d(1,1,kernel_size = 3,padding = 1)
X = torch.rand(size=(8,8))
comp_conv2d(conv2d,X).shape
填充不同的高度和宽度
# 上下填充两行,左右填充一列
conv2d = nn.Conv2d(1,1,kernel_size = (5,3),padding = (2,1))
comp_conv2d(conv2d,X).shape
conv2d = nn.Conv2d(1,1,kernel_size=(7,5),padding=(3,2))
comp_conv2d(conv2d,X).shape
设置高度和宽度的步幅为2
# 上下左右填充1,步幅为2
conv2d = nn.Conv2d(1,1,kernel_size=3,padding=1,stride=2)
comp_conv2d(conv2d,X).shape
conv2d = nn.Conv2d(1,1,kernel_size = (3,5),padding=(0,1),stride=(3,4))
comp_conv2d(conv2d,X).shape