qinghuan
← Agent 算法与学习 / 全部笔记

单元 38 / Agent 算法与学习

P1 · 重点提升L3 · 深入

模仿学习、离线轨迹与 DAgger

让策略偏离示范路线,再比较行为克隆与 DAgger 怎样学习,分清实际执行的动作和专家给出的正确动作标签。

本篇目录 · 按需要跳转

读完这篇,你能做什么

复现行为克隆在扰动下失败、聚合访问状态的专家标签后成功,并核对表格学习器的确定性。

学习理由、准备要求与知识点

为什么学它

多步任务中,策略可能走到示范数据没有覆盖的状态。仅在离线样本上预测动作准确,不保证自己连续执行也能成功。

先读这些内容

知识点清单

  • 行为克隆经验风险
  • 状态分布偏移
  • 错误累积
  • 专家查询与执行分离
  • DAgger数据聚合
  • 混合策略beta
  • 离线轨迹覆盖
  • 标签预算与安全探索

运行前先确认DAgger AISTATS 2011与imitation v1.0.1源码;Java17与Python3.14标准库表格实验。未运行神经策略、GPU或真实交互环境。

01先弄懂它是什么

让 Agent 跟着专家示范学动作,是一种直接的训练方法,叫行为克隆。它用专家遇到的状态或历史作输入,用专家动作作正确标签,类似普通分类训练。但它学到的是在专家状态分布 d_expert 上该怎么做;实际运行时,它自己的动作会决定下一步去哪,形成的 d_policy 可能不同。一旦小错误把它带离熟悉路线,后面就可能接连犯错。

只看已有示范,往往没有“走错以后怎么办”的记录,并不代表专家也不会处理那些情况。DAgger 会让当前策略实际走一遍,再请专家为它真正遇到的状态标注推荐动作,把新样例加入旧数据后重新训练。这样补的是实际交互中缺少的状态,而不是把同一批专家示范重复训练更多遍。

专家可以是人、规划器或可信程序,但必须能够对学习者遇到的状态给出有意义标签。对于已经不可恢复的失败状态,专家可能只能选择安全停止;强行给出“继续成功”的动作会伪造监督。离线模仿没有在线询问能力时,不能把任意生成的自标签都叫DAgger,也不能套用其理论保证。

文中这几个词,先认一认

行为克隆
把专家在不同状态下采取的动作当作标签,通过监督学习训练策略模仿这些选择。
协变量偏移
实际运行时遇到的输入状态,与训练示范里的状态分布不同。
专家查询
请专家为当前遇到的状态给出推荐动作。得到标签,不代表现场一定执行了这个动作。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

DAgger的一轮流程是:选择专家与学习者的混合采集策略,运行环境,收集访问状态,再为这些状态获取专家动作,将新样本与旧集合合并,训练下一轮策略。beta控制采集时执行专家动作的概率;即使本步执行学习者动作,存入监督数据的仍应是当前状态的专家标签。执行动作和训练标签必须分两个字段保存。

本篇走廊目标为状态2,专家示范只覆盖0向右、1向右。测试加入一次固定偏航,把首步后的状态变成3。只记住原示范的表格策略对未知状态默认向右,最终停在4;专家在3和4会建议向左。聚合这两个访问状态的标签后,策略能从3返回2。这个可控例子展示覆盖机制,不声称所有环境经过一轮都会改善。

表格学习器为每个状态统计各动作出现权重,选择多数动作,同票固定按编号或名称决策。它是本篇明确选定的监督学习器,没有神经网络或表示泛化。真实连续状态无法简单查表,需要可泛化模型、足够标签和合适优化。论文的无遗憾分析也有损失与采样等条件,不能缩写成“DAgger保证大模型不会出错”。

先跑通一个最小例子

Python3.14标准库执行experiment.py。表格策略通过计数学习,专家为确定规则;没有调用模型或外部环境。

展开收起代码 · experiment.pypython · 38 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

experiment.py · python
from collections import Counter, defaultdict

def fit(data):
    counts=defaultdict(Counter)
    for state, action in data:
        if type(state) is not int or state<0 or action not in ("left","right"):
            raise ValueError("label")
        counts[state][action]+=1  # 汇总多轮专家标签,不保存学习者错误为标准答案。
    return {s:min(c,key=lambda a:(-c[a],a)) for s,c in counts.items()}
