1. 程式人生 > 其它 >如何正確的猜拳:反事實遺憾最小化演算法

如何正確的猜拳:反事實遺憾最小化演算法

反事實遺憾演算法是一種自我演繹的AI模型。本質是兩個AI代理人互相對抗,從頭開始學習遊戲。事實上在多數情況下,這是一個代理人進行自我對抗,所以它的學習速度會翻倍(重點注意,儘管它本身是和自己玩,但實際上它並沒有足夠聰明到站在對手的位置理解它上一步的行為。)

與許多最近在AI研究中的重大突破(如AlphaGo)不同,CFR不依賴於神經網路計算概率或特定舉措的價值。取代通過自我對局數百萬甚至上億的的方法,它從總結每個操作對特定位置加以考慮的遺憾總量著手。

這個演算法令人興奮的是,隨著遊戲的進行它將越來越接近遊戲的最佳策略,即納什均衡。它已經在許多遊戲和領域證明了自己,最有趣的是撲克特別是無限德州撲克。這是我們目前擁有的最好的撲克AI演算法。

遺憾匹配

遺憾匹配(RM)是一種尋找最大限度地減少對遊戲每個步驟決策遺憾的演算法。顧名思義,它從過去的行為學習告知未來決策,通過贊同它後悔以前沒有采取的行為。

在這個模式中,既有積極的遺憾和消極的遺憾。當消極遺憾被你定義成你期望的情況:在特別得情況下采取特別行動的遺憾。這意味著如果在這種情況下沒有選擇這個行為,代理人會做得更好。

積極的遺憾也定義成你期望的: 這是一種代理人可以跟蹤導致積極結果的行為的機制。(我個人認為應該被稱為別的東西,但這無所謂。)

在代理人在自我對抗的每場遊戲後,最新遊戲中遇到的負面和積極的遺憾都被新增到所有其他盤遊戲中的總結裡,並且計算出新的策略。

簡而言之,通過概率,它偏好採取過去產生的積極成果的行動並避免採取導致負面結果的行為。

與其他的學習機制一樣人類也有遺憾學習機制—我們嘗試做一些事情,如果失敗並引起消極的情緒反應,我們會記下這種遺憾,並讓自己再次嘗試。以“Scissors-Paper-Rock(SPR)”遊戲為例(就是猜拳),如果我們在對手出布的時候出了石頭,我們就後悔沒有出剪子。

這種模式在零和遊戲中向靠攏納什均衡,對於那些不熟悉博弈論的人來說,遊戲中每個代理人的贏或輸完全可以由其他代理人的贏或輸來平衡。例如,剪刀,石頭,布,是零和遊戲。當你打敗你的對手時,你贏了+1,他們輸了-1,和為零。

猜拳

在這裡,我們將猜拳(SPR)標準化作為一種存在納什均衡的標準的零和的雙人遊戲。雖然這句話包含了很多資訊,但我們將其分解成元件。首先,SPR可以定義為元組< N,A,u >,正式稱為規格化形式:

  • N = 1,… .n代表n個玩家的有限集合。在猜拳中,一般是N={1,2}
  • S是一個有限動作的組。這裡每個玩家都有相同的動作選擇,所以S ={ S,P,R }

是所有玩家同時行動的所有可能組合。即,A = {(R,R),……,(S,P)},並且每個組合被稱為動作剖析。

U是一個函式對映,將每個玩家的每個行動的資料儲存成一個效果或者回報的向量。

  • U是每個動作簡檔對映到向量為每個玩家公用事業/回報的功能。例如,(R,P)=(-1,1)意味著如果玩家1出了石頭給玩家2的布(即輸了),玩家1的獎勵為-1。因此,我們可以為玩家1定義一個效用矩陣如下:

此外,我們也可以定義策略σᵢ(s)作為玩家ᵢ選擇動作s∈S的概率。當玩家使用遺憾匹配來更新自己的戰略,σᵢ(s)預支成正比的累積遺憾。請注意,每個效用向量的和都為0,因此我們將其稱為零和博弈。在任何雙人零和博弈中,當兩個玩家都堅持遺憾匹配時,他們的平均策略收斂於納什均衡,即兩者都能夠最大化自己預期的效用:

但現在已經有足夠的數學抽象了。我們言歸正傳,在實踐中瞭解一些關於遺憾匹配實際工作的具體原理。在本教程中,我們將編寫一個實現RM演算法完猜拳遊戲的簡單程式。我們假設讀者有Python程式語言的基礎知識,最好是接觸過Numpy。

我們首先匯入本程式所需的模組。

from __future__import division
from randomimport random
import numpy as np
import pandas as pd

然後我們定義猜拳遊戲的基本引數。“utilities”是上述效用函式,用於確定行動剖析的實用值。按慣例,我們定義行的玩家是玩家1,列的玩家是玩家2.因此,為了查詢給定動作剖析(s1 = Rock,s2 = Paper)的玩家1的實用值,我們呼叫utilities.loc['ROCK', 'PAPER']

class RPS:
    actions= ['ROCK','PAPER','SCISSORS']
    n_actions= 3
    utilities= pd.DataFrame([
        # ROCK  PAPER  SCISSORS
        [0,   -1,   1],# ROCK
        [1,    0,  -1],# PAPER
        [-1,    1,   0] # SCISSORS
    ], columns=actions, index=actions)

每個玩家負責維護自己的策略和遺憾的統計。目前,我們只提出一個類框架,但稍後會回到實現細節。有幾個變數需要解釋:

  • 策略:對應於σ(s),策略向量,表示當前採取行動的概率。
  • 策略總計:

是從遊戲開始到現在策略向量的總和。

  • Regret_sum:是每次迭代的遺憾向量的總和。
  • avg_strategy:歸一化策略總和。
class Player:
    def __init__(self, name):
        self.strategy,self.avg_strategy,
        self.strategy_sum,self.regret_sum= np.zeros((4, RPS.n_actions))
        self.name= name

    def __repr__(self):
        return self.name

    def update_strategy(self):
        """
        set the preference (strategy) of choosing an action to be proportional to positive regrets
        e.g, a strategy that prefers PAPER can be [0.2, 0.6, 0.2]
        """
        self.strategy= np.copy(self.regret_sum)
        self.strategy[self.strategy <0]= 0  # reset negative regrets to zero

        summation= sum(self.strategy)
        if summation >0:
            # normalise
            self.strategy/= summation
        else:
            # uniform distribution to reduce exploitability
            self.strategy= np.repeat(1 / RPS.n_actions, RPS.n_actions)

        self.strategy_sum+= self.strategy

    def regret(self, my_action, opp_action):
        """
        we here define the regret of not having chosen an action as the difference between the utility of that action
        and the utility of the action we actually chose, with respect to the fixed choices of the other player.
        compute the regret and add it to regret sum.
        """
        result= RPS.utilities.loc[my_action, opp_action]
        facts= RPS.utilities.loc[:, opp_action].values
        regret= facts- result
        self.regret_sum+= regret

    def action(self, use_avg=False):
        """
        select an action according to strategy probabilities
        """
        strategy= self.avg_strategyif use_avgelse self.strategy
        return np.random.choice(RPS.actions, p=strategy)

    def learn_avg_strategy(self):
        # averaged strategy converges to Nash Equilibrium
        summation= sum(self.strategy_sum)
        if summation >0:
            self.avg_strategy= self.strategy_sum/ summation
        else:
            self.avg_strategy= np.repeat(1/RPS.n_actions, RPS.n_actions)

遊戲/環境的定義 我們有兩名玩家Alasdair和Calum,除非明確指定max_game,否則玩1000局。“play_regret_matching”函式充分描述了原始遺憾匹配的過程:

1.從累積遺憾計算遺憾匹配策略剖析。

2.根據策略剖析選擇每個玩家操作情況

3.計算玩家遺憾並新增到玩家累積的遺憾中。

