Bootstrap

深度学习笔记13-卷积神经网络1

1.卷积神经网络

        卷积神经网络(CNN),它是由多个卷积层、池化层和全连接层构成的前馈神经网络。在卷积神经网络中,包含了可训练的卷积核,这使得卷积神经网络具有强大的表征学习能力。卷积神经网络通过卷积层和池化层进行特征提取,通过全连接层和输出层进行分类。因此卷积神经网络是一种端到端的机器学习模型。

CNN与传统模型

        卷积神经网络已被广泛用于计算机视觉领域,全面取代传统的机器学习模型。传统的机器学习很难解决复杂的图像处理任务,因为传统的模型无法直接进行特征提取,缺乏特征表达能力。如果使用预训练的深度卷积神经网络(例如VGG19网络,进行图片分类),可以使用非常少的训练样本,达到很好的效果。不只是计算机视觉领域,在NLP等其他问题中,CNN同样表现出色。

        无论是计算机视觉还是NLP领域,深度学习正在不断代替传统的机器学习模型。

CNN与全连接网络

        在使用全连接的前馈神经网络处理图像时,会存在“参数过多”和“局部不变性”两个问题,卷积神经网络可以很好的解决这两个问题。

2.卷积层中的卷积运算

        卷积是数学分析中的一种运算,广泛应用于信号处理,图像处理和深度学习等领域。卷积神经网络中通常使用互相关运算,来实现卷积层中的计算。例如在处理图像数据时,卷积核和图像数据都是一个二维数组,将卷积核数据和图像数据进行卷积运算,就会得到一个输出数据。在计算时,卷积运算会将卷积核中的每个元素,与它对应的输入数据中的元素相乘,再把所有乘积加到一起。

        

5*0+0*(-3)+(-3)*1+4*1+1*2+0*(-3)+0*(-4)+(-2)*0+3*1=6

卷积运算的通用公式

:\begin{bmatrix} x_{11} &x_{12} &x_{13} &x_{14} \\ x_{21} &x_{22} &x_{23} &x_{24} \\ x_{31} &x_{32} &x_{33} &x_{34} \\ x_{41} &x_{42} &x_{43} &x_{44} \end{bmatrix} \circledast \begin{bmatrix} w_{11} & w_{12} &w_{13} \\ w_{21} & w_{22} &w_{23} \\ w_{31} & w_{32} &w_{33} \end{bmatrix}=\begin{bmatrix} y_{11} &y_{12} \\ y_{21}&y_{22} \end{bmatrix}

y_{11}=x_{11}*w_{11}+x_{12}+w_{12}+x_{13}*w_{13}+x_{21}*w_{21}+x_{22}*w_{22}+x_{23}*w_{23}+x_{31}*w_{31}+x_{32}*w_{32}+x_{33}*w_{33}

y_{12}=x_{12}*w_{11}+x_{13}*w_{12}+x_{14}*w_{13}+x_{22}*w_{21}+x_{23}*w_{22}+x_{24}*w_{23}+x_{32}*w_{31}+x_{33}*w_{32}+x_{34}*w_{33}

y_{21}=x_{21}*w_{11}+x_{22}*w_{12}+x_{23}*w_{13}+x_{31}*w_{21}+x_{32}*w_{22}+x_{33}*w_{23}+x_{41}*w_{31}+x_{42}*w_{32}+x_{43}*w_{33}

y_{22}=x_{22}*w_{11}+x_{23}*w_{12}+x_{24}*w_{13}+x_{32}*w_{21}+x_{33}*w_{22}+x_{34}*w_{23}+x_{42}*w_{31}+x_{43}*w_{32}+x_{44}*w_{33}

y_{ij}=\ \sum_{u=1}^{m}\sum_{v=1}^{n}w_{uv}\cdot x_{i+u-1,j+v-1}

卷积核中的参数

        卷积核中的数据就是卷积神经网络中的可训练参数。3*3的卷积核就包括9个待训练的参数(w_{11}-w_{33}),卷积层类似于全连接层,除了w参数还有b参数,完成卷积运算后,会向每个输出数据都加上一个偏置b

3.图像边缘检测 

Python实现卷积运算

