简介这份资源是面向强化学习与逆向强化学习IRL方向学习者与研究人员的一套示例代码教程重点解决从专家演示中反推奖励函数这一核心问题适合具备Java编程基础、希望深入理解IRL算法实现的中高级读者。压缩包共497个文件以484个Java源码为主体辅以少量xml配置、pom构建文件与jar依赖库整体约2.29MB结构紧凑、便于直接导入IDE运行调试。内容涵盖学徒学习ApprenticeshipLearning、网格世界域建模、多智能体性能绘图以及SCPSolver等求解器集成并附带了作者为搭建IRL框架而修改过的BURLAP代码库快照读者可借此完整复现从环境建模到奖励推断的实验流程理解IRL与BURLAP框架的衔接方式。目前已有1046人学习下载适合作为课程设计、科研入门或算法复现的参考素材。1. 逆强化学习教程代码拆解从演示轨迹反推奖励函数的完整路径逆强化学习IRL解决的是一个很实际的问题你手头有一堆专家演示轨迹但不知道背后的奖励函数长什么样想让机器自己把这个奖励函数猜出来。这份 IRLTutorial 示例代码就是干这个的——它把 IRL 的核心流程拆成可运行的模块从轨迹采样、特征构造到奖励函数迭代求解再到策略评估每一步都有对应的实现。适合已经了解强化学习基础、想动手跑通 IRL 流程的开发者也适合需要把 IRL 用到机器人示教、自动驾驶决策复现等场景的从业者。代码结构不复杂但里面有几个参数和收敛判断的细节不踩一遍不容易注意到。2. 环境搭建与核心模块拆解先跑通再改参数2.1 依赖安装与目录结构确认拿到代码包后第一件事不是急着跑主程序而是先确认依赖版本。IRL 类项目对数值计算库的版本比较敏感尤其是 numpy 和 scipy版本差一个大版本矩阵求解的结果可能就对不上。# 建议用虚拟环境隔离避免和系统 Python 冲突 python -m venv irl_env source irl_env/bin/activate # Windows 下用 irl_env\Scripts\activate # 安装核心依赖版本按代码包里的 requirements 来 pip install -r requirements.txt # 如果 requirements 没锁版本我一般会手动固定这几个 pip install numpy1.24.3 scipy1.10.1 matplotlib3.7.1装完之后先看目录结构。常见做法是envs/放环境定义irl/放算法核心utils/放轨迹处理和特征提取main.py或run.py是入口。先别改任何代码直接跑一遍默认配置确认能出结果。# 先跑默认配置观察输出是否正常 python main.py --env gridworld --algo maxent --iterations 50这一步的目的是建立基线。如果默认配置都跑不通后面调参就是瞎调。跑通之后记录下默认的收敛曲线和最终奖励函数的值作为后续对比的参照。2.2 轨迹采样与特征构造逻辑IRL 的输入是专家轨迹代码里通常有一个generate_expert_trajectories或类似的函数。这里的关键是特征函数的设计——它决定了奖励函数能表达多复杂的偏好。# 典型的特征构造把状态映射成特征向量 def state_features(state, env): 将环境状态转换为特征向量。 常见做法是 one-hot 编码 手工特征拼接。 # 基础 one-hot每个状态一个维度 one_hot np.zeros(env.n_states) one_hot[state] 1.0 # 手工特征比如到目标的距离、是否靠近障碍物 dist_to_goal env.distance_to_goal(state) near_obstacle env.is_near_obstacle(state) # 拼接后返回维度 n_states 2 return np.concatenate([one_hot, [dist_to_goal, near_obstacle]])这段代码的逻辑是奖励函数被参数化为R(s) w^T * phi(s)其中phi(s)就是特征向量w是要求解的权重。特征设计得好不好直接决定 IRL 能不能恢复出有意义的奖励。如果只用 one-hot那奖励函数就是每个状态一个独立值表达能力强但容易过拟合加入手工特征相当于引入先验泛化会好一些但特征选得不对就会引入偏差。参数方面n_states是状态总数distance_to_goal和is_near_obstacle是环境提供的辅助函数。改特征的时候注意维度要对齐否则矩阵运算会报错。2.3 最大熵 IRL 的迭代求解流程代码里最核心的部分是最大熵 IRL 的求解循环。它的思路是在当前奖励函数下计算最优策略然后用这个策略产生的状态访问频率去匹配专家轨迹的状态访问频率不匹配就调整奖励权重。# 最大熵 IRL 主循环简化版 def maxent_irl(expert_trajectories, env, feature_fn, lr0.01, n_iters100): 最大熵逆强化学习求解。 lr: 学习率控制每次权重更新的步长 n_iters: 迭代次数太少不收敛太多浪费时间 # 初始化权重 weights np.zeros(feature_fn(0, env).shape[0]) # 计算专家轨迹的特征期望 expert_feature_exp compute_feature_expectation(expert_trajectories, feature_fn, env) for i in range(n_iters): # 1. 用当前权重构造奖励函数 reward_fn lambda s: np.dot(weights, feature_fn(s, env)) # 2. 用当前奖励求最优策略值迭代或策略迭代 policy value_iteration(env, reward_fn) # 3. 用策略采样轨迹计算模型的特征期望 model_trajectories sample_trajectories(policy, env, n_trajlen(expert_trajectories)) model_feature_exp compute_feature_expectation(model_trajectories, feature_fn, env) # 4. 梯度更新专家期望 - 模型期望 grad expert_feature_exp - model_feature_exp weights lr * grad # 5. 打印收敛信息 if i % 10 0: print(fIter {i}, grad norm: {np.linalg.norm(grad):.4f}) return weights这段代码有几个关键点。第一compute_feature_expectation计算的是轨迹上特征的平均值专家轨迹和模型轨迹要用同一套特征函数。第二梯度方向是专家期望 - 模型期望直观理解就是如果某个特征在专家轨迹里出现得比模型轨迹多就提高它的权重。第三学习率lr不能太大否则权重会震荡也不能太小否则迭代 100 次还没收敛。我一般会先跑 50 次看梯度范数的变化如果还在明显下降就加到 200 次。value_iteration是内层循环每次迭代都要跑一遍所以整体计算量不小。如果环境状态多建议用向量化实现别用 Python 循环逐状态更新。3. 奖励函数恢复效果验证怎么判断跑出来的结果是对的3.1 用策略性能做间接验证IRL 没有 ground truth 奖励函数可以对比如果有的话就不用逆强化学习了所以验证只能间接来。最常用的方法是把学到的奖励函数拿去重新做正向强化学习看得到的策略在真实环境里表现如何。# 验证流程用学到的奖励训练策略在真实奖励下评估 def validate_reward(learned_weights, env, feature_fn, true_reward_fn, n_eval100): learned_weights: IRL 学到的权重 true_reward_fn: 真实奖励函数仅用于评估不参与训练 n_eval: 评估回合数 # 用学到的奖励构造奖励函数 learned_reward_fn lambda s: np.dot(learned_weights, feature_fn(s, env)) # 用学到的奖励训练策略 learned_policy value_iteration(env, learned_reward_fn) # 在真实奖励下评估策略 total_return 0.0 for _ in range(n_eval): traj sample_trajectories(learned_policy, env, n_traj1)[0] total_return sum(true_reward_fn(s) for s in traj) avg_return total_return / n_eval print(fAverage return under true reward: {avg_return:.2f}) return avg_return这个验证的逻辑是如果学到的奖励函数和真实奖励函数足够接近那么基于学到的奖励训练出来的策略在真实奖励下也应该表现不错。如果策略性能很差说明奖励函数恢复得不对需要回头检查特征设计或迭代过程。参数n_eval一般设 100 以上太少的话方差大看不出差异。true_reward_fn只在评估时用训练过程中不能泄露给 IRL 算法否则就是作弊了。3.2 特征权重的可视化与对比除了策略性能还可以直接看学到的权重向量。如果特征设计得有意义权重的符号和相对大小应该能反映专家的行为偏好。# 可视化权重和真实权重对比如果已知 import matplotlib.pyplot as plt def plot_weights(learned_weights, true_weightsNone, feature_namesNone): 画出学到的权重如果有真实权重就并排对比。 n len(learned_weights) x np.arange(n) plt.figure(figsize(10, 4)) plt.bar(x - 0.2, learned_weights, width0.4, labelLearned) if true_weights is not None: plt.bar(x 0.2, true_weights, width0.4, labelTrue) if feature_names: plt.xticks(x, feature_names, rotation45) plt.legend() plt.title(Reward Weight Comparison) plt.tight_layout() plt.savefig(weight_comparison.png) plt.show()如果某些特征的权重符号反了比如真实奖励里“靠近目标”是正权重学出来是负的那说明迭代没收敛或者特征尺度差异太大。常见做法是对特征做归一化让每个维度的取值范围接近这样梯度更新时不会偏向某些维度。3.3 收敛性检查与迭代次数选择最大熵 IRL 的收敛判断没有统一标准代码里一般用梯度范数或者权重变化量来卡。# 在迭代循环里加入收敛检查 prev_weights weights.copy() weights lr * grad weight_change np.linalg.norm(weights - prev_weights) if weight_change 1e-4: print(fConverged at iter {i}) break阈值1e-4是我常用的经验值太小会跑很久太大可能还没收敛就停了。另外可以同时监控梯度范数如果梯度范数在某个值附近震荡不再下降也说明基本收敛了。迭代次数不是越多越好。跑太多轮之后如果特征表达能力有限权重可能会过拟合到专家轨迹的噪声上反而让策略性能下降。我一般会跑 200 轮每 20 轮存一次权重最后用验证集挑最好的那组。4. 避坑与常见问题排查那些跑不通和跑不对的情况4.1 梯度爆炸导致权重变成 NaN现象迭代几次后权重突然变成 NaN后续所有计算都失效。原因学习率太大或者特征值范围差异太大导致梯度范数爆炸。解决先把学习率降到 0.001 试试。如果还不行对特征做标准化让每个维度的均值为 0、方差为 1。代码里可以在compute_feature_expectation之前加一步归一化。4.2 学到的奖励函数所有状态都差不多现象跑完 IRL 后奖励函数在每个状态上的值几乎一样策略没有区分度。原因特征设计太单一比如只用 one-hot而专家轨迹覆盖的状态又很少导致很多状态的特征期望都是零权重更新没有信号。解决增加手工特征比如状态到关键点的距离、动作代价等。另外检查专家轨迹的数量如果只有一两条特征期望的估计方差会很大建议至少 20 条以上。4.3 策略评估时回报为负且一直不提升现象用学到的奖励训练策略在真实奖励下评估回报一直是负数且随迭代没有改善。原因奖励函数的符号反了。最大熵 IRL 的梯度方向是专家期望 - 模型期望如果代码里写成了模型期望 - 专家期望学到的奖励就会鼓励模型去模仿错误的行为。解决检查梯度更新那行代码确认是expert_feature_exp - model_feature_exp。另外可以打印前几轮的梯度值如果专家期望和模型期望的差值一直是负的说明方向反了。4.4 环境状态数多时运行极慢现象状态数超过 1000 后每次迭代要跑好几分钟跑完 100 轮要几个小时。原因value_iteration用了 Python 循环逐状态更新没有向量化。解决把值迭代改成矩阵运算。状态转移矩阵提前算好每次迭代用np.max对动作维度做聚合。如果环境是确定性的还可以用策略迭代代替值迭代收敛更快。4.5 专家轨迹和模型轨迹的特征期望量纲不一致现象梯度值很大权重更新剧烈震荡。原因专家轨迹和模型轨迹的采样数量不同导致特征期望的尺度不一样。解决确保compute_feature_expectation里除以的是轨迹数量而不是总步数。另外模型轨迹的采样数量要和专家轨迹一致别一个采 10 条一个采 100 条。5. 进阶技巧把 IRL 用到自己的环境里5.1 自定义环境的接入方式代码包里的环境是示例用的网格世界要接到自己的环境里需要实现几个接口状态转移函数、奖励函数仅用于生成专家轨迹、状态特征函数。常见做法是继承一个基类然后覆盖这几个方法。class CustomEnv(BaseEnv): def __init__(self): self.n_states 500 self.n_actions 4 self.transition_matrix self._build_transition() def _build_transition(self): # 返回形状为 (n_states, n_actions, n_states) 的转移概率矩阵 pass def state_features(self, state): # 返回该状态的特征向量 pass def true_reward(self, state): # 仅用于生成专家轨迹IRL 训练时不使用 pass接入之后先用随机策略生成一批轨迹确认环境本身没有 bug再用专家策略生成专家轨迹。专家策略可以是手工设计的规则也可以是用真实奖励训练出来的最优策略。5.2 特征设计的几个实用原则特征设计是 IRL 里最需要经验的部分。我一般会遵循几个原则第一特征要能区分不同状态的价值如果两个状态的特征向量完全一样IRL 就没法给它们不同的奖励。第二特征维度不要太高一般控制在状态数的 1/10 到 1/5 之间太高容易过拟合。第三尽量用有物理意义的特征比如距离、速度、能量消耗这样学出来的权重可解释性更好。如果特征太多可以用 L1 正则化做稀疏化让不重要的特征权重趋近于零。代码里可以在梯度更新后加一步软阈值操作。5.3 从最大熵到其他 IRL 变体最大熵 IRL 是最常用的基线但它假设专家轨迹是最优的。如果专家演示里有次优行为学到的奖励会有偏差。这种情况下可以试试其他变体比如基于对抗训练的 IRL或者用相对熵做正则化的版本。代码包里如果只实现了最大熵可以自己加一个algo参数把不同算法的求解逻辑分开。我自己的习惯是先用最大熵跑一遍看结果如果策略性能不达标再换其他算法对比。换算法的时候注意保持特征函数和轨迹数据不变只改求解部分这样才能公平比较。从那以后我每次跑 IRL 都会先固定随机种子把专家轨迹和模型轨迹的采样数量对齐再开始调学习率。这个顺序能省掉很多玄学问题。希望帮到你。本文还有配套的精品资源点击获取