:從狀態(tài)空間設(shè)計到?jīng)Q策函數(shù)實現(xiàn))
1. 從能算到會算計算智能體到底在算什么很多人第一次聽到計算智能體這個詞腦子里浮現(xiàn)的可能是科幻電影里那種能自己思考、自己做決定的機器人。但如果你真的動手寫過幾行代碼就會發(fā)現(xiàn)所謂智能體本質(zhì)上就是一個感知環(huán)境、做出決策、執(zhí)行動作的循環(huán)結(jié)構(gòu)。它不神秘甚至可以說樸素得有點讓人失望。真正有意思的地方在于這個循環(huán)里的決策部分到底是怎么算出來的。我在帶一些剛?cè)腴T的朋友做項目時最常遇到的一個誤區(qū)就是他們把智能體等同于神經(jīng)網(wǎng)絡(luò)覺得不搭個深度學習模型就不叫人工智能。實際上計算智能體的核心在于計算兩個字——它強調(diào)的是用可計算的方式去逼近一個決策函數(shù)。這個決策函數(shù)可以簡單到一張查找表也可以復雜到一個多層網(wǎng)絡(luò)關(guān)鍵在于它能不能在給定輸入的情況下穩(wěn)定地輸出一個合理的動作。舉個具體的例子。假設(shè)你要做一個簡單的網(wǎng)格世界導航智能體環(huán)境是一個 5x5 的格子智能體從左上角出發(fā)目標是右下角中間有一些障礙物。這個場景下智能體的感知就是當前坐標和周圍格子的狀態(tài)決策就是往上下左右哪個方向走動作就是移動一步。你可以用強化學習去訓練它也可以用 A* 算法直接算出一條路徑。兩種方式都能讓智能體到達終點但背后的計算邏輯完全不同。這就是計算智能體基礎(chǔ)要講清楚的第一件事智能體的能力邊界取決于你給它設(shè)計的計算模型而不是取決于你用了多花哨的框架。很多初學者一上來就想著用最前沿的算法結(jié)果連環(huán)境的狀態(tài)空間都沒定義清楚最后跑出來的結(jié)果自然是一團糟。我在實際項目中總結(jié)出一個經(jīng)驗先把智能體的決策過程用偽代碼寫出來再考慮用什么技術(shù)去實現(xiàn)。偽代碼里如果出現(xiàn)了根據(jù)經(jīng)驗判斷這種模糊表述那就說明你還沒想清楚它到底該怎么算。真正可落地的智能體每一步?jīng)Q策都應該是可追溯、可復現(xiàn)的。1.1 計算智能體的三個基本構(gòu)件不管你是做游戲 AI、推薦系統(tǒng)還是自動化調(diào)度一個完整的計算智能體通常包含三個部分狀態(tài)表示、決策函數(shù)和反饋機制。狀態(tài)表示決定了智能體看到什么決策函數(shù)決定了它想到什么反饋機制決定了它學到什么。狀態(tài)表示是最容易被低估的一環(huán)。我見過太多項目模型結(jié)構(gòu)設(shè)計得很漂亮但狀態(tài)特征提取得一塌糊涂最后效果還不如一個簡單的規(guī)則引擎。狀態(tài)表示的核心原則是讓相似的狀態(tài)在數(shù)值上接近讓不同的狀態(tài)在數(shù)值上遠離。比如在網(wǎng)格世界里用坐標 (x, y) 表示狀態(tài)就比用格子編號 0-24 更合理因為坐標保留了空間上的鄰近關(guān)系。決策函數(shù)的實現(xiàn)方式有很多種。最簡單的是查表法把每個狀態(tài)對應的最優(yōu)動作存下來運行時直接查。這種方式在狀態(tài)空間小的時候非常高效但狀態(tài)一多就存不下了。稍微復雜一點的是線性函數(shù)逼近用一組權(quán)重和特征做點積選得分最高的動作。再往上就是各種非線性逼近器比如決策樹、神經(jīng)網(wǎng)絡(luò)等。反饋機制是智能體進化的關(guān)鍵。沒有反饋智能體就只是一個固定的程序不會隨著經(jīng)驗變強。反饋可以是即時的獎勵信號也可以是延遲的評估結(jié)果。設(shè)計反饋機制時要注意一點獎勵信號必須和最終目標一致。我見過一個做庫存調(diào)度的項目智能體學會了通過大量下單來獲取滿足訂單的獎勵結(jié)果庫存成本爆炸。這就是獎勵設(shè)計出了偏差。1.2 為什么從計算角度切入比從學習角度切入更靠譜現(xiàn)在市面上講人工智能的內(nèi)容大多是從學習角度切入的監(jiān)督學習、無監(jiān)督學習、強化學習分類得很清楚。但對于剛接觸智能體編程的人來說從計算角度切入其實更友好。原因很簡單學習只是計算的一種手段而不是目的。智能體的目的是做出好的決策學習只是幫助它找到好的決策函數(shù)。如果你連決策函數(shù)長什么樣都沒想清楚盲目上學習算法就像不知道目的地就開車上路油燒完了也到不了。從計算角度切入你會先問自己幾個問題狀態(tài)空間有多大動作空間有多大決策函數(shù)需要多高的精度反饋信號是稀疏的還是密集的這些問題的答案會直接告訴你該用什么方法。狀態(tài)空間小、動作離散查表就夠了狀態(tài)連續(xù)、動作連續(xù)可能就需要函數(shù)逼近反饋稀疏可能需要考慮探索策略。我在實際教學中發(fā)現(xiàn)那些先從計算角度理解智能體的朋友后面學強化學習時反而更快。因為他們已經(jīng)理解了決策函數(shù)和反饋的關(guān)系再學 Q-learning、策略梯度這些方法時只是在學如何更新決策函數(shù)而已。2. 狀態(tài)空間設(shè)計智能體看到的世界決定了它的上限狀態(tài)空間設(shè)計是計算智能體編程里最考驗功力的部分。它不像模型調(diào)參那樣有明確的指標可以優(yōu)化更多時候靠的是對問題本身的理解。我經(jīng)常跟朋友說你給智能體什么樣的狀態(tài)表示它就能學到什么樣的策略不多也不少。舉個我親身經(jīng)歷的例子。之前做過一個簡單的交易策略智能體最初的狀態(tài)表示只用了過去 5 天的價格。結(jié)果智能體學出來的策略非常短視總是在小波動里頻繁買賣。后來我把狀態(tài)改成過去 20 天的價格加上成交量智能體才開始捕捉到一些中期趨勢。狀態(tài)表示一變學出來的策略完全不一樣。狀態(tài)空間設(shè)計的核心矛盾在于信息太少智能體學不到有用的策略信息太多狀態(tài)空間爆炸學習效率急劇下降。這個平衡點怎么找取決于你對問題的理解深度。2.1 離散狀態(tài)與連續(xù)狀態(tài)的取舍離散狀態(tài)和連續(xù)狀態(tài)各有優(yōu)劣選擇哪種取決于問題的性質(zhì)和你要用的算法。離散狀態(tài)的好處是直觀、易于枚舉、便于查表。在網(wǎng)格世界、棋盤游戲、簡單的資源調(diào)度這類問題里離散狀態(tài)是自然的選擇。但離散狀態(tài)有個致命問題維度災難。假設(shè)你有 10 個特征每個特征取 10 個值狀態(tài)空間就是 10 的 10 次方這個數(shù)量級根本沒法枚舉。連續(xù)狀態(tài)的好處是表達能力強、不需要離散化、適合梯度類算法。但連續(xù)狀態(tài)也有麻煩相似性度量變得困難。在離散空間里兩個狀態(tài)要么相同要么不同在連續(xù)空間里你需要定義一個距離度量來判斷兩個狀態(tài)有多接近。我的經(jīng)驗是如果問題本身是離散的就用離散狀態(tài)如果問題本身是連續(xù)的優(yōu)先考慮連續(xù)狀態(tài)實在不行再離散化。離散化會引入人為的邊界處理不好會導致智能體在邊界附近行為不穩(wěn)定。2.2 特征工程在智能體編程中的特殊地位在傳統(tǒng)機器學習里特征工程很重要在智能體編程里特征工程更重要。因為智能體的決策是序列化的一個狀態(tài)表示上的小缺陷可能會在多個時間步上累積放大。做特征工程時我通常會遵循幾個原則。第一保留時間信息。智能體的決策往往依賴于歷史所以狀態(tài)里應該包含最近若干步的信息而不是只有當前時刻。第二歸一化數(shù)值范圍。不同特征的數(shù)值范圍差異太大會導致學習算法難以收斂把特征縮放到相近的范圍是基本操作。第三避免冗余特征。兩個高度相關(guān)的特征同時放進狀態(tài)里不僅增加計算量還可能讓學習算法難以分配權(quán)重。還有一個容易被忽略的點狀態(tài)表示應該盡量滿足馬爾可夫性。也就是說當前狀態(tài)應該包含做出最優(yōu)決策所需的所有信息不需要再回顧更早的歷史。如果狀態(tài)不滿足馬爾可夫性智能體的決策就會變得不穩(wěn)定。當然實際中很難完全滿足但我們可以通過包含更多歷史信息來逼近。3. 決策函數(shù)的幾種實現(xiàn)路徑與選型邏輯決策函數(shù)是智能體的大腦它決定了給定狀態(tài)下該采取什么動作。實現(xiàn)決策函數(shù)的方式有很多種從最簡單的規(guī)則表到復雜的神經(jīng)網(wǎng)絡(luò)每種方式都有它的適用場景。選型的時候不要盲目追求復雜而要根據(jù)問題的規(guī)模、數(shù)據(jù)的多少、計算資源的限制來綜合判斷。我一般會按照從簡到繁的順序來嘗試。先用最簡單的規(guī)則或查表跑通流程看看效果如何。如果效果不夠好再逐步增加復雜度。這樣做的好處是你能清楚地知道每一步改進帶來了多少收益而不是一上來就堆一個復雜模型最后連問題出在哪都找不到。3.1 查表法與規(guī)則引擎簡單但不過時查表法是最樸素的決策函數(shù)實現(xiàn)方式。它的邏輯是預先計算好每個狀態(tài)對應的最優(yōu)動作存成一張表運行時直接查。在狀態(tài)空間小、動作離散的問題里查表法往往是最優(yōu)選擇。規(guī)則引擎是查表法的變體它用一組 if-else 規(guī)則來代替顯式的表格。規(guī)則引擎的好處是易于理解和修改適合那些決策邏輯比較清晰的問題。比如一個簡單的客服分流智能體可以用規(guī)則來判斷用戶的問題類型然后路由到相應的處理流程。但查表法和規(guī)則引擎的局限也很明顯它們無法泛化。對于沒見過的狀態(tài)查表法只能隨機選一個動作規(guī)則引擎可能直接報錯。所以它們適合狀態(tài)空間有限且可以完全枚舉的問題。3.2 線性函數(shù)逼近性價比最高的起點當狀態(tài)空間大到無法枚舉時線性函數(shù)逼近是一個很好的起點。它的核心思想是用一組特征和權(quán)重的線性組合來評估每個動作的價值然后選價值最高的動作。線性函數(shù)逼近的優(yōu)點是計算快、易于調(diào)試、不容易過擬合。缺點是表達能力有限只能表示線性關(guān)系。但在很多實際問題里線性模型的效果已經(jīng)足夠好了。我做過一個資源分配的項目用線性函數(shù)逼近的智能體效果比后來嘗試的神經(jīng)網(wǎng)絡(luò)還要穩(wěn)定因為問題本身的結(jié)構(gòu)就比較接近線性。用線性函數(shù)逼近時特征的設(shè)計是關(guān)鍵。我通常會包含以下幾類特征當前狀態(tài)的直接指標、歷史統(tǒng)計量、動作本身的屬性、狀態(tài)和動作的交互項。交互項特別重要因為它能讓模型捕捉到在某些狀態(tài)下某個動作特別好這種條件性關(guān)系。3.3 非線性逼近與神經(jīng)網(wǎng)絡(luò)什么時候值得上神經(jīng)網(wǎng)絡(luò)作為決策函數(shù)優(yōu)勢在于表達能力強能擬合復雜的非線性關(guān)系。但它的缺點也很明顯需要大量數(shù)據(jù)、訓練不穩(wěn)定、調(diào)參困難、可解釋性差。我的建議是只有當線性模型明顯不夠用且你有足夠的數(shù)據(jù)和計算資源時才考慮上神經(jīng)網(wǎng)絡(luò)。判斷線性模型是否夠用的方法很簡單看看訓練誤差和驗證誤差的差距。如果兩者都很高說明模型欠擬合可能需要非線性模型如果訓練誤差低但驗證誤差高說明過擬合加非線性只會更糟。如果決定用神經(jīng)網(wǎng)絡(luò)有幾個實操要點。第一輸出層根據(jù)動作空間設(shè)計。離散動作空間用 softmax 輸出每個動作的概率或 Q 值連續(xù)動作空間用高斯分布輸出均值和方差。第二輸入層要做歸一化。狀態(tài)特征的數(shù)值范圍差異太大會導致訓練困難。第三網(wǎng)絡(luò)不要一上來就搞太深。很多問題兩層隱藏層就夠了層數(shù)多了反而難訓練。4. 反饋機制與學習循環(huán)智能體如何從經(jīng)驗中變強反饋機制是智能體區(qū)別于普通程序的關(guān)鍵。普通程序的行為是固定的智能體的行為會隨著經(jīng)驗積累而變化。這種變化的能力來自于反饋機制對決策函數(shù)的調(diào)整。設(shè)計反饋機制時最核心的問題是獎勵信號怎么定義。獎勵定義得好智能體學得快、學得對獎勵定義得不好智能體可能學出一些匪夷所思的行為。我見過一個經(jīng)典的獎勵設(shè)計失誤案例。有個朋友做迷宮導航智能體獎勵設(shè)置是到達終點給正獎勵每走一步給小的負獎勵。結(jié)果智能體學會了在終點附近來回踱步因為這樣既能不斷獲得接近終點的獎勵又不用承擔到達終點后游戲結(jié)束的風險。后來他把獎勵改成只有到達終點才給正獎勵其他時候獎勵為零智能體才學會了直奔終點。4.1 稀疏獎勵與密集獎勵的權(quán)衡稀疏獎勵是指只有少數(shù)狀態(tài)有獎勵信號大部分狀態(tài)獎勵為零。密集獎勵是指幾乎每個狀態(tài)都有獎勵信號。兩者各有優(yōu)劣。稀疏獎勵的優(yōu)點是目標明確智能體不會因為中間獎勵而偏離最終目標。缺點是學習效率低智能體可能需要大量隨機探索才能偶然碰到獎勵信號。在稀疏獎勵場景下通常需要配合探索策略比如 ε-greedy 或玻爾茲曼探索。密集獎勵的優(yōu)點是學習信號豐富智能體可以快速獲得反饋。缺點是獎勵設(shè)計不當可能導致智能體鉆空子學出偏離目標的行為。設(shè)計密集獎勵時要確保中間獎勵和最終目標是一致的不能出現(xiàn)獲得中間獎勵但損害最終目標的情況。我的經(jīng)驗是如果問題本身有明確的階段性目標可以用密集獎勵如果問題只有一個最終目標優(yōu)先考慮稀疏獎勵配合探索策略。折中的方案是使用勢函數(shù)來塑造獎勵把最終目標轉(zhuǎn)化為一個勢能函數(shù)智能體每靠近目標一步就獲得一點獎勵但總獎勵仍然由最終目標決定。4.2 探索與利用的平衡策略探索與利用的平衡是智能體學習中的經(jīng)典問題。利用是指選擇當前認為最好的動作探索是指嘗試其他動作以獲取更多信息。只利用不探索智能體可能陷入局部最優(yōu)只探索不利用智能體永遠學不到有效的策略。最簡單的平衡策略是 ε-greedy以 ε 的概率隨機探索以 1-ε 的概率選擇當前最優(yōu)動作。ε 通常隨著時間衰減初期多探索后期多利用。這個策略簡單有效適合大多數(shù)場景。稍微復雜一點的是玻爾茲曼探索根據(jù)動作價值的 softmax 分布來選擇動作價值高的動作被選中的概率大但價值低的動作也有機會被選中。這種方式比 ε-greedy 更平滑但需要調(diào)節(jié)溫度參數(shù)。還有一種思路是樂觀初始化把動作價值的初始值設(shè)得比實際可能的值高這樣智能體在初期會傾向于嘗試那些還沒被充分探索的動作。這種方式不需要顯式的探索參數(shù)但只適合靜態(tài)環(huán)境。4.3 學習率與折扣因子的實際影響學習率決定了每次更新時決策函數(shù)變化的幅度。學習率太大更新不穩(wěn)定可能震蕩甚至發(fā)散學習率太小學習速度慢可能需要很長時間才能收斂。我通常從 0.1 或 0.01 開始嘗試根據(jù)訓練曲線的穩(wěn)定性來調(diào)整。折扣因子決定了智能體對未來獎勵的重視程度。折扣因子接近 1智能體更重視長期回報折扣因子接近 0智能體更重視即時回報。這個參數(shù)的選擇取決于問題的性質(zhì)。如果問題的回報周期很長折扣因子應該設(shè)得大一些如果問題更看重即時效果折扣因子可以設(shè)小一些。這兩個參數(shù)往往需要一起調(diào)。學習率大、折扣因子小智能體學得快但可能短視學習率小、折扣因子大智能體有遠見但學得慢。實際調(diào)參時可以先固定一個調(diào)另一個找到大致范圍后再聯(lián)合微調(diào)。5. 從零搭建一個計算智能體的完整實操路徑前面講了原理和設(shè)計思路這一部分我把整個流程串起來給出一條從零搭建計算智能體的實操路徑。這條路徑適用于大多數(shù)中小規(guī)模的問題你可以根據(jù)具體情況調(diào)整。5.1 環(huán)境建模與狀態(tài)定義第一步是環(huán)境建模。你需要明確智能體所處的環(huán)境是什么狀態(tài)如何轉(zhuǎn)移動作如何影響狀態(tài)。這一步不需要寫代碼用紙筆或者文檔把邏輯理清楚就行。環(huán)境建模的關(guān)鍵是定義清楚狀態(tài)轉(zhuǎn)移函數(shù)給定當前狀態(tài)和動作下一個狀態(tài)是什么。如果狀態(tài)轉(zhuǎn)移是確定性的直接寫映射關(guān)系如果是隨機性的需要定義概率分布。很多初學者跳過這一步直接寫代碼結(jié)果寫到一半發(fā)現(xiàn)邏輯有漏洞返工成本很高。狀態(tài)定義要遵循前面說的原則保留時間信息、歸一化數(shù)值范圍、避免冗余特征、盡量滿足馬爾可夫性。定義完之后可以手動模擬幾條軌跡看看狀態(tài)表示是否合理。5.2 決策函數(shù)的初始化與訓練循環(huán)決策函數(shù)的初始化方式會影響學習效率。查表法可以直接初始化為零或隨機值線性函數(shù)逼近可以把權(quán)重初始化為零或小隨機數(shù)神經(jīng)網(wǎng)絡(luò)通常用隨機初始化但要注意避免全部初始化為零導致對稱性問題。訓練循環(huán)的基本結(jié)構(gòu)是觀察當前狀態(tài)選擇動作執(zhí)行動作觀察獎勵和下一個狀態(tài)更新決策函數(shù)重復。這個循環(huán)看起來簡單但每個環(huán)節(jié)都有細節(jié)需要注意。選擇動作時要平衡探索和利用。執(zhí)行動作后要正確獲取獎勵和下一個狀態(tài)。更新決策函數(shù)時要根據(jù)具體的算法來計算更新量。比如 Q-learning 的更新公式是Q(s,a) Q(s,a) α * (r γ * max Q(s,a) - Q(s,a))。這個公式的含義是把當前估計值和實際獲得的獎勵加上下一狀態(tài)的最大估計值之間的差距按學習率比例更新。5.3 訓練過程的監(jiān)控與調(diào)試訓練智能體時一定要有監(jiān)控手段。最直接的是看累計獎勵的變化曲線。如果累計獎勵穩(wěn)步上升說明智能體在進步如果震蕩劇烈或持續(xù)下降說明有問題。除了累計獎勵還可以監(jiān)控一些輔助指標平均每步獎勵、動作分布、狀態(tài)訪問頻率等。這些指標能幫你判斷智能體是否陷入了局部最優(yōu)或者是否在某些狀態(tài)上反復犯錯。調(diào)試智能體時一個有效的方法是回放把智能體的決策過程記錄下來事后逐步檢查。看看它在哪些狀態(tài)下做了奇怪的選擇然后分析原因。是狀態(tài)表示有問題還是獎勵設(shè)計有偏差還是探索不夠。5.4 常見問題與排查思路智能體訓練中常見的問題有幾類。第一類是不學習累計獎勵一直不上升??赡艿脑虬▽W習率太小、獎勵太稀疏、狀態(tài)表示不包含必要信息。排查時可以先檢查獎勵信號是否正常再檢查狀態(tài)表示是否合理最后調(diào)整學習率。第二類是學偏了累計獎勵上升但行為不符合預期。這通常是獎勵設(shè)計的問題。檢查獎勵函數(shù)是否和最終目標一致是否存在鉆空子的可能。第三類是不穩(wěn)定累計獎勵震蕩劇烈。可能是學習率太大或者折扣因子設(shè)置不當或者狀態(tài)表示中有噪聲??梢試L試降低學習率、調(diào)整折扣因子、對狀態(tài)特征做平滑處理。第四類是過擬合在訓練環(huán)境表現(xiàn)好在新環(huán)境表現(xiàn)差。這通常是因為狀態(tài)表示太具體或者訓練數(shù)據(jù)太少??梢試L試增加狀態(tài)表示的泛化性或者增加訓練輪次。6. 幾個容易踩的坑與我的實操心得做計算智能體這些年踩過的坑不少有些是技術(shù)上的有些是思路上的。挑幾個有代表性的分享一下希望能幫你少走彎路。第一個坑是過早優(yōu)化。剛開始做的時候總想著一步到位用最先進的算法、最復雜的模型。結(jié)果往往是花了很多時間調(diào)參效果還不如一個簡單的基線。后來我養(yǎng)成了一個習慣先用最簡單的方案跑通記錄下基線效果然后再逐步改進。這樣每一步改進都有對比知道什么有用什么沒用。第二個坑是忽略狀態(tài)表示的重要性。有段時間我沉迷于調(diào)模型結(jié)構(gòu)和超參數(shù)覺得效果不好一定是模型不夠強。后來發(fā)現(xiàn)把狀態(tài)表示改一改效果提升比調(diào)參明顯得多?,F(xiàn)在我做項目花在狀態(tài)表示上的時間往往比花在模型上的時間還多。第三個坑是獎勵設(shè)計想當然。獎勵函數(shù)看起來簡單但設(shè)計起來很微妙。我現(xiàn)在的做法是設(shè)計完獎勵函數(shù)后手動模擬幾條軌跡看看累計獎勵是否和預期一致。如果發(fā)現(xiàn)智能體可以通過某種方式刷獎勵就說明獎勵設(shè)計有漏洞。第四個坑是不做監(jiān)控和日志。早期做項目時我只在訓練結(jié)束后看最終效果中間過程一概不管。結(jié)果出了問題也不知道從哪查起。后來我養(yǎng)成了記錄訓練日志的習慣每一步的獎勵、動作、狀態(tài)都記下來出問題時可以回放分析。第五個坑是忽視計算資源限制。有些算法理論上很好但計算量太大實際跑不起來。選型時一定要考慮自己的計算資源不要盲目追求先進。在資源有限的情況下簡單高效的方法往往比復雜的方法更實用。還有一個心得是多動手少空想。智能體編程是一門實踐性很強的技能很多細節(jié)只有親手寫過才能體會到??丛俣嗟慕坛滩蝗缱约簭念^實現(xiàn)一個簡單的智能體。實現(xiàn)過程中遇到問題、解決問題這個過程本身就是最好的學習。最后說一個關(guān)于調(diào)試的技巧。當智能體表現(xiàn)異常時我通常會做一個最小復現(xiàn)把問題簡化到最小的場景去掉所有不必要的復雜性看看問題是否還存在。如果還存在就逐步添加復雜性直到找到觸發(fā)問題的條件。這個方法幫我解決了很多棘手的問題。計算智能體的基礎(chǔ)內(nèi)容大概就是這些。它不像深度學習那樣有那么多花哨的技巧但勝在邏輯清晰、可解釋性強。掌握了這些基礎(chǔ)再去學更復雜的算法會順利很多。