MemER 方法原理讲解
一句话理解 MemERMemER 的核心思想是不要把机器人从开头到现在看到的所有画面都塞给模型而是让一个高层视觉语言模型不断判断“现在该做哪个子任务”和“最近看到的哪些帧以后可能还要用”再把这些被挑出来的关键帧作为长期视觉记忆喂回模型。它解决的是长时程机器人任务里的“我刚才看见过但现在画面里没有”的问题。例如机器人之前看过某个物体在右边箱子里现在要找它时不应该重新翻左边箱子。机器人已经舀了两勺花生当前画面只看到勺子不一定能知道已经舀了几勺。机器人把架子上的物体拿走、擦完架子以后需要记住物体原本放在哪一层。普通策略如果只看当前帧会缺记忆如果直接看很长视频会慢、贵、冗余而且容易把无关历史当捷径。MemER 的做法是用“最近窗口 少量关键帧”代替完整历史。方法整体架构MemER 是一个分层策略高层策略 pi_h:输入: 总任务指令 长期关键帧记忆 K_t 最近 N 帧 R_t输出: 当前应执行的语言子任务 l’_t 最近 N 帧中应保存的候选关键帧 J_t低层策略 pi_l:输入: 当前图像 I_t 机器人状态 q_t 高层给出的子任务 l’_t输出: 未来一小段机器人动作 A_t论文把整体策略写成pi(A_t | o_{0:t}) pi_l(A_t | I_t, q_t, l’_t)* pi_h(l’t, J_t | I{t-N1:t}, K_t)其中A_t 是未来动作 chunk。I_t 是当前多相机图像。q_t 是关节角、夹爪等 proprioception。l_t 是用户给的总任务指令。l’t 是当前要交给低层策略执行的子任务。R_t I{t-N1:t} 是最近 N 帧。K_t 是过去被选中的长期关键帧。J_t 是高层模型从最近窗口里新提名的候选关键帧。当前公开代码主要覆盖高层策略数据导出、Qwen3-VL 微调格式、结构化推理、记忆更新、离线 rollout 评估和在线部署封装。低层 pi0.5 的训练代码不在这个仓库里。为什么需要“高层记忆策略”直接把历史全喂给策略有三个问题太贵视频帧是高维输入长历史会让 VLM/VLA 的 token 和显存成本快速上涨。太慢机器人闭环控制需要低延迟。论文里 Long History baseline 用 32 帧大约 16 秒历史推理延迟已经接近闭环可忍受上限。太脆长历史里包含大量无关动作模型可能学到示范轨迹中的顺序捷径。一旦部署时低层策略卡顿、重试、失败恢复历史分布就变了模型更容易误判当前进度。MemER 的设计取舍是最近窗口保留局部动作上下文。关键帧记忆保留长期必要信息。关键帧由高层模型自己根据任务语义提名不额外训练一个图像检索模型。最后用一个很简单的时间聚类器去掉重复提名让记忆稳定。4. 数据构建从一段视频到很多道训练题MemER 的高层数据构建最简单的理解是把一整段机器人演示视频切成很多道给 VLM 做的训练题。每道题都问模型两件事现在应该执行哪个子任务最近看到的几帧里有没有以后要记住的关键画面完整流程是原始机器人演示轨迹- 每帧都有图像、总任务、当前子任务标签、机器人状态、动作- 根据子任务标签和规则找出整条轨迹里哪些帧值得记住- 对每个时间步 t 单独构造一条高层训练样本- 导出成 Qwen SFT 的 train.json media/*.jpg先把几个名字分清楚后面就不会乱名字 它是什么 在训练样本里扮演什么角色GT keyframes 整条演示轨迹里规则认为应该记住的绝对帧号 离线生成监督信号memory_indices 当前时间步以前、已经离开最近窗口的 GT keyframes 作为输入图片给模型看context_indices 当前时间步附近的最近 N 个下采样帧 作为最近视频输入给模型看keyframe_positions context_indices 里哪些位置应该被提名为新 keyframe 作为模型输出答案一句话说memory_indices 是“过去已经该记住的帧”放进输入keyframe_positions 是“最近刚出现、以后应该记住的帧”放进输出。这个区别是数据构建里最关键的点。MemER 不是让模型直接输出一个历史帧号而是训练模型在“最近窗口”里指出哪些帧值得保存等这些帧未来离开最近窗口后它们才会成为长期 memory。4.1 原始演示数据论文里的每条机器人轨迹可以理解成一串时间步(I_t, q_t, l_t, l’_t, a_t)含义是I_t多相机图像论文实验使用 wrist camera 和 third-person/base camera。q_t机器人自身状态。l_t总任务指令例如“把架子上的物品拿下来擦架子再放回原位”。l’_t当前子任务标签例如“dust bottom shelf”。a_t机器人动作。子任务标签来自数据采集过程操作者执行预先规定的子任务列表完成一个子任务后按键切换到下一个子任务。因此每个时间步天然有当前子任务标签。4.2 高层策略训练样本高层策略不直接预测机器人动作只预测{“current_subtask”: “…”,“keyframe_positions”: [1, 5]}其中current_subtask 是当前应该让低层策略执行的语言子任务。keyframe_positions 是最近窗口中哪些帧值得保存位置是 1-indexed即 1 表示最近窗口第一帧8 表示第八帧。公开代码导出的 Qwen SFT 样本大致长这样{“id”: “episode_000000_t000120”,“image”: [“media/episode_000000/frame_000015.jpg”,“media/episode_000000/frame_000050.jpg”,“media/episode_000000/frame_000085.jpg”,“media/episode_000000/frame_000090.jpg”,“… recent frames …”],“conversations”: [{“from”: “system”, “value”: “…输出 JSON …”},{“from”: “human”, “value”: “Task: … … …”},{“from”: “gpt”, “value”: “{“current_subtask”: “…”, “keyframe_positions”: [7]}”}],“metadata”: {“memory_indices”: [15, 50],“context_indices”: [85, 90, 95, 100, 105, 110, 115, 120],“answer”: {“current_subtask”: “…”,“keyframe_positions”: [7]}}}注意这里的图片顺序是先放 memory keyframes再放 recent context framesprompt 文本会告诉模型前几张是“从完整历史中选出的重要帧”后几张是“最近动作视频”。4.3 多相机图像怎样处理公开代码支持单相机或多相机。多相机时每个时间步的多个相机视角会被竖直拼成一张图stacked_frame_t [wrist image at texterior image at t]默认 Panda/Franka 配置是{“camera_keys”: [“observation.images.wrist_left”,“observation.images.exterior_1_left”],“view_width”: 320,“view_height”: 180}因此最终单帧输入宽度是 320高度是 180 * 相机数。这个拼接方式保证训练、评估、部署看到的视角顺序一致。关键帧标签怎样得到MemER 并不是让人工逐帧标所有关键帧。论文和代码采用的是半自动规则先有每一帧的子任务标签。把连续相同子任务看成一个 segment。人工为每类 subtask 设定规则取 segment 第一帧、最后一帧、两者都取或不取。规则固定后自动应用到所有演示轨迹。比如Object Searchlook inside the bin 取最后一帧因为最后通常能看清箱子里有什么。