RL——强化学习中的方差与偏差


本文主要介绍强化学习中的方差和偏差,以及相关的解法如lambda-return、TD(lambda)、GAE等

  • 本文关键词:
    • Multi-step TD,n-step TD,多步TD,n步TD
    • lambda-return, \(\lambda\)-return
    • TD(lambda),TD-lambda,TD(\(\lambda\))
    • GAE

MC 和 TD 的方差分析


强化学习中 Reward 的方差为什么不为0

  • 策略随机性会导致出现方差: \(a \sim \pi(\cdot|s)\)
  • 执行策略后状态转移随机性会导致出现方差: \(s’ \sim P(\cdot|s,a)\)

Multi-step TD

  • Multi-step TD,也称为n-step TD,中文名多步TD或n步TD
  • 将TD中的即时奖励替换成采用未来多个时间片的奖励和,同时 \(\gamma V_\theta(s_{t+1})\) 值替换成 \(\gamma^n V_\theta(s_{t+n})\)
  • 形式化定义:自 \(t\) 时间步开始, \(n\) -step TD的 \(G_t^{(n)}\) 的值定义如下:
    $$
    \begin{align}
    G_t^{(n)} &= \sum_{l=0}^{n-1} \gamma^l R_{t+l} + \gamma^n V_\theta(S_{t+n}) \\
    G_t^{(n)} &= R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+\cdots+\gamma^n V_{\theta}(S_{t+n})
    \end{align}
    $$
  • 展开来看,不同的步长下对应的n-step TD形式化定义如下
    $$
    \begin{align}
    G_t^{(1)}&=R_t+\gamma V_{\theta}(S_{t+1})\\
    G_t^{(2)}&=R_t+\gamma R_{t+1}+\gamma^2 V_{\theta}(S_{t+2})\\
    &\vdots\\
    G_t^{(n)}&=R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+\cdots+\gamma^n V_{\theta}(S_{t+n})\\
    &\cdots\\
    G_t^{(N)}&=\sum_{l=0}^N \gamma^{l} R_{t+l}\\
    \end{align}
    $$
    • \(G_t^{(N)}\) 表示包含终止状态(即包含最后一个时间步)的情况

\(\lambda\)-return

  • 回顾 Multi-step TD,自 \(t\) 时间步开始, \(n\)-step TD 的 \(G_t^{(n)}\) 的各种值如下
    $$
    \begin{align}
    G_t^{(1)}&=R_t+\gamma V_{\theta}(S_{t+1})\\
    G_t^{(2)}&=R_t+\gamma R_{t+1}+\gamma^2 V_{\theta}(S_{t+2})\\
    &\vdots\\
    G_t^{(n)}&=R_t+\gamma R_{t+1}+\gamma^2 R_{t+2}+\cdots+\gamma^n V_{\theta}(S_{t+n})\\
    &\cdots\\
    G_t^{(N)}&=\sum_{l=0}^N \gamma^{l} R_{t+l}\\
    \end{align}
    $$
  • \(\lambda\)-return 是 Multi-step TD 的加权平均(加权平均意味着权重和为1)
    $$
    \begin{align}
    G^{\lambda}_t&=(1-\lambda)[G_t^{(1)}+\lambda G_t^{(2)}+\cdots+\lambda^{N-2} G_t^{(N-1)}] + \lambda^{N-1} G_t^{(N)}\\
    G^{\lambda}_t&=(1-\lambda)\sum_{n=1}^{N-1}\lambda^{n-1}G_{t}^{(n)} + \lambda^{N-1} G_t^{(N)} \\
    \end{align}
    $$
    • 其中 \((1-\lambda)\) 是一个配平因子
      • 因为等比数列的和为 \(S_n = a+ar+ar^2+\cdots+ar^{n-1} = \frac{a(1-r^n)}{1-r}\)
      • 在我们场景中,系数部分有: \(S_n = 1+\lambda+\lambda^2+\cdots+\lambda^{n-1} = \frac{1-\lambda^n}{1-\lambda}\)
      • 所以最后一项不需要乘以 \((1-r)\),即:
        $$ 1 = (1-\lambda)(\frac{1-\lambda^N}{1-\lambda}) + \lambda^N = (1-\lambda)(1+\lambda+\lambda^2+\cdots+\lambda^{N-1}) + \lambda^N $$
      • 当 \(n\rightarrow \infty\) 时,有 \(1+\lambda+\lambda^2+\cdots+\lambda^{\infty} = \frac{1}{1-\lambda}\),此时不需要再配平,所以此时有:
        $$G^{\lambda}_t=(1-\lambda)\sum_{n=1}^{\infty}\lambda^{n-1}G_{t}^{(n)} \quad \quad \quad \quad \quad \quad$$
  • \(\lambda\)-return方法可用于平衡偏差与方差
  • \(\lambda\)-return方法是off-line的值更新方法,因为它必须用到当前时刻之后直到到达终止状态的所有数据(forward view),也就是说只有在一条episode结束后才能计算出对应的状态估计量
    • GAE也是这样吧,很多更新算法都是完成一整局游戏再更新一次模型,比如PPO,似乎也没有什么问题
  • 实践中,常常用TD(\(\lambda\))来近似拟合 \(\lambda\)-return

