當前位置:
首頁 > 知識 > 我用深度學習分析 LoL 小地圖,自製數據集 DeepLeague 開源(下)

我用深度學習分析 LoL 小地圖,自製數據集 DeepLeague 開源(下)

本文為雷鋒字幕組編譯的技術博客,原標題 DeepLeague: leveraging computer vision and deep learning on the League of Legends mini map + giving away a dataset of over 100,000 labeled images to further esports analytics research,作者 Farza。

翻譯 | 於澤平 安妍 整理 | 凡江

嗨!請確定你已經閱讀過我用深度學習分析 LoL 小地圖,自製數據集 DeepLeague 開源(上),否則你可能會對這一部分感到困惑。

創建數據集

第一部分:神秘的網路套接字

可能許多人都想知道如何創建這個數據集,因為這個數據集實在是太大了。現在我來揭曉答案。我沒有手動標記 100000 張小地圖,那樣做太瘋狂了。每張圖片都有 10 個邊界框(假設所有 10 個英雄都存活),表示這些英雄在哪裡以及它們是什麼英雄。即使我手動標記每張圖片需要 5 秒鐘,也需要超過 8000 分鐘才能完成!

下面讓我來講講我是怎樣創建數據集的,這包含一些聰明的技巧以及 Github 上面另一位名為 remixz(https://github.com/remixz) 的開發者的幫助。這位開發者創建了接下來我將介紹的神秘的網路套接字數據集(我喜歡這樣叫它)。

當你在 lolesports.com 上面觀看英雄聯盟比賽直播時,實際上有一個神秘的網路套接字,它不斷地拋出關於這場比賽直播的數據。我稱之為神秘的網路套接字因為有很少人知道這件事,而且它似乎是半隱藏的。套接字產生的數據包含這場比賽中的選手名字、他選擇的英雄以及每一時刻的英雄位置和血量。它以這種形式存在是因為這樣可以讓直播軟體在網頁上實現統計功能。

你現在可能想知道我如何使用這個數據的了!

我創建了我自己的節點腳本(與 remixz 所創建的類似),每當神秘的網路套接字開啟,腳本監聽傳入的數據,並將數據保存到一個 JSON 文件中。我在 AWS EC2 機器上託管了這個腳本,現在我正在自動保存 LCS(英雄聯盟冠軍聯賽)中北美賽區和歐洲賽區的比賽數據!

如果你仍對數據感到好奇,這裡是從 LCS 一場比賽中獲取的小片段(http://t.cn/RE6IDVq),可以讓你更好地理解它是什麼樣子的。

這個 JSON 數據本身其實沒什麼用。但是請記住,我們這樣做的目的是為了創建一個有標籤的數據集。這個數據集是帶標籤的小地圖圖片,標籤表示各個英雄在小地圖上的位置。我並不關心 JSON 數據本身。

我應該補充說明的是,深度聯盟只從比賽中識別了 55 個英雄(http://t.cn/RE6IsrE),因為我只是用 LCS 的數據。LCS 中的選手通常只使用一部分英雄。例如,中單通常會玩狐狸,但是幾乎沒有人玩提莫!這意味著我沒有辦法訓練一個可以識別提莫的模型。同樣,也意味著我有太多狐狸的數據。我需要在我的數據集中獲得英雄的平衡。你可以查看代碼,看看我如何使用 check_champs 函數平衡我的數據集的。

現在,我擁有的一切是這些 JSON 文件,它們對應某場比賽每一時刻的每一個英雄的位置。因此,我所需要做的就是下載 JSON 文件對應的比賽視頻,並將 JSON 數據與視頻匹配。起初我認為這很簡單。我以為只需要去 YouTube 上面找到並下載比賽視頻,寫一個腳本自動從視頻中提取幀,再將它與 JSON 數據匹配就可以了。

第二部分:理解問題

我犯了一個很大的錯誤。讓我來解釋一下。

我無法像處理我在家裡錄製的英雄聯盟視頻一樣處理 LCS 的視頻。舉個例子,如果我需要記錄我自己在家從頭到尾玩的一局英雄聯盟,我只需要運行這段代碼:

注意:當我說 「幀」 時,我的意思是假設遊戲中的每一秒與一個圖像 「幀」 相對應。一個 60 秒的視頻將一共有 60 幀,每一幀都對應一秒。因此,1FPS(每秒傳輸幀數)!

# first go through every single frame in the VOD.

in_game_timestamp = 0

for frame in vod:

# go in the vod"s json data. find the json data associated with

# that specific timestamp.

frame.json_data = vod_json_data[in_game_timestamp]

in_game_timestamp += 1

一幀的例子。我保存了完整的視頻,只是從中裁剪出了小地圖。

這段代碼可以在我家裡保存的遊戲視頻上完美運行。假設我在遊戲中 0:00 的時間戳開始錄製視頻,在 22:34 時間戳停止。那麼如果我想要遊戲計時器中每一秒的一幀數據,這是很容易的,因為:

我在家裡錄製的視頻的時間戳與遊戲中時間戳是直接對齊的。

哈哈,朋友們,我真希望 LCS 視頻也可以這樣簡單地處理。

獲得 LCS 比賽視頻的唯一方式是通過 Twitch 上的直播流。這些 LCS 比賽的 Twitch 直播流在比賽過程中有許多終端,例如即時回放,選手採訪以及暫停。視頻的 JSON 數據對應遊戲中的計時器。你明白這為什麼會成為一個問題了嗎?我的視頻計時器與遊戲計時器不是對齊的。

溜了溜了

假設發生了這種情況:

LCS 比賽在遊戲計時器中時間戳為 12:21

LCS 比賽在遊戲計時器中時間戳為 12:22

LCS 比賽在遊戲計時器中時間戳為 12:23

流轉換為即時回放,並播放最近的一場團戰,共 17 秒。

LCS 比賽在遊戲計時器中時間戳為 12:24

天哪!這真是太可怕了。我完全失去了遊戲計時器的軌跡,因為這次中斷,遊戲時間戳和視頻時間戳變得不對齊了!我的程序應該怎樣了解如何從視頻中提取數據,並將每一幀與我從網路套接字中獲得的 JSON 數據相關聯呢?

第三部分:取消谷歌雲服務

現在問題已經很清晰了。我用來從視頻中提取數據的腳本必須知道遊戲中的時間戳的實際情況。只有這樣我才能真正確定正在展示的是比賽而不是一些類似即時回放或是其他中斷的內容。同樣,知道遊戲中的時間戳是非常重要的,因為神秘的網路套接字數據集給我們的是實際遊戲中的每一秒對應的幀的數據。即使有類似即時重放的事情出現,神秘的網路套接字仍然在向我們發送數據。因此,為了匹配 JSON 數據中的幀,我們需要知道遊戲中的時間戳。

我做的第一個嘗試是使用基本的 OCR(光學字元識別)識別時間戳。我嘗試了很流行的庫,但都獲得了很糟糕的結果。我猜測的是,奇怪的字體以及總在變化的背景使其變得十分困難。

裁剪出的遊戲計時器的樣例

最後,我發現了谷歌的 Cloud Vision API(http://suo.im/4Fhm2d),其中也有 OCR 功能。這個 API 效果很好,幾乎不犯錯誤。但有一個問題。

每使用 API 處理 1000 張圖片需要花費 1.5 美元。我首先想到的是將所有時間戳放到同一張圖片中,並將它們作為一張圖片進行處理。但由於某種原因,我得到了很糟糕的結果。API 一直在給我錯誤的答案。這意味著我有一個選擇,我將需要每次發送給 API 一個小時間戳。我有超過 100000 幀,這代表我需要付 150 美元。這其實也不算太差,只是我沒有那麼多錢,我還在上大學,我還只是個孩子…

但是!我很幸運地找到了這個:

創建一個賬戶就可以獲得免費使用的 300 美元。無疑,我現在還沒有創建 3 個賬戶,因此我可以用免費的 900 美元處理我的視頻,並在 GCP 上做隨機測試和腳本。這將違反服務條款,也不尊重公司。

無論如何,憑藉這筆免費資金,我編寫了一個腳本,可以逐個使用 Google Vision API 處理視頻。這個腳本輸出了一個名為 time_stamp_data_clean.json 的 JSON 文件,它從遊戲中提取了各個幀,並根據每一幀對應的時間從遊戲計時器中讀取並標記。

time_stamp_data_clean.json 的數據,告訴我們遊戲計時器根據特定幀讀取的內容。

太厲害了! 這種方法是有效的!

在這一點上,一切都接近完美,數據集幾乎準備就緒。 現在是最後一步了。 我們只需要將來自此 JSON 的數據與來自神秘網路套接字的 JSON 匹配。 為此,我創建了這個腳本(http://t.cn/RE6Mx8O)。

對於一個巨大的數據集,如果沒有合適地處理它,使用起來是很麻煩的。我需要一種好方法去說 「這個幀有這些邊界框 + 標籤」。我可以只留下一些. jpg 文件和一個包含所有標籤和坐標信息的. csv 文件。它看起來就像這樣:

frame_1.jpg, Ahri [120, 145], Shen [11, 678], ...

frame_2.jpg, Ahri [122, 147], Shen [15, 650], ...

frame_3.jpg, Ahri [115, 133], Shen [10, 700], ...

但這是不好的。因為 CSV 很煩人,JPG 更煩人。另外,這意味著我將不得不對所有圖片文件重命名,以便使它們與 CSV 對應。這樣肯定不行。必須要有一個更好的辦法。確實有。

我將所有數據保存到一個. npz 文件中用來代替 JPG 和 CSV。這個. npz 文件使用 numpy 的矩陣保存。Numpy 是機器學習的一種語言,所以這很完美。每個圖片被保存到 numpy 數組中,標籤也隨之保存,就像這樣:

[

[[image_as_numpy_array],

[[Ahri, 120, 145, 130, 150],

[Shen, 122, 147, 170, 160],

...

],[[image_as_numpy_array],

[[Ahri, 125, 175, 180, 190],

[Shen, 172, 177, 190, 180],

...

]

...]

現在我們不再需要處理煩人的文件名或者 CSV 了。所有數據都被保存在同一個文件的許多數組中,並且可以通過索引輕鬆訪問。

最後就是困難的深度學習部分了,獲取 + 處理數據,已經完成了!

選擇一個神經網路框架

假如不用模型來訓練數據,那數據有什麼用?

從最開始我就想用一個專門用於檢測物體的現有框架,因為這只不過是個概念驗證罷了。我不想花上幾周時間來搭一個適合電游的框架。這事兒我還是留給未來的博士生們去做吧:)。

我在上文提到過,之所以選用 YOLO 框架,是因為它運行速度很快,而且在一度算得上先進。另外,YOLO 的作者(http://suo.im/2FLpOc)很了不起,他開放了所有源代碼,向開發人員們敞開了大門。但他寫 YOLO 用的是 C++ 語言。我不太愛用 C++,因為它大部分數據的代碼完全可以用 Python 和 Node.js 來做。幸好有人決定創建 YAD2K(http://suo.im/InHKs),這樣大家就能用 Python 和 Keras 來使用 YOLO 了。說實在的,我選擇 YOLO 還有一個重要原因在於我讀懂了它的論文。我深知只有真的讀懂了這篇論文,才能弄清楚框架背後的核心代碼。其他熱門框架的論文我沒能讀得這麼透。YOLO 只需看一眼圖像就能得出結論,這項能力比 R-CNN 用的上千個區域提案還要人性化。最重要的是,YOLO 的代碼對照著論文就很好懂。

至於 YOLO 是如何運行的,我在此就不贅述了。有很多其他資源對此做出了解釋,比如這個(此處有超鏈接),就比我解釋得清楚多了!

對 YOLO 進行再訓練

注意:這部分的技術性較強,如果你有什麼不懂的地方,儘管在推特上問我!

YOLO 是個特別有深度的神經網路。也有可能它其實沒什麼深度,是我太容易被打動了。不管怎樣,框架是這樣的:

我用的是一台 2012 年的 MacBook Pro。我沒法用它來訓練 YOLO 這麼個龐大的模型,那估計得花上好幾年時間。於是我買了個 AWS EC2 GPU 實例,因為我想在 21 世紀結束前完成模型訓練。

以下是再訓練腳本的運行方式:

我沒有完全從頭開始訓練 YOLO。

YAD2K 首先得經得住訓練前的重量,凍結主體的所有層次,然後運行 5 次迭代。

接著,YAD2K 和整個模型在未凍結的狀態下運行 30 次迭代。

然後,當驗證損失開始增加時,YAD2K 會儘早停止並退出,這樣模型就不會被過度訓練了。

所以,起初我還天真地從 5 個 LCS 遊戲中提取了大約 7,500 幀的數據,用 YOLO 運行了一遍,結果數據在 2 次迭代內過度擬合然後湮滅了。這倒也說得通。這個模型有很多參數,而我沒有使用任何形式的數據增強,註定行不通。

說到數據增強,我其實沒用在這個項目上。通常來說,在針對現實中的物體訓練模型時,數據增強對模型的幫助非常大。例如,一個杯子可以在圖像中顯示出數千種不同的尺寸。我們無法得到包含每個尺寸的數據集,因此使用數據增強。但就這個迷你地圖的例子而言,除了冠軍圖標和其他一些東西(比如病房)的位置,一切都是恆定不變的。由於我只用了 1080p 的 VOD,迷你地圖的大小也是恆定的。如果我想為某位冠軍提供更多數據,數據增強會非常有用。所以我可以把迷你地圖的框架翻轉過來,然後從一個框架中得到兩個框架。但與此同時,冠軍圖標也會被翻轉,導致模型混淆。關於這一點我還沒測試過,但說不定能行呢!

經歷了第一次失敗後,我想,「好吧,算了,我用整個數據集試試」。但我的訓練腳本在大約 20 分鐘內一直在崩潰,因為我的系統 「內存不足」(和 RAM 一個道理)。

再一次 溜了溜了

這也講得通,因為我的數據集十分龐大,而訓練腳本是把整個數據集都載入到 RAM 內存里,相當於在電腦上一下子打開十萬張圖。我本可以租一個內存更大的 AWS 實例,這樣就不會有任何問題;但我又摳又窮。於是我給訓練腳本添加了新功能(http://t.cn/RE6Mn5i),以便批量訓練。這樣一來,一次就只載入一部分數據集,而不是一股腦兒全都載入到內存里。得救了!

我用改進後的 「批量訓練」 來運行 YOLO,事情總算出現了轉機。我前前後後試了有 10 次。在這個過程中,我把模型運行了好幾個小時,才意識到代碼有一個巨大的錯誤,於是我終止訓練,從頭再來一遍。最後,我修復了錯誤,損失終於開始降低,而模型這次也沒有過度擬合!我花了大約兩天時間,讓模型運行了完整的訓練時長。可惜這下我的錢包癟了不少,好在最終我還是得到了最後的重量。損失呈現很好的收斂,驗證損失也收斂得很好,而且正好處於訓練損失之上。

我真想向大家展示一下我那美麗的 TensorFlow 圖形。怪我太蠢,我把訓練後的重量保存到筆記本電腦之後,不小心刪除了我實例里的所有內容。KMS :(。我可以再花 2 天時間來訓練模型、分析訓練,只要先給我買 GPU 的錢就行:)。

差不多就是這樣了!忙了這麼久,我終於能取得些可靠的成果來用於我的任務了。寫到這兒,我本可以就此收尾,大談 DeepLeague 是個多麼完美的工具,我是多麼了不起。但那不是實話。特此澄清,DeepLeague 還遠稱不上完美,不過我的確很了不起。

儘管 DeepLeague 大多數情況下都做得很好,但它仍存在一些主要問題。下面我們來看看其中一個。

在上面的輸出中,DeepLeague 錯誤地把 Cho"Gath 標記成了 Karma,還給了它 1.0 的置信度。這很糟。神經網路似乎百分百確定 Cho"Gath 就是 Karma。我在其他地方提到過,我得對數據集加以平衡,否則我就會在掌握某個冠軍很多數據的同時,只掌握另一個冠軍很少的數據。例如,我有 Karma 的很多數據,是因為她已經壞了,而大家都在 LCS 遊戲里扮演過她。但是,並沒有多少人扮演 Cho"Gath!這代表我所掌握的 Cho"Gath 的數據要比 Karma 的少得多。其實,還存在一個更深層次的問題使得平衡數據集如此困難。

比方說,在 Karma 所在的遊戲里,我有 50,000 個幀,整個數據集有 100,000 幀。對於單獨一位冠軍而言,這麼多數據已經相當多了;而如果在很多 Karma 身上訓練我的網路,可能會使得神經網路對其他冠軍的了解變得困難很多。此外,還有可能導致嚴重的本土化問題。

我知道你們在想什麼:「扔掉點兒 Karma 的數據唄!」 我不能為了平衡數據集而扔掉含有 Karma 的幀,因為這些幀同時也包含了其他 9 位冠軍的數據。也就是說,如果我扔掉那些含有 Karma 的幀,就會同時減少其他那 9 位冠軍的數據!我試著儘可能去平衡數據集,但由於網路只在地圖裡的少數幾個地方見到過兩三次 Cho"Gath,而在地圖上到處都能見到好多 Karma,因此 Cho"Gath 很有可能被識別成 Karma。正如很多深度學習中的問題一樣,針對這個問題最簡單的解決方案是:更多的數據。這是非常可行的,我們可以從 web socket 中持續抓取數據!我們還可以試試能 「學著」 平衡數據集的焦點損失(https://arxiv.org/abs/1708.02002),不過我還沒試過。

撇開對於某些冠軍的錯誤分類不看,DeepLeague 仍然好得出奇。我真的很想看看這個項目是不是也能啟發些別的點子。比如,我們可以在電游里進行動作檢測嗎?這樣一來,當某些冠軍在某些時候使用某些法術時,我們就可以識別出來!當 Ahri 把她的 Q 扔出去時,她會做出某些動作,而神經網路可以分析這些動作。試想一下,你可以對 Faker 是如何計算自己的能力、管理自己的法力、在地圖上漫遊的進行分析。這一切都可以通過計算機視覺實現:)。

非常感謝你閱讀本文,我最喜歡你了。如果你有任何疑問,請隨時在 Twitter 上給我留言。先再見啦!

博客原址https://medium.com/@farzatv/deepleague-part-2-the-technical-details-374439e7e09a

更多文章,關注雷鋒網,添加雷鋒字幕組微信號(leiphonefansub)為好友

備註「我要加入」,To be a AI Volunteer !

NLP 工程師入門實踐班:基於深度學習的自然語言處理

三大模塊,五大應用,手把手快速入門 NLP

海外博士講師,豐富項目經驗

演算法 + 實踐,搭配典型行業應用

隨到隨學,專業社群,講師在線答疑

新人福利

關注 AI 研習社(okweiwu),回復1領取

【超過 1000G 神經網路 / AI / 大數據,教程,論文】

我用深度學習分析 LoL 小地圖,自製數據集 DeepLeague 開源(上)


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

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


請您繼續閱讀更多來自 AI研習社 的精彩文章:

YOLO,一種簡易快捷的目標檢測演算法

TAG:AI研習社 |