大鹏说 :本文约1200字,阅读需要4分钟。本文讲述了利用深度学习原理实现自动识别花卉品种的过程。成果优秀,令人惊奇,绝对是情人节选花神助手~
关键词:阿里云 Python 邮件 星座
p.s.文末有源代码分享
转眼,一年一度的情人节又到了。每个情人节我总要面临一个问题:如何给我的女盆友们买花?花店里琳琅满目的花,我又不认识,万一买错品种了,我将会永远失去她们。我决定搞一个模型来帮我完成花的识别工作。
1. 学习素材准备
为了掌握花卉的鉴别能力,我特地找了海量的花卉图片以供赏析。
一共14个品种的花:
image
每个品种共1000张艳照,共计14000张:
image
如何让机器能够准确识别这些花的品种,很简单,一遍一遍反复的学习,直到能认准为止——“一遍一遍反复学习”这一行为,我们称之为深度学习。
学习要讲究方法,比如如何区分玫瑰和菊花,很简单,玫瑰一般为红色,花朵是卷起来的,而菊花是黄灿灿的,花朵的形状…大家都懂的。这种提取特征的学习方法,我们通常称之为卷积。
2. 模型建立
先导入用于深度学习的python包,我们使用Keras框架
from keras.models import Sequential
from keras.layers import Conv2D,MaxPool2D,Activation,Dropout,Flatten,Dense
from keras.optimizers import Adam
from keras.preprocessing.image import ImageDataGenerator,img_to_array,load_img
from keras.models import load_model
因为Keras框架本身是基于tensorflow构建的,性能卓著,又不需要tensorflow超高难度的代码编写,封装十分彻底,用法与skitlearn类似。
在开始组织神经网络前,我们先定义一些参数,方便调试与修改。
IMG_W = 224 #定义裁剪的图片宽度
IMG_H = 224 #定义裁剪的图片高度
CLASS = 14 #图片的分类数
EPOCHS = 5 #迭代周期
BATCH_SIZE = 64 #批次大小
TRAIN_PATH = 'data/train' #训练集存放路径
TEST_PATH = 'data/test' #测试集存放路径
SAVE_PATH = 'flower_selector' #模型保存路径
LEARNING_RATE = 1e-4 #学习率
DROPOUT_RATE = 0 #抗拟合,不工作的神经网络百分比
下面是每个参数的解释:
IMG_W,IMG_H:用于模型学习的图片尺寸。
因为我们的样本图片尺寸各不相同,而模型学习需要大小统一的图片,所有这里设定其宽高。研究表明正方形的图片在模型拟合方面,效果优于长方形的图片,所以这里长宽设置相同。特别注意,图片尺寸越大,训练花费时间越久,所以千万不要设置如1920*1920分辨率大小,否则就算等到天荒地老模型也训练不完。
CLASS:图片的分类数量,
我们一共有14种花,所以,设置为14。
EPOCHS:训练周期。
模型把所有样本图片都训练一次为一周期,而需要训练成一个准确率较高的模型,一个周期显然不行。这里我们可以自由设置周期大小,我一般设置5-40个周期,循环结束保存模型,接着继续。
BATCH_SIZE:批次大小。
按理说一个周期里,我们可以把所有图片一股脑丢给机器训练,但是我们显卡的显存或者CPU的内存只有那么大,放多了容易溢出,也就是常说的爆显存,所以我们要设置个批次,把图片一批一批喂给模型训练,我这里设置为一批64张图片,如果你的显卡性能略差可以设置成32或者更小。
TRAIN_PATH:存放训练集的路径。
TEST_PATH:存放测试集的路径。
LEARNING_RATE:学习率,模型学习的速度。
并不是越快越好(数字越大),越快容易造成模型震荡,无法收敛,也就是没有训练效果。1e-4是科学计数法,为0.0001。这里学习率一般设置有几个选择1e-1,1e-2,1e-3,1e-4。通常探索阶段,设置的小一点。
DROPOUT_RATE:每次训练不工作的神经元百分比。
在模型训练中,我们通常会遇到一个问题,过拟合。也就是说在训练集上准确率很高,而测试集上准确率很低。这里设置一个DROPOUT参数,定义每一次训练,随机一定百分比的部分不参与训练,这样会起到抵抗过拟合的效果。
主要是定义训练集和测试集的图片标准。两者都有的一个参数为rescale,因为图片是RGB色彩,颜色范围从0-255,rescale,使数据归一化为0-1的范围。但是在训练集中我们还定义了旋转,平移,缩放等功能,这些操作的目的是随机变换输入的图片样式,增加样本量。注意!这一步操作仅仅只是确定了功能,并没有对任何图片进行操作,相当于先构建了一个模具。
然后,我们开始搭建神经网络:
model = Sequential() #创建一个神经网络对象
#添加一个卷积层,传入固定宽高三通道的图片,以32种不同的卷积核构建32张特征图,
# 卷积核大小为3*3,构建特征图比例和原图相同,激活函数为relu函数。
model.add(Conv2D(input_shape=(IMG_W,IMG_H,3),filters=32,kernel_size=3,padding='same',activation='relu'))
#再次构建一个卷积层
model.add(Conv2D(filters=32,kernel_size=3,padding='same',activation='relu'))
#构建一个池化层,提取特征,池化层的池化窗口为2*2,步长为2。
model.add(MaxPool2D(pool_size=2,strides=2))
#继续构建卷积层和池化层,区别是卷积核数量为64。
model.add(Conv2D(filters=64,kernel_size=3,padding='same',activation='relu'))
model.add(Conv2D(filters=64,kernel_size=3,padding='same',activation='relu'))
model.add(MaxPool2D(pool_size=2,strides=2))
#继续构建卷积层和池化层,区别是卷积核数量为128。
model.add(Conv2D(filters=128,kernel_size=3,padding='same',activation='relu'))
model.add(Conv2D(filters=128,kernel_size=3,padding='same',activation='relu'))
model.add(MaxPool2D(pool_size=2, strides=2))
model.add(Flatten()) #数据扁平化
model.add(Dense(128,activation='relu')) #构建一个具有128个神经元的全连接层
model.add(Dense(64,activation='relu')) #构建一个具有64个神经元的全连接层
model.add(Dropout(DROPOUT_RATE)) #加入dropout,防止过拟合。
model.add(Dense(CLASS,activation='softmax')) #输出层,一共14个神经元,对应14个分类
先用Sequential()定义一个模型对象,再往里面添加神经网络层。模型结构大致为两个卷积层,一个池化层,共三组,再增加两个全连接层,最终输出为10大类。
我再把网络结构进行可视化,以便更直观地观察模型。
from keras.utils.vis_utils import plot_model
from keras.models import load_model
import matplotlib.pyplot as plt
model = load_model('flower_selector.h5')
plot_model(model,to_file="model.png",show_shapes=True,show_layer_names=True,rankdir='TB')
plt.figure(figsize=(10,10))
img = plt.imread("model.png")
plt.imshow(img)
plt.axis('off')
plt.show()
绘制的结构图如下:
image
再来讲一讲如何构建合理的网络结构。一般为1-2个卷积层,加1个池化层,共2-3组,最后增加1-2个全连接层输出,结构没有定式,但如果样本太少,建议层数不宜过多,容易过拟合。
设置好网络结构,我们设置优化器,这里使用adam优化器,使用梯度下降法优化模型,并返回准确率。
adam = Adam(lr=LEARNING_RATE) #创建Adam优化器
model.compile(optimizer=adam,loss='categorical_crossentropy',metrics=['accuracy']) #使用交叉熵代价函数,adam优化器优化模型,并提取准确率
lr:学习率,数值为脚本开头设置的数值大小。
loss:损失函数,用于衡量模型误差,模型训练的目的就是让损失函数的值尽可能小,这里我们使用交叉熵代价函数。
metrics:返回准确率。
最后,生成训练集和测试集的迭代器,用于把图片按一定批次大小传入模型训练。
train_generator = train_datagen.flow_from_directory( #设置训练集迭代器
TRAIN_PATH, #训练集存放路径
target_size=(IMG_W,IMG_H), #训练集图片尺寸
batch_size=BATCH_SIZE #训练集批次
)
test_generator = test_datagen.flow_from_directory( #设置测试集迭代器
TEST_PATH, #测试集存放路径
target_size=(IMG_W,IMG_H), #测试集图片尺寸
batch_size=BATCH_SIZE, #测试集批次
)
增加判定机制,如果文件夹下没有已训练的模型,那么从头开始训练,如果已有训练过的模型,那么在此基础上,继续训练:
try:
model = load_model('{}.h5'.format(SAVE_PATH)) #尝试读取训练好的模型,再次训练
print('model upload,start training!')
except:
print('not find model,start training') #如果没有训练过的模型,则从头开始训练
构建好所有内容,使用fit_generator()拟合模型:
model.fit_generator( #模型拟合
train_generator, #训练集迭代器
steps_per_epoch=len(train_generator), #每个周期需要迭代多少步(图片总量/批次大小=11200/64=175)
epochs=EPOCHS, #迭代周期
validation_data=test_generator, #测试集迭代器
validation_steps=len(test_generator) #测试集迭代多少步
)
传入训练集和测试集迭代器,传入周期,传入迭代的步数。
模型拟合完毕,保存模型结构,用于预测和再次训练:
model.save('{}.h5'.format(SAVE_PATH)) #保存模型
print('finish {} epochs!'.format(EPOCHS))
3.模型训练
运行脚本~
image
这里会先显示训练集和测试集的样本大小,分类数量,分类名称。如果是使用GPU训练模型,这里还会显示显卡参数。强烈建议使用GPU模式。因为根据运行后的结果,模型在训练200个周期以上后,准确率才能达到80%,一个周期平均1分钟,而使用CPU模式,则要30分钟左右,效率差距巨大。
接下来,正式开始训练,会显示批次,步数,准确率等信息,一开始准确率是很低的。只有7%,约等于1/14,完全随机:
image
训练一段时间后,准确率会上升:
image
这里我训练超过200个周期以后,准确率达到约80%。这里注意,前200个周期我设置了0.5的dropout,发现模型提升速度比较慢,建议数值可以设置小一点,甚至为0。
经过漫长的训练,我训练了约400个周期。模型最终效果如图所示:
image
训练集准确率大于90%,而测试集准确率约84%。我发现再继续训练,训练集准确率还能再提高,但测试集准确率几乎提高不了了,过拟合情况越来越严重,这时,果断停止训练。
4.结果展示
模型训练好了,那么我们采用实际的案例来测试下效果。我从花店拍了一些不同花卉的照片如下:
image
写一个简单的脚本,功能为导入训练好的模型,再把用于预测的图片转换成模型的对应格式。比如尺寸为224*224~
from keras.models import load_model
from keras.preprocessing.image import img_to_array,load_img
import numpy as np
import os
# 载入模型
model = load_model('flower_selector.h5')
label = np.array(['康乃馨','杜鹃花','桂花','桃花','梅花','洛神花','牡丹','牵牛花','玫瑰','茉莉花','荷花','菊花','蒲公英','风信子'])
def image_change(image):
image = image.resize((224,224))
image = img_to_array(image)
image = image/255
image = np.expand_dims(image,0)
return image
然后,遍历文件夹下的图片进行预测:
for pic in os.listdir('./predict'):
print('图片真实分类为',pic)
image = load_img('./predict/' + pic)
image = image_change(image)
print('预测结果为',label[model.predict_classes(image)])
print('----------------------------------')
结果如下:
image
预测准确率达到百分之百,鼓掌~!!!!!
经过了层层挑战,我终于完成了机器识花的工作,准备去给我的女盆友们买花了!大家也快关注“大鹏教你玩数据”后在后台回复【30】,获取源代码和学习素材学起来吧~
免费获取【1.6G AI学习资料】和其他福利,请加QQ群:837627861
转载请在公众号后台联系小编,请勿擅自转载。
网友评论