大模型系列5--卷积神经网络

1. 背景

在看大模型的一些内容中,为了更好的理解其实现,现对一些经典的网络模型进行学习。本文参考bilibili视频内容:https://www.bilibili.com/video/BV1zF411V7xu/,感谢博主

本文学习的卷积神经网络CNN,在视觉领域非常知名,但也不只用于视觉领域。相比于普通NN,CNN引入了卷积层和池化层两个概念

  • 卷积层:对图片的一块一块的小方格位置进行特征提取,通过不同权重的卷积核可以提出图形的各类特征
  • 池化层:在提取完特征后,考虑到特征数目多,可以将特征矩阵小方格内最大的权重拿出来,通过这种方式进行降采样。

本文使用的pytorch进行的代码实践,在涉及到具体CNN代码训练时,本文作者对 train,optimiser,loss.backward等逻辑并没有很好的认知,代码内部有些过于黑盒了,学起来心里面太虚。 于是决定在余下的一些时间中,详细看下基本NN的代码训练逻辑,和NN的一些理论能够结合起来,让作者对代码能够认识的更清楚一些

2. 架构

  • 包括输入层、卷积层、池化层、全连接层
    在这里插入图片描述
    如下图是一个7层卷积神经网络图
    在这里插入图片描述

2.1. 卷积

  • 下图对一个32323的图片,使用一个333的卷积核进行处理
  • 卷积操作是将卷积核在图片中游走,在游走到某个位置时,执行的操作是对应位置的元素进行相乘再相加(下图右边以3*3的核进行的卷积,只表达了两维的概念)
  • 注意:卷积核的第三个维度一定和输入特征的第三个维度相同
    在这里插入图片描述
  • 对于一个三维核,可将第三维展开,例如如下图一个图片的第三维代表RGB三种颜色值,使用一个333的卷积核进行卷积计算,可以展开成3个3*3的二维卷积
    在这里插入图片描述

2.1.1. 单卷积层(多Channel输出)

  • 对一个输入图片32323,每个卷积5*5的卷积核,会对应生成一个二维特征图,也叫 一个channel
  • 可以使用多个卷积核,生成多个二维图,即如下图右边多个平面,平面的数目即是输出channel的数目
    在这里插入图片描述

2.2.2. 多层卷积(卷积堆叠)

  • 实际应用中,不会只有一层卷积层,而是多层卷积堆叠而成
  • 如下图,第一层卷积使用 5 ∗ 5 ∗ 3 5*5*3 553,第二层卷积使用 5 ∗ 5 ∗ 6 5*5*6 556
  • 第一层卷积的输出是 28 ∗ 28 ∗ 6 28*28*6 28286,28和6是怎么来的?
    • 28来源于 32 − 5 + 1 32 - 5 + 1 325+1,其中5是卷积核的大小,且采用的卷积核滑动窗口为1
    • 6是特征图的个数,这表明使用6个553的卷积核
  • 第二层卷积的输出是 24 ∗ 24 ∗ 10 24*24*10 242410,24和10是怎么来的?
    • 24等于 28 − 5 + 1 28 -5 +1 285+1
    • 10是特征图的个数,这里使用的是 5 ∗ 5 ∗ 6 5*5*6 556的卷积核

在这里插入图片描述

2.2.3. 卷积关键参数

  • 滑动窗口步长:最常用是1,或者是2
  • 边缘填充:边缘填充的圈数,填充0(目的是为了让原有边缘更靠内,确保边缘计算公平性)
  • 卷积核大小:最常用是33,或者55
  • 卷积核个数:生成特征图的个数

2.2.4. 卷积常用计算公式

  • 一层卷积输出特征是多少?
    • 以其中一维的长度举例,它等于 输入长度 + 2 ∗ 边缘填充 − 核大小 滑动窗口步长 + 1 \frac{输入长度 + 2 * 边缘填充 - 核大小}{滑动窗口步长} + 1 滑动窗口步长输入长度+2边缘填充核大小+1
    • 以输入 32 ∗ 32 ∗ 3 32*32*3 32323图像为例,用 10 10 10 5 ∗ 5 ∗ 3 5*5*3 553的卷积核filter来指定步长为1,边界填充为2,则最终卷积输出特征为 32 ∗ 32 ∗ 10 32*32*10 323210,其长度计算为 32 + 2 ∗ 2 − 5 1 + 1 = 32 \frac{32 + 2*2 - 5}{1}+1=32 132+225+1=32,宽度计算和长度计算等同。
  • 一层卷积的参数有多少个?
    • 10 10 10 5 ∗ 5 ∗ 3 5*5*3 553的卷积核,每个卷积核包括 W = 5 ∗ 5 ∗ 3 W = 5*5*3 W=553以及额外的 b b b,则总参数个数为: 10 ∗ 5 ∗ 5 ∗ 3 + 10 = 760 10 * 5 * 5 * 3 + 10 = 760 10553+10=760

2.2. 池化层(pooling)

  • 池化是用来降采样的(选取最有作用的特征)
  • 一般是使用最大特征,即Max-Pooling
    在这里插入图片描述

3. 经典网络结构

3.1. VGG-16

  • 13个卷积层(2个 卷积(2)-池化 +3个 卷积(3)-池化)
  • 最后3个全连接层:FC-4096,FC-4096,FC-1000
    在这里插入图片描述

3.2. ResNet

  • 网络更深,不见得更好
  • 直通越过某些无用的层,可以更多层,至少不会比层数少的网络差
    在这里插入图片描述

4. 代码