def expert(state):
    return "left" if state>2 else "right"
def rollout(policy, disturbance, query=False):
    state=0;visited=[];labels=[]
    for t in range(4):
        if state==2: return True,visited,labels
        visited.append(state)
        if query: labels.append((state,expert(state)))  # 在真正访问的状态上询问专家。
        action=policy.get(state,"right")
        state=max(0,min(4,state+(-1 if action=="left" else 1)))
        if disturbance and t==0: state=3  # 固定一次外部偏航,两个策略受到同样扰动。
    return state==2,visited,labels

initial=[(0,"right"),(1,"right")]
bc=fit(initial)
assert rollout(bc,False)[0]
before,visited,labels=rollout(bc,True,query=True)
assert not before and 3 in visited and 4 in visited
assert (3,"left") in labels and (4,"left") in labels
dagger=fit(initial+labels)
assert rollout(dagger,True)[0]
assert bc=={0:"right",1:"right"}  # 新训练没有修改原策略,便于公平比较。
assert fit([(3,"left"),(3,"right")])[3]=="left"
assert fit([])=={}
try: fit([(0,"teleport")])
except ValueError: pass
else: raise AssertionError("invalid label")
print(f"BC_disturbed={before} DAgger_disturbed={rollout(dagger,True)[0]} queried_states={sorted(set(visited))}")
print("PASS 8 checks; tabular imitation with a deterministic expert")

核对运行结果输出BC_disturbed=False、DAgger_disturbed=True、queried_states=[0,3,4],通过8项检查。

正在加载流程图…

在学习者实际访问的状态上询问专家并聚合,避免只重复专家常走的路径。
查看流程图源码(Mermaid)
flowchart TD
  N0["用专家示范训练初始策略"]
  N1["让当前策略访问环境状态"]
  N2["分别记录执行动作与专家标签"]
  N3["聚合旧数据和新标签重训练"]
  N4["在独立扰动与任务上评价"]
  N0 --> N1
  N1 --> N2
  N2 --> N3
  N3 --> N4

按顺序看,每一步做了什么

  1. 用专家示范训练初始策略
  2. 让当前策略访问环境状态
  3. 分别记录执行动作与专家标签
  4. 聚合旧数据和新标签重训练
  5. 在独立扰动与任务上评价

03对照实现代码

DAgger采集器如何保存专家标签 (新标签页)↗

imitation v1.0.1;Ross等AISTATS 2011算法3.1 · InteractiveTrajectoryCollector.step_async / step_wait

根据论文和固定库接口原创的单步模型;不复刻向量化采集器,不执行真实工具或机器人。

核心机制代码 · python
def collect_step(observation, learner, expert, env, use_expert):
    expert_action = expert(observation)  # 标签始终来自专家对当前状态的判断。
    executed = expert_action if use_expert else learner(observation)  # 采集策略决定实际执行。
    next_observation = env.step(executed)  # 下一状态受执行动作影响。
    return next_observation, (observation, expert_action)  # 返回的是监督标签,不是执行动作替身。

原论文算法3.1以当前策略诱导的数据分布采集,再把新状态及专家动作聚合到训练集合。保留历史数据有助于避免只适应最近一轮偏航而遗忘早期路径,但重复采集会改变状态权重,所以数据采样规则仍是实验的一部分。

固定版本InteractiveTrajectoryCollector接收专家动作,按beta保留或替换为学习者动作交给底层环境,同时在轨迹中保存专家标签。这个设计清楚展示“当时做了什么”和“希望学会做什么”的区别。若把替换后的学习者动作当标签,就会训练模型重复自己的错误。

step_wait 接收环境执行结果,继续维护这一局的轨迹,等回合结束后写出示范。如果同时运行多个环境,还要分清每条记录属于哪一局,不能把一个环境重置后的观测接到另一个环境的标签上。本篇短片段省略并行环境、磁盘写入和随机混合,保留的是样本与动作标签应该怎样对应。

