动手实现深度神经网络7 实现CNN

动手实现深度神经网络7 实现CNN

经过了之前的学习,我们已经掌握了如何构造、优化和使用一个深度神经网络了。然而,单纯的深度神经网络无法考虑到图像中相邻像素中可能存在的联系。于是,这里我们将实现一种专门针对图像处理的卷积神将网络CNN。

关于CNN的理论知识我之前的文章有详细的讲述:Python深度学习入门笔记 4 CNN。这里我们依据不再赘述,直接展示代码中可能会遇到的问题。

卷积层的实现

CNN中处理的是4维数据(batch_num, channel, height, width),因此卷积运算的实现看上去会很复杂,但是通过im2col,问题就会变得很简单。

im2col是一个函数,将输入数据展开以适合滤波器(权重)。对每个3维的输入数据应用im2col后,数据转换为2维矩阵(正确地讲,是把包含批数量的4维数据转换成了2维数据)。

使用im2col展开后,展开后的元素个数会多于原方块的元素个数。因此,使用im2col的实现存在比普通的实现消耗更多内存的缺点。但是,汇总成一个大的矩阵进行计算,对计算机的计算颇有益处。比如,在矩阵计算的库(线性代数库)等中,矩阵计算的实现已被高度最优化,可以高速地进行大矩阵的乘法运算。因此,通过归结到矩阵计算上,可以有效地利用线性代数库

im2col和col2im的实现

col2im是im2col的逆过程。

代码中需要注意的地方我是用!标注,然后会在下面进行说明。

def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    """

    Parameters
    ----------
    input_data : 由(数据量, 通道, 高, 长)的4维数组构成的输入数据
    filter_h : 滤波器的高
    filter_w : 滤波器的长
    stride : 步幅
    pad : 填充

    Returns
    -------
    col : 2维数组
    """
    N, C, H, W = input_data.shape
    # 计算输出的大小
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1
    # np.pad用于填充
    # 第一个参数是填充的数组
    # 第二个参数是一个列表,表示各个轴填充的个数:
    # 例如[(1,2),(0,0)]表示 第一个轴上,前面填充1个,后面填充2个 第二个轴上 前面填充0个,后面填充0个
    # 第三个参数是填充的模式,默认是'constant'常数填充,默认是0,可以通过constant_values = (0,0)这样设置。
    #具体到这里,因为第一个轴是数据量,第二个周是通道数,所以不填充,第三第四个轴是高和长,才根据输入进行填充
    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')
    # 一 !!!!!!!!!
    col = np.zeros((N, C, filter_h, filter_w, out_h, out_w))
    # 二 !!!!!!!!!!
    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            col[:, :, y, x, :, :] = img[:, :, y:y_max:stride, x:x_max:stride]

    # 三 !!!!!!!!!!
    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)
    return col


def col2im(col, input_shape, filter_h, filter_w, stride=1, pad=0):
    """

    Parameters
    ----------
    col :
    input_shape : 输入数据的形状(例:(10, 1, 28, 28))
    filter_h :
    filter_w
    stride
    pad

    Returns
    -------

    """
    N, C, H, W = input_shape
    out_h = (H + 2*pad - filter_h)//stride + 1
    out_w = (W + 2*pad - filter_w)//stride + 1
    col = col.reshape(N, out_h, out_w, C, filter_h, filter_w).transpose(0, 3, 4, 5, 1, 2)

    img = np.zeros((N, C, H + 2*pad + stride - 1, W + 2*pad + stride - 1))
    for y in range(filter_h):
        y_max = y + stride*out_h
        for x in range(filter_w):
            x_max = x + stride*out_w
            img[:, :, y:y_max:stride, x:x_max:stride] += col[:, :, y, x, :, :]

    return img[:, :, pad:H + pad, pad:W + pad]

一!!!!!!!!!

这里生成一个六维0矩阵,我们来说明一下。首先把“数据量N”和“通道数C”摘出来不考虑,因为要把二维矩阵转换为一个适合与卷积相乘的形状,所以一个(H*W)的矩阵,要暂时看成out_h*out_w个(ilter_h,filter_w)形状的矩阵,也就是一个(filter_h, filter_w, out_h, out_w)的四维矩阵。

img

二!!!!!!!!!

这里是将img中的数据对应复制到六维矩阵col中。实际上是每次将每个(ilter_h,filter_w)矩阵中的1一个像素复制到col中。

QQ图片20220305164534

三!!!!!!!!!

这里将col转换为最终需要的(N*out_h*out_w, C*filter_h*filter_w)的矩阵。

QQ图片20220305170109

卷积层代码

class Convolution:
    def __init__(self, W, b, stride=1, pad=0):
        self.W = W
        self.b = b
        self.stride = stride
        self.pad = pad

        # 中间数据(backward时使用)
        self.x = None
        self.col = None
        self.col_W = None

        # 权重和偏置参数的梯度
        self.dW = None
        self.db = None

    def forward(self, x
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值