在上一讲中,笔者和大家探讨了迁移学习的基本原理,并利用 keras 基于 VGG16 预训练模型简单了在 mnist 数据集上做了演示。鉴于大家对于迁移学习的兴趣,本节笔者将继续基于迁移学习利用一些花朵数据搭建一个花朵识别系统。因为是带有教学系统的演示,本节将尽量将试验过程完整详细的展示给大家,也会在文章末尾附上试验数据和代码,以供大家学习和复现试验内容。
笔者使用的试验数据来自于罗大钧在 cos 统计之都上的分享,数据可于此处下载:https://pan.baidu.com/s/1jIMOc1S
下载数据后解压可见共有五个文件夹,每个文件夹是一种花类,具体信息如下:
五种花种加起来不过是 3669 张图片,数据量不算小样本但也绝对算不上多。所以我们采取迁移学习的策略来搭建花朵识别系统。
实际数据中玫瑰花存在着一张图片的重复,笔者直接对其进行了删除。花型图片大致如下:
需要导入的 package:
import os
import pandas as pd
import numpy as np
import cv2
import matplotlib.pyplot as plt
from PIL import Image
import time
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
import kerasfrom keras.models import Model
from keras.layers import Dense, Activation, Flatten, Dropout
from keras.utils import np_utils
from keras.applications.resnet50 import ResNet50
提取数据标签
数据没有单独给出标签文件,需要我们自行通过文件夹提取每张图片的标签,建立标签文件:
def tranverse_images(path):
labels = pd.DataFrame()
first_dir_file = [file for file in os.listdir(path)]
for item in first_dir_file:
flower = [image for image in os.listdir(path+item)]
labels_data = pd.DataFrame({'flower': flower, 'labels': item})
labels = pd.concat((labels, labels_data))
return labels
labels = tranverse_images('./flower_photos/')
labels.head()
这样标签文件就顺利拿到了。
图片预处理(缩放)
通过试验可知每张图片像素大小并不一致,所以在搭建模型之前,我们需要对图片进行整体缩放为统一尺寸。我们借助 opencv 的 python 库 cv2 可以轻松实现图片缩放,因为后面我们的迁移学习策略采用的是 ResNet50 作为预训练模型,所以我们这里将图片缩放大小为 224x224x3。单张图片的 resize 效果如下:
img = cv2.imread('./flower_photos/509239741_28e2cfe492_m.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img3)
print(img.shape)
缩放后的效果和尺寸:
img_1 = cv2.resize(img, (224, 224))
print(img_1.shape)
img3_1 = cv2.cvtColor(img3_1, cv2.COLOR_BGR2RGB)
plt.imshow(img_1)
批量缩放代码为:
# resize
def resize_image(path1, path2):
images = [img for img in os.listdir(path1)]
total = 0
start_time = time.time()
for img in images:
img1 = cv2.imread(path1 + img)
img1 = cv2.resize(img1, (224, 224))
total += 1
print('now is resizing {} image.'.format(total))
cv2.imwrite(path2+img, img1)
print('all images are resized, all resized image is {}.'.format(total))
end_time = time.time()
print('the resize time is {}.'.format(end_time - start_time))
resize_image(path1='./flower_photos/', path2='./resize_flower_photos/')
原始图片并不复杂,所以除了对其进行缩放处理之外基本无需多做处理。下一步我们需要将图片转化为可以拿来进行训练的 numpy 数组。
准备训练数据
处理好的图片无法直接拿来训练,我们需要将其转化为 numpy 数组的形式,另外标签也需要进一步的处理。
# image to array
def image2array(labels, path):
lst_imgs = [l for l in labels['flower']]
return np.array([np.array(Image.open(path+img)) for img in lst_imgs])
X = image2array(labels, './resize_flower_photos/')
print(X.shape)
np.save('./X_train.npy', X)
(3669, 224, 224, 3)
可见转化后的数组大小为 3669x224x224x3,跟我们的实际数据一致。然后我们再来处理标签数据,标签变量都是分类值,我们先需要将其进行硬编码 LabelEncoder,然后借助 keras 将其 one-hot 处理。具体处理如下:
lbl = LabelEncoder().fit(list(labels['labels'].values))
labels['code_labels'] = pd.DataFrame(lbl.transform(list(labels['labels'].values)))
labels.head()
训练数据的预处理准备停当,下面我们用 sklearn 划分一下数据集:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
print(X_train.shape, y_train.shape, X_test.shape, y_test.shape)
(2568, 224, 224, 3) (2568,) (1101, 224, 224, 3) (1101,)
训练集和测试集数据划分完毕,终于可以进入建模环节啦。
基于resnet50 的迁移学习模型
试验模型的基本策略就是使用预训练模型的权重作为特征提取器,将预训练的权重进行冻结,只训练全连接层。在正式构建模型前,先将数据进行标准化以及标签数据进行 onehot 处理。
X_train /= 255
X_test /= 255
y_train = np_utils.to_categorical(y_train, 5)
y_test = np_utils.to_categorical(y_test, 5)
构建模型如下:
def flower_model(X_train, y_train):
base_model = ResNet50(include_top=False, weights='imagenet', input_shape=(224, 224, 3))
for layers in base_model.layers:
layers.trainable = False
model = Flatten()(base_model.output)
model = Dense(128, activation='relu')(model)
model = Dropout(0.5)(model)
model = Dense(5, activation='softmax')(model)
model = Model(inputs=base_model.input, outputs=model)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(X_train, y_train, batch_size=128, epochs=50)
return model
模型训练和测试集评估:
model = flower_model(X_train, y_train)
model.evaluate(X_test, y_test, verbose=0)
训练过程:
限于计算资源,笔者的笔记本训练了一轮后就已热的发烫,果断停止了训练。大家若是有 gpu 计算资源的话可尝试将模型训练完。一两轮后模型的识别准确率就达到 70%,相信继续训练下去会有一个较高的识别率。(数据也可阅读原文下载)
参考资料:
https://cosx.org/2017/10/transfer-learning/
往期精彩:
一个数据科学从业者的学习历程