化學(xué)習(xí)實(shí)戰(zhàn):從專(zhuān)家軌跡反推獎(jiǎng)勵(lì)函數(shù)的代碼教程與避坑指南)
簡(jiǎn)介面向強(qiáng)化學(xué)習(xí)與逆強(qiáng)化學(xué)習(xí)研究者的 Java 示例代碼包基于 BURLAP 庫(kù)構(gòu)建逆向強(qiáng)化學(xué)習(xí)IRL框架。資源圍繞學(xué)徒學(xué)習(xí)等經(jīng)典算法展開(kāi)通過(guò)網(wǎng)格世界、單階段正規(guī)博弈等實(shí)驗(yàn)場(chǎng)景演示如何從專(zhuān)家演示數(shù)據(jù)中恢復(fù)獎(jiǎng)勵(lì)函數(shù)也涵蓋多智能體交互與性能對(duì)比場(chǎng)景適合需要理解 IRL 原理并快速上手的算法工程師和研究生。壓縮包共 497 個(gè)文件以 484 個(gè) Java 源碼為主輔以 XML 配置、Maven 工程文件、依賴(lài) jar 包及項(xiàng)目說(shuō)明文檔整體大小僅 2.29MB目錄結(jié)構(gòu)緊湊便于按模塊定位代碼。目前已有 1046 人學(xué)習(xí)下載。代碼內(nèi)含性能繪圖、多智能體可視化組件以及線性規(guī)劃求解器封裝可在實(shí)驗(yàn)后直觀觀察算法收斂趨勢(shì)與策略表現(xiàn)。由于作者對(duì) BURLAP 做了定制修改包內(nèi)直接附帶了修改后的 BURLAP 快照省去自行適配依賴(lài)的步驟解壓即可運(yùn)行示例。這份資料既適合作逆強(qiáng)化學(xué)習(xí)入門(mén)教程的配套代碼也適合作后續(xù)研究與二次開(kāi)發(fā)的參考起點(diǎn)尤其適合希望從示例入手結(jié)合源碼細(xì)讀的初學(xué)者。1. IRLTutorial 是什么從專(zhuān)家軌跡里反推獎(jiǎng)勵(lì)函數(shù)的示例代碼庫(kù)逆強(qiáng)化學(xué)習(xí)IRL很多人卡在看懂公式但寫(xiě)不出第一行代碼。IRLTutorial 這類(lèi)示例代碼庫(kù)的價(jià)值不是幫你復(fù)現(xiàn)某個(gè) SOTA而是把逆強(qiáng)化學(xué)習(xí)的完整閉環(huán)——從專(zhuān)家軌跡出發(fā)反推獎(jiǎng)勵(lì)函數(shù)再拿學(xué)到的獎(jiǎng)勵(lì)去訓(xùn)練策略——用最小可運(yùn)行的代碼串起來(lái)。它適合兩類(lèi)人剛?cè)腴T(mén)模仿學(xué)習(xí)的研究生以及要在真實(shí)環(huán)境里做獎(jiǎng)勵(lì)工程但不想從零造輪子的工程師。我見(jiàn)過(guò)太多人把 IRL 當(dāng)成黑匣子直接套庫(kù)結(jié)果獎(jiǎng)勵(lì)函數(shù)學(xué)出來(lái)完全不可解釋本文就從這個(gè)教程代碼的結(jié)構(gòu)講起落到參數(shù)和踩坑。2. 示例代碼的四個(gè)模塊環(huán)境、專(zhuān)家、獎(jiǎng)勵(lì)學(xué)習(xí)器與策略?xún)?nèi)環(huán)2.1 為什么 IRL 示例代碼必須拆成四個(gè)模塊逆強(qiáng)化學(xué)習(xí)和普通強(qiáng)化學(xué)習(xí)最大的不同在于存在兩層循環(huán)。外層循環(huán)在更新獎(jiǎng)勵(lì)函數(shù)內(nèi)層循環(huán)在給定當(dāng)前獎(jiǎng)勵(lì)的情況下重新訓(xùn)練策略。如果不把環(huán)境、專(zhuān)家數(shù)據(jù)、獎(jiǎng)勵(lì)學(xué)習(xí)器和策略?xún)?yōu)化器拆開(kāi)你會(huì)發(fā)現(xiàn)自己根本沒(méi)法定位是外層梯度算錯(cuò)了還是內(nèi)層策略沒(méi)收斂。常見(jiàn)做法是四個(gè)模塊分文件存放每個(gè)模塊只干一件事我一般會(huì)按“環(huán)境與數(shù)據(jù)”在前、“學(xué)習(xí)器與策略”在后的順序組織代碼因?yàn)檎{(diào)試時(shí)你總是先懷疑數(shù)據(jù)出問(wèn)題。更具體地說(shuō)環(huán)境模塊負(fù)責(zé)定義狀態(tài)空間、動(dòng)作空間和轉(zhuǎn)移邏輯專(zhuān)家模塊負(fù)責(zé)加載或采樣專(zhuān)家軌跡獎(jiǎng)勵(lì)學(xué)習(xí)器模塊維護(hù)獎(jiǎng)勵(lì)函數(shù)的參數(shù)并輸出梯度策略模塊則是一個(gè)標(biāo)準(zhǔn)的強(qiáng)化學(xué)習(xí)外層調(diào)用它把當(dāng)前獎(jiǎng)勵(lì)當(dāng)成環(huán)境的 reward 去訓(xùn)練策略。這種拆分還有一個(gè)額外的好處你可以單獨(dú)替換任何一個(gè)模塊。比如把網(wǎng)格世界替換成 MuJoCo 環(huán)境時(shí)只需要?jiǎng)迎h(huán)境模塊和第 3 章的軌跡格式轉(zhuǎn)換函數(shù)獎(jiǎng)勵(lì)學(xué)習(xí)器完全不用改。2.2 一個(gè)最小文件布局與數(shù)據(jù)流如果你打開(kāi)一份典型的 IRL 示例代碼倉(cāng)庫(kù)目錄結(jié)構(gòu)大致長(zhǎng)這樣不同項(xiàng)目命名有差異但職責(zé)等價(jià)irl_tutorial/ ├── envs/ # 環(huán)境gridworld / gym 接口 │ ├── gridworld.py │ └── wrapper.py ├── expert/ # 專(zhuān)家軌跡生成與讀取 │ ├── collect.py │ └── load.py ├── irl/ # 獎(jiǎng)勵(lì)學(xué)習(xí)器 │ ├── maxent.py │ ├── airl.py │ └── features.py ├── policy/ # 內(nèi)層策略?xún)?yōu)化價(jià)值迭代或 PPO │ ├── value_iteration.py │ └── ppo.py ├── configs/ │ └── gridworld.yaml └── train.py # 主入口串起整個(gè)外環(huán)這里的核心數(shù)據(jù)流是collect.py 先按一個(gè)已知最優(yōu)策略采樣若干條專(zhuān)家軌跡features.py 把狀態(tài)映射成特征向量maxent.py 計(jì)算特征期望差并更新獎(jiǎng)勵(lì)權(quán)重value_iteration.py 用新獎(jiǎng)勵(lì)重新求解策略然后把新策略的軌跡再喂回 maxent.py。整個(gè)流程跑起來(lái)就像一個(gè)循環(huán)策略變好 → 獎(jiǎng)勵(lì)更新 → 策略再變好 → 獎(jiǎng)勵(lì)再更新直到兩條軌跡的特征期望對(duì)齊。代碼里最難理解的是特征期望。簡(jiǎn)單說(shuō)在最大熵 IRL 框架下我們假設(shè)專(zhuān)家不是絕對(duì)最優(yōu)而是“越好的軌跡出現(xiàn)概率越高”所以我們要找一組獎(jiǎng)勵(lì)權(quán)重使得學(xué)出來(lái)的策略在狀態(tài)訪問(wèn)頻率上和專(zhuān)家盡量一致。這個(gè)直覺(jué)寫(xiě)進(jìn)代碼就是先算專(zhuān)家軌跡的平均特征向量再算當(dāng)前策略的平均特征向量?jī)蓚€(gè)向量相減就是梯度方向。下一章我會(huì)給出能直接跑起來(lái)的最小實(shí)現(xiàn)。3. 跑通最大熵 IRL網(wǎng)格世界上的核心訓(xùn)練循環(huán)與兩個(gè)關(guān)鍵參數(shù)3.1 最大熵 IRL 的目標(biāo)函數(shù)與梯度最大熵 IRL 是逆強(qiáng)化學(xué)習(xí)里最容易被理解的一個(gè)版本。它假設(shè)專(zhuān)家軌跡服從一個(gè)指數(shù)形式的概率分布獎(jiǎng)勵(lì)越高的軌跡被專(zhuān)家選擇的概率越大。于是學(xué)習(xí)目標(biāo)就變成調(diào)整獎(jiǎng)勵(lì)函數(shù)參數(shù)讓“模型期望特征”逼近“專(zhuān)家特征期望”。這里的“特征”可以是網(wǎng)格世界里的狀態(tài)指示、機(jī)器人控制里的關(guān)節(jié)角度、速度等手工特征選擇范圍很寬但原則上要能區(qū)分不同狀態(tài)的好壞。對(duì)應(yīng)的梯度其實(shí)非常簡(jiǎn)潔損失函數(shù)對(duì)獎(jiǎng)勵(lì)權(quán)重求導(dǎo)等于專(zhuān)家特征期望減去當(dāng)前策略特征期望。這意味著你不需要通過(guò)反向傳播去算獎(jiǎng)勵(lì)的梯度只需要做兩件事統(tǒng)計(jì)專(zhuān)家軌跡的特征均值統(tǒng)計(jì)當(dāng)前策略 rollout 的特征均值然后相減、乘以學(xué)習(xí)率、更新權(quán)重。很多初學(xué)者把 IRL 想象得很神秘實(shí)際上代碼里最難的部分反而是內(nèi)層策略?xún)?yōu)化——也就是每次更新完獎(jiǎng)勵(lì)你能不能快速得到一個(gè)新策略。3.2 網(wǎng)格世界訓(xùn)練循環(huán)的可運(yùn)行代碼下面這段代碼是我建議的最小實(shí)現(xiàn)它用 5x5 網(wǎng)格世界狀態(tài)特征用 one-hot 向量策略用價(jià)值迭代求解獎(jiǎng)勵(lì)權(quán)重用梯度上升更新import numpy as np # 5x5 網(wǎng)格狀態(tài)編號(hào) 0..24動(dòng)作 0:上 1:下 2:左 3:右 # 獎(jiǎng)勵(lì)權(quán)重初始為零特征為 one-hot 狀態(tài)指示 n_states 25 n_actions 4 theta np.zeros(n_states) # 獎(jiǎng)勵(lì)權(quán)重即我們要學(xué)的獎(jiǎng)勵(lì)函數(shù) learning_rate 0.1 outer_iters 50 # 外層獎(jiǎng)勵(lì)更新輪數(shù) inner_iters 100 # 內(nèi)層價(jià)值迭代輪數(shù) expert_trajs, expert_feature load_expert() # 專(zhuān)家軌跡及其特征均值 for it in range(outer_iters): # 用當(dāng)前 theta 作為獎(jiǎng)勵(lì)做價(jià)值迭代得到策略 V np.zeros(n_states) for _ in range(inner_iters): Q np.zeros((n_states, n_actions)) for s in range(n_states): for a in range(n_actions): s_next step(s, a) # 環(huán)境轉(zhuǎn)移 Q[s, a] theta[s] V[s_next] V Q.max(axis1) # 價(jià)值迭代更新 policy Q.argmax(axis1) # 由最優(yōu)價(jià)值導(dǎo)出策略 # 按當(dāng)前策略采樣軌跡計(jì)算策略特征期望 policy_feature np.zeros(n_states) for _ in range(200): # 采樣 200 條軌跡來(lái)估計(jì) s env_reset() for t in range(20): policy_feature[s] 1 # one-hot 特征直接累加 s step(s, policy[s]) policy_feature / 200 * 20 # 歸一化成訪問(wèn)頻率 # 梯度上升專(zhuān)家特征期望 - 當(dāng)前策略特征期望 grad expert_feature - policy_feature theta learning_rate * grad print(fiter {it}, grad_norm {np.linalg.norm(grad):.3f})這段代碼的邏輯分三步第一步用當(dāng)前獎(jiǎng)勵(lì)權(quán)重做價(jià)值迭代價(jià)值迭代收斂后取 argmax 得到策略第二步讓這個(gè)策略在環(huán)境里跑 200 條軌跡統(tǒng)計(jì)狀態(tài)訪問(wèn)頻率作為策略特征期望第三步把專(zhuān)家特征期望和策略特征期望相減作為梯度更新獎(jiǎng)勵(lì)權(quán)重。三個(gè)步驟交替執(zhí)行IRL 的訓(xùn)練環(huán)就閉合了。兩個(gè)關(guān)鍵參數(shù)決定了訓(xùn)練質(zhì)量。第一個(gè)是 outer_iters 和 inner_iters 的比例inner_iters 太小策略還沒(méi)收斂你就去更新獎(jiǎng)勵(lì)梯度方向是噪聲inner_iters 太大訓(xùn)練時(shí)間成倍增加我一般先固定 inner_iters100再去看梯度范數(shù)有沒(méi)有持續(xù)下降。第二個(gè)是學(xué)習(xí)率IRL 的獎(jiǎng)勵(lì)權(quán)重沒(méi)有上下界學(xué)習(xí)率稍大就會(huì)出現(xiàn)權(quán)重爆炸保守起見(jiàn)從 0.05 開(kāi)始調(diào)當(dāng) grad_norm 出現(xiàn)連續(xù)增長(zhǎng)時(shí)就減半。另外專(zhuān)家軌跡數(shù)量也很重要少于 10 條軌跡時(shí)特征期望的方差會(huì)很大這個(gè)在下一章專(zhuān)門(mén)講。4. 把獎(jiǎng)勵(lì)函數(shù)換成神經(jīng)網(wǎng)絡(luò)的三個(gè)必調(diào)超參4.1 線性特征為什么不夠用網(wǎng)格世界的 one-hot 特征能用但換到連續(xù)控制任務(wù)就會(huì)立刻失效。原因是線性獎(jiǎng)勵(lì)假設(shè)所有狀態(tài)的好壞可以疊加表示而真實(shí)環(huán)境里往往是組合特征在起作用——機(jī)器人的某個(gè)關(guān)節(jié)角度本身無(wú)害但它和另一個(gè)角度的組合可能是危險(xiǎn)姿態(tài)。這時(shí)候手工特征維度會(huì)爆炸而神經(jīng)網(wǎng)絡(luò)獎(jiǎng)勵(lì)函數(shù)可以直接從狀態(tài)輸入里學(xué)出非線性組合。所以現(xiàn)在的 IRL 示例代碼普遍提供兩套實(shí)現(xiàn)一套是第 3 章的線性最大熵適合跑通流程、驗(yàn)證直覺(jué)另一套是 AIRLAdversarial Inverse RL用判別器把專(zhuān)家軌跡和策略軌跡區(qū)分開(kāi)同時(shí)輸出一個(gè)可解釋的獎(jiǎng)勵(lì)項(xiàng)。AIRL 的本質(zhì)就是把生成對(duì)抗的思想搬進(jìn)逆強(qiáng)化學(xué)習(xí)策略是生成器獎(jiǎng)勵(lì)網(wǎng)絡(luò)是判別器兩者交替訓(xùn)練直到判別器區(qū)分不出兩條軌跡。這套做法對(duì)初學(xué)者不那么友好但代碼框架和第 3 章完全兼容換掉 irl/maxent.py保留環(huán)境、專(zhuān)家采樣和 rollout 邏輯。4.2 非線性 IRL 訓(xùn)練中的三個(gè)必調(diào)超參第一是獎(jiǎng)勵(lì)網(wǎng)絡(luò)的更新比例。在 AIRL 里判別器更新太快會(huì)把獎(jiǎng)勵(lì)梯度磨平策略更新太快又會(huì)讓判別器失去學(xué)習(xí)信號(hào)我一般把兩者比例穩(wěn)定在 1:1 到 1:3 之間——每更新一次策略就更新一至三次判別器。第二是熵正則系數(shù)。獎(jiǎng)勵(lì)網(wǎng)絡(luò)很容易退化成“只關(guān)注當(dāng)前軌跡里的少數(shù)高光狀態(tài)”加入熵正則能逼它保持探索讓狀態(tài)覆蓋更全面這個(gè)系數(shù)我通常從 0.01 起調(diào)觀察 rollout 的狀態(tài)覆蓋度。第三是判別器輸出層的初始化范圍。如果獎(jiǎng)勵(lì)網(wǎng)絡(luò)的輸出在初始階段就波動(dòng)很大策略會(huì)跟著劇烈變動(dòng)整個(gè) IRL 訓(xùn)練直接崩掉。常見(jiàn)做法是把最后一層權(quán)重初始化為接近零的小值讓獎(jiǎng)勵(lì)在訓(xùn)練初期保持平坦這個(gè)細(xì)節(jié)幾乎所有翻車(chē)案例中都出現(xiàn)過(guò)。下面這段 PyTorch 代碼展示了獎(jiǎng)勵(lì)網(wǎng)絡(luò)的初始化與更新節(jié)奏控制import torch import torch.nn as nn class RewardNet(nn.Module): def __init__(self, state_dim): super().__init__() self.net nn.Sequential( nn.Linear(state_dim, 64), nn.ReLU(), nn.Linear(64, 64), nn.ReLU(), nn.Linear(64, 1) ) # 關(guān)鍵最后一層零初始化保證起始獎(jiǎng)勵(lì)平坦 nn.init.zeros_(self.net[-1].weight) nn.init.zeros_(self.net[-1].bias) def forward(self, s): return self.net(s) reward_net RewardNet(state_dim4) optimizer torch.optim.Adam(reward_net.parameters(), lr1e-3) # 偽代碼每迭代一次策略就更新 reward_net 三次 for _ in range(3): expert_score reward_net(expert_states) policy_score reward_net(policy_states) loss -torch.mean(torch.log(torch.sigmoid(expert_score)) torch.log(1 - torch.sigmoid(policy_score))) optimizer.zero_grad(); loss.backward(); optimizer.step()這段代碼里最容易被忽略的是 nn.init.zeros_ 這一行。沒(méi)有它獎(jiǎng)勵(lì)網(wǎng)絡(luò)一開(kāi)始就會(huì)給狀態(tài)打一個(gè)隨機(jī)的正負(fù)分策略被迫去追一個(gè)不存在的“好狀態(tài)”訓(xùn)練曲線看起來(lái)在下降實(shí)際獎(jiǎng)勵(lì)函數(shù)已經(jīng)學(xué)歪了。把網(wǎng)絡(luò)最后一層歸零后前幾百步策略是在一種“所有狀態(tài)獎(jiǎng)勵(lì)相同”的環(huán)境里探索判別器再逐步拉開(kāi)差距穩(wěn)定很多。如果你跑的是這套非線性版本建議每輪訓(xùn)練記錄兩個(gè)指標(biāo)判別器的準(zhǔn)確率接近 0.5 說(shuō)明獎(jiǎng)勵(lì)接近收斂和策略軌跡的覆蓋范圍。前者告訴你生成對(duì)抗是否平衡后者告訴你策略有沒(méi)有坍縮到單一狀態(tài)。這兩個(gè)指標(biāo)配合第 5 章的常見(jiàn)問(wèn)題排查基本能定位大部分訓(xùn)練失敗原因。5. IRL 示例代碼避坑獎(jiǎng)勵(lì)漂移、局部最優(yōu)與數(shù)據(jù)稀疏的排查記錄5.1 獎(jiǎng)勵(lì)權(quán)重爆炸現(xiàn)象、原因與修復(fù)現(xiàn)象是訓(xùn)練輪數(shù)增加后獎(jiǎng)勵(lì)權(quán)重?cái)?shù)值越來(lái)越大策略反而越跑越差或者第 3 章代碼里打印的 grad_norm 不降反升。很多人在這一步開(kāi)始懷疑算法本身實(shí)際上原因是獎(jiǎng)勵(lì)函數(shù)的尺度沒(méi)有約束。IRL 學(xué)的是相對(duì)好壞而不是絕對(duì)分?jǐn)?shù)權(quán)重?zé)o約束增長(zhǎng)不會(huì)影響策略排序但會(huì)讓價(jià)值迭代的數(shù)值穩(wěn)定性變差甚至溢出。解決方法是給梯度加 L2 正則或者在每輪更新后把權(quán)重縮放到固定范數(shù)比如限制在 [-1, 1]。這也是為什么你會(huì)看到部分示例代碼里有個(gè) normalize_theta 的輔助函數(shù)。5.2 內(nèi)層策略?xún)?yōu)化不收斂先把外循環(huán)停掉現(xiàn)象是外層 loss 劇烈震蕩怎么調(diào)學(xué)習(xí)率都沒(méi)用。原因通常不在獎(jiǎng)勵(lì)學(xué)習(xí)器而在內(nèi)層價(jià)值迭代沒(méi)有收斂策略每次都在變導(dǎo)致算出來(lái)的特征期望方差極大。我一般會(huì)用兩段式排查先固定獎(jiǎng)勵(lì)權(quán)重只單獨(dú)調(diào)策略?xún)?yōu)化器看策略的目標(biāo)函數(shù)能不能穩(wěn)定等內(nèi)層穩(wěn)定后再打開(kāi)外層更新并把 inner_iters 設(shè)成比調(diào)試時(shí)更大一些的值。新手最常見(jiàn)的誤用是把 IRL 當(dāng)成黑匣子一上來(lái)就同時(shí)調(diào)所有超參結(jié)果一個(gè)都調(diào)不準(zhǔn)。5.3 專(zhuān)家軌跡太少學(xué)出退化策略現(xiàn)象是 IRL 學(xué)完之后的策略還不如行為克隆。這不是算法的問(wèn)題而是專(zhuān)家特征期望的估計(jì)方差太大。假設(shè)你只有 5 條專(zhuān)家軌跡高訪問(wèn)頻率狀態(tài)只有那么幾個(gè)梯度更新會(huì)把這些狀態(tài)推到極高獎(jiǎng)勵(lì)其他狀態(tài)完全失去信號(hào)。解決思路是數(shù)據(jù)增強(qiáng)把專(zhuān)家軌跡按不同長(zhǎng)度截?cái)?、增加噪聲重采樣、或者在連續(xù)控制里用動(dòng)作擾動(dòng)生成多條近似專(zhuān)家軌跡。另外一個(gè)務(wù)實(shí)的選擇是降低特征維度讓有限的專(zhuān)家數(shù)據(jù)更集中地估計(jì)少量特征。5.4 獎(jiǎng)勵(lì)函數(shù)不可辨識(shí)策略很好但獎(jiǎng)勵(lì)很怪這是 IRL 領(lǐng)域最常見(jiàn)的翻車(chē)現(xiàn)象策略在測(cè)試指標(biāo)上不比專(zhuān)家差但把學(xué)到的獎(jiǎng)勵(lì)單獨(dú)畫(huà)出來(lái)完全不符合直覺(jué)。原因是任何對(duì)專(zhuān)家策略保序的獎(jiǎng)勵(lì)變換都能解釋同樣的專(zhuān)家軌跡這種獎(jiǎng)勵(lì)模糊性是理論層面的不是代碼 bug。遇到這種情況要么放棄可視化獎(jiǎng)勵(lì)、只驗(yàn)證策略表現(xiàn)要么在獎(jiǎng)勵(lì)網(wǎng)絡(luò)上加入結(jié)構(gòu)化約束比如懲罰高方差、強(qiáng)制稀疏性讓結(jié)果更可解釋。我自己的經(jīng)驗(yàn)是先把“獎(jiǎng)勵(lì)可解釋”和“策略可用”分開(kāi)評(píng)價(jià)不要在同一個(gè)實(shí)驗(yàn)里同時(shí)要求兩者否則永遠(yuǎn)調(diào)不出一個(gè)“看起來(lái)合理”的結(jié)果。6. 驗(yàn)證獎(jiǎng)勵(lì)是否學(xué)對(duì)狀態(tài)分布對(duì)比與獎(jiǎng)勵(lì)重?cái)M合檢查驗(yàn)證 IRL 結(jié)果不能只看策略回報(bào)因?yàn)椴呗院貌淮愍?jiǎng)勵(lì)學(xué)對(duì)了。我習(xí)慣做兩個(gè)輕量檢查第一個(gè)是狀態(tài)分布距離。用專(zhuān)家軌跡和訓(xùn)練后的策略各采樣一批狀態(tài)計(jì)算兩個(gè)分布的 KL 散度或 Wasserstein 距離這個(gè)數(shù)字應(yīng)該隨著訓(xùn)練下降并穩(wěn)定在低位。第二個(gè)是獎(jiǎng)勵(lì)重?cái)M合把學(xué)到的獎(jiǎng)勵(lì)當(dāng)成監(jiān)督信號(hào)重新訓(xùn)練一個(gè)一模一樣的策略如果重訓(xùn)策略的行為和訓(xùn)練時(shí)的策略不一致說(shuō)明獎(jiǎng)勵(lì)函數(shù)存在記憶效應(yīng)或過(guò)擬合需要回頭檢查特征和正則。下面這段代碼可以快速完成第二個(gè)檢查from scipy.stats import wasserstein_distance def state_distribution_distance(expert_states, policy_states, bins20): # 把狀態(tài)按每個(gè)維度分箱計(jì)算一維 Wasserstein 距離后取平均 dists [] for dim in range(expert_states.shape[1]): d wasserstein_distance( expert_states[:, dim], policy_states[:, dim]) dists.append(d) return float(np.mean(dists))我有一次在連續(xù)控制任務(wù)里策略回報(bào)漲了 30%但狀態(tài)分布距離一直沒(méi)降最后發(fā)現(xiàn)是判別器鉆了空子只憑軌跡長(zhǎng)度就能區(qū)分專(zhuān)家和策略。修掉這個(gè)泄漏特征之后距離才真正開(kāi)始下降。從那以后我養(yǎng)成了每個(gè)實(shí)驗(yàn)都同時(shí)看策略回報(bào)和狀態(tài)分布距離的習(xí)慣前者騙人后者相對(duì)誠(chéng)實(shí)。IRL 的坑不比訓(xùn)練一個(gè)強(qiáng)化學(xué)習(xí)代理少但把這幾條排查路徑記在心里至少能讓你的示例代碼第一次跑通時(shí)知道該往哪個(gè)方向改。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取