计算机视觉(下)——图像分类

       图像分类是根据图像的语义信息对不同类别图像进行区分,是计算机视觉的核心,是物体检测、图像分割、物体跟踪、行为分析、人脸识别等其他高层次视觉任务的基础。图像分类在许多领域都有着广泛的应用,例如:安防领域的人脸识别和智能视频分析等,交通领域的交通场景识别,互联网领域基于内容的图像检索和相册自动归类,医学领域的图像识别等。

        我们上一节学习总结了卷积算子应用的一些例子,本文将基于眼疾分类数据集iChallenge-PM,对图像分类领域的经典卷积神经网络进行剖析,介绍如何应用这些基础模块构建卷积神经网络,解决图像分类问题。按照被提出的时间顺序,涵盖如下卷积神经网络:

  • LeNet:Yan LeCun等人于1998年第一次将卷积神经网络应用到图像分类任务上[1],在手写数字识别任务上取得了巨大成功。

  • AlexNet:Alex Krizhevsky等人在2012年提出了AlexNet[2], 并应用在大尺寸图片数据集ImageNet上,获得了2012年ImageNet比赛冠军(ImageNet Large Scale Visual Recognition Challenge,ILSVRC)。

  • VGG:Simonyan和Zisserman于2014年提出了VGG网络结构[3],是当前最流行的卷积神经网络之一,由于其结构简单、应用性极强而深受广大研究者欢迎。

  • GoogLeNet:Christian Szegedy等人在2014提出了GoogLeNet[4],并取得了2014年ImageNet比赛冠军。

  • ResNet:Kaiming He等人在2015年提出了ResNet[5],通过引入残差模块加深网络层数,在ImagNet数据集上的错误率降低到3.6%,超越了人眼识别水平。ResNet的设计思想深刻地影响了后来的深度神经网络的设计。

一、LeNet

LeNet是最早的卷积神经网络之一[1]。1998年,Yann LeCun第一次将LeNet卷积神经网络应用到图像分类上,在手写数字识别任务中取得了巨大成功。LeNet通过连续使用卷积和池化层的组合提取图像特征,其架构如 图1 所示,这里展示的是用于MNIST手写体数字识别任务中的LeNet-5模型:

                                                 图1:LeNet模型网络结构示意图

卷积层的输出数据格式是[N,C,H,W][N, C, H, W][N,C,H,W],在输入全连接层的时候,会自动将数据拉平,也就是对每个样本,自动将其转化为长度为KKK的向量,其中K=C×H×W,一个mini-batch的数据维度变成了N×K的二维向量 。

LeNet在手写数字识别上的应用

LeNet网络的实现代码如下:

# 导入需要的包
import paddle
import numpy as np
from paddle.nn import Conv2D, MaxPool2D, Linear

## 组网
import paddle.nn.functional as F

# 定义 LeNet 网络结构
class LeNet(paddle.nn.Layer):
    def __init__(self, num_classes=1):
        super(LeNet, self).__init__()
        # 创建卷积和池化层
        # 创建第1个卷积层
        self.conv1 = Conv2D(in_channels=1, out_channels=6, kernel_size=5)
        self.max_pool1 = MaxPool2D(kernel_size=2, stride=2)
        # 尺寸的逻辑:池化层未改变通道数;当前通道数为6
        # 创建第2个卷积层
        self.conv2 = Conv2D(in_channels=6, out_channels=16, kernel_size=5)
        self.max_pool2 = MaxPool2D(kernel_size=2, stride=2)
        # 创建第3个卷积层
        self.conv3 = Conv2D(in_channels=16, out_channels=120, kernel_size=4)
        # 尺寸的逻辑:输入层将数据拉平[B,C,H,W] -> [B,C*H*W]
        # 输入size是[28,28],经过三次卷积和两次池化之后,C*H*W等于120
        self.fc1 = Linear(in_features=120, out_features=64)
        # 创建全连接层,第一个全连接层的输出神经元个数为64, 第二个全连接层输出神经元个数为分类标签的类别数
        self.fc2 = Linear(in_features=64, out_features=num_classes)
    # 网络的前向计算过程
    def forward(self, x):
        x = self.conv1(x)
        # 每个卷积层使用Sigmoid激活函数,后面跟着一个2x2的池化
        x = F.sigmoid(x)
        x = self.max_pool1(x)
        x = F.sigmoid(x)
        x = self.conv2(x)
        x = self.max_pool2(x)
        x = self.conv3(x)
        # 尺寸的逻辑:输入层将数据拉平[B,C,H,W] -> [B,C*H*W]
        x = paddle.reshape(x, [x.shape[0], -1])
        x = self.fc1(x)
        x = F.sigmoid(x)
        x = self.fc2(x)
        return x

