二维卷积层
最近在B站看沐神的动手学深度学习视频,记录一下学习过程
查看本文的jupyter notebook格式,更加清晰美观哦!
卷积神经网络是含有卷积层的神经网络。此文章所介绍的卷积神经网络均使用最常见的二维卷积层。它有高和宽两个空间维度,常用来处理图像数据。
二维互相关运算
虽然卷积层得名于卷积运算,但我们通常在卷积层中使用更加直观的互相关(cross-correlation)运算.在corr2d函数中实现互相关运算。它接受输入数组X与核数组K,并输出数组Y。
from mxnet import autograd, nd
from mxnet.gluon import nn
def corr2d(X, K):
h, w = K.shape
Y = nd.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 = nd.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
K = nd.array([[0, 1], [2, 3]])
corr2d(X, K)
[[19. 25.]
[37. 43.]]
<NDArray 2x2 @cpu(0)>
二维卷积层。下面基于corr2d函数来实现一个自定义的二维卷积层。前向计算函数forward直接调用corr2d函数再加上偏差。
class Conv2D(nn.Block):
def __init__(self, kernel_size, **kwargs):
super(Conv2D, self).__init__(**kwargs)
self.weight = self.params.get('weight', shape=kernel_size)
self.bias = self.params.get('bias', shape=(1, ))
def forward(self, x):
return corr2d(x, self.weight.data()) + self.bias.data()
(nd.random.normal(shape=(3, )))d
[0.4838046 0.29956347 0.15302546]
<NDArray 3 @cpu(0)>
图像中物体边缘检测.下面来看一个卷积层的简单应用:检测图像中物体边缘,即找到像素变化的位置。首先构造一张6 x 8的图像(即高和宽分别为6像素和8像素的图像)。它的中间4列为黑(0),其余为白(1)。
(nd.random.normal(shape=3))
[ 1.2546344 -0.54877406 -0.68106437]
<NDArray 3 @cpu(0)>
X = nd.ones((6, 8))
X[:, 2:6] = 0
X
[[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]])
Y = corr2d(X, K)
Y
[[ 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)>
从上面的结果可以看出,从白到黑和从黑到白的边缘分别被检测成了1和-1。其余部分的输出全是0。由此,卷积层可以通过重复使用卷积核有效表征局部空间。
通过数据学习核数组
之前构造的Conv2D类,由于corr2d使用了对单个元素赋值([i, j]=)的操作,因而无法自动求梯度。下面我们是用Gluon提供的Conv2D类来实现。
# 构造一个输出通道数为1,核数组形状为(1,2)的二维卷积层
conv2d = nn.Conv2D(2, kernel_size=(1, 2))
conv2d.initialize()
# 二维卷积层使用4维输入,格式为(样本,通道,高,宽),这里批量大小(批量中的样本数)和通道数均为1
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
for i in range(10):
with autograd.record():
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()))
batch 2, loss 10.347
batch 4, loss 1.781
batch 6, loss 0.317
batch 8, loss 0.061
batch 10, loss 0.013
可以看到迭代10次之后已经降到了一个比较小的值。学习到的核数组入下:
conv2d.weight.data().reshape((1, 2))
[[ 0.98359346 -0.99278635]]
<NDArray 1x2 @cpu(0)>


被折叠的 条评论
为什么被折叠?



