赞
踩
CNN由卷积层,池化层,激活函数、全连接层构成,
有时也会搭配激活函数来加入非线性因素,如下图所示在卷积层CONV后增加激活函数RELU。
通俗来讲,CNN用在图像处理上,就是通过卷积层、池化层来提取和简化图像的特征信息矩阵,
之后全连接层将特征信息矩阵转化为神经网络的输入,输出为图像是某种东西的概率。
卷积层大大简化了神经网络需要的训练参数
假设输入一个 100*100 的矩阵类型数据,神经网络隐藏层有10个神经元:
对于传统的神经网络模型,输入的是 100*100 个数据,每个数据都与神经元连接,需要训练的参数为 100 * 100 * 10 个参数。
如果加上卷积层,则通过卷积核会过滤一遍数据得到特征矩阵,假设此时过滤后的特征矩阵是10 * 10,此时每个神经元与特征矩阵相连接,只需要训练 10 * 10 * 10 个参数,大大降低了训练量。
卷积层还有一个“权值共享”的特性,由于特征图的每个元素都是由一个同样的卷积核滤波得到,所以与特征图连接的每个神经元的权值都相等,此时实际需要训练的参数为 10 * 10 * 1。
卷积的过程如图所示,卷积矩阵也称为滤波器
对于3 * 3的卷积核,最后得到的特征矩阵称为特征图(Feature Map),特征图的每个数据都是输入矩阵的 3* * 3局部连接,即为特征图元素的感受野。
卷积核有 size、stride、padding 、channels 等参数:
size:卷积核的的尺寸,例如3 * 3
stride:卷积核每次移动的步长,如图所示当 stride = 2 时,特征图的尺寸会发生变化
padding:即在输入矩阵周围增加填充,可以减少输入边缘的影响,同 stride配合可以实现输入矩阵与输出矩阵维度一致。如图所示 padding = 1,stride = 2 的情况:
padding常用术语为:
(1)valid:无填充,padding = 0
(2)same:有填充,且需保证输入和输出的矩阵维度一致
输出矩阵的维度 = floor {
(输入矩阵维度-卷积核维度 + 2 * padding)/ stride + 1
}
channels 代表卷积核的通道数,例如一张彩色照片的通道数为3,包含RGB三个通道,所以对应的卷积核的通道数也为3,卷积过程如图所示:
每个卷积核的channel与输入层对应channel进行卷积后,将每个channel的卷积结果按照位相加得到最终的 Feature Map
当有多个卷积核时,可以得到多种不同的特征,对应产生多个 channel 的feature map,如下图所示:
单纯的卷积层是一个线性处理的过程,激活层主要为了引入非线性因素,一般在卷积或者池化之后引入非线性激活。常用的激活函数有 sigmoid、relu、elu函数等:
池化层对特征图进行亚采样处理,用于降低矩阵维度以及防止过拟合。
池化层包含三个超参数:
1、pool size 采样窗口的大小
2、stride 移动步长
3、type 采样方式,包含 Max Pooling(取窗口最大值)、Mean Pooling(取窗口的平均值)两种
全连接层相当于将特征图数据扁平化,例如特征图的维度为 2*2 的矩阵扁平化为 1 * 4 的列向量,每个元素对应一个输入的神经元,再加上隐藏层,构成传统的神经网络。
卷积层+激化层+池化层可以看作对输入数据的特征简化与提取,得到特征图之后通过全连接层扁平化之后,开始机器学习。
CNN识别手写字母,数据来源于Minist 数据集;
Minist 数据集包含训练集和测试集,训练集用于CNN网络各参数的训练,测试集用于测试CNN网络的求解精度。
每个集又包括要识别的图像 + 图像对应的标签。
训练CNN网络时,将images输入进去,得到CNN网络求解出来的labels,再与真实的labels作比较,根据误差不断修正CNN网络的参数之。
下面截取了一段建立与训练CNN模型的代码,完整的代码参考reference的链接。
'''开始建立CNN网络''' class CNN(nn.Module): def __init__(self): super(CNN,self).__init__() ''' 一般来说,卷积网络包括以下内容: 1.卷积层 2.神经网络 3.池化层 ''' self.conv1=nn.Sequential( nn.Conv2d( #--> (1,28,28) in_channels=1, #传入的图片是几层的,灰色为1层,RGB为三层 out_channels=16, #输出的图片是几层 kernel_size=5, #代表扫描的区域点为5*5 stride=1, #就是每隔多少步跳一下 padding=2, #边框补全,其计算公式=(kernel_size-1)/2=(5-1)/2=2 ), # 2d代表二维卷积 --> (16,28,28) nn.ReLU(), #非线性激活层 nn.MaxPool2d(kernel_size=2), #设定这里的扫描区域为2*2,且取出该2*2中的最大值 --> (16,14,14) ) self.conv2=nn.Sequential( nn.Conv2d( # --> (16,14,14) in_channels=16, #这里的输入是上层的输出为16层 out_channels=32, #在这里我们需要将其输出为32层 kernel_size=5, #代表扫描的区域点为5*5 stride=1, #就是每隔多少步跳一下 padding=2, #边框补全,其计算公式=(kernel_size-1)/2=(5-1)/2= ), # --> (32,14,14) nn.ReLU(), nn.MaxPool2d(kernel_size=2), #设定这里的扫描区域为2*2,且取出该2*2中的最大值 --> (32,7,7),这里是三维数据 ) self.out=nn.Linear(32*7*7,10) #注意一下这里的数据是二维的数据 def forward(self,x): x=self.conv1(x) x=self.conv2(x) #(batch,32,7,7) #然后接下来进行一下扩展展平的操作,将三维数据转为二维的数据 x=x.view(x.size(0),-1) #(batch ,32 * 7 * 7) output=self.out(x) return output
# 添加优化方法 optimizer=torch.optim.Adam(cnn.parameters(),lr=LR) # 指定损失函数使用交叉信息熵 loss_fn=nn.CrossEntropyLoss() step=0 for epoch in range(EPOCH): #加载训练数据 for step,data in enumerate(train_loader): x,y=data #分别得到训练数据的x和y的取值,x为images图像,y为图像对应的标签 b_x=Variable(x) b_y=Variable(y) output=cnn(b_x) #调用模型预测 loss=loss_fn(output,b_y)#计算损失值 optimizer.zero_grad() #每一次循环之前,将梯度清零 loss.backward() #反向传播 optimizer.step() #梯度下降 #每执行50次,输出一下当前epoch、loss、accuracy if (step%50==0): #利用测试集数据计算一下模型预测正确率 test_output=cnn(test_x) y_pred=torch.max(test_output,1)[1].data.squeeze() accuracy=sum(y_pred==test_y).item()/test_y.size(0) print('now epoch : ', epoch, ' | loss : %.4f ' % loss.item(), ' | accuracy : ' , accuracy)
由此得到训练好的可以识别字符的CNN模型。
理论部分参考:http://hejunhao.me/archives/1364#more-1364
代码部分参考:https://blog.csdn.net/weixin_38468077/article/details/106592690
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。