Advertisement

基于深度学习的车型识别系统的设计与实现

阅读量:

**计算机系统的介绍

文章目录

    • 一 概要
  • 二、绪论

    • 1.1.研究背景与意义
    • 1.1.1.研究背景
    • 1.1.2.研究意义
    • 1.2.主要研究内容
  • 二、车型识别系统总体设计

    • 2.1.系统需求分析
    • 2.2.环境介绍
    • 2.3.整体方案设计
    • 2.4.原理概述
  • 三、数据集设计与制作

    • 4.1.数据集介绍
    • 4.2.数据爬取与整理
    • 4.2.1.数据爬取
    • 4.2.2.数据整理
    • 4.3.图像预处理
  • 四、模型应用

    • 4.1.分类程序设计
    • 4.1.1.目的与意义
    • 6.1.2.实现方案
    • 4.2.图像识别网站设计
    • 核心代码
  • 总结

  • 五、 文章目录

一 概要

随着城市化建设的不断发展,我国对交通建设的需求也不断增长。发展智能交通是一项重要的任务,而汽车车型的识别在智能交通领域又起着极其重要的作用。本文采用PyTorch这一深度学习框架,利用ResNet-50预训练模型为基础训练得到了用于车型识别的模型,通过分析比对全连接层中的各项参数与各类函数,对该模型的全连接层进行了修改与调优以适应本模型。除此之外,本文通过编写爬虫脚本采集迁移学习时所需要的样本图片以完成训练集的制作。经过不断尝试与挑选得到了训练完成的最优模型并将其应用到实际场景中,分别设计了一个汽车图片分类程序以及一个以html为前端、python为后端的车型识别网站。
关键字:深度学习、车型识别、迁移学习、PyTorch。

二、绪论

1.1.研究背景与意义

1.1.1.研究背景

随着城市化建设不断发展,我国对交通建设的需求也不断增长,我国成为了世界上在交通领域基础设施建设最快的国家之一[1],但车辆管控问题、道路交通问题、车辆违章问题等层出不穷,很难做到全面、有效的管理。目前,计算机技术不断提高,在大数据、物联网和机器学习等技术的支持下,人类社会日渐趋于智能化,智能地管理城市交通正是目前急需发展的,即智能交通。智能交通指的是将计算机技术、物联网、机器学习等技术综合运用于交通体系,能够有效、准确、全面地管理交通[2]。目前,智能交通在国外一些发达国家已经得到了广泛地使用,在我国也快速地发展着。
汽车车型的识别在智能交通领域有着极其重要的作用,它采用图像处理等技术,根据不同汽车车型的图像特征,对其进行分类。我国从九十年代已经开始了这方面的研究,社会上很多人才已经在这一方面投入了大量的研究,对交通智能化领域的发展提供了巨大的帮助。
对于车辆的智能识别来说,深度学习提供了一种高效可靠的解决方案,其中卷积神经网络在车型识别中得到了广泛的应用[3]。近几年来,随着大数据的发展,传统机器学习的发展并未随着数据量的增多而发展,深度学习却有了很好的表现。运用大量数据来提高自身性能的深度学习迅速被应用到各种场合,并发挥了独特的作用。从2017年1月Facebook AI 研究团队发布PyTorch以来的三年的时间,PyTorch发展迅速,在深度学习领域不断发光发热,为深度学习爱好者提供了一个优秀的深度学习框架。

1.1.2.研究意义

一方面,汽车车型识别能有效地简化一些交通管理问题,减小了交通规划所需的成本,提升管理效率。例如,有些路段禁止大货车通行,要严格管控违章车辆需要投入大量人力成本,但智能化识别车型便能方便有效地管理这些交通问题。另一方面,也有利于道路车辆信息实时地采集,通过对采集到的这些数据进行统计分析,有利于之后对不同路段道路交通的规划建设。除此之外,车型识别对于数据集的管理也有重要的意义,对于其他汽车智能定损项目的数据集的筛选制作提供了便捷的方法,极大地提升了素材管理的人力成本。

1.2.主要研究内容

本文开发了一个以python为后台,以html为前台用于车型识别的网站系统。系统后台通运用 PyTorch深度学习框架,根据性能及需求选择并搭建了一个适合的神经网络,通过采集大量素材,制作了训练数据集、验证数据集及测试数据集对网络不断训练和测试,根据结果不断进行优化和完善,最终得到了一个可靠的识别系统。
与此同时,本文开发一个了python程序,用于将文件夹中的汽车图片按照车型分入不同的文件夹中,该程序可以用于到一些实际项目中,帮助项目整理车型类图像样本素材,节约素材整理时间,提高效率。

二、车型识别系统总体设计

2.1.系统需求分析

