爱笑的小姐姐 · 2021年12月23日

LeNET识别十种动物水果

简 介:利用经典的LeNet深度学习网络,可以完成对于智能车竞赛中智能视觉组对搬运物品分类的要求。虽然实际比赛中还会包含有五种交通工具,那么利用LeNet仍然是可以完成小类别的识别与定位的。仅仅使用标准的数据库训练的样本,还是无法满足实际要求,后面还需要:进一步增加数据库,使得模型能够适应实际环境下所采集到的的图片数据;进一步对LeNet的网络参数进行精简,毕竟将来改模型需要部署在NXP单片机中,过大的神经网络会使得单片机识别速度降低。
关键词MNISTNXP智能车竞赛

01 faMNIST图片库

1.1 背景

第十六届全国大学生智能车竞赛AI视觉组[1] 中,要求车模在NXP嵌入式平台上完成对于赛道上的数字、Apriltag、动物(五种)、水果(五种)的识别。在新的一届智能车竞赛中,要求对于动物、水果(总共10种)进行子类的识别,即对具体哪一种动物(牛、狗、猪、猫、马)、水果(榴莲、橙子、苹果、葡萄、香蕉)进行分类。至于 子类中[2] 的细分就不再要求了。
image.png
▲ 图1.1.1  智能视觉组中的动物与水果
640.gif
▲ 图1.1  智能车模在进行图片任务识别

1.2 三种“MNIST”数据集合

1.2.1 MNIST

在深度学习中, MNIST数据集合[3] 是一种基本图片集合,是进行模式识别与深度学习中的“Hello World ”数据集合。可以在 YANN.LECUN[4] : http://yann.lecun.com/exdb/mnist/ 网站获取。它包括有训练和测试样本总共有60000个。
image.png
▲ 图1.2.1 MNIST数据库

1.2.2 Fashion-MNIST

Fashion-MNIST[5] 是一个替代 MNIST 手写数字集[6] 的图像数据集。它是由 Zalando(一家德国的时尚科技公司)旗下的研究部门提供。其涵盖了来自 10 种类别的共 7 万个不同商品的正面图片。

FashionMNIST 的大小、格式和训练集/测试集划分与原始的 MNIST 完全一致。60000/10000 的训练测试数据划分,28x28 的灰度图片。你可以直接用它来测试你的机器学习和深度学习算法性能,且不需要改动任何的代码。
image.png
▲ 图1.2.2 Fashion-MNIST数据集合

1.2.3 faMNIST

第十六届智能汽车竞赛AI视觉组分赛区数据集[2] 包含了906张 283×283彩色动物(Animal)和水果(Fruit)的数据集合,原本 用于智能车竞赛中智能视觉组嵌入式识别的训练集合,在 LeNet对于水果与动物进行分类[7] 被转换成与MNIST相同的尺寸格式。这样可以用于神经网络初学者学习CNN的测试集合。

faMNIST10图片库:
种类:10大类
色彩:灰度
尺寸:32×32

下面是faMNIST10中的部分图片样本。
image.png
▲ 图1.2.3  faMNIST10-gray 中部分图片样本

飞桨AI Studio - 人工智能学习与实训社区[8] : https://aistudio.baidu.com/aistudio/datasetdetail/121924 可以下载数据集合。其中包括有五个目录:

├─famnist-all :283×283彩色图片
├─famnist10 :32×32彩色图片
├─famnist10-64 :64×64彩色图片
├─famnist10-64-gray :64×64灰度图片
└─famnist10-gray :32×32灰度图片

这个集合会随着智能车竞赛同学们的搜集逐步丰富起来。

02 LeNet网络

2.1 背景

LeNet神经网络[9] 由深度学习三巨头之一的Yan LeCun提出,他同时也是卷积神经网络 (CNN,Convolutional Neural Networks)之父。LeNet主要用来进行手写字符的识别与分类,并在美国的银行中投入了使用。

