第六章.卷积神经网络(CNN)—卷积层(Convolution)&池化层(Pooling)

本文介绍了卷积神经网络(CNN)的基础,包括卷积层如何保持数据形状,卷积运算的细节,如填充和步幅对输出大小的影响,以及im2col函数在简化卷积运算中的作用。同时,文章还讨论了池化层的功能,如最大池化和平均池化,以及它们在处理图像数据时的优势。
摘要由CSDN通过智能技术生成

第六章.卷积神经网络(CNN)

6.1 卷积层(Convolution)&池化层(Pooling)

1.整体结构

以5层神经网络的实现为例:

1).基于全连接层(Affine)的网络

全连接层:相邻层的所有神经元之间都有连接
在这里插入图片描述

2).常见的CNN的网络

在这里插入图片描述

3).全连接层存在的问题

数据的形状容易被“忽视”了,比如输入的数据是图像时,图像通常是高,长,通道方向上的3维形状,但是,全连接层输入时,需要将3维数据拉平为1维数据,所以无法利用与形状相关的信息。

2.卷积层

卷积层可以保持形状不变,当输入数据是图像时,卷积层会以3维数据的形式接受输入数据,并同样以3维数据的形式输出至下一层,因此在CNN中可以正确理解图像等具有形状的数据。

1).卷积运算

  • 一维数据的卷积计算

    示例:填充为0,步幅为1的卷积运算在这里插入图片描述

  • 三维数据的卷积计算

    示例:填充为0,步幅为1的卷积运算
    在这里插入图片描述
    计算方式
    通道方向有多特征图时,会按通道进行输入数据和滤波器的卷积运算,并结果相加,从而得到输出。

    注意
    ①.在三维数据的卷积运算中,输入数据和滤波器的通道数必须设置为相同的值。(在本例中同时设置为3)

  • 结合方块来思考卷积计算
    在这里插入图片描述
    图像描述:
    每个通道只有一个偏置,这里偏置的形状为(FN,1,1),滤波器的输出结果形状为(FN,OH,OW),这两个方块相加,要对滤波器的输出结果按通道加上相同的偏置。

  • 卷积计算的批处理
    在这里插入图片描述
    注意
    ①.网络间传递的是四维数据,对这N个数据进行了卷积运算,也就是说,批处理将N次的处理汇总成1次进行。

2).填充&步幅

  • 填充

    ①.定义
    在进行卷积层处理之前,有时需要向输入数据的周围填入固定的数据(比如填充值0等),这称为填充。
    在这里插入图片描述

    ②.目的
    主要是为了调整输出的大小,因为每次在进行卷积运算时都会缩小空间,那么在某个时刻输出大小就有可能变为1,导致无法在进行卷积运算,为了避免出现这种情况,就要使用填充

  • 步幅

    ①.定义
    应用滤波器的位置间隔称为步幅。(之前的应用都是步幅为1,下面的应用步幅为2)
    在这里插入图片描述

3).计算输出核的大小

假设输入大小为(H,W),滤波大小为(FH,FW),输出大小为(OH,OW),填充为P,步幅为S,输出大小为:
在这里插入图片描述
注意
①.所设定的值必须使式(H+2P-FH)/S(W+2P-FW)/S分别可以整除,当输入大小无法整除时,需要采取报错等对策。有的深度学习框架,当值无法除尽时,有时会向最接近的整数四舍五入,不进行报错而继续进行。

4).实现扩展

①.CNN处理4维数据时,卷积运算的实现看上去会很复杂,可以使用im2col(图像转化成矩阵)这个技巧,问题会变得很简单。

②.im2col函数会将输入数据展开以适合滤波器(权重)。具体来说,对于输入数据,将应用滤波器的区域(3维方块)横向展开为一列。

在这里插入图片描述
③.卷积运算的滤波器处理细节:使用im2col函数展开输入数据后,将卷积层的滤波器纵向展开为一列,计算两个矩阵的乘积,最后转化(reshape)为输出数据大小。
在这里插入图片描述