TD(\(\lambda\))

  • TD(\(\lambda\))方法是在Backward视角下,对n-step TD的各个值进行加权求和
    • TD(\(\lambda\))在每个时间步骤后,马上就可以调整预测
    • TD(\(\lambda\))是on-line的值更新方法,可以解决 \(\lambda\)-return需要等到 episode 结束才能获得状态估计量的缺点
  • TD(\(\lambda\))算法是强化学习中的一种重要方法,它结合了蒙特卡罗(MC)方法和时间差分(TD)方法的优点,同时引入了一个新的参数 \(\lambda\),用于平衡一步预测和多步预测之间的关系。TD(\(\lambda\))的计算公式涉及到如何更新状态或动作-状态的价值函数,并且利用了所谓的“资格迹”(eligibility trace)来追踪哪些权重对当前的 TD 误差负有责任

表格型学习下的 TD(\(\lambda\))

  • 对于表格型强化学习(Tabular RL)下的 TD(\(\lambda\)),其价值函数更新公式可以表示为:
    $$ V(S_t) \leftarrow V(S_t) + \alpha \delta_t E_t $$
  • 其中:
    • \(V(S_t)\) 是状态 \(S_t\) 的价值估计
    • \(\alpha\) 是学习率,决定了每次更新的步伐大小
    • \(\delta_t = R_{t} + \gamma V(S_{t+1}) - V(S_t)\) 是TD误差,衡量了实际观察到的结果与预测结果之间的差异,这里将这个差异用于更新当前episode上遇到的所有的相关状态的价值
    • \(E_t\) 是资格迹向量,在每个时间点 \(t\) 上根据之前的状态访问情况累积而成
      • 每次访问任意状态 \(\hat{s}\),都会更新所有状态的资格迹,假设在第 \(t\) 轮次,访问到状态 \(s_t\) 后,任意状态 \(s\) 对应的资格迹的更新规则是:
        $$ E_t(s) = \gamma \lambda E_{t-1}(s) + \mathbf{1}(s_t = s) $$
      • 当前轮次更新所有状态时,首先所有状态都会在上一轮的资格迹基础上乘以 \(\gamma \lambda \),然后,对于这轮遇到的状态 \(s = s_t\) 上资格迹加1,否则不加( \(\mathbf{1}(s_t = s)\) ),如果展开资格迹,则有如下表示:
        $$ E_t(s) = \begin{cases}
        \gamma \lambda E_{t-1}(s), & \text{if } s \neq S_t \\
        \gamma \lambda E_{t-1}(s) + 1, & \text{if } s = S_t
        \end{cases} $$
      • 注意:这里的 \(t\) 不是episode里面的时间片, \(t\) 是表示当前更新轮次 \(t\),这个更新轮次遇到的状态则表示为 \(s_t\), \(E_{t-1}(s)\) 则表示上一轮次访问到状态 \(\hat{s}\) (这里的状态 \(\hat{s}\) 可以是任意状态)以后,状态 \(s\) 更新得到的结果
      • 这意味着每当访问到某个状态 \(s\) 时,它的资格迹会被重置为1加上前一时刻该状态的资格迹乘以 \(\gamma\lambda\) 。如果当前不是访问这个状态,则仅按比例衰减
    • TD(\(\lambda\))是在访问完一个时间片以后,立刻用当前时间片访问得到的信息更新之前遇到过的其他状态,且离当前时间片越近、历史访问次数越多的状态被更新的幅度越大(实现时,资格迹默认置0,未被访问过的状态是不会被更新的)
  • 说明:虽然上述操作使得TD- \(\lambda\) 在计算上显得更优雅,但显然它的合理性弱于 \(\lambda\)-return,实际效果也可能会弱于 \(\lambda\)-return
  • TD \(\lambda\) 的代码实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class TDLambda:
    def __init__(self, alpha, gamma, lambda_, num_states):
    self.alpha = alpha
    self.gamma = gamma
    self.lambda_ = lambda_
    self.V = np.zeros(num_states) # 存储所有状态价值函数V值
    self.E = np.zeros(num_states) # 存储所有状态的资格迹
    def update(self, state, next_state, reward, done):
    delta = reward + (0 if done else self.gamma * self.V[next_state]) - self.V[state]
    self.E *= self.gamma * self.lambda_ # 更新所有状态的资格迹
    self.E[state] += 1 # 对当前状态增加踪迹
    self.V += self.alpha * delta * self.E # 更新所有状态的价值函数,资格迹为0的(不在当前episode的状态资格迹都为0)
    if done:
    self.E[:] = 0 # 如果episode结束,重置资格迹,保证新的回合开始时所有状态资格迹为0

    td_lambda = TDLambda(alpha=0.1, gamma=0.9, lambda_=0.8, num_states=env.observation_space.n)
    state = env.reset()
    for t in range(max_steps):
    action = policy(state) # 根据某种策略选择行动
    next_state, reward, done, _ = env.step(action) # 执行动作
    td_lambda.update(state, next_state, reward, done) # 更新用TD(\lambda)更新所有状态的价值函数和资格迹
    state = next_state
    if done:
    break