从视频中,一字一句抄了一份代码,目前IDE的补全能力很强,基本上写几个字母就可以猜到后续要用什么,因此代码实际抄起来还挺快。

这份代码包含三个部分

  • 数据构建和载入
  • 网络结构定义
  • 网络训练和验证

4.1. 数据构建和载入

torchvision有datasets库可以自动从网上下载MNIST数据集,完全不需要任何数据准备的工作,傻瓜式写代码。数据集要和dataloader绑定在一起,具体dataloader的作用机制是如何的,可以参考大模型系列3-pytorch dataloader的原理

4.2. 网络结构定义

利用前述知识,可以很容易构建一个三层CNN,它包括两个卷积层和一个全连接层,每个卷积后面接Relu和MaxPooling,经典网络有2卷1pooling,和三卷1pooling的。

  • conv1:第1层卷积
  • conv2:第2层卷积
  • out:全连接层

特别要注意的是卷积的各个参数的设定

  • in_channels:输入数据的第三维的大小
  • out_channels:输出的特征图的个数
  • kernel_size:卷积核第1和2维的大小
  • stripe:卷积的滑动窗口步长
  • padding:卷积边缘补充0的宽度,为2表明周围补充两圈
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels=1,out_channels=16,kernel_size=5,stride=1,padding=2),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        ) #14*14*16
        self.conv2 = nn.Sequential(
            nn.Conv2d(in_channels=16,out_channels=32,kernel_size=5,stride=1,padding=2),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        )#7*7*32
        self.out = nn.Linear(32*7*7,10)
    def forward(self,x):
        x=self.conv1(x)
        x=self.conv2(x)
        x=x.view(x.size(0),-1)
        output=self.out(x)
        return output

4.3. 模型训练和验证

训练阶段的代码很简单,基本上都是分成多轮epoch,每轮分batch遍历所有的数据,在每个batch中执行如下工作:训练,损失计算,优化器,反向传播

net.train()
output = net(data)
loss = criterion(output, target)
optimizer.zero_grad()
loss.backward()
optimizer.step()
  • 对这块代码做不到很强的认知,后续会从基本NN的逻辑出发,看看其实现的机制是什么,本文暂不展开,等分析完其逻辑之后,会将相关的链接贴在这里。
for epoch in range(num_epochs):
    train_rights=[]
    for batch_idx, (data, target) in enumerate(train_loader):
        net.train()
        output = net(data)
        loss = criterion(output, target)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        rights= accuracy(output, target)
        train_rights.append(rights)
        if batch_idx % 100 == 0:
            net.eval()
            val_rights=[]
            for (data,target) in test_loader:
                output = net(data)
                right = accuracy(output, target)
                val_rights.append(right)
            train_r = (sum([top[0] for top in train_rights]), sum([top[1] for top in train_rights]))
            val_r = (sum([top[0] for top in val_rights]), sum([top[1] for top in val_rights]))

            print('当前epoch: {}[{}/{}({:.0f}%)]\t损失:{:.6f}\t训练集准确率:{:.2f}%\t测试集正确率:{:.2f}%'.format(
                epoch,
                batch_idx * batch_size,
                len(train_loader.dataset),
                100.*batch_idx/len(train_loader),
                loss.data,
                100.*train_r[0]/train_r[1],
                100.*val_r[0]/val_r[1]))

4.4. 完整代码

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision import datasets,transforms
import matplotlib.pyplot as plt
import numpy as np

#定义超参数
input_size=28 #图像的总尺寸28*28
num_classes=10 #标签的种类数
num_epochs=3 #训练的总循环周期
batch_size=64 #一个(批次)的大小,64张图片

#训练集
train_dataset = datasets.MNIST(root="./data", train=True, transform=transforms.ToTensor(), download=True)
# 测试集
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):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Sequential(
            nn.Conv2d(in_channels=1,out_channels=16,kernel_size=5,stride=1,padding=2),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        ) #14*14*16
        self.conv2 = nn.Sequential(
            nn.Conv2d(in_channels=16,out_channels=32,kernel_size=5,stride=1,padding=2),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),
        )#7*7*32
        self.out = nn.Linear(32*7*7,10)
    def forward(self,x):
        x=self.conv1(x)
        x=self.conv2(x)
        x=x.view(x.size(0),-1)
        output=self.out(x)
        return output
def accuracy(predictions, labels):
    pred = torch.max(predictions.data,1)[1]
    rights = pred.eq(labels.data).cpu().sum()
    return rights,len(labels)

net = CNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)

for epoch in range(num_epochs):
    train_rights=[]
    for batch_idx, (data, target) in enumerate(train_loader):
        net.train()
        output = net(data)
        loss = criterion(output, target)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        rights= accuracy(output, target)
        train_rights.append(rights)
        if batch_idx % 100 == 0:
            net.eval()
            val_rights=[]
            for (data,target) in test_loader:
                output = net(data)
                right = accuracy(output, target)
                val_rights.append(right)
            train_r = (sum([top[0] for top in train_rights]), sum([top[1] for top in train_rights]))
            val_r = (sum([top[0] for top in val_rights]), sum([top[1] for top in val_rights]))

            print('当前epoch: {}[{}/{}({:.0f}%)]\t损失:{:.6f}\t训练集准确率:{:.2f}%\t测试集正确率:{:.2f}%'.format(
                epoch,
                batch_idx * batch_size,
                len(train_loader.dataset),
                100.*batch_idx/len(train_loader),
                loss.data,
                100.*train_r[0]/train_r[1],
                100.*val_r[0]/val_r[1]))

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值