0
本文作者: skura | 2019-10-08 18:56 |
不久前,我們開發(fā)了代碼來模擬 21 點游戲。通過這些模擬,我們發(fā)現(xiàn)了賭場之所以具有優(yōu)勢的關(guān)鍵驅(qū)動因素。主要是以下 2 點:
賭場迫使玩家在莊家面前采取行動(面對的是不完整的信息),從而獲得了 21 點玩家的優(yōu)勢。這首先會讓玩家面臨破產(chǎn)的風(fēng)險(所以他們可能在莊家有機(jī)會采取行動之前就已經(jīng)破產(chǎn)了)。
當(dāng)玩家的手牌值總數(shù)在 12 到 16 之間時,他們處于尤其危險的情況中(他們可能會被下一張牌打爆),而莊家則顯示出一張高數(shù)值牌。在這些情況下,假設(shè)莊家手中的牌總點數(shù)很多,則玩家要么拿牌要么不動。我們可以通過玩家在 12 到 16 之間獲勝或平局的概率來直觀地看到這一點。
獲勝或平局的概率與玩家手中牌值總數(shù)(21 未顯示,因為概率為 100%)
最后,我們觀察到一個簡單的策略:只有在沒有機(jī)會打爆的情況下才拿牌,這大大提高了我們獲勝的幾率,因為它將打爆的風(fēng)險完全轉(zhuǎn)移到了賭場。
如果你不熟悉 21 點游戲,我之前的文章描述了玩這個游戲的規(guī)則。
但是深度學(xué)習(xí)能做得更好嗎?
本文的目標(biāo)是討論我們是否可以用深度學(xué)習(xí)來找到一個比上面更好的策略。我們將:
使用我們上次編碼的 BLUJACK 模擬器生成數(shù)據(jù)(通過一些修改,使其更適合于訓(xùn)練算法)。
編碼訓(xùn)練神經(jīng)網(wǎng)絡(luò)玩 21 點。
如果你不熟悉神經(jīng)網(wǎng)絡(luò),我在這篇文章中寫過它們的簡介。
一個簡單神經(jīng)網(wǎng)絡(luò)的視覺缺陷
在我們開始訓(xùn)練之前,讓我們退一步,快速討論一下在這種情況下使用神經(jīng)網(wǎng)絡(luò)的優(yōu)缺點。神經(jīng)網(wǎng)絡(luò)是高度靈活的算法——像軟粘土一樣,神經(jīng)網(wǎng)絡(luò)調(diào)整自己,以適應(yīng)數(shù)據(jù)的輪廓。神經(jīng)網(wǎng)絡(luò)可以很容易地處理那些會給線性回歸等帶來麻煩的數(shù)據(jù)。此外,網(wǎng)絡(luò)中的層和神經(jīng)元將學(xué)習(xí)數(shù)據(jù)中可能存在的任何深度嵌入的非線性關(guān)系。
然而,這種功能是有代價的——神經(jīng)網(wǎng)絡(luò)是一個黑箱模型。與回歸不同的是,我們可以通過觀察回歸系數(shù)來了解模型是如何做出決策的,神經(jīng)網(wǎng)絡(luò)并不具備這種透明性。此外,神經(jīng)網(wǎng)絡(luò)也有可能把我們的數(shù)據(jù)擬合得太好,以致于不能很好地概括樣本外數(shù)據(jù)。在我看來,這些缺點值得牢記,并為其設(shè)計保護(hù)措施,但它們并不是回避使用神經(jīng)網(wǎng)絡(luò)的理由。
生成我們的訓(xùn)練數(shù)據(jù)
在訓(xùn)練神經(jīng)網(wǎng)絡(luò)之前,我們首先需要弄清楚如何構(gòu)造訓(xùn)練數(shù)據(jù),這樣我們用它建立的模型才會是有用的。
我們想要預(yù)測什么?在我看來,我們的目標(biāo)變量有兩個:
輸?shù)舯荣惖目赡苄?。考慮到這種情況,我們可能希望模型告訴我們輸?shù)母怕适嵌嗌?。再說一次,只有當(dāng)我們可以增加或減少賭注時,這才有用,而在 21 點游戲中我們不能這樣做。
相反,我們希望我們的神經(jīng)網(wǎng)絡(luò)能夠識別正確的動作,拿牌或不動。所以我們的目標(biāo)變量應(yīng)該是「正確的動作是拿牌還是不動」。
實際上我花了一段時間才找到最好的方法來設(shè)置這個。下面這是我想到的。
我們需要一種方法讓神經(jīng)網(wǎng)絡(luò)知道給定的動作是否正確。它不需要萬無一失,只需要大體正確。所以我決定一個動作是否正確的方法是模擬 21 點游戲:把牌交給玩家和莊家,檢查是否有人有 21 點,只做一個動作(拿牌或不動),模擬游戲結(jié)束并記錄結(jié)果。由于模擬玩家只做一個決定,我們可以通過他是贏還是輸來評估這個決定的質(zhì)量:
如果玩家拿牌并獲勝,那么拿牌(y=1)是正確的決定。
如果玩家打輸了,那么不動(y=0)是正確的決定。
如果玩家不動并獲勝,那么不動(Y=0)是正確的決定。
如果玩家不動并輸了,那么拿牌(y=1)是正確的決定。
這允許我們訓(xùn)練出一個模型,其輸出是一個預(yù)測是拿牌或者不動。其代碼與上一次類似,因此我不會在這里給出詳細(xì)的概述(你也可以在我的 github 上找到它)。其主要特點是:
莊家的正面卡(另一張隱藏起來)。
玩家手上牌的總值。
玩家是否有王牌。
玩家的動作(拿牌或不動)。
目標(biāo)變量是由上述邏輯定義的正確決策。
訓(xùn)練神經(jīng)網(wǎng)絡(luò)
我們將使用 keras 庫訓(xùn)練我們的神經(jīng)網(wǎng)絡(luò)。我們需要導(dǎo)入的庫文件為:
from keras.models import Sequential
from keras.layers import Dense, LSTM, Flatten, Dropout
現(xiàn)在我們設(shè)置輸入變量來訓(xùn)練神經(jīng)網(wǎng)絡(luò)。變量 feature_list 是一個列表,其中列有我在上面列出的特征(x 個變量)的列名。dataframe model_df 是存儲我運行的 21 點模擬的所有數(shù)據(jù)的地方。
# Set up variables for neural net
feature_list = [i for i in model_df.columns if i not in
['dealer_card','Y','lose','correct_action']
]
train_X = np.array(model_df[feature_list])
train_Y = np.array(model_df['correct_action']).reshape(-1,1)
實例化和訓(xùn)練神經(jīng)網(wǎng)絡(luò)的代碼行非常簡單。第一行(第 1 行)創(chuàng)建一個順序型神經(jīng)網(wǎng)絡(luò),它是神經(jīng)網(wǎng)絡(luò)層的線性序列。第一行之后的代碼逐個地向我們的模型中添加層。
最后,對于最后一層,我們需要選擇一個激活函數(shù)。這將神經(jīng)網(wǎng)絡(luò)的原始輸出轉(zhuǎn)換成我們可以解釋的東西。在最后一層需要注意兩件事。首先,它只包含一個神經(jīng)元,因為我們正在預(yù)測兩種可能的結(jié)果(二分類問題)。第二,我們使用 sigmoid 函數(shù)激活,因為我們希望我們的神經(jīng)網(wǎng)絡(luò)像邏輯回歸一樣,并預(yù)測正確的動作是拿牌(y=1)還是不動(y=0)——換句話說,我們想知道拿牌是否是正確的動作。
最后兩行告訴我們的神經(jīng)網(wǎng)絡(luò)模型使用什么損失函數(shù)將模型匹配到我們的數(shù)據(jù)。我沒有花太多的時間來調(diào)整層數(shù)或神經(jīng)元的數(shù)量,但是如果有人玩我的代碼,我會建議他對這些潛在的地方進(jìn)行改進(jìn)。
# Set up a neural net with 5 layers
model = Sequential() # line 1
model.add(Dense(16))
model.add(Dense(128))
model.add(Dense(32))
model.add(Dense(8))model.add(Dense(1, activation='sigmoid')) # final layer
model.compile(loss='binary_crossentropy', optimizer='sgd')
model.fit(train_X, train_Y, epochs=20, batch_size=256, verbose=1)
檢查我們模型的性能
檢查我們的模型是否增加任何值的一種快速方法是使用 ROC 曲線。ROC 曲線告訴我們,我們的模型在收益(真陽性率)和成本(假陽性率)之間的權(quán)衡情況——曲線下的面積越大,模型越好。
下面的圖片顯示了我們玩 21 點的神經(jīng)網(wǎng)絡(luò)的 ROC 曲線——神經(jīng)網(wǎng)絡(luò)似乎是增加了一個合理的猜測值(紅色虛線)。曲線下面積(AUC)為 0.73,明顯高于隨機(jī)猜測的 AUC(0.50)。
21 神經(jīng)網(wǎng)絡(luò)的 ROC 曲線
我用我的訓(xùn)練數(shù)據(jù)繪制 ROC 曲線。通常我們希望使用驗證或測試數(shù)據(jù)繪制它,但在這種情況下,我們知道只要樣本足夠大,它就能代表總體(假設(shè)我們繼續(xù)使用相同的規(guī)則玩 21 點)。我們希望我們的模型能夠很好地概括任何新的數(shù)據(jù)都與訓(xùn)練數(shù)據(jù)相同的基本統(tǒng)計特征。
玩游戲的時間到了!
在我們的神經(jīng)網(wǎng)絡(luò)正式開始工作之前,我們需要給它一個決策規(guī)則。記住,sigmoid 激活(從我們最后的神經(jīng)網(wǎng)絡(luò)層)使我們的神經(jīng)網(wǎng)絡(luò)輸出一個正確的動作的概率。我們需要一個決策規(guī)則,在給定這個概率的情況下,我們決定是拿牌還是不動。
我寫了下面的函數(shù)來實現(xiàn)這一點——model_decision 函數(shù)具有神經(jīng)網(wǎng)絡(luò)所需的特征,使用這些特征進(jìn)行預(yù)測,并將預(yù)測與預(yù)定義的閾值進(jìn)行比較,以決定是拿牌還是不動。我使用 0.52,因為我們已經(jīng)從上一次就知道,對 21 點玩家來說,爆破是最大的風(fēng)險。因此,使用 0.52 作為拿牌的截止值會使我們的模型爆破的可能性稍微降低,因此輸?shù)舻目赡苄陨晕⒔档汀?/p>
def model_decision(model, player_sum, has_ace, dealer_card_num):
input_array = np.array([player_sum, 0, has_ace,
dealer_card_num]).reshape(1,-1)
predict_correct = model.predict(input_array)
if predict_correct >= 0.52:
return 1
else:
return 0
現(xiàn)在我們只需要將上面的函數(shù)添加到代碼中,在這里我們可以決定是否拿牌。因此,當(dāng)決定該做什么時,神經(jīng)網(wǎng)絡(luò)將根據(jù)莊家出示的牌、自己牌的總面值以及是否持有王牌來做出決定。
我們的模型很好!
最后,我們將神經(jīng)網(wǎng)絡(luò)的性能與原始策略和隨機(jī)策略進(jìn)行比較。需要提醒大家的是:
我為每種策略類型(神經(jīng)網(wǎng)絡(luò)、簡單和隨機(jī)策略)運行了大約 300000 個 21 點模擬實驗。
簡單策略只有在沒有破發(fā)機(jī)會的情況下才出手(手牌總數(shù)低于 12 時拿牌,手牌總數(shù)為 12 或更多時才出手)。
隨機(jī)的策略就像是擲硬幣——如果它出現(xiàn)頭部則拿牌,否則不動。如果你拿牌了,但沒有爆破,那么再擲硬幣,重新開始整個過程。
讓我們看看我們的神經(jīng)網(wǎng)絡(luò)是否能找到更好的策略。下表顯示了每種策略類型的結(jié)果分布。我有兩件事要做。首先,我們的神經(jīng)網(wǎng)絡(luò)在玩游戲時只損失了不到一半(49%)。我認(rèn)為這是相當(dāng)體面的游戲,賠率對你來說是固定的。第二,它實際上不會比簡單的策略更容易獲勝,相反,它能夠產(chǎn)生更多的關(guān)系。
各種戰(zhàn)略的結(jié)果
我們還可以看看策略如何利用我們的主要特征(莊家的牌數(shù)和玩家手牌總值)。首先,讓我們來看看在我們的三個策略中,莊家出示的牌對獲勝或平手概率的影響。在下面的圖形中,如果莊家牌數(shù)很少,我們的神經(jīng)網(wǎng)絡(luò)執(zhí)行效果。但是當(dāng)經(jīng)莊家牌數(shù)更高(7或更多)時,我們的神經(jīng)網(wǎng)絡(luò)表現(xiàn)得更好。
與莊家出示的牌平手或勝出的概率(牌數(shù)越多越好?。?/p>
我們可以看看贏或平手的概率是如何隨玩家的初始牌數(shù)而變化的。這看起來很有希望——我們的神經(jīng)網(wǎng)絡(luò)也表現(xiàn)得很好或者更好。與簡單的策略不同,它比玩家手牌值在 12 和 16 之間時的結(jié)果更壞,我們的神經(jīng)網(wǎng)絡(luò)表現(xiàn)更好。
平局或獲勝的概率與玩家的初始手牌值的關(guān)系
上面的圖展示了神經(jīng)網(wǎng)絡(luò)如何超越簡單的策略。簡單的策略不愿意冒險,甚至遠(yuǎn)離爆破風(fēng)險。另一方面,神經(jīng)網(wǎng)絡(luò)經(jīng)常命中 12、13、14 或 15。這是更細(xì)微的決策,而承擔(dān)計算風(fēng)險的能力似乎將它與簡單的策略區(qū)分開來。
神經(jīng)網(wǎng)絡(luò)和簡單策略隨玩家初始手值的變化趨勢
我們可以看看當(dāng)玩家的手值總數(shù)在 12 到 16 之間時,神經(jīng)網(wǎng)絡(luò)會做些什么來改善我們的簡單策略,以免損失太多的錢到賭場。
當(dāng)莊家出示一張高值的牌(8、9 或 10)時,似乎有強(qiáng)烈的偏好。但是即使當(dāng)莊家出示低值牌,如 3 時,神經(jīng)網(wǎng)絡(luò)仍然在 60% 的情況下選擇拿牌——這是因為神經(jīng)網(wǎng)絡(luò)是考慮到所有的特征然后才作出決定。因此,我們似乎不能輕易地將它的決定提煉成幾個簡單的經(jīng)驗法則。
神經(jīng)網(wǎng)絡(luò)的拿牌頻率與莊家出示的牌值
結(jié)論
希望這篇文章給了你一個關(guān)于如何使用機(jī)器學(xué)習(xí)輔助現(xiàn)實生活中決策的不錯介紹。以下是當(dāng)你訓(xùn)練自己的模型時要記住的一些事情(不管它們是決策樹、回歸或神經(jīng)網(wǎng)絡(luò)):
我的目標(biāo)變量的結(jié)構(gòu)是否能讓我預(yù)測它,然后我就能解決我的問題?在你開始收集數(shù)據(jù)和構(gòu)建你的模型之前,確保你的預(yù)測是正確的至關(guān)重要。
新的數(shù)據(jù)和我訓(xùn)練過的數(shù)據(jù)有什么不同?如果它變化很大,那么統(tǒng)計模型甚至可能不是你的問題的正確答案。至少,你必須認(rèn)識到這一點,并建立保障措施,例如用正規(guī)化和嚴(yán)格的驗證和測試集對模型進(jìn)行基準(zhǔn)測試。
無法理解模型是如何到達(dá)它的決策的,所以你沒有辦法去理解和檢驗?zāi)愕哪P驮趪?yán)格的測試之外做出的決策,而這些測試是在模型訓(xùn)練過程中進(jìn)行的。
最后一句關(guān)于 21 點的話。我可能暫時不會再寫關(guān)于賭博的文章了(我還有太多其他的話題想探討)。但是,如果有人對使用或擴(kuò)展我的代碼有興趣,這里有幾個對這個項目潛在的有趣擴(kuò)展:
嘗試通過更優(yōu)化的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)來改進(jìn)模型,或者添加用于拆分 A 的代碼(我沒有把它構(gòu)建到我原來的模擬器中),或者選擇比我使用的基本特征更好的特征。
給模型計算卡片的能力,看看它對一副牌和六副牌的性能有什么影響(這是拉斯維加斯的標(biāo)準(zhǔn))。
希望你和我一樣發(fā)現(xiàn)它們的樂趣……干杯!
via:https://towardsdatascience.com/teaching-a-neural-net-to-play-blackjack-8ec5f39809e2
雷鋒網(wǎng)雷鋒網(wǎng)雷鋒網(wǎng)
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。