利用蒙特卡洛方法實現21點問題的最優解(內含python原始碼)
想要獲取完整程式碼,請訪問麵包多進行支援哦,僅需一口奶茶的錢!
一、實驗目的
實現基於蒙特卡洛法的21點問題的最優解,瞭解強化學習的基本原理,理解蒙特卡洛法並編寫相應的程式碼。
二、實驗內容
賭場上流行的21點紙牌遊戲的目的是獲得其數值之和儘可能大而不超過21的牌。所有的人形牌面都算作10,而A可以算作1或11。我們的實驗僅考慮每個玩家獨立與莊家競爭的版本。遊戲開始時,莊家和玩家都有兩張牌。莊家的一張牌面朝上,另一張牌面朝下。如果玩家有21張牌(一張A和一張10牌),則稱為自然牌。他就贏了,除非莊家也有自然牌,在這種情況下,遊戲是平局。如果玩家沒有自然牌,那麼他可以要求額外的牌,單張發牌(hits),直到他停止(sticks
三、實驗過程
本次實驗需要匯入如下包:
import gym
import numpy as np
from collections import defaultdict
import matplotlib
import matplotlib.pyplot as plt
運用gym自帶的21點遊戲進行接下來的程式設計:
env = gym.make('Blackjack-v0') observation = env.reset() print(env.action_space, env.observation_space, sep='\n')
這段程式碼返回了玩家的當前點數之和 ∈{0,1,…,31} ,莊家朝上的牌點數之和 ∈{1,…,10} ,及玩家是否有能使用的ace(no =0 、yes =1 ),和智慧體可以執行兩個潛在動作:STICK = 0,HIT = 1。
本次實驗採用On-policy first-visit MC control,On-policy方法在一定程度上解決了exploring starts這個假設,讓策略既greedy又exploratory,最後得到的策略也一定程度上達到最優。如下圖所示:
我們定義一個巢狀函式:
def make_epsilon_greedy_policy(Q_table, nA, epsilon): def generate_policy(observation): prob_A = np.ones(nA) * epsilon / nA optimal_a = np.argmax(Q_table[observation]) prob_A[optimal_a] += (1.0 - epsilon) return prob_A return generate_policy
MC演算法是逐幕進行的,所以我們要根據策略來生成一幕資料。
這裡要注意:generate_policy是一個函式即make_epsilon_greedy_policy的返回值。generate_policy的返回值是 π \piπ 。這裡迴圈了1000次只是為了確保能獲得完整的一幕。
接下來是MC控制的主體部分,我們要迴圈足夠多的次數使得價值函式收斂,每次迴圈都首先根據策略生成一幕樣本序列,然後遍歷每個“狀態—價值”二元組,並用所有首次訪問的回報的平均值作為估計.
這裡要注意:Return和Count是字典,每個“狀態—價值”二元組是一個key,該二元組每一幕的回報是它的value,隨著越來越多的迭代,根據大數定律,它的平均值會收斂到它的期望值。並且在下一輪迭代生成另外一幕樣本序列的時候,generate_policy函式會根據Q_table更新。
def MC_control(env, iteration_times=500000, epsilon=0.1, discount_factor=1.0):
Return, Count, Q_table = defaultdict(float), defaultdict(float), defaultdict(lambda: np.zeros(env.action_space.n))
policy = make_epsilon_greedy_policy(Q_table, env.action_space.n, epsilon)
for i in range(iteration_times):
if i % 1000 == 0:
print(str(i) + "次")
trajectory = generate_one_episode(env, policy)
s_a_pairs = set([(x[0], x[1]) for x in trajectory])
for state, action in s_a_pairs:
s_a = (state, action)
first_visit_id = next(i for i, x in enumerate(trajectory) if x[0] == state and x[1] == action)
G = sum([x[2] * (discount_factor ** i) for i, x in enumerate(trajectory[first_visit_id:])])
Return[s_a] += G
Count[s_a] += 1.
Q_table[state][action] = Return[s_a] / Count[s_a]
return policy, Q_table
接下來是將價值函式視覺化:
def plot_value_function(Q_table):
x = np.arange(12, 21)
y = np.arange(1, 10)
X, Y = np.meshgrid(x, y)
Z_noace = np.apply_along_axis(lambda x: Q_table[(x[0], x[1], False)], 2, np.dstack([X, Y]))
Z_ace = np.apply_along_axis(lambda x: Q_table[(x[0], x[1], True)], 2, np.dstack([X, Y]))
def plot_surface(X, Y, Z, title):
程式碼過長略
實驗結束。想要獲取完整程式碼,請訪問麵包多進行購買
四、實驗結果
執行如上程式碼,在程式碼檔案RL中,輸出圖所示: