勵(lì)強(qiáng)化學(xué)習(xí)實(shí)戰(zhàn):HER事后經(jīng)驗(yàn)回放原理與PyTorch實(shí)現(xiàn))
“hindsight”這個(gè)詞在算法領(lǐng)域有一個(gè)被低估得厲害的含義事后復(fù)盤。這恰好是稀疏獎(jiǎng)勵(lì)強(qiáng)化學(xué)習(xí)里最值錢的一招。把失敗經(jīng)驗(yàn)重新標(biāo)記成有用樣本讓智能體從“沒(méi)做到”里學(xué)到“能做到什么”這是DeepMind提出的HER算法Hindsight Experience Replay事后經(jīng)驗(yàn)回放最核心的出發(fā)點(diǎn)。這篇博文就把這套方法從原理到PyTorch實(shí)現(xiàn)完整拆開(kāi)給那些被稀疏獎(jiǎng)勵(lì)卡到懷疑人生的朋友一份可直接上手的實(shí)操筆記。我還記得第一次跑機(jī)器人抓取仿真時(shí)的窘境reward函數(shù)寫得很“標(biāo)準(zhǔn)”抓到目標(biāo)給1沒(méi)抓到給0結(jié)果訓(xùn)練了上萬(wàn)步Q值紋絲不動(dòng)連個(gè)殘影都學(xué)不到。后來(lái)?yè)Q成HER同樣的網(wǎng)絡(luò)、同樣的環(huán)境幾千個(gè)episode之后策略就開(kāi)始像樣了。差別不在于網(wǎng)絡(luò)或超參而是思路換了一個(gè)維度別讓智能體只從成功中學(xué)也要讓它從失敗中帶點(diǎn)東西走。這套方法對(duì)做機(jī)器人操控、導(dǎo)航、推薦、游戲AI的開(kāi)發(fā)者都非常值得參考。無(wú)論是你在用DQN、DDPG還是SACHER都可以作為一套改造成本極低但收益極高的復(fù)盤機(jī)制疊加進(jìn)去。下面我從四個(gè)角度把這個(gè)“事后復(fù)盤”講透整體思路、核心細(xì)節(jié)、實(shí)操實(shí)現(xiàn)、以及我踩過(guò)的坑。1. 項(xiàng)目概述HER在解決什么1.1 稀疏獎(jiǎng)勵(lì)問(wèn)題是怎么卡住強(qiáng)化學(xué)習(xí)的先還原一下場(chǎng)景。你讓一個(gè)機(jī)械臂學(xué)習(xí)抓取桌上某個(gè)固定位置的紅色方塊獎(jiǎng)勵(lì)設(shè)計(jì)很直白末端執(zhí)行器到達(dá)目標(biāo)位置附近給reward1否則reward0。這種獎(jiǎng)勵(lì)設(shè)計(jì)在強(qiáng)化學(xué)習(xí)里被叫“稀疏獎(jiǎng)勵(lì)”。它看起來(lái)簡(jiǎn)單干凈但它致命的地方在于隨機(jī)探索幾乎不可能碰到成功狀態(tài)。如果目標(biāo)位置在三維空間里機(jī)械臂末端動(dòng)作空間是連續(xù)的隨機(jī)碰到的概率趨近于零那么智能體收集到的每一條經(jīng)驗(yàn)都是0回報(bào)沒(méi)有正樣本沒(méi)有梯度信號(hào)訓(xùn)練就像對(duì)著墻推車——車不動(dòng)你也不知道該往哪使勁。在真實(shí)任務(wù)里情況更復(fù)雜。比如開(kāi)門、疊衣服、插線纜這類任務(wù)的目標(biāo)狀態(tài)本身都很窄——要么門開(kāi)了要么沒(méi)開(kāi)要么插孔對(duì)齊了要么沒(méi)對(duì)齊。稀疏獎(jiǎng)勵(lì)之下agent早期學(xué)不到任何“好”與“壞”的相對(duì)概念它的策略分布基本就是隨機(jī)擾動(dòng)任何一個(gè)方向的嘗試都沒(méi)有被鼓勵(lì)或懲罰于是陷入了“死循環(huán)式探索”。稀疏獎(jiǎng)勵(lì)問(wèn)題的本質(zhì)并不是獎(jiǎng)勵(lì)函數(shù)設(shè)計(jì)得不好而是目標(biāo)在狀態(tài)空間中過(guò)于“孤獨(dú)”。成功樣本太少樣本效率就無(wú)限趨近于零。很多人第一反應(yīng)是給獎(jiǎng)勵(lì)塑形——中間每一步都加點(diǎn)引導(dǎo)信號(hào)。這當(dāng)然有效果但獎(jiǎng)勵(lì)塑形也有自己的麻煩你要設(shè)計(jì)很多中間態(tài)、猜測(cè)什么樣的引導(dǎo)是合理的、還要防著agent薅羊毛比如為了靠近目標(biāo)亂轉(zhuǎn)甚至抖動(dòng)得獎(jiǎng)勵(lì)。HER走的是另一條路不去手動(dòng)造中間獎(jiǎng)勵(lì)而是“事后再造目標(biāo)”。1.2 HER的“事后諸葛亮”核心思路HER的核心想法用一個(gè)類比最容易說(shuō)清你本想去公司附近一家餐館吃飯結(jié)果走錯(cuò)了到了一家看起來(lái)也不錯(cuò)的面館。雖然和原計(jì)劃不符但你記住“這條路能走到一家不錯(cuò)的面館”下次想吃面的時(shí)候就知道往這走。放到強(qiáng)化學(xué)習(xí)里就是一個(gè)episode雖然沒(méi)達(dá)到預(yù)設(shè)目標(biāo)但它一定“達(dá)到了某個(gè)實(shí)際狀態(tài)”。HER做的就是把這個(gè)實(shí)際狀態(tài)當(dāng)作目標(biāo)把整條軌跡重新標(biāo)記成一次成功經(jīng)驗(yàn)。假設(shè)機(jī)械臂想抓A點(diǎn)卻沒(méi)抓到最后停在了B點(diǎn)旁邊。對(duì)任務(wù)“抓A點(diǎn)”來(lái)說(shuō)這一整條軌跡全是失敗回報(bào)全0。但如果把目標(biāo)改成“抓B點(diǎn)”那這還是一條失敗軌跡嗎不是。它是一次完美成功——agent確實(shí)到達(dá)了B點(diǎn)。于是我們就可以給這條軌跡復(fù)制一份把目標(biāo)從A改成B把最后一步的reward改成1存進(jìn)經(jīng)驗(yàn)池。這個(gè)操作的妙處在于它用一次“失敗的探索”憑空制造出好幾條“成功的經(jīng)驗(yàn)”。這些經(jīng)驗(yàn)雖然對(duì)原始任務(wù)沒(méi)有幫助但它們教給agent一個(gè)更基礎(chǔ)、更通用的知識(shí)在某個(gè)狀態(tài)下采取某類動(dòng)作可以把環(huán)境帶到一個(gè)特定的狀態(tài)。當(dāng)所有這類知識(shí)積累得足夠多agent實(shí)際上就學(xué)會(huì)了一張“環(huán)境動(dòng)態(tài)圖”再去完成原始目標(biāo)它只需要知道怎么從這張圖里找路徑就行。這也是為什么HER特別適合多目標(biāo)、goal-conditioned的任務(wù)。因?yàn)樗郊拥恼恰澳繕?biāo)”這一維度經(jīng)驗(yàn)不再只對(duì)一個(gè)goal有效而是對(duì)一條軌跡結(jié)束時(shí)的真實(shí)狀態(tài)有效。后面所有細(xì)節(jié)都在圍繞這個(gè)思想展開(kāi)。2. 算法細(xì)節(jié)拆解與實(shí)現(xiàn)要點(diǎn)2.1 目標(biāo)重標(biāo)記的四類策略怎么選HER論文里給了四種重標(biāo)記策略final、future、episode和random。它們的區(qū)別在于“選軌跡里的哪個(gè)狀態(tài)當(dāng)作新目標(biāo)”的方式不同。這里逐個(gè)過(guò)一遍順便說(shuō)說(shuō)實(shí)際用下來(lái)的感受。final把軌跡最后一個(gè)狀態(tài)作為新目標(biāo)。簡(jiǎn)單粗暴適合最終狀態(tài)有固定意義的任務(wù)比如機(jī)械臂末端最終停在哪就追哪。缺點(diǎn)是如果軌跡很長(zhǎng)前面一大半狀態(tài)離目標(biāo)距離很遠(yuǎn)這條經(jīng)驗(yàn)對(duì)策略早期的指導(dǎo)意義有限。future從當(dāng)前時(shí)刻往后隨機(jī)采樣k個(gè)狀態(tài)之一作為新目標(biāo)。這是論文里實(shí)測(cè)最推薦的方案。原因很自然目標(biāo)不能離當(dāng)前狀態(tài)太遠(yuǎn)。如果拿最后的成功狀態(tài)去重標(biāo)記軌跡開(kāi)頭的transition相當(dāng)于要求agent一步就從老遠(yuǎn)跳到終點(diǎn)這太難學(xué)了。future策略保證目標(biāo)總是出現(xiàn)在當(dāng)前狀態(tài)之后的某個(gè)時(shí)間點(diǎn)從時(shí)間順序上更合理學(xué)習(xí)曲線也更平滑。episode從整條軌跡里隨機(jī)選一個(gè)狀態(tài)作為目標(biāo)不受時(shí)間先后限制。實(shí)現(xiàn)比f(wàn)uture簡(jiǎn)單但因?yàn)榭赡苓x出“過(guò)去”的狀態(tài)當(dāng)作目標(biāo)會(huì)造成部分transition目標(biāo)太遠(yuǎn)或邏輯混亂。random完全隨機(jī)從狀態(tài)空間里采樣一個(gè)狀態(tài)基本不推薦因?yàn)殡S機(jī)目標(biāo)命中“有意義區(qū)域”的概率太低。我實(shí)際做實(shí)驗(yàn)時(shí)future k4是性價(jià)比最高的組合。每一條真實(shí)軌跡額外復(fù)制4份每份用未來(lái)某一步的狀態(tài)替換原目標(biāo)。這樣經(jīng)驗(yàn)池里“接近成功”的樣本密度一下子高了非常多。有一點(diǎn)值得注意k不能開(kāi)太大。因?yàn)檫@個(gè)現(xiàn)在的4份經(jīng)驗(yàn)里有一部分目標(biāo)離當(dāng)前狀態(tài)還挺遠(yuǎn)已經(jīng)對(duì)agent形成挑戰(zhàn)了。你復(fù)制太多經(jīng)驗(yàn)池里大部分是噪聲很大的“遠(yuǎn)程目標(biāo)”反而稀釋了高質(zhì)量近端樣本。k選4還是8要看環(huán)境步長(zhǎng)episode比較長(zhǎng)的任務(wù)可以適當(dāng)增大但不要超過(guò)一個(gè)上限。2.2 網(wǎng)絡(luò)結(jié)構(gòu)把goal接進(jìn)value functionHER不是單獨(dú)的一種actor-critic架構(gòu)它更多是一種“經(jīng)驗(yàn)組織方式”。但要把這套機(jī)制跑起來(lái)網(wǎng)絡(luò)的輸入必須同時(shí)包含state和goal。因?yàn)橹貥?biāo)記之后同一份transition會(huì)在經(jīng)驗(yàn)池里對(duì)應(yīng)兩個(gè)不同goal的副本原始goal和重標(biāo)記goal網(wǎng)絡(luò)必須能區(qū)分“我是在追求哪個(gè)目標(biāo)”。最省事的做法是直接把goal拼到state末端形成一個(gè)高維向量然后正常輸出Q值或策略。這在簡(jiǎn)單任務(wù)里能跑但有一個(gè)隱患goal和state的數(shù)值尺度可能完全不同。比如state是機(jī)械臂關(guān)節(jié)角度范圍[-3.14, 3.14]goal是物體位置坐標(biāo)范圍可能是[0, 5]這種量綱不匹配很容易讓網(wǎng)絡(luò)訓(xùn)練初期重點(diǎn)跑偏。實(shí)踐中我會(huì)分別對(duì)state和goal做歸一化再拼接到一起會(huì)穩(wěn)很多。更規(guī)范一點(diǎn)的做法是采用**UVFAUniversal Value Function Approximator**的思路網(wǎng)絡(luò)不直接輸出單個(gè)Q值而是輸入state-goal組合輸出對(duì)“所有g(shù)oal”的估計(jì)——或者說(shuō)把goal當(dāng)成輸入條件傳給網(wǎng)絡(luò)。在DeepMind的原始實(shí)現(xiàn)里網(wǎng)絡(luò)結(jié)構(gòu)大概是state經(jīng)過(guò)幾層全連接得到特征goal經(jīng)過(guò)幾層全連接得到目標(biāo)特征然后兩者融合拼接或相加過(guò)一個(gè)輸出層預(yù)測(cè)Q值。這個(gè)結(jié)構(gòu)的好處是讓網(wǎng)絡(luò)學(xué)會(huì)“基于目標(biāo)來(lái)理解狀態(tài)價(jià)值”而不是簡(jiǎn)單把兩個(gè)向量當(dāng)成一個(gè)大向量處理。我在實(shí)際項(xiàng)目里兩種都試過(guò)差異在任務(wù)復(fù)雜度上去之后才顯現(xiàn)簡(jiǎn)單擺塊無(wú)所謂復(fù)雜操作UVFA訓(xùn)練明顯穩(wěn)。如果你用的是SAC這類連續(xù)動(dòng)作算法actor和critic都需要輸入goal條件一般做法是把goal拼進(jìn)observation后給actorcritic里再用UVFA結(jié)構(gòu)。這里有個(gè)工程細(xì)節(jié)actor和critic里的goal編碼層可以共享權(quán)重但實(shí)踐下來(lái)各用各的反而更穩(wěn)定因?yàn)閮烧叩膌oss不同共享權(quán)重會(huì)互相干擾。2.3 核心代碼實(shí)現(xiàn)Goal-conditioned DQN HER直接上一個(gè)簡(jiǎn)化版的PyTorch實(shí)現(xiàn)框架。這里我用最經(jīng)典的DQN舉例因?yàn)榻Y(jié)構(gòu)最清楚換成SAC/DDPG只是把目標(biāo)拼接和重標(biāo)記的代碼原封不動(dòng)搬過(guò)去。import torch import torch.nn as nn import numpy as np import random from collections import deque class GoalConditionedQNetwork(nn.Module): def __init__(self, state_dim, goal_dim, hidden_dim256): super().__init__() self.state_encoder nn.Sequential( nn.Linear(state_dim, hidden_dim), nn.ReLU(), ) self.goal_encoder nn.Sequential( nn.Linear(goal_dim, hidden_dim), nn.ReLU(), ) self.fuse nn.Sequential( nn.Linear(hidden_dim * 2, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, 1), ) def forward(self, state, goal): s self.state_encoder(state) g self.goal_encoder(goal) return self.fuse(torch.cat([s, g], dim-1))訓(xùn)練主循環(huán)里的經(jīng)驗(yàn)存儲(chǔ)和目標(biāo)重標(biāo)記是整篇文章最值得盯緊的部分。大致流程先按普通邏輯跑一個(gè)episode把原始軌跡存進(jìn)一個(gè)臨時(shí)列表。episode結(jié)束后遍歷里面的每一條transition做重標(biāo)記然后一次性寫入回放緩沖區(qū)。def hindsight_replay(episode_transitions, achieved_states, k4): 對(duì)一條完整episode做HER重標(biāo)記返回?cái)U(kuò)展后的transition列表。 episode_transitions: [(obs, goal, action, reward, next_obs, done)] achieved_states: 每個(gè)時(shí)間步的真實(shí)到達(dá)狀態(tài) her_transitions [] for i, (obs, goal, action, reward, next_obs, done) in enumerate(episode_transitions): # 原始過(guò)渡保留 her_transitions.append((obs, goal, action, reward, next_obs, done)) # future策略從當(dāng)前時(shí)刻之后的軌跡中隨機(jī)挑k個(gè)狀態(tài)作為新目標(biāo) future_indices np.random.randint(i 1, len(achieved_states), sizek) future_indices future_indices[future_indices len(achieved_states)] for idx in future_indices: new_goal achieved_states[idx] # 以“是否到達(dá)新目標(biāo)”為標(biāo)準(zhǔn)重新計(jì)算reward這里用簡(jiǎn)易判定 new_reward 1.0 if np.linalg.norm(next_obs - new_goal) 0.1 else 0.0 her_transitions.append((obs, new_goal, action, new_reward, next_obs, done)) return her_transitions這里有幾個(gè)容易被代碼被忽略但致命的點(diǎn)。第一個(gè)是future_indices必須過(guò)濾掉越界的索引或者直接限制范圍否則重標(biāo)記目標(biāo)用的是不存在的狀態(tài)。第二個(gè)是reward重新計(jì)算的判定閾值不要拍腦袋定最好用環(huán)境里真實(shí)目標(biāo)判定函數(shù)否則會(huì)出現(xiàn)“目標(biāo)明明沒(méi)到但給了1分”的污染經(jīng)驗(yàn)。第三個(gè)是done的重新判斷重標(biāo)記后如果下一狀態(tài)已經(jīng)“到達(dá)新目標(biāo)”那么done應(yīng)該設(shè)為True而不是沿用原trajectory的False。如果目標(biāo)就是“到達(dá)某狀態(tài)”到達(dá)了就該終止這條經(jīng)驗(yàn)才對(duì)Q學(xué)習(xí)沒(méi)有誤導(dǎo)。完整的訓(xùn)練循環(huán)里目標(biāo)網(wǎng)絡(luò)、經(jīng)驗(yàn)回放采樣這些都是DQN的常規(guī)操作。HER真正改動(dòng)的地方只有兩塊一是存經(jīng)驗(yàn)時(shí)多存一份“真實(shí)到達(dá)狀態(tài)”序列方便episode結(jié)束后重標(biāo)記二是采樣訓(xùn)練時(shí)把state和goal共同作為網(wǎng)絡(luò)輸入。這兩塊加進(jìn)去DQN立刻就有了“事后復(fù)盤”的能力。3. 實(shí)操過(guò)程與參數(shù)經(jīng)驗(yàn)3.1 訓(xùn)練流程與經(jīng)驗(yàn)回放設(shè)計(jì)HER訓(xùn)練一個(gè)完整流程的推薦順序是這樣的初始化Q網(wǎng)絡(luò)和目標(biāo)Q網(wǎng)絡(luò)經(jīng)驗(yàn)池。用epsilon-greedy策略跑一個(gè)完整episode記錄每一步的state、action、reward、goal、next_state。額外記錄每一步的“achieved goal”——也就是環(huán)境實(shí)際轉(zhuǎn)移到的狀態(tài)中與goal同維度的部分。這里概念很容易混如果goal是“物體位置”那achieved goal就是“物體實(shí)際位置”如果goal是“機(jī)械臂末端位置”achieved goal就是“末端實(shí)際位置”。它們的維度必須完全一致否則重標(biāo)記沒(méi)法做。episode結(jié)束后調(diào)用hindsight_replay把原始transition和重標(biāo)記transition一起塞進(jìn)經(jīng)驗(yàn)池。從經(jīng)驗(yàn)池里隨機(jī)采樣一個(gè)batch計(jì)算Q目標(biāo)reward gamma * max(Q_target(next_state, goal))。注意這里的目標(biāo)值里也帶著goal因?yàn)榫W(wǎng)絡(luò)是goal-conditioned的。正常梯度更新周期性同步目標(biāo)網(wǎng)絡(luò)。經(jīng)驗(yàn)池的設(shè)計(jì)有個(gè)講究。HER會(huì)產(chǎn)生大量額外經(jīng)驗(yàn)一個(gè)episode存4份重標(biāo)記副本整個(gè)buffer里重標(biāo)記經(jīng)驗(yàn)的比例就是80%。這時(shí)候如果全部混合采樣原始目標(biāo)的經(jīng)驗(yàn)會(huì)被淹沒(méi)。但實(shí)際里這通常不是問(wèn)題因?yàn)樵寄繕?biāo)經(jīng)驗(yàn)難學(xué)重標(biāo)記經(jīng)驗(yàn)提供基礎(chǔ)動(dòng)力學(xué)知識(shí)兩者互補(bǔ)。如果你想更精細(xì)可以像優(yōu)先經(jīng)驗(yàn)回放那樣給原始經(jīng)驗(yàn)稍微加一點(diǎn)采樣權(quán)重。我試過(guò)把原始經(jīng)驗(yàn)權(quán)重設(shè)為1.2倍訓(xùn)練速度確實(shí)有小幅提升但提升幅度不大不值得為這個(gè)引入復(fù)雜度?;胤啪彌_區(qū)的容量也要根據(jù)經(jīng)驗(yàn)數(shù)量調(diào)整。HER agent一個(gè)epoch產(chǎn)生的經(jīng)驗(yàn)是普通經(jīng)驗(yàn)的k1倍假設(shè)每episode 100步1萬(wàn)episode就是100萬(wàn)條普通的經(jīng)驗(yàn)加上重標(biāo)記就是500萬(wàn)條。如果buffer設(shè)太小比如10萬(wàn)大量舊經(jīng)驗(yàn)被新經(jīng)驗(yàn)擠出去訓(xùn)練的穩(wěn)定性會(huì)下降。建議buffer容量至少能覆蓋最近5000個(gè)episode產(chǎn)生的所有經(jīng)驗(yàn)。3.2 超參選擇k值、buffer比例、目標(biāo)取樣區(qū)間HER涉及幾個(gè)關(guān)鍵超參這里把經(jīng)驗(yàn)值直接擺出來(lái)方便大家參照調(diào)參。k每步重標(biāo)記數(shù)我推薦4。k2時(shí)重標(biāo)記樣本太少樣本效率提升不明顯k8時(shí)經(jīng)驗(yàn)池膨脹太快而且很多重標(biāo)記目標(biāo)離當(dāng)前狀態(tài)太遠(yuǎn)學(xué)起來(lái)開(kāi)銷大。k4是論文里測(cè)試過(guò)的值也是我實(shí)測(cè)下來(lái)的甜點(diǎn)。目標(biāo)取樣區(qū)間future horizonfuture策略里新目標(biāo)從“當(dāng)前時(shí)刻之后的所有狀態(tài)”里選。如果你的episode特別長(zhǎng)比如200步老遠(yuǎn)的未來(lái)狀態(tài)和當(dāng)前狀態(tài)差太多一條transition里“起始狀態(tài)和目標(biāo)”的距離跨度太大。建議限制只從當(dāng)前時(shí)刻到最后時(shí)刻的前80%區(qū)間里采樣或者干脆從當(dāng)前時(shí)刻往后固定N步比如N30的范圍內(nèi)采樣。這個(gè)改動(dòng)在長(zhǎng)episode任務(wù)里提升明顯。HER經(jīng)驗(yàn)的比例如果你想單獨(dú)調(diào)節(jié)可以在寫入buffer時(shí)以一定概率丟棄重標(biāo)記經(jīng)驗(yàn)。實(shí)踐中我一般保持全部寫入因?yàn)镼學(xué)習(xí)本身會(huì)泛化不需要手動(dòng)控比例。reward閾值判斷“是否到達(dá)目標(biāo)”的閾值非常影響效果。定得太大虛假成功太多定得太小成功樣本依然稀少。建議至少參考環(huán)境本身的誤差容忍度——比如物體抓取位置精度是多少閾值就設(shè)在那個(gè)量級(jí)附近。調(diào)參的時(shí)候有個(gè)共性建議先不改任何網(wǎng)絡(luò)結(jié)構(gòu)只調(diào)k和reward閾值觀察Q值曲線和成功率的對(duì)應(yīng)關(guān)系。如果成功率上來(lái)了但Q值還在震蕩通常是threshold導(dǎo)致的reward偏差如果Q值很穩(wěn)定但成功率低通常是目標(biāo)分布太廣、網(wǎng)絡(luò)沒(méi)辨清。3.3 計(jì)算代價(jià)與工程優(yōu)化一個(gè)說(shuō)爛但還是要強(qiáng)調(diào)的點(diǎn)HER不增加任何策略網(wǎng)絡(luò)的計(jì)算量代價(jià)全在“經(jīng)驗(yàn)重標(biāo)記和存儲(chǔ)”這一層。以k4為例訓(xùn)練一條episode后你寫入buffer的經(jīng)驗(yàn)數(shù)量是原來(lái)的5倍。這帶來(lái)兩個(gè)問(wèn)題一個(gè)是內(nèi)存翻倍一個(gè)是采樣時(shí)的IO壓力。工程上我有三個(gè)實(shí)用優(yōu)化方案。第一個(gè)方案是把重標(biāo)記結(jié)果用numpy結(jié)構(gòu)化數(shù)組預(yù)存不要用Python的list of tuple否則在大buffer下采樣速度會(huì)變得不可接受。第二個(gè)方案是不對(duì)每一條transition都做重標(biāo)記而是每隔幾步才做一次。比如每5步生成一份重標(biāo)記目標(biāo)經(jīng)驗(yàn)數(shù)量從5倍壓縮到2倍多一點(diǎn)訓(xùn)練效果損失很小。第三個(gè)方案是懶重標(biāo)記先只存原始經(jīng)驗(yàn)訓(xùn)練時(shí)隨機(jī)選一部分軌跡用HER邏輯在線重標(biāo)記。這個(gè)對(duì)代碼侵入更大但在經(jīng)驗(yàn)池極大時(shí)能省大量?jī)?nèi)存我在實(shí)際工程里用過(guò)唯一要注意的是每次采樣時(shí)多傳一個(gè)“goal序列”代碼復(fù)雜度上升不少。訓(xùn)練速度和算法選擇上HER配合DQN在離散動(dòng)作任務(wù)里很穩(wěn)但連續(xù)控制任務(wù)我還是推薦SACHER。SAC自帶熵正則探索性更強(qiáng)配上HER的經(jīng)驗(yàn)復(fù)用能處理更復(fù)雜的操作任務(wù)。區(qū)別主要在actor和critic的輸入拼接方式上前面2.2節(jié)提到的UVFA結(jié)構(gòu)可以直接套過(guò)去。4. 常見(jiàn)問(wèn)題與避坑心得4.1 訓(xùn)練不收斂排查速查表HER做起來(lái)不算復(fù)雜但問(wèn)題也不少。這里整理了我實(shí)際跑課時(shí)遇到的高頻問(wèn)題直接以速查表形式給出。問(wèn)題現(xiàn)象大概率原因處理方法訓(xùn)練前期Q值完全不動(dòng)goal和state拼接后量綱差異過(guò)大分開(kāi)歸一化state和goal再拼接成功率上去了但Q值不收斂reward閾值過(guò)大虛假成功太多調(diào)小閾值或換成環(huán)境真實(shí)判定函數(shù)重標(biāo)記樣本沒(méi)起什么作用new_goal維度與achieved state不一致檢查goal維度與狀態(tài)維度是否嚴(yán)格對(duì)齊訓(xùn)練中段突然崩潰經(jīng)驗(yàn)池過(guò)大采到大量尺度異常樣本減小buffer容量或?qū)?jīng)驗(yàn)做標(biāo)準(zhǔn)化重標(biāo)記目標(biāo)太遠(yuǎn)學(xué)不動(dòng)future采樣范圍太大限制目標(biāo)只從未來(lái)N步內(nèi)采樣效果比不用HER還差done標(biāo)志沒(méi)重設(shè)重標(biāo)記后到達(dá)新目標(biāo)應(yīng)把done置True這表里最容易被忽略的是最后一行。很多人改了reward漏該done結(jié)果agent明明已到達(dá)新目標(biāo)卻因?yàn)樗斑€在舊任務(wù)語(yǔ)境里”繼續(xù)當(dāng)作未完成狀態(tài)Q值更新邏輯就亂了。這個(gè)bug在調(diào)試時(shí)很難找因?yàn)樗挥绊憀oss數(shù)值但影響整個(gè)訓(xùn)練的方向。我建議排查時(shí)直接打印幾條重標(biāo)記transition肉眼核對(duì)reward和done是否一致。另一個(gè)高頻問(wèn)題出在多目標(biāo)泛化能力上。HER本質(zhì)是教一個(gè)goal-conditioned策略所以最終效果好壞取決于網(wǎng)絡(luò)區(qū)分不同目標(biāo)的能力。如果所有g(shù)oal在表征空間里差異很小重標(biāo)記經(jīng)驗(yàn)就是純?cè)肼?。遇到這種情況我會(huì)先檢查即將送入網(wǎng)絡(luò)的目標(biāo)維度是否包含了足夠多的區(qū)分信息。比如目標(biāo)是“位置坐標(biāo)”只有x和y兩個(gè)數(shù)在平面簡(jiǎn)單任務(wù)里沒(méi)問(wèn)題但在復(fù)雜環(huán)境里建議把物體相對(duì)位置、速度等關(guān)鍵信息都寫進(jìn)goal表征否則agent會(huì)迷失在高相似度目標(biāo)里。4.2 容易被忽略的幾個(gè)細(xì)節(jié)關(guān)于“利用未來(lái)信息”的適用性。有一條值得單獨(dú)拿出來(lái)講HER重標(biāo)記用到了“未來(lái)的狀態(tài)”當(dāng)目標(biāo)很多初學(xué)者擔(dān)心這會(huì)不會(huì)造成信息泄漏。這個(gè)顧慮本身合理但在ER算法背景下是沒(méi)問(wèn)題的。因?yàn)榛胤沤?jīng)驗(yàn)已經(jīng)是一次完整實(shí)現(xiàn)之后的離線樣本我們用“事后知道的結(jié)果”來(lái)重新組織目標(biāo)恰好是HER的核心目的而在policy evaluation時(shí)網(wǎng)絡(luò)接收的是“實(shí)時(shí)狀態(tài)當(dāng)時(shí)的目標(biāo)”并沒(méi)有偷看未來(lái)。理解這一點(diǎn)能避免很多人把自己繞暈。關(guān)于歸一化的時(shí)間點(diǎn)。state和goal的歸一化必須在replay buffer層面做而不是在環(huán)境層面直接改數(shù)值。因?yàn)橹貥?biāo)記產(chǎn)生的goal來(lái)自經(jīng)驗(yàn)池內(nèi)部必須和網(wǎng)絡(luò)訓(xùn)練時(shí)的取值范圍保持一致。如果你在環(huán)境里就縮放好那沒(méi)問(wèn)題但如果你把歸一化放在網(wǎng)絡(luò)輸入層那重標(biāo)記的goal也要經(jīng)過(guò)同一套歸一化否則分布不一致網(wǎng)絡(luò)泛化能力會(huì)很差。關(guān)于真實(shí)機(jī)械臂遷移。如果要部署到真實(shí)機(jī)器人HER仿真訓(xùn)練出來(lái)的策略直接遷移通常會(huì)摔跟頭。一個(gè)可行路線是在sim里調(diào)通k值和reward閾值然后做domain randomization把物體位置、摩擦系數(shù)、末端執(zhí)行器的初始偏差都加隨機(jī)擾動(dòng)。HER的goal-conditioned特性決定了它對(duì)“同一目標(biāo)從不同初始狀態(tài)出發(fā)”的泛化能力比較強(qiáng)這正好適合應(yīng)對(duì)sim-to-real的分布偏移。但這個(gè)話題展開(kāi)又能寫一整篇只說(shuō)一個(gè)關(guān)鍵經(jīng)驗(yàn)遷移時(shí)不要重訓(xùn)HER只用它學(xué)到的goal-conditioned策略再疊加一個(gè)低層的位置控制器去做插值平滑成功率會(huì)比直接用原始策略高很多。關(guān)于訓(xùn)練時(shí)的目標(biāo)采樣。訓(xùn)練時(shí)每次從buffer里采樣batch里頭的goals來(lái)自兩部分原始目標(biāo)和重標(biāo)記目標(biāo)。它們的reward分布差異很大原始目標(biāo)幾乎全是0重標(biāo)記目標(biāo)則有一小部分為1。這個(gè)不平衡是正常的不需要額外處理。真正要注意的是Q update里對(duì)重標(biāo)記經(jīng)驗(yàn)計(jì)算目標(biāo)價(jià)值時(shí)用目標(biāo)網(wǎng)絡(luò)輸出時(shí)要按“該條經(jīng)驗(yàn)對(duì)應(yīng)的目標(biāo)”來(lái)取max。如果每條經(jīng)驗(yàn)的goal不同必須向量化地分別取不要混用同一個(gè)goals張量否則你會(huì)在訓(xùn)練中不斷累積錯(cuò)誤偏差。寫在最后的一點(diǎn)體會(huì)從第一次聽(tīng)說(shuō)HER到真正用它跑通機(jī)械臂任務(wù)我最深的感觸是強(qiáng)化學(xué)習(xí)里很多看起來(lái)是“算法能力”的問(wèn)題本質(zhì)上是“經(jīng)驗(yàn)結(jié)構(gòu)”的問(wèn)題。稀疏獎(jiǎng)勵(lì)也好、探索效率也好很多方法在往“怎么更聰明地探索”方向使勁而HER換了一個(gè)成本低得多的思路——不改變探索策略把已經(jīng)探索過(guò)的經(jīng)驗(yàn)重新挖一遍價(jià)值。這種“事后復(fù)盤”的邏輯放到真實(shí)工作里其實(shí)也說(shuō)得通多復(fù)盤失敗路徑從中找到可復(fù)用的規(guī)律往往比悶頭嘗試下一次成功更高效。如果你正被某個(gè)稀疏獎(jiǎng)勵(lì)任務(wù)卡住不妨先別急著寫更復(fù)雜的獎(jiǎng)勵(lì)函數(shù)先讓agent學(xué)會(huì)利用已有的失敗樣本。用我項(xiàng)目里的經(jīng)驗(yàn)來(lái)說(shuō)這一個(gè)改動(dòng)常常比換任何高級(jí)算法都更快見(jiàn)到效果。