深度学习入门阅读笔记(第7-8章)

126 阅读5分钟

第七章主要讲CNN的实现。第八章更多的作者对未来的展望,粗略阅读即可,介绍了一点CV的应用和发展期待。

卷积神经网络(CNN)

使用场景

  • 图像识别,语音识别

基本概念

  • 总体上,以 卷积层——输出层(ReLu)——池化层(Pooling)
  • 在全连接层中,MNIST数据集被拉成一个784 * 1的向量,这忽视了各个相近像素块之间的信息,而且输入特征太多了,计算复杂,而卷积则解决了该问题
  • 随着卷积层数的加深,信息会越来越 抽象。比如一开始对图像的边缘有反应,到后来是对复杂的物体表面有反应

基本操作

  • 卷积:输入图像对应区域 与 卷积核做相乘相加操作,把结果保存到对应位置中
  • 填充(Padding):指在输入的图像上加上一圈0(行列都加),这个操作的目的是 调整输出的大小
  • 步幅(Stride):卷积核会在输入图像上按照 滑动窗口的思想进行滑动计算,每次滑行的距离称为 步幅
  • 计算公式,假设输入大小是(H,W),卷积核大小是(FH,FW),填充为P,Stride为S,则输出图像大小为?OH=H+2PFHS+1OH=\frac{H+2P-FH}{S}+1 OW=W+2PFWS+1OW=\frac{W+2P-FW}{S}+1

模块

im2col函数

  • 该函数主要是为了落地卷积使用的。实际使用中,采用 矩阵乘法的形式来进行卷积,为此需要把卷积核化为 列向量,把输入图像卷积的图像化为 行向量
import numpy as np  
def im2col(input_data, filter_h, filter_w, stride=1, pad=0):  
    # 输入是一个批次为 N , 通道为C,高为H,宽度为W的图像  
    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 #计算卷积后的图像宽度  
  
    # 对图像进行Padding填充  
    img = np.pad(input_data, [(0,0), (0,0), (pad, pad), (pad, pad)], 'constant')  
    # 6D张量来存储图像的每个滑动窗口。这个张量的维度分别代表:批量大小、通道数、卷积核的高度、卷积核的宽度、输出的高度和输出的宽度。  
    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]  
    # 这两个嵌套的for循环遍历了卷积核的每个位置。对于每个位置,从填充后的图像中提取与卷积核对应位置的子图像,并将其存储在col张量中。  
    col = col.transpose(0, 4, 5, 1, 2, 3).reshape(N*out_h*out_w, -1)  
    # 首先,使用transpose方法对col张量的维度进行重新排序,使其从(N, C, filter_h, filter_w, out_h, out_w)变为(N, out_h, out_w, C, filter_h, filter_w)。然后,使用reshape方法将其变形成一个2D张量,其中行数为N*out_h*out_w(即所有可能的滑动窗口的数量),列数为C*filter_h*filter_w(即每个滑动窗口中的元素数量)。  
    return col
    x1 = np.random.randn(1,3,7,7) # 1个数据,3个通道,宽高为7  
col1 = im2col(x1, 5,5,stride=1,pad=0)  
print(col1.shape)   
#结果是 (9,75)x1 = np.random.randn(10,3,7,7)  
col1 = im2col(x1,5,5,stride=1,pad=0)  
print(col1.shape)  
#结果是 (90,75)

卷积层

三维卷积

  • 图像的输入是通道,高,宽,用(C,H,W)来表示。那么卷积核的大小就是(C,FH,FW)。也就是说,通道数必须是一致的

实现

class Convolution:  
    def __init__(self,W,b,stride=1,pad=0):  
        self.W=W  
        self.b=b  
        self.stride=stride  
        self.pad=pad  
    def forward(self,x):  
        # 卷积核的参数,分别是数目,通道,高度,宽度  
        FN,C,FH,FW = self.W.shape  
        #输入图像的参数  
        N,C,H,W = x.shape  
        # 计算输出图像的高度,宽度  
        out_h = int(1 + (H-FH+2*self.pad)/self.stride)  
        out_w = int(1 + (W-FW+2*self.pad)/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维度从N,H,W,C变为 N,C,H,Wout = out.reshape(N,out_h,out_w,-1).transpose(0,3,1,2)

池化层

  • 目的是把特征图像进行高,宽方向上的缩小,比如把一个2* 2大小的矩阵,缩小成1个元素,如果该元素是矩阵中最大的,也叫 MaxPooLing
  • 特点:1.没有参数2.通道数目不变3.对微小变化有健壮性(池化的原因,小的变了不影响大的)
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  
    def foward(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)  
          
        out = np.max(col,axis=1)  
        out = out.reshape(N,out_h,out_w,C).transpose(0,3,1,2)  
        return out

其他网络模型(这本书就略微提了一下)

VGG

  • 由卷积层和池化层构成的CNN,有16层和19层。
  • 3* 3的小滤波器连续进行,最后由全连接层输出结果

GooLeNet

  • 横向上有 宽度,这种结构称为 Inception,使用了多个 大小不同的滤波器和池化层,最后再合并他们的结果。使用了1* 1的滤波,减少了通道大小和参数。

ResNet

  • 使用了 快捷结构,在连续的两层卷积层中,会把卷积层的输出由F(x)变为F(x)+xF(x)变为F(x)+x.引入这种结构后,加深层数也能继续学习,因为反向传播的时候信号可以没有衰减地传递,可以缓解梯度消失的问题