以杭州某公司为例,该公司的车辆保险项目是利用机器学习方法,仅需通过车损照片就可以快速、智能、高效地判断赔损情况。由于定损过程中对车型具有限制,因此需要员工进行网络车型图片爬取并人工筛选制作数据集,效率低下且常常因为员工的失误导致车型数据集存在不属于该车型的杂质样本。本车型识别系统很好地可以应对这一需求,该系统能将车型图片进行快速有效地分类,即节约了该公司研发时间,又降低了研发成本,可以实现高度自动化。
除此之外,随着机器学习技术的快速进步,智慧交通这一概念不断被提起。实现交通智能化、管理高效化时钟离不开基于各项机器学习技术的一些识别系统,本车型识别系统就是其中的一个重要组成部分。利用本车型识别系统,可以实时监控路段车型情况,有效排查违规车型这一智能交通领域的基本需求。
综上所述,制作一个基于深度学习框架的车型识别系统无论对于相关企业或是交通智能管理都有着较大的需求和重要的意义。

2.2.环境介绍

操作系统:Windows 10
Python IDE(集成开发环境): JetBrains PyCharm+ Jupyter notebook
Python版本:Python3.7
深度学习框架:PyTorch
PyTorch框架介绍:深度学习框架可以有效简化神经网络搭建的代码,同时使代码有更好的可读性。相较其他较为常用的框架,PyTorch的性能更好,具体表现在模型的训练上有着更快的速度,这也是深度学习上一个重要的指标,主要由于PyTorch是由C语言开发。PyTorch框架的出现相对比较迟,目前的学习资源也相对来说较少,但由于该框架的实用性很高,目前也受到了很多人的关注。同时,PyTorch在学习上和使用上相对来说比较简单灵活。
CUDA版本:10.2(用于进行GPU运算,相较于CPU运算大大提高训练效率)
爬虫编写相关库:requests、urllib3
神经网络训练相关库:numpy、torch、torchvision、opencv、matplotlib

2.3.整体方案设计

本文分析了Pytorch和TensorFlow深度学习框架的优劣势和所适用的应用场景,结合车型识别这一实际目的,最终选择了灵活简易,易于应用的Pytorch框架。在深度学习网络的设计中,对经过不同层数的预训练的RestNet模型进行迁移学习实验。迁移学习是目前深度学习中的一个热点,主要也是为了解决目前很多情况下标注数据稀缺的问题[4],在素材较少的场合下也能达到满意的识别结果。本文用全链接神经网络代替RestNet网络最后一层,并将该输出经过Softmax函数进行分类判断得到预测结果。实验过程中还对预训练模型的最后一层进行了包括Loss函数、学习率及其动态变化方法、正则化函数、梯度下降算法及优化器等在内的算法和函数的调整以最优化适应本车型识别模型。
用于训练的车型被分为小汽车、越野车、面包车、大巴车、货车、SUV六类,通过爬虫技术采集该六类车型的图片素材,通过筛选和清洗之后制作包括训练集、验证集和测试集在内的完整数据集。训练集将输入到模型中用来训练和更新模型的参数,验证集则将在每一轮次的训练后对模型识别准确率进行验证,评估选择在迭代过程中的最优模型。根据分析验证集的准确率数值,与训练集的数据同时分析之后可以对模型最后一层的各项函数和参数进行人工调优后得到更适应车型识别的模型,最终可以将测试集输入到训练结果最好的模型中进行考核该模型的泛化能力。
模型训练测试完成之后,可以将这个模型应用于各种车辆图片的车型识别与分类。分类程序中,程序得到需要被分类的车型图片的文件夹路径之后将各图片输入到训练完的模型之中,根据测试模型输出的对应标签将各车型图片复制到该目录下生成的对应车型文件夹中,完成分类工作。此外,本文还设计了一个示意网站来与用户进行交互,用户将上传汽车图片到后端进行识别,识别结果将返还到用户界面上。
系统整体框架流程图如图1-1所示。
在这里插入图片描述

图1-1 系统整体框架图

2.4.原理概述

深度神经网络可以模拟出人体的神经结构,建立神经网络来解析数据进行学习。神经网络一般包括了输入层、隐藏层和输出层,深度神经网络一般含有较多数量的隐藏层,层与层之间进行输入与输出进行加权计算连接,加上偏置后,通过激活函数对输出按照激活函数进行变换,变换后即为该神经元的输出。神经网络的训练就是给网络输入大量的数据集进行训练,对神经元中未知的权重值通过反向传播进行计算和修正,使之总结出事物特征。
卷积深度神经网络是常用于图像识别的深度学习方法之一,其隐藏层较为复杂。一般来说,一个卷积网络的隐藏层包含了卷积层、池化层、全连接层。卷积层能够计算得到素材样本的局部特征,从而当图片本质未发生变化,只是位置发生变化时,能够依然保留图像的本质特征,如当图片进行旋转、镜像、变换位置时,依然使神经网络能提取出与原图类似的特征。
由于训练一个完全的卷积神经网络需要大量数据集,训练时长将无法预估,所以本文将应用到卷积神经网络中的ResNet网络模型进行迁移学习。迁移学习,就是将已经经过预训练的适用于大数据的模型及其参数迁移到自己的模型中,然后根据本身数据集的需求,对训练好的模型的输入层、输出层进行调整,使其适用于自己的数据集,再进行训练。车型识别系统总体设计。

