欢迎来到《看图说话实战教程》系列第二节。在这一节中,我们要为看图说话模型准备所需的图片及文本数据集。
准备图片数据
图片是看图说话模型训练的两个输入之一。任何神经网络模型的输入必须以向量的形式才能喂给模型。因此,需要采用某种方法把每张图片转换成一个固定大小向量,然后输入到神经网络中。
在这篇教程中,我们使用一个预训练好的模型来抽取图像中的信息,而不是让模型自己从头开始训练。这是迁移学习 (Transfer Learning) 的一种实现方式,将已有的知识迁移到另一个模型当中。这样做的一个好处是我们无需耗费大量资源重新学习图片知识,就能直接利用已有的知识来进行图像信息的抽取与理解。而且,这些模型一般是在大规模的图像数据集(如ImageNet)上进行训练的,本身已经包含了大量的图像知识。这里,我们使用赢得2014年ImageNet竞赛冠军的VGG16模型,详细的模型信息可以阅读原始论文《Very Deep Convolutional Networks for Large-Scale Visual Recognition》。也有其他的一些流行的模型可供选择,如InceptionV3、ResNet等,关于如何使用这些模型可以参见Keras的官方文档。
VGG16的模型结构如下图所示:
在Keras中使用VGG16模型代码如下:
# 加载VGG16模型
model = VGG16()
# 移除最后一层
model.layers.pop()
model = Model(inputs=model.inputs, outputs=model.layers[-1].output)
VGG16模型是训练在ImageNet数据集上的图像分类模型,在这篇教程中,我们的目的并不是进行图像分类,而是通过VGG16模型获取固定长度的包含图像特征信息的向量。所以,我们需要将其最后的softmax层移除,抽取的图片特征向量长度为4096维。
Keras框架已经提供了训练好的VGG16模型权重文件,大家可以直接拿来使用。如果是第一次使用这个模型,Keras框架会自动下载对应的模型文件,大小约为500MB。下载快慢取决于电脑网速。如果还是无法下载成功,大家可以直接打开此链接下载。下载好的文件保存在对应的目录下。
- Mac或Linux系统: ~/.keras/models
- Windows系统:C:\Users\你的用户名.keras\models
现在,我们把每张图片喂给这个模型,得到相应的4096维的特征向量,代码如下:
# 加载图片
image = load_img(filename, target_size=(224, 224))
# 转换图像像素值为NumPy数组类型
image = img_to_array(image)
# 扩展数据维度 [height, width, channels]->[1, height, width, channels]
image = np.expend_dims(image, axis=0)
# 将图片数处理成可被VGG16模型接受的格式
image = preprocess_input(image)
# 执行预测获取图像特征
feature = model.predict(image, verbose=0)
获得每张图片的特征向量后,我们需要将所有图片及对应的特征向量以字典的形式保存到文件中。当需要图像特征时,再加载这些特征并喂给我们的模型。其实这跟直接将VGG16模型集成到我们的模型中没有任何区别。这样做的好处在于可以减少模型的内存占用及加快模型的训练速度。
用VGG16模型提取所有图片的特征,完整的代码如下:
import os
import pickle
from keras.applications.vgg16 import VGG16, preprocess_input
from keras.preprocessing.image import load_img, img_to_array
# 定义一个指定目录下所有图片的特征抽取函数
def extract_features(directory):
# 加载VGG16模型
model = VGG16(weights='imagenet', include_top=False)
# 打印模型的结构概要信息
print(model.summary())
# 定义一个字典类型的变量存储每张图片对应的特征表示
features = dict()
# 列出指定目录下所有的图片文件
files = listdir(directory)
# 遍历所有的图片文件,通过进度条来显示跟踪处理进度
for i in tqdm(range(len(files)), total=len(files)):
# 加载图片
filename = os.path.join(directory, files[i])
image = load_img(filename, target_size=(224