用Kuhn Poker将反事实的遗憾降至最低

2020-05-26 04:53:27

如果您有意见,请在Twitter(@vpj)上找到我,或者在Github repo中打开一个问题。

库恩扑克是一种双人3牌投注游戏,玩家从王牌、国王牌和皇后牌中各拿到一张牌(没有花色),每包只有三张牌,所以少了一张牌。王牌击败国王和王后,国王击败皇后-就像正常的纸牌排名一样。

两个玩家都下注$S$筹码(盲目押注$S$筹码),看过牌后,第一个玩家可以通过或下注$1$筹码。

如果第一个玩家下注,第二个玩家可以下注(即叫牌)$1$筹码或传球(即折叠)。如果第二个玩家下注,且牌较高的玩家赢得赌注。

这个游戏是重复进行的,一个好的策略将优化长期效用(或赢利)。

KAP-玩家1得到K,玩家2获得A。玩家1传球。2号玩家没有下注机会,2号玩家赢得了2美元筹码。

QKBP-1号玩家得到Q号,2号玩家得到K号,1号玩家下注一个筹码。玩家2传球(折叠)。1号博弈者得到$2S+1$的赌注,因为2号博弈者放弃了。

QAbb-1号玩家得到Q,2号玩家得到A,1号玩家下注一个筹码。2号玩家也会下注(打电话)。2号玩家赢得2美元+2美元的赌注。

根据纳什定理,该博弈存在纳什均衡,即存在两个参与者的组合策略,使得两个参与者都不能通过改变她的策略来增加效用。

如果第一个玩家因为1.和2而得了K,那么他只有在下注的情况下才会输。因此,如果第一名选手得了K,他就应该及格。

如果第一个玩家有一个Q,他有时应该虚张声势并下注。比方说,如果第一个玩家得到了Q,他可能用$p_1$下注。

类似地,如果第二个玩家得到K,他将以概率$p_2$下注(如果第一个玩家确实下注了)。

那么第一个牌手(AK,AQ,KA,KQ,QA,QK)的总效用是。

$$\BEGIN{ALIGN}U_1=\frac{1}{6}\BIG[&;+(1+S)p_2+S(1-p_2)\Label{Ak}\Tag{Ak}\\&;+S\Label{aq}\Tag{Aq}\\&;-S\Label{Ka}\Tag{Ka}\\&;+S\Label{Kq}\Tag{Kq}\\&;-(1+S)p_1-S(1-p_1)\Label{qa}\Tag{QA}\\&;-(1+S)p_1 p_2+S p_1(1-p_2)-S(1-p_1)\BIG]\Label{qk}\tag{qk}\\=\frac{1}{6}\BIG[&;+S+p_2\\&;+S\\&;-S-p_1\\&;-p_1 p_2-2 S p_1 p_2+2 S p_1-S\BIG]\\=\frac{1}{6}\BIG[&;+p_2-p_1-p_1 p_2-2 S p_2+2 S p_1\BIG]\\=\frac{1}{6}\BIG[&;+p_1(2 S-1-p_2-2 S p_2)+p_2\BIG]\Label{a}\Tag{1}\\=\frac{1}{6}\BIG[&;+p_2(1-p_1-2 Sp_1)-p_1+2 S p_1\BIG]\Label{b}\Tag{2}\End{Align}$$。

从$(2)$,$1-p_1-2 S p_1\gtrless 0\Equiv\frac{1}{1+2 S}\gtrless p_1$。

案例1:如果$\frac{1}{1+2S}<;p_1$,第二个玩家将选择$p_2=1$,第一个玩家的最大效用为$\frac{1}{6}[1-2 p_1]=\frac{1}{6}\frac{2 S-1}{1+2 S}$

案例2:如果$\frac{1}{1+2 S}>;p_1$,第二个玩家将选择$p_2=0$,第一个玩家的最大效用为$\frac{1}{6}p_1(2 S-1)=\frac{1}{6}\frac{2 S-1}{1+2 S}$。

案例3:如果$\frac{1}{1+2 S}=p_1$,则该实用程序将独立于$p_2$,第一个玩家的最大效用将为$\frac{1}{6}p_1(2 S-1)=\frac{1}{6}\frac{2 S-1}{1+2 S}$。

在这三种情况下,第一个玩家的最佳策略都是$p_1=\frac{1}{1+2S}$。

