看图说话实战教程 | 第二节 | 图像及文本数据预处理

标签: 实战教程

欢迎来到《看图说话实战教程》系列第二节。在这一节中,我们要为看图说话模型准备所需的图片及文本数据集。

准备图片数据

图片是看图说话模型训练的两个输入之一。任何神经网络模型的输入必须以向量的形式才能喂给模型。因此,需要采用某种方法把每张图片转换成一个固定大小向量,然后输入到神经网络中。

在这篇教程中,我们使用一个预训练好的模型来抽取图像中的信息,而不是让模型自己从头开始训练。这是迁移学习 (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, 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)
    # 获取图片唯一标识
    image_id = files[i].split('.')[0]
    # 存储抽取的特征到字典中
    features[image_id] = feature
  return features
  
# call the extract_features function
directory = 'Flickr8k_Dataset'
features = extract_features(directory)
# 保存到文件中
pickle.dump(features, open("features.pkl", "wb"))

这个过程可能会花费大约一到两个小时,时间长短主要取决于你的工作机的性能。

准备文本数据

Flickr8K数据集中包含了对应图像的描述性文本,为了能够让我们的模型能够学习生成这样的一句话,我们需要对文本做一些处理。

1. 加载文件

Flickr8k.token.txt文件中包含了每张图片的唯一标识符及其对应的5条文字描述,如下:

1000268201_693b08cb0e.jpg#0	A child in a pink dress is climbing up a set of stairs in an entry way .
1000268201_693b08cb0e.jpg#1	A girl going into a wooden building .
1000268201_693b08cb0e.jpg#2	A little girl climbing into a wooden playhouse .
1000268201_693b08cb0e.jpg#3	A little girl climbing the stairs to her playhouse .
1000268201_693b08cb0e.jpg#4	A little girl in a pink dress going into a wooden cabin .
1001773457_577c3a7d70.jpg#0	A black dog and a spotted dog are fighting
1001773457_577c3a7d70.jpg#1	A black dog and a tri-colored dog playing with each other on the road .
1001773457_577c3a7d70.jpg#2	A black dog and a white dog with brown spots are staring at each other in the street .
1001773457_577c3a7d70.jpg#3	Two dogs of different breeds looking at each other on the road .
1001773457_577c3a7d70.jpg#4	Two dogs on pavement moving toward each other .

文件中每一行包含了图片名称、描述序号及描述文本,格式为 <image name>#i <caption>,0i40\leq i\leq4

首先,我们定义一个加载所有文字描述的函数,代码如下:

# 加载所有的文字描述到内存
def load_doc(filename):
  # 以只读方式打开文件
  file = open(filename, 'r')
  # 读取所有文本
  text = file.read()
  # 关闭该文件
  file.close()
  return text

filename = 'Flick8k_text/Flickr8k.token.txt'
# 加载所有的文字描述
doc = load_doc(filename)

接下来,我们一步步遍历文件的每一行,并创建一个字典保存每张图片ID及其对应的文字描述列表,把文字描述与图片关联起来:

# 抽取每个图像对应的描述文件,并保存在字典中
def load_descriptions(doc):
  mapping = dict()
  # 遍历处理文件的每一行
  for line in doc.split('\n'):
    # 以空格拆分行,过滤掉字符数量小于2的行
    tokens = line.split()
    if len(line) < 2:
      continue
    # 取第一个token作为图片id,其余的作为该图片的文字描述
    image_id, image_desc = tokens[0], tokens[1:]
    image_id = image_id.split('.')[0]
    # 将拆分后的文字描述的字符列表拼接成正常的字符串
    image_desc = ' '.join(image_desc)
    # 如果图像有多个文字描述,创建列表类型
    if image_id not in mapping:
      mapping[image_id] = list()
    # 存储文字描述
    mapping[image_id].append(image_desc)
 return mapping

# 解析文字描述
descriptions = load_descriptions(doc)
print('Loaded: %d ' % len(descriptions))

字典变量descriptions的结果如下(部分):

descriptions['101654506_8eb26cfb60'] = ['A brown and white dog is running through the snow .', 'A dog is running in the snow', 'A dog running through snow .', 'a white and brown dog is running through a snow covered field .', 'The white and brown dog is running over the surface of the snow .']

执行结果为:

Loaded: 8,092

2. 数据清洗