实验使用标准库表格统计,并未安装或运行imitation训练器。真实库还依赖监督训练器、向量环境和检查点等组件;从源码理解采集协议后,才应换成神经网络并验证张量形状、动作空间和专家接口。可运行的局部例子用于检查概念,不替代真实机器人或工具环境的安全约束。

代码先调用谁,再调用谁

  1. 当前观测交给专家取得标签
  2. 按beta混合专家和学习者动作
  3. 执行所选动作
  4. 保存观测与专家标签
  5. 回合结束聚合并训练

04动手写一个例子

聚合专家标签的加权表格分类器

输入非负状态编号、动作编号和正整数权重,为每个出现状态选择权重最多的动作,同票选较小编号;未知状态不输出预测。

输入 1
状态0:动作1权重2,动作0权重1
输出 1
预测1
输入 2
动作0和1各一票
输出 2
预测0

每个状态有独立计数行,动作列累加标签权重。因为表格模型允许各状态独立选择动作,总加权零一误差等于各状态误差之和;选最多票动作就分别最小化每一项,因此得到该模型类的经验风险最小解,不需要搜索所有联合策略。

同票时固定选小编号,是可重复性约定而非理论上的更优行为。训练结果不包含未见状态,调用方必须显式选择默认动作、拒绝或专家查询。把未知状态偷偷映射成已有类别,会让研究者误以为模型具有从未验证过的泛化能力。

权重使用long并以精确加法检测溢出,非法动作范围和非正权重立即拒绝。哈希表在通常假设下提供常数时间定位;每个状态创建K列并扫描一次,不能只根据N条样本就宣称整个算法O(N),还必须计入状态动作表的初始化和选择成本。

为什么这样写是对的每个计数行恰好累计该状态全部已读专家标签的权重;输出动作最大化该行权重并遵循固定平局规则。

时间 通常散列假设下O(N+S×K)额外空间 O(S×K)

N为标签数,S为出现状态数,K为动作数;每行初始化和最大值扫描均为K。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

展开收起代码 · Main.java · 完整可运行程序Java · 43 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

Main.java · 完整可运行程序 · Java
import java.util.*;
public class Main {
    static void near(double expected,double actual) {
        if (!Double.isFinite(actual) || Math.abs(expected-actual)>1e-9*Math.max(1,Math.abs(expected)))
            throw new AssertionError(expected+" != "+actual);
    }
    static void check(boolean ok) { if(!ok) throw new AssertionError(); }
    static void bad(Runnable r) {
        try { r.run(); } catch(IllegalArgumentException e) { return; }
        throw new AssertionError("invalid input accepted");
    }
record Example(int state,int action,long weight) {}
    static Map<Integer,Integer> fit(List<Example> data,int actions) {
        if(data==null||actions<1||actions>10000) throw new IllegalArgumentException("shape");
        Map<Integer,long[]> counts=new HashMap<>();
        for(Example e:data) {
            if(e==null||e.state()<0||e.action()<0||e.action()>=actions||e.weight()<=0)
                throw new IllegalArgumentException("example");
            long[] row=counts.computeIfAbsent(e.state(),k->new long[actions]);
            try { row[e.action()]=Math.addExact(row[e.action()],e.weight()); }
            catch(ArithmeticException ex) { throw new IllegalArgumentException("weight overflow",ex); }
            // 保留全部轮次的标签权重,而非用最后一次动作覆盖历史。
        }
        Map<Integer,Integer> result=new HashMap<>();
        for(var entry:counts.entrySet()) {
            int best=0;long[] row=entry.getValue();
            for(int a=1;a<actions;a++) if(row[a]>row[best]) best=a; // 同票时固定选较小编号。
            result.put(entry.getKey(),best); // 每个状态独立最小化加权零一错误。
        }
        return Map.copyOf(result);
    }
    public static void main(String[] args) {
check(fit(List.of(new Example(0,1,2),new Example(0,0,1)),2).get(0)==1);
        check(fit(List.of(new Example(3,0,1),new Example(3,1,1)),2).get(3)==0);
        check(fit(List.of(new Example(0,1,1),new Example(3,0,1)),2).size()==2);
        check(fit(List.of(),2).isEmpty());
        bad(()->fit(List.of(new Example(0,2,1)),2));
        bad(()->fit(List.of(new Example(0,0,0)),2));
        bad(()->fit(List.of(new Example(0,0,Long.MAX_VALUE),new Example(0,0,1)),2));
        check(!fit(List.of(new Example(0,1,1)),2).containsKey(4));
        System.out.println("PASS 8 cases");
    }
}

