二维卷积层
卷积神经网络(convolutional neural network
)是含有卷积层(convolutional layer
)的神经网络
二维互相关运算
通常在卷积层中使用更加直观的互相关(cross-correlation
)运算,在二维卷积层中,一个二维输入数组和一个二维核(kernel
)数组通过互相关运算输出一个二维数组.核数组在卷积计算中又称卷积核或过滤器(filter
)卷积核窗口(又称卷积窗口)的形状取决于卷积核的高和宽.在二维互相关运算中,卷积窗口从输入数组的最左上方开始,按从左往右、从上往下的顺序,依次在输入数组上滑动。当卷积窗口滑动到某一位置时,窗口中的输入子数组与核数组按元素相乘并求和,得到输出数组中相应位置的元素。
将上述过程实现在corr2d
函数里。它接受输入数组X
与核数组K
,并输出数组Y
from mxnet import autograd, nd
from mxnet.gluon import nn
def corr2d(X, K): # 本函数已保存在d2lzh包中方便以后使用
# 核数组的长宽
h, w = K.shape
# 输出置0,同时计算出输出数组Y的长宽
Y = nd.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
#枚举Y的长宽
for i in range(Y.shape[0]):
for j in range(Y.shape[1]):
#依次计算X*K再求和
Y[i, j] = (X[i: i + h, j: j + w] * K).sum()
return Y
二维卷积层
二维卷积层将输入和卷积核做互相关运算,并加上一个标量偏差来得到输出。卷积层的模型参数包括了卷积核和标量偏差。在训练模型的时候,通常我们先对卷积核随机初始化,然后不断迭代卷积核和偏差。
#Con2D类继承Block类
class Conv2D(nn.Block):
def __init__(self, kernel_size, **kwargs):
super(Conv2D, self).__init__(**kwargs)
#使用get函数获得param实例,shape=初始化的卷积shape
self.weight = self.params.get('weight', shape=kernel_size)
#使用get函数获得param实例,shape=(1,)
self.bias = self.params.get('bias', shape=(1,))
def forward(self, x):
#卷积计算
return corr2d(x, self.weight.data()) + self.bias.data()
卷积窗口形状为(p imes q)的卷积层称为(p imes q)卷积层。同样,(p imes q)卷积或(p imes q)卷积核说明卷积核的高和宽分别为(p)和(q)。
图像中物体边缘检测
一个卷积层的简单应用:检测图像中物体的边缘,即找到像素变化的位置.构造一张(6 imes 8)的图像(即高和宽分别为6像素和8像素的图像)。它中间(4)列为黑((0)),其余为白((1))
X = nd.ones((6, 8))
X[:, 2:6] = 0
X
#output:
[[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.]]
<NDArray 6x8 @cpu(0)>
构造一个高和宽分别为(1)和(2)的卷积核(K)。当它与输入做互相关运算时,如果横向相邻元素相同,输出为(0);否则输出为非(0)
K = nd.array([[1, -1]])
输入X
和我们设计的卷积核K
做互相关运算.将从白到黑的边缘和从黑到白的边缘分别检测成了1
和-1
。其余部分的输出全是0
Y = corr2d(X, K)
Y
#output
[[ 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.]]
<NDArray 6x7 @cpu(0)>
通过数据学习核数组
使用物体边缘检测中的输入数据X
和输出数据Y
来学习我们构造的核数组K
首先构造一个卷积层,将其卷积核初始化成随机数组。接下来在每一次迭代中,我们使用平方误差来比较Y和卷积层的输出,然后计算梯度来更新权重.简单起见,这里的卷积层忽略了偏差
使用Gluon
提供的Conv2D
类来实现这个例子:
# 构造一个输出通道数为1(将在“多输入通道和多输出通道”一节介绍通道),核数组形状是(1, 2)的二
# 维卷积层
conv2d = nn.Conv2D(1, kernel_size=(1, 2))
#初始化权重参数
conv2d.initialize()
# 二维卷积层使用4维输入输出,格式为(样本, 通道, 高, 宽),这里批量大小(批量中的样本数)和通
# 道数均为1
#重新reshape
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
#迭代十次
for i in range(10):
with autograd.record():
#将卷积层应用在X上计算出预测值
Y_hat = conv2d(X)
#计算出平方损失
l = (Y_hat - Y) ** 2
l.backward()
# 简单起见,这里忽略了偏差
# 利用梯度迭代
conv2d.weight.data()[:] -= 3e-2 * conv2d.weight.grad()
if (i + 1) % 2 == 0:
print('batch %d, loss %.3f' % (i + 1, l.sum().asscalar()))
互相关运算和卷积运算
有点没看懂
特征图和感受野
二维卷积层输出的二维数组可以看作输入在空间维度(宽和高)上某一级的表征,也叫特征图(feature map
)
影响元素(x)的前向计算的所有可能输入区域(可能大于输入的实际尺寸)叫做(x)的感受野(receptive field
)
经过卷积层的特征提取之后,新生成的元素的感受野是原输入数组的部分元素