第三章:计算机视觉(下)


图像分类是根据 图像的语义信息 对不同类别图像进行区分, 计算机视觉的核心,是物体检测、图像分割、物体跟踪、行为分析、人脸识别等其他 高层次视觉任务的基础。图像分类在许多领域都有着广泛的应用,如:

  • 安防领域的人脸识别和智能视频分析等
  • 交通领域的交通场景识别
  • 互联网领域基于内容的图像检索和相册自动归类
  • 医学领域的图像识别等

上一节主要介绍了卷积神经网络常用的一些基本模块,本节将基于眼疾分类数据集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年,Yan LeCun第一次将LeNet卷积神经网络应用到图像分类上,在手写数字识别任务中取得了巨大成功。LeNet通过连续使用卷积和池化层的组合提取图像特征,其架构如 图1 所示,这里展示的是作者论文中的LeNet-5模型:


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

  • 第一模块:包含5×5的6通道卷积和2×2的池化。卷积提取图像中包含的特征模式(激活函数使用sigmoid),图像尺寸从32减小到28。经过池化层可以降低输出特征图对空间位置的敏感性图像尺寸减到14

  • 第二模块:和第一模块尺寸相同,通道数由6增加为16。卷积操作使图像尺寸减小到10,经过池化后变成5。

  • 第三模块:包含5×5的120通道卷积。卷积之后的图像尺寸减小到1,但是通道数增加为120。将经过第3次卷积提取到的特征图输入到全连接层。第一个全连接层的输出神经元的个数是64,第二个全连接层的输出神经元个数是分类标签的类别数,对于手写数字识别其大小是10。然后使用Softmax激活函数即可计算出每个类别的预测概率


【提示】:

卷积层的输出特征图如何当作全连接层的输入使用呢?

卷积层的输出数据格式是 [ N , C , H , W ] [N, C, H, W] [N,C,H,W]在输入全连接层的时候,会自动将数据拉平

也就是对每个样本,自动将其转化为长度为 K K K的向量,

其中 K = C × H × W K = C \times H \times W K=C×H×W,一个mini-batch的数据维度变成了 N × K N\times K N×K的二维向量。


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

LeNet网络的实现代码如下:

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

# 定义 LeNet 网络结构
class LeNet(fluid.dygraph.Layer):
    def __init__(self, num_classes=1):
        super(LeNet, self).__init__()

        # 创建卷积和池化层块,每个卷积层使用Sigmoid激活函数,后面跟着一个2x2的池化
        self.conv1 = Conv2D(num_channels=1, num_filters=6, filter_size=5, act='sigmoid')
        self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
        self.conv2 = Conv2D(num_channels=6, num_filters=16, filter_size=5, act='sigmoid')
        self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
        # 创建第3个卷积层
        self.conv3 = Conv2D(num_channels=16, num_filters=120, filter_size=4, act='sigmoid')
        # 创建全连接层,第一个全连接层的输出神经元个数为64, 第二个全连接层输出神经元个数为分类标签的类别数
        self.fc1 = Linear(input_dim=120, output_dim=64, act='sigmoid')
        self.fc2 = Linear(input_dim=64, output_dim=num_classes)
    # 网络的前向计算过程
    def forward(self, x):
        x = self.conv1(x)
        x = self.pool1(x)
        x = self.conv2(x)
        x = self.pool2(x)
        x = self.conv3(x)
        x = fluid.layers.reshape(x, [x.shape[0], -1])
        x = self.fc1(x)
        x = self.fc2(x)
        return x

下面的程序使用随机数作为输入,查看经过LeNet-5的每一层作用之后,输出数据的形状

# 输入数据形状是 [N, 1, H, W]
# 这里用np.random创建一个随机数组作为输入数据
x = np.random.randn(*[3,1,28,28])
x = x.astype('float32')
with fluid.dygraph.guard():
    # 创建LeNet类的实例,指定模型名称和分类的类别数目
    m = LeNet(num_classes=10)
    # 通过调用LeNet从基类继承的sublayers()函数,
    # 查看LeNet中所包含的子层
    print(m.sublayers())
    x = fluid.dygraph.to_variable(x)
    for item in m.sublayers():
        # item是LeNet类中的一个子层
        # 查看经过子层之后的输出数据形状
        try:
            x = item(x)
        except:
            x = fluid.layers.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)
[<paddle.fluid.dygraph.nn.Conv2D object at 0x7f1656aab770>, <paddle.fluid.dygraph.nn.Pool2D object at 0x7f1656aaba10>, <paddle.fluid.dygraph.nn.Conv2D object at 0x7f1656aabef0>, <paddle.fluid.dygraph.nn.Pool2D object at 0x7f1616bb1050>, <paddle.fluid.dygraph.nn.Conv2D object at 0x7f1616bb10b0>, <paddle.fluid.dygraph.nn.Linear object at 0x7f1616bb11d0>, <paddle.fluid.dygraph.nn.Linear object at 0x7f1616bb1350>]
conv2d_0 [3, 6, 24, 24] [6, 1, 5, 5] [6]
pool2d_0 [3, 6, 12, 12]
conv2d_1 [3, 16, 8, 8] [16, 6, 5, 5] [16]
pool2d_1 [3, 16, 4, 4]
conv2d_2 [3, 120, 1, 1] [120, 16, 4, 4] [120]
linear_0 [3, 64] [120, 64] [64]
linear_1 [3, 10] [64, 10] [10]