LeNet的实现确立了CNN的结构,现在神经网络中的许多内容在LeNet的网络结构中都能看到,例如卷积层,Pooling层,ReLU层。虽然LeNet早在20世纪90年代就已经提出了,但由于当时缺乏大规模的训练数据,计算机硬件的性能也较低,因此LeNet神经网络在处理复杂问题时效果并不理想。虽然LeNet网络结构比较简单,但是刚好适合神经网络的入门学习。
image.png
▲ 图2.1.1  LeNET神经网络结构
640 (1).gif
▲ 图2.1.2  手写体字符数据

2.2 图片分类

无论是手写体字符,还是普通的图片,在计算机中都是由数字组成的矩阵。同样的CNN网络也可以进行分类。下面就利用LeNet来对于faMNIST数据集合进行分类。

测试分类环境就利用百度的PaddlePaddle中的 AI Studio[10] ,利用AI Studio就无需在自己的电脑上安装复杂的深度学习环境,只要电脑能够联网就可以在任何一个接入点完成深度学习网络的搭建与训练。百度 已经是连续第三年对全国大学生智能车竞赛竞赛进行赞助[11] 了,免费的培训和算力卡更是帮助同学们对车模作品注入更多的人工智能。

2.2.1 读入图像数据

从子目录 /home/studio/data/famnist/famnist10-gray中读入灰度图片数据,并根据文件名称生成图片的标号。文件名的第一个字母对应的数字与动物、水果对应关系参加下面python 字典中的说。

afname = {'cat':0, 'cow':1, 'dog':2, 'horse':3, 'pig':4,
          'apple':5, 'banana':6, 'durian':7, 'grape':8, 'orange':9}
import cv2

import paddle
import paddle.nn.functional as F
from paddle import to_tensor as TT
from paddle.nn.functional import square_error_cost as sqrc

famnist = '/home/aistudio/data/famnist'
imgdir = 'famnist10-gray'

 #------------------------------------------------------------
def loadimgdata(imgdir):
    '''
    loadimgdata: Load test image data into RAM
    Param imgdir: Directory for storing the image picture .
    Return: imgdata,imglabel
    '''

    imgfile = os.listdir(imgdir)

    imgdata = []
    imglabel = []
    for f in imgfile:
        img = cv2.imread(os.path.join(imgdir, f))
        imgdata.append(img.T[0][newaxis,:])
        imglabel.append(int(f[:1]))

    return array(imgdata), array(imglabel)

imgdata,imglabel = loadimgdata(os.path.join(famnist,imgdir))

2.2.2 构建训练数据加载函数

为了后面对网络进行训练,根据paddle技术文档说明,构建如下的数据加载函数。它本质上需要完成以下两个函数的重载:

  • getitem :返回有参数 index指定下表的图片数据和对应的标号。数据类型为Tensor('float32')以及Tensor('int64')。
  • len :返回训练数据的总数。

    class famnist(paddle.io.Dataset):
     def __init__(self, num_samples):
         super(famnist, self).__init__()
         self.num_samples = num_samples
    
     def __getitem__(self, index):
         data = imgdata[index]/255
         label = imglabel[index]
         return TT(data, dtype='float32'), TT(label, dtype='int64')
    
     def __len__(self):
         return self.num_samples
    
    _dataset = famnist(len(imglabel))
    train_loader = paddle.io.DataLoader(_dataset, batch_size=100, shuffle=True)

    最终有 paddle.io.DataLoader函数进行封装,提供批训练的数据块,以及对数据进行随机打乱。

2.2.3 构建LeNet网络