三、数据集设计与制作

4.1.数据集介绍

该系统包含识别的车型为六类,分别为大巴车、货车、小汽车、面包车、越野车、SUV,数据集被分为训练集、验证集和测试集。
训练集作用:训练集输入到神经网络中,使网络自动学习数据集中图片的特征,从而对神经元中未知的权重值通过反向传播进行计算和修正,使之能总结出图片额的特征。
验证集作用:验证集将在每一轮次的训练后对模型识别准确率进行验证,评估选择在迭代过程中的最优模型。根据分析验证集的准确率数值和变化趋势,根据准确率的收敛和发散情况,可以对模型最后一层的各项函数和参数进行人工调优后得到更适应车型识别的模型。
测试集作用:用于对最终的模型进行测试与评估。
训练集、验证集、测试集分别放入三个文件夹内,分别命名为train、valid、test。每个文件夹内包含六个文件夹:0_BUS、1_TRUCK、2_CAR、3_MINIBUS、4_JEEP、5_SUV,每个文件夹对应一类车型。将相应车型的图片放入相应的文件夹,用来给图片打上标签。

4.2.数据爬取与整理

4.2.1.数据爬取

由于深度神经网络的训练需要大量的图像素材,所以本文使用python编写爬虫脚本,进行多线程爬取百度图库中相关的车型的搜索结果图片,关键代码如下:
1)对输入的搜索关键字进行url编码,urllib库中的urllib.parse用于url解析:
queryEnc = urllib.parse.quote(keyword)
2)为了提高图片爬取的效率,需要进行多线程工作。将进程池设为5,当有新的请求被提交时,如果进程池中的进程数未到最大,则为提交的请求创建一个新的进程,直至进程数到达上限,其余的请求则需要等待池中其中一个进程结束。在多线程中对图片进行读取和保存的相关代码,首先需要导入相关的库,设置进程池的大小,通过异步非阻塞的方式对图片进行读取和存储:

复制代码
    from multiprocessing import Pool
    p = Pool(5)
    p.apply_async(func=get_and_save, args=(real_urls,))

3)在百度图库中搜索关键字面包车之后,可以查看到ajax请求信息及其url,如图4-1所示。
在这里插入图片描述

图4-1 包含ajax请求信息的示意图
如图4-2所示,%E9%9D%A2%E5%8C%85%E8%BD%A6该字符串是搜索的关键字“面包车”的url编码形式。
在这里插入图片描述

图4-2 ajax请求响应信息
所以在编写爬取图片时的ajax请求的url,需要使用queryEnc(上文中对中文的关键字进行url编码后的结果)代替%E9%9D%A2%E5%8C%85%E8%BD%A6,同时对pn=30根据输入的所需图片数进行修改,主函数代码如下:

复制代码
    def main():
    keyword = input('输入爬取的图片名: ')
    page = int(input("请输入爬取的页数(每页30张图): "))
    	queryEnc = urllib.parse.quote(keyword)
    	p = Pool(5)
    	for i in range(page):
    	url="https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=2013	26592&is=&fp=result&queryWord="+queryEnc+"&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=&hd=&latest=&copyright=&word="+queryEnc+"&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn="+str(i*30)+"&rn=30&gsm=1e&1582816234965="
    html = get_page_html(url)
    		real_urls = parse_result(html)
    		p.apply_async(func=get_and_save, args=(real_urls,))
    	p.close()
    p.join()

4)def get_page_html(url)函数用于获取该ajax请求返回的文本,文本内容包含了多张请求图片的信息,并以json格式封装,响应文本信息的json格式如图4-3所示。
在这里插入图片描述

图4-3 json格式下的响应文本内容
其中data展开后,包了含所有请求图片的信息,以其中的一张图片为例,该图片所包含的信息如图4-4所示。
在这里插入图片描述

图4-4 data中某图片信息
5)def parse_result(html)函数则是通过正则表达式从文本上一步获取的文本中提取每一张图片的真实url,代码如下:
url_real = re.findall(‘“thumbURL”:“(.*?)”,’, html)
该正则表达式用来匹配"thumbURL":“和”,中间的数据,即每一张图片的真实url。此外,re.findall()方法用来返回所有捕获组的列表。
6)对列表中获取的图片url,发送request get请求获取返回的数据包,代码如下:
r = requests.get(url_real, headers=headers)
在上述代码中,首先对响应包中的http状态码进行判断,如果状态码值为200,即响应一切正常,则将图片的信息以二进制形式写入,代码如下:

复制代码
    if r.status_code == 200:
    	r.encoding = r.apparent_encoding

将图片的content写入到指定路径下面,代码如下:

复制代码
    with open(path, 'wb') as f:
    	f.write(content)

4.2.2.数据整理