函数近似型学习下的资格迹

  • 函数近似强化学习(Function Approximation RL)情况下,由于状态往往无法通过表格完全枚举,所以资格迹是针对记录参数的,而不是记录状态的,但本质都是对状态价值函数的估计
  • 这种资格迹出现在 Sutton 的《Reinforcement Learning: An Introduction》中,此时的资格迹(Eligibility Traces)定义与上述表格型资格迹有所不同,其更侧重于从权重更新的角度进行定义
    • 注:详情见第二版书籍《Reinforcement Learning: An Introduction Second Edition》的 第 12.2 章节
  • 对于状态价值函数的估计,资格迹向量 \(z_t\) 的更新公式为:
    $$
    \begin{align}
    z_{-1} &\doteq 0 \\
    z_t &\doteq \gamma\lambda z_{t - 1}+\nabla\hat{v}(S_t,w_t)
    \end{align}
    $$
    • 其中 \(\gamma\) 是折扣因子,
    • \(\lambda\) 是资格迹衰减参数,
    • \(\nabla\hat{v}(S_t,w_t)\) 是状态价值函数 \(\hat{v}(S_t,w_t)\) 关于权重 \(w_t\) 的梯度
  • 权重 \(w_t\) 的更新公式为:
    $$w_{t + 1} \doteq w_t+\alpha\delta_t z_t$$
    • 其中 \(\alpha\) 是学习率
    • \(\delta_t\) 是时序差分误差,定义为
      $$ \delta_t \doteq R_{t + 1}+\gamma\hat{v}(S_{t + 1},w_t)-\hat{v}(S_t,w_t) $$
  • 这种定义方式强调了资格迹与价值函数梯度的关系,通过资格迹来记录权重向量的各个维度对最近的状态值函数估计的贡献程度,从而在权重更新时能够更有效地利用多步的时序差分信息
  • 在强化学习中,上述提到的两种“资格迹”定义并非本质冲突,而是同一核心思想在不同价值函数表示框架下的具体实现 ,其差异源于对“状态价值函数 \( V(s) \)”的建模方式不同(表格型 vs 函数近似型),这也正是 Sutton《Reinforcement Learning: An Introduction》中从基础到进阶的核心逻辑脉络