class Game:
    def __init__(self, max_game=10000):
        self.p1= Player('Alasdair')
        self.p2= Player('Calum')
        self.max_game= max_game

    def winner(self, a1, a2):
        result= RPS.utilities.loc[a1, a2]
        if result== 1:    return self.p1
        elif result== -1: return self.p2
        else:              return 'Draw'

    def play(self, avg_regret_matching=False):
        def play_regret_matching():
            for iin xrange(0,self.max_game):
                self.p1.update_strategy()
                self.p2.update_strategy()
                a1= self.p1.action()
                a2= self.p2.action()
                self.p1.regret(a1, a2)
                self.p2.regret(a2, a1)

                winner= self.winner(a1, a2)
                num_wins[winner]+= 1

        def play_avg_regret_matching():
            for iin xrange(0,self.max_game):
                a1= self.p1.action(use_avg=True)
                a2= self.p2.action(use_avg=True)
                winner= self.winner(a1, a2)
                num_wins[winner]+= 1

        num_wins= {
            self.p1:0,
            self.p2:0,
            'Draw':0
        }

        play_regret_matching()if not avg_regret_matchingelse play_avg_regret_matching()
        print num_wins

    def conclude(self):
        """
        let two players conclude the average strategy from the previous strategy stats
        """
        self.p1.learn_avg_strategy()
        self.p2.learn_avg_strategy()

現在讓我們實現未完成的玩家類,從“regret”和“action”函式開始。考慮到行動剖析(Rock,Paper),不採取行動相應的遺憾基本上是行動與對手行為之間的效用差異,即(Rock = -1,Scissor = 1,Paper = 0),剪刀被明確的捨棄了。

class Player:
    # ....
    def regret(self, my_action, opp_action):
        """
        We here define the regret of not having chosen an action as the difference between the utility of that action and the utility of the action we actually chose, with respect to the fixed choices of the other player. Compute the regret and add it to regret sum.
        """
        result= RPS.utilities.loc[my_action, opp_action]
        facts= RPS.utilities.loc[:, opp_action].values
        regret= facts- result
        self.regret_sum+= regret

    def action(self, use_avg=False):
        """
        select an action according to strategy probabilities
        """
        strategy= self.avg_strategyif use_avgelse self.strategy
        return np.random.choice(RPS.actions, p=strategy) # p refers to 'probability'

“update_strategy”函式與遺憾匹配演算法的核心思想一致:“選擇與過去沒有選擇的積極後悔成正比的行為”,因此策略實際上是積極的遺憾歸一化。然而,請注意,當沒有積極的遺憾(也就是,說上一場比賽是完美的)時,我們採取隨機策略,儘可能地減少暴露採取行動的偏見,因為這種偏見可以被對手利用。

class Player:
    # ....
    def update_strategy(self):
        """
        Set the preference (strategy) of choosing an action to be proportional to positive regrets. e.g, a strategy that prefers PAPER can be [0.2, 0.6, 0.2]
        """
        self.strategy= np.copy(self.regret_sum)
        self.strategy[self.strategy <0]= 0  # reset negative regrets to zero

        summation= sum(self.strategy)
        if summation >0:
            # normalise
            self.strategy/= summation
        else:
            # uniform distribution to reduce exploitability
            self.strategy= np.repeat(1 / RPS.n_actions, RPS.n_actions)

        self.strategy_sum+= self.strategy

    def learn_avg_strategy(self):
        # averaged strategy converges to Nash Equilibrium
        summation= sum(self.strategy_sum)
        if summation >0:
            self.avg_strategy= self.strategy_sum/ summation
        else:
            self.avg_strategy= np.repeat(1/RPS.n_actions, RPS.n_actions)

現在我們可以開始運行了!嘗試多次執行後你可能會發現:單純的遺憾匹配並不總是產生均勻分佈的贏家數,反而平均後悔遺憾匹配可以產生。如果你在單純的遺憾匹配的在每個中間迭代列印“num_wins”,不甚至看到優勝者定期在p1和p2之間產生。這是由於簡單遺憾匹配的可利用性。想象你知道對手喜歡出剪刀,你可能會在接下來的幾個回合中偏向出石頭,並且需要一段時間後悔自己的偏見。

然而,平均的結果是在SPR中隨機選擇一個完美的策略。這是對對手採取的任何固定策略的最佳反應,在每個動作選擇中都沒有留下可利用的資訊。

if __name__== '__main__':
    game= Game()
    print '==== Use simple regret-matching strategy === '
    game.play()
    print '==== Use averaged regret-matching strategy === '
    game.conclude()
    game.play(avg_regret_matching=True)

完整的原始碼可從以下網址獲得:

https://gist.github.com/namoshizun/7a3b820b013f8e367e84c70b45af7c34