華文網

手把手學會人工智慧:TensorFlow實現馬里奧賽車自動駕駛,含源碼

這次年假沒啥事做,作者想著試試完成一個兩年前就已開始的項目:訓練人工智慧神經網路來玩《馬里奧賽車 64》遊戲。因為一年前做過一些機器學習,現在想蹭個熱點,TensorFlow 越來越火了。

項目:使用 TensorFlow 來訓練一個模型玩《馬里奧賽車 64》遊戲。

目標:作者想做個人人都能跟著“說明書”一步步理解和實現的機器學習 demo。

在不斷的玩《馬里奧賽車》遊戲並且用 C 語言開發了一個模擬器後,作者得到了一個漂亮的結果。

下圖是駕駛未經訓練的 Royal Raceway 部分:

動畫參看:

https://media.giphy.com/media/1435VvCosVezQY/giphy.gif

下圖是駕駛 Luigi Raceway 部分:

動畫參看 https://youtu.be/vrccd3yeXnc

達到上面的成績還是不容易的,作者想分享自己的經驗和學習到的知識。

訓練資料集

為了創建一個訓練資料集,作者編寫了一個程式來截圖 Xbox 玩《馬里奧賽車》遊戲的桌面。然後作者跑一個任天堂 N64 模擬器,定位截圖區域的位置,

如下圖:

這只是作者開始該專案以來完成的部分成果。有趣的是在幾年來做該項目中發現自己代碼風格的變化以及工具選用的變化。

機器學習模型

剛開始是修改 TensorFlow tutorial 的字元識別的例子( MNIST 資料集),改變輸入層和輸出層的大小,因為作者準備的圖片比 MNIST 資料集中的 28 x 28 要大。

修改圖片大小的工作是非常無趣的,但是 TensorFlow 提供了很好的函數來説明我們。

後來,作者轉而使用 Nvidia’s Autopilot 來開發自動駕駛部分。這簡化了資料預處理的代碼,該模型可以直接將彩色圖片轉化成向量,而不需要手動轉化成灰度圖再展開為向量。

訓練模型

為了訓練一個 TensorFlow 模型,你必須定義一個 TensorFlow 優化函數。在 MNIST tutorial 中使用的優化函數是錯誤分類的總和,而 Nvidia’s Autopilot 中使用的是預測值和角度傳感記錄的差值。

對於本項目來講,有一些重要的遊戲手柄輸出。作者使用預測輸出向量和記錄值兩者的歐式距離作為優化函數。

TensorFlow 一個最重要的特徵之一是顯式的函式定義。High Level 的機器學習框架會抽象,但是 TensorFlow 逼著開發者來思考發生了什麼,幫助他們消除機器學習的神秘。

模型訓練其實是本專案最容易的一部分。TensorFlow 有著優秀的官方文檔,以及大量的 tutorial 和原始程式碼。

一顯身手

訓練完模型,作者打算讓其在《馬里奧賽車 64》上一顯身手。但是好像還缺點啥:如何將模型訓練的結果轉換到任天堂 N64 模擬器?首先,作者使用 python-uinput 來發送輸入事件。該功能在作者以前的幾個專案中能夠作為遊戲手柄工作,但是不幸地,這次不行。

接著作者深挖開發出 mupen64plus-input-sdl,所以作者覺得開發自己的輸入外掛程式是明智的。

編寫一個 mupen64plus 輸入外掛程式

好長一段時間沒寫過一個像樣的 C 程式了,我拿到一個原始輸入驅動程式,做了一些簡單的複製粘貼工作,我的目標是在模擬器中編譯和運行一個外掛程式。 當外掛程式載入時,模擬器檢查幾個函式定義和錯誤(如果有的話)。這意味著我的外掛程式需要定義幾個空函數。

當外掛程式工作後,我搞明白了如何設置控制器輸出,讓馬里奧能夠在跑道上無限的跑起來。

下一步是向本機伺服器詢問輸入的內容。 這需要在 C 中發出 http 請求,這證明是個複雜的任務。人們抱怨分散式系統很難,的確是這樣!經過解決幾個小問題之後,我終於完成了消費資料並將其輸出到內部的 N64 模擬器。

我完成的輸入外掛程式可以在GitHub上找到。

https://github.com/kevinhughes27/mupen64plus-input-bot

玩《馬里奧賽車 64》遊戲

現在是見證奇跡的時刻了,不過第一場比賽太讓人失望了:馬里奧直接開車撞到牆上,連一個彎都沒轉。

為了 debug,作者增加了一個手動的控制,當需要時才從 AI 獲取控制。作者通過試玩,觀察返回的輸出結果,提出了兩件需要 fix 的點:

重新檢查訓練資料時,作者發現偶爾有些截圖是模擬器視窗的圖片。解決方法是簡單的從資料集中移除,繼續訓練。

作者注意到《馬里奧賽車 64》是一種比較急速的遊戲,典型的是你不能平緩的轉彎。通常,遊戲者需要在大轉彎時來個急速調整。由於資料的混淆,一些《馬里奧賽車 64》的圖片在轉彎的中間,假設模型學習從來不轉彎,而這將導致一些小誤差。模型訓練仍然是一個優化問題,如果資料集有問題,那訓練的結果也會這樣。

記住這些繼續玩《馬里奧賽車 64》遊戲來記錄新的訓練資料。

隨著上面兩點的調整,作者得到更好的結果,見文章開始部分。

最終暢想

TensorFlow 是一個超級棒的 low level 抽象的深度學習框架。受益於 cuDNN,TensorFlow 的計算速度不錯,並且梯度下降演算法也是頂尖的。不過,如果再做另外一個深度學習項目的話,作者說不會直接使用 TensorFlow,而是更願意嘗試 keras ,keras 是基於 TensorFlow 基礎上加了一些語法糖。

在寒假的幾周裡,作者已經能使用 Google 的自動駕駛技術來駕駛虛擬的賽車。資料訓練大概 20 分鐘之後,作者的 AI 已經可以駕駛大部分簡單的賽道,Luigi Raceway 和未經訓練的 racetack。作者期待使用更多的資料來構建一個完整的 AI 自動駕駛馬里奧賽車 64 遊戲。

原始程式碼

http://github.com/kevinhughes27/TensorKart

http://github.com/kevinhughes27/mupen64plus-input-bot

我拿到一個原始輸入驅動程式,做了一些簡單的複製粘貼工作,我的目標是在模擬器中編譯和運行一個外掛程式。 當外掛程式載入時,模擬器檢查幾個函式定義和錯誤(如果有的話)。這意味著我的外掛程式需要定義幾個空函數。

當外掛程式工作後,我搞明白了如何設置控制器輸出,讓馬里奧能夠在跑道上無限的跑起來。

下一步是向本機伺服器詢問輸入的內容。 這需要在 C 中發出 http 請求,這證明是個複雜的任務。人們抱怨分散式系統很難,的確是這樣!經過解決幾個小問題之後,我終於完成了消費資料並將其輸出到內部的 N64 模擬器。

我完成的輸入外掛程式可以在GitHub上找到。

https://github.com/kevinhughes27/mupen64plus-input-bot

玩《馬里奧賽車 64》遊戲

現在是見證奇跡的時刻了,不過第一場比賽太讓人失望了:馬里奧直接開車撞到牆上,連一個彎都沒轉。

為了 debug,作者增加了一個手動的控制,當需要時才從 AI 獲取控制。作者通過試玩,觀察返回的輸出結果,提出了兩件需要 fix 的點:

重新檢查訓練資料時,作者發現偶爾有些截圖是模擬器視窗的圖片。解決方法是簡單的從資料集中移除,繼續訓練。

作者注意到《馬里奧賽車 64》是一種比較急速的遊戲,典型的是你不能平緩的轉彎。通常,遊戲者需要在大轉彎時來個急速調整。由於資料的混淆,一些《馬里奧賽車 64》的圖片在轉彎的中間,假設模型學習從來不轉彎,而這將導致一些小誤差。模型訓練仍然是一個優化問題,如果資料集有問題,那訓練的結果也會這樣。

記住這些繼續玩《馬里奧賽車 64》遊戲來記錄新的訓練資料。

隨著上面兩點的調整,作者得到更好的結果,見文章開始部分。

最終暢想

TensorFlow 是一個超級棒的 low level 抽象的深度學習框架。受益於 cuDNN,TensorFlow 的計算速度不錯,並且梯度下降演算法也是頂尖的。不過,如果再做另外一個深度學習項目的話,作者說不會直接使用 TensorFlow,而是更願意嘗試 keras ,keras 是基於 TensorFlow 基礎上加了一些語法糖。

在寒假的幾周裡,作者已經能使用 Google 的自動駕駛技術來駕駛虛擬的賽車。資料訓練大概 20 分鐘之後,作者的 AI 已經可以駕駛大部分簡單的賽道,Luigi Raceway 和未經訓練的 racetack。作者期待使用更多的資料來構建一個完整的 AI 自動駕駛馬里奧賽車 64 遊戲。

原始程式碼

http://github.com/kevinhughes27/TensorKart

http://github.com/kevinhughes27/mupen64plus-input-bot