核心区别:表格型方法 vs 函数近似方法

  • 两种资格迹的本质差异,根植于“是否用参数化模型拟合价值函数”,具体可从适用场景、表示形式、更新逻辑三个维度对比:
    对比维度 第一种定义(表格型资格迹) 第二种定义(Sutton 书的函数近似型资格迹)
    适用场景 表格型强化学习:状态空间 有限且离散(如网格世界、 Blackjack),每个状态 \( s \) 有独立的价值 \( V(s) \),无需参数拟合 函数近似强化学习:状态空间 无限或高维(如连续动作机器人、图像输入游戏),需用参数化模型 \( \hat{v}(s, \boldsymbol{w}) \) 拟合 \( V(s) \)(\( \boldsymbol{w} \) 为模型参数)
    资格迹的表示形式 针对“单个状态”的迹值:\( e_t(s) \) 是标量,每个状态 \( s \) 对应一个独立的资格迹(可理解为“状态级迹”) 针对“模型参数”的迹向量:\( \boldsymbol{z}_t \) 是与参数 \( \boldsymbol{w} \) 同维度的向量(如神经网络的权重向量),每个参数维度对应一个迹值(可理解为“参数级迹”)
    核心更新逻辑 直接更新“状态价值”:通过 \( e_t(s) \) 记录“状态 \( s \) 近期被访问的程度”,更新时直接调整 \( V(s) \) 间接更新“参数权重”:通过 \( \boldsymbol{z}_t \) 记录“每个参数对近期状态价值估计的贡献程度”,更新时先调整参数 \( \boldsymbol{w} \),再通过 \( \hat{v}(s, \boldsymbol{w}) \) 间接体现状态价值的变化
    梯度项的角色 无梯度项:因每个状态价值独立,无需对参数求导,用“指示函数 \( \mathbb{I}(s=s_t) \)”标记当前访问的状态 核心依赖梯度:\( \nabla_{\boldsymbol{w} } \hat{v}(S_t, \boldsymbol{w}) \)(价值函数对参数的梯度)决定“哪些参数对当前状态 \( S_t \) 的价值估计有贡献”,是资格迹更新的核心输入
  • 两种资格迹具体公式对比:从“状态迹”到“参数迹”的映射
    • 为更直观理解,将两种定义的核心公式并列,可清晰看到“函数近似型”是“表格型”的泛化与扩展
      公式类型 表格型资格迹(第一种定义) 函数近似型资格迹(Sutton 书定义)
      资格迹初始化 \( e_0(s) = 0 \)(所有状态的迹值初始为 0) \( \boldsymbol{z}_{-1} = \boldsymbol{0} \)(参数向量维度的迹向量初始为 0 向量)
      资格迹更新 \( e_t(s) = \gamma\lambda \cdot e_{t-1}(s) + \mathbb{I}(s = S_t) \)
      (当前状态迹值+1,其余衰减,所有状态都更新)
      \( \boldsymbol{z}_t = \gamma\lambda \cdot \boldsymbol{z}_{t-1} + \nabla_{\boldsymbol{w} } \hat{v}(S_t, \boldsymbol{w}) \)
      (参数迹向量衰减后,叠加当前状态的价值梯度)
      TD 误差 \( \delta_t = R_{t+1} + \gamma V(S_{t+1}) - V(S_t) \)
      (基于真实状态价值 \( V(s) \))
      \( \delta_t = R_{t+1} + \gamma \hat{v}(S_{t+1}, \boldsymbol{w}) - \hat{v}(S_t, \boldsymbol{w}) \)
      (基于参数化估计 \( \hat{v}(s, \boldsymbol{w}) \))
      价值/参数更新 \( V(s) \leftarrow V(s) + \alpha \cdot \delta_t \cdot e_t(s) \)
      (直接更新每个状态的价值)
      \( \boldsymbol{w}_{t+1} \leftarrow \boldsymbol{w}_t + \alpha \cdot \delta_t \cdot \boldsymbol{z}_t \)
      (更新参数向量,间接优化价值估计)
  • 核心思考:两种资格迹本质是统一的,资格迹的核心思想从未改变,尽管形式差异明显,但两种定义都遵循资格迹的核心逻辑 都是
    • 用“衰减的痕迹”记录“近期相关性” ,让后续的 TD 误差能高效回溯并更新“对当前奖励有贡献的状态/参数”
      • \(\mathbb{I}(s = S_t)\) 和 \(\nabla_{\boldsymbol{w} } \hat{v}(S_t, \boldsymbol{w})\) 表示的都是针对状态 \(S_t\) 更新的,记录的是当前状态 \(S_t\) 的相关度
      • 等价性:在表格型方法下,可以将表格位置看做参数,则参数维度为表格位置数量(与状态数量一致),此时状态价值与其他位置参数无关,仅仅与指定当前状态 \(S_t\) 对应的位置(唯一位置,假定对应参数为 \(w_i\))有关,此时的梯度:
        • 对 \(S_t\) 对应的位置参数 \(w_i\) 有: \(\nabla_{\boldsymbol{w_i} } \hat{v}(S_t, \boldsymbol{w_i}) = 1\)
        • 对其他所有 \(S_t\) 无关的位置参数 \(w_{\neg i}\)有:\(\nabla_{\boldsymbol{w_{\neg i}} } \hat{v}(S_t, \boldsymbol{w_{\neg i}}) = 0\)
    • 简言之,两种定义是“同一思想在不同问题复杂度下的适配”,函数近似型是表格型的自然延伸,而非替代
    • 对 \(\boldsymbol{w}_{t+1} = \boldsymbol{w}_t + \alpha \cdot \delta_t \cdot \boldsymbol{z}_t\) 的更新,相当于是对价值函数 \(\hat{v}(s,\boldsymbol{w}_{t+1})\) 的更新
  • 总结:一致性体现在(可能还有其他):
    • 1)衰减机制一致 :均通过 \( \gamma\lambda \) 实现“远期痕迹的指数衰减”(\( \gamma \) 折扣未来奖励,\( \lambda \) 控制多步信息的范围);
    • 2)更新触发一致 :均以 TD 误差 \( \delta_t \) 为“更新信号”,痕迹值越高的状态/参数,获得的更新幅度越大;
    • 3)目标一致 :都是为了平衡 TD(0)(单步、低方差高偏差)和 MC(全步、低偏差高方差),高效利用多步信息
    • 4)资格迹都是用于更新权重,且最终都是用于对价值函数的估计,对参数的更新会自动更新到状态价值网络
  • 何时用哪种定义?
    • 在处理简单离散状态问题(如 10x10 网格世界)时,表格型资格迹(第一种定义)更直观,无需考虑参数拟合,直接操作状态价值即可;
    • 在处理复杂高维/连续状态问题(如自动驾驶、Atari 游戏)时,必须采用 Sutton 书的函数近似型资格迹,通过参数向量 \( \boldsymbol{w} \) 和梯度 \( \nabla_{\boldsymbol{w} } \hat{v} \),才能用有限参数拟合无限状态的价值函数

