化學(xué)習(xí)泛化難題:從場景指紋到通用策略的重構(gòu)實(shí)踐)
上個月復(fù)盤一套智能路口信號控制項(xiàng)目時統(tǒng)計(jì)到一件讓人很尷尬的事在A路口訓(xùn)練收斂的強(qiáng)化學(xué)習(xí)模型平移到一條相隔不到兩公里、車道布局幾乎一樣、流量只差15%的B路口后平均延誤不降反升了12%。團(tuán)隊(duì)成員開玩笑說“這模型可能只認(rèn)A路口的瀝青顏色”。玩笑歸玩笑這個尷尬的結(jié)果正好戳中了強(qiáng)化學(xué)習(xí)在交通場景里最疼的一塊軟肋——泛化能力。圈子里默認(rèn)RL學(xué)的是“控制規(guī)律”但真實(shí)項(xiàng)目里模型經(jīng)常是在死記一套場景指紋遇到相似但不完全相同的交通問題決策就崩掉了。標(biāo)題問“連相似的交通問題都失靈研究者怎么辦”我特別能理解這個疑問因?yàn)檫^去半年我就在這個泥潭里反復(fù)掙扎。這篇文章不打算灌雞湯而是把我做失效復(fù)現(xiàn)、根因排查、重構(gòu)狀態(tài)表示和訓(xùn)練方案的全過程整理出來包括實(shí)驗(yàn)數(shù)據(jù)、踩過的坑、最后驗(yàn)證泛化能力用的評估協(xié)議給同樣被困在“一個路口只能訓(xùn)一個模型”狀態(tài)里的同行一個可以照做的參考。1. 相似路口的“近遷移測試”一次讓我拍桌子的實(shí)驗(yàn)1.1 實(shí)驗(yàn)是怎么設(shè)計(jì)的先說清楚當(dāng)時那個讓我“破防”的實(shí)驗(yàn)設(shè)計(jì)。兩個仿真路口A路口和B路口控制周期相同進(jìn)口道都是四相位車道數(shù)也基本一致。唯一的顯著區(qū)別是B路口的中片區(qū)多了一條右轉(zhuǎn)專用道同時全天總體流量比A路口低約15%。我當(dāng)時抱著“這么輕微的變化應(yīng)該沒問題吧”的心態(tài)把訓(xùn)練好的一套PPO權(quán)重直接零樣本加載進(jìn)B路口環(huán)境里測試不重啟訓(xùn)練不做任何適配。結(jié)果是這樣的指標(biāo)A路口訓(xùn)練收斂值B路口零樣本遷移值相對變化平均排隊(duì)長度輛18.627.447.3%平均停車延誤秒/車42.347.412.1%路口總吞吐量輛/小時18621705-8.4%歸一化回合獎勵0.870.71-18.4%最離譜的是這還不是“完全沒見過的新場景”。兩個路口在拓?fù)渖蠋缀跬瑯?gòu)交通流量差異也不算大按常識判斷模型應(yīng)該有很高的“近遷移”成功率??蓴?shù)據(jù)告訴我們策略不僅在B路口沒有保持住最優(yōu)性連最基礎(chǔ)的“別把排隊(duì)搞得更長”這條底線都沒守住。當(dāng)時我第一反應(yīng)是獎勵函數(shù)計(jì)算有問題檢查了三遍之后才不得不承認(rèn)模型真的“認(rèn)生了”。1.2 “數(shù)據(jù)不夠再訓(xùn)一訓(xùn)”的直覺陷阱遇到這種遷移失敗團(tuán)隊(duì)里最常見的提議就是是不是數(shù)據(jù)量不夠多采集一些B路口的樣本在原有預(yù)訓(xùn)練權(quán)重上繼續(xù)微調(diào)。這個思路聽起來合理但實(shí)際做出來又給了我一記悶棍。我在A路口預(yù)訓(xùn)練權(quán)重基礎(chǔ)上加入B路口數(shù)據(jù)繼續(xù)做微調(diào)訓(xùn)練訓(xùn)練兩萬個環(huán)境步后B路口的平均延誤確實(shí)降下來了從47.4秒回到44.9秒左右但緊接著A路口的平均延誤卻從42.3秒漲到了46.7秒。也就是說增量數(shù)據(jù)把網(wǎng)絡(luò)參數(shù)拉向了B場景同時又覆蓋掉了原先已經(jīng)學(xué)好的A場景行為。災(zāi)難性遺忘在連續(xù)微調(diào)里表現(xiàn)得非常干脆。這個結(jié)果讓我意識到問題根本不在“數(shù)據(jù)量”。即便我們把兩個路口的數(shù)據(jù)全部混在一起訓(xùn)練也只是得到一個在兩個場景之間“和稀泥”的策略勉強(qiáng)讓A和B都不太好也不太差。真正缺的不是樣本而是模型結(jié)構(gòu)里沒有把“場景特有信息”和“通用決策信息”分開。當(dāng)所有的場景線索都糾纏在一組網(wǎng)絡(luò)權(quán)重里遷移本質(zhì)上是失敗的。2. 場景漂移為什么能騙過強(qiáng)化學(xué)習(xí)模型三層根因2.1 狀態(tài)接口把“幾何指紋”當(dāng)成決策依據(jù)先看當(dāng)時喂給策略網(wǎng)絡(luò)的狀態(tài)向量。那套模型用的是很常見的結(jié)構(gòu)車道級排隊(duì)長度、當(dāng)前信號相位、相位剩余時間、各進(jìn)口道通過車輛數(shù)。這些特征聽起來沒問題但它們天然把路口幾何結(jié)構(gòu)滲進(jìn)了數(shù)值里。A路口和B路口車道數(shù)相同但因?yàn)橛杏肄D(zhuǎn)專用道B路口在右轉(zhuǎn)方向上的排隊(duì)分布和A路口完全不同。神經(jīng)網(wǎng)絡(luò)在提取特征時不會去區(qū)分“這個差異對決策有沒有用”它只會發(fā)現(xiàn)這些特征能高度辨識當(dāng)前場景于是把車道特有分布當(dāng)成決策依據(jù)一起學(xué)進(jìn)去了。于是模型學(xué)到的不是“看到某方向排隊(duì)接近飽和就延長綠燈”而是“當(dāng)右轉(zhuǎn)專用道隊(duì)列特征長成某個固定形狀時延長綠燈大概率有好處”。換一個路口幾何特征分布變了策略自然失效。用更直白的話說模型把“這個路口長什么樣”和“這個路口該怎么控制”兩件事揉在了一起。我們以為它學(xué)會了控制規(guī)律實(shí)際上它在背路口指紋。2.2 獎勵尺度的不統(tǒng)一同一個數(shù)字在不同路口含義完全不同第二個根因出現(xiàn)在獎勵函數(shù)上。那套模型用絕對排隊(duì)長度作為主獎勵項(xiàng)目標(biāo)是最小化每個周期內(nèi)各進(jìn)口道的總排隊(duì)車輛數(shù)。在A路口排隊(duì)30輛基本就等于嚴(yán)重?fù)矶乱驗(yàn)檫M(jìn)口道容量就這么大但在B路口由于通行能力不同排隊(duì)30輛可能只是晚高峰的常態(tài)水平。這會導(dǎo)致什么后果同一個獎勵權(quán)重在A路口能形成“擁堵就往疏”的正確梯度到B路口卻變成了“無論怎么壓都有30輛排隊(duì)獎勵怎么都上不去”。模型在B路口里看到的獎勵地形被拉平了梯度信號弱什么都不做反而是局部最優(yōu)。就算遷移后繼續(xù)訓(xùn)練策略也會往一個奇怪的方向偏——不是學(xué)交通控制而是在學(xué)“怎么讓排隊(duì)數(shù)勉強(qiáng)小于那個絕對閾值”。獎勵數(shù)值的絕對尺度不歸一是一切遷移失敗里最隱蔽的坑。因?yàn)閱螆鼍坝?xùn)練時你根本看不出問題A路口的獎勵曲線很漂亮你只會以為模型學(xué)得不錯。2.3 經(jīng)驗(yàn)池分布固化策略擬合的是“場景轉(zhuǎn)移概率”而非“交通流規(guī)律”第三層根因要往強(qiáng)化學(xué)習(xí)的訓(xùn)練機(jī)制里挖。單場景訓(xùn)練時經(jīng)驗(yàn)回放池里的樣本分布完全由該場景的交通流模式?jīng)Q定。比如A路口早高峰流量大、排隊(duì)飽和時間長經(jīng)驗(yàn)池里絕大多數(shù)樣本都是高占用率狀態(tài)B路口流量低15%中等占用率狀態(tài)占大頭。策略網(wǎng)絡(luò)在優(yōu)化時本質(zhì)上是在擬合“給定狀態(tài)s采取動作a后在某個轉(zhuǎn)移概率下能拿到多少獎勵”的條件期望。它擬合的對象是A路口的轉(zhuǎn)移函數(shù)和獎勵分布而不是交通流本身的一般規(guī)律。到了B路口流量分布變了模型大概率會碰到訓(xùn)練時很少見的狀態(tài)組合。這些狀態(tài)處于分布外策略輸出的動作本質(zhì)上是在做外推自然不可信。這就好比一個人在暢通馬路上練熟了過馬路你把他丟到車速相近但車距分布完全不同的環(huán)島里他的判斷模型會被瞬間打亂。不是因?yàn)樗粫^馬路而是他學(xué)到的“經(jīng)驗(yàn)池”里全是雙向平交路口的樣本沒有環(huán)島的先驗(yàn)。3. 從狀態(tài)接口和獎勵函數(shù)下手切斷“記場景”的路徑3.1 策略無關(guān)狀態(tài)表示無量綱、幾何無關(guān)、供需導(dǎo)向既然根因之一是狀態(tài)向量攜帶了大量幾何指紋信息最直接的應(yīng)對就是重做狀態(tài)接口。我的做法是把原始狀態(tài)全部替換成無量綱的比值型特征讓策略只看到“供需關(guān)系”和“壓力等級”不需要知道路口具體長什么樣。改造后的狀態(tài)向量大概長這樣各路口的飽和度當(dāng)前到達(dá)流量除以該進(jìn)口道的理論通行能力數(shù)值范圍0到1.2排隊(duì)占用率當(dāng)前排隊(duì)車輛數(shù)除以該進(jìn)口道最大可容納排隊(duì)車輛數(shù)純比例延誤相對變化率當(dāng)前平均延誤減去自由流延誤再除以自由流延誤反映延誤惡化程度相位進(jìn)度當(dāng)前相位已執(zhí)行時長除以該相位最大可選時長上一個周期內(nèi)各方向的綠燈利用率實(shí)際通過車數(shù)除以最大理論通過車數(shù)。構(gòu)造出來的狀態(tài)向量的關(guān)鍵特點(diǎn)是任何一個路口都能計(jì)算這些值而計(jì)算所用的分母本身已經(jīng)蘊(yùn)含了場景特性。飽和度高意味著這條路已經(jīng)承載不住了飽和度低則意味著還有余量。策略不用再去記住“這個路口是幾車道”它只需要知道“現(xiàn)在這個路口的壓力有多大”。我直接用一個簡單的Python片段來說明狀態(tài)構(gòu)造邏輯實(shí)際工程中這只是狀態(tài)編碼器一小部分def build_scene_free_state(lane_volume, lane_capacity, queue_len, max_queue, delay, free_flow_delay): saturation lane_volume / max(lane_capacity, 1e-6) occupancy queue_len / max(max_queue, 1e-6) delay_ratio (delay - free_flow_delay) / max(free_flow_delay, 1e-6) return np.array([saturation, occupancy, delay_ratio], dtypenp.float32)最直觀的變化是這個狀態(tài)向量里的所有數(shù)字在A路口和B路口都有了近似相同的語義。策略不再需要從“車道數(shù)差異”里反向推導(dǎo)交通壓力壓力已經(jīng)直接在輸入端擺好了。3.2 獎勵函數(shù)改成“比例化標(biāo)尺”狀態(tài)接口換掉之后獎勵函數(shù)也必須同步改造不能繼續(xù)用絕對排隊(duì)數(shù)或者絕對延誤做獎勵項(xiàng)。我最后用的獎勵公式是三個無量綱項(xiàng)的加權(quán)組合R -α · |S_current ? S_target| ? β · (delay / free_flow_delay) ? γ · switch_penalty這里S_current是當(dāng)前路口的綜合飽和度S_target是目標(biāo)飽和度一般設(shè)為0.8到0.9之間的理想?yún)^(qū)間delay / free_flow_delay是延誤放大倍數(shù)反映的是“比自由流慢了多少倍”而不是具體的秒數(shù)switch_penalty用來懲罰過于頻繁的相位切換。這樣設(shè)計(jì)的核心好處是獎勵函數(shù)各項(xiàng)在不同場景下?lián)碛型粋€“標(biāo)尺”。A路口晚高峰排隊(duì)30輛可能意味著飽和度接近1.0B路口排隊(duì)30輛可能飽和度只有0.6獎勵函數(shù)會自動給出完全不同的評價。模型學(xué)到的優(yōu)化目標(biāo)是“把壓力拉回目標(biāo)區(qū)間”而不是“把某個絕對數(shù)字壓到最小”。這是整個泛化改造里性價比最高的一處調(diào)整改完之后就算不換模型結(jié)構(gòu)B路口的零樣本遷移獎勵也回升了不少。3.3 動作空間從“指定綠燈時長”改成“延長決策”第三步改造是動作空間。舊版本的動作輸出的是“下一個相位的綠燈絕對時長”這意味著網(wǎng)絡(luò)必須學(xué)會不同路口綠燈時長約束的絕對值差異A路口最大綠燈60秒B路口最大綠燈45秒輸入輸出映射完全不同。這個設(shè)計(jì)天然讓動作層面也綁定了場景。我把動作設(shè)計(jì)改成了“保持/結(jié)束當(dāng)前相位以及是否延長一個固定決策步長”。模型只輸出兩個動作繼續(xù)延長當(dāng)前相位或者切換到下一個相位。具體延長多少秒由環(huán)境內(nèi)部的約束參數(shù)處理。這樣改動之后動作的語義在任意路口都是一樣的——“我現(xiàn)在覺得當(dāng)前相位還需要再等等”還是一個二分類決策。模型不需要在輸出層去擬合不同路口的絕對時長差異環(huán)境約束又保證了動作不會超過該路口的最大綠燈限制。從實(shí)際效果看這個改造雖然簡單卻讓策略在結(jié)構(gòu)變化的路口之間遷移時少了一個很大自由度的外推。4. “多世界訓(xùn)練”的組合拳域隨機(jī)化、課程學(xué)習(xí)與場景條件化策略4.1 域隨機(jī)化必須隨機(jī)“結(jié)構(gòu)變體”不能只隨機(jī)參數(shù)光改狀態(tài)、獎勵和動作還不夠訓(xùn)練數(shù)據(jù)分布還是來自單一路口模型依然會被單一場景的經(jīng)驗(yàn)池鎖住。要真正逼出泛化能力必須讓訓(xùn)練過程本身就覆蓋多個“世界”。我用的第一招是域隨機(jī)化即訓(xùn)練時不斷隨機(jī)環(huán)境的配置。不少文章提域隨機(jī)化時只隨機(jī)數(shù)值參數(shù)比如車流量乘個0.7到1.3的系數(shù)駕駛員激進(jìn)程度隨機(jī)取值。這樣做的效果有限因?yàn)槟P腿匀恢馈奥房谝欢ㄊ撬南辔?、一定沒有右轉(zhuǎn)專用道”這個隱性前提。我的做法是在參數(shù)隨機(jī)之外加上了結(jié)構(gòu)性變體的隨機(jī)化像車道數(shù)量組合、是否存在右轉(zhuǎn)專用道、相位配置是四相位還是三相位、信號周期范圍都作為可隨機(jī)項(xiàng)加入訓(xùn)練環(huán)境。初始域隨機(jī)化的配置表大概長這樣隨機(jī)維度隨機(jī)范圍各進(jìn)口道流量倍率0.6x ~ 1.4x跟車模型的安全距離參數(shù)0.8 ~ 1.5駕駛員激進(jìn)程度隨機(jī)采樣三檔車道數(shù)組合雙向二車道/雙向三車道/雙向四車道相位結(jié)構(gòu)三相位、四相位、帶右轉(zhuǎn)專用道四相位最大綠燈時長30秒 ~ 75秒這樣訓(xùn)練時模型每換一個episode就身處一個新“世界”同一套策略權(quán)重必須同時在各種結(jié)構(gòu)變體上都表現(xiàn)得不錯才能獲得穩(wěn)定獎勵。模型就失去了“背單一路口指紋”的機(jī)會。4.2 課程學(xué)習(xí)別讓多任務(wù)一擁而上不過把所有隨機(jī)變體直接同時丟進(jìn)去前期訓(xùn)練會非常容易出現(xiàn)梯度震蕩。原因很簡單不同難度場景的回報(bào)差異太大低難度場景已經(jīng)把獎勵推得很高高難度場景還在原地掙扎優(yōu)化方向互相拉扯。我用了課程學(xué)習(xí)來做平滑過渡。訓(xùn)練階段分三級第一階段用最簡單的場景組合低流量、雙向二車道、四相位固定結(jié)構(gòu)先把策略學(xué)到能穩(wěn)定處理基礎(chǔ)壓力。第二階段加入中等復(fù)雜度的結(jié)構(gòu)變體比如雙向三車道、帶右轉(zhuǎn)道、流量倍率放寬到0.8到1.2。第三階段才打開全部隨機(jī)域。關(guān)于“難度”的定義我在代碼里按一個綜合分?jǐn)?shù)排序擁堵狀態(tài)出現(xiàn)頻率、相位數(shù)量、結(jié)構(gòu)變體差異度。每一級訓(xùn)練中環(huán)境采樣遵循難度越高采樣概率越小的原則防止模型被極端難的高維結(jié)構(gòu)變體直接打懵。課程學(xué)習(xí)在日志里看起來會犧牲一點(diǎn)前期的單場景峰值分?jǐn)?shù)但它換來的是中后期更平滑的收斂和終局階段更好的泛化。4.3 場景條件化策略把“場景ID”明明白白喂給網(wǎng)絡(luò)第三個組合拳是給策略網(wǎng)絡(luò)增加一個“場景描述向量”作為額外輸入。訓(xùn)練時我們把每個環(huán)境的靜態(tài)結(jié)構(gòu)配置編碼成一個向量例如車道數(shù)、相位數(shù)、是否存在右轉(zhuǎn)專用道、當(dāng)前流量倍率拼在狀態(tài)向量后面一起送進(jìn)網(wǎng)絡(luò)。這樣做的好處很直接網(wǎng)絡(luò)可以學(xué)會一種條件化的映射——看到場景描述向量為“四相位無右轉(zhuǎn)道”就走A策略分支看到“三相位有右轉(zhuǎn)道”就走B策略分支。訓(xùn)練目標(biāo)是學(xué)習(xí)條件分布π(a|s, c)而不是把所有場景的平均解壓到一個點(diǎn)。這樣一來不同場景之間的參數(shù)沖突大幅降低域隨機(jī)化訓(xùn)練也不容易顧此失彼。實(shí)現(xiàn)上就是把狀態(tài)拼接一下obs np.concatenate([scene_free_state, scene_embedding], axis-1) action_logits policy_net(obs)需要注意的是加了這個條件輸入之后初期訓(xùn)練曲線看起來反而變慢了因?yàn)榫W(wǎng)絡(luò)需要額外學(xué)習(xí)“不同場景描述對應(yīng)不同策略”這個映射關(guān)系。我一開始差點(diǎn)把它關(guān)掉后來堅(jiān)持訓(xùn)練到中期才看到效果。這個現(xiàn)象后面還會再講。5. 驗(yàn)證泛化能力評估協(xié)議、防騙清單與實(shí)測數(shù)據(jù)解讀5.1 構(gòu)造“零樣本測試矩陣”改造做完之后驗(yàn)證階段是最不能糊弄的。很多人說模型泛化了但拿出來的證據(jù)永遠(yuǎn)是一張測試集獎勵曲線圖。我的建議是直接做一個結(jié)構(gòu)化的“零樣本測試矩陣”把測試環(huán)境按難度分成三檔每檔測五個以上不同配置測試類型具體配置示例零樣本要求參數(shù)擾動類A路口/流量±15%/周期長度變化不動權(quán)重直接評估結(jié)構(gòu)微變類三相位改四相位/增加右轉(zhuǎn)專用道/車道數(shù)增減不動權(quán)重直接評估極端模式類行人過街比例提高/公交優(yōu)先觸發(fā)/事故占道不動權(quán)重直接評估報(bào)告的指標(biāo)不直接報(bào)絕對數(shù)值而是報(bào)“相對基線規(guī)則比如固定最大綠信比規(guī)則的性能提升比例”。用相對基線的變化能消掉場景本身難易差異防止你拿一個本身就很簡單的場景當(dāng)“泛化成功”的證據(jù)。5.2 三個容易被“表面上好結(jié)果”騙過去的時刻第一次做泛化評估的人很容易被好結(jié)果騙到我自己就上過三次當(dāng)。第一次是在單一路口上反復(fù)調(diào)超參把A路口的獎勵曲線調(diào)到近乎完美自我感覺良好。后來換了測試矩陣才發(fā)現(xiàn)這種“完美”完全是和A場景綁定的連參數(shù)擾動類的測試都過不了。第二次是只看訓(xùn)練總獎勵曲線不看評估時的狀態(tài)分布。后來我把評估過程中的狀態(tài)做了分布外檢測發(fā)現(xiàn)訓(xùn)練曲線漂亮只是因?yàn)榻?jīng)驗(yàn)池里塞滿了同一場景樣本評估時模型遇到一堆訓(xùn)練中沒見過的狀態(tài)動作完全在瞎猜。第三次是對狀態(tài)加了極小的高斯噪聲做靈敏度測試結(jié)果策略立刻崩潰。這說明模型記住的不是“狀態(tài)規(guī)律”而是“精確的數(shù)值模板”稍微一點(diǎn)擾動就暴露了。這個測試做起來成本極低強(qiáng)烈建議每個做RL交通控制的人都加上。5.3 我現(xiàn)在的評估流程與實(shí)測數(shù)據(jù)解讀經(jīng)過以上幾個版本的改造我現(xiàn)在做RL信號控制模型評估時會走一套固定流程先跑單場景收斂性實(shí)驗(yàn)確認(rèn)策略在原場景沒有明顯性能倒退再跑參數(shù)擾動類零樣本測試矩陣驗(yàn)證對流量、周期等數(shù)值漂移的魯棒性然后跑結(jié)構(gòu)微變類測試觀察增加右轉(zhuǎn)專用道這類結(jié)構(gòu)變化時的性能保持率最后隨機(jī)挑兩個在訓(xùn)練中未出現(xiàn)過的極端場景做壓力測試。這套流程跑完之后再回頭量化改進(jìn)前后的效果。狀態(tài)與獎勵改造加上域隨機(jī)化加場景條件化之后我手里項(xiàng)目的零樣本遷移成功率從最初的約四成提升到了七成上下結(jié)構(gòu)微變類場景的平均延誤提升幅度從最初比基線規(guī)則還差變成了比基線規(guī)則領(lǐng)先約15%到20%。參數(shù)擾動類的場景表現(xiàn)更穩(wěn)定獎勵波動幅度比改造前縮小了接近一半。這個結(jié)果離“一個模型搞定所有路口”還很遠(yuǎn)但至少它證明了“相似交通問題失靈”不一定是強(qiáng)化學(xué)學(xué)習(xí)的死穴很多時候是我們把狀態(tài)表示、獎勵函數(shù)和訓(xùn)練數(shù)據(jù)分布設(shè)計(jì)成了“單場景定制款”模型沒有機(jī)會學(xué)到可泛化的決策邏輯。最后分享一個自己踩過的坑做場景條件化策略時因?yàn)榧恿藞鼍扒度牒笤缙谟?xùn)練曲線明顯變慢我一度想把它回退掉。后來堅(jiān)持多訓(xùn)練了幾十萬步模型才逐漸學(xué)會“看清場景再決策”曲線一下子反超了不加條件時的版本。強(qiáng)化學(xué)習(xí)項(xiàng)目的迭代最怕的不是模型能力不夠而是評估標(biāo)準(zhǔn)不統(tǒng)一導(dǎo)致你永遠(yuǎn)在單場景里自嗨。先把狀態(tài)里的“場景指紋”拆掉、獎勵換成一個通用標(biāo)尺、訓(xùn)練時讓模型見過足夠多的“世界”這是目前我驗(yàn)證過最靠譜的破局順序。