Counterfactual Regret Minimization (CFR)

Counterfactual regret minimization(下称CFR)是目前针对不完全信息博弈问题的一类主流求解方法,在棋牌类游戏中有较为广泛和成功的应用。理解这类算法的思路和性质,将有助于后续对一般的不完全信息博弈问题的研究。在这篇文章中,我将对近期对CFR的学习做一个简单的整理。内容主要包括:

  • 对原始的CFR算法及其经典变种的简单实现,以及实现过程中遇到的一些问题点和注意点;
  • 对CFR算法思路的一些个人理解,以及后续拓展方向的思考;
  • 通过一个简单的测试问题(Leduc纸牌游戏),对实现的算法的效果进行仿真评估;相应的代码仓库地址在这里

问题背景

在不完全信息(imperfect information)的博弈问题中,系统的一些参与方对系统中的信息并不是完全感知的;例如,在许多牌类游戏中,一个玩家并不知道其他玩家的手牌信息。一个值得思考的问题是:不完全信息博弈问题的解决方法,与完全信息博弈问题的解决方法,有何异同?

答:对于完全信息博弈(perfect information game)问题,当系统状态的演化方程(transition)已知时,一种自然的求解方法,是通过暴力搜索的方式,对未来可能发生的情况(包括对手的响应策略)进行仿真推演,从而选择当下较优的行为动作(action);在该类求解方法中,一种较为知名的是蒙特卡洛树搜索(MCTS),该方法近年来在围棋等游戏中取得了很好的效果。但是,这类方法较难直接应用到不完全信息博弈中。其中一个主要难点在于仿真的执行问题:由于我们并不清楚对手具体掌握了哪些信息,因此无法仅通过系统演化方程和现有信息,对对手的策略进行仿真推演。

针对上述问题,一种可能的解决思路是:首先,遍历每种与当前信息相容(consistent)的完全信息情况,利用系统演化方程对其进行仿真推演,估计选择不同动作所产生的后果(outcome)的价值;然后,通过某种方式对每种完全信息情况赋予一定权重(例如,在牌类游戏中,根据对手历史行为信息,估计对手手牌的可能情况与相应概率),进而对各行为动作在不同完全信息情况下的价值进行累加,得到期望价值;最后,根据该期望价值,生成行为策略。

在某种程度上,我们可以认为,CFR的做法基本与上述思路一致,只是在后果的价值评估、权重计算、行为策略生成等步骤上给出了更为明确的计算过程。

算法实现