GAE

  • 原始论文:(GAE)High-Dimensional Continuous Control Using Generalized Advantage Estimation, ICLR 2016, UC Berkeley

  • GAE,Generalized advantage estimation,平衡了强化学习中的方差与偏差,常用于 TRPO 和 PPO 中,用于评估优势函数

  • 推导过程(基于多步时序差分的思想)

    • \(\lambda\) 是一个介于 0 和 1 之间的参数,用于控制偏差-方差的权衡
      • 当 \(\lambda = 0\) 时,GAE 退化为单步 TD 误差
      • 当 \(\lambda = 1\) 时,GAE 等价于无限步回报(详情见下文的推导)
    • \(\gamma\) 是折扣因子
  • GAE 本质上是对不同步数的优势函数进行加权求和,最终得到一个的优势函数估计值

  • GAE 可通过参数调整控制方差和偏差的关系

GAE 中 \(\lambda=1\) 时的结论和证明

  • 当 GAE 中的 \(\lambda=1\) 时,优势估计退化为纯粹的蒙特卡洛形式
    • 不再使用任何 TD 自举(bootstrap),完全依赖实际采样的回报
    • 估计无偏,但方差最大
    • 对应 GAE 偏差-方差权衡谱的“高方差、零偏差”极端
  • 注意:不能直接从定义上证明,因为定义中包含 \(1-\lambda\),会导致结果看起来是 0,忽略前面又无法得到精确解,所以要从上面的结论形式出发推导
  • 回顾:写出 GAE(\(\lambda\)) 的一般定义:即 GAE(\(\lambda\)) 对优势函数的估计为:
    $$
    \hat{A}^{(\lambda)}_t = \sum_{l=0}^{\infty}(\lambda\gamma)^l\delta_{t+l},
    \qquad
    \delta_t = r_t + \gamma V(s_{t+1}) - V(s_t).
    $$
    • 其中
      • \(\delta_t\) 是 \(t\) 时刻的 TD 误差;
      • \(\gamma\) 是折扣因子;
      • \(\lambda\) 是 GAE 平滑参数,\(0\le\lambda\le1\)
  • 令 \(\lambda=1\),得到
    $$
    \hat{A}^{(1)}_t = \sum_{l=0}^{\infty}\gamma^l\delta_{t+l}.
    $$
  • 把 \(\delta\) 的定义代入并做级数求和:即将 \(\delta_{t+l}=r_{t+l}+\gamma V(s_{t+l+1})-V(s_{t+l})\)代入,展开:
    $$
    \begin{aligned}
    \hat{A}^{(1)}_t
    &= \sum_{l=0}^{\infty}\gamma^l\bigl[r_{t+l}+\gamma V(s_{t+l+1})-V(s_{t+l})\bigr] \\
    &= \sum_{l=0}^{\infty}\gamma^l r_{t+l}
    + \sum_{l=0}^{\infty}\gamma^{l+1}V(s_{t+l+1})
    - \sum_{l=0}^{\infty}\gamma^l V(s_{t+l}).
    \end{aligned}
    $$
  • 把第二项的指标平移\(l\to l-1\),得到
    $$
    \sum_{l=0}^{\infty}\gamma^{l+1}V(s_{t+l+1})
    = \sum_{l=1}^{\infty}\gamma^{l}V(s_{t+l}).
    $$
  • 于是
    $$
    \hat{A}^{(1)}_t
    = \sum_{l=0}^{\infty}\gamma^l r_{t+l}
    + \sum_{l=1}^{\infty}\gamma^{l}V(s_{t+l})
    - \sum_{l=0}^{\infty}\gamma^l V(s_{t+l}).
    $$
  • 后两项相减,仅留下\(-V(s_t)\):
    $$
    \hat{A}^{(1)}_t = \sum_{l=0}^{\infty}\gamma^l r_{t+l} - V(s_t).
    $$
  • 右侧\(\sum_{l=0}^{\infty}\gamma^l r_{t+l}\)正是从 \(t\) 时刻开始的折扣回报\(R_t\),因此
    $$
    \hat{A}^{(1)}_t = R_t - V(s_t).
    $$
  • 这就是蒙特卡洛优势估计(Monte-Carlo advantage estimate)