由于爬取的图像中存在大量不符合训练要求的图像,因此需要对爬取到的图像进行筛选和清洗。这些不规范的图像包括图像中同时包含多种车型的多辆车、车辆较不完整、非爬取类型的车型等情况。反复清洗核验之后每种车型各取大巴车277张、货车211张、小汽车127张、面包车210张、越野车133张、SUV214张作为验证集。取每种车型各取40张车型图像组成测试集。其余图像作为训练集,最终完成的数据集的训练集中拥有大巴车样本654个、货车样本931个、小汽车样本632个、面包车样本513个、越野车样本861、SUV样本722个。取数据集中的每类样本样例展示如图4-5所示,统计结果如表4-1所示。
在这里插入图片描述

图4-5.数据集展示

表4-1 数据集样本数量统计表
在这里插入图片描述

4.3.图像预处理

当出现训练模型过于复杂,训练数据集样本少,特征的维度过多等一些情况时,训练结束得到的模型常常会出现过拟合现象。从数据上通俗地来说就是训练集得到的训练准确率远远大于验证集和测试集的准确率。过拟合是神经网络模型训练过程中比较常见的一个现象,除了加入Dropout函数或正则化算法等方法之外,还可以通过上一章中提到的数据集清洗来清洗掉噪音过多的训练样本的方法,以及本小结所介绍的图像预处理(数据增强)的方法来尽可能改善训练完成的模型过拟合程度,保证模型有较好的泛化能力,使模型的识别率更高。
在将图片输入到Resnet网络进行训练、验证、测试之前,都需要对图像进行一定的数据增强。实验表明[10],数据增强有一定的抗过拟合问题,一定程度上增加了样本的随机性。在本文的数据增强方法中,先对训练集的图片进行按随机的长宽比例裁剪,输出分辨率为224,裁剪照片为原照片的80%100%,随机长宽比范围为0.81.2,由于原图片的长宽比与输出图片不一致,在长宽比变化时需要进行插值,本文选用了RandomResizedCrop函数的默认插值法,即双线性插值,代码如下:
在这里插入图片描述
在这里插入图片描述

图5-1 某点坐标
完成随机长宽比例裁剪之后需要将图片进行随机旋转,参数degree设置为15度,即将图像在范围内随机旋转。重采样方法采用RandomRotation函数默认的最邻近法。该方法处理简单,处理速度快。其原理为变换后得点得RGB值为取原图对应点得最近邻整数坐标得RGB值。随机旋转代码如下:

复制代码
    transforms.RandomRotation(degrees=15)

为了增加图像的随机性,使上述处理完成的图像有50%的可能性会发生水平方向上的翻转,代码如下:

复制代码
    transforms.RandomHorizontalFlip()

然后将图片进行中心裁剪为224*224像素的训练图像,代码为:

复制代码
    transforms.CenterCrop(size=224)

完成图像的一些随机处理之后,需要将图转换为张量以便输入给神经网络。最后将图片进行标准化操作,即将图片的RGB三通道减去均值(0.485, 0.456, 0.406),再除以标准差(0.229, 0.224, 0.225),这里的均值和标准差是由数百万张图像数据计算得到,本文使用的时ResNet-50预训练神经网络,因此可以使用该计算完成的均值和标准差。转换为张量和标准化的代码如下:

复制代码
    transforms.ToTensor()
    transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))

对于验证集和测试集而言,数据预处理方法较为简单,其顺序为:最邻近法重置图像分辨率;224*224像素中心裁剪;转换为张量;标准化。
在加载训练集、验证集和测试集的图片时,都需要使用Pytorch库中的ImageFolder函数。以加载训练集为例,设置训练集的路径root以及数据增强方式,该数据增强方式为上述的所有训练集数据增强方式的按序组合,具体代码如下:

复制代码
    train=datasets.ImageFolder(root='mydata/train',transform=train)

完成Dataset对象的创建之后,需要用到DataLoader函数。该函数用于在Dataset对象中随机取batch_size个样本放入到DataLoader中,以便进行训练、验证、测试时分批输入到深度学习网络中,在本设计中,batch_size设计为32。以训练集为例,具体代码如下:

复制代码
    train_loader=torch.utils.data.DataLoader(train,batch_size=batch_size,shuffle=True)

四、模型应用

4.1.分类程序设计

4.1.1.目的与意义

该分类程序的目的是应用先前训练完成的识别效果较好的深度模型,设计完成一个批量车型图片自动化分类的程序。将批量图片文件夹的路径输入到程序中后,程序会在该文件夹中生成一个存放分类结果的文件夹,该文件夹中包含了六类车型对应的文件夹:bus、truck、car、minibus、jeep、suv,并根据每张图片的识别结果将图片分类放入对应的文件夹中。
随着智能化交通的发展,目前,许多车辆识别的项目对车型有指定要求,在训练素材和测试素材采集时,需要人工对杂乱的爬取的图像根据车型进行筛选和分类。由于图片的素材量是十分大,该车型分类程序可以十分有效地减少了人工成本,节省了素材管理事件,提升工作效率。除此之外,交通部门可以通过对采集的道路车辆图像进行分类分析,有利于之后对道路交通进行规划建设。