import numpy as np
#函数传入输入张量x和卷积核张量K
#在函数中,计算X和K的互相关运算结果
def correlation2d(X,K):
    Xh,Xw=X.shape#保存输入数据的高度和宽度
    Kh,Kw=K.shape#保存卷积核的高度和宽度
    #输出数据的大小等于输入数据的大小减去卷积核的大小,再加1
    Yh=Xh-Kh+1#计算输出数据的高度
    Yw=Xw-Kw+1#计算输出数据的宽度

    Y=np.zeros((Yh,Yw))#保存输出的结果
    #通过两层循环计算出数组Y的值
    for i in range(Yh):#第一层循环Y的高度
        for j in range(Yw):#第二层循环Y的宽度
            #取出对应位置的输入数据X,保存到sub中
            sub=X[i:i+Kh,j:j+Kw]
            #计算输出结果Y
            Y[i,j]=(sub*K).sum()
            #打印出当前的i、j、sub和Y,进行调试
            print('i=%d j=%d'%(i,j))
            print(sub)
            print(Y[i,j])
            print('')
    return Y#返回结果Y

1.局部窗口 sub = X[i:i+Kh, j:j+Kw]

  • 这行代码通过切片操作,从输入 X 中取出一个与卷积核 K 大小相同的子矩阵(也称为感受野)。
  • i:i+Kh 表示子矩阵在 X 上的行范围,j:j+Kw 表示子矩阵在 X 上的列范围。
  • KhKw 分别是卷积核 K 的高度和宽度。

2.卷积计算 Y[i, j] = (sub * K).sum()

  • 将提取出的局部窗口 sub 与卷积核 K 进行逐元素相乘操作 (sub * K),得到的结果再进行 .sum() 操作求和。
  • 这一步的结果是该位置的卷积输出值,将其保存到输出矩阵 Y 中的 (i, j) 位置。

测试函数效果: 

if __name__=='__main__':
    #测试函数的效果
    X=np.array([[1,2,3,0],
                [0,1,2,3],
                [3,0,1,2],
                [2,3,0,1]])
    K=np.array([[2,0,1],
                [0,1,2],
                [1,0,2]])
    result=correlation2d(X,K)
    print(f'result=\n {result}')

边缘检测 

#将真实图片转为一个灰色通道
def rgb_to_gray(rgb):
    r,g,b=rgb[:,:,0],rgb[:,:,1],rgb[:,:,2]
    gray=0.2989*r+0.5870*g+0.1140*b
    return gray

        这段代码实现了将 RGB 图像转换为灰度图像的功能。具体来说,它使用了人眼对不同颜色通道的感知权重(红、绿、蓝通道的加权平均)来转换为灰度值。 

 #对一张真实图片进行边缘检测
    img=plt.imread('C:/Users/lenovo/Pictures/img01.png')
    plt.imshow(img)
    plt.show()
    #将它转为灰色通道后再进行卷积运算
    img=rgb_to_gray(img)

        定义一个3*3的卷积核,中间的时数字8,周围时-1,这个卷积核被称为拉普拉斯算子,通过卷积核可以将图像的边缘显示出来。相同的9个数字和拉普拉斯卷积核计算时,会得到数字0。如果9个数字不完全相同时,计算的结果就不是0。输出数据中不为0的数据,就对应图像的边缘。

#拉普拉斯算子
    kernel=np.array([[-1,-1,-1],
                     [-1,8,-1],
                     [-1,-1,-1]])
    #计算img和卷积核kernel的互相关运算
    img=correlation2d(img,kernel)
    fig=plt.imshow(img)
    #将他展示出来,可以看到img的边缘显示出来了
    plt.show()

 

4.卷积核的训练

        已知,图像原始数据,和这个图像经过某卷积核计算后的新数据,求卷积核中的具体参数。

        在Pytorch中最常使用的卷积层接口是nn.Conv2d,它实现了二维卷积运算的功能,在创建卷积神经网络的卷积层时就需要这个接口。对于Conv2d接口,其中in_channels(输入通道数)、out_channels(输出通道数)和kernel_size(卷积核大小)是必须传入的参数。

import torch
from torch import nn
from torch.nn import functional as F