GAE 实现

  • GAE 递归形式推导
    $$
    \begin{align}
    A^{\text{GAE}}_t &= \sum_{l=0}^\infty (\gamma\lambda)^l \delta_{t+l} \\
    A^{\text{GAE}}_t &= \delta_t + \sum_{l=1}^\infty (\gamma\lambda)^l \delta_{t+l} \\
    A^{\text{GAE}}_t &= \delta_t + (\gamma\lambda)\sum_{l=1}^\infty (\gamma\lambda)^{l-1} \delta_{(t+1)+(l-1)} \\
    A^{\text{GAE}}_t &= \delta_t + (\gamma\lambda)\sum_{k=0}^\infty (\gamma\lambda)^{k} \delta_{(t+1)+k} \\
    A^{\text{GAE}}_t &= \delta_t + (\gamma\lambda)A^{\text{GAE}}_{t+1} \\
    \end{align}
    $$
    • 实现代码(来自《动手学强化学习》):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      # 从td_deltas计算各个状态的GAE
      def compute_advantage(gamma, lmbda, td_deltas):
      td_deltas = td_deltas.detach().numpy()
      advantage_list = []
      advantage = 0.0
      for delta in td_deltas[::-1]:
      advantage = gamma * lmbda * advantage + delta # 对应推导的递归形式
      advantage_list.append(advantage)
      advantage_list.reverse()
      return torch.tensor(advantage_list, dtype=torch.float)
      # compute_advantage函数的使用
      td_target = rewards + self.gamma * self.critic(next_states) * (1 - dones)
      td_deltas = td_target - self.critic(states)
      advantage = rl_utils.compute_advantage(self.gamma, self.lmbda, td_deltas.cpu()).to(self.device)

      # 利用advantage更新PPO Agent
      mu, std = self.actor(states)
      action_dists = torch.distributions.Normal(mu.detach(), std.detach())
      # 动作是正态分布
      old_log_probs = action_dists.log_prob(actions)
      for _ in range(self.epochs):
      mu, std = self.actor(states)
      action_dists = torch.distributions.Normal(mu, std)
      log_probs = action_dists.log_prob(actions)
      ratio = torch.exp(log_probs - old_log_probs)
      surr1 = ratio * advantage
      surr2 = torch.clamp(ratio, 1 - self.eps, 1 + self.eps) * advantage
      actor_loss = torch.mean(-torch.min(surr1, surr2))
      critic_loss = torch.mean(F.mse_loss(self.critic(states), td_target.detach()))
      self.actor_optimizer.zero_grad()
      self.critic_optimizer.zero_grad()
      actor_loss.backward()
      critic_loss.backward()
      self.actor_optimizer.step()
      self.critic_optimizer.step()

讨论:GAE 中 \(\lambda\) 的作用(平滑作用)

  • GAE 的优势优势函数计算如下:
    $$ \hat{A}_t = \delta_t + (\gamma\lambda)\delta_{t+1} + (\gamma\lambda)^2\delta_{t+2} + \dots $$
  • 当 \(\lambda = 1\) 时(Monte Carlo) :
    • 中间的 \(V\) 项会相互抵消(Telescoping Sum),最后 \(\hat{A}_t = R_{final} - V(s_t)\)
    • 此时中间的 \(\delta_t\) 确实在数学形式上被“合并”了,退化为单纯的蒙特卡洛回报减去基线
    • 这会导致方差极大,因为 Critic 在中间步骤的判断被忽略了
  • 当 \(\lambda < 1\) 时(GAE 的标准用法) :
    • \(\hat{A}_t\) 变成了 \(\delta_t\) 的加权和,意味着当前的优势函数高度依赖于当前的 \(\delta_t\)
    • 如果模型在第 \(t\) 步犯错,导致 \(V(s_{t+1})\) 下降,\(\delta_t\) 会是巨大的负数
    • 由于 \(\lambda\) 的衰减,这个巨大的负数主要惩罚的是当前动作 \(a_t\),而对 \(a_{t-1}\) 的惩罚会衰减
    • 这正是我们想要的:谁犯错,谁负责
      • 如果没有中间的 \(\delta_t\),模型就不知道到底是哪一步导致了最后的失败