在这一节中,我将主要介绍CFR及其基础变种的具体步骤和实现细节。为了更好地进行描述和讨论,我将首先引入一些常用记号(notation)来表示前一节中提到的基本元素。

  • 在某个系统状态下,我们用$h \in \mathcal{H}$(对应history)代表系统的完全信息(特别地,包含所有历史信息),用$I \in \mathcal{I}$(对应Information)代表当前参与者(agent)$p \in \mathcal{P} = \{1,\cdots,N\}$(对应player)的现有信息。特别地,对于给定的完全信息$h$和当前参与者$p$,其相应的现有信息$I$可以通过函数$I(h) = I(h,p(h))$表示。反之,与现有信息$I$相合的所有完全信息$h$组成的集合可用$\mathcal{H}(I)$表示。
  • 我们用函数$f$代表系统状态的演化规律:具体来说,对于当前系统状态$h$,如果相应的当前参与者$p$选取的动作是$a \in \mathcal{A}$(对应action),那么下一阶段的系统状态可以用$h’ = f(h,a)$来表示。
  • 对于每种系统终态$z \in \mathcal{Z} \subset \mathcal{H}$,各系统参与者所获得的收益可以用向量$u(z) \in \mathbb{R}^N$表示,其中$N$是系统参与者的数量。特别地,对于两人零和博弈,向量$u(z)$的自由度为$2-1=1$,因此可以直接使用参与者1的收益进行替代。
  • 系统参与者$p$的行为策略用函数$\sigma_p: \mathcal{I} \times \mathcal{A} \to \mathbb{R}$表示:对于每种可能的现有信息$I$,$\sigma_p(I)$对应行为动作集合$\mathcal{A}(I)$上一个概率分布。
  • 给定系统参与者$p$的策略$\sigma_p$和系统规律$(f,I(\cdot))$,对任一完全信息$h$,我们可以根据系统演化规律$f$复现出从系统初态$h_0$到当前状态$h$的过程中,参与者$p$所作出的所有行为动作$(I_1,a_1),\cdots,(I_m,a_m)$,从而计算出$p$对$h$这一事件的发生的概率贡献:$\pi^{\sigma}_p(h) = \prod_{m} \sigma_p(I_m,a_m)$。
    • 相应地,给定所有系统参与者的策略$\sigma=\{\sigma_p\}$,我们可以计算出,所有系统参与者对$h$这一事件的发生的概率贡献$\pi^{\sigma}(h) = \prod_{p’\in \mathcal{P}}\pi^{\sigma}_{p’}(h)$,以及除$p$外其他系统参与者的贡献$\pi^{\sigma}_{-p}(h) = \pi^{\sigma}(h) / \pi^{\sigma}_p(h)$。
    • 进一步地,我们可以将$p$的策略$\sigma_p$对“从系统状态$h$到一个后续系统状态$h’$的发生概率”的贡献值定义为$\pi^{\sigma}_p(h,h’) = \pi^{\sigma}_p(h’)/\pi^{\sigma}_p(h)$。其他参与者的贡献值则可相应定义为$\pi^{\sigma}_{-p}(h,h’) = \pi^{\sigma}_{-p}(h’)/\pi^{\sigma}_{-p}(h)$。
  • 最后,在给定了各系统参与者的策略$\sigma$的情况下,系统状态$h$的预期收益向量可表示为$$u^{\sigma}(h) = \sum_{z \in \mathcal{Z}} \pi^{\sigma}(h,z) u(z).$$ 在实际计算中,我们可以通过递归的方式简化计算量,避免对$z$的重复遍历:$$u^{\sigma}(h) = \sum_{a \in \mathcal{A}} \sigma_{p(h)}(I(h),a) \cdot u^{\sigma}(f(h,a)).$$

CFR

假设在第$t$次迭代中,各参与者的行为策略$\sigma^t$已经给定。一次CFR迭代的计算流程包括以下几步:

  1. 计算本次迭代的regret:$$r_t(I,a) = \sum_{h \in \mathcal{H}(I)} \pi_{-p(h)}^{\sigma^t}(h) [u^{\sigma^t}(f(h,a)) - u^{\sigma^t}(h)].$$ 在实际计算中,通常通过深度优先搜索(DFS)加递归的方式遍历所有$h$并更新$r_t(I,a)$:$$r_t(I(h),a) \gets r_t(I(h),a) + \pi_{-p(h)}^{\sigma^t}(h) [u^{\sigma^t}(f(h,a)) - u^{\sigma^t}(h)].$$ 这种计算方式的好处是,可以在前向传播(即$h+a\to h’$)的过程中迭代计算概率$\pi_{-p(h)}(h)$,在后向传播(即$h’ \to (h,a)$)的过程中迭代计算$u(h)$和$r(I,a)$,这样通过一次DFS就能完成全部计算,节省计算量。
  2. 更新累计regret:$$R_t(I,a) = R_{t-1}(I,a) + r_t(I,a).$$
  3. 根据累计regret做regret matching,产生下一次迭代的行为策略:$$\sigma^{t+1}_{p(I)}(I,a) = \frac{R_t^+(I,a)}{\sum_a R_t^+(I,a)},$$其中$R_t^+(I,a) = \max(R_t(I,a),0).$

