目录
TensorFlow官方show and tell代码添加coco-caption描述评估
1 背景
TensorFlow 有show and tell的官方实现,位于models/research/im2txt
中,代码下载地址为:https://github.com/tensorflow/models。
coco-caption可以用于计算图像描述使用的BLEU、METEOR、ROUGE-L、CIDEr、SPICE等指标,代码下载地址为:https://github.com/tylin/coco-caption。
coco-caption项目readme中有相应的使用介绍和示例,环境需求java 1.8.0和python 2.7。示例给出的使用方法是将把测试集中图像生成的描述caption和每个图像对应的id号组织成如下格式的json文件:
[
{"image_id": 173598, "caption": "a group of people on a field playing baseball ."},
{"image_id": 389490, "caption": "a baseball player swinging a bat at a ball"},
{"image_id": 124232, "caption": "a garden filled with lots of green plants ."},
...
]
然后照着示例代码改改待测试描述的json文件路径,其中evaluate()
函数计算待测试描述json文件中的描述和MS COCO测试集参考描述json文件中的描述相比较得到的指标。MS COCO参考描述json文件位于coco-caption/annotations/captions_val2014.json
。
因此进行评估的重点是:收集模型生成的待测试图像的描述,与图像对应的id一起存储为所要求的json文件
但是在im2txt中,并没有coco-caption评估部分,以及保存模型生成的图像描述的相关代码。
2 im2txt代码修改
im2txt项目readme
文件给出了项目的运行指导,给的指导过程是利用Bazel(类似与Makefile)对代码进行编译,我感觉应该就是把相关联的代码文件整合在一块,自动生成相应的可执行文件。
im2txt将数据集预处理成了TFRecord的格式,所以其实也可以直接从生成的TFRecord文件里获取测试集图像的id和文件名,但是相应的run_inference.py
文件的修改会更多(一个是如何从TFRecord
中获取相关信息;其次run_inference
测试时读取的是单个图像,改为TFRecord一次传入一批数据,得改到模型构建里面,后面继续看),而且(我也还不清楚TFRecord具体是个啥形式!— . — !!!)
于是,我就把Bazel生成的文件夹都删除了,直接运行项目目录下im2txt路径下的python文件也可以(train.py
、run_inference.py
和evaluate.py
,但是evaluate.py
的作用不是很懂),同时需要一下两点改动:
- 为im2txt路径下的
/ops
和/inference_utils
下添加__init__.py
文件(可以是空文件,目的是为了在其他文件中import这些目录下python文件时的识别)。 - 同时需要修改python文件中对于项目自身的一些python文件的引用格式。
例如configuration.py
和show_and_tell_model.py
与train.py
位于同一路径下,因此在train.py
中修改如下(其余几个文件的修改类似):
"""Train the model."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import tensorflow as tf
#from im2txt import configuration
#from im2txt import show_and_tell_model
import configuration
import show_and_tell_model
为了生成图像描述,需要对模型进行训练,这里用了两块K80,闲着没事训练了大概5、6天?应该只训练了80多万步:
python train.py
--input_file_pattern="data/mscoco/train-?????-of-00256" \
--inception_checkpoint_file="data/pretrained/inception_v3.ckpt" \
--train_dir="data/model/train" \
--train_inception=false \
--number_of_steps=1000000
2.1 获取测试集图像路径
im2txt在val2014基础上划分测试集,为了利用模型生成测试集所有图像的描述,需要预先获取测试集所有图像的路径。在MS COCO数据集的caption_val2014.json
文件下,包含了val所有图像的id、文件名,我们需要从中提取出划分出来的测试集所有图像的id和文件名(文件名用于构成所在路径)。
代码如下:
import json
import tensorflow as tf
import numpy as np
import os.path
# MS COCO ecaluation captions
captions_file = './data/mscoco/raw-data/annotations/captions_val2014.json'
with tf.gfile.FastGFile(captions_file, 'r') as f:
caption_data = json.load(f)
# get the id and file name
id_to_filename = [{"id": x["id"], "file_path": x["file_name"]} for x in caption_data['images']]
# add the full path
for msg in id_to_filename:
msg['file_path'] = os.path.join('data/mscoco/raw-data/val2014', msg['file_path'])
print(msg['id'],msg['file_path'])
# split the final test set
val_len = len(id_to_filename)
print(val_len)
cut = int(0.9*val_len)
final_test = id_to_filename[cut:]
print(len(final_test))
with tf.gfile.FastGFile('./final_test.json', 'w') as f:
json.dump(final_test, f, ensure_ascii=False)
2.2 获取并存储测试集所有图像的描述
对im2txt/run_inference.py
中的main
函数进行修改,本来的main
函数可用来测试单张或多张图像,输出模型生成的前三个图像描述。
我将其读入单张图像或多张图像路径的参数修改为读取上一步中生成的final_test.json
,其中包含测试集所有图像的路径信息。遍历所有图像的路径,读取图像并利用模型生成图像描述,将第一个描述联合图像对应的id号保存到test_caption.json
文件中。修改后的main
函数代码如下:
def main(_):
# Build the inference graph.
g = tf.Graph()
with g.as_default():
model = inference_wrapper.InferenceWrapper()
restore_fn = model.build_graph_from_config(configuration.ModelConfig(),
FLAGS.checkpoint_path)
g.finalize()
# Create the vocabulary.
vocab = vocabulary.Vocabulary(FLAGS.vocab_file)
filenames = []
# by Curya
# get image path from final_test.json
with tf.gfile.FastGFile(FLAGS.input_files, 'r') as f:
final_names = json.load(f)
filenames = [x for x in final_names]
test_captions = []
#for file_pattern in FLAGS.input_files.split(","):
# filenames.extend(tf.gfile.Glob(file_pattern))
#tf.logging.info("Running caption generation on %d files matching %s",
# len(filenames), FLAGS.input_files)
with tf.Session(graph=g) as sess:
# Load the model from checkpoint.
restore_fn(sess)
# Prepare the caption generator. Here we are implicitly using the default
# beam search parameters. See caption_generator.py for a description of the
# available beam search parameters.
generator = caption_generator.CaptionGenerator(model, vocab)
print('begin caption generating!')
index = 0
for filename in filenames:
with tf.gfile.GFile(filename['file_path'], "rb") as f:
image = f.read()
captions = generator.beam_search(sess, image)
index += 1
print("Captions for image %s:%s" % (os.path.basename(filename['file_path']),index))
for i, caption in enumerate(captions):
# Ignore begin and end words.
sentence = [vocab.id_to_word(w) for w in caption.sentence[1:-1]]
sentence = " ".join(sentence)
#print(" %d) %s (p=%f)" % (i, sentence, math.exp(caption.logprob)))
if i==0:
test_captions.append({"image_id":filename['id'], "caption":sentence})
with tf.gfile.FastGFile('./test_caption.json', 'w') as f:
json.dump(test_captions, f, ensure_ascii=False)
print('captions generating over!')
2.3 利用coco-caption计算图像描述评价指标
将coco-caption/cocoEvalCapDemo.ipynb
中的待评价json文件路径修改为上一步生成的test_caption.json
的路径,计算得到的评价指标如下:
CIDEr: 0.908
Bleu_4: 0.289
Bleu_3: 0.385
Bleu_2: 0.520
Bleu_1: 0.694
ROUGE_L: 0.517
METEOR: 0.244
SPICE: 0.169
示例代码还提供展示CIDEr得分偏低的图像描述以及CIDEr得分的分布情况,如下:
3 其他
im2tx代码中对于文件的读取采用tf.gfile.FastGFile(file_path, mode)
或者tf.gfile.GFile(file_path, mode)
。功能类似与open(file_path, mode)
函数。
比较特别的是,对于图像的读取也采用以上两个函数,例如:
image_path = '***'
with tf.gfile.FastGFile(image_path, 'r') as f:
decode_image = f.read()
print(type(decode_image))
print(len(decode_image))
输出:
<type 'str'>
276257
代码使用read()
对图像进行读取,会把图像所有数据读取为一长串的string
格式,如果要将这些数据送入模型,还需要将其还原成三通道的图像数据,直接调用tf.image.decode_jpeg()
函数即可(图像是JPEG格式):
image = tf.image.decode_jpeg(decode_image, channels=3)
image = tf.image.convert_image_dtype(image, dtype=tf.float32)
with tf.Session() as sess:
image_np = image.eval()
print(image_np.shape)
plt.imshow(image_np)
plt.show()
输出:
(640, 480, 3)