讨论:只有终止状态有非 0 奖励的情况下,GAE 的作用

  • 疑问:在只有最后一个 Token 有奖励时,GAE 中前面的 \(\delta_t\) 实际上都是 \(r_t = 0\),请问这种情况下,我们的 前面的 \(\delta_t\) 是不是没有什么意义?
    • 回答:前面的 \(\delta_t\) 绝对是有意义的,而且很有意义,在稀疏奖励场景下起到了至关重要的“信用分配”(Credit Assignment)作用
  • \(\delta_t\) 的定义是:
    $$ \delta_t = r_t + \gamma V(s_{t+1}) - V(s_t) $$
    • 在 \(r_t=0\) 且 \(\gamma=1\) 的情况下,它简化为:
      $$ \delta_t = V(s_{t+1}) - V(s_t) $$
    • 这意味着 \(\delta_t\) 代表了价值函数(Critic)对当前这一步动作带来的“预期改变”
  • Critic 充当了“密集奖励”的代理人:在只有最终奖励(Sparse Reward)的任务中,环境不说话(\(r_t=0\)),但 Critic(价值网络)一直在说话
    • \(V(s_t)\) :代表在 \(t\) 时刻,Critic 认为最终能拿多少分
    • \(V(s_{t+1})\) :代表在执行了动作 \(a_t\) 后,Critic 认为最终能拿多少分
  • \(\delta_t\) 实际上衡量了动作 \(a_t\) 是“好”还是“坏”
    • 如果 \(a_t\) 是一个关键的好步骤(比如在推理题中迈出了正确的一步),Critic 会觉得“稳了”,\(V(s_{t+1})\) 会比 \(V(s_t)\) 高,此时 \(\delta_t > 0\)
    • 如果 \(a_t\) 是一个致命错误(比如开始胡言乱语),Critic 会觉得“完了”,\(V(s_{t+1})\) 会暴跌,此时 \(\delta_t < 0\)
    • 如果 \(a_t\) 是废话(不影响结果),\(V(s_{t+1}) \approx V(s_t)\),此时 \(\delta_t \approx 0\)
    • \(\delta_t\) 将稀疏的最终奖励,通过 Critic 的预测能力,回传到了每一个具体的 Token 上
一个直观的例子
  • 假设题目是:“1+1 等于几?”
    • Step 1 : 生成 “The”
      • \(V(s_1) \approx 0.9\) (Critic 觉得大概率能答对)
      • \(V(s_2) \approx 0.9\) (生成 “The” 很正常)
      • \(\delta_1 \approx 0\) (无功无过)
    • Step 2 : 生成 “answer”
      • \(V(s_3) \approx 0.95\)
      • \(\delta_2 \approx 0.05\) (稍微有点进展)
    • Step 3 : 生成 “is”
      • \(V(s_4) \approx 0.95\)
      • \(\delta_3 \approx 0\)
    • Step 4 (关键步) : 生成 “three” (错误答案)
      • \(V(s_5) \approx 0.0\) (Critic 意识到这局要输了,因为答案错了)
      • \(\delta_4 = 0 + 1 \cdot 0.0 - 0.95 = -0.95\)
      • 注意:这里出现了巨大的负信号!
    • Step 5 : 生成 <EOS>
      • 最终奖励 \(r=0\) (因为答错了)
      • \(\delta_5 \approx 0\)
  • 注意:正是 Step 4 的 \(\delta_4\) 提供了极强的负反馈,告诉 Policy 网络:“是你把事情搞砸的”
    • 如果没有 \(\delta_t\),模型就只能收到最后那个 0 分,却不知道是哪个词出了问题
  • 在 \(r_t=0\) 的阶段,\(\delta_t\) 的意义在于 “信息增益”“预期的修正”,\(\delta_t\) 是 Critic 告诉 Actor 的一句话:“这一步走完,我对结局的看法改变了多少”:
    • \(\delta_t \approx 0\) : 符合预期,按部就班
    • \(\delta_t \neq 0\) : 出现了转折点(惊喜或惊吓)
  • 理解:正是这些非零的 \(\delta_t\),让 PPO 能够在数千个 Token 的长序列中,精准地找到那些决定成败的关键 Token 进行强化或惩罚!

几种方法辨析

