博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MXNET:卷积神经网络基础
阅读量:7057 次
发布时间:2019-06-28

本文共 3726 字,大约阅读时间需要 12 分钟。

卷积神经网络(convolutional neural network)。它是近年来深度学习能在计算机视觉中取得巨大成果的基石,它也逐渐在被其他诸如自然语言处理、推荐系统和语音识别等领域广泛使用。

目前我关注的问题是:

  • 输入数据的构建,尤其是多输入、多输出的情况。
  • finetune的实现,如何将已训练网络的部分层拿出来作为其他网络的一部分。

二维卷积层

二维卷积:

%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-07-21%20%E4%B8%8B%E5%8D%882.34.40.png

实现如下:

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)# output [[ 19.  25.] [ 37.  43.]]

二维卷积层就是将输入和卷积核做相关运算,然后加上一个标量偏差来得到输出。

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()

卷积运算的计算与二维相关运算类似,唯一的区别是反向的将核数组跟输入做乘法,即 Y[0, 0] = (X[0:2, 0:2] * K[::-1, ::-1]).sum()。

但是因为在卷积层里 K 是学习而来的,所以不论是正向还是反向访问都可以

通过数据学习核数组

虽然我们之前构造了 Conv2D 类,但由于 corr2d 使用了对单个元素赋值([i, j]=)的操作会导致无法自动求导,下面我们使用 Gluon 提供的 Conv2D 类来实现这个例子。

# 构造一个输出通道是 1(将在后面小节介绍通道),核数组形状是 (1,2) 的二维卷积层。conv2d = nn.Conv2D(1, 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        if i % 2 == 1:            print('batch %d, loss %.3f' % (i, l.sum().asscalar()))    l.backward()    # 为了简单起见这里忽略了偏差。    conv2d.weight.data()[:] -= 3e-2 * conv2d.weight.grad()

填充和步幅

一般来说,假设输入形状是 \(n_h×n_w\),卷积核形状是 \(k_h×k_w\),那么输出形状将会是

\[(n_h-k_h+1) \times (n_w-k_w+1).\]

所以卷积层的输出形状由输入形状和卷积核形状决定。下面我们将介绍卷积层的两个超参数,填充和步幅,它们可以在给定形状的输入和卷积核下来改变输出形状。

填充是指在输入高和宽的两端填充元素。如果在高两侧一共填充 \(p_h\) 行,在宽两侧一共填充 \(p_w\) 列,那么输出形状将会是

\[(n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1),\]

通常我们会设置 \(p_h=k_h−1\)\(p_w=k_w−1\) 使得输入和输出有相同的高宽,这样方便在构造网络时容易推测每个层的输出形状。假设这里 \(k_h\) 是奇数,我们会在高的两侧分别填充 \(p_h/2\) 行。如果其是偶数,一种可能是上面填充 \(\lceil p_h/2\rceil\) 行,而下面填充 \(\lfloor p_h/2\rfloor\) 行。在宽上行为类似。

卷积神经网络经常使用奇数高宽的卷积核,例如 1、3、5、和 7,所以填充在两端上是对称的。

# 注意这里是两侧分别填充 1,所以 p_w = p_h = 2。conv2d = nn.Conv2D(1, kernel_size=3, padding=1)conv2d.initialize()X = nd.random.uniform(shape=(8, 8))X = X.reshape((1, 1,) + X.shape)Y = conv2d(X)print Y.shape[2:]# output(8, 8)

当然我们可以使用非方形卷积核,使用对应的填充同样可得相同高宽的输出。

conv2d = nn.Conv2D(1, kernel_size=(5, 3), padding=(2, 1))

前面的例子中,在高和宽两个方向上步幅均为 1。自然我们可以使用更大步幅。

一般来说,如果在高上使用步幅 \(s_h\),在宽上使用步幅 \(s_w\),那么输出大小将是

\[\lfloor(n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor(n_w-k_w+p_w+s_w)/s_w\rfloor.\]

如果我们设置\(p_h=k_h−1\)\(p_w=k_w−1\),那么输出大小为\(\lfloor(n_h+s_h-1)/s_h\rfloor \times \lfloor(n_w+s_w-1)/s_w\rfloor\).更进一步,如果输出高宽能分别被高宽上的步幅整除,那么输出将是 \(n_h/s_h \times n_w/s_w\)。也就是说我们成倍的减小了输入的高宽。

conv2d = nn.Conv2D(1, kernel_size=3, padding=1, strides=2)conv2d = nn.Conv2D(1, kernel_size=(3, 5), padding=(0, 1), strides=(3, 4))# output(4, 4)(2, 2)

通道

下图展示了输入通道是 2 的一个例子

%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-07-21%20%E4%B8%8B%E5%8D%883.30.31.png

输入是\(c_i\)通道时,需要一个\(c_i \times k_h \times k_w\)的卷积核。在每个通道里对相应的输入矩阵和核矩阵做相关计算,然后再将通道之间的结果相加得到最终结果。

上面是\(c_o=1\)的情况,如果是多通道输出,那么卷积核的形状变为:\(c_o \times c_i \times k_h \times k_w\).

1x1卷积层

因为使用了最小窗口,它失去了卷积层可以识别高宽维上相邻元素构成的模式的功能,它的主要计算则是在通道维上。

%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202018-07-21%20%E4%B8%8B%E5%8D%883.36.07.png

在之后的模型里我们将会看到 1×1 卷积层是如何当做保持高宽维形状的全连接层使用,其作用是通过调整网络层之间的通道数来控制模型复杂度。

池化层

池化层提出可以缓解卷积层对位置的过度敏感性,也为了降低显存。

同卷积层一样,池化层也可以填充输入高宽两侧的数据和调整窗口的移动步幅来改变输出大小。

我们先构造一个 (1, 1, 4, 4) 形状的输入数据,前两个维度分别是批量和通道。

X = nd.arange(16).reshape((1, 1, 4, 4))

MaxPool2D 类里默认步幅设置成跟池化窗大小一样。下面使用 (3, 3) 窗口,默认获得 (3, 3) 步幅。

pool2d = nn.MaxPool2D(3)# 因为池化层没有模型参数,所以不需要调用参数初始化函数。pool2d(X)# output[[[[ 10.]]]]

我们可以手动指定步幅和填充。

pool2d = nn.MaxPool2D(3, padding=1, strides=2)pool2d = nn.MaxPool2D((2, 3), padding=(1, 2), strides=(2, 3))

转载地址:http://pfgol.baihongyu.com/

你可能感兴趣的文章
如何摆脱工具类
查看>>
Widnows批处理异地备份数据
查看>>
四边形优化DP学习
查看>>
红黑树
查看>>
远程推送脚本,创建任务计划
查看>>
设计模式 工厂和抽象工厂
查看>>
Maven学习第1期---Maven简单介绍
查看>>
#include <bits/stdc++.h>头文件
查看>>
iOS swift 语句只能写在函数体内
查看>>
C# 接收form表单中多个相同name值的问题
查看>>
Eclipse下配置使用Hadoop插件
查看>>
5/3上午
查看>>
回顾“.NET技术”.NET Remoting分布式开发
查看>>
移动开发多平台代码共享“.NET研究”
查看>>
Convert IPv6 Address to IP numbers (C#)
查看>>
总是弹出visual studio 实时调试器 三种解决办法
查看>>
12岁男孩发现Firefox严重安全漏洞获奖3000美元
查看>>
谷歌发安全警告:社交网络威胁用户隐私
查看>>
一起谈.NET技术,System.DateTime详解
查看>>
一起谈.NET技术,VS2010技巧:如何在js文件中使用jQuery智能感知
查看>>