在完成整个(多步)CFR迭代流程后,我们进一步对此前各步策略取平均(average policy),作为最终产出的策略形式:
$$\bar{\sigma}^{T}(I,a) = \frac{\sum_{t \leq T} \sum_{h\in \mathcal{H}(I)} \pi_{p(h)}^{\sigma^t}(h) \cdot \sigma^{t}_{p(h)}(I,a)}{\sum_{t \leq T} \sum_{h\in \mathcal{H}(I)} \pi_{p(h)}^{\sigma^t}(h)}$$
在实际计算中,为了简化计算量,在迭代过程中我们通常只维护上述表达式中的分子部分$\tilde{\sigma}^T(I,a)$;当需要用到策略$\bar{\sigma}^{T}$时,我们只需要进行一步归一化即可:$\bar{\sigma}^{T}(I,a) = \frac{\tilde{\sigma}^T(I,a)}{\sum_{a\in \mathcal{A}(I)}\tilde{\sigma}^T(I,a)}$。
而$\tilde{\sigma}^T(I,a)$同样可以通过迭代的方式进行更新:具体来说,在上面的单步CFR迭代流程中,在第1步遍历$h$计算当次迭代的regret时,我们同时计算平均策略的变动量$$\Delta\tilde{\sigma}^{t}(I(h),a) \gets \Delta\tilde{\sigma}^{t}(I(h),a) + \pi_{p(h)}^{\sigma^t}(h) \cdot \sigma^{t}_{p(h)}(I(h),a);$$ 然后在第2步更新累计regret时,我们同时更新平均策略$$\tilde{\sigma}^t(I,a) = \tilde{\sigma}^{t-1}(I,a) + \Delta\tilde{\sigma}^{t}(I,a).$$

在这一小节的最后,我们简单地分析CFR这一算法的计算复杂度。首先,在第1步的计算中,需要遍历所有$h$的所有动作选项$a$,因此时间复杂度是$O(|\mathcal{H}|\cdot|\mathcal{A}|)$;而空间复杂度则是$O(|\mathcal{I}|\cdot|\mathcal{A}|)$。而在第2和第3步的计算中,时间和空间复杂度都是$O(|\mathcal{I}|\cdot|\mathcal{A}|)$。因此,算法的整体时间复杂度是$O(|\mathcal{H}|\cdot|\mathcal{A}|\cdot T)$,空间复杂度是$O(|\mathcal{I}|\cdot|\mathcal{A}| \cdot T)$,其中$T$是算法的迭代步数。可以看出,当问题的状态空间$\mathcal{H}$和可观测空间$\mathcal{I}$很大时,算法在计算上是不太可行的。

CFR+

CFR+算法在CFR的基础上做了一些微小的改造,却能取得很大的收敛效率提升。一次CFR+迭代的计算流程包括以下几步:

  1. 计算本次迭代的regret和平均策略变动量,并更新累计regret和平均策略:通过深度优先搜索(DFS)加递归的方式遍历所有$h$,并计算
    $$\begin{aligned}
    R(I(h),a) & \gets R(I(h),a) + \pi_{-p(h)}^{\sigma^t}(h) [u^{\sigma^t}(f(h,a)) - u^{\sigma^t}(h)]; \\
    \tilde{\sigma}(I(h),a) & \gets \tilde{\sigma}(I(h),a) + \pi_{p(h)}^{\sigma^t}(h) \cdot \sigma^{t}_{p(h)}(I(h),a). \\
    \end{aligned}$$
  2. 对累计regret取正值$R(I,a) \gets \max(R(I,a),0)$,并对累计平均策略做折减$\tilde{\sigma}(I,a) \gets \frac{t}{t+1} \cdot \tilde{\sigma}(I,a)$。
  3. 根据累计regret做regret matching,产生下一次迭代的行为策略:$$\sigma^{t+1}_{p(I)}(I,a) = \frac{R(I,a)}{\sum_a R(I,a)}.$$

容易看出,CFR+算法与CFR算法的主要区别在于增加了第2步的处理。其中,对平均策略做折减等价于,在对各迭代步的策略进行平均时,给近期迭代步的策略更高的权重(事实上,权重=第几次迭代);直观上,由于越到后期,策略表现效果越好,因此在对策略做平均时,给近期策略更高的权重有利于平均策略更快地收敛。

(external sampling) MCCFR