这些特殊情况也要试一试

  • 空数据得到空策略
  • 同票保持确定性
  • 未知状态不伪造预测
  • 长整型权重溢出拒绝
进阶选读 · 昂贵专家与安全查询预算

场景专家只能审核少量状态,学习者探索还可能触发不可逆操作。

做法先在沙箱中按不确定性或新颖性筛选查询,记录选择偏差;高风险动作使用固定许可边界,不能让降低标注成本变成绕过环境安全约束。

复杂度表格查询O(1)期望;神经策略训练、专家成本和环境执行须分别计量。

好处与代价主动筛选节省标签,却可能遗漏自信但错误的状态,需保留随机审计样本。

05项目里怎么用

为每轮采集保存策略版本、专家版本、beta、任务种子及标签来源。若专家规则升级,旧数据可能与新标签冲突,应分析是观测不足、任务变化还是标注错误。多数票只是本篇学习规则,不是自动解决所有语义冲突的依据。

评估要在学习者独立执行时进行,并与训练采集的专家混合比例区分。一个高beta采集过程表现很好,可能只是专家不断纠正而非学习者真的变强。报告闭环成功率、专家调用数、恢复次数和任务成本,才能判断方法的实际收益。

保留未参与聚合的环境扰动与任务家族做测试,否则收集到每个测试状态后再宣布泛化提升属于泄漏。CPU例子刻意只有一个确定扰动,展示一次数据聚合的机制;它不衡量真实Agent分布变化,也没有证明长期运行中的安全性。

容易踩的坑

  • 把学习者执行动作误当专家标签
  • 只看专家混合执行的成功率
  • 丢掉早期数据导致遗忘
  • 未见状态默认动作不记录
  • 测试状态进入聚合后仍称独立测试

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
纯离线行为克隆训练简单、环境交互少覆盖受专家示范限制已有高质量轨迹
DAgger在线聚合补充学习者实际访问状态依赖可查询专家与安全环境闭环误差累积明显
选择性专家查询降低标签成本查询偏差可能遗漏错误有独立审计预算

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01DAgger为何不只是更多示范?

展开参考答案与推导

新标签来自学习者诱导的访问状态,改变了数据覆盖。

02执行动作与监督动作可以不同吗?

展开参考答案与推导

可以,混合策略决定执行,专家动作仍作为标签保存。

03离线误差小为什么仍失败?

展开参考答案与推导

部署动作改变后续状态分布,未覆盖状态上的错误可能累积。

换个情况,还会处理吗

01增加DAgger轮次却退化如何查?

展开参考答案与推导

检查旧数据保留、专家版本冲突、状态权重及独立评测是否一致。

02专家无法处理失败状态怎么办?

展开参考答案与推导

允许安全停止或标记无可靠标签,不编造成功动作。

03采集成功率很高但部署低怎么办?

展开参考答案与推导

分离专家混合采集与纯学习者执行,核对beta和调用次数。

写代码试一试

01实现标签多数投票。

输入 / 条件
同状态动作1两票、动作0一票
输出 / 预期
动作1

提示 按状态分组

展开参考答案与推导

累计权重并固定同票规则。

02构造偏航测试。

输入 / 条件
首步后跳到状态3
输出 / 预期
BC失败、聚合后成功

提示 专家从3向左

展开参考答案与推导

两种策略使用同一扰动与步数预算。

03检测权重溢出。

输入 / 条件
Long.MAX_VALUE再加1
输出 / 预期
拒绝输入

提示 精确加法

展开参考答案与推导

捕获溢出并报错,不允许计数变负。

读完之后,独立完成

课后作业:自己做一遍

本篇共 10 道作业,先完成 8 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

复现行为克隆在扰动下失败、聚合访问状态的专家标签后成功,并核对表格学习器的确定性。