6.1.2.实现方案

本文挑选已训练完成识别效果较好的模型在分类程序中进行加载,用于识别输入的图片。由于版本兼容性问题需要重新声明池化层,代码如下:

复制代码
    model =torch.load('models/mydata_model_24.pt')

首先,在程序中输入指定文件夹,通过os库中的os.makedirs()函数获取文件夹中的图片名,并采用os.path.join()函数获取图片的绝对路径,将加载的模型和图片绝对路径传入到函数def classify(model, test_image_name)中。该函数用于识别图片的类别,首先将需要识别的图像根据第三章图像预处理中所提到的方法,数据增强之后转换为张量再进行标准化,然后将图像张量输入到模型。CUDA为英伟达公司推出的运算平台,能够实现CPU+GPU并行计算从而提高计算速度[14],因为GPU在图片处理速度上远大于CPU,PyTorch中cuda()用于将变量传输到GPU上,在电脑环境配备的情况下,因选择使用GPU来运行模型。在分类程序中,不需要计算梯度也不需要进行反向传播,torch.no_grad()能够将这部分时间省略。model.eval()函数用于对输入图像进行测试。具体代码如下:

复制代码
    test_image_tensor = [test_image_tensor.view(1, 3, 224,224),test_image_tensor.view(1, 3, 224, 224).cuda()][torch.cuda.is_available()]
    with torch.no_grad():
    model.eval()
    out =model(test_image_tensor)
    ps = torch.exp(out)

模型对每个图像的输出结果如图6-1所示,torch.exp()可以对该结果张量中的元素求指数,将负数结果转化为正数,ps.topk(1, dim=1)用于获取最大指数值及其下标,即其对应的车型类别标签。
在这里插入图片描述

图6-1 图片对应张量示意图
将每一类车型文件夹通过os.path.join()函数结合用户输入的文件夹路径获取车型文件夹的绝对路径,并根据模型中每一类车型对应的位置将文件夹写入列表new_path[]中,若未存在文件夹,就在该路径创建对应文件夹。最终,根据classify函数返回的结果,将遍历的每一张图片复制到对应文件夹中,代码如下,其中i为遍历的每张图片的绝对路径:

复制代码
    shutil.copyfile(img_path,os.path.join(new_path[x],i) )

4.2.图像识别网站设计

该图像识别网站的开发主要用于将先前编写好的识别函数应用于网站上,用户可以在该网站上传汽车图片,上传界面如图6-2所示,后端将图片写入服务器中后调用车型识别函数,将函数返还的结果显示在前端界面上,识别结果如图6-3所示。
在这里插入图片描述

