習(xí)實戰(zhàn):MaxEnt IRL與Deep MaxEnt IRL算法解析及Python實現(xiàn))
簡介這套Python/TensorFlow逆向強化學(xué)習(xí)IRL代碼實現(xiàn)面向機器學(xué)習(xí)與強化學(xué)習(xí)研究者集中解決了從專家演示中重建獎勵函數(shù)的難點覆蓋線性IRL、最大熵IRL、深度最大熵IRL三種經(jīng)典與深度方法并配套1D/2D網(wǎng)格世界與值迭代求解器。資源共48個文件以22個Python腳本為核心另含18張訓(xùn)練結(jié)果圖、模型檢查點及說明文檔壓縮包僅2.58MB便于快速下載與本地實驗。目前已有1281人學(xué)習(xí)瀏覽。代碼入口demo.py可直接運行l(wèi)inear_irl、maxent_irl、deep_maxent_irl等腳本分別對應(yīng)不同算法實現(xiàn)value_iteration與gridworld模塊提供MDP環(huán)境與求解基礎(chǔ)相應(yīng)圖片展示了獎勵圖、值函數(shù)對比和深度最大熵迭代過程可幫助讀者直觀理解各IRL算法的收斂效果與差異適合作為算法復(fù)現(xiàn)、課程設(shè)計或?qū)Ρ葘嶒灥膮⒖紝崿F(xiàn)。 自己在實際項目里把逆向強化學(xué)習(xí)IRL這一套從理論擼到代碼完整實現(xiàn)了三個經(jīng)典算法MaxEnt IRL、Deep MaxEnt IRL 和 LPIRL全程基于 Python 和 TensorFlow 2.x。IRL 的核心問題很簡單給一堆專家軌跡但沒有獎勵函數(shù)怎么反推出“專家為什么這么走”。這在模仿學(xué)習(xí)、機器人軌跡規(guī)劃、自動駕駛行為建模里都很有用。如果你已經(jīng)玩過行為克隆想進一步把專家行為“翻譯”成可解釋的獎勵信號這篇文章就是我的完整踩坑記錄。1. 核心思路與算法選型1.1 IRL 到底在解決什么問題傳統(tǒng)強化學(xué)習(xí)是給定獎勵函數(shù)去學(xué)策略IRL 正好反過來給定專家軌跡去反推獎勵函數(shù)。這樣做的好處非常直接獎勵函數(shù)學(xué)出來之后不只是能模仿專家軌跡還能泛化到其他任務(wù)場景比如同一個獎勵函數(shù)換一個初始位置策略依然能表現(xiàn)得像專家。IRL 的基礎(chǔ)假設(shè)是專家在某個 MDP馬爾可夫決策過程中執(zhí)行了近乎最優(yōu)的策略但我們看不到這個策略只看到它產(chǎn)生的軌跡。目標(biāo)就是找一組獎勵函數(shù)的參數(shù)讓專家軌跡在“從這個獎勵函數(shù)推導(dǎo)出的最優(yōu)策略”下有最大的概率被生成。這個過程聽起來順理成章但真正做起來會碰到一個致命問題能解釋專家軌跡的獎勵函數(shù)有無限多個。你給軌跡加一個常數(shù)最優(yōu)策略不變給所有經(jīng)過的狀態(tài)都加同樣的偏置策略還是不變。所以 IRL 必須通過約束、先驗或者熵來從這無限多個解里挑一個合理的。1.2 三種算法的定位差異我這次選的三個算法正好代表了 IRL 三條技術(shù)路線直接對比一下算法獎勵函數(shù)形式核心求解思路典型適用場景MaxEnt IRL線性特征加權(quán)最大熵原理 Soft Value Iteration狀態(tài)空間有限、特征可手工設(shè)計的小型環(huán)境比如 Grid World、小型機器人路徑規(guī)劃Deep MaxEnt IRL神經(jīng)網(wǎng)絡(luò)最大熵原理 深度網(wǎng)絡(luò)逼近獎勵高維狀態(tài)空間、特征難以手工設(shè)計比如圖片輸入、連續(xù)狀態(tài)LPIRL線性特征加權(quán)線性規(guī)劃讓專家策略優(yōu)于候選策略策略集合可枚舉或采樣、獎勵參數(shù)較少的教學(xué)演示場景選型邏輯很清晰如果你剛接觸 IRL先跑 MaxEnt因為它的每一步都有明確的可視化和數(shù)值意義能幫你建立“特征期望”“配分函數(shù)”這些直覺如果狀態(tài)是高維的就上 Deep MaxEnt如果問題規(guī)模很小可以枚舉候選策略LPIRL 這種線性規(guī)劃思路反而最可控。2. 環(huán)境準備與軌跡數(shù)據(jù)構(gòu)造2.1 Python 與 TensorFlow 版本選擇我這次的環(huán)境是 Python 3.10 TensorFlow 2.12。坦率說TensorFlow 2.10 之后的 API 已經(jīng)非常穩(wěn)定tf.GradientTape、tf.function這些接口完全夠用。需要裝的庫不多pip install tensorflow2.12.0 numpy scipy matplotlib有一個容易被忽略的點如果你要跑 Deep MaxEntTensorFlow 和 Python 版本的兼容性一定要提前查好。比如 TensorFlow 2.12 在 Windows 上只支持到 Python 3.11裝錯版本會出現(xiàn)ModuleNotFoundError: No module named tensorflow這種最折磨人的問題。2.2 構(gòu)造一個可調(diào)試的專家軌跡我用了一個 5x5 的 Grid World 作為測試環(huán)境動作空間是上下左右專家策略是先繞開障礙物、再以最短路徑走到終點。為了生成專家軌跡我先手工寫了一個“已知最優(yōu)策略”的規(guī)則策略然后讓智能體按這個策略走 50 條軌跡出來。代碼里表示狀態(tài)的方式比較關(guān)鍵我用的是 one-hot 特征25 個狀態(tài)就映射成 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這條軌跡數(shù)據(jù)會同時喂給 MaxEnt 和 LPIRLDeep MaxEnt 那邊雖然也是同樣格式但因為它內(nèi)部有神經(jīng)網(wǎng)絡(luò)特征維度可以更大不用手工設(shè)計。如果你用的環(huán)境狀態(tài)維度特別高建議先用 one-hot 跑通整個流程再去換復(fù)雜特征。3. MaxEnt IRL從最大熵原理到完整實現(xiàn)3.1 為什么是“最大熵”MaxEnt IRL 的核心思想是在滿足“專家特征期望 學(xué)習(xí)到的策略特征期望”這個約束的前提下找一個熵最大的策略分布。直覺上就是不要假設(shè)太多額外信息得到一個最“隨機”、最不偏不倚的獎勵函數(shù)。在數(shù)學(xué)上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) )。也就是說累計獎勵越高的軌跡被專家選中的概率越大。這個概率分布用能量模型來解釋獎勵就是負能量。有了這個概率分布學(xué)習(xí)目標(biāo)就很自然了用梯度上升最大化專家軌跡的似然。求梯度時會出現(xiàn)一個配分函數(shù)直接用暴力求和會指數(shù)爆炸Ziebart 等人給出的解決方案是 Soft Value Iteration在表格型環(huán)境中反復(fù)迭代以下 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 的工程實現(xiàn)這一塊是我調(diào)試最久的地方因為公式看著簡單落到代碼里全是矩陣維度和數(shù)值穩(wěn)定性的問題。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 提高數(shù)值穩(wěn)定性 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 參數(shù) tau默認設(shè) 0.01控制策略的隨機程度。tau 越小策略越接近貪心但在求梯度時越容易遇到極小值導(dǎo)致梯度消失所以實踐中我是從 0.1 開始收斂后再調(diào)低到 0.01。3.3 特征期望計算與權(quán)重更新有了 soft policy 之后就可以計算在當(dāng)前參數(shù)下的特征期望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然后計算專家軌跡上的平均特征更新權(quán)重。完整訓(xùn)練循環(huán)大致是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用神經(jīng)網(wǎng)絡(luò)替代手工特征4.1 為什么要上深度版本MaxEnt IRL 最大的瓶頸是獎勵函數(shù)只能是特征向量的線性函數(shù)。在 5x5 的 Grid World 里 one-hot 特征還沒問題但換成圖像輸入手工提取特征基本不現(xiàn)實。Deep MaxEnt IRL 的核心改動是把獎勵函數(shù) ( R_\omega(s, a) ) 換成一個神經(jīng)網(wǎng)絡(luò)參數(shù)從線性權(quán)重變成網(wǎng)絡(luò)權(quán)重。訓(xùn)練時每步都需要走一遍 Soft Value Iteration 來求當(dāng)前網(wǎng)絡(luò)的策略然后用這個策略計算損失、回傳梯度。這聽起來很順但第一次跑的時候我折騰了很久不收斂。問題不在于網(wǎng)絡(luò)結(jié)構(gòu)而在于“獎勵函數(shù)的尺度”沒法控制。線性模型里權(quán)重范圍天然有限神經(jīng)網(wǎng)絡(luò)輸出層一旦沒有任何限制獎勵值的尺度會亂飄直接導(dǎo)致 Soft Policy 迅速坍縮到確定性策略損失變成 NaN。4.2 核心模型與訓(xùn)練循環(huán)我采用的是比較保守的做法獎勵網(wǎng)絡(luò)用兩層全連接輸出層加上 LayerNorm 來限制輸出范圍然后再乘一個可學(xué)習(xí)的縮放系數(shù)。這樣既保持了表達能力又把獎勵值范圍控制在穩(wěn)定區(qū)間。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訓(xùn)練循環(huán)大致結(jié)構(gòu)如下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 下的最優(yōu)策略再計算專家軌跡的負對數(shù)似然 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所以如果你的環(huán)境維度太大訓(xùn)練會非常慢。我的經(jīng)驗是先在 5x5 的小環(huán)境里把網(wǎng)絡(luò)結(jié)構(gòu)調(diào)通再考慮擴大規(guī)模。4.3 訓(xùn)練不收斂的三個關(guān)鍵修正第一個是輸出層不要加sigmoid或tanh不然梯度很容易消失獎勵值也表達不了負收益。第二個是temperature要從大往小調(diào)不要一開始就用 0.01我一般從 1.0 起步每 20 輪降一半。第三個是獎勵網(wǎng)絡(luò)的學(xué)習(xí)率要比普通監(jiān)督學(xué)習(xí)小一個量級因為損失信號經(jīng)過 Soft Value Iteration 傳遞過來本身波動就大學(xué)習(xí)率太大很容易震蕩。5. LPIRL線性規(guī)劃求解獎勵函數(shù)5.1 線性規(guī)劃建模思路LPIRL 的思路和最大熵完全不同它不假設(shè)概率分布而是直接利用 IRL 的定義如果專家策略是最優(yōu)的那么專家策略的累計回報應(yīng)該大于等于任意其他策略的累計回報。給定候選策略集合 ( \Pi {\pi_1, \pi_2, \dots, \pi_m} )其中包含專家策略 ( \pi_E )我們要找一個獎勵權(quán)重 ( 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)] )。每個約束都是關(guān)于 ( w ) 的線性不等式所以整個問題可以轉(zhuǎn)成線性規(guī)劃。為了從無限多個解里挑出穩(wěn)定的解我加了兩個額外目標(biāo)讓 ( w ) 的 L2 范數(shù)盡可能小同時最大化“專家策略超過其他策略的平均邊際”。這兩個目標(biāo)本身也是線性的可以直接塞進線性規(guī)劃里。5.2 用 SciPy 實現(xiàn)核心求解構(gòu)造候選策略集合是最麻煩的部分。我直接枚舉了 Grid World 里所有確定性策略但現(xiàn)實任務(wù)里不可能這樣一般會用隨機采樣策略或者用一些探索性策略代替。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這個實現(xiàn)里有一個很容易忽略的坑候選策略集合里必須包含專家策略本身。如果你的采樣策略里沒有覆蓋到專家策略線性規(guī)劃會有多個可行解求解器返回的結(jié)果會非常依賴初始值。5.3 LPIRL 與 MaxEnt 的對比觀察我同樣在 5x5 Grid World 上跑了 LPIRL最后得到的獎勵權(quán)重?zé)崃D和 MaxEnt 的結(jié)果比較接近但訓(xùn)練過程明顯更“脆”。MaxEnt 是梯度上升每一步都平滑地優(yōu)化期望LPIRL 則是一錘子買賣約束稍微沒構(gòu)造好解出來的獎勵就會非常離譜。比如我一開始忘了加 L2 正則項解出來的權(quán)重達到了幾千完全沒法用。后來在目標(biāo)函數(shù)里加了權(quán)重的 L2 項才把尺度拉回正常范圍。6. 常見問題與排查技巧實錄6.1 MaxEnt 訓(xùn)練日志里 loss 變成 NaN這是我最常碰到的問題十次里有八次是 Soft Value Iteration 的數(shù)值溢出。reduce_logsumexp能解決一部分問題但因為 Grid World 的轉(zhuǎn)移矩陣是稀疏的某些狀態(tài)在迭代過程中V會一直減小最后變成-inf。我在V上加了上限鉗制比如限制到 [-100, 100]效果非常明顯。另一個常見原因是梯度更新步長太大直接把權(quán)重推出穩(wěn)定范圍。6.2 Deep MaxEnt 訓(xùn)練慢得無法忍受Deep MaxEnt 的每一步都要跑 Soft Value Iteration在小環(huán)境里還行一旦狀態(tài)空間變大迭代次數(shù)和網(wǎng)絡(luò)訓(xùn)練的耗時都會飛漲。解決辦法是把 Soft Value Iteration 的迭代次數(shù)上限設(shè)小一點比如 50 次訓(xùn)練前期不需要那么高的精度后面再加大。還有一個經(jīng)驗是與其每輪更新一次網(wǎng)絡(luò)參數(shù)不如攢一批專家軌跡多更新幾次收斂速度反而更快。6.3 TensorFlow 版本相關(guān)的奇怪報錯如果你在跑代碼時遇到類似No module named tensorflow.python.ops.numpy_ops這種報錯先檢查版本是不是太老建議直接升到 2.10 以上。另一個高頻報錯是tf.gather_nd索引維度不匹配因為expert_idx的形狀必須和policy的維度一致建議在傳參前打印一下expert_idx.shape和policy.shape絕大多數(shù)問題一眼就能看出來。6.4 專家軌跡數(shù)量少導(dǎo)致獎勵嚴重偏向?qū)<臆壽E只有十幾條的時候?qū)W出來的獎勵函數(shù)經(jīng)常會出現(xiàn)“只認軌跡經(jīng)過的狀態(tài)其他狀態(tài)隨意”的情況。這個問題的根源不是算法本身而是特征表達不夠平滑。我后來在特征上加了一個高斯核平滑把 one-hot 特征替換成每個狀態(tài)與終點之間的距離特征效果立竿見影。這說明 IRL 對特征設(shè)計的敏感度比我想象中高得多。7. 我自己在實踐中積累的幾條經(jīng)驗最后分享三個我一直在用的實操技巧少踩一個是一個。第一所有 IRL 算法實現(xiàn)的檢查順序都是先看策略分布的形狀再看損失曲線最后再看獎勵值。如果策略分布很快就變到完全確定性先懷疑 temperature 是不是太低如果損失是平的先懷疑 Soft Value Iteration 有沒有收斂而不是懷疑網(wǎng)絡(luò)結(jié)構(gòu)。第二專家軌跡的質(zhì)量比數(shù)量重要得多。我試過用 100 條噪聲很大的軌跡效果遠不如 20 條非常干凈的最優(yōu)軌跡。這也符合 IRL 的概率假設(shè)它是在給“專家”建模不是給“平均水平”建模。第三當(dāng)行為克隆能輕松解決這個問題時不要為了炫技而強行用 IRL。行為克隆是監(jiān)督學(xué)習(xí)速度快、穩(wěn)定IRL 的優(yōu)勢在于多任務(wù)泛化和獎勵可解釋性。如果需求只是“模仿軌跡”行為克隆一天就能搞定IRL 可能要一周。只有在需要“解釋專家為什么這么做”或者“把獎勵遷移到新環(huán)境”時IRL 才真正值得投入。本文還有配套的精品資源點擊獲取