当$\mathcal{H}$或$\mathcal{I}$很大时,CFR和CFR+单次迭代的复杂度较高,在计算上不太可行。一种自然的提高计算效率的想法是通过蒙特卡洛方法对$h \in \mathcal{H}$进行采样,然后根据这些采样得到的$h$对regret $R$和策略$\bar{\sigma}$进行更新。这类基于蒙特卡洛采样的CFR方法统称为MCCFR,而通过不同的蒙特卡洛采样方式可以得到不同的MCCFR实现方式。在这篇文章中,我将介绍一种最容易实现的MCCFR方法:external sampling MCCFR。在这种方法中,对$h$的采样是通过对除一个目标参与者$p_0$外的其他参与者的行为进行采样实现的。具体来说,一次external sampling MCCFR迭代的计算流程包括以下几步:对于给定的目标参与者$p_0$,

  1. 计算本次迭代的regret和平均策略变动量,并更新累计regret和平均策略:
    1. 通过深度优先搜索(DFS)的方式遍历$h$:当$p(h) = p_0$时,遍历所有$a$;否则,按照策略$\sigma^t_{p(h)}(I(h))$随机选择一个$a^*(h)$。
    2. 计算$u^{\sigma^t}(h)$:当$p(h) = p_0$时,按照CFR的方式,令$u^{\sigma^t}(h) = \sum_{a \in \mathcal{A}} \sigma^t_{p(h)}(I(h),a) \cdot u^{\sigma^t}(f(h,a))$;否则,直接令$u^{\sigma^t}(h) = u^{\sigma^t}(f(h,a^{*}(h)))$为采样的$a^{*}$对应的期望收益。
    3. 更新regret和平均策略:当$p(h) = p_0$时,更新regret$$R(I(h),a) \gets R(I(h),a) + \pi_{-p(h)}^{\sigma^t}(h) [u^{\sigma^t}(f(h,a)) - u^{\sigma^t}(h)];$$ 否则,更新平均策略$$\tilde{\sigma}(I(h),a) \gets \tilde{\sigma}(I(h),a) + \frac{\pi_{p(h)}^{\sigma^t}(h)}{\pi_{-p_0}^{\sigma^t}(h)} \cdot \sigma^{t}_{p(h)}(I(h),a).$$
  2. 根据累计regret做regret matching,产生下一次迭代的行为策略:$$\sigma^{t+1}_{p(I)}(I,a) = \frac{R(I,a)}{\sum_a R(I,a)}.$$

同样,在迭代过程中,我们可以仿照CFR+,引入折减系数$\frac{t}{t+1}$以提高策略收敛效率。

在上述MCCFR的计算过程中,需要特别注意的是平均策略的更新方式:我们仅在$p(h) \neq p_0$的时候更新平均策略,且更新步骤中增加了权重$\frac{1}{\pi_{-p_0}^{\sigma^t}(h)}$。这是因为,由于采样的原因,在原本的平均策略更新值$$\sum_{h\in \mathcal{H}(I)} \pi_{p(h)}^{\sigma^t}(h) \cdot \sigma^{t}_{p(h)}(I,a)$$中,有许多$h$未被遍历到,故直接按照原方式进行累加会导致结果有偏。在这里,我们的解决方案是使用importance sampling:在上述累加式中,对每一项除以其采样概率,从而得到在期望意义上无偏的估计量。特别地,在external sampling中,状态$h$的采样概率恰好是$\pi_{-p_0}^{\sigma^t}(h)$。进一步地,在两人博弈、且chance node的行为概率完全均匀的场景中,对于$p(h) \neq p_0$,权重$w=\frac{\pi_{p(h)}^{\sigma^t}(h)}{\pi_{-p_0}^{\sigma^t}(h)}$恰好等价于1,故此时平均策略的更新公式可以进一步化简。

思路解析

在上一节中,我介绍了CFR系列算法的实现细节。在这一节中,我将简单介绍CFR算法的目标和设计思路。

首先,对于一个不完全信息博弈问题,常见的求解目标包括:

  1. 在理论上,我们希望(在充分的时间和资源下)能够得到(近似)“最优解”。例如,近似Nash均衡——给定我方策略,对手通过选择最优策略所造成我方的收益损失被控制在一个小范围内。
  2. 在实践中,我们通常关注的是(在一定的计算和存储资源内)找到“好的解”——即面对现有(existing)策略时有较好表现的策略。