imageSize = 32
ks = 5
in_channel=1
L = ((imageSize-ks+1)//2-ks+1)//2

class mnist(paddle.nn.Layer):
    def __init__(self, ):
        super(mnist, self).__init__()
        self.conv1 = paddle.nn.Conv2D(in_channels=in_channel, out_channels=6, kernel_size=ks, stride=1, padding=0)
        self.conv2 = paddle.nn.Conv2D(in_channels=6, out_channels=16, kernel_size=ks, stride=1, padding=0)
        self.mp1    = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.mp2    = paddle.nn.MaxPool2D(kernel_size=2, stride=2)
        self.L1     = paddle.nn.Linear(in_features=16*L*L, out_features=120)
        self.L2     = paddle.nn.Linear(in_features=120, out_features=86)
        self.L3     = paddle.nn.Linear(in_features=86, out_features=10)

    def forward(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.mp1(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = self.mp2(x)
        x = paddle.flatten(x, start_axis=1, stop_axis=-1)
        x = self.L1(x)
        x = F.relu(x)
        x = self.L2(x)
        x = F.relu(x)
        x = self.L3(x)
        return x

这是标准的LeNet网络结构。是由两个卷积-Pooling层,三个全连接层组成。卷积核尺寸为5。

下面是由 paddle.summary()函数给出的网络基本参数。

Layer (type)       Input Shape          Output Shape         Param #    
*************************************=
   Conv2D-7      [[100, 1, 32, 32]]    [100, 6, 28, 28]         156      
  MaxPool2D-7    [[100, 6, 28, 28]]    [100, 6, 14, 14]          0       
   Conv2D-8      [[100, 6, 14, 14]]   [100, 16, 10, 10]        2,416     
  MaxPool2D-8   [[100, 16, 10, 10]]    [100, 16, 5, 5]           0       
   Linear-10        [[100, 400]]          [100, 120]          48,120     
   Linear-11        [[100, 120]]          [100, 86]           10,406     
   Linear-12        [[100, 86]]           [100, 10]             870      
*************************************=
Total params: 61,968
Trainable params: 61,968
Non-trainable params: 0
Input size (MB): 0.39
Forward/backward pass size (MB): 6.18
Params size (MB): 0.24
Estimated Total Size (MB): 6.80

网络的运算参数,可以由paddle.flops函数给出。下表显示了卷积核尺寸为5的情况下,对应的每层的计算量。可以看到前面两层的卷积运算占了整个网络运算量的86%,减少卷积运算是提高网络速度的关键。

+--------------+-------------------+-------------------+--------+----------+
|  Layer Name  |    Input Shape    |    Output Shape   | Params |  Flops   |
+--------------+-------------------+-------------------+--------+----------+
|   conv2d_6   |  [100, 1, 32, 32] |  [100, 6, 28, 28] |  156   | 12230400 |
|   conv2d_7   |  [100, 6, 14, 14] | [100, 16, 10, 10] |  2416  | 24160000 |
| max_pool2d_6 |  [100, 6, 28, 28] |  [100, 6, 14, 14] |   0    |    0     |
| max_pool2d_7 | [100, 16, 10, 10] |  [100, 16, 5, 5]  |   0    |    0     |
|   linear_9   |     [100, 400]    |     [100, 120]    | 48120  | 4800000  |
|  linear_10   |     [100, 120]    |     [100, 86]     | 10406  | 1032000  |
|  linear_11   |     [100, 86]     |     [100, 10]     |  870   |  86000   |
+--------------+-------------------+-------------------+--------+----------+
Total Flops: 42308400     Total Params: 61968
paddle.summary(net, input_size=(100,in_channel,imageSize,imageSize))
paddle.flops(net, input_size=(100, in_channel, imageSize, imageSize), print_detail=True)

2.2.4 网络训练

下面就是对该网络进行100循环的训练。

net = mnist()
EPOCH_NUM = 100
optimizer = paddle.optimizer.Adam(learning_rate=0.01, parameters=net.parameters())

for epoch in range(EPOCH_NUM):
    for batchid, data in enumerate(train_loader()):
        out = net(data[0])
        loss = F.cross_entropy(out, data[1])
        acc = paddle.metric.accuracy(out, data[1])

        loss.backward()
        optimizer.step()
        optimizer.clear_grad()

        if batchid %100*0:
            print("Pass:{}, Loss:{}, Acc:{}".format(epoch,loss.numpy(), acc.numpy()))

训练过程中网络的损失函数和分类精度变化曲线由下图所示。可以看到经过30个循环的训练网络基本上就达到了100%的识别性能。
image.png
▲ 图2.2.1 LeNet在faMNIST训练收敛情况

2.3 训练对比

LeNet作为一个简单的CNN,虽然对于复杂的问题来说,它的分类能力有限。但对于faMNIST数据集合,它还是具有很强的识别性能。

2.3.1 卷积核尺寸

对于FAMNIST中的十种动物和水果进行识别测试[12] 对比了不同的卷积核尺寸大小对于faMNIST数据集合分类效果对比。除了卷积核不能够为 1 之外,其他尺寸的卷积核对于识别能力影响不大。卷积核尺寸越小,网络的运算量就越小。

下面给出了卷积核的尺寸为2和7两种情况下网络训练收敛情况,两种情况差别不大。
image.png
▲ 图2.3.1  卷积核尺寸为2 对应的训练收敛曲线
image.png
▲ 图2.3.2  卷积核尺寸为7对应的训练收敛曲线

2.3.2 学习速率

学习速率是网络训练过程中的调节参数(Super-Parameters,超参)。在LeNet对faMNIST训练集合上,学习速率 lr 在小于0.05的情况下都可以使得网络很快收敛。而且 lr 越大,网络收敛越快。
image.png
▲ 图2.3.3  不同的学习速率对应的网络收敛情况

一旦学习速率超过0.05,网络训练就会不收敛。网络识别的精度一直很低。
image.png
▲ 图2.3.4  网络收敛情况会随着学习速率大于0.05而发生突变

2.3.3 不同训练集合

LeNet网络对于faMNIST五种不同的规格都可以很好的分类,无论是黑白还是彩色图片,无论尺寸是32×32,还是64×64,识别效果差不多。甚至对于原始的283×283也能够很好的识别。

之所以这样,最主要的原因是:

  • 图片的质量比较好,没有ub受到实际环境光线的影响;
  • 样本数量比较少;

因为,为了将来能够使得训练模型能够适应不同环境下、不同视角下、不同距离下采集到的的图片识别,需要通过一定的数据增强的方法来扩展训练集合。

识别总结

利用经典的LeNet深度学习网络,可以完成对于智能车竞赛中智能视觉组对搬运物品分类的要求。虽然实际比赛中还会包含有五种交通工具,那么利用LeNet仍然是可以完成小类别的识别与定位的。

仅仅使用标准的数据库训练的样本,还是无法满足实际要求,后面还需要:

  • 进一步增加数据库,使得模型能够适应实际环境下所采集到的的图片数据;
  • 进一步对LeNet的网络参数进行精简,毕竟将来改模型需要部署在NXP单片机中,过大的神经网络会使得单片机识别速度降低。

参考资料
[1]第十六届全国大学生智能车竞赛AI视觉组: https://zhuoqing.blog.csdn.net/article/details/117424506
[2]子类中: https://zhuoqing.blog.csdn.net/article/details/121899620
[3]MNIST数据集合: https://blog.csdn.net/simple_the_best/article/details/75267863
[4]YANN.LECUN: http://yann.lecun.com/exdb/mnist/
[5]Fashion-MNIST: https://zhuanlan.zhihu.com/p/28847070
[6]MNIST 手写数字集:https://link.zhihu.com/?target=http://yann.lecun.com/exdb/mnist/
[7]LeNet对于水果与动物进行分类: https://zhuoqing.blog.csdn.net/article/details/121973850
[8]飞桨AI Studio - 人工智能学习与实训社区: https://aistudio.baidu.com/aistudio/datasetdetail/121924
[9]LeNet神经网络: https://www.jianshu.com/p/cd73bc979ba9
[10]AI Studio: https://zhuoqing.blog.csdn.net/article/details/121885758
[11]已经是连续第三年对全国大学生智能车竞赛竞赛进行赞助: https://zhuoqing.blog.csdn.net/article/details/121983083
[12]对于FAMNIST中的十种动物和水果进行识别测试: https://zhuoqing.blog.csdn.net/article/details/121985820

原文链接:TsinghuaJoking
作者:卓晴

推荐阅读

更多嵌入式AI技术相关内容请关注嵌入式AI专栏。
推荐阅读
关注数
16591
内容数
1230
嵌入式端AI,包括AI算法在推理框架Tengine,MNN,NCNN,PaddlePaddle及相关芯片上的实现。欢迎加入微信交流群,微信号:aijishu20(备注:嵌入式)
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息