该网站是一个以html为前端,python为后端的图片识别网站,采用了bottle这一微型web框架。
前端采用html编写,元素定义html表单,表单中包含文件上传控件、按钮控件和文本框控件。将表单的action属性设为"/upload",即当鼠标点击上传按钮时,会将表单中的内容提交到upload中,并且界面将跳转到upload中显示识别结果。file控件放入表单中,客户端可以选择上传本地文件。用时file控件中的accept属性可以用于限制上传文件的类型,因该网站的目的是用于图片识别,可以将accept属性设为accept=”image/*”。
后端用了python进行编写,使用bottle框架实现前后端交互。前端提交表单数据传给后端后,后端接收数据并进行处理后返回一个新页面,通过 @app.route 装饰器中的POST方法返回一个新页面,页面路径为/upload,代码如下:

复制代码
    @route('/upload', method='POST')

在修饰器中加入do_upload()函数,该函数用于将表单中获取的图片数据存储到服务器文件夹中,使用request.files.get()函数获取图片数据,filedata.save()函数用于存储图片,调用先前编写好的图片识别函数def classify(model, filename)获取识别结果,该函数具体在。

核心代码

复制代码
    附件1:程序源码
    训练与模型筛选程序:
    import torch
    from torch.optim import lr_scheduler
    from torch.utils.data import DataLoader
    import torchvision
    from torchvision import datasets, models, transforms
    import torchvision.transforms
    import torch.nn as nn
    import torch.optim as optim
    import time
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    import os
    import random
    
    train = transforms.Compose([
    transforms.RandomResizedCrop(size=224, scale=(0.8, 1.0)),
    transforms.RandomRotation(degrees=15),
    transforms.RandomHorizontalFlip(),
    transforms.CenterCrop(size=224),
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
    
    ])  # 修改的位置
    valid = transforms.Compose([
    transforms.Resize(size=224),
    transforms.CenterCrop(size=224),
    transforms.ToTensor(),
    transforms.Normalize(mean=(0.485, 0.456, 0.406), std=(0.229, 0.224, 0.225))
    ])
    
    batch_size = 24
    num_classes = 6
    epochs_sum = 100
    
    train = datasets.ImageFolder(root='mydataf/train', transform=train)
    valid = datasets.ImageFolder(root='mydataf/valid', transform=valid)
    
    train_loader = torch.utils.data.DataLoader(train, batch_size=batch_size, shuffle=True)
    valid_loader = torch.utils.data.DataLoader(valid, batch_size=batch_size, shuffle=True)
    
    train_lenth = len(train)
    valid_lenth = len(valid)
    print(train_lenth, valid_lenth)
    
    
    def train_mydata(model, optimizer, loss_f, scheduler, epoch):
    loss_train = 0.0
    acc_train = 0.0
    
    print('*' * 15)
    print('Epoch {}/{}'.format(epoch + 1, epochs_sum))
    
    model.train()  # 启用BatchNormalization和Dropout
    
    for i, (inputs, labels) in enumerate(train_loader):
    
        inputs = inputs.to(device)
        labels = labels.to(device)
    
        optimizer.zero_grad()  # 梯度清0
    
        with torch.set_grad_enabled(True):  # 计算导数
            outputs = model(inputs)  # 将图片输入至网络中得到对应输出
            ret, preds = torch.max(outputs, 1)  # ret:预测标签 preds:预测概率对应值
            loss = loss_f(outputs, labels)  # 计算损失
            loss.backward()
            optimizer.step()  # 更新网络参数
    
        loss_train += loss.item() * inputs.size(0)
    
        right_sum = preds.eq(labels.data.view_as(preds))  # 对比统计预测正确数量,得到布尔型
        acc = torch.mean(right_sum.type(torch.FloatTensor))  # 转换为tensor并计算平均得到正确率
        acc_train += acc.item() * inputs.size(0)  # 正确率累加
    
    scheduler.step()  # 更新学习率
    print("1" * 20)
    print(optimizer)
    print("1" * 20)
    
    return model, loss_train, acc_train
    
    
    def valid_mydata(model, optimizer, loss_f, scheduler, epoch):
    loss_valid = 0.0
    acc_valid = 0.0
    
    model.train()  # 启用BatchNormalization和Dropout
    
    for i, (inputs, labels) in enumerate(valid_loader):
        inputs = inputs.to(device)
        labels = labels.to(device)
        outputs = model(inputs)  # 将图片输入至网络中得到对应输出
    
        ret, preds = torch.max(outputs, 1)  # ret:预测标签 preds:预测概率对应值
        loss = loss_f(outputs, labels)  # 计算损失
        loss_valid += loss.item() * inputs.size(0)
    
        right_sum = preds.eq(labels.data.view_as(preds))  # 对比统计预测正确数量,得到布尔型
        acc = torch.mean(right_sum.type(torch.FloatTensor))  # 转换为tensor并计算平均得到正确率
        acc_valid += acc.item() * inputs.size(0)  # 正确率累加
    
    return loss_valid, acc_valid
    
    
    device = torch.device("cuda:0")
    
    history = []
    acc_max = 0.0
    acc_max_epoch = 0
    
    resnet50 = models.resnet50(pretrained=True)
    
    for param in resnet50.parameters():
    param.requires_grad = False
    
    fc_inputs = resnet50.fc.in_features
    resnet50.fc = nn.Sequential(
    nn.Linear(fc_inputs, 256),
    nn.ReLU(),
    nn.Linear(256,128),
    nn.ReLU(),
    nn.Linear(128,64),
    nn.ReLU(),
    nn.Dropout(0.4),
    nn.Linear(64,6),
    nn.LogSoftmax(dim=1)
    )
    
    resnet50 = resnet50.to('cuda:0' )
    
    loss_f = nn.NLLLoss()
    optimizer = optim.SGD(resnet50.parameters(), lr=0.001, momentum=0.95)
    # optimizer = optim.RMSprop(resnet50.parameters())
    scheduler = lr_scheduler.StepLR(optimizer, step_size=8, gamma=0.5)
    
    for epoch in range(epochs_sum):
    
    tstart = time.time()
    
    model, loss_train, acc_train = train_mydata(resnet50, optimizer, loss_f, scheduler, epoch)
    
    with torch.no_grad():
        model.eval()
        loss_valid, acc_valid = valid_mydata(resnet50, optimizer, loss_f, scheduler, epoch)
    
    avgloss_train = loss_train / train_lenth
    avgacc_train = acc_train / train_lenth
    avgloss_valid = loss_valid / valid_lenth
    avgacc_valid = acc_valid / valid_lenth
    avgacc = avgacc_valid * 0.7 + avgacc_train * 0.3
    if acc_max < avgacc:
        acc_max = avgacc
        acc_max_epoch = epoch + 1
    
    history.append([avgloss_train, avgloss_valid, avgacc_train, avgacc_valid])
    
    tend = time.time()
    print(
        "Epoch: {:03d}, Training: Loss: {:.4f}, Accuracy: {:.4f}%, \n\t\tValidation: Loss: {:.4f}, Accuracy: {:.4f}%, Time: {:.4f}s".format(
            epoch + 1, avgloss_train, avgacc_train * 100, avgloss_valid, avgacc_valid * 100,
            tend - tstart))
    print("Best Accuracy for validation : {:.4f} at epoch {:03d}".format(acc_max, acc_max_epoch))
    
    torch.save(model, 'models/' + 'mydata' + '_model_' + str(epoch + 1) + '.pt')
    
    图像识别函数:
    def classify(model, test_image_name):
    image_transforms = {
        'test': transforms.Compose([
            transforms.Resize(size=224),
            transforms.CenterCrop(size=224),
            transforms.ToTensor(),
            transforms.Normalize([0.485, 0.456, 0.406],
                                 [0.229, 0.224, 0.225])
        ])
    }
    num_classes = 6
    transform = image_transforms['test']
    test_image = Image.open(test_image_name)
    test_image_tensor = transform(test_image)
    test_image_tensor = [test_image_tensor.view(1, 3, 224, 224), test_image_tensor.view(1, 3, 224, 224).cpu()][torch.cuda.is_available()]
    with torch.no_grad():
        model.eval()
        out = model(test_image_tensor)
        ps = torch.exp(out)
        print(out)
        topk, topclass = ps.topk(1, dim=1)
        num=topclass.cpu().numpy()[0][0]
        return num
    
    本地图片批量分类程序代码:
    model = torch.load('models/mydata_model_25.pt', map_location=lambda storage, loc: storage)
    model.avgpool = nn.AvgPool2d(kernel_size=7, stride=1, padding=0)
    print("请输入本地路径:")
    path=input()
    # path = eval(repr(s).replace('\ ', '/'))
    root_path=['classify_finish\ dabache','classify_finish\ huoche','classify_finish\ xiaoqiche','classify_finish\ mianbaoche','classify_finish\ yueyeche','classify_finish\ SUV']
    new_path=[]
    for i in root_path:
    new_path.append(os.path.join(path,i))
    for i in new_path:
    if not os.path.exists(i):
        os.makedirs(i)
    lsdir = os.listdir(path)
    geshi=['jpg','png','tif','bmp']
    print(lsdir)
    for i in lsdir:
    img_path=os.path.join(path,i)
    if '.'  in img_path:
        if i.split('.')[1] in geshi:
            x=classify(model,img_path)
            shutil.copyfile(img_path,os.path.join(new_path[x],i) )
    
    车型识别网页代码:
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    import os
    from bottle import *
    from classify import classify
    import torch
    import torch.nn as nn
    from torchvision import datasets, transforms
    HTML = """
    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>车型识别</title>
    <style type="text/css">
    </style>
    </head>
    <body>
    <div style="text-align: center;">
    <h3>请上传车辆图片</h3>
    </div>
    <div class="file-box">
    <form action="/upload" method="post" enctype="multipart/form-data">
    <input type='text' name='pic' id='pic' class='txt' />  
    <input type='button' class='btn' value='浏览...' />
    <input type="file" name="filepic" class="file" id="filepic" size="28" accept="image/*" onchange="document.getElementById('pic').value=this.value" />
    <input type="submit" name="submit" class="btn" value="上传" onclick=""/>
    </form>
    </div>
    </body>
    </html>
    """
    model = torch.load('models/mydata_model_25.pt', map_location=lambda storage, loc: storage)
    model.avgpool = nn.AvgPool2d(kernel_size=7, stride=1, padding=0)
    base_path = os.path.dirname(os.path.realpath(__file__))  # 获取脚本路径
    upload_path = os.path.join(base_path, 'upload')  # 上传文件目录
    if not os.path.exists(upload_path):
    os.makedirs(upload_path)
    @route('/', method='GET')
    @route('/upload', method='GET')
    def index():
    return HTML
    result_name=['大巴车','货车','小汽车','面包车','越野车','SUV']
    @route('/upload', method='POST')
    def do_upload():
    filedata = request.files.get('filepic') 
    if filedata.file:
        file_name = os.path.join(upload_path, filedata.filename)
        try:
            filedata.save(file_name)
        except IOError:
            return '文件上传失败'
        result=classify(model,file_name)
        if (os.path.exists(file_name)):  # 判断文件是否存在
            os.remove(file_name)
        return '<div style="text-align: center;">文件上传成功, 分类结果: {}&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://127.0.0.1:6080/">返回</a></div>'.format(result_name[result])
    else:
        return '文件上传失败'
    @route('/favicon.ico', method='GET')
    def server_static():
    return static_file('favicon.ico', root=base_path)
    @error(404)
    def error404(error):
    return '404 发生页面错误, 未找到内容'
    run(port=6080, reloader=False)
    
    多线程爬虫脚本:
    import os
    import re
    import requests
    import urllib
    
    from multiprocessing import Pool
    
    def get_page_html(page_url):
    headers = {
        'Referer': 'https://image.baidu.com/search/index?tn=baiduimage',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
    }
    try:
        r = requests.get(page_url, headers=headers)
        if r.status_code == 200:
            r.encoding = r.apparent_encoding
            return r.text
        else:
            print('请求失败')
    except Exception as e:
        print(e)
    
    def parse_result(text):
    url_real = re.findall('"thumbURL":"(.*?)",', text)
    return url_real
    
    def get_image_content(url_real):
    headers = {
        'Referer': url_real,
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36'
    }
    try:
        r = requests.get(url_real, headers=headers)
        if r.status_code == 200:
            r.encoding = r.apparent_encoding
            return r.content
        else:
            print('请求失败')
    except Exception as e:
        print(e)
    
    def save_pic(url_real, content):
    root = 'D:\ xiaojiaoche\ '
    path = root + url_real.split('/')[-1]
    if not os.path.exists(root):
        os.mkdir(root)
    if not os.path.exists(path):
        with open(path, 'wb') as f:
            f.write(content)
            print('图片{}保存成功,地址在{}'.format(url_real, path))
    else:
        pass
    
    def get_and_save(real_urls):
    for real_url in real_urls:
        try:
            content = get_image_content(real_url)
            save_pic(real_url, content)
        except:
            print('Error:', real_url)
    
    def main():
    keyword = input('请输入你要查询的关键字: ')
    keyword_quote = urllib.parse.quote(keyword)
    depth = int(input("请输入要爬取的页数(每页30张图): "))
    p = Pool(10)
    for i in range(depth):
        url = 'https://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord+=&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&word={}&z=&ic=0&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&step_word={}&pn={}&rn=30&gsm=1e&1541136876386='.format(
            keyword_quote, keyword_quote, i * 30)
        html = get_page_html(url)
        real_urls = parse_result(html)
        p.apply_async(func=get_and_save, args=(real_urls,))
    p.close()
    p.join()
    if __name__ == '__main__':
    main()

总结

本文通过查阅大量文献资料,分析该系统在目前社会背景下的研究意义,以及国内外深度学习和图像识别应用的发展情况,确定了该课题。同时,本文通过有目的地研究不同深度学习方法的利弊,将迁移学习运用到了该系统中,因此,通过查阅一些文献资料和影像资料,分析并选择了比较先进、效果较好的深度学习网络模型——ResNet,进行实验比对,尽量提高识别准确率。
因深度学习的网络模型在不同应用场景下的检测效果会有偏差,本文根据训练完成模型的测试结果,查找有利于提升准确率的方法,考虑了如下参数的选择与调优: Loss函数选择、学习率、选择何种Regularization、选择何种梯度下降算法……除此,本文也研究学习了卷积神经网络的卷积过程,从而能够对这些模型有自己的认识和理解,在参数调优过程中能针对性地解决测试中出现的问题。
本文主要工作工作如下:
1) 深度学习框架搭建、训练与测试,其中包括:数据集的采集和数据集的清洗;
根据性能及需求选择并搭建深度学习网络(PyTorch)、反复调整网络参数并进行训练;网络的测试并与其他网络对比、分析及总结。
2) 系统人机交互的设计,其中包括:前后端交互,数据传输;界面与功能设计;Python办公自动化实现。
3) 其它内容包括:编写爬虫程序爬取训练素材和测试素材;对各类结果的统计进行分析来设计优化模型,使用较少的数据得到较好的模型;具体应用于公司车型分类工作中的可行性及优缺点的调查分析。
通过本次对车型识别系统的设计与应用,我自学掌握了一些深度学习的知识,也对人工智能技术有了更多的了解。虽然深度学习只是人工智能的一小个分支,但它给我们的生活与工作带来了很大的便捷。目前,行业中已有许多科学家研究者在这一领域打造了良好的基础,为我们学习者提供了非常好的学习环境,同时,借助一些深度学习的框架和一些预训练模型,我们在应用深度学习时也十分方便快捷。在接下来的工作与生活中,我也会学习并结合一些先进技术解决工作中一些繁杂问题,极大程度上提高了效率,享受人工智能带来的便捷。

五、 文章目录

目录
摘 要 1

  1. 引言 3
    1.1. 研究背景与意义 3
    1.2. 主要研究内容 3
    1.3. 论文组织结构 4

  2. 车型识别系统总体设计 5
    2.1. 系统需求分析 5
    2.2. 环境介绍 5
    2.3. 整体方案设计 5
    2.4. 原理概述 6

  3. 相关技术介绍 8
    3.1. ResNet-50网络架构介绍 8
    3.2. 前后端技术介绍 9

  4. 数据集设计与制作 11
    4.1. 数据集介绍 11
    4.2. 数据爬取与整理 11

  5. ResNet-50训练与调试 15
    5.1. 图像预处理 15
    5.2. 全连接层设计 16
    5.3. 损失函数与优化器设计 19
    5.4. 模型训练 20
    5.5. 泛化能力测试及结果分析 22

  6. 模型应用 23
    6.1. 分类程序设计 23
    6.2. 图像识别网站设计 24

  7. 总结与展望 25
    7.1. 本文总结 25
    7.2. 设想与展望 25
    致谢 26
    参考文献 27
    附件1:程序源码 28

全部评论 (0)

还没有任何评论哟~