简介
s2vt_captioner.py是使用之前训练出来的s2vt model进行caption的脚本。整体逻辑跟训练的时候差不多,但还是有一些区别,比如
- decode时候LSTM2的x不再是gt,而是上一个unit生成的单词
- 使用了python的接口调用caffe
下面按照脚本的核心函数调用链来展开。
核心函数调用链
- main: 划分为vid chunks
- run_pred_iters: 划分为一个个vid
- encode_video_frames: 使用vid的feats进行encode
- run_pred_iter: decode一个vid,返回captions(有可能多个)
- predict_image_caption: 根据caption策略选择对应的caption方式
- predict_image_caption_beam_search: 使用beam search的方式进行caption
- predict_single_word: 生成下一个单词
- predict_image_caption_beam_search: 使用beam search的方式进行caption
- predict_image_caption: 根据caption策略选择对应的caption方式
- run_pred_iters: 划分为一个个vid
main
将任务划分为一个个chunk,交给run_pred_iters完成任务
|
|
- video_gt_pairs: dict{vid: list[gt_sents]}
- 假若没有给gt的话,list[gt_sents]为空
|
|
- outputs: dict{vid: output_batch}
- 每个vid有可能有多于一个caption,所以是output_batch
- output_batch: list[dict{caption, prob, gt, source}]
- caption: 预测的句子,list[word1, word2, word3, …]
- prob: 句子各单词发生的概率,list[prob_word1, prob_word2, prob_word3, …]
- source: 搜索句子的策略 e.g. sample / beam
|
|
- text_output_types: dict{source_type: list[output string]}
- source_type: 搜索句子的策略 e.g. sample / beam
run_pred_iters
将chunk进一步划分为一个个vid,交给run_pre_iter完成任务
|
|
video_features: list[1 * FEAT_DIM]
|
|
caption的流程:
- 将frame_feats encode到LSTM
- 从encode好的LSTM中decode出caption
encode_video_frames
|
|
将数据格式匹配输入,将frame_feats依次传入LSTM,并设置input_sentence为0
run_pred_iter
|
|
对每种strategy都调用predict_image_caption来进行caption
predict_image_caption
根据strategy选择对应的image_caption函数进行caption
predict_image_caption_beam_search
使用beam_search的方式生成句子。所谓的beam_search实际上为启发式的BFS,使用了贪心的思想:即每次搜索完下一层后,只在下一层中取beam_size个结点进一步展开,而其余的结点则不要
|
|
了解了数据结构后,先看内层循环
|
|
将每条beam生成的单词中,概率最高的beam_size个单词保存到expansions中。内层循环结束后,expansions中含有len(beams) * beam_size个单词的信息。接下来看外层循环
|
|
保留expansions中概率最高的beam_size个单词,并用这bean_size个单词扩展beams,得到size为beam_size的new_beams。一直循环,直到beams中所有beam都结束
predict_single_word
同理encode_video_frame,不过这里以pad作为frames_fc7的输入
关于caffemodel参数不匹配的问题
可以看到
- 在使用s2vt_captioner.py进行test的时候,网络的模型是没有展开的,然后通过将每次的output作为下次的input来传递c和h
- 然而,在train的时候,可以看到网络的模型是将LSTM展开的
- 显然,展开的LSTM的参数会比不展开的参数要多得多
- 那为什么还能用train出来的caffemodel给test用呢?
原因就在于,train的时候,展开的各部分实际上是share同一份参数的。而实现参数共享的trick就在我一开始想不明白有什么用的add_param()->set_name()
|
|
再从caffe.proto中看这个param参数的含义
|
|
所以说,所有参数都是共享同一份的
收获
- 在脚本迭代更新,备份上一份脚本时,备份命名要有意义,不要只加个.bak就算了,回去一个周末就忘记了.bak1, .bak2, .bak3是哪个跟哪个了
- debug的时候还需要考虑环境变量,比如PYTHONPATH这种,特别是两份相同的代码在两台机子上跑出不一样的结果