卷积神经网络(CNN)之卷积层的实现

        阅读完上一篇介绍卷积神经网络基础知识之后,相信对于卷积神经网络有了一个初步认识,这一节将如何来实现卷积层,在这之前先来熟悉下输入数据(输入特征)是怎么进行卷积运算的,看图更直观。

卷积神经网络(CNN)之卷积层的实现_第1张图片

图中使用方块来表示,因为实际应用中,输入特征通常都不会是二维数据,比如图片结构(通道,高度,长度),那卷积运算就是输入特征和滤波器(核)做对应的乘法累加的计算,滤波器可以看做是权重,更形象的看成是一个窗口在输入数据里面进行滑动计算。

卷积神经网络(CNN)之卷积层的实现_第2张图片

批处理就是将输入数据打包处理,在之前的全连接神经网络中实现过,通过批处理可以实现处理的高效化,也就是输入数据从3维变成了4维,按照(批处理数量N个,通道,高度,长度)(batchNum,channel,height,width)的顺序来保存数据。网络间传递的是4维数据,对N个数据进行了卷积运算,也就是说将N次的处理汇总成了1次进行。

如果我们按照上述老老实实地实现卷积运算,会有很多for嵌套,而在numpy中使用for语句会效率变得很低,访问元素最好不要使用for语句,所以我们在这之前会引入一个im2col(image to column)图像到矩阵的函数,这个函数在很多使用CNN的框架都会有出现,作用就是将输入数据展开以适合滤波器,最终将会变为矩阵的计算,而这个是numpy对矩阵的乘积是有优化的,非常高效。


im2col示意图:

卷积神经网络(CNN)之卷积层的实现_第3张图片图中可以看出,是将应用了滤波器的区域块,横向展开为1列,所以N个输入数据,将展开成N行的对应列的矩阵。
im2col函数实现如下:

import numpy as np
def im2col(input_data,filter_h,filter_w,stride=1,pad=0):
    '''
    四维转二维
    input_data : 由(数据量,通道,高,长)的4维数组构成的输入数据
    filter_h : 滤波器的高
    filter_w : 滤波器的长
    stride : 步幅
    pad : 填充
    '''
    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
x=np.random.rand(10,3,7,7)#批量10个,通道为3的(7,7)形状的输入数据
im2col(x,5,5,stride=1,pad=0).shape
#经过应用形状为(5,5)步幅为1,填充为0的im2col滤波器之后的形状结果:(90, 75)

 最后我们贴出卷积层实现的代码(在以前的文章也有出现过):

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=1+int((H+2*self.pad-FH)/self.stride)
        out_w=1+int((W+2*self.pad-FW)/self.stride)

        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

其中的反向传播使用到一个col2im函数:

def col2im(col,input_shape,filter_h,filter_w,stride=1,pad=0):
    '''
    input_shape : 输入数据的形状(例:(10,1,28,28))
    '''
    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]

你可能感兴趣的:(Python,深度学习,CNN卷积层,CNN滤波器,CNN卷积神经网络)