LeNet 识别手写数字

# -*- coding: utf-8 -*-

# LeNet 识别手写数字

import os
import random
import paddle
import paddle.fluid as fluid
import numpy as np

# 定义训练过程
def train(model):
    print('start training ... ')
    model.train()
    epoch_num = 5
    opt = fluid.optimizer.Momentum(learning_rate=0.001, momentum=0.9, parameter_list=model.parameters())
    # 使用Paddle自带的数据读取器
    train_loader = paddle.batch(paddle.dataset.mnist.train(), batch_size=10)
    valid_loader = paddle.batch(paddle.dataset.mnist.test(), batch_size=10)
    for epoch in range(epoch_num):
        for batch_id, data in enumerate(train_loader()):
            # 调整输入数据形状和类型
            x_data = np.array([item[0] for item in data], dtype='float32').reshape(-1, 1, 28, 28)
            y_data = np.array([item[1] for item in data], dtype='int64').reshape(-1, 1)
            # 将numpy.ndarray转化成Tensor
            img = fluid.dygraph.to_variable(x_data)
            label = fluid.dygraph.to_variable(y_data)
            # 计算模型输出
            logits = model(img)
            # 计算损失函数
            loss = fluid.layers.softmax_with_cross_entropy(logits, label)
            avg_loss = fluid.layers.mean(loss)
            if batch_id % 1000 == 0:
                print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, avg_loss.numpy()))
            avg_loss.backward()
            opt.minimize(avg_loss)
            model.clear_gradients()

        model.eval()
        accuracies = []
        losses = []
        for batch_id, data in enumerate(valid_loader()):
            # 调整输入数据形状和类型
            x_data = np.array([item[0] for item in data], dtype='float32').reshape(-1, 1, 28, 28)
            y_data = np.array([item[1] for item in data], dtype='int64').reshape(-1, 1)
            # 将numpy.ndarray转化成Tensor
            img = fluid.dygraph.to_variable(x_data)
            label = fluid.dygraph.to_variable(y_data)
            # 计算模型输出
            logits = model(img)
            pred = fluid.layers.softmax(logits)
            # 计算损失函数
            loss = fluid.layers.softmax_with_cross_entropy(logits, label)
            acc = fluid.layers.accuracy(pred, label)
            accuracies.append(acc.numpy())
            losses.append(loss.numpy())
        print("[validation] accuracy/loss: {}/{}".format(np.mean(accuracies), np.mean(losses)))
        model.train()

    # 保存模型参数
    fluid.save_dygraph(model.state_dict(), 'mnist')


if __name__ == '__main__':
    # 创建模型
    with fluid.dygraph.guard():
        model = LeNet(num_classes=10)
        #启动训练过程
        train(model)
start training ... 
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

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

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
epoch: 0, batch_id: 0, loss is: [2.4476852]
epoch: 0, batch_id: 1000, loss is: [2.2948332]
epoch: 0, batch_id: 2000, loss is: [2.334548]
epoch: 0, batch_id: 3000, loss is: [2.283103]
epoch: 0, batch_id: 4000, loss is: [2.2783697]
epoch: 0, batch_id: 5000, loss is: [2.3309145]
[validation] accuracy/loss: 0.10530000925064087/2.2899725437164307
epoch: 1, batch_id: 0, loss is: [2.2863708]
epoch: 1, batch_id: 1000, loss is: [2.276087]
epoch: 1, batch_id: 2000, loss is: [2.3081589]
epoch: 1, batch_id: 3000, loss is: [2.2395322]
epoch: 1, batch_id: 4000, loss is: [2.2013073]
epoch: 1, batch_id: 5000, loss is: [2.257289]
[validation] accuracy/loss: 0.554900050163269/2.004681348800659
epoch: 2, batch_id: 0, loss is: [1.9385501]
epoch: 2, batch_id: 1000, loss is: [1.5589781]
epoch: 2, batch_id: 2000, loss is: [1.3764482]
epoch: 2, batch_id: 3000, loss is: [0.8067332]
epoch: 2, batch_id: 4000, loss is: [0.6518642]
epoch: 2, batch_id: 5000, loss is: [0.77211165]
[validation] accuracy/loss: 0.8388999700546265/0.6187658905982971
epoch: 3, batch_id: 0, loss is: [0.41283408]
epoch: 3, batch_id: 1000, loss is: [0.40613115]
epoch: 3, batch_id: 2000, loss is: [0.4012765]
epoch: 3, batch_id: 3000, loss is: [
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值