大數據文摘作品
編譯:元元、Chloe、朱顏夫、亭八
上周,我們帶領著團隊去參加了三星開發者大會(Samsung Developer Conference, SDC)。眾所周知,一個展位會很容易讓人變得無聊透頂,人們想要了解一個產品,可以在網際網路搜索到各種相關的信息,而傳統的免費T恤+產品傳單早已過時。在設計SDC展位的時候,我們開始思考如何製作一個有趣的展位,畢竟我們的團隊也要在上面呆兩天。於是我們做了一件事情:讓Gyroscope的AI在超級任天堂(Super Nintendo Entertainment System ,SNES)上徵戰「街頭霸王2:究極格鬥」,通過各個角色間的互搏,讓Gyroscope學會格鬥技巧。
Gyroscope 的AI通常不會用來玩電子遊戲,我們也沒有超級任天堂的軟體開發包(Software development kit,SDK)。所以在SDC大會前,我們想法設法從「街頭霸王2:究極格鬥」中提取遊戲信息,建立Gyroscope的超級任天堂SDK,然後讓 Gyroscope的 AI與遊戲內置的計算機對手進行數千場遊戲比拼,同時我們不斷調整AI的參數,讓它適應這個特殊的應用程式。為了讓現場的氣氛活躍起來,我們還為每個角色舉行了「四強」單淘汰制比賽,同時讓與會者選擇他們認為會贏的角色,選對的人可以參加SNES Classic的抽獎活動。
訓練AI
首先,我們必須弄清楚我們實際上要解決什麼問題。我們把玩街頭霸王2的問題抽象為強化學習問題(Gyroscope的AI解決方案支持的問題類型之一)。在強化學習問題中,AI評估各種方案,選擇要採取的行動,為此獲得回報。這個AI程序的目標是根據過去觀察到的行為,採取最佳的行動,來最大化可獲得的獎賞。所以在我們開始應用AI之前,我們需要定義「街頭霸王2」的觀察內容,即人工智慧「看到的」是什麼,以及行動和獎賞。
觀察內容
我們可以將這些想像成AI在環境中「看到」的東西。當人類觀察遊戲的時候,首先看到的是每個角色,以及角色的跳躍、移動、踢等,同時還能看到角色的血條和計時器。我們需要將這些信息提取出來,轉化成AI可以理解的格式,這種格式被稱為「觀察空間」。在強化學習中,觀察空間有兩種常見的思路,傳統的方法是測量我們人類認為與問題相關的具體信號。現代的方法是為AI提供每次行動後的全部環境圖像,讓AI決定圖像中的重要元素。我們通常認為現代的方法更好,因為它有更好的普適性,不需要對特徵的重要性做過多的假設,但是這種方法往往需要更長的訓練時間,考慮到時間的限制,我們選擇了傳統的方法,並手動定義觀察空間。
具體而言,我們將觀察空間定義為:
每個玩家的X和Y坐標
每個玩家的血條
每個玩家是否在跳躍
每個玩家是否蹲伏
為每個玩家的動作編號
玩家之間X和Y坐標差的絕對值
遊戲時鐘
遊戲觀察空間示例
另外,這個觀察空間是超大的,不重複的觀察點達到了萬億甚至更多!
行動
AI觀察環境後必須採取行動,最簡單的使角色行動的方法是採用超級任天堂手柄上的按鈕:上、下、左、右、A、B、X、Y、L、R。然後,單個行動就是一組按鈕組合,如果我們考慮所有可能的按鈕組合,就會產生1024(2^10)個可能的行動。但這個可能的行動太多了!即便AI最終能學會,也需要訓練很長一段時間才能知道哪些行為有效,哪些不行。不過,任何「街頭霸王2」的玩家都知道並不是所有的按鈕都可以隨時按下,而且,許多動作要通過按鍵順序達到更好的效果。
基本動作
「街頭霸王2:究極格鬥」充分利用了CAPCOM搖杆和超級任天堂手柄。下面的示意圖包括8種基本控制的位置以及他們在遊戲中的用法。
方向控制(來自街頭霸王2:究極格鬥遊戲說明書)
從12點方法順時針:跳躍,向前翻轉,向前,進攻蹲伏,蹲伏,防禦蹲伏,防禦,向後跳躍。
手柄按鈕控制(來自街頭霸王2:究極格鬥遊戲說明書)
從左上順時針:重擊,重踢,中等擊打,中等踢,輕踢,輕擊
考慮行動空間的另一個方向是一組動作,比如高踢、扔、勾拳等, 我們可以讓AI選擇一個動作,然後把這個動作轉換成一組按鈕。但確定一個角色的動作需要一段時間,因為我們需要大量的查閱Google,以及自己親自玩,而且對每個角色都要重複這個過程。所以為了縮短訓練時間,我們將動作空間簡化為一個按下方向控制和按下一個按鈕控制(例如「上+A」或「L」)的組合,同時是否按下都是可選的,這一構建方法使得行動空間縮減成了35個可能的行動。另外還要告訴各位的是,更高級的動作和組合仍然可能隨著訓練時間的增加而出現,但是這部分留給了AI自行探索。
回報
最後,我們還要去思考:一旦採取行動,AI就會收到回報。當人類玩遊戲的時候,會通過血條和傷害的大小,對遊戲目前的狀況大體上有一個認識。所以AI需要通過一個數字的形式來理解遊戲狀況,讓它們使這個數字最大化從而獲得最佳獎勵,我們選擇了每一幀的血條差距作為回報。所以,在每次觀察時,AI都會得到相當於玩家之間血條差距的獎勵。例如,如果AI通過踢對手造成對方受到10點傷害,之後的血條差距將會是10點,AI得到同樣數量的回報。但如果AI在下次觀察後不採取行動,在「無」的情況下仍然得到10點,因為它保持了血條差距。相反的,如果AI被踢並且沒有防禦下來,則血條差距將會減小。所以這個差值可能也是負的,這表明AI此時的狀態不佳。
一場街頭爭霸比賽中Dhalsim(一個遊戲角色)得到的回報
創造人工智慧的人工智慧
以上是我們討論的最終在比賽中採用問題的構建方法。同時我們也調整了AI系統的參數,因為Gyroscope的自主AI是一個算法的算法,它可以找到每個問題適用的算法。有了這麼多關於「街頭霸王」問題的信息之後,我們抄了一個近道,選擇深度Q網絡 (Deep Q-network, DQN)作為強化學習方法,也對DQN進行了一些修改。值得一提的是基於圖像觀察空間缺失的修改:DQN使用模型來預測哪些行動最佳,而不是用窮舉法測試每個可能的行動。畢竟考慮到觀察空間的大小,探索每個可能的行動幾乎是不可能的。
模擬器連接
在我們訓練人工智慧之前,我們必須把它連接到街頭霸王上。Gyroscope可以通過iOS和Unity的SDK連接。但我們目前還沒有超級任天堂的SDK,因此我們需要找到可以幫助我們測試超級任天堂遊戲的工具,以便我們可以使用我們的AI技術來玩這些遊戲。幸運的是,我們從電腦輔助競速(Tool-assisted speedrun)社區找到了合適的連接經典遊戲機的工具(競速一族為了儘可能快地贏得各種遊戲,他們通常一幀一幀的查找遊戲漏洞)。
怎麼誇都不夠
我們不僅需要模擬器,還需要支持模擬器核心的工具。我們發現了BizHawk,它可以支持多種模擬器核心,包括超級任天堂的核心。
BizHawk可以提供的重要功能:
一個Lua語言腳本界面,讓我們逐幀控制遊戲;
一套控制臺內存檢查工具,以便檢查遊戲內存(全部或特定地址);
運行中可以不受速度限制,也不需要顯示,從而最大化遊戲的幀率;
BizHawk原始碼。
特別對於「街頭霸王」而言,Lua界面允許我們發送手柄按鍵信號,讀取按下按鈕信號,讀取存儲位置以及控制核心模擬器。內存檢測器讓我們獲取對手的血條情況,對手的動作以及其他觀察數據。請注意,我們只使用人類玩家知道的信息;我們沒有讓AI了解人類不知道的任何信息。
老實說,我們怎麼誇BizHawk都不夠。不僅產品是一流的,原始碼也非常整潔,可讀,可擴展。我們很高興與這個代碼庫合作——後面你就可以看到了,原始碼變非常重要。
初次嘗試:用Lua寫Gyroscope SDK
BizHawk應用程式嵌入了Lua腳本引擎,並對該引擎開放了一些模擬器功能。所以初次嘗試我們自然而然地想到用Lua寫Gyroscope SDK。我們寫了一個Lua庫,用於訪問所有的內存位置,這些位置隨後會被轉換為觀察結果,還用於向模擬器發送鍵盤按鍵。
但是,如何把Lua中的數據放入Gyroscope呢?要知道,Lua接口不支持任何networkI/O!而我們的服務又在雲端運行,這是一個大問題。對此我們唯一能用的只有fileI/O和SQLite I/O。
我們寫了一些python代碼,從Lua寫的文件中讀取遊戲觀察結果並將其發送到Gyroscope,但是很難與Lua同步,而且將動作(按按鈕)返回到Lua也很奇怪。再者,這一過程特別慢,即便把文件放入RAM磁碟也依然很慢。我們嘗試用SQLite來做同樣的事,也遇到了同樣的問題——速度太慢。
鑑於此,我們決定將SDK代碼從Lua轉移到本地的BizHawk工具;這些工具是用C#寫的,BizHawk全部都是用C#寫的。我們保留了之前寫的python代碼,因為它提供了一個簡易的接口與我們的服務(gRPC語言)對接,還保證了AI玩家之間的同步(確保他們在相同的幀,等等)。我們把這些python代碼命名為模擬器控制器。
妥了:全部用C#寫
BizHawk提供了一個簡單的C#界面,利用工具來控制遊戲和模擬器的方方面面。我們使用這一接口將Lua代碼導入C#,很快有了一個用C#操作街霸的工具。
在C#中我們能夠訪問所有的.NET庫,所以很快通過插口連接到我們的模擬器控制器代碼。我們從遊戲中的每一幀抓取一個觀察結果,將觀察結果發送給模擬器控制器,控制器將諮詢Gyroscope AI,並向模擬器返回下一幀應該按下的動作(按鈕)。
我們現在有了運行街霸II的有效方法,就像在主機上玩一樣快,向Gyroscope發送遊戲觀察結果,返回控制器應按下的按鈕動作。我們也有能力同步比賽的AI機器人雙方。是時候出來訓練了!
綜合起來:訓練AI
訓練初期,AI(圖中Dhalsim)隨機按下按鈕
定義好觀察結果、動作、獎勵值,再將AI連接到超級任天堂,我們準備好啦。針對內置的遊戲機器人來訓練我們的AI。每個角色大約訓練8小時或者說3000場比賽。
我們的假設是,訓練好的AI將:
(1)最大限度地提高獎勵值,
(2)可以在訓練結束後擁有相當高的勝率。
訓練3000場比賽後,Dhalsim積極進取,勝率50%
因為玩街霸是對我們服務創造性的應用,所以我們預設要做一些必要的調整——我們的AI之前不會對上述快速獎勵值進行優化,也沒有控制過這樣大的動作空間。經過了兩個特別有意思的周末,我們嘗試了觀察空間、動作空間、獎勵值函數和DQN參數的許多變體,直到得到一個高勝率的AI。
訓練期間的勝率和模型損失
除了標準模型調優技術和良好的科學原則(一次只改變一個量),我們還有一個重大發現:方向控制按壓與按鈕控制按壓的權重不同。我們發現方向控制只對一幀有效,在遊戲中影響很小;然而,按鈕控制一旦按下,作用會維持一系列幀,在遊戲中影響重大。比如,完成拳擊這一動作需要很多幀。這意味著在一幀內採取的動作會延續很多幀。此外,雖然與方向控制按壓相比,按鈕按壓非常重要,但相應地也需要更加頻繁的按壓才能起作用。為了完成這一遊戲行為,也為了使AI行為更加人性化,我們讓AI在20幀(即1/3秒)內一直重複按鈕按壓,完後再採取下一個動作。在這20幀內獎勵值累積。換句話說,我們讓AI以1/3秒遊戲時間為單位來採取動作、觀察結果,不是以每幀為單位。
我們常常被問到,為什麼不用「獲勝」作為獎勵值。簡單來說,這樣做獎勵值是延遲的,會導致訓練更困難更耗時。健康差值是合理的探索,我們相信這樣會引起獲勝——嗯,它做到了。
Gyroscope獲勝!
80%勝率;注意AI機智地攔截對方招數,還神奇地走位
剛開始訓練的時候,我們的AI隨機行動,對戰3星級對手(街霸採用星級評價體系)的勝率是20%。所以,20%勝率是底線,超過20%才能說明AI取得了成效。最後, AI對抗遊戲內置3星級機器人的勝率達到90%!設置得非常簡單,訓練時間又短,能取得這樣的成績我們很激動。此外,我們預計訓練過程再長點的話還能達到更高的勝率,但可能會對訓練用的機器人過擬合。針對比賽,取得80%勝率後我們就停止訓練,以避免過擬合。
WindowsBAT腳本最差
一切都很糟
AI獲勝後,我們開始用街霸II的每個角色訓練它。為了訓練每個角色,我們使用Google雲端平臺支持多個Windows Server 2016實例(在Windows上構建BizHawk效果最佳),然後寫了一些.bat腳本進行全部的訓練。訓練需要通過一些R腳本來自動完成玩家選擇、遊戲重置、模型記錄、進度繪製等功能。我們為BizHawk增加了一些命令行選項,使其更容易自動化。
會場上:戰鬥吧!
SDC上我們的展位
會場上,我們布置展位來展示四場AI戰鬥,場場都是兩個AI控制的角色對抗。我們還畫了比賽樹狀圖——安排展位沒展示到的角色參加比賽。
我們擺放了分別貼有每個角色照片的罐子,向觀眾發放抽獎券。觀眾將抽獎券放入自己認為會獲勝的角色的罐子裡;比賽結束後,我們從獲勝角色的罐子中抽一張券,這張券的持有人獲得一臺迷你超級任天堂!我們還展示了訓練過程,讓觀眾看到GyroscopeAI的工作原理。
每天下午4:30,我們按樹狀圖進行比賽。先測試一場,再進行正式比賽。
第一天的比賽:M.Bison碾壓全場。
四分之一決賽
Guile對戰Vega:Guile被吊打。Vega AI很快就學會了縮短距離,彎腰躲閃,刺向對方,用著飄逸的走位,Vega勝出。
Blanka對戰M.Bison:M.Bison實力碾壓。他的獨門攻擊幾乎無法阻擋,就這樣,M.Bison勝出。
Chun-Li對戰Sagat:Chun-Li也是近距離作戰——她的速度和近地攻擊打敗了Sagat的長距離襲擊和頻繁神奇的走位。Chun-Li勝出。
Balrog對戰Dhalsim:十分有趣——Dhalsim幾乎一直在空中,用他的長腿攻擊Balrog。Dhalsim勝出。
半決賽
Vega對戰M.Bison:M.Bison的攻擊太猛烈了。M.Bison進入決賽。
Chun-Li對戰Dhalsim:Dhalsim在空中發起的進攻殺傷力非常大,輕鬆擊敗Chun-Li。
決賽
M.Bison對戰Dhalsim:基本上M.Bison角色太過強大以至於無可匹敵。M.Bison獲勝!
第二天的比賽:E.Honda攪動風雲
E.Honda對戰Blanka
第二天,我們重新開始比賽,M.Bison從比賽中除名(夜裡他因以作弊代碼的形式濫用興奮劑被捕)。我們選擇讓E.Honda加入,但他在測試時表現很差。
當天有兩場戰鬥格外引人注目:
(1)Vega對抗Sagat,是一場持久戰,Vega靠近Sagat時,至少躲過了三次Sagat的神奇走位(兩次是拿準時間的彎腰躲閃,一次是躍過火球);(2)決賽,E.Honda對抗Sagat。決賽E.Honda擊敗Sagat是一場難以置信的戰鬥,最後他倆的血量都已經接近0了,這時E.Honda一擊制勝。E.Honda這次能贏真的是運氣特別好,因為我們之後又重新進行了100場E.Honda對戰Sagat的比賽,E.Honda只贏了11場。
Gyroscope創始人(我們)和第二天迷你超級任天堂的獲得者