飞桨会根据实际图像数据的尺寸和卷积核参数自动推断中间层数据的W和H等,只需要用户表达通道数即可。下面的程序使用随机数作为输入,查看经过LeNet-5的每一层作用之后,输出数据的形状。

# 输入数据形状是 [N, 1, H, W]
# 这里用np.random创建一个随机数组作为输入数据
x = np.random.randn(*[3,1,28,28])
x = x.astype('float32')

# 创建LeNet类的实例,指定模型名称和分类的类别数目
model = LeNet(num_classes=10)
# 通过调用LeNet从基类继承的sublayers()函数,
# 查看LeNet中所包含的子层
print(model.sublayers())
x = paddle.to_tensor(x)
for item in model.sublayers():
    # item是LeNet类中的一个子层
    # 查看经过子层之后的输出数据形状
    try:
        x = item(x)
    except:
        x = paddle.reshape(x, [x.shape[0], -1])
        x = item(x)
    if len(item.parameters())==2:
        # 查看卷积和全连接层的数据和参数的形状,
        # 其中item.parameters()[0]是权重参数w,item.parameters()[1]是偏置参数b
        print(item.full_name(), x.shape, item.parameters()[0].shape, item.parameters()[1].shape)
    else:
        # 池化层没有参数
        print(item.full_name(), x.shape)

 卷积Conv2D的padding参数默认为0,stride参数默认为1,当输入形状为[Bx1x28x28]时,B是batch_size,经过第一层卷积(kernel_size=5, out_channels=6)和maxpool之后,得到形状为[Bx6x12x12]的特征图;经过第二层卷积(kernel_size=5, out_channels=16)和maxpool之后,得到形状为[Bx16x4x4]的特征图;经过第三层卷积(out_channels=120, kernel_size=4)之后,得到形状为[Bx120x1x1]的特征图,在FC层计算之前,将输入特征从卷积得到的四维特征reshape到格式为[B, 120x1x1]的特征,这也是LeNet中第一层全连接层输入shape为120的原因。

# -*- coding: utf-8 -*-
# LeNet 识别手写数字
import os
import random
import paddle
import numpy as np
import paddle
from paddle.vision.transforms import ToTensor
from paddle.vision.datasets import MNIST

# 定义训练过程
def train(model, opt, train_loader, valid_loader):
    # 开启0号GPU训练
    use_gpu = True
    paddle.device.set_device('gpu:0') if use_gpu else paddle.device.set_device('cpu')
    print('start training ... ')
    model.train()
    for epoch in range(EPOCH_NUM):
        for batch_id, data in enumerate(train_loader()):
            img = data[0]
            label = data[1] 
            # 计算模型输出
            logits = model(img)
            # 计算损失函数
            loss_func = paddle.nn.CrossEntropyLoss(reduction='none')
            loss = loss_func(logits, label)
            avg_loss = paddle.mean(loss)

            if batch_id % 2000 == 0:
                print("epoch: {}, batch_id: {}, loss is: {:.4f}".format(epoch, batch_id, float(avg_loss.numpy())))
            avg_loss.backward()
            opt.step()
            opt.clear_grad()

        model.eval()
        accuracies = []
        losses = []
        for batch_id, data in enumerate(valid_loader()):
            img = data[0]
            label = data[1] 
            # 计算模型输出
            logits = model(img)
            pred = F.softmax(logits)
            # 计算损失函数
            loss_func = paddle.nn.CrossEntropyLoss(reduction='none')
            loss = loss_func(logits, label)
            acc = paddle.metric.accuracy(pred, label)
            accuracies.append(acc.numpy())
            losses.append(loss.numpy())
        print("[validation] accuracy/loss: {:.4f}/{:.4f}".format(np.mean(accuracies), np.mean(losses)))
        model.train()

    # 保存模型参数
    paddle.save(model.state_dict(), 'mnist.pdparams')


# 创建模型
model = LeNet(num_classes=10)
# 设置迭代轮数
EPOCH_NUM = 5
# 设置优化器为Momentum,学习率为0.001
opt = paddle.optimizer.Momentum(learning_rate=0.001, momentum=0.9, parameters=model.parameters())
# 定义数据读取器
train_loader = paddle.io.DataLoader(MNIST(mode='train', transform=ToTensor()), batch_size=10, shuffle=True)
valid_loader = paddle.io.DataLoader(MNIST(mode='test', transform=ToTensor()), batch_size=10)
# 启动训练过程
train(model, opt, train_loader, valid_loader)

运行后这里除了显示item进程进度,还报了ValueError。