一种面向上述目标的算法开发哲学是,从简单结构问题出发,设计理论上可靠的算法,然后将其应用到更一般(general)的问题中,希望其能取得好的效果。CFR的设计思路正是follow了这种方法:首先,从两人零和博弈出发,找到能够收敛到近似Nash均衡的方法;然后,对于更一般的多人不完全信息博弈,通过实践说明其超人的表现。

而在设计CFR的算法形式和证明其收敛性时,主要包括以下几个步骤:

  1. 先通过一组等价关系,转换目标:在两人零和博弈中,Nash均衡性质可以通过策略regret的次线性收敛保证。因此,算法的目标可以转为控制策略regret的增长速度。
    • 在上述Nash均衡和regret次线性收敛的等价性中,Nash均衡是相对于各参与者自身的“平均策略”的,这种平均是整体意义上的,而非局部意义上的;这意味着在产出平均策略结果时,需要对各动作概率$\sigma(I,a)$考虑权重$\pi^{\sigma}_{p(I)}(I)$。
  2. 其次,通过递推运用不等式,可证明策略整体的regret可以被各个信息集$I$上的regret之和控制住,因此算法的目标可以进一步拆分为控制各$I$上regret的增长速度。
    • 在上述将整体regret拆分为局部regret的过程中,为了利用归纳原理(进行证明),需要保证单步拆分后的结构一致性。又由于在拆分递归的步骤中,固定了当前参与者$p(h)$的动作,因此递归步骤前后的差异在于其他参与者对历史信息$h$的概率贡献$\pi^{\sigma}_{-p(h)}(h)$。因此,我们需要在regret的计算中引入权重$\pi^{\sigma}_{-p(h)}(h)$来吸收这部分差异。
  3. 最后,利用已有方法解决拆分后的子问题:根据Blackwell’s Approachability,通过regret matching这种迭代方式可以使$I$上的regret次线性收敛。

在这一节的最后,我们来进行一些扩展讨论:在理解了CFR算法的大致思路后,我们可以如何将其迁移到其他应用场景?

  1. 理论层面:CFR中对各$I$独立进行regret最小化的思路,对于什么问题是可行的,而在哪些场景中会存在漏洞?这些漏洞可以通过什么方式解决?例如,在近期一篇关于相关均衡的文章中,作者引入了一种新的regret的概念,然后用类似CFR的思路提出了一种求解相关均衡的方法,并证明了其收敛性。
  2. 计算层面:CFR的主要计算瓶颈来源于其对regret的表示缺乏泛化能力,故在计算中需要遍历$I$,导致时间和空间的复杂度很高。那么,我们能否剥离出CFR的核心步骤,并将其融入一些其他的具有泛化能力的求解框架中?特别地,注意到regret的形式与RL中的advantage有一定的相似性,因此或许可以对现有RL算法进行一些针对性的改造,以复现CFR的优秀的收敛效果。特别地,近期的deep CFR就是一次很好的尝试。

研究前沿