5).卷积层的实现

import numpy as np


# 从图像到矩阵
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    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

    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):
    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]


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):
        FN, C, FH, FW = self.W.shape
        N, C, H, W = x.shape
        out_h = int((H + 2 * self.pad - FH) / self.stride) + 1
        out_w = int((W + 2 * self.pad - FW) / self.stride) + 1

        col = im2col(x, FH, FW, self.stride, self.pad)
        col_W = self.W.reshape(FN, -1).T

        out = np.dot(col, col_W) + self.b
        out = out.reshape(N, out_h, out_w, -1).transpose(0, 3, 1, 2)

        self.x = x
        self.col = col
        self.col_W = col_W

        return out

    # 反向传播
    def backward(self, dout):
        FN, C, FH, FW = self.W.shape
        dout = dout.transpose(0, 2, 3, 1).reshape(-1, FN)

        self.db = np.sum(dout, axis=0)
        self.dW = np.dot(self.col.T, dout)
        self.dW = self.dW.transpose(1, 0).reshape(FN, C, FH, FW)

        dcol = np.dot(dout, self.col_W.T)
        dx = col2im(dcol, self.x.shape, FH, FW, self.stride, self.pad)

        return dx

3.池化层

池化是缩小高,长方向上的空间运算。

1).池化的处理方法

示例:填充为0,步幅为2的池化

  • Max池化(本书中所说的池化层是Max池化)
    方式:从目标区域中取最大值
    在这里插入图片描述

  • Average池化
    方式:从目标区域中取均值在这里插入图片描述

2).池化层的特征

  • 没有要学习的参数
    池化层和卷积层不同,没有要学习的参数。池化只是从目标区域中选出最大值(或均值)。

  • 通道数不发生改变
    经过池化运算,输入数据和输出数据的通道数不发生变化,计算是按通道独立进行的。
    在这里插入图片描述

  • 对微小的位置变化具有鲁棒性(健壮)
    输入数据发生微小偏差时,池化仍会返回相同的结果。

    示例:输入数据在高方向上只偏离一个像素时:
    在这里插入图片描述

3).池化层的实现步骤

①.展开输入数据
②.求各行的最大值
③.转换为合适的输出大小

在这里插入图片描述

4).池化层的实现

import numpy as np


# 从图像到矩阵
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):
    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

    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):
    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]


class Pooling:
    def __init__(self, pool_h, pool_w, stride=1, pad=0):
        self.pool_h = pool_h
        self.pool_w = pool_w
        self.stride = stride
        self.pad = pad

        self.x = None
        self.arg_max = None

    # 正向传播
    def forward(self, x):
        N, C, H, W = x.shape
        out_h = int(1 + (H - self.pool_h) / self.stride)
        out_w = int(1 + (W - self.pool_w) / self.stride)

        col = im2col(x, self.pool_h, self.pool_w, self.stride, self.pad)
        col = col.reshape(-1, self.pool_h * self.pool_w)

        arg_max = np.argmax(col, axis=1)
        out = np.max(col, axis=1)
        out = out.reshape(N, out_h, out_w, C).transpose(0, 3, 1, 2)

        self.x = x
        self.arg_max = arg_max

        return out

    # 反向传播
    def backward(self, dout):
        dout = dout.transpose(0, 2, 3, 1)

        pool_size = self.pool_h * self.pool_w
        dmax = np.zeros((dout.size, pool_size))
        dmax[np.arange(self.arg_max.size), self.arg_max.flatten()] = dout.flatten()
        dmax = dmax.reshape(dout.shape + (pool_size,))

        dcol = dmax.reshape(dmax.shape[0] * dmax.shape[1] * dmax.shape[2], -1)
        dx = col2im(dcol, self.x.shape, self.pool_h, self.pool_w, self.stride, self.pad)

        return dx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值