使用预先训练网络和特征抽取大力提升图像识别率

2023-10-10 18:50

本文主要是介绍使用预先训练网络和特征抽取大力提升图像识别率,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

神经网络在项目实践中遇到的一大问题是数据不足。任何人工智能项目,在数据不足面前都会巧妇难为无米之炊,算法再精巧,只要数据量不足,最后的效果都不尽如人意,我们目前正在做的图像识别就是如此,要想让网络准确的识别猫狗图片,没有几万张图片以上是做不到的。

君子擅假于物,我们没有图片对模型进行训练,但如果别人有足够的图片,并且已经训练好了相应网络,我们能不能直接拿过来就用呢?答案是肯定的。有一些机构使用大量图片训练网络后,并把训练好的网络分享出来,假设别人用几万张猫狗图片训练出了网络,我们直接拿过来用于识别自己的猫狗图片,那显然效率和准确率比我们自己构造一个网络要高的多。

有很多机构,构造了自己的网络后,将ImageNet上海量的图片输入到网络中训练,最后得到了识别率很高的网络,而且他们愿意把劳动成果分享出来,由此我们可以不客气的直接借用。后面我们将使用一个大型卷积网络,它经过了大量数据的严格训练,这些图片数据来源于ImageNet,该网站包含140万张图片资源,这些图片大多涉及我们日常生活的物品以及常见动物,显然很多不同种类的猫和狗必然包含在内。

我们将使用一个训练好的神经网络叫VGG16,后面我们还会遇到一系列稀奇古怪的公开网络,例如ResNet, Inception, Xception等等,这些网络很像程序开发中的开源库,别人做好后分享给别人直接用。由于别人做出的网络肯定跟我们自己面对的应用场景有所区别,所以在使用时,我们必须对其进行相应改造,常用的方法有特征抽取和参数调优,我们分别就这两种方法进行深入讨论。

我们先看所谓的特征抽取。在我们构造卷积网络时,一开始先是好几层卷积层和Max Pooling层,然后会调用Flatten()把他们输出的多维向量压扁后,传入到普通层,下面代码就是我们前几节做过的卷积网络,它的结构正如我们刚才描述的那样:

from keras import layers
from keras import models
from keras import optimizersmodel = models.Sequential()
#输入图片大小是150*150 3表示图片像素用(R,G,B)表示
model.add(layers.Conv2D(32, (3,3), activation='relu', input_shape=(150 , 150, 3)))
model.add(layers.MaxPooling2D((2,2)))model.add(layers.Conv2D(64, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))model.add(layers.Conv2D(128, (3,3), activation='relu'))
model.add(layers.MaxPooling2D((2,2)))model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))model.compile(loss='binary_crossentropy', optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])model.summary()

我们现在要借用的的VGG16网络,其结构与上面差不多,只不过它的Conv2D和MaxPooling层要比我们上面做的多得多而已。在我们借用别人训练好的网络时,往往要去掉Flatten()后面的网络层,因为那些网络层与别人构造网络时的具体应用场景相关,他们的应用场景与我们肯定不同,我们要借用的是Flatten上面那些由卷积层和Max Pooling层输出的结果,这些结果蕴含着对训练图片本质的认知,这才是我们想要的,去掉Flatten后面的神经层,换上我们自己的神经层,这个行为就叫特征抽取,具体流程如下图:

屏幕快照 2018-07-23 下午4.26.25.png

VGG16网络早已包含在keras框架中,我们可以方便的直接引用,我们通过如下代码来初始化一个VGG16网络实例:

from keras.applications import VGG16conv_base = VGG16(weights = 'imagenet', include_top = False, input_shape=(150, 150, 3))conv_base.summary()

weight参数告诉程序将网络的卷积层和max pooling层对应的参数传递过来,并将它们初始化成对应的网络层次。include_top表示是否也要把Flatten()后面的网络层也下载过来,VGG16对应的这层网络用来将图片划分到1000个不同类别中,由于我们只用来区分猫狗两个类别,因此我们去掉它这一层。input_shape告诉网络,我们输入图片的大小是150*150像素,每个像素由[R, G, B]三个值表示。上面代码执行后结果如下:

屏幕快照 2018-07-23 下午4.48.49.png

从上面输出结果看出,VGG16的网络结构与我们前面做的网络差不多,只不过它的层次要比我们多不少。最后的(None, 4, 4, 512)表示它将输出4*4的矩阵,而这些矩阵有512层,或者你也可以看成它将输出一个4*4的矩阵,而矩阵每个元素是包含512个值的向量。

接下来我们将把自己的图片读进来,把图片喂给上面网络,让它把图片的隐含信息给抽取出来:

import os 
import numpy as np
from keras.preprocessing.image import ImageDataGeneratorbase_dir = '/Users/chenyi/Documents/人工智能/all/cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')datagen = ImageDataGenerator(rescale = 1. / 255)
batch_size = 20def extract_features(directory, sample_count):features = np.zeros(shape = (sample_count, 4, 4, 512))labels = np.zeros(shape = (sample_count))generator = datagen.flow_from_directory(directory, target_size = (150, 150), batch_size = batch_size,class_mode = 'binary')i = 0for inputs_batch, labels_batch in generator:#把图片输入VGG16卷积层,让它把图片信息抽取出来features_batch = conv_base.predict(inputs_batch)#feature_batch 是 4*4*512结构features[i * batch_size : (i + 1)*batch_size] = features_batchlabels[i * batch_size : (i+1)*batch_size] = labels_batchi += 1if i * batch_size >= sample_count :#for in 在generator上的循环是无止境的,因此我们必须主动break掉breakreturn features , labels#extract_features 返回数据格式为(samples, 4, 4, 512)
train_features, train_labels = extract_features(train_dir, 2000)
validation_features, validation_labels = extract_features(validation_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)

上面代码利用VGG16的卷积层把图片的特征抽取出来,接下来我们就可以吧抽取的特征输入到我们自己的神经层中进行分类,代码如下:

train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4* 512))from keras import models
from keras import layers
from keras import optimizers#构造我们自己的网络层对输出数据进行分类
model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim = 4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation = 'sigmoid'))model.compile(optimizer=optimizers.RMSprop(lr = 2e-5), loss = 'binary_crossentropy', metrics = ['acc'])
history = model.fit(train_features, train_labels, epochs = 30, batch_size = 20, validation_data = (validation_features, validation_labels))

由于我们不需要训练卷积层,因此上面代码运行会很快,我们把训练结果和校验结果画出来看看:

import matplotlib.pyplot as pltacc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']epochs = range(1, len(acc) + 1)plt.plot(epochs, acc, 'bo', label = 'Train_acc')
plt.plot(epochs, val_acc, 'b', label = 'Validation acc')
plt.title('Trainning and validation accuracy')
plt.legend()plt.figure()plt.plot(epochs, loss, 'bo', label = 'Training loss')
plt.plot(epochs, val_loss, 'b', label = 'Validation loss')
plt.title('Training and validation loss')
plt.legend()plt.show()

上面代码运行后结果如下:

屏幕快照 2018-07-23 下午6.02.32.png

从上面可以看出,经过一百多万张图片训练的网络,其识别效果就要比我们用4000张图片训练的网络要好很多,网络对图片的校验正确率达到了99%以上,同时对训练数据和校验数据的损失估计完全是一模一样的。

上面的方法叫特征提取,还有一种方法叫参数调优。特征提取时,我们把图片输入VGG16的卷积层,让他直接帮我们把图片中的特征提取出来,我们并没有通过自己的图片去训练更改VGG16的卷积层,参数调优的做法在于,我们会有限度的通过自己的数据去训练VGG16提供的卷积层,于是让其能从我们的图片中学习到相关信息。我们从VGG16模型中获取了它六层卷积层,我们在调优时,让这六层卷积层中的最高2层也去学习我们的图片,于是最高两层的链路权重参数会根据我们的图片性质而更改,基本情况如下:

屏幕快照 2018-07-24 上午8.58.14.png

上图就是我们从VGG16拿到的卷积层,我们用自己的图片去训练修改它最高的两层,其他层次不做修改,这种只影响模型一部分的方法就叫参数调优。调优必须只对VGG16的卷积层做小范围修改,因为它的模型是经过大数据,反复训练得到的,如果我们对它进行大范围修改,就会破坏它原来训练的结果,这样人家辛苦做出来的工作成果就会被我们毁于一旦。所以参数调优的步骤如下:

1,将我们自己的网络层添加到VGG16的卷积层之上。
2,固定VGG16的卷积层保持不变。
3,用数据训练我们自己添加的网络层
4,将VGG16的卷积层最高两层放开
5,用数据同时训练放开的那两层卷积层和我们自己添加的网络层

我们看看代码就明白上面步骤所要描述的意思:

model = models.Sequential()
#将VGG16的卷积层直接添加到我们的网络
model.add(conv_base)
#添加我们自己的网络层
model.add(layers.Flatten())
model.add(layers.Dense(256, activation = 'relu'))
model.add(layers.Dense(1, activation = 'sigmoid'))
model.summary()

上面代码运行后结果如下:

屏幕快照 2018-07-24 上午9.13.59.png

从上面输出结果看,VGG16的卷积层已经有一千多万个参数了!用个人电脑单个CPU是不可能对这个模型进行训练的!但我们可以训练它的其中一部分,我们把它最高三层与我们自己的网络层结合在一起训练,同时冻结最低四层。下面的代码将会把卷积层进行部分冻结:

conv_base.trainable = True
set_trainable = False
#一旦读取到'block5_conv1'时,意味着来到卷积网络的最高三层
#可以使用conv_base.summary()来查看卷积层的信息
for layer in conv_base.layers:if layer.name == 'block5_conv1':set_trainable = Trueif set_trainable:#当trainable == True 意味着该网络层可以更改,要不然该网络层会被冻结,不能修改layer.trainable = Trueelse:layer.trainable = False

然后我们把数据传入网络,训练给定的卷积层和我们自己的网络层:

#把图片数据读取进来
test_datagen = ImageDataGenerator(rescale = 1. / 255)
train_generator = test_datagen.flow_from_directory(train_dir, target_size = (150, 150), batch_size = 20,class_mode = 'binary')
validation_generator = test_datagen.flow_from_directory(validation_dir, target_size = (150,150),batch_size = 20,class_mode = 'binary')
model.compile(loss = 'binary_crossentropy', optimizer = optimizers.RMSprop(2e-5),metrics = ['acc'])history = model.fit_generator(train_generator, steps_per_epoch = 100, epochs = 30, validation_data = validation_generator,validation_steps = 50)

由于我的电脑运行上面代码时太慢,因此这里我没有把训练结果显示出来,有兴趣的读者可以自己尝试一下。

更详细的讲解和代码调试演示过程,请点击链接

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
这里写图片描述

这篇关于使用预先训练网络和特征抽取大力提升图像识别率的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/182368

相关文章

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像

使用Java发送邮件到QQ邮箱的完整指南

《使用Java发送邮件到QQ邮箱的完整指南》在现代软件开发中,邮件发送功能是一个常见的需求,无论是用户注册验证、密码重置,还是系统通知,邮件都是一种重要的通信方式,本文将详细介绍如何使用Java编写程... 目录引言1. 准备工作1.1 获取QQ邮箱的SMTP授权码1.2 添加JavaMail依赖2. 实现

MyBatis与其使用方法示例详解

《MyBatis与其使用方法示例详解》MyBatis是一个支持自定义SQL的持久层框架,通过XML文件实现SQL配置和数据映射,简化了JDBC代码的编写,本文给大家介绍MyBatis与其使用方法讲解,... 目录ORM缺优分析MyBATisMyBatis的工作流程MyBatis的基本使用环境准备MyBati

使用Python开发一个图像标注与OCR识别工具

《使用Python开发一个图像标注与OCR识别工具》:本文主要介绍一个使用Python开发的工具,允许用户在图像上进行矩形标注,使用OCR对标注区域进行文本识别,并将结果保存为Excel文件,感兴... 目录项目简介1. 图像加载与显示2. 矩形标注3. OCR识别4. 标注的保存与加载5. 裁剪与重置图像

使用Python实现表格字段智能去重

《使用Python实现表格字段智能去重》在数据分析和处理过程中,数据清洗是一个至关重要的步骤,其中字段去重是一个常见且关键的任务,下面我们看看如何使用Python进行表格字段智能去重吧... 目录一、引言二、数据重复问题的常见场景与影响三、python在数据清洗中的优势四、基于Python的表格字段智能去重

使用Apache POI在Java中实现Excel单元格的合并

《使用ApachePOI在Java中实现Excel单元格的合并》在日常工作中,Excel是一个不可或缺的工具,尤其是在处理大量数据时,本文将介绍如何使用ApachePOI库在Java中实现Excel... 目录工具类介绍工具类代码调用示例依赖配置总结在日常工作中,Excel 是一个不可或缺的工http://

Java之并行流(Parallel Stream)使用详解

《Java之并行流(ParallelStream)使用详解》Java并行流(ParallelStream)通过多线程并行处理集合数据,利用Fork/Join框架加速计算,适用于大规模数据集和计算密集... 目录Java并行流(Parallel Stream)1. 核心概念与原理2. 创建并行流的方式3. 适

如何使用Docker部署FTP和Nginx并通过HTTP访问FTP里的文件

《如何使用Docker部署FTP和Nginx并通过HTTP访问FTP里的文件》本文介绍了如何使用Docker部署FTP服务器和Nginx,并通过HTTP访问FTP中的文件,通过将FTP数据目录挂载到N... 目录docker部署FTP和Nginx并通过HTTP访问FTP里的文件1. 部署 FTP 服务器 (

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、

Python中配置文件的全面解析与使用

《Python中配置文件的全面解析与使用》在Python开发中,配置文件扮演着举足轻重的角色,它们允许开发者在不修改代码的情况下调整应用程序的行为,下面我们就来看看常见Python配置文件格式的使用吧... 目录一、INI配置文件二、YAML配置文件三、jsON配置文件四、TOML配置文件五、XML配置文件