基于Keras的图片多分类问题详解

    上一篇博文介绍了使用imageai通过五行代码来实现图像分类的问题,如果不使用imageai,使用keras和tensorflow如何灵活的训练图片多分类问题呢,其实imageai也是基于keras api封装的图像识别库。

    python,tensorflow,keras等库的安装参见上一篇博文的环境搭建部分:开发环境搭建

    学习keras推荐keras之父亲自撰写的一本keras入门书籍《Deep Learning with Python》,中文翻译版《Python深度学习》,浅显易懂,通过里面的例子可以很快上手训练,如果对于基本原理和算法不是很熟悉可以学习周志华的《机器学习》。

技术分享图片

    好了废话不多说,开始本章的内容,本章所用的训练数据还是上一篇博文使用imageai训练的数据:identprof-jpg,下载方式还是参见上一篇博文。

   一、第一次使用一般卷积网络的来训练数据看看训练效果如何,代码如下:

from tensorflow import keras
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import models
from keras import layers
import os
from keras import optimizers
from keras.utils import to_categorical
import matplotlib.pyplot as plt

# 动物数据预处理
train_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/train'
val_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/test'

# 不使用数据增强
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)

# 使用数据增强
# train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
# val_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)

# 使用迭代器生成图片张量
train_gen = train_datagen.flow_from_directory(train_dir, target_size=(224, 224), batch_size=20)
val_gen = train_datagen.flow_from_directory(val_dir, target_size=(224, 224), batch_size=20)

model = models.Sequential()
# 卷积采用3x3滑动卷积
model.add(layers.Conv2D(32, (3, 3), activation='relu',input_shape=(224, 224, 3)))
# 最大池化 采用 2x2
model.add(layers.MaxPool2D((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())
# 使用dropout操作,降低过拟合,说白了就是随机丢掉一些神经元
# model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
# 因为要分类10种图片,所以模型的最后一层采用10通道的输出对应10个分类
model.add(layers.Dense(10, activation='softmax'))
# 显示模型的层级结构
model.summary()
# 编译模型
model.compile(optimizer=optimizers.RMSprop(lr=1e-4), loss='categorical_crossentropy', metrics=['acc'])
# 训练模型
history = model.fit_generator(train_gen, epochs=30, validation_data=val_gen)

# 绘制训练精度损失曲线
acc = 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='training acc')
plt.plot(epochs, val_acc, 'bo', label='val acc')
plt.title('training & val accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'bo', label='training loss')
plt.plot(epochs, val_loss, 'bo', label='val loss')
plt.title('training & val loss')
plt.legend()

plt.show()

    从上图可以看出30轮后训练的精度已经达到了99.72%,而验证精度只有60.85%,差别还是很大的,说明了现在的模型过拟合比较严重的,再看下曲线显示

    可以看出该模型的效果不是很好,因为模型只是采用了4个卷积层,4个池化操作,效果不是很好。接下来我们使用VGG16模型来看看效果如何

    二、使用VGG16,进行特征提取,上代码

from tensorflow import keras
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import models
from keras import layers
import os
from keras import optimizers
from keras.utils import to_categorical
import matplotlib.pyplot as plt
from keras.applications import VGG16

# 动物数据预处理
train_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/train'
val_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/test'

# 下载VGG16模型文件,可以通过浏览器下载存放在C:\Users\Administrator\.keras\models下即可
conv_base = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))

# 不使用数据增强
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)

# 使用数据增强
# train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
# val_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)

# 使用迭代器生成图片张量
train_gen = train_datagen.flow_from_directory(train_dir, target_size=(224, 224), batch_size=20, class_mode='binary')
val_gen = train_datagen.flow_from_directory(val_dir, target_size=(224, 224), batch_size=20, class_mode='binary')

# 提取数据,因为构造器生成的数据标签是一维向量我们要分类10种不同的类型,所以需要将数据提取出来,并将标签one-hot
labelsx = []
datasx = np.zeros((9000, 224, 224 ,3))
for i in range(len(train_gen)):
    aa = train_gen.next()
    labelsx = np.hstack((labelsx, aa[1]))
    for j in range(20):
        datasx[20 * i + j] = aa[0][j]
        
train_data = datasx
train_labels = to_categorical(labelsx)

labelsy = []
datasy = np.zeros((2000, 224, 224 ,3))
for i in range(len(val_gen)):
    aa = val_gen.next()
    labelsy = np.hstack((labelsy, aa[1]))
    for j in range(20):
        datasy[20 * i + j] = aa[0][j]
        
val_data = datasy
val_labels = to_categorical(labelsy)

# 使用VGG16的h5文件进行特征提取
train_features = conv_base.predict(train_data)
val_features = conv_base.predict(val_data)

# reshape一下,因为全连接层接收的是一维向量,所以要要展开,为什么是7 * 7 * 512,可以看下train_features的shape就是7 * 7 * 512
train_features = np.reshape(train_features, (len(train_features), 7 * 7 * 512))
val_features = np.reshape(val_features, (len(val_features), 7 * 7 * 512))

model = models.Sequential()
model.add(layers.Dense(512, activation='relu', input_dim=7 * 7 * 512))
# 使用dropout 
# model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.compile(optimizer=optimizers.RMSprop(lr=1e-4), loss='categorical_crossentropy', metrics=['acc'])
history = model.fit(train_features, train_labels, epochs=30, validation_data=(val_features, val_labels))

# 绘制训练精度损失曲线
acc = 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, 'b', label='training acc')
plt.plot(epochs, val_acc, 'b', label='val acc')
plt.title('training & val accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'b', label='training loss')
plt.plot(epochs, val_loss, 'b', label='val loss')
plt.title('training & val loss')
plt.legend()

plt.show()

    通过上图可以看出,训练精度已经非常接近100%了,但是验证精度在几次之后就已经震荡不前了,但是要比第一次普通卷积网络好一些,而且特征提取速度非常快,再看下精度曲线。

    通过曲线可以看出验证精度在第4次左右基本上就已经达到70%左右了。接下来使用VGG16全网络训练一遍。

    三、VGG16全网络训练

    VGG16网络结构比较复杂,可参见网络介绍,本文不做赘述,使用全网络训练代价很大,需要很长时间,代码如下

from tensorflow import keras
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import models
from keras import layers
import os
from keras import optimizers
from keras.utils import to_categorical
import matplotlib.pyplot as plt
from keras.applications import VGG16

# 动物数据预处理
train_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/train'
val_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/test'

conv_base = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))

# 不使用数据增强
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)

# 使用数据增强
# train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
# val_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)

# 使用迭代器生成图片张量
train_gen = train_datagen.flow_from_directory(train_dir, target_size=(224, 224), batch_size=20)
val_gen = train_datagen.flow_from_directory(val_dir, target_size=(224, 224), batch_size=20)

model = models.Sequential()
# 添加VGG16 网络结构
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
# 使用dropout 
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))

model.compile(optimizer=optimizers.RMSprop(lr=1e-4), loss='categorical_crossentropy', metrics=['acc'])
history = model.fit_generator(train_gen, validation_data=val_gen, epoch=30)

# 绘制训练精度损失曲线
acc = 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, 'b', label='training acc')
plt.plot(epochs, val_acc, 'b', label='val acc')
plt.title('training & val accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'b', label='training loss')
plt.plot(epochs, val_loss, 'b', label='val loss')
plt.title('training & val loss')
plt.legend()

plt.show()

    通过上图可以看出效果并没有明显提升,再看看曲线

    可以看到直接使用VGG16的全网络效果也并没有多好,下面在VGG16模型的基础上微调

    四、微调VGG16模型

    冻结其中的一些层,并使用一些特种提取,然后保存成h5文件,上代码

from tensorflow import keras
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import models
from keras import layers
import os
from keras import optimizers
from keras.utils import to_categorical
import matplotlib.pyplot as plt
from keras.applications import VGG16

# 动物数据预处理
train_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/train'
val_dir = 'E:/KaggleDatas/idenprof-jpg/idenprof/test'

conv_base = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))

# 不使用数据增强
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)

# 使用数据增强
# train_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
# val_datagen = ImageDataGenerator(rescale=1./255, rotation_range=40., width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)

# 使用迭代器生成图片张量
train_gen = train_datagen.flow_from_directory(train_dir, target_size=(224, 224), batch_size=20)
val_gen = train_datagen.flow_from_directory(val_dir, target_size=(224, 224), batch_size=20)

# 微调模型 冻结VGG16部分层
conv_base.trainable = False
flag = False
for layer in conv_base.layers:
    if layer.name == 'block5_conv1':
        flag = True
    if flag:
        layer.trainable = True

model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
# 使用dropout 
model.add(layers.Dropout(0.5))
model.add(layers.Dense(10, activation='softmax'))
# 编译模型
model.compile(optimizer=optimizers.RMSprop(lr=1e-4), loss='categorical_crossentropy', metrics=['acc'])
# 训练模型
history = model.fit_generator(train_gen, epochs=20, validation_data=val_gen)
# 将模型训练文件保存
model.save('E:/KaggleDatas/idenprof-jpg/idenprof/identprof.h5')

# 绘制训练精度损失曲线
acc = 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, 'b', label='training acc')
plt.plot(epochs, val_acc, 'b', label='val acc')
plt.title('training & val accuracy')
plt.legend()

plt.figure()

plt.plot(epochs, loss, 'b', label='training loss')
plt.plot(epochs, val_loss, 'b', label='val loss')
plt.title('training & val loss')
plt.legend()

plt.show()

    如图效果还是没有好到哪里去,还是需要调整模型,看精度曲线

    五、用保存的模型测试

    用百度或者谷歌搜索医生、工程师、服务员、警察等图片,用保存的模型测试识别效果

    上代码

import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
from keras import models
from keras import layers
from keras import optimizers
import matplotlib.pyplot as plt
from keras.applications import VGG16

from tensorflow import keras
from keras import models
import numpy as np
from keras.preprocessing import image
from keras.applications.imagenet_utils import preprocess_input, decode_predictions
import os
import tkinter as tk
from tkinter import filedialog

model = models.load_model('E:/KaggleDatas/idenprof-jpg/idenprof/identprof.h5')

root = tk.Tk()
root.withdraw()

print('press enter to discern your image.')
print('press e to exit.')
for i in range(1000):
    cmd = input('input command.')
    if cmd == '':
        # 选择文件
        path = filedialog.askopenfilename()
        img = image.load_img(path)
        img = img.resize((224, 224))
        x = image.img_to_array(img)
        x = np.expand_dims(x, axis=0)
        # 缩放到 0-1
        x /= 255
        # 也可以使用下面函数自动缩放数据,可参考源码
        # x = preprocess_input(x)
        y = model.predict(x)
        print(y)
    elif cmd == 'e':
        break

    上述代码,可以选择图片进行测试,循环1000次,按enter选择突破进行识别,按e退出程序。

   识别结果是一个one-hot张量,可以通过10个类别对应位置上的概率来判断识别结果如上图第一个红框识别出来的是医生,第二个红框识别出来的是农民,因为idenprof目录结构如下:

   还需要继续调试模型识别效果并不好!接下来会尝试其他模型进行测试!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值