接下来,我们需要对文字描述做一些清理工作,以减少字典的大小。处理方式包括:

  1. 将字母全部变成小写形式,如 (将’Hello’变成’hello’)
  2. 移除所有的标点符号、特殊字符,如 (’%’、’$’、’#'等)
  3. 移除只有一个字母组成的单词或字符
  4. 移除所有的数字及包含数字的单词,如 ('hey199’等)

数据清洗代码如下:

import string

# 定义一个函数来对文字描述做处理
def clean_descriptions(descriptions):
  # 标点符号转换表
  table = str.maketrans('', '', string.punctuation)
  for key, desc_list in descriptions.items():
    for i in range(len(desc_list)):
      desc = desc_list[i]
      desc = desc.split()
      # 转换成小写字母
      desc = [word.lower() for word in desc]
      # 移除标点字符
      desc = [w.translate(table) for w in desc]
      # 移除单个字母的单词
      desc = [word for word in desc if len(word) > 1]
      # 移除数字
      desc = [word for word in desc if word.isalpha()]
      # 存储为字符串
      desc_list[i] = ' '.join(desc)
  # 调用函数
  clean_descriptions(descriptions)

3. 生成词汇表

一旦清理完毕,我们就可以计算语料库中词汇表的大小。词汇表是训练深度学习模型必须的重要文件,其重要性等价于小时候学习汉字必备的新华字典。词汇表大小描绘了模型已经掌握的词汇量。它包含了在经过处理后语料库中的所有唯一的单词。理想状态下,词汇表既要有意义,又要大小适中。一般情况下,词汇表由常用的单词组成。如果词汇表文件包含了大量的不常用的单词,一方面会导致模型训练速度下降,另一方面增大模型需要学习的参数量。

# 将文字描述转换成单词组成的词汇表集合
def to_vocabulary(descriptions):
  all_desc = set()
  for key in descriptions.keys():
    [all_desc.update(d.split()) for d in descriptions[key]]
  return all_desc

# 调用函数
vocabulary = to_vocabulary(descriptions)
print('Vocabulary Size: %d ' % len(vocabulary))

输出结果如下:

Vocabulary Size: 8,763

4. 保存文字描述

最后,我们需要把字典descriptions保存为文件。

def save_descriptions(descriptions, filename):
  lines = list()
  for key, desc_list in descriptions.items():
    for desc in desc_list:
      lines.append(key + ' ' + desc)
    data = '\n'.join(lines)
    file = open(filename, 'w')
    file.write(data)
    file.close()

# 调用函数
save_descriptions(descriptions, 'descriptions.txt')

完整代码

这篇教程第一讲完整的代码如下:

# 加载所有的文字描述到内存
def load_doc(filename):
  # 以只读方式打开文件
  file = open(filename, 'r')
  # 读取所有文本
  text = file.read()
  # 关闭该文件
  file.close()
  return text

# 抽取每个图像对应的描述文件,并保存在字典中
def load_descriptions(doc):
  mapping = dict()
  # 遍历处理文件的每一行
  for line in doc.split('\n'):
    # 以空格拆分行,过滤掉字符数量小于2的行
    tokens = line.split()
    if len(line) < 2:
      continue
    # 取第一个token作为图片id,其余的作为该图片的文字描述
    image_id, image_desc = tokens[0], tokens[1:]
    image_id = image_id.split('.')[0]
    # 将拆分后的文字描述的字符列表拼接成正常的字符串
    image_desc = ' '.join(image_desc)
    # 如果图像有多个文字描述,创建列表类型
    if image_id not in mapping:
      mapping[image_id] = list()
    # 存储文字描述
    mapping[image_id].append(image_desc)
 return mapping

# 将文字描述转换成单词组成的词汇表集合
def to_vocabulary(descriptions):
  all_desc = set()
  for key in descriptions.keys():
    [all_desc.update(d.split()) for d in descriptions[key]]
  return all_desc

def save_descriptions(descriptions, filename):
  lines = list()
  for key, desc_list in descriptions.items():
    for desc in desc_list:
      lines.append(key + ' ' + desc)
    data = '\n'.join(lines)
    file = open(filename, 'w')
    file.write(data)
    file.close()

filename = 'Flick8k_text/Flickr8k.token.txt'
# 加载所有的文字描述
doc = load_doc(filename)
# 解析文字描述
descriptions = load_descriptions(doc)
print('Loaded: %d ' % len(descriptions))
# 调用函数
vocabulary = to_vocabulary(descriptions)
print('Vocabulary Size: %d ' % len(vocabulary))
# 调用函数
save_descriptions(descriptions, 'descriptions.txt')

感谢您的阅读,敬请期待下一讲!希望您能这篇教程中受益匪浅!


想要了解更多的自然语言处理最新进展、技术干货及学习教程,欢迎关注微信公众号“语言智能技术笔记簿”或扫描二维码添加关注。
在这里插入图片描述

版权声明:本文为jlqCloud原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/jlqCloud/article/details/105104886

智能推荐

CentOS学习之路1-wget下载安装配置

参考1: https://blog.csdn.net/zhaoyanjun6/article/details/79108129 参考2: http://www.souvc.com/?p=1569 CentOS学习之路1-wget下载安装配置 1.wget的安装与基本使用 安装wget yum 安装软件 默认安装保存在/var/cache/yum ,用于所有用户使用。 帮助命令 基本用法 例子:下载...

深入浅出Spring的IOC容器,对Spring的IOC容器源码进行深入理解

文章目录 DispatcherServlet整体继承图 入口:DispatcherServlet.init() HttpServletBean.init() FrameworkServlet.initServletBean() 首先大家,去看Spring的源码入口,第一个就是DispatcherServlet DispatcherServlet整体继承图 入口:DispatcherServlet....

laravel框架的课堂知识点概总

1. MVC 1.1 概念理解 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑 MVC 是一种使用 MVC(Model View Controller ...

Unity人物角色动画系统学习总结

使用动画系统控制人物行走、转向、翻墙、滑行、拾取木头 混合树用来混合多个动画 MatchTarget用来匹配翻墙贴合墙上的某一点,人物以此为支点翻墙跳跃 IK动画类似于MatchTarget,控制两只手上的两个点来指定手的旋转和位置,使得拾取木头时更逼真 创建AnimatorController: 首先创建一个混合树,然后双击 可以看到该混合树有五种状态机,分别是Idle、WalkForward、...

Composer 安装 ThinkPHP6 问题

Composer 安装 ThinkPHP6 问题 先说说问题 一.运行环境要求 二.配置 参考: ThinkPHP6.0完全开发手册 先说说问题 执行ThinkPHP6的安装命令 遇到问题汇总如下: 看提示是要更新版本,执行命令更新。 更新之后,再次安装ThinkPHP,之后遇到如下问题。 尝试了很多方法,依然不能解决。其中包括使用https://packagist.phpcomposer.com...

猜你喜欢

Spring Boot 整合JDBC

今天主要讲解一下SpringBoot如何整合JDBC,没啥理论好说的,直接上代码,看项目整体结构 看一下对应的pom.xml 定义User.java 定义数据源配置,这里使用druid,所以需要写一个配置类 上面指定druid的属性配置,和用户登录的账号信息以及对应的过滤规则: 下面定义数据访问接口和对应的实现: 数据访问层很简单,直接注入JdbcTemplate模板即可,下面再看对应的servi...

html鼠标悬停显示样式

1.显示小手:     在style中添加cursor:pointer 实现鼠标悬停变成小手样式     实例:         其他参数: cursor语法: cursor : auto | crosshair | default | hand | move | help | wait | tex...

Yupoo(又拍网)的系统架构

Yupoo!(又拍网) 是目前国内最大的图片服务提供商,整个网站构建于大量的开源软件之上。以下为其使用到的开源软件信息: 操作系统:CentOS、MacOSX、Ubuntu 服务器:Apache、Nginx、Squid 数据库:MySQLmochiweb、MySQLdb 服务器监控:Cacti、Nagios、 开发语言:PHP、Python、Erlang、Java、Lua 分布式计算:Hadoop...

创建一个Servlet项目流程(入门)

版本 IDEA 2020.2 JDK1.8 apache-tomcat-9.0.36 项目流程 一、IDEA中新建JaveEE项目 项目起名,选择项目存放地址,点击finish创建成功 进入项目后,右键选择项目,选择add Framework Support 选择Web Application,点击OK 此时项目文件夹 在WEB-INF下创建两个目录classes和lib 按ctrl+alt+sh...

Docker部署SpringCloud ELK+RabbitMQ日志

Docker部署SpringCloud ELK+RabbitMQ日志  Im_Coder 原文:https://www.jianshu.com/p/f773f23096a9 一、效果图 image.png 二、ELK是什么? ELK由ElasticSearch、Logstash和Kiabana三个开源工具组成。 其中Elasticsearch是个开源分布式搜索引擎,它的特点有:分布式,索...