當前位置:
首頁 > 科技 > 如何讓 AI像NBA 球星一樣投籃?

如何讓 AI像NBA 球星一樣投籃?

本文旨在使用 Unity3D 和 TensorFlow 來教 AI 怎樣玩一個簡單的遊戲:把球投進籃筐。

遊戲介紹

我們說的這個遊戲里玩家只有一個主要目標:把球投進籃筐里。聽起來貌似不難,但當你血液上涌、心跳加速、觀眾們吶喊時,嗯,想投進還是挺困難的。這是不是北美的經典遊戲——籃球?不是,沒聽說過。我說的是 Midway 出品的經典街機遊戲 NBA Jam。

如果你玩過 NBA Jam 或任何受到它啟發的遊戲(包括真實世界中的 NBA 大聯盟,我記得應該是在 NBA Jam 之後誕生的),那你肯定知道從玩家的角度來看,投籃的原理是非常簡單的。只需按住投球鍵,然後在正確的時機鬆開即可。但你有沒有想過,從遊戲的角度來看,投籃的過程是什麼?球的弧線怎樣確定?投球的力度多大?計算機怎樣知道投球的角度?

聰明並且喜歡數學的你肯定能用紙筆得到答案,但筆者八年級的代數不及格……所以這種「聰明人」的答案就免了吧。我需要用更難的辦法解決。

我不想用簡單、快捷、有效的方式,用數學解決投籃的問題,而是想學一些簡單的 TensorFlow,然後試著投籃就好了。

讓我們開始吧!

我們需要一堆東西來完成這個項目。

Unity 模擬籃球和物理運動;

Node.js 和 TensorFlow.js 用於訓練模型;

TensorFlowSharp 用於將模型通過 ML-Agents asset 包集成到 Unity 中;