item  181/2421 [=>............................] - ETA: 2s - 1ms/ite
Cache file /home/aistudio/.cache/paddle/dataset/mnist/train-images-idx3-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/train-images-idx3-ubyte.gz 
Begin to download
item  182/2421 [=>............................] - ETA: 2s - 1ms/item
item  183/2421 [=>............................] - ETA: 2s - 1ms/item
item  184/2421 [=>............................] - ETA: 2s - 1ms/item
item  185/2421 [=>............................] - ETA: 2s - 1ms/item
item  186/2421 [=>............................] - ETA: 2s - 1ms/item
item  187/2421 [=>............................] - ETA: 2s - 1ms/item
item  188/2421 [=>............................] - ETA: 2s - 1ms/item
item  189/2421 [=>............................] - ETA: 2s - 1ms/item
item  190/2421 [=>............................] - ETA: 2s - 1ms/item
item  191/2421 [=>............................] - ETA: 2s - 1ms/item
item  192/2421 [=>............................] - ETA: 2s - 1ms/item
item  193/2421 [=>............................] - ETA: 2s - 1ms/item
item  194/2421 [=>............................] - ETA: 2s - 1ms/item
item  195/2421 [=>............................] - ETA: 2s - 1ms/item
item  196/2421 [=>............................] - ETA: 2s - 1ms/item
item  197/2421 [=>............................] - ETA: 2s - 1ms/item
item  198/2421 [=>............................] - ETA: 2s - 1ms/item
 
 

.

.

.

.

.

Download finished
Cache file /home/aistudio/.cache/paddle/dataset/mnist/t10k-labels-idx1-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/t10k-labels-idx1-ubyte.gz 
Begin to download

Download finished
---------------------------------------------------------------------------ValueError                                Traceback (most recent call last)/tmp/ipykernel_93/1097620047.py in <module>
     65 valid_loader = paddle.io.DataLoader(MNIST(mode='test', transform=ToTensor()), batch_size=10)
     66 # 启动训练过程
---> 67 train(model, opt, train_loader, valid_loader)
/tmp/ipykernel_93/1097620047.py in train(model, opt, train_loader, valid_loader)
     13     # 开启0号GPU训练
     14     use_gpu = True
---> 15     paddle.device.set_device('gpu:0') if use_gpu else paddle.device.set_device('cpu')
     16     print('start training ... ')
     17     model.train()
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/device/__init__.py in set_device(device)
    311         data = paddle.stack([x1,x2], axis=1)
    312     """
--> 313     place = _convert_to_place(device)
    314     framework._set_expected_place(place)
    315     return place
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/device/__init__.py in _convert_to_place(device)
    254                 raise ValueError(
    255                     "The device should not be {}, since PaddlePaddle is "
--> 256                     "not compiled with CUDA".format(avaliable_gpu_device))
    257             device_info_list = device.split(':', 1)
    258             device_id = device_info_list[1]
ValueError: The device should not be <re.Match object; span=(0, 5), match='gpu:0'>, since PaddlePaddle is not compiled with CUDA

没有具体解决。 这里我们把环境换成高级版。

再次运行:

Download finished
Cache file /home/aistudio/.cache/paddle/dataset/mnist/t10k-labels-idx1-ubyte.gz not found, downloading https://dataset.bj.bcebos.com/mnist/t10k-labels-idx1-ubyte.gz 
Begin to download

Download finished
start training ... 
epoch: 0, batch_id: 0, loss is: 2.4376
epoch: 0, batch_id: 2000, loss is: 2.2881
epoch: 0, batch_id: 4000, loss is: 1.8477
[validation] accuracy/loss: 0.7793/0.8106
epoch: 1, batch_id: 0, loss is: 0.6600
epoch: 1, batch_id: 2000, loss is: 0.6726
epoch: 1, batch_id: 4000, loss is: 0.5597
[validation] accuracy/loss: 0.9084/0.3219
epoch: 2, batch_id: 0, loss is: 0.7125
epoch: 2, batch_id: 2000, loss is: 0.0881
epoch: 2, batch_id: 4000, loss is: 0.4845
[validation] accuracy/loss: 0.9352/0.2320
epoch: 3, batch_id: 0, loss is: 0.2157
epoch: 3, batch_id: 2000, loss is: 0.5528
epoch: 3, batch_id: 4000, loss is: 0.2133
[validation] accuracy/loss: 0.9482/0.1687
epoch: 4, batch_id: 0, loss is: 0.4239
epoch: 4, batch_id: 2000, loss is: 0.6634
epoch: 4, batch_id: 4000, loss is: 0.0348
[validation] accuracy/loss: 0.9561/0.1516

 通过运行结果可以看出,LeNet在手写数字识别MNIST验证数据集上的准确率最高达95%以上。

training文件。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

spiritqi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值