機器學習原來這麼有趣!【第二章】:用機器學習製作超級馬里奧的關卡
- 2020 年 3 月 4 日
- 筆記
在第一章中我們談到,機器學習是用泛型算法告訴你一些有關數據的有趣結論,而這個過程中你不需要寫任何與問題有關的特定代碼。(如果你還沒有讀過第一章,現在先去讀吧!機器學習原來這麼有趣!【第一章】)
這一次,我們要來用其中一種泛型算法來做件很酷炫的事情——創造一個看起來像是人類設計的電子遊戲關卡!我們會構造一個神經網絡,並提供給它已存在的超級馬里奧的關卡設計,然後就可以等它自己創造出新的關卡了!
http://mpvideo.qpic.cn/0bf23aaa4aaaiaaomvkmj5pfbwgdb3maadqa.f10002.mp4?dis_k=476d777ef6e273a8444618a34bb04d77&dis_t=1583305232
回到第1部分,我們創建了一個簡單的算法,可以根據房屋的屬性估算房屋的價值。給定關於如下圖所示的房屋數據:

我們最終得到了這個簡單的估算函數:
def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): price = 0 # a little pinch of this price += num_of_bedrooms * 0.123 # and a big pinch of that price += sqft * 0.41 # maybe a handful of this price += neighborhood * 0.57 return price
換句話說,我們通過將房屋的每個屬性乘以權重來估算房屋的價值。然後,我們僅將這些數字相加即可得出房屋的價值。
讓我們再用簡單的圖來表示相同的功能,而不是使用代碼:

箭頭代表我們功能中的權重
但是,該算法僅適用於結果與輸入具有線性關係的簡單問題。如果房價背後的真相不是那麼簡單怎麼辦?例如,對於大房子和小房子來說,鄰里關係很重要,而對中型房子根本就不重要。我們如何在模型中捕捉到那種複雜的細節?
為了更聰明的建立模型,我們可以使用不同的權重多次運行此算法,每個權重分別捕獲不同的邊緣情況:

讓我們嘗試以四種不同方式解決問題
現在,我們有四個不同的價格估算。讓我們將這四個價格估算合併為一個最終估算。我們將通過相同的算法再次運行它們(但使用另一組權重)!

我們的新"超級答案"結合了我們為解決問題而進行的四種不同嘗試得出的估計值。因此,與我們在一個簡單模型中捕獲的案例相比,它可以對更多的案例進行建模。
神經網絡是什麼?
讓我們將我們的四種嘗試猜測結合成一個大圖:

這是一個神經網絡!每個節點都知道如何接收一組輸入,對其施加權重並計算輸出值。通過將許多這些節點鏈接在一起,我們可以對複雜的函數進行建模。
為了保持簡短,我跳過了很多內容(包括功能縮放和激活功能),但是最重要的部分是這些基本概念:
- 我們製作了一個簡單的估算函數,該函數接受一組輸入並將它們乘以權重以得到輸出。將此簡單函數稱為神經元。
- 通過將許多簡單的神經元鏈接在一起,我們可以對過於複雜而無法由一個神經元建模的函數進行建模。
就像樂高!我們不能用一個樂高積木搭建一個大廈,但是如果我們有足夠的樂高積木粘合在一起,我們就可以搭建成世界上任何一個東西:

讓我們的神經網絡擁有記憶的能力
當您輸入相同的輸入時,我們看到的神經網絡總是返回相同的答案。它沒有內存。用編程的術語來說,這是一種無狀態算法。
在許多情況下(例如估算房屋價格),這正是您想要的。但是這種模型不能做的一件事就是隨着時間的流逝響應數據模式。
想像一下,我遞給您一個鍵盤,要求您寫一個故事。但是在您開始之前,我的工作是猜測您將鍵入的第一個字母。我應該猜什麼字母?
我可以利用我的英語知識來增加猜對正確字母的幾率。例如,您可能會在單詞開頭鍵入一個常見的字母。如果我查看您過去撰寫的故事,則可以根據您故事開始時通常使用的詞語來進一步縮小範圍。一旦有了所有這些數據,就可以使用它來構建一個神經網絡,以模擬您以任何給定字母開頭的可能性。
我們的模型可能如下所示:

但是,讓問題變得更加棘手。假設我需要猜測故事中任何時候要鍵入的下一個字母。這是一個更有趣的問題。
讓我們以歐內斯特·海明威(Ernest Hemingway)的《太陽升起》為例:
Robert Cohn was once middleweight boxi
猜猜下一個字母?
您可能猜到了'n'這個詞可能是"boxing"。我們基於句子中已經看到的字母以及我們對英語常用單詞的了解而知道這一點。同樣,「middleweight」一詞為我們提供了有關boxing的更多線索。
換句話說,如果我們考慮下一個字母的順序並將其與我們對英語規則的了解相結合,就很容易猜測下一個字母。
為了用神經網絡解決這個問題,我們需要將狀態添加到模型中。每次我們向神經網絡詢問答案時,我們還將保存一組中間計算,並在下一次再次使用它們作為輸入的一部分。這樣,我們的模型將根據最近看到的輸入來調整其預測。

通過跟蹤模型中的狀態,不僅可以預測故事中最可能出現的第一個字母,而且還可以根據給定的所有先前字母來預測最可能出現的下一個字母。
這是循環神經網絡的基本思想。每次使用網絡時,我們都會對其進行更新。這使它可以根據最近看到的內容更新其預測。只要我們有足夠的內存,它甚至可以隨着時間對模式進行建模。
猜字母有什麼意義?
預測故事中的下一個字母可能似乎毫無用處。那它到底有什麼用呢?
一種很酷的用途是可以自動預測手機鍵盤:

下一個最可能的字母是「 t」
但是,如果我們將這個想法發揮到極致呢?如果我們要求模型不斷地預測下一個最可能出現的角色,該怎麼辦?我們會要求它為我們寫一個完整的故事!
生成一個故事
我們看到了如何猜測海明威句子中的下一個字母。讓我們嘗試以海明威風格製作一個完整的故事。
要做到這一點,我們將使用遞歸神經網絡實現的是「Andrej Karpathy」寫道。Andrej是斯坦福大學的深度學習研究員,他撰寫了一篇出色的介紹,介紹了如何使用RNN生成文本。您可以在github上查看該模型的所有代碼(後台回復github獲取代碼)。
我們將從「太陽照常升起」的完整文本中創建模型-362,239個字符,使用84個唯一字母(包括標點符號,大寫/小寫字母等)。與典型的實際應用程序相比,該數據集實際上很小。為了生成一個真正好的海明威風格模型,最好具有幾倍的示例文本。但這足以作為示例。
當我們剛剛開始訓練RNN時,預測字母並不是很好。這是經過100次循環訓練後產生的結果:

您可以看到,它已經發現有時候單詞之間有空格。
經過大約1000次迭代,優化了很多:

該模型已開始識別基本句子結構中的模式。它在句子甚至引用對話框的末尾添加句點。可以識別一些單詞,但是仍然有很多廢話。
但是經過數千次訓練迭代後,它看起來還不錯:

至此,該算法已捕獲了海明威簡短直接對話的基本模式。幾句話甚至說得通。
將其與書中的一些真實文本進行比較:

即使一次只尋找一個字符的圖案,我們的算法也以正確的格式重現了看起來似是而非的散文。太神奇了!
我們也不必完全從頭開始生成文本。我們可以通過提供前幾個字母,然後讓它找到下幾個字母來播種算法。
為了好玩,讓我們通過使用「 Er」,「 He」和「 S」的文本生成一個新的作者姓名和一個新的標題,為虛構的書製作一個假書的封面:

真正的書在左邊,而計算機生成的廢話在右邊。
看起來不錯!
但是真正令人興奮的部分是,該算法可以找出任何數據序列中的模式。它可以輕鬆生成真實的食譜或虛假的奧巴馬演講。但是,為什麼要限制自己的人類語言呢?我們可以將相同的思想應用於具有模式的任何類型的順序數據。
2015年,任天堂發佈了適用於Wii U遊戲系統的Super Mario Maker™。

每個孩子的夢想!
該遊戲可讓您在遊戲板上繪製自己的超級馬里奧兄弟關卡,然後將其上傳到互聯網,以便您的朋友可以通過它們玩。您可以在您的關卡中包含所有原始Mario遊戲中的經典道具和敵人。這就像是為樂於玩超級馬里奧兄弟的人們而設計的虛擬樂高玩具。
我們可以使用生成假海明威文字的模型來生成假超級馬里奧兄弟關卡嗎?
首先,我們需要一個數據集來訓練我們的模型。讓我們從1985年發佈的原始《超級馬里奧兄弟》遊戲中獲取所有室外水平:

有史以來最好的聖誕節。謝謝爸爸媽媽!
該遊戲共有32個等級,其中約70%具有相同的戶外風格。所以我們會堅持下去。
為了獲得每個關卡的設計,我製作了遊戲的原始副本,並編寫了一個程序將關卡設計拉出遊戲的內存。超級馬里奧兄弟(Super Mario Bros.)已有30年的歷史了,在線上有很多資源可以幫助您弄清楚關卡是如何存儲在遊戲內存中的。從舊的視頻遊戲中提取關卡數據是一項有趣的編程練習,您應該嘗試一下。
這是遊戲的第一個關卡(如果玩過遊戲,您可能還記得):
如果仔細觀察,我們可以看到該關卡是由一個簡單的對象網格組成的:

我們可以像用一個字符序列(每個對象代表一個字符)一樣容易地表示此網格:
-------------------------- -------------------------- -------------------------- #??#---------------------- -------------------------- -------------------------- -------------------------- -##------=--=----------==- --------==--==--------===- -------===--===------====- ------====--====----=====- =========================-我們用一個字母替換了關卡中的每個對象:
- 「-」是空格
- '='是一個固體塊
- '#'是易碎的磚塊
- '?' 是一個硬幣塊
…依此類推,對關卡中每種不同的對象使用不同的字母。
我最終得到的文本文件如下所示:
查看文本文件,可以看到逐行閱讀Mario關卡的方式實際上並不多:

逐行閱讀,實際上沒有可捕獲的模式。許多行是完全空白的。
當您將級別視為一系列列時,級別中的模式才真正出現:

逐列查看,這是一個真實的模式。例如,每列以'='結尾。
因此,為了使算法能夠在我們的數據中找到模式,我們需要逐列提供數據。弄清楚輸入數據的最有效表示形式(稱為功能選擇)是正確使用機器學習算法的關鍵之一。
要訓練模型,我需要將文本文件旋轉90度。這樣可以確保將字符按照更容易顯示圖案的順序輸入到模型中:
-----------= -------#---= -------#---= -------?---= -------#---= -----------= -----------= ----------@= ----------@= -----------= -----------= -----------= ---------PP= ---------PP= ----------== ---------=== --------==== -------===== ------====== -----======= ---========= ---=========
就像我們在創建海明威散文模型時所看到的那樣,模型在訓練過程中不斷完善。
經過一些訓練後,我們的模型產生了垃圾:
-------------------------- LL+<&=------P------------- -------- ---------------------T--#-- ----- -=--=-=------------=-&--T-------------- -------------------- --=------$-=#-=-_ --------------=----=<---- -------b -
它有點想法,「-」和「 =」應該出現很多,僅此而已。它尚未弄清楚模式。
經過數千次迭代,它開始看起來像:
-- -----------= ----------= --------PP= --------PP= -----------= -----------= -----------= -------?---= -----------= -----------=
該模型幾乎計算出每行的長度應相同。它甚至已經開始弄清楚Mario的一些邏輯:mario中的管道總是兩個塊寬,至少兩個塊高,因此數據中的「 P」應該出現在2×2群集中。太酷了!
經過大量的培訓,模型達到了生成完全有效數據的地步:
--------PP= --------PP= ----------= ----------= ----------= ---PPP=---= ---PPP=---= ----------=
讓我們從模型中採樣整個級別的數據,然後將其旋轉回水平:
這個數據看起來很棒!有幾件事要注意:
- 它在關卡開始時將Lakitu(漂浮在雲上的怪物)放在天空中–就像在真正的馬里奧關卡中一樣。
- 它知道漂浮在空中的管道應該放在堅固的塊體上,而不僅僅是懸掛在空中。
- 它將敵人放置在合乎邏輯的地方。
- 它不會產生任何阻礙玩家前進的東西。
- 這感覺就像從超級馬里奧兄弟1真實水平,因為它是基於關閉該在那場比賽中存在的原始水平的風格。
最後,讓我們採用此級別並在Super Mario Maker中重新創建它:

輸入Super Mario Maker後的關卡數據
自己玩!
如果您擁有Super Mario Maker,則可以通過在線對其進行書籤或使用級別代碼4AC9–0000–0157-F3C3進行查找來播放此級別。
玩具是真實世界的應用?
我們用於訓練模型的遞歸神經網絡算法與現實世界中的公司用於解決諸如語音檢測和語言翻譯之類的難題的算法相同。使我們的模型成為「玩具」而不是尖端技術的原因是,我們的模型是從很少的數據生成的。最初的《超級馬里奧兄弟》遊戲沒有足夠的關卡,無法為真正好的模型提供足夠的數據。
如果我們可以訪問任天堂擁有的成千上萬個用戶創建的超級馬里奧製造商級別,那麼我們可以做一個令人驚嘆的模型。但是我們不能-因為任天堂不會讓我們擁有它們。大公司不會免費提供其數據。
隨着機器學習在更多行業中變得越來越重要,好的程序和不好的程序之間的區別將在於,您必須訓練模型多少數據。這就是為什麼像Google和Facebook這樣的公司非常需要您的數據的原因!
例如,谷歌最近開放了TensorFlow的源代碼,它的軟件工具包用於構建大型機器學習應用程序。Google免費提供了如此重要且功能強大的技術,這是一筆不小的數目。這與為Google翻譯提供支持的功能相同。
但是,如果沒有Google每種語言的海量數據,您就無法與Google Translate競爭。數據是讓Google脫穎而出的原因。下次打開您的Google Maps位置記錄或Facebook位置記錄時,請考慮一下,請注意,它存儲了您去過的每個地方。
在機器學習中,從來沒有一種解決問題的方法。在決定如何預處理數據以及使用哪種算法時,您有無限的選擇。通常,將多種方法結合起來會比任何一種方法提供更好的結果。
讀者給我發送了其他鏈接來生成超級馬里奧關卡的有趣方法:
- 賈斯汀·米肖(Justin Michaud)擴展了我在這裡使用的生成關卡的方法,並弄清楚了如何將其生成的關卡破解回原始NES rom文件(30年前編寫的代碼)!您甚至可以在線播放他被黑的rom。
- 艾米·胡佛(Amy K. Hoover)的小組使用了一種方法來表示每種類型的關卡對象(管道,地面,平台等),就好像它是整個交響樂中的單一聲音一樣。使用稱為功能支架的過程,系統可以使用任何給定對象類型的塊來增加級別。例如,您可以繪製關卡的基本形狀,並可以添加管道和問題塊來完成設計。
- 史蒂夫·達爾斯科格(Steve Dahlskog)的團隊表明,將級別數據的每一列建模為一系列n-gram「單詞」 ,從而可以通過比大型RNN 更簡單的算法來生成級別。