类似于$(1)$,第二方的最佳策略是$p_2=\frac{2S-1}{1+2S}$。

在这一点上,任何一方都不能通过改变策略来增加自己的效用,因此这是纳什均衡。

从键入导入列表,newtype,dict,cast将Altair导入为alt导入Numpy作为NP从labml导入手电筒从labml导入分析从labml导入记录器,实验,monit,跟踪器从labml导入文本.logger从matplotlib导入pylot作为PLT。

$S$,Blinds,是每位玩家在看牌前需要下注的筹码数量。

P_1,P_2=NP。网格(Range(0,100,5),Range(0,100,5))p_1,p_2=p_1/100,p_2/100p_1=火炬。张量(p_1,Requires_grad=True)p_2=Torch。张量(p_2,Requires_grad=True)u_1=1/6*(p_2-p_1-p_1*p_2-2*百叶窗*p_1*p_2+2*百叶窗*p_1)

def PLOT_Matrix():x=p_1。分离()。Numpy()。Ravel()#*100 y=p_2。分离()。Numpy()。RAVEL()#*100 z=u_1。分离()。Numpy()。RAVEL()data=[]for i in range(len(X)):data。APPEND({';p1';:x[i],';p2';:y[i],';U1';:Z[i]})source=alt。DATA(VALUES=DATA)返回ALT。图表(来源)。mark_rect()。编码(x=';p1:n';,y=alt.。y(';p2:N';,Sort=';降序';),color=';U1:q&39;)Plot_Matrix()。

def lot_grads():U_1。SUM()。向后(RETAIN_GRAPH=TRUE)p_1_grad=p_1。格拉德。克隆()p_1。格拉德。零p2。格拉德。ZERO_()(-u_1)。SUM()。向后(RETAIN_GRAPH=TRUE)p_2_grad=p_2。格拉德。克隆()p_1。格拉德。零p2。格拉德。ZERO_()_,AX=PLT。子图(FigSize=(9,9))箭图=AX。箭筒(p_1。DETACH(),p_2。DETACH(),p_1_grad,p_2_grad)plt.。show()Plot_grads()。

当$S=1$时,纳什均衡位于$(0.33,0.33)$附近,梯度似乎围绕着它。

反事实后悔最小化利用后悔匹配来寻找最小化当前遗憾的策略,在大量迭代中的平均策略将收敛到纳什均衡。

$a$,动作是玩家或凭运气进行的动作。这要么是打赌(p代表传球,b代表下注),要么是发出的牌(A,K,Q)。

$h$,History,是当前游戏的历史,也就是玩家发出的牌和采取的动作。我们表示$h$是一个字符串,其中前两个字母是发给两个玩家的牌,其余是下注动作。

KA-玩家1得到K,玩家2得到A,这是玩家1采取行动的机会。

KAP-玩家1得到K,玩家2获得A。玩家1传球。游戏结束了。这是一份终结性的历史记录。

KAB-博弈者1得到K,博弈者2得到A,博弈者1下注筹码。这是玩家采取行动的机会。

KAbp-博弈者1得到K,博弈者2得到A,博弈者1下注筹码。2号玩家传球。游戏结束了。这是一份终结性的历史记录。

KABB-博弈者1得到K,博弈者2得到A,博弈者1下注筹码。2号玩家通过下注筹码来叫牌。游戏结束了。这是一份终结性的历史记录。