tsjs-converter(https://github.com/tensorflow/tfjs-converter)將 TensorFlow.js 模型轉換為圖,以便在 Unity 使用;

Google Sheets(http://sheets.google.com/)用於將線性回歸可視化。

你不懂某個技術也完全沒關係!(其實我也不是完全懂!)我會儘力解釋這些東西怎樣合作的。使用這麼多技術的缺點之一就是我沒法詳細解釋每一種技術,但我會儘可能多地提供資源供大家學習。

下載項目

我不想手把手建立這個項目,所以我建議從 GitHub 上下載(https://github.com/abehaskins/tf-jam)代碼,然後隨著我的解釋一步步做。

注意:你需要下載並導入 ML-Agents(https://github.com/Unity-Technologies/ml-agents)這個 Unity asset 包,才能在 C# 中使用TensorFlow。如果在 Unity 中看到任何 TensorFlow 找不到的錯誤,請確保你按照 Unity 的 TensorflowSharp 文檔(https://github.com/Unity-Technologies/ml-agents/blob/master/docs/Using-TensorFlow-Sharp-in-Unity.md)完成了設置。

目標是什麼?

簡單來說,我們希望的輸出很簡單。我們想要解決的問題是:如果投籃者與籃筐的距離為 X,那麼就以力度 Y 投籃。就這麼簡單!我們不考慮瞄準或其他任何東西。我們只想找出要多大力氣投籃才能命中。

如果你想知道怎樣才能在 Unity 中製作更複雜的 AI,可以參考 Unity 的更完整的 ML-Agents(https://github.com/Unity-Technologies/ml-agents)項目。我這裡介紹的項目本身非常簡單易行,而且不一定使用了最佳實踐(我也在學習呀!)。

我有限的關於 TensorFlow、機器學習和數學的知識並不是障礙。所以就把這個項目當做娛樂吧。

籃筐和籃球

我們已經說過這個項目的目標了——投籃。要把球投進籃筐里,首先我們需要一個籃筐,還有一個球。這就該 Unity 上場了。

如果你不熟悉 Unity,那隻需要記住它是個遊戲引擎,可以在任何平台上製作二維和三維遊戲。它有內置的物理引擎,基本的三維建模,和一個非常好用的腳本運行時(Mono:https://www.mono-project.com/),所以我們可以利用它用 C# 來寫遊戲。

我不是藝術家,所以我簡單地拖了幾個立方體放在了場景里。

紅色立方體顯然是玩家。籃筐上有個看不見的觸發器(https://unity3d.com/learn/tutorials/topics/physics/colliders-triggers),用於檢測物體(籃球)通過籃筐。

在 Unity 編輯器中可以看到隱形的觸發器的綠色邊框。你可以看到我們放了兩個觸發器。這樣可以保證我們只統計那些從頂部一直落到底部的球。

看一下 /Assets/BallController.cs(這個文件是每個籃球上的腳本)中的 OnTriggerEnter方法,會發現這兩個觸發器是同時使用的。

這個函數做了幾件事情。首先,它確保頂部和底部的兩個觸發器都觸發了,然後改變球的材質,這樣我們就能直觀地看到球進了籃筐,最後輸出我們關 心的兩個變數:distance 和 force.y。

投籃

打開 /Assets/BallSpawnerController.cs。這個腳本運行在投籃運動員上,負責生成籃球,並嘗試投籃。看一下末尾處的 DoShoot() 方法。

在這段代碼中,Instantiates 初始化一個新的籃球實例,然後設置投籃的力度,以及與籃筐的距離(這樣後面輸出就會更容易,如前一段代碼所示)。

如果你還沒關 /Assets/BallController.cs,你可以看看它的 Start() 方法。每次創建新的籃球時都會調用這個方法。

換句話說,我們建立一個新的球,給它一些力量,然後在 30 秒之後自動銷毀這個球,因為我們要處理很多很多球,我們希望場景能幹凈一些。

試著運行一下項目,看看我們的全明星投籃運動員投得怎麼樣。點擊 Unity 編輯器中的 Play 按鈕,我們就會看到……

玩家(我們叫他「小紅」)要向斯蒂芬·庫里挑戰了!

為啥小紅投籃這麼差?答案是 Assets/BallController.cs 中的一行,float force = 0.2f。這一行說每次投籃都應該是完全一樣的力度。我們發現 Unity 忠實地執行了這個「完全一樣」。同一個對象,同樣的力度,不斷重複,彈跳方式都完全一樣。真棒。

當然這並不是我們希望的結果。不嘗試新的東西就永遠不可能成為詹姆斯。所以我們來嘗試下吧。

隨機投籃,收集數據

我們簡單地改變力度為隨機數,來引入一些隨機的雜訊。

這樣投籃就是隨機的了,我們終於看到有球命中的樣子了,儘管需要花上好長一段時間才能命中。

小紅很笨,他偶爾會投進,但完全是靠蒙。不過沒關係。現在,任何投進的球都是我們需要的數據點。我們一會兒就會用到。

同時,我們不想從一個固定的位置投籃。我們希望小紅能從任意距離投進籃筐(如果他運氣足夠好的話)。在 Assets/BallSpawnController.cs 中找到這幾行,然後去掉 MoveToRandomDistance() 前面的注釋。

運行之後,我們會發現小紅充滿活力地一邊投籃一邊跳來跳去。

隨機運動和隨機力度的組合能創建出非常有用的東西:數據。看看Unity的控制台,就會看到每次投進後都會顯示出數據。

每次成功投進都會輸出目前的投中次數、到籃筐的距離,和投籃的力度。這個模擬很慢,我們來加快一些。回到我們添加 MoveToRandomDistance()的地方,把 0.3f(兩次投籃之間300毫秒延遲)改成 0.05f(50毫秒延遲)。

再點擊 Play 按鈕看看能投中多少。

這個訓練不錯!我們可以從後面的計數器看到,成功投中的次數大概是 6.4%。庫里也做不到這麼高吧?不過說起訓練,我們從這裡學到什麼了嗎?說好的 TensorFlow 呢?這有什麼意思?好吧,TensorFlow 是下一步的十二。我們現在要把 Unity 中的數據拿出來,建立一個模型來預測力度。

預測,模型和回歸

看看 Google Sheets 里收集到的數據。

在進入 TensorFlow 之前,我想先看看數據,所以我讓 Unity 一直運行,直到小紅投中 50 個球。這會在 Unity 項目的根目錄下生成一個文件 successful_shots.csv。這是從 Unity 中保存下的投籃命中的原始數據!我讓 Unity 導出這些數據,這樣就可以在工作表中進行分析了。

.csv 文件只有三列:index、distance 和 force。我把這個文件(https://support.google.com/docs/answer/40608?co=GENIE.Platform%3DDesktop&hl=en)導入到 Google Sheets 中,然後建了個散點圖(https://support.google.com/docs/answer/190718?hl=en)並畫上趨勢線(https://support.google.com/docs/answer/6075154?hl=en&co=GENIE.Platform%3DDesktop),這樣就能大概知道數據的分布。

哇!看這個圖。我是說,看圖中的那條線。嗯,好吧,我承認,一開始我也不知道這條線是啥意思。來一步步看看。

這張圖顯示的是一系列點,Y 軸是投籃力度,X 軸是投籃距離。我們可以看到力度和距離之間有很明顯的相關性(除了一些不正常的彈跳引起的隨機異常之外)。

用正常的話說就是「TensorFlow很會處理這種數據」。

雖然這個例子很簡單,但 TensorFlow 很棒的一點就是,如果有需要,我們可以用極其簡單的代碼做出非常複雜的模型。例如,在完整的遊戲中,我們可以包含許多特徵——如其他玩家的位置,以及他們以前的阻擋投籃的頻率等,來確定玩家應該選擇投籃還是傳球。

用 TensorFlow.js 建立模型

用你喜歡的編輯器打開 tsjs/index.js 文件。這個文件跟 Unity 沒關係,它只是用來根據 successful_shots.csv 的數據訓練模型的。

下面是所有訓練並保存模型的代碼。

可以看到,代碼沒多少。首先從 .csv 文件中載入數據,然後建立一系列 X 和 Y 點(就像 Google Sheets 一樣)。然後我們要求模型去「擬合」數據。之後把模型存下來供以後使用。

很可惜,TensorFlowSharp 要求的模型的格式跟 Tensorflow.js 能保存的格式不一樣。所以我們得做一些轉換才能把模型導入到 Unity。我用了一些工具來做這項工作。基本的流程就是把模型從 TensorFlow.js 格式轉換成 Keras 格式,這樣我們就能設置保存點(https://www.tensorflow.org/get_started/checkpoints),然後將Protobuf圖形定義(https://www.tensorflow.org/extend/tool_developers/)合併進去,得到凍結的圖形定義(https://github.com/tensorflow/tensorflow/blob/master/tensorflow/python/tools/freeze_graph.py),這樣就能導入到 Unity 中了。

幸運的是,你可以跳過這一切,只需運行 tsjs/build.sh,如果一切正常,它會自動完成所有步驟,然後把凍結的模型導入到 Unity 中。

在 Unity 中,看看 Assets/BallSpawnController.cs 文件中的 GetForceFromTensorFlow(),看看怎樣使用我們的模型。

在製作圖形定義時,需要定義一個包含多個步驟的複雜系統。在這個例子中,我們把模型定義成單密集層(以及一個隱含的輸入層),意思就是我們的模型接受一個輸入,然後給出一個輸出。

如果在 TensorFlow.js 中調用model.predict(https://js.tensorflow.org/api/0.11.7/#tf.Model.predict),它會自動把輸入放到正確的輸入圖節點上,然後在計算完成之後,從正確的節點上拿到輸出。但 TensorFlowSharp 的工作原理不同,需要你自己去根據節點名稱操作節點。

知道這些之後,我們只需把輸入轉換成圖期待的格式,然後把輸出發回給小紅即可。

遊戲時間!

使用上面的系統,我根據模型做了幾個不同的版本。下面是小紅在一個根據 500 次命中數據訓練過的模型上的投籃結果。

我們看到命中率提高了 10 倍!如果我們訓練小紅幾個小時,收集一萬條,或者十萬條命中數據會怎樣?肯定會讓他提高更多!這個工作就留給讀者了。

我強烈推薦你看看 GitHub 上的源代碼

https://github.com/abehaskins/tf-jam

如果能超過 60% 的命中率,歡迎在 Twitter 上告訴我:

https://twitter.com/abeisgreat

(劇透:超過 60% 是完全可能的,回到本文開頭看看那張圖,小紅能被訓練得很好!)

原文:https://medium.com/tensorflow/tf-jam-shooting-hoops-with-machine-learning-7a96e1236c32?linkId=54634097

作者:TensorFlow

譯者:彎月,責編:屠敏

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 CSDN 的精彩文章:

華為超蘋果成第一;5G來臨時間已定;谷歌禁止重複內容

TAG:CSDN |