--- title: TF Jam tags: ai,unity,tensorflow description: base on https://blog.tensorflow.org/2018/07/tf-jam-shooting-hoops-with-machine-learning.html --- # TF Jam 復刻 利用真正的AI來做遊戲AI吧! 這篇文章會帶領你從Python 利用Keras 訓練模型,再把他放到Unity內使用 此文章基於[此處](https://blog.tensorflow.org/2018/07/tf-jam-shooting-hoops-with-machine-learning.html)重新撰寫 在訓練模型的部分,選擇使用[此處](https://www.youtube.com/watch?v=ElDzPCCIpdI)所提供方式訓練(keras->tensorflow) 文末有修改好的專案 沒意外應該能直接用 ## 環境設置 * Unity (2018.4.20f1 or higher) -建議使用Unity hub安裝 * Unity 專案的[source code](https://github.com/stevecox1964/tf-jam-clone) * Python (Install Python 3.6.1 or Higher 盡量使用虛擬環境) * pip install jupyter * pip install numpy * pip install pandas * pip install matplotlib * pip install tensorflow == 1.15.2 (新安裝的版本號就是2開頭了 ,後面會沒辦法使用) * pip install -I keras == 2.1.6 ~~※註:Python的版本很有可能是32bit的,而Tensorflow的要求為64bit,需要從python官網下載特定版本~~ # 環境除錯 這邊主要說明Unity的環境除錯 * [TensorFlowSharp](https://s3.amazonaws.com/unity-ml-agents/0.5/TFSharpPlugin.unitypackage) 載入後,記得在設定中打開 Edit>Project Settings>選擇Player 選單中的Other Settings 裡面的Scripting Define Symbols參數調整為ENABLE_TENSORFLOW 下面的Allow 'unsafe' Code 打勾 ![](https://i.imgur.com/nFCKXOt.png) 因為會自動儲存 就可以按叉叉了 最後到File>Save Project>>重啟 * Boo.Lang.dll 找不到 因為Unity已經預設不支援**Boo.Lang** 這個函式庫能夠從"C:\Program Files\Unity\Hub\Editor\2018.4.21f1\Editor\Data\Mono\lib\mono\2.0" 這個位置下找到"Boo.Lang.dll" 並新增到專案下的"Asset/Plugins" 但這個函式庫好像也沒用到 ## 目標 目標很簡單,就是 X-籃框距離 Y-投籃力道 ![](https://i.imgur.com/lw9t6Ik.png) ## 流程 1. 收集資料 有資料才能訓練 所以我們的第一步是蒐集資料 投球需要哪些資料? 力道?距離?角度? 如果不考慮籃板反彈的話 其實我們不需要角度的資料 所以我們只需要蒐集力道跟距離兩個資料就好了 在Unity內怎麼收集資料呢? 用比較白話的方式來說明 詳細的可以點到Github裡面看source code :::danger 因為數據的收集會收集到彈地球,所以我把"BallController.cs"裡的一段做修改了 private void OnCollisionEnter (Collision other) { if (other.gameObject.name == "Court") { // StartCoroutine (DoDespawn (2.5f)); Destroy (gameObject); } } ::: ```pseudo code= void 角色操作(){ 隨機距離(); 隨機力道(); 投球(距離,力道); } void 投球(float 距離,float 力道){ if(進球){ 儲存到csv檔(力道,距離); } } ``` 在Unity中 我們能夠設置一個像下面圓柱一樣的觸發器,只要球通過就會觸發進球的事件,然後加一筆資料到CSV檔裡面 ![](https://i.imgur.com/hxhzO9o.png) ![](https://i.imgur.com/Tx0yiQz.png) 收集到的資料型態 ![](https://i.imgur.com/1cSgEiV.png) 2. 訓練模型 2.1 環境設置 因為套件的函式的使用有時候會因為版本更新而改變,所以在訓練模型的時候還是建立一個專屬的虛擬環境會比較好。 ``` 1.virtualenv unity 建立unity的環境 2.cd ./ unity /scripts 移動到代碼部分 3.activate 啟動虛擬環境,就能開始下載套件了 pip install jupyter pip install numpy pip install pandas pip install matplotlib pip install tensorflow == 1.15.2 (新安裝的版本號就是2開頭了 ,後面會沒辦法使用) pip install -I keras == 2.1.6 4.把虛擬環境加入jupyter(選用) pip install ipykernel python -m ipykernel install --user --name=virtual_env_name 開啟jupyter notebook 就有新環境可以選擇囉(下圖的tensorflow 就是建立的新環境) ``` ![](https://i.imgur.com/Vm7yTEq.png) 2.2 訓練 這邊就能用到我們剛剛蒐集的資料了,利用PYTHON進行訓練的動作,是一種監督式學習的方式 下圖為792筆資料的分布圖 x-距離 y-力道, 可以看到蠻明顯的有一條比較集中的線條 ![](https://i.imgur.com/6Y1XRTQ.png) 先用小畫家簡單畫一下我們想訓練出的模型 應該會是下面紅線的樣子 ![](https://i.imgur.com/DJTprcX.png) 1. 讀取csv ```python= shots_csv = 'successful_shots.csv' shots = pd.read_csv(shots_csv) ``` 2. 標準化 為了降低數據落差 所以我們先對資料做標準化的動作 ```python= x = shots["dist"].values.reshape(-1,1).astype(np.float32) minX = min(x) maxX = max(x) X = (x-minX)/(maxX-minX) y = shots["force"].values.reshape(-1,1).astype(np.float32) minY = min(y) maxY = max(y) Y = (y-minY)/(maxY-minY) #X,Y就是我們標準化後的數據 ``` 3. 模型建立 再重新看一下我們預計建立的模型 ![](https://i.imgur.com/DJTprcX.png) 我們先試試看利用線性回歸的方式來訓練 :::info 線性回歸(Linear Regression) 簡單來說,就是將複雜的資料數據,擬和至一條直線上,就能方便預測未來的資料。 [來源](https://ithelp.ithome.com.tw/articles/10206114) ::: ``` from sklearn import linear_model regr = linear_model.LinearRegression() regr.fit(X,Y) y_pre_LinearRegression = regr.predict(X) plt.scatter(X,Y,color='blue',s=1) plt.scatter(X,y_pre_LinearRegression,color='red',s=1) ``` ![](https://i.imgur.com/4xYy0Oc.png) 我們再簡單分析一下訓練出來的模型 估計只能達到20%~30%左右的準確率 ![](https://i.imgur.com/xrZfCEj.png) 如果要達到100%的準確度 想必要自己來設計模型了 看完下圖 再看看自己想做出來的模型 沒錯 激勵函數就是relu了 ![](https://i.imgur.com/lZuI1Ka.png) 所以設計出下面的模型 ```python= model = Sequential() model.add(Dense(4, name='shot_in' , input_dim=1, activation='relu')) model.add(Dense(1, name='shot_out')) model.compile(loss='mean_squared_error', optimizer='adam') model.fit(X, Y, epochs=100, batch_size=10) ``` ![](https://i.imgur.com/JhFJRJl.png) ### 測試過程 在建立模型的過程 主要以神經元個數與epoch來做修改 神經元在3以下的情況下沒有辦法正確測試 epoch 20開始有一個形狀出現 epoch 30基本上準確率就達到100%了 ![](https://i.imgur.com/17a2qCA.png) ![](https://i.imgur.com/FTt9yJK.png) ![](https://i.imgur.com/dSWleky.png) ![](https://i.imgur.com/ics6euP.png) ![](https://i.imgur.com/CEoLG9v.png) ![](https://i.imgur.com/uFiThDN.png) ![](https://i.imgur.com/QmcvphF.png) ![](https://i.imgur.com/oUomHx0.png) 訓練完模型後,我們把它儲存成一個.h5的檔案 ``` model_name = 'shotsmode.h5' ``` 因為unity無法直接讀取這種格式,所以我們還要再把它改成tensorflow的格式 還好已經有大神幫我們用好轉換的Code了 我們只要呼叫同目錄下的**keras_to_tensorflow.py** 就能順利執行轉換了 ``` python keras_to_tensorflow.py -input_model_file shotsmode.h5 -graph_def True ``` 最後出來的shotsmodel.h5.pb就是我們unity的輸入了 ![](https://i.imgur.com/MGte0JO.png) 3. 使用模型作為輸入 在unity裡面輸入距離 然後輸出力道 這邊是原作者所提供的程式碼 ```csharp= //有錯誤 請注意!!! String filename = "shotsmodel.h5.pb"; var graphModel = File.ReadAllBytes ("./Assets/resources/" + filename); graph_ = new TFGraph (); graph_.Import (graphModel, ""); session_ = new TFSession (graph); float force = GetForceFromTensorFlow(dist) * shotBoost; float GetForceFromTensorFlow(float distance){ var runner = session.GetRunner(); runner.AddInput(graph["shot_in_input"][0], new float[1, 1] { { distance } }); runner.Fetch(graph["output_node0"][0]); float[,] recurrent_tensor = runner.Run()[0].GetValue() as float[,]; return recurrent_tensor[0, 0] /10; } ``` 你會發現 力道輸出好像怪怪的? ![](https://i.imgur.com/vi3MvOt.png) 仔細看程式碼 ``` float force = GetForceFromTensorFlow(dist) * shotBoost; return recurrent_tensor[0, 0] /10; ``` 為甚麼回傳會先/10後面又要在*shotBoost? 後來我自己去測試一些資料後發現 還記得剛開始訓練資料有先做一個標準畫的動作嗎? ![](https://i.imgur.com/WR6ayif.png) 所以我們在unity輸入輸出資料也需要先做標準化與還原的動作 ```csharp= var distt = (dist-minX)/(maxX-minX); float force = GetForceFromTensorFlow2(distt)*(maxY-minY)+minY ; ``` ![](https://i.imgur.com/M3iQt4Y.png) 4. 小結 總共測試了500,1000,9000筆資料的訓練, 其實差異量不大, 最主要是後面忘記有標準化這個動作導致測試很久 ## 修改後的檔案 [Unity專案](https://drive.google.com/file/d/1xij60TF9SOevfsoWgVZcFVjosK4Ia3K9/view?usp=sharing) [訓練模型](https://drive.google.com/file/d/1TQxfuhtkyJIwBSOY9eQyqprSAUGlbOzN/view?usp=sharing) ## 參考資料 > [TF Jam](https://blog.tensorflow.org/2018/07/tf-jam-shooting-hoops-with-machine-learning.html) > [KTF Jam - AI Robot Basketball using Unity/Keras/Tensorflow](https://www.youtube.com/watch?v=ElDzPCCIpdI) > [深度學習:使用激勵函數的目的、如何選擇激勵函數 Deep Learning : the role of the activation function](https://mropengate.blogspot.com/2017/02/deep-learning-role-of-activation.html) {%hackmd theme-dark %}