本篇重点加练

专家查询与数据聚合共同决定标签预算和学习分布,需检验重复状态与历史数据权重。

  • 专家查询与执行分离
  • DAgger数据聚合

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    为一条狭窄网格路径设计专家示范数据,解释专家访问状态与学习者访问状态可能有何差异,并定义两种误差指标。

    提交内容
    状态分布图与指标表
    怎样算完成
    • 一步误差和整轨迹成功分开
    • 分布来源明确

    练到的知识点行为克隆经验风险 · 状态分布偏移 · 错误累积

  2. 作业 02代码推演

    设两轮混合系数为 0.8、0.2,自建六个状态的执行记录,分别标注执行者、实际动作、专家标签与进入训练集的记录。

    提交内容
    两轮数据账本
    怎样算完成
    • 执行和查询不混用
    • 聚合样本身份保留

    练到的知识点专家查询与执行分离 · DAgger数据聚合 · 混合策略beta

  3. 作业 03原理问答

    同一状态在三个采集轮次出现,专家标签相同但执行动作不同。分别定义按访问次数保留与按状态去重的训练分布,说明二者何时可比较。

    提交内容
    两种聚合契约与样本权重表
    怎样算完成
    • 专家标签和执行动作分开
    • 去重改变的权重显式

    练到的知识点专家查询与执行分离 · DAgger数据聚合

综合应用

  1. 作业 04编程实践

    用 CPU 可枚举小环境实现行为克隆和两轮 DAgger,记录专家查询数、状态覆盖及保留初始状态上的结果。

    提交内容
    实现与逐轮报告
    怎样算完成
    • 专家预算单独统计
    • 评估使用纯学习者策略

    练到的知识点行为克隆经验风险 · 专家查询与执行分离 · DAgger数据聚合

  2. 作业 05排错实验

    构造学习者偏离示范后出现的新状态,比较固定离线数据与新增查询数据的表现;保持分类器和训练预算可比。

    提交内容
    偏离实验与覆盖表
    怎样算完成
    • 新状态来源可复现
    • 效果不归因未控制因素

    练到的知识点状态分布偏移 · 混合策略beta · 离线轨迹覆盖

  3. 作业 06面试场景

    专家对一部分危险状态拒绝标注。设计查询、回退与评估协议,明确未覆盖状态如何影响最终能力报告。

    提交内容
    协议与拒绝事件样例
    怎样算完成
    • 安全边界具体
    • 缺失标签不冒充正确示范

    练到的知识点专家查询与执行分离 · 标签预算与安全探索

  4. 作业 07编程实践

    CPU 小环境固定总专家查询预算,记录每次查询是否新增状态;对比缓存专家标签与重复询问两种采集器的有效覆盖和训练集组成。

    提交内容
    采集器及查询账本
    怎样算完成
    • 预算按真实查询计数
    • 缓存命中不冒充新标签

    练到的知识点专家查询与执行分离 · DAgger数据聚合

  5. 作业 08设计评审

    聚合轮次增长后早期数据占比下降。设计按轮等权、按记录等权两种训练目标,并提出在同一保留状态上比较的协议。

    提交内容
    两目标公式与实验方案
    怎样算完成
    • 数据身份及权重完整
    • 不同目标不宣称等价

    练到的知识点DAgger数据聚合

进阶挑战 · 掌握前置后选做

  1. 作业 09设计评审

    在相同专家标签总量下比较集中补充离线示范与自适应 DAgger 查询,使用多个预定种子并分析状态分布。

    提交内容
    公平对照实验
    怎样算完成
    • 标签和训练预算明确
    • 失败路径也有记录

    练到的知识点错误累积 · DAgger数据聚合 · 离线轨迹覆盖 · 标签预算与安全探索

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 10思维拓展 · 条件变化

    若专家策略随时间改善,旧专家标签和新标签冲突,DAgger 的聚合含义如何改变?自建状态与版本记录,提出可验证的处理假设。

    提交内容
    版本化专家案例与研究问题
    怎样算完成
    • 专家版本可追踪
    • 冲突处理不依赖事后测试分数

    练到的知识点专家查询与执行分离 · DAgger数据聚合

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