论文《Vision-and-Language Navigation: Interpreting visually-grounded navigation instructions in real environments》是VLN的开篇之作,这里记录下对它在R2R任务里面的源码理解。
github仓库地址:VLN-R2R
模型训练
- 程序入口:
train.py/train_val()
- vocab的建立和保存
- 创建tokenizer
- 创建训练环境train_env,是一个R2RBatch类
- R2RBatch类初始化init
- 导入feature和image的信息并建立batch个Simulator()
- 加载数据集,保存scans信息,记录instruction信息,并通过vocab对instruction进行encode
- load_nav_graphs,加载每个scan的链接图信息
- all_pairs_dijkstra_path,计算所有的最短路径
- R2RBatch类初始化init
- 创建val_envs,即验证环境,分为seen和unseen
- 这里使用字典保存
val_seen: (R2RBatch(), Evaluation()), val_unseen: (R2RBatch(), Evaluation())
- Evaluation类
_get_nearest()
是从path中找到距离goal id最近的一个节点id_score_item()
是计算最终结果和目标点的- nav error即最终点id和goal之间的距离
- oracle error即和目标id最接近的id,它和goal之间的距离
- trajectory steps即path的step个数减1
- trajectory lengths即path的总距离
score()
是通过和目标点的距离,验证每个agent 的轨迹- 通过
score_item()
得到每个path的分数 - 返回每个路径的平均分数
- 以及两个成功率:nav error小于目标值的概率,oracle error小于目标值的概率
- 通过
- Evaluation类
- 这里使用字典保存
- 创建模型,encoder和decoder
- encoder是EncoderLSTM类,对navigation instruction进行embedding,并用lstm进行encode,返回hidden state、用于decoder初始化的一个state、以及cell state
- hidden和cell state初始化都是0
- forward过程是embeedding->dropout->init->pack and pad->lstm->得到h_t和c_t->linear层处理hidden state再加上tanh得到decoder init state-> pad and packed sequence-> dropout-> return ctx,decoder init,cell state
- decoder是AttnDecoderLSTM类
- forward过程是:对action进行embedding->concat action embedding和feature -> dropout -> lstm -> dropout -> attention layer得到经过dot attention得到的h_tilde和attention权重 -> h_tilde通过linear得到logit
- encoder是EncoderLSTM类,对navigation instruction进行embedding,并用lstm进行encode,返回hidden state、用于decoder初始化的一个state、以及cell state
- 训练过程train()
- agent用Seq2SeqAgent,基于seq2seq和attention和LSTM的agent
- 用三维元组表示每个方向
- feedback可选teacher,argmax,sample
- 初始化的encoder和decoder就是之前创建好的
- 迭代过程
- Seq2SeqAgent.train()
- encoder.train()
- decoder.train()
- n_iter里面
- optimizer梯度置0
- rollout()
- self.env.reset(),加载一个新的mini batch数据
- 把输入,按照每个observation中的instructions的长度降序排序,方便padding
- 记录开始的observation信息
- encoder得到context state和hidden state cell state
- 初始化start action和ended 标识,都是batch个
- 用encoder得到的context state, hidden state, cell state和每个observation里面的feature,输入到decoder中得到输出的hidden state,cell state, attention权重,和logit
- 对无法forward的部分进行mask,即把logit[idx, index of forward action]置为负无穷
- _teacher_action(),提取ground truth的agent的方向信息,保存在target中
- 用decoder得到的logit和target计算交叉熵损失
- 根据feedback策略获得a_t变量,即action target
- teacher force策略,action target是ground truth
- student force策略,action target是logit的argmax
- sample策略,是按照概率对logit结果采样
- 更新结束标志,如果结束了,后面agent就不用再继续了
- 对所有的observation进行遍历,如果没有end就更新traj路径,即
traj[i]['path'].append((ob['viewpoint'], ob['heading'], ob['elevation']))
- 如果所有的agent都end了,就不用到下一个场景了,否则就继续下一个场景
- 最后所有场景迭代完,保存每个场景的平均损失,并且返回agent的轨迹traj
- loss反向传播
- optimizer.step()
- 记录loss等
- 进行validation
- agent.test(use_dropout=True),保持和训练时的环境一样,即encoder,decoder都train(),再进行test()
- 这里的test调用了BaseAgent的test()
- **reset_epoch()**,重置self.id为1,即data index变成了epoch开始那会的index
- 一个looped标志,记录测试集是否跑完一遍
- 只有在出现相同的instr_id时,才回退出循环,而想要出现相同的instr_id,就得遍历完一遍测试集
- 因为rollout函数里每次都会进行一个
self.env.reset()
,这个地方会进行_next_minibatch()
_next_minibatch()
会在剩下的data不足一个batch时,shuffle所有的data,然后继续采样
- agent.test(use_dropout=False),encoder和decoder都eval(),再test()
- 记录loss和metric
- agent.test(use_dropout=True),保持和训练时的环境一样,即encoder,decoder都train(),再进行test()
- Seq2SeqAgent.train()
agent.env=train_env
- 记录log,保存checkpoint
- agent用Seq2SeqAgent,基于seq2seq和attention和LSTM的agent
模型验证
- 程序入口:
eval.py/eval_simple_agents()
- 对每个split进行验证,train, val_seen, val_unseen, test
可视化
略