ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

逆向强化学习实战:MaxEnt IRL与Deep MaxEnt IRL算法解析及Python实现

逆向强化学习实战:MaxEnt IRL与Deep MaxEnt IRL算法解析及Python实现 简介这套Python/TensorFlow逆向强化学习IRL代码实现面向机器学习与强化学习研究者集中解决了从专家演示中重建奖励函数的难点覆盖线性IRL、最大熵IRL、深度最大熵IRL三种经典与深度方法并配套1D/2D网格世界与值迭代求解器。资源共48个文件以22个Python脚本为核心另含18张训练结果图、模型检查点及说明文档压缩包仅2.58MB便于快速下载与本地实验。目前已有1281人学习浏览。代码入口demo.py可直接运行linear_irl、maxent_irl、deep_maxent_irl等脚本分别对应不同算法实现value_iteration与gridworld模块提供MDP环境与求解基础相应图片展示了奖励图、值函数对比和深度最大熵迭代过程可帮助读者直观理解各IRL算法的收敛效果与差异适合作为算法复现、课程设计或对比实验的参考实现。 自己在实际项目里把逆向强化学习IRL这一套从理论撸到代码完整实现了三个经典算法MaxEnt IRL、Deep MaxEnt IRL 和 LPIRL全程基于 Python 和 TensorFlow 2.x。IRL 的核心问题很简单给一堆专家轨迹但没有奖励函数怎么反推出“专家为什么这么走”。这在模仿学习、机器人轨迹规划、自动驾驶行为建模里都很有用。如果你已经玩过行为克隆想进一步把专家行为“翻译”成可解释的奖励信号这篇文章就是我的完整踩坑记录。1. 核心思路与算法选型1.1 IRL 到底在解决什么问题传统强化学习是给定奖励函数去学策略IRL 正好反过来给定专家轨迹去反推奖励函数。这样做的好处非常直接奖励函数学出来之后不只是能模仿专家轨迹还能泛化到其他任务场景比如同一个奖励函数换一个初始位置策略依然能表现得像专家。IRL 的基础假设是专家在某个 MDP马尔可夫决策过程中执行了近乎最优的策略但我们看不到这个策略只看到它产生的轨迹。目标就是找一组奖励函数的参数让专家轨迹在“从这个奖励函数推导出的最优策略”下有最大的概率被生成。这个过程听起来顺理成章但真正做起来会碰到一个致命问题能解释专家轨迹的奖励函数有无限多个。你给轨迹加一个常数最优策略不变给所有经过的状态都加同样的偏置策略还是不变。所以 IRL 必须通过约束、先验或者熵来从这无限多个解里挑一个合理的。1.2 三种算法的定位差异我这次选的三个算法正好代表了 IRL 三条技术路线直接对比一下算法奖励函数形式核心求解思路典型适用场景MaxEnt IRL线性特征加权最大熵原理 Soft Value Iteration状态空间有限、特征可手工设计的小型环境比如 Grid World、小型机器人路径规划Deep MaxEnt IRL神经网络最大熵原理 深度网络逼近奖励高维状态空间、特征难以手工设计比如图片输入、连续状态LPIRL线性特征加权线性规划让专家策略优于候选策略策略集合可枚举或采样、奖励参数较少的教学演示场景选型逻辑很清晰如果你刚接触 IRL先跑 MaxEnt因为它的每一步都有明确的可视化和数值意义能帮你建立“特征期望”“配分函数”这些直觉如果状态是高维的就上 Deep MaxEnt如果问题规模很小可以枚举候选策略LPIRL 这种线性规划思路反而最可控。2. 环境准备与轨迹数据构造2.1 Python 与 TensorFlow 版本选择我这次的环境是 Python 3.10 TensorFlow 2.12。坦率说TensorFlow 2.10 之后的 API 已经非常稳定tf.GradientTape、tf.function这些接口完全够用。需要装的库不多pip install tensorflow2.12.0 numpy scipy matplotlib有一个容易被忽略的点如果你要跑 Deep MaxEntTensorFlow 和 Python 版本的兼容性一定要提前查好。比如 TensorFlow 2.12 在 Windows 上只支持到 Python 3.11装错版本会出现ModuleNotFoundError: No module named tensorflow这种最折磨人的问题。2.2 构造一个可调试的专家轨迹我用了一个 5x5 的 Grid World 作为测试环境动作空间是上下左右专家策略是先绕开障碍物、再以最短路径走到终点。为了生成专家轨迹我先手工写了一个“已知最优策略”的规则策略然后让智能体按这个策略走 50 条轨迹出来。代码里表示状态的方式比较关键我用的是 one-hot 特征25 个状态就映射成 25 维向量import numpy as np def state_to_features(state_idx, n_states25): feat np.zeros(n_states, dtypenp.float32) feat[state_idx] 1.0 return feat这条轨迹数据会同时喂给 MaxEnt 和 LPIRLDeep MaxEnt 那边虽然也是同样格式但因为它内部有神经网络特征维度可以更大不用手工设计。如果你用的环境状态维度特别高建议先用 one-hot 跑通整个流程再去换复杂特征。3. MaxEnt IRL从最大熵原理到完整实现3.1 为什么是“最大熵”MaxEnt IRL 的核心思想是在满足“专家特征期望 学习到的策略特征期望”这个约束的前提下找一个熵最大的策略分布。直觉上就是不要假设太多额外信息得到一个最“随机”、最不偏不倚的奖励函数。在数学上MaxEnt IRL 把整条轨迹的概率建模成[ P(\tau) \propto \exp\left(\sum_{t} R(s_t, a_t)\right) ]其中 ( R(s_t, a_t) \theta^T f(s_t, a_t) )。也就是说累计奖励越高的轨迹被专家选中的概率越大。这个概率分布用能量模型来解释奖励就是负能量。有了这个概率分布学习目标就很自然了用梯度上升最大化专家轨迹的似然。求梯度时会出现一个配分函数直接用暴力求和会指数爆炸Ziebart 等人给出的解决方案是 Soft Value Iteration在表格型环境中反复迭代以下 Bellman 算子[ V_{soft}(s) \log \sum_{a} \exp(Q_{soft}(s, a)) ][ Q_{soft}(s, a) R(s, a) \gamma \sum_{s} P(s|s, a) V_{soft}(s) ]3.2 Soft Value Iteration 的工程实现这一块是我调试最久的地方因为公式看着简单落到代码里全是矩阵维度和数值稳定性的问题。import tensorflow as tf def soft_value_iteration(reward, transitions, gamma0.99, tau0.01, max_iter1000): # reward: shape [n_states, n_actions] n_states, n_actions reward.shape V tf.zeros(n_states, dtypetf.float32) for _ in range(max_iter): Q reward gamma * tf.reduce_sum(transitions * V[tf.newaxis, tf.newaxis, :], axis-1) # log-sum-exp带 maxtrick 提高数值稳定性 V_new tf.reduce_logsumexp(Q / tau, axis1) * tau if tf.reduce_max(tf.abs(V_new - V)).numpy() 1e-5: break V V_new Q reward gamma * tf.reduce_sum(transitions * V[tf.newaxis, tf.newaxis, :], axis-1) policy tf.nn.softmax(Q / tau, axis1) return V, Q, policy这里我用了 temperature 参数 tau默认设 0.01控制策略的随机程度。tau 越小策略越接近贪心但在求梯度时越容易遇到极小值导致梯度消失所以实践中我是从 0.1 开始收敛后再调低到 0.01。3.3 特征期望计算与权重更新有了 soft policy 之后就可以计算在当前参数下的特征期望def compute_feature_expectation(policy, transitions, init_state, features, horizon20): n_states, n_actions policy.shape state_dist tf.one_hot([init_state], n_states, dtypetf.float32) feat tf.zeros(features.shape[-1], dtypetf.float32) for _ in range(horizon): action_probs tf.matmul(state_dist, policy)[0] feat tf.reduce_sum(action_probs[:, tf.newaxis] * features, axis0) state_dist tf.matmul(tf.transpose(action_probs[:, tf.newaxis] * transitions, [0, 1]), state_dist) return feat然后计算专家轨迹上的平均特征更新权重。完整训练循环大致是def train_maxent_irl(expert_features, transitions, features, lr0.01, epochs100): theta tf.Variable(tf.random.normal([features.shape[-1]], stddev0.01)) for step in range(epochs): with tf.GradientTape() as tape: reward tf.linalg.matvec(features, theta) reward tf.reshape(reward, [n_states, n_actions]) V, Q, policy soft_value_iteration(reward, transitions) policy_feat compute_feature_expectation(policy, transitions, init_state0, featuresfeatures) log_likelihood tf.reduce_sum(expert_feature * theta) - tf.reduce_sum(policy_feat * theta) loss -log_likelihood grads tape.gradient(loss, [theta]) theta.assign_sub(lr * grads[0])4. Deep MaxEnt IRL用神经网络替代手工特征4.1 为什么要上深度版本MaxEnt IRL 最大的瓶颈是奖励函数只能是特征向量的线性函数。在 5x5 的 Grid World 里 one-hot 特征还没问题但换成图像输入手工提取特征基本不现实。Deep MaxEnt IRL 的核心改动是把奖励函数 ( R_\omega(s, a) ) 换成一个神经网络参数从线性权重变成网络权重。训练时每步都需要走一遍 Soft Value Iteration 来求当前网络的策略然后用这个策略计算损失、回传梯度。这听起来很顺但第一次跑的时候我折腾了很久不收敛。问题不在于网络结构而在于“奖励函数的尺度”没法控制。线性模型里权重范围天然有限神经网络输出层一旦没有任何限制奖励值的尺度会乱飘直接导致 Soft Policy 迅速坍缩到确定性策略损失变成 NaN。4.2 核心模型与训练循环我采用的是比较保守的做法奖励网络用两层全连接输出层加上 LayerNorm 来限制输出范围然后再乘一个可学习的缩放系数。这样既保持了表达能力又把奖励值范围控制在稳定区间。class RewardNetwork(tf.keras.Model): def __init__(self, hidden_dim64): super().__init__() self.fc1 tf.keras.layers.Dense(hidden_dim, activationrelu) self.fc2 tf.keras.layers.Dense(hidden_dim, activationrelu) self.out tf.keras.layers.Dense(1) self.ln tf.keras.layers.LayerNormalization() def call(self, states, actions): x tf.concat([states, actions], axis-1) x self.fc1(x) x self.fc2(x) reward self.ln(self.out(x)) return reward训练循环大致结构如下optimizer tf.keras.optimizers.Adam(learning_rate1e-3) for epoch in range(100): with tf.GradientTape() as tape: reward reward_net(states, actions) # 批量计算 reward tf.reshape(reward, [n_states, n_actions]) V, Q, policy soft_value_iteration(reward, transitions) # 计算 soft Q 下的最优策略再计算专家轨迹的负对数似然 log_pi tf.math.log(tf.gather_nd(policy, expert_idx) 1e-8) loss -tf.reduce_mean(log_pi) grads tape.gradient(loss, reward_net.trainable_variables) optimizer.apply_gradients(zip(grads, reward_net.trainable_variables))这里每一步都要跑一次完整的 Soft Value Iteration所以如果你的环境维度太大训练会非常慢。我的经验是先在 5x5 的小环境里把网络结构调通再考虑扩大规模。4.3 训练不收敛的三个关键修正第一个是输出层不要加sigmoid或tanh不然梯度很容易消失奖励值也表达不了负收益。第二个是temperature要从大往小调不要一开始就用 0.01我一般从 1.0 起步每 20 轮降一半。第三个是奖励网络的学习率要比普通监督学习小一个量级因为损失信号经过 Soft Value Iteration 传递过来本身波动就大学习率太大很容易震荡。5. LPIRL线性规划求解奖励函数5.1 线性规划建模思路LPIRL 的思路和最大熵完全不同它不假设概率分布而是直接利用 IRL 的定义如果专家策略是最优的那么专家策略的累计回报应该大于等于任意其他策略的累计回报。给定候选策略集合 ( \Pi {\pi_1, \pi_2, \dots, \pi_m} )其中包含专家策略 ( \pi_E )我们要找一个奖励权重 ( w )使得[ V_{E}(w) \geq V_{i}(w) \quad \forall \pi_i \in \Pi ]其中 ( V_\pi(w) E_\pi[\sum_t w^T f(s_t, a_t)] )。每个约束都是关于 ( w ) 的线性不等式所以整个问题可以转成线性规划。为了从无限多个解里挑出稳定的解我加了两个额外目标让 ( w ) 的 L2 范数尽可能小同时最大化“专家策略超过其他策略的平均边际”。这两个目标本身也是线性的可以直接塞进线性规划里。5.2 用 SciPy 实现核心求解构造候选策略集合是最麻烦的部分。我直接枚举了 Grid World 里所有确定性策略但现实任务里不可能这样一般会用随机采样策略或者用一些探索性策略代替。from scipy.optimize import linprog def solve_lpirl(expert_feature, candidate_features, eps1e-3): # 变量: [w_0, w_1, ..., w_{d-1}, slack_0, ..., slack_{m-1}] d expert_feature.shape[0] m candidate_features.shape[0] c np.zeros(d m) c[d:] -1.0 # 最大化 slack等价于最小化 -slack A_ub [] b_ub [] for i in range(m): row np.zeros(d m) row[:d] candidate_features[i] - expert_feature row[d i] 1.0 A_ub.append(row) b_ub.append(-eps) A_ub np.array(A_ub) b_ub np.array(b_ub) res linprog(c, A_ubA_ub, b_ubb_ub, bounds[(None, None)] * d [(0, None)] * m, methodhighs) return res.x[:d], res.fun这个实现里有一个很容易忽略的坑候选策略集合里必须包含专家策略本身。如果你的采样策略里没有覆盖到专家策略线性规划会有多个可行解求解器返回的结果会非常依赖初始值。5.3 LPIRL 与 MaxEnt 的对比观察我同样在 5x5 Grid World 上跑了 LPIRL最后得到的奖励权重热力图和 MaxEnt 的结果比较接近但训练过程明显更“脆”。MaxEnt 是梯度上升每一步都平滑地优化期望LPIRL 则是一锤子买卖约束稍微没构造好解出来的奖励就会非常离谱。比如我一开始忘了加 L2 正则项解出来的权重达到了几千完全没法用。后来在目标函数里加了权重的 L2 项才把尺度拉回正常范围。6. 常见问题与排查技巧实录6.1 MaxEnt 训练日志里 loss 变成 NaN这是我最常碰到的问题十次里有八次是 Soft Value Iteration 的数值溢出。reduce_logsumexp能解决一部分问题但因为 Grid World 的转移矩阵是稀疏的某些状态在迭代过程中V会一直减小最后变成-inf。我在V上加了上限钳制比如限制到 [-100, 100]效果非常明显。另一个常见原因是梯度更新步长太大直接把权重推出稳定范围。6.2 Deep MaxEnt 训练慢得无法忍受Deep MaxEnt 的每一步都要跑 Soft Value Iteration在小环境里还行一旦状态空间变大迭代次数和网络训练的耗时都会飞涨。解决办法是把 Soft Value Iteration 的迭代次数上限设小一点比如 50 次训练前期不需要那么高的精度后面再加大。还有一个经验是与其每轮更新一次网络参数不如攒一批专家轨迹多更新几次收敛速度反而更快。6.3 TensorFlow 版本相关的奇怪报错如果你在跑代码时遇到类似No module named tensorflow.python.ops.numpy_ops这种报错先检查版本是不是太老建议直接升到 2.10 以上。另一个高频报错是tf.gather_nd索引维度不匹配因为expert_idx的形状必须和policy的维度一致建议在传参前打印一下expert_idx.shape和policy.shape绝大多数问题一眼就能看出来。6.4 专家轨迹数量少导致奖励严重偏向专家轨迹只有十几条的时候学出来的奖励函数经常会出现“只认轨迹经过的状态其他状态随意”的情况。这个问题的根源不是算法本身而是特征表达不够平滑。我后来在特征上加了一个高斯核平滑把 one-hot 特征替换成每个状态与终点之间的距离特征效果立竿见影。这说明 IRL 对特征设计的敏感度比我想象中高得多。7. 我自己在实践中积累的几条经验最后分享三个我一直在用的实操技巧少踩一个是一个。第一所有 IRL 算法实现的检查顺序都是先看策略分布的形状再看损失曲线最后再看奖励值。如果策略分布很快就变到完全确定性先怀疑 temperature 是不是太低如果损失是平的先怀疑 Soft Value Iteration 有没有收敛而不是怀疑网络结构。第二专家轨迹的质量比数量重要得多。我试过用 100 条噪声很大的轨迹效果远不如 20 条非常干净的最优轨迹。这也符合 IRL 的概率假设它是在给“专家”建模不是给“平均水平”建模。第三当行为克隆能轻松解决这个问题时不要为了炫技而强行用 IRL。行为克隆是监督学习速度快、稳定IRL 的优势在于多任务泛化和奖励可解释性。如果需求只是“模仿轨迹”行为克隆一天就能搞定IRL 可能要一周。只有在需要“解释专家为什么这么做”或者“把奖励迁移到新环境”时IRL 才真正值得投入。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表