夜市美食街的尽头,老王摆了个炒面摊。他最大的苦恼是调料配比——盐多一勺、糖少半勺,客人的回头率就天差地别。他不可能挨个问遍全城食客,只能先按经验调一版配方,做一批试吃装请路人品尝,看谁回头、谁皱眉,再根据反馈微调。试的版本多了,味道自然向最受欢迎的方向靠拢。AI 里的 REINFORCE 算法,本质就是这套“试吃-反馈-微调”的循环:智能体先按当前策略“炒”出一串动作,观察环境给出的回报,再沿着回报更好的方向调整策略。 本文不跳步,从目标函数开始,把 log 梯度技巧、折扣回报、baseline 降方差、蒙特卡洛估计一个个推给你看,最后落到可运行的代码。读完你会明白,那些看着高深的策略梯度公式,其实就是摊主手里那本“配方改进记录”。
掌握 REINFORCE 是理解 PPO、A3C 等现代强化学习算法的地基。用数学的眼光看透“如何试、如何改”,对机器人控制、游戏 AI、大模型对齐等方向都极为关键。
可视化演示
展示REINFORCE算法核心:轨迹采样(状态-动作-奖励序列)、log梯度技巧推导链与Baseline方差降低,动态对比策略优化过程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
轨迹采样:蒙特卡洛策略评估 靛蓝为Agent(智能体) 浅靛为轨迹(状态-动作序列) 绿色为回报(Reward) tau = (s0,a0,r1,s1,a1,r2,...,sT) Agent与环境交互采集多条轨迹,每条轨迹包含状态动作奖励序列用于估计策略梯度
目标函数定义
老王炒面摊每天打烊后,会翻一翻账本:今天卖出几份、回头客几个。这个“生意数字”虽然每天有波动,但平均下来,就是这版配方的真实水平。REINFORCE 也一样,第一步先要有一个能衡量的“生意数字”,也就是目标函数。智能体在环境里走完一条轨迹 \(\tau=(s_0,a_0,r_0,s_1,\dots)\),从环境拿到的总回报越高,说明当前策略越好。
环境给智能体的反馈,由策略 \(\pi_\theta\) 决定,这里的 \(\theta\) 是策略的参数,好比配方的咸度、辣度旋钮。我们希望找到一个 \(\theta\),让“平均生意得分” \(J(\theta)\) 最大。在强化学习的标准设定中,这个目标被写成“轨迹回报关于轨迹分布取期望”。
有了评分标准,老王才知道改配方该往左还是往右。接下来看看配方上的“旋钮”——策略——是怎么长出来的。
- 轨迹采样\(\tau\sim\pi_\theta\) 表示轨迹由当前策略生成,相当于“用这版配方做一批试吃装”。
- 期望的含义\(\mathbb{E}\) 是对无数条轨迹取平均,相当于“长期统计回头客比例”。
- 折扣累计回报\(\sum_t\gamma^{t}r_t\) 把每一步奖励按时序加权求和,越往后的奖励权重越小。
策略参数化
老王把调料抽象成两个旋钮:咸度和辣度。旋钮转得是否顺滑,决定了微调配方时手感好不好。策略也一样,必须参数化、可求导,才能做梯度上升。对离散动作空间,最常用的是 Softmax 策略:神经网络给每个动作打一个分数,再通过指数和归一化变成概率。
公式里 \(h_\theta(s,a)\) 是“打分函数”,它输出动作 \(a\) 在状态 \(s\) 下的得分。指数函数把分数差距放大,分母把分数归一化成概率。这样无论原始分数是多少,输出的概率总和永远是 1,而且处处可导。
对于连续动作(比如机械臂的力矩),可以换成高斯策略:网络输出均值和方差,动作从高斯分布中抽样。参数可微这一点至关重要,因为下一节的 log 梯度技巧必须建立在可求导的基础上。旋钮顺滑了,老王才敢轻轻拧、慢慢试。
- 打分函数\(h_\theta(s,a)\) 给状态 \(s\) 下的每个动作 \(a\) 一个实数分数,分数越高越可能被选。
- 指数放大\(e^{x}\) 让正分被放大、负分被压缩,拉开动作之间的差距。
- 归一化分母对所有动作的指数求和,保证概率之和为 1。
log梯度技巧
老王想知道“改了配方,回头客变动多少”。与其直接问顾客“你对这版有多满意”(绝对值),不如观察“客人夹菜频率的相对变化”(变化率)。log 梯度技巧做的就是这件事:把“绝对概率的梯度”变成“相对变化率”。
这条“零期望恒等式”后面推导 baseline 无偏性时还会用到。对老王来说,它就像“平均回头率不变的基准日”——扣掉这个基准,才能看出配方本身的真实影响力。
- 对数函数的导数\(\frac{d}{dx}\log f(x)=\frac{f'(x)}{f(x)}\),这是微积分基本结论。
- 代入策略把 \(f\) 换成 \(\pi_\theta(a|s)\),就得到策略的对数导数。
- 零期望性质因为概率归一化 \(\sum_a\pi_\theta(a|s)=1\),两边对 \(\theta\) 求导可得一个关键恒等式。
梯度推导
老王想算“生意平均分对配方的敏感度”。期望里嵌套着复杂的轨迹分布,直接求导很麻烦,但用 log 技巧可以把梯度“搬进”期望内部,变成可以靠试吃采样来估计的形式。
先对 \(J(\theta)\) 的定义展开:轨迹分布的梯度乘以回报,再对全部轨迹积分。用对数导数把 \(\nabla_\theta p_\theta(\tau)\) 改写成 \(p_\theta(\tau)\nabla_\theta\log p_\theta(\tau)\),梯度就进入了期望号内部。接着把轨迹概率分解,消去与 \(\theta\) 无关的环境动态项,只留下策略项的梯度求和。
推导完成后,梯度公式里还藏着两个待解释的量:折扣回报 \(G_t\) 和方差问题。下一节先解剖 \(G_t\)。
- 对数导数转换把对轨迹分布的梯度改写为“分布 × log 分布的梯度”。
- 轨迹概率分解轨迹概率拆成初始状态分布、策略、环境转移三部分连乘。
- 消去环境项初始分布和状态转移都不含 \(\theta\),求导后为零,只剩策略项。
- 因果回报替换第 \(t\) 步的动作只影响当下和未来的回报,因此用 \(G_t\) 替代全程回报。
回报与折扣
老王有个朴素经验:今天的回头客比上周的回头客更有参考价值。一周前的口碑已经衰减了大半,折扣因子 \(\gamma\) 就是用来量化“记忆衰减速度”的。\(G_t\) 叫作折扣累计回报:从第 \(t\) 步开始,把未来每一步奖励乘上 \(\gamma^k\) 再求和。
选择 \(\gamma\) 不只是调参,它直接定义了你追求的是“短期生意”还是“百年老店”。老王决定多重视昨天的问卷,本质上是把“口味偏好”写进了目标函数。
- 从当前步开始下标 \(t\) 表示站在第 \(t\) 步往后看,\(k\) 是相对步数。
- 折扣幂次每一步奖励乘以 \(\gamma^k\),时间越远,权重指数级衰减。
- 贴现因子\(\gamma\in[0,1]\),\(\gamma=0\) 时只看立即奖励,\(\gamma\to1\) 时长远与近期同等重要。
基线降方差
下雨天顾客少,不代表你的配方变差了。如果直接拿绝对回头率当反馈,天气这个“环境因素”会带来巨大的噪声。解决办法是减去一条“基准线”:不管晴天雨天,先比基准高还是低。策略梯度里的 baseline 正是这个道理。
给每一步的回报 \(G_t\) 减去一个与动作无关的基线 \(b(s_t)\),期望不变,但方差下降。无偏性的数学根源,就是上一节那条零期望恒等式:\(\nabla_\theta\log\pi_\theta(a|s)\) 在策略分布下的期望为零,所以乘上任何只依赖状态的 \(b(s_t)\) 再取期望,结果依然是零。
实践中常用状态价值函数 \(V(s_t)\) 近似最优基线,或者干脆把一批轨迹的回报做标准化。老王记住了这个道理:扣掉“天气分”,才能看清配方的真实水平。
- 基线项\(b(s_t)\) 是只依赖当前状态的函数,可以是常数、状态价值或回报均值。
- 无偏性验证对任意 \(b(s_t)\),由零期望性质可得交叉项期望为零。
- 最优基线使方差最小的基线是回报关于梯度平方的加权平均。
蒙特卡洛估计
老王不可能问遍全城人,请十个路人试吃,取平均,就是很有参考价值的估计。REINFORCE 里的期望同样没法精确计算,只能用有限条轨迹的样本均值来近似。这就是蒙特卡洛估计。
完整算法流程是:用当前策略采样若干条轨迹 → 计算每步的 \(G_t\) 和 baseline → 用上式估计梯度 → 沿梯度方向更新参数 \(\theta\) → 重新采样。注意这是 on-policy 方法:每次更新完,旧轨迹就作废了,必须用新配方重新试吃。
- 采样 N 条轨迹每条轨迹 \(i\) 由当前策略独立生成,相当于 \(N\) 位路人独立试吃。
- 逐时刻求梯度对每条轨迹的每个时刻,计算对数概率梯度乘以减基线后的回报。
- 取平均把所有样本的贡献加总除以 \(N\),得到梯度的无偏估计。
代码实现要点
老王把每次改动的配方和结果记在小本本上,防止手抖改过头。代码里的自动微分就是这本“配方笔记”——你只需要定义好损失函数,PyTorch 会自动算出我们推了一整场的 \(\nabla_\theta\log\pi\)。
实现 REINFORCE 有三个实用要点。第一,用自动微分:构造损失 \(\mathcal{L}=-\sum\log\pi_\theta(a|s)(G-b)\),对损失做反向传播,等价于对目标函数做梯度上升。第二,回报标准化:把整局回报减去均值、除以标准差,这是一种简单粗暴却有效的 baseline,能让正负样本对比鲜明。第三,熵正则:在损失里加上策略熵的负项,惩罚过早锁定次优配方,鼓励智能体持续探索。
采样时用 Categorical 分布按概率抽取动作;一个 batch 内把所有时间步的损失加起来再统一更新,梯度更稳定;学习率要适中,太大容易震荡,太小收敛过慢。把这些细节组合起来,一套完整的 REINFORCE 就落地了。老王的小本本终于进化成了自动化的“炒面配方优化器”。
AI 应用场景
游戏AI训练
阶段:策略学习 / 模型训练
解决的问题:传统游戏 AI 靠人工写规则或搜索算法,动作空间稍大就写不过来。REINFORCE 让 AI 从零开始试错,根据整局得分调整“每个状态下选动作的概率”,不需要人工标注最优动作。
具体案例:训练 CartPole 平衡小车的智能体。AI 每局随机尝试左右推力的时机,拿到整局步数作为回报,REINFORCE 用“表现好的动作更可能被重复”的原则更新策略网络,几百回合后小车就能稳稳保持平衡。
推荐系统冷启动策略
阶段:在线学习 / 策略优化
解决的问题:新用户没有历史行为数据,传统推荐只能推热门内容,难以个性化。把“展示哪个物品”当作动作、“点击或购买”当作回报,用 REINFORCE 在线试错,逐步学会对陌生用户展示更合适的物品。
具体案例:某购物 App 对新用户随机展示几个品类,24 小时内统计点击率,REINFORCE 根据反馈更新展示策略,让新客首单转化率明显提升。
机器人操作学习
阶段:控制策略训练
解决的问题:机械臂抓取动作难以手工设计,关节力矩参数繁多。把力矩当作连续动作(高斯策略),整轮抓取成功率当作回报,REINFORCE 一次次尝试,逐步调整动作分布的均值和方差。
具体案例:科研团队让机械臂学习把积木推到目标点,每轮只需真实回报(是否到位),REINFORCE 数百轮后找到稳定的力矩模式,可作为 PPO 等高级算法的初始化策略。
Python 演示
import gymimport torchimport torch.nn as nnimport torch.optim as optim class PolicyNet(nn.Module): def __init__(self, state_dim=4, action_dim=2): super(PolicyNet, self).__init__() self.net = nn.Sequential( nn.Linear(state_dim, 128), nn.ReLU(), nn.Linear(128, action_dim), nn.Softmax(dim=-1) ) def forward(self, x): return self.net(x) def compute_returns(rewards, gamma=0.99): returns = [] g = 0 for r in reversed(rewards): g = r + gamma * g returns.insert(0, g) return returns def main(): env = gym.make('CartPole-v1') policy = PolicyNet() optimizer = optim.Adam(policy.parameters(), lr=1e-3) for episode in range(300): state, _ = env.reset() states, actions, rewards = [], [], [] done = False while not done: probs = policy(torch.FloatTensor(state)) dist = torch.distributions.Categorical(probs) action = dist.sample().item() next_state, reward, terminated, truncated, _ = env.step(action) done = terminated or truncated states.append(torch.FloatTensor(state)) actions.append(action) rewards.append(reward) state = next_state returns = torch.FloatTensor(compute_returns(rewards)) returns = (returns - returns.mean()) / (returns.std() + 1e-8) loss = [] for s, a, ret in zip(states, actions, returns): prob = policy(s) log_prob = torch.log(prob[a] + 1e-8) loss.append(-log_prob * ret) loss = torch.stack(loss).sum() optimizer.zero_grad() loss.backward() optimizer.step() if episode % 50 == 0: print(f"Episode {episode}, total reward: {sum(rewards)}") if __name__ == '__main__': main()
g = r + gamma * g从最后一步向前折算折扣回报,等价于公式里的 \(G_t=\sum\gamma^k r_{t+k}\)。dist = torch.distributions.Categorical(probs)把概率向量包装成可采样的分布对象,对应“试吃采样”环节。action = dist.sample().item()按概率随机抽一个动作,这是策略随机性的体现。returns = (returns - returns.mean()) / (returns.std() + 1e-8)回报标准化,相当于给整局成绩做相对比较,是 baseline 的简化版。loss.append(-log_prob * ret)最小化“负对数概率 × 回报”,等价于对目标函数做梯度上升。loss.backward()自动微分,PyTorch 替我们算出了推导中的 \(\nabla_\theta\log\pi\) 那一串。
常见误区
误区我直接把 \(p(a|s)\) 和回报相乘求均值不就行了吗?为什么要多此一举加个 log?
正解直接使用概率 \(p\) 作为权重,相当于在期望里缺少了 \(1/p\) 的校正因子,得到的估计不再等于真实梯度,是有偏的。log 技巧通过 \(\nabla p = p\nabla\log p\) 把梯度“搬”进期望,让 \(\nabla J=\mathbb{E}[\nabla\log\pi\cdot G]\) 严格成立。就像评价配方时如果只看“被选中的动作概率”,忽略概率本身会被归一化,就会把采样噪声当成真实信号。
为何常见:直觉上“概率越大权重越大”听起来很合理,但忽略了概率分布需要归一化,梯度必须作用于参数而非采样频率。
误区教程里都写 baseline 用 \(V(s)\),是不是只有这一种选择?
正解任何与动作 \(a\) 无关的 \(b(s)\) 都合法,常数、回报均值、状态价值都可以。无偏性只要求“与动作无关”,\(V(s)\) 只是最常用的近似。最优基线是梯度平方加权的平均回报,但实践中标准化回报已经够用。
为何常见:看到主流框架都写 \(V(s)\),误以为它是唯一选项,其实那只是工程上的便利选择。
误区\(\gamma<1\) 只是让计算方便,去掉它 REINFORCE 也能训练。
正解\(\gamma\) 定义在回报 \(G_t\) 当中,它直接决定目标函数的含义——你要的是短期收益还是长期收益。去掉它,无限时域任务的回报可能发散,策略也会忽视眼前反馈。它不是锦上添花的技巧,而是目标函数的一部分。
为何常见:\(\gamma\) 只以超参数形式出现在公式里,容易被当成正则化或加速手段。
误区既然所有回报都大于 0,把每个动作都加强不就行了吗?那 baseline 是不是多余?
正解即使回报全为正,梯度期望依然无偏,但每个被采样到的动作都会被强化,未被采样的动作被相对削弱,导致方差很大、训练剧烈震荡。减去 baseline 能让正负样本对比分明,学习更稳定。这就像雨天顾客少时,扣掉天气影响才能看出配方好坏。
为何常见:直觉上“正回报都该鼓励”忽略了采样带来的相对比较需求。
面试题
Q1. REINFORCE 算法的核心数学技巧是什么?
- A.用Q函数逼近最优价值函数
- B.用log概率替换直接概率来估计梯度← 正确答案
- C.用经验回放提升数据效率
- D.用TD目标降低计算开销
答案:B。REINFORCE 用 \(\nabla p=p\nabla\log p\) 把梯度搬进期望,使梯度估计无偏。A 是 DQN 的思路,C 是 DQN/PPO 常用手段,D 是时序差分类方法的特征,都不是 REINFORCE 的核心技巧。
Q2. 在 REINFORCE 中,轨迹按什么分布采样?
- A.当前策略π_θ← 正确答案
- B.随机均匀策略
- C.最优策略π*
- D.固定行为策略β
答案:A。目标函数 \(J(\theta)\) 是当前策略 \(\pi_\theta\) 下的期望回报,所以轨迹必须来自当前策略。用别人的配方来评价自己的生意,梯度就会偏离真实方向。
Q3. 折扣因子γ的主要作用是什么?
- A.加速神经网络训练
- B.权衡近期与远期奖励← 正确答案
- C.控制策略的探索程度
- D.调节Baseline大小
答案:B。\(\gamma<1\) 时越远的奖励权重越小,\(\gamma\) 越接近 1 越看重长远收益。它直接定义目标函数的含义,不负责加速训练、控制探索或调节基线。
Q4. 为何 REINFORCE 被归为蒙特卡洛方法?
- A.它用完整轨迹回报估计梯度← 正确答案
- B.它用神经网络逼近价值
- C.它使用经验回放机制
- D.它每一步都做自举更新
答案:A。REINFORCE 必须等整条轨迹结束、拿到真实回报 \(G_t\) 后才更新,这是蒙特卡洛的典型特征。B 是价值近似,C 是 DQN 特征,D 是 TD 方法的自举特征。
Q5. baseline 为什么不会让梯度估计产生偏差?
- A.因为baseline也可学习
- B.因为减基线的期望为零← 正确答案
- C.因为baseline总是为零
- D.因为用了重要性采样
答案:B。由概率归一化条件 \(\sum_a\pi=1\) 求导可得 \(\mathbb{E}[\nabla\log\pi]=0\),所以与动作无关的 \(b(s)\) 乘以 \(\nabla\log\pi\) 后取期望仍为零。这就是“减基线不改变无偏性”的数学根源。
Q6. 若不用 log 技巧而直接用 p(a|s) 作为权重,梯度估计会怎样?
- A.仍然无偏但方差大幅增大
- B.有偏差不再等于真实梯度← 正确答案
- C.梯度完全无法进行计算
- D.退化成Q学习更新规则
答案:B。直接使用 \(p\) 相当于缺少了 \(1/p\) 的校正因子,期望不再等于 \(\nabla J(\theta)\),所以有偏。这个 \(1/p\) 正是 log 技巧自动引入的校正项。
Q7. 推导 \(\nabla J=\mathbb{E}[\sum_t\nabla\log\pi(a_t|s_t)G_t]\) 中,去掉过去回报的数学依据是什么?
- A.价值函数必然收敛
- B.动作与过去回报条件独立← 正确答案
- C.折扣因子必须趋于1
- D.策略梯度方差最小化
答案:B。给定当前状态与历史后,动作 \(a_t\) 对过去的奖励没有因果影响,条件期望中 \(\nabla\log\pi(a_t|s_t)\) 与 \(r_k(k<t)\) 的交叉项期望为零,所以只剩从 \(t\) 时刻往后的 \(G_t\)。
Q8. 用有限样本估计策略梯度时,N条轨迹的均值估计量具有什么性质?
- A.有偏但方差较小
- B.无偏方差随N减小← 正确答案
- C.有偏方差随N增大
- D.无偏且方差为零
答案:B。样本均值的期望等于真实梯度,所以无偏;方差正比于 \(1/N\),轨迹越多估计越稳定,但永远不为零。一张问卷很随机,问一百个人平均后就稳定了。
Q9. 以下哪些方法可以降低 REINFORCE 的方差?
- A.使用与动作无关的基线← 正确答案
- B.增加每次采样轨迹数目← 正确答案
- C.移除折扣因子γ
- D.对回报做标准化处理← 正确答案
答案:ABD。baseline 和回报标准化都能减去“平均水平”,缓解奖励正偏置;多条轨迹平均直接降低估计方差。去掉折扣因子不会降低方差,反而会放大远期噪声。
Q10. 关于恒等式 \(\mathbb{E}[\nabla\log\pi_\theta(a|s)]=0\),正确的说法是?
- A.由概率归一化条件求导← 正确答案
- B.它是基线无偏性基础← 正确答案
- C.说明策略梯度始终为零
- D.说明梯度上升完全无效
答案:AB。\(\sum_a\pi=1\) 两边对 \(\theta\) 求导便得此恒等式,它是 baseline 无偏性的数学根基。C 和 D 把恒等式误读为“梯度消失”,实际上它只说明“平均变化率”为零,不等于梯度上升无效。