\(\lambda\)-return和TD(\(\lambda\))对比

  • 相同点:
    • \(\lambda\)-return和TD(\(\lambda\))的目标都是在单步TD和完整的蒙特卡罗回报之间找到一个折衷方案。通过调整 \(\lambda\) 值,可以在不同的时间尺度上分配信用,从而实现更有效的学习
    • 当 \(\lambda=0\) 时, \(\lambda\)-return和TD(\(\lambda\))都退化为TD(0),即只考虑一步奖励;而当 \(\lambda=1\) 时,则等价于使用整个episode的回报进行更新
  • 不同点:
    • \(\lambda\)-return是forward的,而TD(\(\lambda\))是backward的,两种视角的对比如下:
    • \(\lambda\)-return是off-line的值更新方法,而TD(\(\lambda\))是on-line的值更新方法
      • 估计值函数时,需要等到episode结束才能开始计算的算法为off-line方法(如 \(\lambda\)-return)
      • 估计值函数时,不需要等到episode结束就可以开始计算的算法称为on-line方法(如TD(\(\lambda\)))
  • 一些总结:
    • TD(\(\lambda\))是 \(\lambda\)-return的一种近似实现( \(\lambda\)-return需要采样到整个episode才可以更新一版)
    • 实际上几乎所有的on-policy方法训练时(比如许多开源实现都是这样)都需要完整完成一次episode再更新,此时直接使用( \(\lambda\)-return更好
    • TD \(\lambda\) 更多地应用于实际问题解决当中,尤其是在那些需要快速响应和持续学习的任务上。由于它可以实时更新模型参数,因此非常适合处理非终止型任务或者非常长的 episodes,在这些情况下等待整个 episode 完成后再做更新可能是不切实际的
      • 问题:这些任务上 GAE 是否也不适用?
  • 其他参考: \(\lambda\)-return和TD(\(\lambda\))的关系

GAE 和 \(\lambda\)-return

  • GAE 和 \(\lambda\)-return的思路基本一致,区别在于 GAE 关注的是优势函数, \(\lambda\)-return关注的是累计回报

附录:对 TD(\(\lambda\))和资格迹的深入理解

  • 整体描述
    • TD(\(\lambda\)) 使用当前时间片的 TD error 更新其他状态的能力源于其引入的资格迹(eligibility traces)机制。资格迹本质上是一个短期记忆向量,它记录了每个权重对于预测当前状态价值的贡献程度。当一个新的 TD error 被计算出来时,这个误差不仅用于直接更新当前状态的价值估计,还会根据资格迹的影响范围去调整之前的状态或动作的价值估计。这种机制允许 TD(\(\lambda\)) 在每一步都进行在线更新,而不需要等待整个 episode 结束
  • 对于资格迹的理解
    • 资格迹的关键在于它可以跟踪哪些状态或状态-动作对在最近被访问过,并且根据这些迹来决定如何分配新的信息。具体来说,每当智能体从一个状态转移到另一个状态并获得奖励时,资格迹会为该状态增加一定的值,同时以 \(\gamma\lambda\) 的速率衰减其他所有状态的资格迹。这里的 \(\gamma\) 是折扣因子, \(\lambda\) 是控制资格迹衰减速率的参数。因此,资格迹提供了一种方式来衡量不同状态之间的关联性,使得较早出现的状态也能受益于后来发生的事件
  • 为何可以用当前时间片的 TD error 更新其他状态?
    • 考虑一个简单的情况:假设我们有一个序列 \(S_0, A_0, R_1, S_1, A_1, R_2, …\),其中 \(S_t\) 表示第 t 步的状态, \(A_t\) 表示采取的动作,而 \(R_{t+1}\) 则是紧接着收到的即时奖励。现在假设我们在时间步 t 处得到了一个新的 TD error:
      $$ \delta_t = R_{t+1} + \gamma V(S_{t+1}) - V(S_t) $$
    • 这里 \(\delta_t\) 反映了当前状态下实际观察到的结果与之前的预期之间的差异。接下来,我们将使用这个误差来更新所有之前状态的价值估计。具体地,如果某个状态 \(S_i\) ( \(i < t\) )曾经出现在这一序列中,并且它的资格迹不为零,那么我们可以按照以下规则更新它的价值:
      $$ V(S_i) \leftarrow V(S_i) + \alpha \delta_t E_i $$
    • 其中 \(\alpha\) 是学习率, \(E_i\) 是状态 \(S_i\) 的资格迹。这样做的效果是,那些更接近当前时间点并且资格迹较高的状态将会受到更大的影响,因为它们被认为对当前的结果负有更多的责任。相反,距离较远或者资格迹较低的状态则只会得到较小幅度的调整
  • 为什么需要TD(\(\lambda\))?
    • TD(\(\lambda\)) 通常会在每次转移后立即更新所有相关状态的价值估计。这意味着算法可以在每一步都利用最新的反馈来改进自己的预测,而不是等到整个 episode 完成后再做一次性的修正。此外,由于资格迹的存在,即使某些状态可能不会再次出现在同一 episode 中,它们仍然有机会基于后续的发展情况进行适当的调整
    • 通过引入资格迹,TD(\(\lambda\)) 不仅能够有效地利用当前时间片的 TD error 来更新其他状态,而且还能够在不显著增加计算复杂度的情况下综合考虑所有步数的预测
    • 这种方法既保持了 TD(0) 的快速响应特性,又继承了 MC 方法在长序列上的准确性优势