$i$,InfoSet`是一个信息集。它代表玩家可用的信息。这类似于代理的状态。在库恩扑克的情况下,它是他们手中的卡片和投注信息。

类信息集:遗憾:DICT[Action,Float]策略:DICT[Action,Float]Cumulative_Strategy:DICT[Action,Float]Average_Strategy:DICT[Action,Float]def__init__(self,key:str):Self。KEY=KEY SELF。后悔={a:0表示操作中(自我)}n_操作=len(操作(自我))自我。策略={a:1/n_Actions for a In Actions(Self)}Self。Cumulative_Strategy={a:0,用于In Actions(Self)}自身。Average_Strategy={a:0 for a in Actions(Self)}def__repr__(Self):return repr(sel.。策略)INFO_SETS:dict[字符串,信息集]={}。

KA-Player 1是当前玩家。他可以看到他的卡K,所以信息集是K。

KAB-玩家2是当前玩家。他可以看到他的卡a和投注历史b,所以信息集是Ab。

def info_set(h:历史记录)->;InfoSet:i=Player(H)Visible=h[i]+h[2:]如果不可见,则不在INFO_SETS中:INFO_SETS[Visible]=InfoSet(Visible)返回INFO_SETS[Visible]

$A(I)$,Actions,给出玩家在由$i$表示的当前状态下可以采取的动作集。

函数IS_CHANGE检查下一个动作是否是机会动作,而SAMPLE_CHANGE对某个机会动作的动作进行采样。这是用来发牌的。

定义为_Chance(h:历史)->;bool:Return len(H)<;2 def Sample_Chance(h:历史)->;操作:While True:R=NP。随机的。Randint(len(Chance))Chance=h中c的机会[r]:if c==Chance:Chance=None Break If Chance不是None:return cast(Action,Chance)。

$\sigma_i^t(I)$,策略是玩家$i$在信息集合$i$的训练迭代$t$时的策略。它给出了A(I)$中动作$a\的概率。

$u_i(Z)$,TERMINAL_UTILITY是玩家$i$在终端游戏历史记录$z$的效用。终端游戏历史记录是已经完成的游戏的历史记录。如果给定的历史记录是终端,则返回IS_TERMINAL,并且。

def is_Terminal(h:历史):if len(H)<;=2:如果h[-1]==';p';:如果h[-2:]==';则返回True如果h[-2:]==';bb';:返回True返回False def Terminal_Utility(h:History,i:Player)->;Float:Winner=-1+2*(h[0]<;h[1])Utility=0 if h[-2:]==';BP';:Utility=Blinds elif h[-2:]==';bb&39;:Utility=Winner*(1+Blinds)Elif h[-1]==';p';:Utility=Winner*Blinds if i==PLALES[0]:Return Utility ELSE:Return-Utility。

博弈者$i$的预期效用为$h$,而博弈者的策略为$\sigma$,

$$u_i(\sigma,h)=\sum_{z\in Z,h\sqset z}\pi^\sigma(h,z)u_i(Z)$\pi^\sigma(h,z)$是具有$\sigma$策略的玩家从$h$到达$z$的概率。

如果$u_i(\sigma_{i\to a},h)$总是在当前信息集中执行操作$a$,则$u_i是实用程序。

$$\BEGIN{ALIGN}u_i(\sigma_{i\to a},h)&;=u_i(\sigma,ha)\\u_i(\sigma,h)&;=\sum_{a\in A(I)}\sigma(i,a)u_i(\sigma_{i\to a},h)\end{Align}$$。

$\pi_{-i}^\sigma(H)$是如果玩家$i$采取了所有导致$h$的动作,概率为$1$,则达到$h$的概率。

历史$h$没有采取行动的反事实遗憾是$$r_i(h,a)=v_i(\sigma_{i\to a},h)-v_i(\sigma,h)$$。

在信息集合$i$处未采取行动的反事实遗憾为$$r_i(i,a)=\sum_{h\in i}r_i(h,a)$$。

直到$T$的所有迭代的累积反事实遗憾是,$$R_i^T(i,a)=\sum_{t=1}^T r_i^t(i,a)$$。

def UPDATE_REGREADS(i:infoSet,v:Float,va:dict[Action,Float],i:Player):对于操作中的(I):遗憾(I)[a]=遗憾(I)[a]+(va[a]-v)。

策略计算采用HART和Mas-Collell的遗憾匹配。$$\BEGIN{ALIGN}R_i^{T,+}(i,a)&;=\max(R_i^T(i,a),0)\\sigma_i^{T+1}(i,a)&;=\Begin{Cases}\frac{R_i^{T,+}(i,a)}{\sum_{a';\in A(I)}R_i^{T,+}(i,a;)},&;\text{if$\sum_{a;\in A(I)}R_i^{T,+}(i,a;)>;0$}\frac{1}{|A(I)|}&;\text{否则}\end{case}\end{ign}$

定义CALCULATE_NEW_STARTICAL(i:InfoSet):r_plus={a:max(r,0),表示遗憾(I)中的a,r。Items()}r_plus_sum=sum(r表示r_plus中的r。值())n_action=len(action(I)),用于操作(I)中的a:if r_plus_sum>;0:Strategy(I)[a]=r_plus[a]/r_plus_sum Else:Strategy(I)[a]=1/n_action。

玩家$I$从迭代到$T$的平均策略是,$$\bar{\sigma}_i^T(i,a)=\frac{\sum_{t=1}^T\pi_i^{\sigma^t}(I)\sigma_i^T(i,a)}{\sum_{t=1}^T\pi_i^{\sigma^t}(I)}$$。

$\pi_i^\sigma$是达到$ℎ$的概率,如果除$𝑖$之外的所有玩家都采取了导致$ℎ$的操作,概率为$1$。

定义UPDATE_CURSORATION_STARTICAL(i:InfoSet,pi_i:Float):用于输入操作(I):i.。Cumulative_Strategy[a]+=pi_i*Strategy(I)[a]def Calculate_Average_Strategy(i:InfoSet):Strategy_sum=sum(i..。Cumulative_Strategy[a]for a In Actions(I))n_Actions=len(Actions(I))for a In Actions(I):if Strategy_sum>;0:i。平均策略[a]=i。Cumulative_Strategy[a]/Strategy_sum否则:i.。Average_Strategy[a]=1/n_Actions。

这是主要的递归反事实遗憾最小化函数,它递归地探索博弈树并计算新的策略。cfr返回$u_i(\sigma,h)$。

def cfr(h:History,i:player,pi:dict[Player,Float])->;Float:IF IS_TERMINAL(H):RETURN TERMINAL_UTILITY(h,i)elIF IS_Chance(H):A=sample_Chance(H)return CFR(h+a,i,pi)i=info_set(H)pi_neg_i=1 for j,pi_j in pi。Items():if j!=player(H):pi_neg_i*=pi_j ua={}u=0,对于a in action(I):pi_next=pi。copy()pi_next[player(H)]=Strategy(I)[a]ua[a]=cfr(h+a,i,pi_next)u+=Strategy(I)[a]*ua[a]va={a:Pi_neg_i*uA[a]用于动作(I)}v=pi_neg_i*u如果player(H)==i:update_refresses(i,v,va,i)update_cumulative_Strategy(i,pi[i])返回u。

我们为插图迭代了大约$5,000$迭代。平均策略在大约25,000美元的迭代后完全稳定下来。

def solve():跟踪器。为t设置_scalar(';';),单位为monit。LOOP(5_000):对于播放器中的i:对于INFO_SETS中的i,cfr(';';,i,{p:1 for p in player})。VALUES():Calculate_new_Strategy(I)Calculate_Average_Strategy(I)if t==9:for a in Actions:Tracker。set_scalar(f';策略。{I.key}。{a}';,is_print=false)跟踪器。设置标量(f';Average_Strategy。{I.key}。{a}';,is_print=false)跟踪器。设置标量(f';抱歉。{I.key}。{a}';,is_print=false)if(t+1)%10==0:用于In Actions(I):Tracker。保存({f';策略。{I.key}。{a}';:Strategy(I)[a],f&##;Average_Strategy。{I.key}。{a}';:i.。平均策略[a],后悔。{I.key}。{a}';:抱歉(I)[a]})记录器。日志(';平均策略';,文本。标题)记录器。检查({k:i.。INFO_SETS中k,i的Average_Strategy。项目()})

kuhn_poker:90a456ee9b1c11eaaf7bf218981c2492[脏]:";拼写错误&34;平均策略K:{';p';:0.9982164090368609,';b';:0.0017835909631391202}qb:{';p';:0.9997039668442865,';b';b':0.0002960331557134399}A:{';p';:0.0006207324643078833,';b。第#39;页:0.6255611876080304,第#39;页:0.3744388123919696}Ab:{';第#39;页:0.00031486146095717883,';第#39;页:0.9996851385390428}q:{';第#39;页;:0.6573929386464191,#39;第#39;页:0.3426070613535808}共6件。

我们可以看到占主导地位的策略(当第一个玩家得到A,当第二个玩家得到A时)很早就确定了。

然而,第一个玩家用Q下注,第二个玩家用K下注的策略需要时间才能收敛。

平均策略在收敛时在纳什均衡附近振荡,目前的策略$\sigma_i^T(i,a)$是循环的,而平均策略$\sigma}_i^T(i,a)$在收敛时在纳什均衡附近振荡,而目前的策略$\sigma_i^T(i,a)$是循环的。

我们可以在散布图上更清楚地看到他们。在这里,$sigma_i^T(i,a)$似乎在移动。