化學(xué)習(xí)中的HER:用事后經(jīng)驗(yàn)回放破解稀疏獎勵難題)
先聊一個很微妙的心理現(xiàn)象事情明明搞砸了過一會兒回過頭看你總覺得“我早就知道會這樣”。心理學(xué)管這叫hindsight bias后見之明偏差放在日常里就是事后諸葛亮。但在我接觸過的強(qiáng)化學(xué)習(xí)項(xiàng)目里有一種做法把“事后之明”變成了實(shí)打?qū)嵉膶W(xué)習(xí)信號那就是Hindsight Experience Replay中文常譯作“事后經(jīng)驗(yàn)回放”縮寫HER。我第一次看OpenAI的機(jī)械臂抓取demo時被這個思路驚艷到——原來讓智能體從失敗里學(xué)到東西不需要給它更精細(xì)的獎勵函數(shù)只需要在經(jīng)驗(yàn)回放時偷偷換一個“事后目標(biāo)”。這篇文章我把HER從原理到實(shí)現(xiàn)、再到調(diào)參踩坑從頭到尾講一遍。適合正在跟稀疏獎勵較勁、跑過DQN或DDPG但效果不理想、或者想搞目標(biāo)導(dǎo)向機(jī)器人策略的朋友。不需要太深的數(shù)學(xué)基礎(chǔ)但最好知道強(qiáng)化學(xué)習(xí)里的狀態(tài)、動作、獎勵和Q函數(shù)是怎么回事。1. 為什么需要Hindsight稀疏獎勵下的學(xué)習(xí)困境1.1 稀疏獎勵到底“難”在哪先從一個幾乎所有RL項(xiàng)目都會撞上的問題說起稀疏獎勵。拿機(jī)械臂推方塊到指定位置來說如果機(jī)械臂末端和目標(biāo)的距離小于一個閾值比如0.05米就獎勵0距離超了就獎勵-1。這樣的話一個episode里絕大多數(shù)動作得到的獎勵都是-1。也就是說智能體在絕大部分時間里根本不知道自己在靠近目標(biāo)因?yàn)樵讵剟顢?shù)值上“沒差”。這叫沒有梯度信號不是沒獎勵是獎勵就像一面白墻你撞上去不會被彈開也不會被引導(dǎo)。我見過不少人在這種環(huán)境里直接上PPO或DDPG結(jié)果訓(xùn)練幾千個episode成功率仍然是0。原因不復(fù)雜隨機(jī)探索時機(jī)械臂恰好碰到目標(biāo)的概率極低而且即使偶爾碰上了到目標(biāo)的距離誤差也可能大得離譜或者由于隨機(jī)目標(biāo)千變?nèi)f化同一個經(jīng)驗(yàn)在下一次采樣里又變成無效樣本。說白了智能體連“成功長什么樣”都沒見過自然學(xué)不到通向成功的動作序列。很多人第一反應(yīng)是設(shè)計(jì)reward shaping比如讓獎勵與“到目標(biāo)的距離”連續(xù)相關(guān)。這樣做確實(shí)能讓訓(xùn)練提速但代價也明顯手工設(shè)計(jì)的獎勵會注入人類偏見有可能誘導(dǎo)策略走捷徑、鉆漏洞。而且換一個環(huán)境距離函數(shù)又要重新設(shè)計(jì)維護(hù)成本很高。我希望找到一個更通用的辦法讓算法自己從“看似失敗”的經(jīng)驗(yàn)里挖掘價值。HER就是為了這個目的出現(xiàn)的。1.2 經(jīng)驗(yàn)回放為什么救不了場再說說經(jīng)驗(yàn)回放Experience Replay。DQN和DDPG這類off-policy算法會把(狀態(tài), 動作, 獎勵, 下一狀態(tài))存進(jìn)一個很大的buffer訓(xùn)練時隨機(jī)采樣小批量打亂相關(guān)性。經(jīng)驗(yàn)回放本身解決的是數(shù)據(jù)利用率和訓(xùn)練穩(wěn)定性問題但請注意它只是“存儲和讀取經(jīng)驗(yàn)”的機(jī)制并不會改變獎勵信號本身。在稀疏獎勵環(huán)境里buffer里塞滿了失敗經(jīng)驗(yàn)獎勵全是-1。你采樣一萬條正樣本還是零。Q函數(shù)無法從全負(fù)樣本中學(xué)到一個有區(qū)分度的價值面。有人會說既然全負(fù)至少能讓網(wǎng)絡(luò)學(xué)會“別亂動”但問題在于目標(biāo)環(huán)境里每個狀態(tài)s的價值并不相同如果所有狀態(tài)對應(yīng)的目標(biāo)g都被固定為原始目標(biāo)而原始目標(biāo)從未達(dá)成過那么Q網(wǎng)絡(luò)只能絕望地把所有狀態(tài)價值都壓向-1。這等于把目標(biāo)當(dāng)成了“海市蜃樓”看得見但永遠(yuǎn)觸不到。HER的思路很直接既然原始目標(biāo)達(dá)不成那我不要求你在當(dāng)前軌跡中一定達(dá)成它。我可以把這條軌跡“重寫”成朝另一個目標(biāo)成功的樣本。比如機(jī)械臂本來想去左上角結(jié)果跑到右上角那我就把“飛到右上角”當(dāng)作這個樣本的目標(biāo)重新算一遍獎勵這時候這條原本失敗的經(jīng)驗(yàn)就成了一條漂亮的正樣本。聽起來有點(diǎn)像自欺欺人但實(shí)際效果驚人。這個“換目標(biāo)”的操作就是HER的核心。1.3 HER的核心直覺換個目標(biāo)就是成功用一個生活化比喻來解釋一個新手投籃目標(biāo)是籃筐球沒進(jìn)砸到了籃板左側(cè)。如果他只會按“籃筐”這個目標(biāo)來訓(xùn)練這次投球就是發(fā)球失誤沒任何收獲。但如果他換一個目標(biāo)“下次爭取砸到籃板左側(cè)那個點(diǎn)”那么剛才那次投籃在“籃板左側(cè)”這個目標(biāo)下其實(shí)是完美命中的。他可以把這個“命中了籃板左側(cè)”的經(jīng)驗(yàn)記下來下次遇到“把球打到籃板左側(cè)”的新任務(wù)時就知道該用什么樣的力度和角度。強(qiáng)化學(xué)習(xí)里的HER正是這個流程每一條transition除了保留原始目標(biāo)g下的樣本還會額外用episode中后續(xù)某個狀態(tài)的achieved_goal事后再看真正到達(dá)的位置作為新目標(biāo)g把這條經(jīng)驗(yàn)重新計(jì)算獎勵后也存進(jìn)buffer。訓(xùn)練過程中網(wǎng)絡(luò)會同時面對大量不同目標(biāo)下的經(jīng)驗(yàn)它的Q函數(shù)可以逐漸學(xué)會“給定任意目標(biāo)我大概知道哪些動作值錢”。等到真正布置一個全新目標(biāo)時它已經(jīng)具備了泛化能力不需要從頭再學(xué)。這種泛化依賴一個老概念通用價值函數(shù)近似器UVFA。意思很簡單價值函數(shù)不再只是Q(s, a)而是Q(s, a, g)把目標(biāo)也當(dāng)作輸入的一部分。HER大量生成“目標(biāo)-狀態(tài)-轉(zhuǎn)移”三元組本質(zhì)上就是在用數(shù)據(jù)迫使Q函數(shù)對目標(biāo)維度泛化。1.4 為什么必須是off-policy算法有一個必須強(qiáng)調(diào)的點(diǎn)HER不能直接套在on-policy算法上使用。原因是重標(biāo)注后的樣本其轉(zhuǎn)移是在“試圖達(dá)成原始目標(biāo)”的行為策略下產(chǎn)生的。如果我們把經(jīng)驗(yàn)里的目標(biāo)換成g那么這條經(jīng)驗(yàn)就不再符合當(dāng)前策略對目標(biāo)g的行為分布。換成Q學(xué)習(xí)和actor-critic這類off-policy算法它們用Q函數(shù)去估計(jì)價值經(jīng)驗(yàn)來自不同策略也沒關(guān)系只要狀態(tài)、動作、獎勵、下一狀態(tài)對換目標(biāo)后依然自洽即可。我在實(shí)際嘗試中踩過這個坑一開始圖省事想在某on-policy的policy gradient框架里硬加HER結(jié)果訓(xùn)練完全飄掉loss亂跳。后來想明白policy gradient算法需要計(jì)算的是“當(dāng)前策略路徑下的梯度”重標(biāo)注目標(biāo)后路徑和目標(biāo)不匹配重要性采樣也救不回來。所以要玩HER安心選擇DQN、DDPG、TD3、SAC這類off-policy方法。2. Hindsight Experience Replay原理拆解2.1 數(shù)學(xué)描述與目標(biāo)重標(biāo)注先把符號理清楚。設(shè)在goal-conditioned MDP里狀態(tài)是由三部分構(gòu)成的observation機(jī)器人的關(guān)節(jié)角、速度等原始觀測achieved_goal當(dāng)前已到達(dá)的目標(biāo)相關(guān)狀態(tài)desired_goal想要到達(dá)的目標(biāo)。在Gym的Fetch系列環(huán)境里observation通常是一個15到25維向量achieved_goal是3維坐標(biāo)desired_goal也是3維坐標(biāo)。獎勵函數(shù)可以寫成r(s, a, g) 1 if ||achieved_goal(s) - g|| epsilon else 0或者是稀疏負(fù)獎勵r(s, a, g) 0 if ||achieved_goal(s) - g|| epsilon else -1其中s是動作執(zhí)行后的下一狀態(tài)achieved_goal(s)表示轉(zhuǎn)移后真正到達(dá)的位置。這一點(diǎn)非常重要判決是要看動作執(zhí)行之后的狀態(tài)離目標(biāo)有多近而不是動作執(zhí)行之前。存儲一條經(jīng)驗(yàn)時標(biāo)準(zhǔn)做法是保存(s_t, a_t, r_t, s_{t1}, g_t)其中s_t (obs_t, ag_t, g_t)。HER重標(biāo)注時從同一個episode中挑一個未來時間點(diǎn)tk取s_{tk}的achieved_goal作為新的目標(biāo)g然后代入獎勵函數(shù)重新計(jì)算r_t于是得到一條新經(jīng)驗(yàn)(s_t, a_t, r_t, s_{t1}, g)注意動作a_t和轉(zhuǎn)移(s_t, s_{t1})都沒變變的只是目標(biāo)和獎勵。正因?yàn)樵紕幼髟凇叭ネ硞€新目標(biāo)”這個語境下被重新評估原本是無用的失敗樣本才有了正樣本的價值。實(shí)際代碼中我習(xí)慣直接保存obs_t、act_t、rew_t、next_obs_t、ag_t、next_ag_t和g_t做重標(biāo)注時再用next_ag_t和新目標(biāo)計(jì)算獎勵。2.2 采樣策略怎么選final / future / episode / randomHER論文里給出了四種從episode中選擇新目標(biāo)的策略實(shí)際效果差異很大這里逐個講清楚。final策略直接用該episode最后一個狀態(tài)作為所有transition的新目標(biāo)。比如這一輪機(jī)械臂最終停在坐標(biāo)(1.2, 0.3, 0.8)那就把整條軌跡的目標(biāo)全部替換成(1.2, 0.3, 0.8)。優(yōu)點(diǎn)是實(shí)現(xiàn)最簡單一條episode只生成一個額外目標(biāo)計(jì)算量小。缺點(diǎn)是如果episode很長前面很多步驟距離最終位置很遠(yuǎn)對價值函數(shù)來說是很大的挑戰(zhàn)學(xué)習(xí)起來可能不平穩(wěn)。future策略對第t步的transition從第t1步到episode結(jié)束的所有狀態(tài)中隨機(jī)挑未來狀態(tài)作為新目標(biāo)。這是論文最推薦、也是我實(shí)測最穩(wěn)的策略。因?yàn)槟繕?biāo)在時間上位于當(dāng)前transition之后給網(wǎng)絡(luò)提供的信號具有天然因果性“我現(xiàn)在做的動作接下來會把我?guī)У竭@個位置所以這個位置應(yīng)該成為我的目標(biāo)”。正是這種順序關(guān)系讓Q函數(shù)更容易學(xué)到動作到結(jié)果的映射。episode策略從整個episode中任意狀態(tài)里隨機(jī)挑目標(biāo)不要求未來。這樣做增加了目標(biāo)多樣性但可能引入“先到A再到B但目標(biāo)可以是B”這類混亂信號。如果episode比較長還是建議保留一些episode采樣的目標(biāo)因?yàn)樗芨采w到“達(dá)到過去某個狀態(tài)”的能力。random策略從buffer里所有episode中隨機(jī)挑狀態(tài)當(dāng)目標(biāo)。多樣性最大但和目標(biāo)任務(wù)的關(guān)聯(lián)度也最弱。如果目標(biāo)分布跨度很大random很容易把智能體往莫名其妙的地方帶。我在實(shí)際任務(wù)里基本不單獨(dú)用random頂多摻一小點(diǎn)作為正則化。論文中的標(biāo)準(zhǔn)做法是每條原始transition額外生成k個future目標(biāo)k通常取4并把原始目標(biāo)經(jīng)驗(yàn)也保留下來。也就是說一條經(jīng)驗(yàn)最終可能會有1原始k條目標(biāo)變體進(jìn)入buffer。k不小算力和存儲都會上漲后面我們專門說優(yōu)化。2.3 與DDPG等off-policy算法的融合HER不是一個獨(dú)立算法它更像經(jīng)驗(yàn)回放模塊的一種增強(qiáng)插件。它需要一個基礎(chǔ)的off-policy強(qiáng)化學(xué)習(xí)算法最經(jīng)典的是DDPG。在DDPG里actor網(wǎng)絡(luò)μ(s, g)輸入狀態(tài)和目標(biāo)輸出動作critic網(wǎng)絡(luò)Q(s, a, g)輸入狀態(tài)、動作和目標(biāo)輸出估計(jì)的價值。訓(xùn)練時的TD目標(biāo)長這樣y r γ * Q_target(s, μ_target(s, g), g)注意公式里有兩個g一個是重標(biāo)注后的新目標(biāo)用于計(jì)算目標(biāo)Q值另一個是critic輸入時要傳的目標(biāo)。在實(shí)現(xiàn)里采集batch時我除了取obs和act還要把重標(biāo)注后的g也作為target_goal保存起來否則直接在經(jīng)驗(yàn)里改目標(biāo)會污染原始樣本。在DDPG的actor更新中梯度通過Q(s, μ(s, g), g)對μ(s, g)的參數(shù)求導(dǎo)。因?yàn)镼函數(shù)學(xué)會了對任意目標(biāo)g的價值評估actor才能學(xué)到“針對不同目標(biāo)給出不同動作”的行為模式。如果Q只在一個目標(biāo)上做過預(yù)測那actor也只能在一個目標(biāo)上動作。HER生成了大量不同目標(biāo)下的樣本恰好幫助Q面變得更光滑。2.4 數(shù)據(jù)流與存儲細(xì)節(jié)HER需要存儲episode結(jié)構(gòu)不能只存一條條獨(dú)立的transition。因?yàn)橹貥?biāo)注需要知道“這條transition之后同一episode里有哪些狀態(tài)”。所以我的buffer設(shè)計(jì)往往是兩層第一層是一個episodic臨時區(qū)記錄當(dāng)前episode里每一步的obs、act、next_obs、ag、next_ag、g、done第二層是真正的經(jīng)驗(yàn)池當(dāng)episode結(jié)束時對整個episode做重標(biāo)注產(chǎn)生額外transition再寫入經(jīng)驗(yàn)池。這樣做有一個額外好處避免在每步step時都判斷“現(xiàn)在這條transition是否多采樣幾個未來目標(biāo)”邏輯更清晰也方便批量向量化。缺點(diǎn)是該episodic臨時區(qū)的內(nèi)存占用和一次episode長度成正比但通常幾百步以內(nèi)完全可以接受。2.5 歸一化與動作范圍HER對歸一化很敏感。因?yàn)閟tate和goal往往不在同一個尺度比如關(guān)節(jié)角度范圍可能是[-3, 3]物體坐標(biāo)范圍可能是[0, 1]直接拼接后作為網(wǎng)絡(luò)輸入actor的探索噪聲如果按幅度加容易讓某些維度被噪聲淹沒。建議把每個維度做running mean/variance歸一化或者至少縮放到[-1, 1]。動作范圍同樣要注意。Fetch環(huán)境的action維度是2到4維范圍是[-1, 1]但物理模型要求機(jī)械臂位置變化很小。如果噪聲標(biāo)準(zhǔn)差設(shè)成0.3以上可能整個episode都是高頻抖動目標(biāo)永遠(yuǎn)靠不近。我一般初始設(shè)置標(biāo)準(zhǔn)差0.1并隨訓(xùn)練衰減。這不是論文參數(shù)但實(shí)測下來穩(wěn)定得多。3. 從零實(shí)現(xiàn)在FetchReach上跑通HER3.1 環(huán)境與工具選型為了直觀驗(yàn)證HER效果我推薦用Gym的FetchReach-v1。機(jī)械臂需要從初始位置把末端移動到三維空間中的隨機(jī)目標(biāo)點(diǎn)episode長度50距離閾值0.05獎勵是稀疏的。環(huán)境里沒有物體抓取、沒有避障純粹看算法是否能學(xué)習(xí)目標(biāo)導(dǎo)向的到達(dá)能力。對HER來說這是最容易診斷的起步環(huán)境。工具方面直接用PyTorch。OpenAI Baselines里雖然有HER的參考實(shí)現(xiàn)但基于TensorFlow 1.x舊接口太多復(fù)現(xiàn)成本高。我提供的版本會簡化掉一些和圖網(wǎng)絡(luò)相關(guān)的trick只保留DDPGHER核心讓改造成自己的任務(wù)更容易。GPU可有可無這種小環(huán)境CPU也能跑出結(jié)果。3.2 網(wǎng)絡(luò)與超參數(shù)設(shè)計(jì)網(wǎng)絡(luò)結(jié)構(gòu)不需要花哨。我給出的配置Actor輸入維度原始o(jì)bs維度FetchReach是10維有效狀態(tài)實(shí)際observation有25維其中包含噪聲關(guān)節(jié)信息goal維度3輸出維度等于action維度2。中間兩層全連接256激活ReLU輸出層用tanh再乘以動作上限。Critic輸入維度原始o(jì)bs goal action中間層同樣256最終輸出一個標(biāo)量Q值。超參數(shù)學(xué)習(xí)率actor和critic都取1e-3gamma0.98polyak滑動平均系數(shù)tau0.05batch_size256buffer_size1e5每輪episode結(jié)束后訓(xùn)練50步探索噪聲標(biāo)準(zhǔn)差0.1。Actor噪聲在高斯基礎(chǔ)上可以加一個很小的帶通濾波但我直接用純高斯也很好。這里說下tau的選擇DDPG目標(biāo)網(wǎng)絡(luò)更新公式是target - tau * target (1 - tau) * source如果你的tau寫成0.95那幾乎不更新嚴(yán)重拖慢訓(xùn)練。常見框架里默認(rèn)tau都是0.005或0.001用0.05已經(jīng)算很大了。3.3 核心代碼與注釋下面給出一個簡版但能跑的HERBuffer實(shí)現(xiàn)重點(diǎn)在重標(biāo)注邏輯import numpy as np class HERBuffer: def __init__(self, buffer_size100000, k4): self.buffer_size buffer_size self.k k self.episodes [] # 每個元素是list of transition self.data { obs: [], act: [], rew: [], next_obs: [], ag: [], next_ag: [], g: [], g_her: [] } def push_episode(self, episode_transitions, env): # episode_transitions: list of dict # 先存原始樣本 for t_idx, trans in enumerate(episode_transitions): self._store( trans[obs], trans[act], trans[rew], trans[next_obs], trans[ag], trans[g], trans[g] ) # 再生成HER樣本 horizon len(episode_transitions) for t_idx, trans in enumerate(episode_transitions): # future策略從t_idx之后所有狀態(tài)中隨機(jī)選k個 if horizon - t_idx - 1 0: continue for _ in range(self.k): future_idx t_idx 1 np.random.randint(horizon - t_idx - 1) new_goal episode_transitions[future_idx][ag].copy() # 注意獎勵要基于 next_ag 而不是 ag new_rew env.compute_reward( trans[next_ag], new_goal, None ) self._store( trans[obs], trans[act], new_rew, trans[next_obs], trans[ag], new_goal, new_goal ) def _store(self, obs, act, rew, next_obs, ag, g, g_her): if len(self.data[obs]) self.buffer_size: # 簡單實(shí)現(xiàn)清空最老的10% drop int(self.buffer_size * 0.1) for key in self.data: self.data[key] self.data[key][drop:] self.data[obs].append(obs) self.data[act].append(act) self.data[rew].append(rew) self.data[next_obs].append(next_obs) self.data[ag].append(ag) self.data[g].append(g) self.data[g_her].append(g_her) def sample(self, batch_size): idx np.random.choice(len(self.data[obs]), batch_size, replaceFalse) batch {} for key in self.data: arr np.array(self.data[key], dtypenp.float32) batch[key] arr[idx] return batch注意代碼里env.compute_reward(trans[next_ag], new_goal, None)這里的第三個參數(shù)info在Gym某些版本里需要傳dict否則報(bào)錯。更穩(wěn)妥的做法是直接從環(huán)境定義里取出閾值自己算距離reward -1.0 if np.linalg.norm(next_ag - new_goal) 0.05 else 0.0。接下來是DDPG訓(xùn)練主循環(huán)的關(guān)鍵部分env gym.make(FetchReach-v1) def select_action(actor, obs, goal, noise0.1): obs_t torch.FloatTensor(obs).unsqueeze(0) goal_t torch.FloatTensor(goal).unsqueeze(0) action actor.act(torch.cat([obs_t, goal_t], dim1)) action np.clip(action noise * np.random.randn(*action.shape), -1, 1) return action for episode in range(200): obs env.reset() ep_transitions [] done False score 0 while not done: action select_action(actor, obs[observation], obs[desired_goal]) next_obs, reward, done, info env.step(action) ep_transitions.append({ obs: obs[observation], act: action, rew: reward, next_obs: next_obs[observation], ag: obs[achieved_goal], next_ag: next_obs[achieved_goal], g: obs[desired_goal] }) obs next_obs score reward her_buffer.push_episode(ep_transitions, env) # 從buffer采樣訓(xùn)練 for _ in range(50): batch her_buffer.sample(256) # 這里用batch[g_her]作為critic的目標(biāo)goal而不是batch[g] # actor的監(jiān)督信號是Q(s, actor(s, g_her), g_her) # 具體更新代碼省略就是標(biāo)準(zhǔn)DDPG if episode % 20 0: print(fEpisode {episode}, avg_reward: {score})我在實(shí)際調(diào)試時把sample函數(shù)的batch[g_her]單獨(dú)挑出來確保critic在計(jì)算TD target時使用的是重標(biāo)注后的目標(biāo)。很多初學(xué)者容易在這里犯錯直接用batch里的原始goal那等于HER白做了。3.4 訓(xùn)練觀察與效果分析在FetchReach上跑我的觀察是只用DDPG不加HER成功率幾乎在0到1%之間波動跑500個episode都不動。加上HER后通常100到150個episode之內(nèi)rollout成功率就能漲到90%以上。訓(xùn)練曲線不是平滑上升的而是一段平臺后突然跳變。原因是前期buffer里攢到了足夠數(shù)量的正樣本“候選”一旦Q函數(shù)開始對目標(biāo)維度做出正確梯度策略會突然學(xué)會一項(xiàng)簡單技能比如“往某個坐標(biāo)多推一點(diǎn)”。我建議同時畫兩條曲線一條是online success rate另一條是“平均達(dá)到距離”的變化。平均達(dá)到距離比成功率更平滑能幫你判斷是否在緩慢進(jìn)步。如果平均距離在下降但成功率因?yàn)殚撝档脑蛞恢笔?這說明方向是對了繼續(xù)訓(xùn)練就行。如果平均距離紋絲不動那大概率HER沒生效回去檢查buffer里有沒有非-1的獎勵以及采樣時是否用了新的目標(biāo)。4. 常見問題與排查技巧實(shí)錄4.1 不收斂的排查清單在實(shí)際跑HER時我遇到過的失敗模式整理成一個速查表癥狀可能原因排查動作成功率長時間為0平均距離也不降HER重標(biāo)注邏輯沒生效buffer里仍全是-1打印一批buffer樣本檢查有沒有新目標(biāo)下獎勵為0的transition訓(xùn)練loss震蕩劇烈甚至出現(xiàn)NAN輸入和goal沒有歸一化數(shù)值爆炸對obs和goal做running normalization或用固定scale縮放到[-1,1]actor直接學(xué)到不動成功率某段突然掉探索噪聲太大動作一直飽和在邊界降低噪聲標(biāo)準(zhǔn)差檢查action是否在[-1,1]內(nèi)critic的TD target不穩(wěn)定獎勵用舊achieved_goal計(jì)算了確保重標(biāo)注時使用next_ag而不是ag收斂變慢后期上不去future策略采樣到太近的future狀態(tài)目標(biāo)過于重復(fù)提高k值比如k8或混合episode策略增加多樣性4.2 目標(biāo)采樣策略容易踩的坑future策略雖然好但有一個隱藏問題當(dāng)episode長度很短比如FetchReach只有50步如果當(dāng)前transition接近episode末尾剩余可選狀態(tài)很少重標(biāo)注目標(biāo)可能總是落在一個小范圍。目標(biāo)是取自achieved_goal也就是機(jī)械臂實(shí)際經(jīng)過的位置。如果機(jī)械臂只在起點(diǎn)附近徘徊那么這些新目標(biāo)都集中在起點(diǎn)附近網(wǎng)絡(luò)沒機(jī)會看到離起點(diǎn)遠(yuǎn)的目標(biāo)泛化就會差。解決方式是調(diào)整episode長度或增加動作噪聲讓機(jī)械臂多探索。如果任務(wù)確實(shí)很短可以用futureepisode混合一部分目標(biāo)取自當(dāng)前episode未來狀態(tài)一部分取自整個episode的任意狀態(tài)。注意不要全部用episode策略否則因果性過弱訓(xùn)練穩(wěn)定性反而下降。我在機(jī)器人抓取任務(wù)里通常設(shè)置k4的future外加每個episode額外生成2條episode目標(biāo)效果會比純future好一些。4.3 與PER、優(yōu)先回放的配合優(yōu)先經(jīng)驗(yàn)回放PER和HER經(jīng)常被一起討論因?yàn)槎寄芴嵘龜?shù)據(jù)效率。我的建議是在HER剛開始投入時慎用PER。HER生成的額外樣本里有一類是“把當(dāng)前狀態(tài)作為目標(biāo)”的高獎勵樣本這類樣本TD誤差可能很大PER選中它的概率極高導(dǎo)致更新時被大量“這步本來就接近目標(biāo)”的樣本刷屏反而壓制了對困難目標(biāo)的探索。如果非要加PER可以把優(yōu)先級計(jì)算公式改溫和一點(diǎn)比如p abs(td_error) 1e-3采樣權(quán)重不要按論文那樣嚴(yán)格最好設(shè)置一個最小概率防止完全丟棄低TD誤差的樣本。說到底HER已經(jīng)把稀疏獎勵變得更密了PER的邊際收益不如在獎勵密集環(huán)境里那么高。4.4 資源與效率優(yōu)化實(shí)戰(zhàn)HER會放大經(jīng)驗(yàn)量k4意味著buffer里的樣本數(shù)大約是原始樣本的5倍訓(xùn)練一次需要sample的transition也變多了。內(nèi)存還好但訓(xùn)練吞吐量下降明顯。我常用的優(yōu)化手段有三個第一不要每步都往主buffer里寫重標(biāo)注樣本。先緩存整個episode等episode結(jié)束后批量生成這樣能利用numpy向量化計(jì)算新目標(biāo)和新獎勵。第二buffer容量不是越大越好。HER需要?dú)v史episode足夠多來提供多樣的狀態(tài)目標(biāo)但太大了也會讓訓(xùn)練收斂慢因?yàn)闃颖痉植紲笥诋?dāng)前策略。我一般設(shè)成1e5到1e6之間具體看狀態(tài)維度。第三多進(jìn)程采樣時讓每個worker持有自己的episodic buffer訓(xùn)練主線程只負(fù)責(zé)采樣和更新能大幅提高數(shù)據(jù)吞吐。4.5 從仿真到真實(shí)機(jī)器人落地要記的筆記如果是把HER拿到真實(shí)機(jī)器人上有幾個細(xì)節(jié)跟仿真相差很大。真實(shí)機(jī)器人的末端坐標(biāo)通常來自動捕或視覺估計(jì)achieved_goal本身帶噪聲而你還要把噪聲后的位置當(dāng)新目標(biāo)寫進(jìn)buffer。這會讓策略學(xué)到“去一個不存在的點(diǎn)”或者“重復(fù)抖動”。建議在傳感器層先做濾波和標(biāo)定讓achieved_goal的精度至少高于環(huán)境閾值一個量級。另外真實(shí)環(huán)境探索成本高一定要做domain randomization讓仿真里機(jī)械臂初始狀態(tài)、物體位置、摩擦系數(shù)都在一個區(qū)間里變化。HER重標(biāo)注出的目標(biāo)只是在仿真軌跡上“事后達(dá)到過”的位置如果仿真和真實(shí)之間的狀態(tài)分布偏移太大這些目標(biāo)對真實(shí)環(huán)境的指導(dǎo)意義有限。安全方面真實(shí)機(jī)器人初始探索階段可以加一個夾爪保護(hù)和限位速度避免機(jī)械臂超出關(guān)節(jié)極限。5. 經(jīng)驗(yàn)與擴(kuò)展思考最后再說一個我在多個任務(wù)里反復(fù)驗(yàn)證的經(jīng)驗(yàn)HER特別適合“目標(biāo)從狀態(tài)中直接提取”的任務(wù)尤其是機(jī)器人運(yùn)動控制、路徑規(guī)劃、操作任務(wù)。但如果目標(biāo)是從圖像之類的高維傳感器里提取挑戰(zhàn)就會大很多因?yàn)橹貥?biāo)注出的新目標(biāo)必須對應(yīng)一個具體的像素塊或者潛在表征單純改標(biāo)簽可能引入語義錯誤?,F(xiàn)在很多研究在做“表示空間里的HER”比如先學(xué)一個狀態(tài)編碼器然后在編碼空間里做目標(biāo)重標(biāo)注思路是一樣的。我自己在跑通HER后最大的體會是強(qiáng)化學(xué)習(xí)里“失敗”不是最可怕的“沒有替代目標(biāo)”才是最可怕的。HER把失敗經(jīng)驗(yàn)重新解釋成其他目標(biāo)下的成功等于給智能體鋪了一條從探索到利用的緩坡。這種“事后聰明”式學(xué)習(xí)也讓我反思自己平時做項(xiàng)目復(fù)盤時的態(tài)度——與其盯著沒達(dá)成的原始目標(biāo)懊悔不如把做出來的東西當(dāng)作新的參照坐標(biāo)提煉出它對其他問題有沒有價值。這可能就是hindsight這個詞給我的最大禮物。