在经典的CFR方法的基础上,近期又出现了许多改进工作,进一步提高了CFR系列方法的计算可行性。在这篇文章的最后,我将对Noam Brown的博士论文中提到的一些改进方法进行简单总结:

  1. discounted CFR (DCFR):在上面关于CFR+的介绍中,我们已经知道,通过对历史平均策略信息进行折减、以更多利用当前策略的信息,可以提高算法的收敛效果。
    • 在此基础上,一种自然的拓展思路是对历史累计的regret也进行折减。
    • 另外,CFR+中的折减方法相对固定;通过设计新的折减方法(例如,考虑折减系数$t^{\alpha}/(t^{\alpha}+1)$,其中$\alpha$是超参数),可以进一步提高收敛效率。
  2. warm start:在一些场景下,可能刚开始已经存在较好的行为策略$\sigma_0$(例如,启发式方法);此时,可以考虑使用这些策略对regret取值进行warm start,从而节省部分CFR的前期计算量。在上述文章中,作者证明了,当所初始化的regret取值满足一定的不等式条件时,基于该regret取值进行CFR迭代的效果等价于通过CFR迭代$T(\sigma_0)$步后继续进行CFR迭代的效果。基于此认知,作者进一步提出了一套高效的基于现有策略$\sigma_0$初始化regret的方法。
  3. pruning:另外一种提高CFR算法效率的思路是,(在保证理论可靠性的情况下)跳过一些“明显”价值不高的动作选项。
    • regret-based pruning (RBP):使得regret $R(I,a)\leq 0$的动作选项$a$通常价值不高,因此可以暂时“挂起”一段时间,不更新子树$(I,a)$内的regret信息,并在向上回传收益值$u(I,a)$时用best response (BR)的结果进行代替,直到regret重新变为正值为止。
    • dynamic thresholding:在RBP的基础上,可以进一步对选择概率很低的动作选项$a$做pruning;用于判定“选择概率很低”的阈值可以通过一组理论公式获取。
    • best-response pruning (BRP):这种pruning方式从最终产出的平均策略的角度出发,对于对结果策略影响不大的动作选项$a$(例如,其期望价值的上界估计不超过其他动作选项的期望价值下界估计),暂时“挂起”一段时间不做更新,直到其影响幅度恢复到超过一定阈值为止。具体来说,如果选择一个动作选项$a$后,在后续动作中完全使用“针对对手当前的平均策略$\bar{\sigma}$”的counterfactual best response (CBR)的期望价值$u_{BR}(I,a)$都低于历史平均实现的$\sum_{t} u^t(I)$,那么我们将暂时“挂起”动作$a$。
  4. info-set abstration:还有一种提高CFR算法效率的思路是,将一些类似的信息集$I$进行抽象和合并,当做同一个$\bar{I}$,然后在合并后的问题中应用CFR。通过合并这种方式,问题的状态空间$\mathcal{H}$和可观测空间$\mathcal{I}$的大小得到了一定的缩减,故求解效率得到了一定的提升。
    • 一种简单的思路是根据问题具体形式和人工经验,手动设计$I$的合并方法。
    • deep CFR:进一步地,我们可以使用神经网络等表达能力强的参数化模型来表征regret值和策略,从而达到自动化对相似$I$进行合并的效果:如果模型根据历史数据判断出两个$I$比较类似,那么在后续迭代过程中,每个$I$所产生的数据都可以用在另一个$I$的regret和策略更新上。
    • regret transfer:当系统的终态收益函数$u$可以表示为某种连续化参数形式$u(\theta)$时,我们可以证明,(在一定的正则性条件下,)基于一组参数$\theta_1$下、通过CFR得到的regret值可以通过discount的形式对另一组相似参数$\theta_2$下的regret值进行初始化,从而达到warm start的效果。在此基础上,如果参数$\theta$本身也是系统中的决策变量,那么我们可以设计出近似梯度来对$\theta$的取值进行优化。
    • dynamic abstraction:历史经验说明,当abstraction越粗糙时,求解速度越快,但是所得的策略$\bar{\sigma}$的效率损失越大;abstration越精细,所得策略$\bar{\sigma}$效果越好,但是求解速度越快。一种平衡求解速度和所得策略效果的方式是先从一个粗糙的abstraction出发,在迭代过程中动态地对abstration进行精细化。具体来说,首先,我们可以通过BR等方式计算abstraction外的action $a$的regret,并通过一些理论推导得到的不等式判断是否应该将该$a$增加进abstration中。其次,当我们设计好abstraction的精细化方案后,我们可以通过一些方式(包括上面介绍的regret transfer、或者对新增子博弈单独进行数步CFR迭代)对新增行为动作$a$对应的子树下各$I$的regret估计进行初始化,以降低abstraction精细化后的实际regret水平(从而提高后续算法的收敛效率)。最后,我们对原来的regret进行一定的折减,以保证在合并两部分regret后,CFR仍然能够维持正常的收敛速度。