卷积神经网络广泛应用于图像处理,比起神经网络最大的优点是同一个卷积核可以在图像的所有位置复用,这就极大的减少了参数量,而图像是由像素点组成的,大量的数据如果使用神经网络来进行训练的话参数量过于庞大,得不偿失。并且相比于神经网络,卷积核可以关注局部细节,而不是整体,这符合人类在识别图像时实质上时关注该物体的局部特征,最后,卷积神经网络的池化层可以减小计算量的同时保留关键特征。

基本原理

卷积神经网络由众多清晰的组件组成,对于初学者比较友好,主要内容就是进行图像特征提取,重点内容在卷积层

一.卷积层

滑动窗口步长,越小越好,一般为1

卷积核尺寸,越少越好,一般为3*3

边缘填充适中

卷积核个数越多越好

卷积进行多层,每一层都在前一层的卷积核的基础上进行的,因此越往后,卷积层事业越大,特征越清晰

以上那个公式主要是为了计算padding大小,使卷积后的图像不改变大小,常对应的参数是

卷积核:5,Padding:2

卷积核:3,Padding:1   

卷积核参数共享,同一次遍历过程中,不论图像大小,卷积核参数不变,即同一次遍历中滑动窗口所使用的卷积核是共享的

每一组卷积核计算之后要加上偏置b

每一层卷积后加一层标准化,relu

二.激活层

引入非线性,提升模型表达,常用ReLU

三.池化层

MAX POOLING最大池化,在卷积层之后,将数据进行压缩

防止过拟合

实际上池化层就相当于将数据进行压缩,在已经完成的卷积后的特征中在一定区域选择特征值最大的或者平均特征值来作为代表

四.全连接层

整合全局特征,方便映射到输出空间

全连接层原理和神经网络相同,目的是在最后进行分类标签的输出,将所有特征进行整合

以下为经典案例(基础中的基础)

#卷积神经网络分类问题
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets,transforms#transforms用于数据预处理
from matplotlib import pyplot as plt
import numpy as np

#定义超参数
input_size=28 #图像的大小
num_classes=10#标签的种类
num_epochs=3#训练的循环次数
batch_size=64#每一批的样本数
#训练集
train_dataset=datasets.MNIST(root='./data',
                             train=True,
                             transform=transforms.ToTensor(),
                             download=True
                             )
#测试集 格式必须是Tensor格式
test_dataset=datasets.MNIST(root='./data',
                            train=False,
                            transform=transforms.ToTensor(),
                            download=True
                            )
#构建batch数据集
train_loader=torch.utils.data.DataLoader(dataset=train_dataset,
                                         batch_size=batch_size,
                                         shuffle=True#打乱数据集
                                         )
test_loader=torch.utils.data.DataLoader(dataset=test_dataset,
                                         batch_size=batch_size,
                                         shuffle=True#不打乱数据集
                                         )

#定义网络结构
class CNN(nn.Module):#nn.Module 支持自动跟踪模型中的参数(Parameter 类型)。
    def __init__(self):
        super(CNN,self).__init__()  #继承父类的初始化方法
        self.conv1=nn.Sequential(   #卷积层第一层
            nn.Conv2d(
                in_channels=1,      #灰度图,表示输入通道只有1个,即为灰度图,如果是彩色图,则输入通道为3,表示红黄蓝
                out_channels=16,    #卷积核的数量,表示得到多少个特征图
                kernel_size=5,      #卷积核的大小
                stride=1,
                padding=2,          #边缘补零的大小,通过补padding来保证卷积后图像的大小不变,常见的是kernel=5,padding=2 kernel=3,padding=1
            ),
            nn.ReLU(),              #激活函数,每一次卷积后输出都会经过激活函数
            nn.MaxPool2d(kernel_size=2),#池化层,将卷积后的特征图缩小,常见的池化核大小为2,表示缩小一般(16*14*14)
        )
        self.conv2=nn.Sequential(   #卷积层第二层
            nn.Conv2d(16,32,5,1,2),#前一层输出16个特征值,这一层就需要输入16个通道(32*14*14)
            nn.ReLU(),
            nn.Conv2d(32,32,5,1,2),#(32*14*14)再经过一次卷积层
            nn.ReLU(),
            nn.MaxPool2d(2),        #(32*7*7)
        )
        self.conv3=nn.Sequential(   #卷积层第三层
            nn.Conv2d(32,64,5,1,2),
            nn.ReLU(),              #(64*7*7)
        )
        #对输入的二维张量做线性变换
        self.out=nn.Linear(in_features=64*7*7,out_features=num_classes)#全连接层,然后输入到全连接层,输出10个特征值,表示10个类别
    def forward(self,x):#向前传播,输入x,修改参数
        x=self.conv1(x)     #第一层卷积
        x=self.conv2(x)     #第二层卷积
        x=self.conv3(x)     #第三层卷积
        x=x.view(x.size(0),-1)#展平,将特征图展平,x.size(0)表示batch_size的大小,-1表示将特征图展平,-1是自适应,根据输入的维度自动计算,将其变为二维张量,用于进行全连接层的输入
        output=self.out(x)
        return output

#准确率作为评估标准
def accuracy(predictions,labels):
    #dim 表示维度,0表示行,1表示列
    pred=torch.max(predictions,dim=1)[1]#预测值[1]表示该最大值的索引[0]表示返回的最大值,相当于求众多预测结果的最大值
    rights=pred.eq(labels.data.view_as(pred)).sum()#预测值与真实值比较,相同则right+1
    return rights,len(labels)#返回准确率

#训练网络模型
#1.实例化
net=CNN()
#2.定义损失函数
criterion=nn.CrossEntropyLoss()#交叉熵损失函数,用于分类问题
#3.定义优化器
optimizer=optim.Adam(net.parameters(),lr=0.001)#Adam优化器,lr表示学习率

#训练
for epoch in range(num_epochs):
    #保存当前epoch
    train_rights=[]

    for batch_idx,(data,targets)in enumerate(train_loader):#enumerate函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据下标和数据
        net.train()
        output=net(data)#输入数据
        loss=criterion(output,targets)#计算损失
        optimizer.zero_grad()#梯度清零,必须要加,否则会累加
        loss.backward()#反向传播
        optimizer.step()#更新参数
        right=accuracy(output,targets)
        train_rights.append(right)#计算准确率

        if batch_idx%100==0:#每100次验证一次
            net.eval()#设置为评估模式
            val_rights=[]

            for(data,targets)in test_loader:
                output=net(data)
                right=accuracy(output,targets)
                val_rights.append(right)

            #准确率计算
            #训练集
            train_r=(sum([tup[0]for tup in train_rights]),sum([tup[1]for tup in train_rights]))
            #测试集
            val_r=(sum([tup[0]for tup in val_rights]),sum([tup[1]for tup in val_rights]))

            print('当前epoch:{},[{}/{} ({:.0f}%)]\t损失:{:.6f}\t训练集准确率:{:.2f}%\t测试集准确率:{:.2f}%'.format(

                epoch+1,batch_idx*batch_size,len(train_loader.dataset),
                100.*batch_idx/len(train_loader),
                loss.data,
                100.*train_r[0].numpy()/train_r[1],
                100.*val_r[0].numpy()/val_r[1])

            )
save_path = "E:/AI/project_data/model_data/mnist_cnn.pth"
torch.save(net.state_dict(), save_path)
print(f"模型参数已保存到:{save_path}")

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