现在我们已经有了图片的表示了,还需要我们的模型能学会把这些表示解码成能被理解的字幕。因为文字天生的序列特性,我们会利用一个RNN/LSTM网络里的循环特点。这些网络被训练来预测在给定的一系列前置词汇和图片表示的情况下的下一个词。 长短期记忆(LSTM)单元能让这个模型能更好地选择什么样的信息可以用于字幕词汇序列,atv直播,什么需要记忆,什么信息需要忘掉。TensorFlow提供了一个封装的功能可以对于给定的输入和输出维度生成一个LSTM层。 为了把词汇能变化成适合LSTM的固定长度表示的输入,我们使用一个向量层来把词汇映射成256维的特征(也叫词向量)。词向量帮助我们把词汇表示成向量,同时相似的词向量在语义上也相似。想了解更多关于词向量如何获取不同词汇之间的关系,请看这篇《用深度学习来获取文本语义》。 在这个VGG-16图片分类器里,卷积层抽取出了4096维表示,然后送入最后的softmax层来做分类。因为LSTM单元需要的是256维的文本输入,我们需要把图片表示转化成目标字幕所需的这种表示。为了实现这个目标,我们需要加入另外一个向量层来学习把4096维的图片特征映射成256维的文本特征空间。 构建和训练这个模型 全合在一起,Show and Tell模型就大概像这个样子:
图3. 来源《Show and Tell:2015 MSCOCO图片字幕大赛所获得的经验教训》 图3中,{s0, s1, …, sN}表示我们试着去预测的字幕词汇,{wes0, wes1, …, wesN-1}是每个词的词向量。LSTM的输出{p1, p2, …, pN}是这个模型产生的句子里下一个词的概率分布。模型的训练目标是最小化对所有的词概率取对数后求和的负值。 def build_model(self): # declaring the placeholders for our extracted image feature vectors, our caption, and our mask # (describes how long our caption is with an array of 0/1 values of length `maxlen` img = tf.placeholder(tf.float32, [self.batch_size, self.dim_in]) caption_placeholder = tf.placeholder(tf.int32, [self.batch_size, self.n_lstm_steps]) mask = tf.placeholder(tf.float32, [self.batch_size, self.n_lstm_steps]) # getting an initial LSTM embedding from our image_imbedding image_embedding = tf.matmul(img, self.img_embedding) + self.img_embedding_bias # setting initial state of our LSTM state = self.lstm.zero_state(self.batch_size, dtype=tf.float32) total_ loss = 0.0 with tf.variable_scope(“RNN”): for i in range(self.n_lstm_steps): if i > 0: #if this isn’t the first iteration of our LSTM we need to get the word_embedding corresponding # to the (i-1)th word in our caption with tf.device(“/cpu:0”): current_embedding = tf.nn.embedding_lookup(self.word_embedding, caption_placeholder[:,i-1]) + self.embedding_bias else: #if this is the first iteration of our LSTM we utilize the embedded image as our input current_embedding = image_embedding if i > 0: # allows us to reuse the LSTM tensor variable on each iteration tf.get_variable_scope().reuse_variables() out, state = self.lstm(current_embedding, state) print (out,self.word_encoding,self.word_encoding_bias) if i > 0: #get the one-hot representation of the next word in our caption labels = tf.expand_dims(caption_placeholder[:, i], 1) ix_range=tf.range(0, self.batch_size, 1) ixs = tf.expand_dims(ix_range, 1) concat = tf.concat([ixs, labels],1) onehot = tf.sparse_to_dense( concat, tf.stack([self.batch_size, self.n_words]), 1.0, 0.0) #perform a softmax classification to generate the next word in the caption logit = tf.matmul(out, self.word_encoding) + self.word_encoding_bias xentropy = tf.nn.softmax_cross_entropy_with_logits(logits=logit, labels=onehot) xentropy = xentropy * mask[:,i] loss = tf.reduce_sum(xentropy) total_loss += loss total_loss = total_loss / tf.reduce_sum(mask[:,1:]) return total_loss, img, caption_placeholder, mask 使用推断来生成字幕 完成训练后,我们就获得了一个在给定图片和所有的前置词汇的前提下,可以给出字幕里下一个词概率的模型。那么我们怎么用这个模型来生成字幕? 最简单的方法就是把一张图片作为输入,循环输出下一个概率最大的词,由此生成一个字幕。 def build_generator(self, maxlen, batchsize=1): #same setup as `build_model` function img = tf.placeholder(tf.float32, [self.batch_size, self.dim_in]) image_embedding = tf.matmul(img, self.img_embedding) + self.img_embedding_bias state = self.lstm.zero_state(batchsize,dtype=tf.float32) #declare list to hold the words of our generated captions all_words = [] print (state,image_embedding,img) with tf.variable_scope(“RNN”): # in the first iteration we have no previous word, so we directly pass in the image embedding # and set the `previous_word` to the embedding of the start token ([0]) for the future iterations output, state = self.lstm(image_embedding, state) previous_word = tf.nn.embedding_lookup(self.word_embedding, [0]) + self.embedding_bias for i in range(maxlen): tf.get_variable_scope().reuse_variables() out, state = self.lstm(previous_word, state) # get a one-hot word encoding from the output of the LSTM logit = tf.matmul(out, self.word_encoding) + self.word_encoding_bias best_word = tf.argmax(logit, 1) with tf.device(“/cpu:0”): # get the embedding of the best_word to use as input to the next iteration of our LSTM previous_word = tf.nn.embedding_lookup(self.word_embedding, best_word) previous_word += self.embedding_bias all_words.append(best_word) return img, all_words 很多情况下,这个方法都能用。但是“贪婪”地使用下一个概率最大的词可能并不能产生总体上最合适的字幕。 (责任编辑:本港台直播) |