第三部分 用tensorflow建立image caption模型

前言:

  写这篇博客的目的是一开始想要使用Tensorflow建立一个image
caption模型,在一开始觉得不就是一个LSTM模型,因为之前有用Tensorflow实现过CNN结构,所以觉得并不会很难。但实际上,在这个过程中也遇到了不少困难。
按照我自己的学习顺序,这个系列的博客将分为三个部分,最终将构建一个基于Tensorflow的简单的image caption模型:
1. 系列的第一部分 <http://blog.csdn.net/u013548453/article/details/79177020>,搬运于另一个博主
<http://blog.csdn.net/jerr__y/article/details/61195257>
,加上了自己的理解做了一些扩展性的,主要是对语言模型有关的注释。这篇博客主要是为了了解一些LSTM一些基本的结构。
2. 系列的第二篇部分 <http://blog.csdn.net/u013548453/article/details/79181910>,同样搬运于
另一个博主 <https://gaussic.github.io/2017/08/24/tensorflow-language-model/>,这篇博客将
《tensorflow 实战》 <https://book.douban.com/subject/26974266/>
用Tensorflows实现的循环神经网络语言模型大大简化,增加了书上例子的可读性,在我学习的过程中给了我很多帮助。
3. 本篇是这个系列的第三部分,也是最后一部分,参考了上述两篇博客和cs231n Assignment3
<http://cs231n.github.io/assignments2016/assignment3/>
中的部分,最后算是整合成了一个符合自己预期的模型,也是自己收获最多的一部分。
4. 最后感谢所有我在学习过程中互联网上间接或直接提供帮助的朋友,世界因为你们而美好。无法一一罗列,所有参考资料会放在文章最后。

阅读建议:

由于这篇blog中大部分内容是根据cs231n Assignment3
<http://cs231n.github.io/assignments2016/assignment3/>中image
caption为基础修改的,可以理解为这篇作业中image
caption部分的一个tensorflow版的实现。使用的数据也是这个作业给的数据,所以建议阅读本篇先独自完成这个作业。
其次,由于我本身没有很好的一个tensorflow的基础,这是第一次用tf搭建RNN模型,所以代码结构可能有些混乱。
最后最重要的是,这个代码的训练损失跟作业中的训练损失相差很大,我检查了很多天没有查出为什么,所以目前只能先把代码放在这里,希望可以得到网友的帮助
,欢迎大家指出错误。
最后的最后,强烈推荐文章最后的参考资料,即使不看我的,也要看看最后的参考资料,再次感谢。

基本网络结构如图所示:

完整代码:
#读取数据 import os,json import h5py import tensorflow as tf import numpy as np
data = {} datafold ='~/coco_captioning' caption_file = '/coco2014_captions.h5'
with h5py.File(datafold + caption_file,'r') as f: n = 1 for k, v in f.items():
data[k] = np.asarray(v) dict_filename ='/coco2014_vocab.json' with open
(datafold + dict_filename,'r') as f: dict_data = json.load(f) for k, v in
dict_data.items(): data[k] = v train_feature_file = '/train2014_vgg16_fc7.h5'
with h5py.File(datafold + train_feature_file,'r') as f: data['train_features']
= np.asarray(f['features']) val_feature_file = '/val2014_vgg16_fc7.h5' with
h5py.File(datafold + val_feature_file,'r') as f: data['val_features'] =
np.asarray(f['features']) train_url_file = '/train2014_urls.txt' with open
(datafold + train_url_file,'r') as f: train_urls = np.asarray([line.strip() for
line in f]) data['train_urls'] = train_urls val_url_file = '/val2014_urls.txt'
with open(datafold + val_url_file, 'r') as f: val_urls = np.asarray([line
.strip()for line in f]) data['val_urls'] = val_urls #一个获取数据的函数 def
sample_coco_minibatch(data, batch_size =2,split = 'train'): split_size = data[
'%s_captions' % split].shape[0] #标注的总数 mask = np.random
.choice(split_size,batch_size) captions = data['%s_captions' % split][mask]
image_idxs = data['%s_image_idxs' % split][mask] image_features = data[
'%s_features' % split][image_idxs] urls = data['%s_urls' % split][image_idxs]
return captions,image_features,urls #获取输入输出的函数 def get_batch(data,batch_size = 2
,split = 'train'): captions,features,urls =
sample_coco_minibatch(data,batch_size,split) captions_in = captions[:,:-1]
captions_out = captions[:,1:] return captions_in, captions_out, features, urls
import tensorflowas tf import numpy as np #所需要的参数 '''不知道为什么多层的话现在会出错'''
num_layers =1 # rnn的层数, drop_out = 0.3 model = 'lstm'#可选lstm 和 gru learn_rate =
5e-3 iter_num = 300000 TRAIN_RATE_DELAY = 0.9 delay_steps = 500
#这两个参数数参考Assignment3给的参数指定的 hidden_dim = 512 word_vec_dim = 256 batch_size = 50
#下边一般不要改,是根据数据制定的 feature_dim = 4096 vocab_size = 1004 time_steps = 16 #要训练的变量
# feature - > hidden_layer Wprob =
tf.Variable(tf.truncated_normal([feature_dim, hidden_dim], stddev=0.1)) bprob =
tf.Variable(tf.truncated_normal([hidden_dim,], stddev=0.1)) # hidden_layer - >
out Wvec = tf.Variable(tf.truncated_normal([hidden_dim,vocab_size],stddev=0.1))
bvec = tf.Variable(tf.truncated_normal([vocab_size,],stddev=0.1)) #开始构造数据占位符 X
= tf.placeholder(tf.int32, [None, time_steps]) y_ = tf.placeholder(tf.int32,
[None, time_steps]) features = tf.placeholder(tf.float32,[None,feature_dim])
#用于单词转变词向量 with tf.device("/cpu:0"): embedding_ =
tf.Variable(tf.truncated_normal([vocab_size,word_vec_dim], stddev=0.1),
dtype=tf.float32)# print(X.get_shape().as_list()) #
print(embedding_.get_shape().as_list()) inputs_ =
tf.nn.embedding_lookup(embedding_, X)#(None,time_steps,word_vev_dim) ''
'rnn模型构建,每一步的意思见本文档第一部分''' def lstm_cell(): # 基本的lstm cell return
tf.contrib.rnn.BasicLSTMCell(hidden_dim, state_is_tuple=True) def gru_cell():#
gru cell,速度更快 return tf.contrib.rnn.GRUCell(hidden_dim) def dropout_cell(model):
# 在每个cell后添加dropout if (model == 'lstm'): cell = lstm_cell() else: cell =
gru_cell()return tf.contrib.rnn.DropoutWrapper(cell, output_keep_prob=
drop_out) cells = [dropout_cell(model)for _ in range(num_layers)] cell =
tf.contrib.rnn.MultiRNNCell(cells, state_is_tuple=True)# 多层rnn '''网络中间状态初始化'''
# #初始化中间状态 根据features [None,feature_dim] #初始化中间状态 根据features [None,feature_dim]
#看源码:c_state是记忆细胞,h_state就是当前状态, # new_c = (c * sigmoid(f + self._forget_bias)
+ sigmoid(i) * self._activation(j)) # new_h = self._activation(new_c) *
sigmoid(o) #也就是h初始状态应该为features #c初始化为零应该没什么问题
#BasicLSTMCell的源码,cell_state_应该零初始化 hidden_state_ = tf.matmul(features, Wprob)
+ bprob# [batch_size,hidden_dim] cell_state_ = tf.zeros([batch_size,
hidden_dim]) init_state = tf.contrib.rnn.LSTMStateTuple( cell_state_,
hidden_state_ ) L = list() L.append(init_state)#转换成tuple init_state_ = tuple(L)
X_lengths = tf.count_nonzero(X,1) outputs_ ,states_= tf.nn.dynamic_rnn(cell =
cell, inputs = inputs_ , time_major = False, sequence_length = X_lengths,
initial_state = init_state_#还没决定怎么写 ) #此时outputs 的 shape为 [batch_size,
time_steps, hidden_dim] 代表每一步的输出 #计算预测 pred_ = tf.matmul(tf.reshape(outputs_,
shape = [batch_size * time_steps, hidden_dim]),Wvec) pred = tf.reshape(pred_,
shape = [batch_size, time_steps, vocab_size]) + bvec#下面需要对y_进行one_hot编码
y_本来是(batch_size,time_steps), #
需要按照每一位的值进行one_hot编码,将其处理成(batch_size,time_steps,vocab_size) label_ =
tf.one_hot(y_ , vocab_size,#one_hot编码长度 axis=2 #one_hot的维度是第几维 ) '''损失'''
#下边就可以根据pred和label_计算损失了,他们维度都是(batch_size,time_steps,vocab_size) def
cost(output, target): output = tf.nn.softmax(output) cross_entropy = target *
tf.log(output) cross_entropy = -tf.reduce_sum(cross_entropy, 2) mask =
tf.cast(tf.sign(X),dtype = tf.float32) cross_entropy *= mask
print(cross_entropy.get_shape().as_list()) cross_entropy =
tf.reduce_sum(cross_entropy,1) cross_entropy /= tf.reduce_sum(mask, 1) return
tf.reduce_mean(cross_entropy) Loss = cost(pred,label_)"""设置优化器(adam)"""
global_step = tf.Variable(0, trainable=False) learn_rate_ =
tf.train.exponential_decay(learn_rate,global_step, delay_steps,
TRAIN_RATE_DELAY,staircase=True) optimizer =
tf.train.AdamOptimizer(learning_rate = learn_rate_) optim =
optimizer.minimize(Loss,global_step = global_step) print(type(optim))"""计算正确率"""
preds_ = tf.reshape(pred,shape = [batch_size*time_steps, vocab_size]) preds =
tf.argmax(preds_,1) preds = tf.reshape(preds,shape = [batch_size,time_steps])
acc = tf.equal(y_, tf.cast(preds,tf.int32)) acc_rate =
tf.reduce_mean(tf.cast(acc, tf.float32))'''开始训练''' #运行 if __name__ == '__main__'
:# 创建session,初始化变量 sess = tf.Session()
sess.run(tf.global_variables_initializer()) print('Start training...') import
tqdmfor i in tqdm.tqdm(range(iter_num+1)): # 迭代轮次 x_batch, y_batch,
features_batch, urls = get_batch(data,batch_size) feed_dict = {X: x_batch, y_:
y_batch, features : features_batch } sess.run(optim, feed_dict=feed_dict)if i %
100 == 0: loss = sess.run(Loss, feed_dict=feed_dict) print('第%d步的损失是%.5e' %
(i,loss)) Acc = sess.run(acc_rate, feed_dict=feed_dict) print('第%d步的准确率是%.5f' %
(i,Acc)) print('第%d步的学习率是%.5e\n' %
(i,sess.run(learn_rate_,feed_dict=feed_dict))) print('Finish training...') sess.
close()
参考资料:

* Tensorflow基础知识—损失函数详解
<https://sthsf.github.io/wiki/Algorithm/DeepLearning/Tensorflow%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/Tensorflow%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86---%E6%8D%9F%E5%A4%B1%E5%87%BD%E6%95%B0%E8%AF%A6%E8%A7%A3.html>
* tf.one_hot()的使用 <http://blog.csdn.net/m0_37561765/article/details/78207508>
* tensor 和 numpy互转
<http://blog.csdn.net/accumulate_zhang/article/details/78867890>
* tf.cast(x, dtype, name=None) 类型转换函数
* LSTM中间状态的自定义初始化 <https://www.jianshu.com/p/ada1449bb13f>
* tf.nn.dynamic_rnn <http://blog.csdn.net/u010223750/article/details/71079036>
讨论了初始化状态、变长输入、padding、mask等问题
* tf.nn.dynamic_rnn
<https://stackoverflow.com/questions/44395715/how-tf-nn-dynamic-rnn-works-with-different-lengths-of-inputs-sepcially-when-work>
的使用
* 这是篇英语blog,质量很高
<https://r2rt.com/non-zero-initial-states-for-recurrent-neural-networks.html>
,讨论了非零初始化的解决办法,然而我并没有使用
* 信息量、熵、相对熵、交叉熵 <http://blog.csdn.net/rtygbwwwerr/article/details/50778098>
,知乎上有一个想问题也很好
* 从香农熵到手推KL散度:一文带你纵览机器学习中的信息论
<http://blog.csdn.net/FnqTyr45/article/details/79067865>
* tensorflow接口研读math_ops(三) <https://www.jianshu.com/p/7fbce28e85a4>
* tf.contrib.rnn.BasicLSTMCell, tf.contrib.rnn.MultiRNNCell深度(源码)解析
<http://blog.csdn.net/john_xyz/article/details/70548140>看完这篇博客,会对RNN结构了解更加深一点
* TensorFlow中RNN实现的正确打开方式 <https://zhuanlan.zhihu.com/p/28196873>知乎这篇专题质量特别高
* Variable Sequence Lengths in TensorFlow
<https://danijar.com/variable-sequence-lengths-in-tensorflow/>
* tensorflow源码分析——BasicLSTMCell <https://www.cnblogs.com/yuetz/p/6563377.html>
* tensorflow中有关张量转换的API <https://www.jianshu.com/p/00ab12bc357c>
* tensorflow有关常量张量,序列操作,随机数张量的API <https://www.jianshu.com/p/d05ded5678b0>