博弈論與強化學習實戰——CFR演算法——剪刀石頭布
博弈論與強化學習實戰——CFR演算法——剪刀石頭布
目錄感謝:
淺談德州撲克AI核心演算法:CFR - 掘金 (juejin.cn)
虛擬遺憾最小化演算法(CFR)基礎知識詳解 - 知乎 (zhihu.com)
一 遊戲介紹
-
有兩個參與者,
-
每個參與者有三個可選動作 剪刀石頭布 ,分別用0,1,2表示
-
獎勵:獲勝獎勵為1,失敗獎勵為-1,平局沒有獎勵,收益矩陣如下
-
博弈過程用博弈樹進行描述:
第二個玩家在決策的時候有三個可能的狀態\(h1,h2,h3\),但由於三個狀態在同一個資訊集中,所以玩家2在決策的時候並不知到具體處於哪個資訊集,所以玩家2的決策並不依賴於玩家1的行動結果,從效果上來看就等同於兩者同時划拳。
-
玩家的策略即玩家選擇三個不同動作的概率,
使用程式碼將遊戲流程
#遊戲設定 NUM_ACTIONS = 3 #可選的動作數量 actions = [0,1,2] # 0代表剪刀scissors , 1代表石頭rock ,2 代表布 paper actions_print=['剪刀','石頭','布'] #動作的收益 ,兩個人進行博弈,結果 utility_matrix = np.array([ [0,-1,1], [1,0,-1], [-1,1,0] ]) """基本資訊初始化""" # 玩家,初始化 #策略 player1_strategy = np.array([0,0,1]) player2_strategy = np.array([0.4,0.3,0.3]) #動作收益 player1_utility = np.zeros(3) player2_utility = np.zeros(3) """1局遊戲的過程""" print(f'----------------遊戲開始-------------------') # 使用當前策略 選擇動作 action_p1 = np.random.choice(actions, p=player1_strategy) action_p2 = np.random.choice(actions, p=player2_strategy) print(f'玩家1 動作:{actions_print[action_p1]} ,玩家2 動作:{actions_print[action_p2]} .') # 得到收益 reward_p1 = utility_matrix[action_p1, action_p2] reward_p2 = utility_matrix[action_p2, action_p1] # 輸出遊戲結果 print(f'----遊戲結束-----') print(f'玩家1 收益{reward_p1} ,玩家2 收益{reward_p2}.') # 更新玩家的收益 player1_utility[action_p1] += reward_p1 player2_utility[action_p2] += reward_p2 # 輸出一局遊戲後的動作收益矩陣 print(f'收益更新---------動作:{actions_print[0]} {actions_print[1]} {actions_print[0]}') print(f'玩家1的累計收益 收益:{player1_utility[0]}; {player1_utility[1]}; {player1_utility[2]} ') print(f'玩家2的累計收益 收益:{player2_utility[0]}; {player2_utility[1]}; {player2_utility[2]} ')
二 問題引出
假定現在有一個玩家(玩家1)的策略(動作集合上的概率分佈)為 0.4,0.3 ,0.3 ,那麼玩家2的策略應該是怎樣的呢?
方法一 :求解期望獎勵最大的策略
假定玩家2的概率分別為a,b,(1-a-b)
那麼其期望收益(獎勵乘以發生的概率)為:
\[[(0.4* a) *0+(0.3* a) *-1+(0.3* a) *1]+ \\ [(0.4* b) *1+(0.3* b) *0+(0.3* b) *-1]+ \\ [(0.4* 1-a-b) *-1+(0.3*1- a-b) *1+(0.3* 1-a-b) *0] \\ =0.2b+0.1a-0.1 \]要想使得收益最大,結果為\(b=1\),
所以玩家2的策略應為\([0,1,0]\)
方法2 : 使用CFR演算法求解
方法3 :使用強化學習方法求解
擴充套件問題:
- 當對戰雙方都使用相同的演算法進行學習,最終結果會不會達到均衡?
- 當雙方使用不同的學習演算法進行學習,哪個演算法達到均衡速度更快?
三 CFR演算法求解
1 Regret matching 演算法
1 遺憾值的定義
\[R^{T}(a)=\sum_{t} a \cdot r^{t}-\sum_{t} \sigma^{t} \cdot r^{t} \]含義: 選擇動作a和事實上的策略(概率\(\sigma\))產生的收益的差別 ,也就是遺憾值(本可以獲得更多) ;
遺憾值大於0表示動作\(a\)比當前策略更好,遺憾值小於0表示動作\(a\)不如當前策略
2 Regret matching 演算法
\[\sigma^{T}(a)= \frac{R^{T-1}(a)^{+}}{\sum_{b \in A} R^{T-1}(b)^{+}} , \\ where\ x^+ = max(x,0) \]上式中 \(R^{T-1}(a)\)表示動作\(a\)的歷史遺憾值,然後對其和0取最大值。
和0取最大值目的是要得到累計正的遺憾值,因為只有正的遺憾值對應的動作才是改進的方向。
這個結果就是得到歷史遺憾為正的動作,在所有的正的歷史遺憾對應的動作計算其分佈(也就是概率)然後作為下一次博弈的策略
3 演算法流程
Regret matching演算法流程為:
-
對於每一位玩家,初始化所有累積遺憾為0。
-
for from 1 to T(T:迭代次數):
a)使用當前策略與對手博弈
b)根據博弈結果計算動作收益,利用收益計算後悔值
c)歷史後悔值累加
d)根據後悔值結果更新策略
-
返回平均策略(累積後悔值/迭代次數)
作者:行者AI
連結:https://juejin.cn/post/7057430423499964424
來源:稀土掘金
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
4 程式碼實現
完整程式碼:
# -*- coding: utf-8 -*-
"""
@author : zuti
@software : PyCharm
@file : rock_cfr.py
@time : 2022/11/21 9:26
@desc :
"""
import numpy as np
#動作設定
NUM_ACTIONS = 3 #可選的動作數量
actions = [0,1,2] # 0代表剪刀scissors , 1代表石頭rock ,2 代表布 paper
actions_print=['剪刀','石頭','布']
#動作的收益 ,兩個人進行博弈,結果
utility_matrix = np.array([
[0,1,-1],
[-1,0,1],
[1,-1,0]
])
"""基本資訊初始化"""
# 玩家,初始化
#策略
player1_strategy = np.array([0.4,0.3,0.3])
player2_strategy = np.array([1/3,1/3,1/3])
#動作收益
player1_utility = np.zeros(3)
player2_utility = np.zeros(3)
#遺憾值
player2_regret = np.zeros(3)
#每一局策略(動作的概率分佈)之和
player2_strategy_count = np.zeros(3)
for i in range(10000):
"""1局遊戲的過程"""
#對策略進行計數
player2_strategy_count += player2_strategy
print(f'----------------遊戲開始-------------------')
# 使用當前策略 選擇動作
action_p1 = np.random.choice(actions, p=player1_strategy)
action_p2 = np.random.choice(actions, p=player2_strategy)
print(f'玩家1 動作:{actions_print[action_p1]} ,玩家2 動作:{actions_print[action_p2]} .')
# 得到收益
reward_p1 = utility_matrix[action_p2, action_p1]
reward_p2 = utility_matrix[action_p1, action_p2]
# 輸出遊戲結果
print(f'----遊戲結束-----')
print(f'玩家1 收益{reward_p1} ,玩家2 收益{reward_p2}.')
# 更新玩家的收益
player1_utility[action_p1] += reward_p1
player2_utility[action_p2] += reward_p2
# 輸出一局遊戲後的動作收益矩陣
print(f'收益更新---------動作:{actions_print[0]} {actions_print[1]} {actions_print[2]}')
print(f'玩家1的累計收益 收益:{player1_utility[0]}; {player1_utility[1]}; {player1_utility[2]} ')
print(f'玩家2的累計收益 收益:{player2_utility[0]}; {player2_utility[1]}; {player2_utility[2]} ')
#
"""遺憾值更新"""
# 根據結果收益計算所有動作的遺憾值
for a in range(3):
# 事後角度 選擇別的動作的收益
counterfactual_reward_p2 = utility_matrix[action_p1,a ] # 如果選擇動作a(而不是事實上的動作action_p1) ,會獲得的收益
regret_p2 = counterfactual_reward_p2 - reward_p2 # 選擇動作a和事實上的動作action_p1產生的收益的差別 ,也就是遺憾值(本可以獲得更多)
# 更新玩家的動作遺憾值,歷史遺憾值累加
player2_regret[a] += regret_p2
print(f'遺憾值更新--------動作:{actions_print[0]} {actions_print[1]} {actions_print[0]}')
print(f'玩家2的累計遺憾值 {player2_regret[0]}; {player2_regret[1]}; {player2_regret[2]} ')
"""根據遺憾值更新策略"""
"""遺憾值歸一化"""
# 歸一化方法: 1 只看遺憾值大於0的部分,然後計算分佈
palyer2_regret_normalisation = np.clip(player2_regret, a_min=0, a_max=None)
print(f'遺憾值歸一化')
print(f'玩家1歸一化後的累計遺憾值 {palyer2_regret_normalisation [0]}; {palyer2_regret_normalisation [1]}; {palyer2_regret_normalisation [2]} ')
"""根據歸一化後的遺憾值產生新的策略"""
palyer2_regret_normalisation_sum = np.sum(palyer2_regret_normalisation) # 求和
if palyer2_regret_normalisation_sum > 0:
player2_strategy = palyer2_regret_normalisation / palyer2_regret_normalisation_sum
else:
player2_strategy = np.array([1 / 3, 1 / 3, 1 / 3]) #否則就採取平均策略
"""最終結果:得到平均策略"""
print(f'-----迭代結束,得到最終的平均策略---------')
#根據累計的策略計算平均策略
average_strategy = [0, 0, 0]
palyer2_strategy_sum = sum(player2_strategy_count)
for a in range(3):
if palyer2_strategy_sum > 0:
average_strategy[a] = player2_strategy_count[a] / palyer2_strategy_sum
else:
average_strategy[a] = 1.0 / 3
print(f'玩家2經過迭代學習得到的平均策略為')
print(f'玩家2的動作 \n 動作:{actions_print[0]} 概率:{average_strategy[0]};動作:{actions_print[1]} 概率:{average_strategy[1]};動作:{actions_print[2]} 概率:{average_strategy[2]} ')
2 CFR演算法
1 博弈樹中間結點的收益
概念
基於終止狀態的收益\(u\)對博弈樹中的每個節點都定義一個收益。
最主要的目的是給出博弈樹中的中間非葉子結點的收益。
當玩家\(p\)遵循策略\(σ\)時,對於博弈樹中任意的一個狀態\(h\),該狀態的收益定義為:
\[u_{p}^{\sigma}(h)=\sum_{z \in Z, h \sqsubset z} \pi^{\sigma}(z) u_{p}(z) \]式子中,\(u_p(z)\) 按著前面的定義即為 玩家\(p\)到達終止狀態\(z\)(葉子節點)所獲得的收益;
前面的\(\pi^\sigma(z)\)表示從初始狀態出發,當所有玩家都遵循策略\(σ\)時,到達終止狀態\(z\)的概率;
求和即表示從初始狀態開始把所有包含路徑\(h\)到達終點\(z\)的序列進行求和
這個收益即表示 玩家\(p\) 從博弈起點到中間狀態\(h\) 再根據策略\(\sigma\)到達終點\(z\)得到的收益。
可以將右端前一項根據概率式1 進行拆分 ,得到
\[\begin{aligned} u_{p}^{\sigma}(h) &=\sum_{z \in Z, h \sqsubset z} \pi^{\sigma}(z) u_{p}(z) \\ &=\sum_{z \in Z, h \sqsubset z} \pi_{p}^{\sigma}(z) \pi_{-p}^{\sigma}(z) u_{p}(z) \ 參與者拆分(1) \\ &=\sum_{z \in Z, h \sqsubset z} \pi^{\sigma}(h) \pi^{\sigma}(z \mid h) u_{p}(z) \ 路徑拆分(3) \\ &=\pi_{p}^{\sigma}(h) \sum_{z \in Z, h \sqsubset z} \pi_{-p}^{\sigma}(h) \pi^{\sigma}(z \mid h) u_{p}(z) 參與者+路徑拆分(1)+(3) \end{aligned} \]
根據此定義,整局遊戲的收益即為博弈樹根節點的收益 $ u_{p}{\sigma}=u_{p}{\sigma}(\varnothing) $
當玩家\(p\)遵循策略\(σ\)時,對於博弈樹中的一個資訊集\(I \in \mathcal{I}\)的收益定義為:
\[u_{p}^{\sigma}(I)=\sum_{h \in I} u_{p}^{\sigma}(h) \]算例
這裡給出第二個問題作為一個計算的例子:
玩家\(p\)(為玩家2),其策略\(\sigma\)為\([a,b,1-a-b]\) ,其他玩家\(-p\)(也就是玩家1)的策略\(\sigma\)為\([0.4,0.3,0.3]\),博弈樹見上。
根據上述定義,我們來嘗試計算博弈樹中間結點\(h1\)的收益
首先,包含中間結點\(h1\),從遊戲開始到達最終結果\(z1,z2,z3\)的路徑總共3條。
根據定義式:
第二項:玩家\(p\)玩家2在最終結果的收益分別為
\[u_{p2}(z1)=0 , u_{p2}(z2)= 1, u_{p2}(z3)= -1 \]第一項:從起點出發,經過中間結點\(h1\),到達最終結果\(z1,z2,z3\)的概率,根據玩家\(p2\)和\(-p\)(也就是玩家1)的策略\(\sigma\)計算為
\[0.4a,0.4b,0.4(1-a-b) \]
概率乘以收益再求和便得到了博弈樹中間結點\(h1\)的收益
\[\begin{aligned} u_{p2}^{\sigma}(h1) &= \sum_{z \in Z, h \sqsubset z} \pi^{\sigma}(z) u_{p2}(z) \\ &= 0.4a * 0 + 0.4 b* 1+0.4(1-a-b) *-1 \\ &= 0.4a+0.8b-0.4 \end{aligned} \]同樣的方法還可以得到博弈樹中間結點\(h2,h3\)的收益
\[\begin{aligned} u_{p2}^{\sigma}(h2) &= \sum_{z \in Z, h \sqsubset z} \pi^{\sigma}(z) u_{p}(z) \\ &= 0.3a * -1 + 0.3 b* 0+0.3(1-a-b) *1 \\ &= -0.6a-0.3b+0.3 \end{aligned} \begin{aligned} u_{p2}^{\sigma}(h3) &= \sum_{z \in Z, h \sqsubset z} \pi^{\sigma}(z) u_{p}(z) \\ &= 0.3a * 1 + 0.3 b* -1+0.3(1-a-b) *0 \\ &= 0.3a - 0.3b \end{aligned} \]資訊集\(I\)包含三個結點\(h1,h2,h3\),因此資訊集\(I\)的收益為
\[\begin{aligned} u_{p2}^{\sigma}(I) &=\sum_{h \in I} u_{p2}^{\sigma}(h) \\ &=u_{p2}^{\sigma}(h1)+u_{p2}^{\sigma}(h2)+u_{p2}^{\sigma}(h3) \\ &= 0.1a+0.2b-0.1 \end{aligned} \]理解:資訊集\(I\)的收益是基於玩家\(-p\)(玩家1)的策略\(\sigma\) 和 從開始到達最終結點的各條路徑。
如果玩家\(p\)(玩家2)想使在資訊集\(I\)的收益最大,那麼玩家\(p\)(玩家2)的策略(動作集合上的概率)為\([0,1,0]\),能夠獲得的期望收益為\(0.1\)
這個結果和我們之前的計算是一致的。由於資訊集,所以遍歷這個博弈樹和矩陣博弈的效果是完全相同的。
2 反事實值
概念
\[v_{p}^{\sigma}(h)=\sum_{z \in Z, h \sqsubset z} \pi_{-p}^{\sigma}(h) \pi^{\sigma}(z \mid h) u_{p}(z) \]看這個式子的定義:
右端第一項\(\pi_{-p}^{\sigma}(h)\) 表示 其他玩家\(-p\)選擇策略\(\sigma\) 從起點到達中間結點\(h\)的概率 ;
第二項\(\pi^{\sigma}(z \mid h)\) 表示路徑 經過中間結點\(h\),然後根據策略\(\sigma\)到達最終結點\(z\)的概率 ,
右端第三項 表示 玩家\(p\)在最終結點\(z\)的收益 , 然後對所有經過中間結點\(h\)到達最終結點\(z\)的路徑進行求和。
結合第2小節中關於\(π\)概率的三個等式,我們可以很容易地推匯出狀態\(h\)的收益值與反事實值之間的關係:
\[\begin{aligned} u_{p}^{\sigma}(h)&=\Sigma_{z \in Z, h \sqsubset z} \pi^{\sigma}(z) u_{p}(z) \\ &=\Sigma_{z \in Z, h \sqsubset z} \pi^{\sigma}(h) \pi^{\sigma}(z \mid h) u_{p}(z) \\ & =\Sigma_{z \in Z, h \sqsubset z} \pi_{p}^{\sigma}(h) \pi_{-p}^{\sigma}(h) \pi^{\sigma}(z \mid h) u_{p}(z) \\ & =\pi_{p}^{\sigma}(h) \Sigma_{z \in Z, h \sqsubset z} \pi_{-p}^{\sigma}(h) \pi^{\sigma}(z \mid h) u_{p}(z) \\ & =\pi_{p}^{\sigma}(h) v_{p}^{\sigma}(h) \\ \end{aligned} \]玩家p在結點\(h\)的期望收益既與其他玩家\(-p\)的策略\(\pi_{-p}^{\sigma}(h)\)和到終點玩家\(p\)的收益\(u_{p}(z)\),又和玩家p的策略\(\pi_{p}^{\sigma}(h)\)有關。
當終點收益和其他玩家的策略等其他因素是一定的時候,玩家\(p\)在結點\(h\)的期望收益就只與玩家\(p\)的策略有關,這時候把除玩家\(p\)的策略以外的因素(其他玩家的策略和收益的乘積),即不考慮玩家\(p\)的策略影響下玩家\(p\)在結點\(h31\)收益期望 稱之為反事實值。
當除玩家\(p\)的策略以外的因素固定的情況下,玩家\(p\)在結點\(h\)的期望收益就只取決於玩家\(p\)的策略,當玩家選定自己的策略想要到達這個狀態時候,玩家可以獲得一個在這個狀態的期望收益,如果玩家\(p\)特別想要到達這個狀態,這時候\(\pi_{p}^{\sigma}(h)=1\),這個時候有兩個含義,一當結點\(h\)實在玩家選擇動作之前的結點,其含義為是玩家\(p\)的策略不影響這個中間狀態期望的收益,二當結點\(h\)實在玩家選擇動作之前的結點,其含義為玩家選擇策略\(\sigma\),想要盡力促成這個結果,獲得一個在結點\(h\)的收益。
當\(\pi_{p}^{\sigma}(h)=0\)的時候,這時只有結點\(h\)在玩家\(p\)之後才有這個情況,這個時候玩家採取策略\(\sigma\)(動作分佈為\([0,a,\cdots,z]\))的目的是來儘量避免到達中間結點\(h\)。
反事實值實際上就反映了不考慮玩家\(p\)採取策略\(\sigma\)對到達中間結點\(h\)的影響的時候,事實上玩家\(p\)的期望收益。
同樣的,將概念擴充套件到資訊集上有 the counterfactual value for player $p $ of an information set \(I \in \mathcal{I}_p\) is
\[v_{p}^{\sigma}(I)=\sum_{h \in I} v_{p}^{\sigma}(h) \\ v_{p}^{\sigma}(I \cdot a)=\sum_{h \in I \cdot a} v_{p}^{\sigma}(h) \]算例
同樣給出第二個問題作為一個計算的例子:
玩家\(p2\)(為玩家2),其策略\(\sigma\)為\([a,b,1-a-b]\) ,其他玩家\(-p\)(也就是玩家1)的策略\(\sigma\)為\([0.4,0.3,0.3]\),博弈樹見上。
根據上述定義,我們來嘗試計算博弈樹中間結點\(h1\)的收益
首先,包含中間結點\(h1\),從遊戲開始到達最終結果\(z1,z2,z3\)的路徑總共3條,
根據定義式
右端第一項\(\pi_{-p}^{\sigma}(h)\) 表示 其他玩家\(-p\)(也就是玩家1)選擇策略\(\sigma\) 從起點到達中間結點\(h1\)的概率 :
\[\pi_{-p}^{\sigma}(h1) = 0.4 \]第二項\(\pi^{\sigma}(z \mid h)\) 表示路徑 經過中間結點\(h\),然後根據策略\(\sigma\)到達最終結點\(z\)的概率 ;右端第三項 表示 玩家\(p\)(玩家2)在最終結點\(z\)的收益 ;兩者相乘表示經過中間結點的收益
\[u_p(z1)=0 , u_p(z2)= 1, u_p(z3)= -1 \\ \pi^{\sigma}(z1 \mid h1) = a,\pi^{\sigma}(z2 \mid h1) =b ,\pi^{\sigma}(z3 \mid h1) = 1-a-b \]
相乘求和就得到了中間結點\(h1\)的反事實值
\[\begin{aligned} v_{p2}^{\sigma}(h1) &=\sum_{z \in Z, h1 \sqsubset z} \pi_{-p}^{\sigma}(h1) \pi^{\sigma}(z \mid h1) u_{p2}(z) \\ &= 0.4a * 0 + 0.4 b* 1+0.4(1-a-b) *-1 \\ &= 0.4a+0.8b-0.4 \end{aligned} \]這裡計算出來的反事實值與前面計算出來的收益值相等,而兩者其實是有如下關係的
\[u_{p}^{\sigma}(h) =\pi_{p}^{\sigma}(h) v_{p}^{\sigma}(h) \\ \]在這裡也就是
\[u_{p2}^{\sigma}(h1) =\pi_{p2}^{\sigma}(h1) v_{p2}^{\sigma}(h1) \]根據我們的計算又有
\[u_{p2}^{\sigma}(h1) = v_{p2}^{\sigma}(h1) \]所以唯一的解釋就是
\[\pi_{p2}^{\sigma}(h1) =1 \]這裡怎麼來理解呢:
玩家\(p2\)選擇策略\(\sigma\)到達中間結點\(h1\)的概率為1,也就是到達中間結點\(h1\)和玩家\(p2\)的策略無關。這是因為結點\(h1\)是在玩家\(p2\)採取行動之前的結點,所以玩家採取的策略不影響這個結點的期望收益。
只有當玩家\(p\)的策略選擇影響到後續中間結點\(h\)的時候,玩家\(p\)在中間結點\(h\)的收益和玩家\(p\)在中間結點\(h\)的反事實值會有差別,差別就是玩家選擇的策略\(\pi^\sigma_p(h)\)(動作概率),選擇該動作的概率越小,反事實值越大。下面給出一個示例進行說明.。
博弈樹如上圖所示,有三個參與者:玩家1,玩家2,玩家3 ,博弈的過程為玩家1,玩家2,玩家3依次行動。
玩家1有三個動作\([0,1,2]\),其策略(動作概率)為\([0.4,0.3,0.3]\)。玩家2有兩個動作,其策略為\([a,1-a]\)。玩家3有兩個動作,其策略為\([b,1-b]\)。
可以參照上面的過程來計算玩家2在結點\(h31\)的收益\(u^\sigma_{p2}(h31)\)和反事實值\(v^\sigma_{p2}(h31)\)。
收益計算:把所有從遊戲起點經過中間結點\(h31\)的路徑的概率乘以收益求和
\[\begin{aligned} u^\sigma_{p2}(h31) &= \sum_{z1,z2} \pi^{\sigma}(z) u_{p2}(z) \\ & = (0.4 \cdot a \cdot b) *r1 +(0.4 \cdot a \cdot 1-b) *r2 \\ \end{aligned} \]反事實值計算:除玩家p2以外的人遵循策略到達中間結點\(h31\)的概率 乘以 從中間結點\(h31\)到結果\(z1,z2\)的不同路徑的分佈及收益
\[\begin{aligned} v_{p2}^{\sigma}(h31) &= \sum_{z1,z2} \pi_{-p}^{\sigma}(h31) \pi^{\sigma}(z \mid h31) u_{p2}(z) \\ &= 0.4 *b* r1 + 0.4 *1-b * r2 \\ \end{aligned} \]兩者的差別就是
\[\pi_{p2}^{\sigma}(h31) = a \\ u^\sigma_{p2}(h31) = a * v_{p2}^{\sigma}(h31) \]當其他玩家的策略和結點收益是既定的時候,後面這一項是事實既定,它不隨玩家\(p2\)的策略改變。
當\(p2\)想要到達結點\(h2\)時,它可以提高選擇動作\(0\)的比重,即當策略為\([1,0]\)時,玩家\(p2\)在結點\(p2\)的收益為\(1 * v_{p2}^{\sigma}(h31)\),
當\(p2\)不想要到達結點\(h2\)時,它可以降低選擇動作\(0\)的比重,即當策略為\([0,1]\)時,玩家\(p2\)在結點\(p2\)的收益為\(0\),這個時候也就是由於玩家\(p2\)的策略選擇,結點\(h2\)是永遠不可能到達的,即這個結點事實上是不存在的。
當玩家\(p2\)的策略和結點的收益是固定的時候,其他玩家的策略選擇就決定了玩家\(p2\)在結點\(h31\)的收益。這時侯,反事實值越大,反映其他玩家通過選擇策略,想要到達這個結點。反事實值越小,反映其他玩家選擇策略,想要儘量避免到達這個結點,其他玩家可以調整策略使得$v_{p2}^{\sigma}(h31) =0 \(,這個時候結點\)h31$就是不存在的。
當玩家2選擇動作0的概率a越大,玩家2在中間結點\(h31\)獲得的期望獎勵值就越大。因為只有到達結點\(h31\)才會有中間這個結點的獎勵,如果不到達結點\(h31\)(此時,動作a的概率為0),那麼自然玩家2在結點\(h31\)就不會有收益。
玩家p2在結點\(h31\)的期望收益既與其他玩家的策略和終點收益,又和玩家p2的策略有關。當其他因素是一定的時候,就只與玩家p2的策略有關,把從其他玩家的策略和收益的乘積即不考慮\(p2\)的策略影響下結點\(h31\)收益期望 稱之為反事實值。
把結點收益固定,那麼玩家\(p2\)選擇動作0的概率(也就是玩家2的策略)會影響反事實值的大小。
\(a\)越大,反事實值越小。如果\(a\)是1,此時反事實值和收益相等,就說明此時玩家2的動作是固定的或者玩家2的策略不影響狀態\(h2\)出現的概率,這個時候說明玩家2採取策略\(\sigma=[1,0]\)一定能夠到達狀態\(h31\)。
\(a\)越小,反事實值越大。如果\(a\)是0.0001,此時反事實值比上述情況大得多,說明當玩家2採取策略\(\sigma=[0.0001,0.9999]\)時,能夠到達狀態\(h31\)的可能性很小。
反事實值\(v_{p2}^{\sigma}(h31)\)就說明了玩家\(p2\)選擇策略\(\sigma\)對到達狀態\(h31\)的可能性,反事實值越大,說明在玩家2使用策略\(\sigma\)時越不可能到達狀態\(h31\) ,當反事實值與收益相等的時候就說明玩家\(p2\)選擇策略\(\sigma\)不影響到達狀態\(h31\)的可能性或者所選擇的策略能夠一定到達狀態\(h31\)。
3 反事實遺憾
概念
\[R^{T}(I, a)=\sum_{t=1}^{T} v_{p}^{\sigma^{t}}(I \cdot a)-\sum_{t=1}^{T} \sum_{a \in A(I)} \sigma^{t}(I, a) v_{p}^{\sigma^{t}}(I, a) \]其定義是基於某個資訊集\(I\)和在這個資訊集上的特定動作來定義的。
右端後面一項,是對在該資訊集上動作期望遺憾值的累和,右端第一項選取該動作的遺憾值。
算例
同樣給出第二個問題作為一個計算的例子:
玩家\(p2\)(為玩家2),其策略\(\sigma\)為\([a,b,1-a-b]\) ,其他玩家\(-p\)(也就是玩家1)的策略\(\sigma\)為\([0.4,0.3,0.3]\),博弈樹見上。
根據上述定義,我們來嘗試計算博弈樹在第一次迭代時候,玩家\(p2\)在資訊集\(I=\{h1,h2,h3\}\)採取動作\(a=0\)的反事實遺憾\(R^1(I,a)\)
由於是第一次迭代,沒有歷史資訊
\[R^1(I,0) =R^0(I,0)+ v_{p2}^{\sigma^{t1}}(I \cdot 0) - \sum_{a \in [0,1,2]} \sigma^{t}(I, a) v_{p}^{\sigma^{t}}(I, a) \\ R^0(I,0) = 0(因為是第一次迭代,所以累計值為0) \]首先計算反事實收益
\[\begin{aligned} v_{p}^{\sigma^{t}}(I, a) &=\sum_{h \in I \cdot a} v_{p}^{\sigma^{t}}(h) \\ &=\sum_{h \in I \cdot a} \sum_{z \in Z, h \sqsubset z} \pi_{-p}^{\sigma^{t}}(h) \pi^{\sigma^{t}}(z \mid h) u_{p}(z) \end{aligned} \] \[\begin{aligned} v_{p2}^{\sigma^{t}}(I, 0) &= v_{p2}^{\sigma^{t}}(h1 , 0)+v_{p2}^{\sigma^{t}}(h2 , 0)+v_{p2}^{\sigma^{t}}(h3 , 0) \\ &=[\pi_{-p}(h1,0)\pi(z1|h1,0)u_p(z1)]+ [\pi_{-p}(h2,0)\pi(z4|h2,0)u_p(z4)] +[\pi_{-p}(h3,0)\pi(z7|h3,0)u_p(z7)] \\ &=0.4 * 1* 0+ 0.3 *1 *-1 +0.3*1*1 \end{aligned} \]同樣還可以得到
\[\begin{aligned} v_{p2}^{\sigma^{t}}(I, 1) &= v_{p2}^{\sigma^{t}}(h1 , 1)+v_{p2}^{\sigma^{t}}(h2 , 1)+v_{p2}^{\sigma^{t}}(h3 \cdot 1) \\ &=[\pi_{-p}(h1,1)\pi(z2|h1,0)u_p(z2)]+ [\pi_{-p}(h2,1)\pi(z4|h2,a)u_p(z4)] +[\pi_{-p}(h3,1)\pi(z8|h3,1)u_p(z8)] \\ &=0.4 * 1* 1+ 0.3 *1 *0 +0.3*1*-1 \end{aligned} \] \[\begin{aligned} v_{p2}^{\sigma^{t}}(I, 2) &= v_{p2}^{\sigma^{t}}(h1 , 2)+v_{p2}^{\sigma^{t}}(h2 , 2)+v_{p2}^{\sigma^{t}}(h3 \cdot 2) \\ &=[\pi_{-p}(h1,2)\pi(z3|h1,2)u_p(z3)]+ [\pi_{-p}(h2)\pi(z6|h2,2)u_p(z6)] +[\pi_{-p}(h3)\pi(z9|h3,2)u_p(z9)] \\ &=0.4 * 1*- 1+ 0.3 *1 *1 +0.3*1*0 \end{aligned} \]在資訊集\(I\)上選擇動作\(0,1,2\)的概率分別為
\[\sigma^1(I,0) = a\\ \sigma^1(I,1) = b\\ \sigma^1(I,2) = 1-a-b\\ \]因此在資訊集\(I\)上的期望反事實值為
\[\begin{aligned} &\sum_{a \in [0,1,2]} \sigma^{t}(I, a) v_{p}^{\sigma^{t}}(I, a) \\ &= 0 * a + 0.1 *b + (-0.1) *(1-a-b) \\ &=0.1a+0.2b-0.1 \end{aligned} \]經過上述計算我們會發現此時計算出來的玩家\(p2\)在資訊集\(I\)上的期望反事實值和在第一部分計算出來的資訊集\(I\)上玩家\(p2\)的期望收益是一樣的。其原因就是資訊集\(I\)的出現並不依賴於玩家\(p2\)的動作。
反事實遺憾為
\[R^1(I,0) =R^0(I,0)+ v_{p2}^{\sigma^{t1}}(I \cdot 0) - \sum_{a \in [0,1,2]} \sigma^{t}(I, a) v_{p}^{\sigma^{t}}(I, a) \\ = 0 +0 - (0.1a+0.2b-0.1) \]因此。第一次迭代時,玩家\(p2\)在資訊集\(I\)上採取動作\(0\)的反事實遺憾為\(- (0.1a+0.2b-0.1)\)
4 原始CFR演算法
演算法步驟:
-
Generate strategy profile σt from the regrets, as described above.
根據regret-matching演算法計算本次博弈的策略組
For all $ I \in \mathcal{I}, a \in A(I) $, and $ p=p(I) $:
\[\sigma_{p}^{t}(I, a)=\left\{\begin{array}{ll}R^{t}(I, a)^{+} / \sum_{b \in A(I)} R^{t}(I, b)^{+} & \sum_{b \in A(I)} R^{t}(I, b)^{+}>0 \\ \frac{1}{|A(I)|} & \text { otherwise }\end{array}\right. \]因為動作\(a\)的遺憾值為正表示該動作正確,在下次迭代中無需更改,體現了遺憾匹配演算法“有錯就改,無錯不改”的特點。
其中如果所有動作的遺憾值為0,則在下次迭代中採取每一種動作的概率相同。
-
Update the average strategy profile to include the new strategy profile.
使用上一步中新計算的策略組更新平均策略組
For all $ I \in \mathcal{I}, a \in A(I) $, and $ p=p(I) $:
\[\begin{aligned} \bar{\sigma}_{p}^{t}(I, a) &=\frac{1}{t} \sum_{t^{\prime}=1}^{t} \pi_{p}^{\sigma^{t}}(I) \sigma_{p}^{t^{\prime}}(I, a) \\ &=\frac{t-1}{t} \bar{\sigma}_{p}^{t-1}(I, a)+\frac{1}{t} \pi_{p}^{\sigma^{t}}(I) \sigma_{p}^{t}(I, a) \end{aligned} \]上式表示玩家\(p\)的平均策略\(\bar{\sigma}_{p}^{t}(I, a)\),即為前\(t\)次的即時策略的平均值
-
Using the new strategy profile, compute counterfactual values.
使用第一步計算的新策略組計算雙方參與者的反事實收益值
For all $ I \in \mathcal{I}, a \in A(I) $, and $ p=p(I) $:
\[\begin{aligned} v_{p}^{\sigma^{t}}(I, a) &=\sum_{h \in I \cdot a} v_{p}^{\sigma^{t}}(h) \\ &=\sum_{h \in I \cdot a} \sum_{z \in Z, h \sqsubset z} \pi_{-p}^{\sigma^{t}}(h) \pi^{\sigma^{t}}(z \mid h) u_{p}(z) \end{aligned} \] -
Update the regrets using the new counterfactual values.
使用反事實收益值更新遺憾值
For all $ I \in \mathcal{I}, a \in A(I) $, and $ p=p(I) $:
\[R^{t}(I, a)=R^{t-1}(I, a)+v_{p}^{\sigma^{t}}(I, a)-\sum_{a \in A(I)} \sigma^{t}(I, a) v_{p}^{\sigma^{t}}(I, a) \]
-
對於每一位玩家,初始化反事實遺憾值\(R^t(I,a)\)為0 平均策略\(\bar{\sigma}_p(I,a)\)為0 ,初始化策略為隨機策略
-
for from 1 to T(T:迭代次數):
a) 根據regret-matching演算法計算本次博弈的策略組\(\sigma_p^t(I,a)\)
a)使用當前策略更新平均策略\(\bar{\sigma}^t_p(I,a)\)
c)計算反事實收益值\(v^{\sigma^t}_p(I,a)\)
d) 使用反事實收益值計算遺憾值\(R^t(I,a)\)
-
返回平均策略(累積後悔值/迭代次數)
虛擬碼:
演算法分析:
通過上述演算法步驟我們可以得到:
對於每個資訊集\(I\)和動作\(a\) , \(R\)和\(\bar{\sigma}\)都相當於一個歷史列表,儲存了過去迭代過程中的累計遺憾值和累計平均策略 。 \(\sigma\)和\(v\)是臨時列表,用來儲存當前的策略和反事實值。
值得注意的是,雖然 CFR 處理的都是行為策略(即在每個資訊集上動作的概率分佈),但求平均策略的過程,是在混合策略或序列形式策略的空間中進行的。使用序列形式進行描述, 維持一個玩家\(p\)的平均策略, 是通過在每個資訊集\(I \in \mathcal{I}\)和動作\(a \in A(I)\)上 增量地更新$ \bar{\sigma}{p}(I, a)=\sum{t=1}^{T} \pi_{p}^{t}(I) \sigma^{t}(I, a) $完成的。這裡,我們忽略了上面給出的演算法步驟第二種把和轉化為平均的形式,這是因為在將序列形式的策略轉化為行為形式的策略 其實是涉及到了 在每個資訊集上的概率的正則化。
通過在博弈樹的狀態深度優先遍歷中結合策略計算、平均策略更新和價值計算,可以提高 CFR 的實現效率。演算法在下一部分
程式碼實現
# -*- coding: utf-8 -*-
"""
@author : zuti
@software : PyCharm
@file : rockpaperscissors_cfr_1.py
@time : 2022/11/24 15:51
@desc :
嘗試使用CFR演算法來實現剪刀石頭布遊戲
第一次嘗試,使用演算法流程進行
"""
import numpy as np
"""遊戲設定"""
# 動作設定
NUM_ACTIONS = 3 # 可選的動作數量
actions = [0, 1, 2] # 0代表剪刀scissors , 1代表石頭rock ,2 代表布 paper
actions_print = ['剪刀', '石頭', '布']
# 動作的收益 ,兩個人進行博弈,結果
utility_matrix = np.array([
[0, 1, -1],
[-1, 0, 1],
[1, -1, 0]
])
""" 遊戲基本情況"""
# 玩家1 策略固定 [0.4,0.3,0.3]
# 玩家2,初始化策略為隨機策略[1/3,1/3,1/3],的目的是通過CFR演算法,學習得到一個能夠獲得最大收益的策略
# 整個遊戲只有一個資訊集,其中包含三個結點,在這個資訊集合上可選的動作有3個
# 玩家,初始化
# 策略
player1_strategy = np.array([0.4, 0.3, 0.3])
player2_strategy = np.array([1 / 3, 1 / 3, 1 / 3])
# 玩家2在資訊集I上關於三個動作的累計的遺憾值
player2_regret_Information = np.zeros(NUM_ACTIONS)
# 玩家2在資訊集I上關於三個動作的累計的平均策略
player2_average_strategy = np.zeros(NUM_ACTIONS)
def RegretToStrategy(regret):
"""
使用遺憾值匹配演算法 ,根據累計的遺憾值,來確定新的策略
:return: 新的策略 strategy
"""
# 歸一化方法: 1 只看遺憾值大於0的部分,然後計算分佈
regret_normalisation = np.clip(regret, a_min=0, a_max=None)
#print(f'歸一化後的累計遺憾值 {regret_normalisation[0]}; {regret_normalisation[1]}; {regret_normalisation[2]} ')
"""根據歸一化後的遺憾值產生新的策略"""
regret_normalisation_sum = np.sum(regret_normalisation) # 求和
strategy = np.zeros( NUM_ACTIONS)
if regret_normalisation_sum > 0:
strategy = regret_normalisation / regret_normalisation_sum
else:
strategy = np.array([1 / 3, 1 / 3, 1 / 3]) # 否則就採取平均策略
return strategy
def UpdateAverage(strategy , average_strategy ,count ):
"""
根據本次計算出來的策略,更新平均策略
進行歷史累計,然後對迭代次數進行平均
:param strategy:
:param average_strategy:
:return:
"""
average_strategy_new = np.zeros( NUM_ACTIONS)
#不管玩家p2選擇哪個動作,資訊集I 的出現概率為 1
for i in range(NUM_ACTIONS):
average_strategy_new[i] = (count -1) / count * average_strategy[i] + 1/count * 1 * strategy[i]
return average_strategy_new
def StrategyToValues(strategy):
"""
計算反事實收益值 v
:param strategy:
:return:
"""
#首先計算資訊集I上所有動作的反事實收益 ,見第三節算例
#計算每個動作的反事實收益
counterfactual_value_action = np.zeros(NUM_ACTIONS)
for i in range(NUM_ACTIONS) :
counterfactual_h1 = player1_strategy[0] * 1 * utility_matrix[0][i]
counterfactual_h2 = player1_strategy[1] * 1 * utility_matrix[1][i]
counterfactual_h3 = player1_strategy[2] * 1 * utility_matrix[2][i]
counterfactual_value_action[i] = counterfactual_h1 + counterfactual_h2 +counterfactual_h3
return counterfactual_value_action
def UpdateRegret( regret , strategy , counterfactual_value_action):
"""
更新累計反事實遺憾
:param regret:
:param strategy:
:param counterfactual_value_action:
:return:
"""
# 每個動作的反事實值 乘以 策略(每一個動作的概率) 求和 得到 期望
counterfactual_value_expect = np.sum(counterfactual_value_action * strategy)
for i in range(NUM_ACTIONS):
regret[i] = regret[i] + counterfactual_value_action[i] - counterfactual_value_expect
return regret
def NormaliseAverage(average_strategy):
"""
歸一化得到最後結果
:param average_strategy:
:return:
"""
strategy_sum = sum(average_strategy)
strategy = np.zeros(NUM_ACTIONS)
for i in range( NUM_ACTIONS):
strategy[i] = average_strategy[i] / strategy_sum
return strategy
#使用CFR求
for count in range(10):
print(f'玩家2 當前策略 :{player2_strategy}')
#2 根據當前策略,更新平均策略
player2_average_strategy = UpdateAverage(player2_strategy , player2_average_strategy ,count+1 )
print(f'累計平均策略 :{player2_average_strategy}')
# 3 根據當前策略計算反事實收益
player2_counterfactual_value_action = StrategyToValues(player2_strategy)
print(f'當前策略對應的反事實收益 :{player2_counterfactual_value_action}')
#4 更新累計反事實遺憾
player2_regret_Information = UpdateRegret(player2_regret_Information, player2_strategy, player2_counterfactual_value_action)
print(f'累計反事實遺憾 :{player2_regret_Information}')
# 1 用遺憾值匹配演算法 ,根據累計的遺憾值,來確定新的策略
player2_strategy = RegretToStrategy(player2_regret_Information)
print(f'-------------迭代次數{count+1}------------')
result = NormaliseAverage(player2_average_strategy)
print(f'最終結果:{result}')