if __name__ == '__main__':
    #声明img数组,保存输入的图像
    img=torch.tensor([[[[0,0,0,0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0,0,0,0],
                        [0,0,0,100,100,100,100,100,100,0,0,0],
                        [0,0,0,100,100,100,100,100,100,0,0,0],
                        [0,0,0,100,100,100,100,100,100,0,0,0],
                        [0,0,0,0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0,0,0,0],
                        [0,0,0,0,0,0,0,0,0,0,0,0]]]],dtype=torch.float)
    #设置一个拉普拉斯卷积核用作输出数据Y的生成
    kernel=torch.tensor([[[[-1,-1,-1],
                          [-1,8,-1],
                          [-1,-1,-1]]]],dtype=torch.float)
    #计算img和kernal的互相关运算,结果保存在Y中
    Y=F.conv2d(img,kernel)

        在后面的训练中只会使用img和Y,不会使用kernel,这里使用kernel是为了计算Y。后面要根据img和Y,重新训练出kernel中的参数。

    conv=nn.Conv2d(in_channels=1,out_channels=1,kernel_size=3,bias=False)
    #设置学习速率0.0000001和迭代次数10000
    lr=1e-7
    num=10000
    #进入卷积核的迭代循环
    for i in range(num):
        #计算基于当前参数的预测值predict
        predict=conv(img)
        #根据平方误差,计算预测值和真实值之间的损失值
        loss=(predict-Y)**2

        #使用backward函数进行反向传播
        #计算出损失loss关于参数weight的梯度
        loss.sum().backward()
        #使用梯度下降算法,更新weight中保存的数据
        conv.weight.data-=lr*conv.weight.grad.data
        conv.weight.grad.zero_()#清空上一轮迭代梯度

        #每迭代1000轮,打印一次loss
        if (i+1)%1000==0:
            print('epoch %d loss %.3lf'%(i+1,loss.sum()))
    print(conv.weight.data.reshape((3,3)))

5.卷积运算的填充和步幅 

        卷积运算的两个重要参数分别是填充(padding)和步幅(stride)。它们决定了输出结果的大小和特征提取的方式。

        在输入数据和卷积和运算的时候,如果卷积核的尺寸大于1,输出结果的尺寸会小于输入数据的尺寸,输出数据的形状为:(H-Kh+1)*(W-Kw+1)。说明在卷积核尺寸大于1的情况下,如果输入数据经过多次卷积运算,输出数据的尺寸就会不断变小。为了避免这种情况,就需要在图片的外围数据进行填充。例如4*4的数据,外围填充一圈后会变为6*6的数据,这样经过卷积运算后输出数据的尺寸就不会变小。填充的数据一般是0。另外只是对原数据在周围填充一圈0,这不会对计算结果产生太大影响。

        步幅也被称为步长,在进行卷积运算的时候,卷积窗口在数据上滑动,每次滑动的像素被称为步幅。通过增加步幅可以使卷积核更快的扫描数据。

输出数据与填充和步幅的关系

输入矩阵(m*n):X\in R^{M*N}

卷积核W(u*v):W\in R^{U*V} 

填充P,步长S

最终输出矩阵的大小:{M}'=\frac{M+2P-U}{S}+1

                                    {N}'=\frac{N+2P-V}{S}+1

        另外,如果步长大于1,很可能出现无法除尽的情况,也就使窗口在扫描的时候,不能恰好将全部数据扫描完整。解决方法有:报错或者直接四舍五入算出近似的值。

6.特征图和感受野 

        图像处理中,卷积运算是特征提取的有效方法,一个图像在经过卷积运算后得到的结果被称为特征图。在一个卷积层中,可以包含多个卷积核,每个卷积核都可以提取图像的一组特征,生成对应的特征图,不同的卷积核相当于不同的特征提取器。一个图像经过不同的卷积层和池化层会生成不同大小不同数量的特征图。

        影响某个计算结果的所有可能的输入区域叫做感受野。输入图像中的任意元素的改变都会影响这个输出点的输出值。我们可以通过更深的卷积神经网络,使特征图中的单个元素的感受野变得更加广阔,从而捕捉到在输入图片上更大的尺寸特征。这时输出特征图中的一个像素点,就会包含原图像中更多的语义信息。卷积神经网络越深,越能感受到原始输入图片中更大的范围。因此当增加卷积神经网络的深度时,可以获得输入图片上,更大尺寸的特征。

;