强化学习(五)—— AlphaGo与Alpha Zero

1. AlphaGo

1.1 论文链接

查看原文

1.2 输入编码(State)

在这里插入图片描述

  • 棋盘大小:[19,19]的矩阵, 落子则为1,反之为0。
  • Input Shape:[19,19,17]。
  • 白棋当前状态及其过去7步的状态:[19,19,1]与[19,19,7]。
  • 黑棋当前状态及其过去7步的状态:[19,19,1]与[19,19,7]。
  • 当前到谁落子:[19,19,1] (黑棋全为1,白棋全为0)

1.3 训练及评估流程

  1. 使用behavior cloning 对策略网络进行初步训练;
  2. 两个策略网络互相对弈,并使用策略梯度对策略网络进行更新;
  3. 使用策略网络去训练状态价值网络;
  4. 基于策略网络和价值网络,使用蒙特卡洛树(Monte Carlo Tree Search, MCTS)进行搜索。

1.4 模仿学习(Behavior Cloning)

通过Behavior Cloning从人的经验中初始化策略网络的参数,策略网络的结构为:在这里插入图片描述
随机初始化网络参数后,基于人类对弈的落子序列数据,进行模仿学习(分类任务),使用交叉熵作为损失函数进行参数更新:

  1. 观测得到状态: s t s_t st
  2. 使用策略网络进行预测 p t = [ π ( 1 ∣ s t ; θ ) , π ( 2 ∣ s t ; θ ) , . . . , π ( 361 ∣ s t ; θ ) ] p_t=[\pi(1|s_t;\theta),\pi(2|s_t;\theta),...,\pi(361|s_t;\theta)] pt=[π(1st;θ),π(2st;θ),...,π(361st;θ)]
  3. 高级人类玩家采取的动作为 a t ∗ a_t^* at
  4. 策略网络的预测进行one-hot编码后,和人类玩家的动作进行交叉熵计算,并更新网络参数。
    模仿学习可认为是循规蹈矩

1.5 策略网络依据策略梯度进行学习

在这里插入图片描述

  1. 两个策略网络进行对弈直到游戏结束。Player V.S. Opponent,Player 使用策略网络最新的参数,Opponent随机选用过去迭代中的网络参数。
  2. 得到对弈的序列数据: s 1 , a 1 , s 2 , a 2 , s 3 , a 3 , . . . , s T , a T s_1,a_1,s_2,a_2,s_3,a_3,...,s_T,a_T s1,a1,s2,a2,s3,a3,...,sT,aT
  3. Player获得的回报为: u 1 = u 2 = u 3 = u T ( 赢 了 为 1 , 输 了 为 − 1 ) u_1=u_2=u_3=u_T(赢了为1,输了为-1) u1=u2=u3=uT(11)
  4. 近似策略梯度(连加) g θ = ∑ t = 1 T ∂ l o g ( π ( ⋅ ∣ s t ; θ ) ) ∂ θ ⋅ u t g_\theta=\sum_{t=1}^T \frac{\partial log(\pi(\cdot|s_t;\theta))}{\partial\theta}\cdot u_t gθ=t=1Tθlog(π(st;θ))ut
  5. 参数更新 θ ← θ + β ⋅ g θ \theta\gets\theta+\beta\cdot g_{\theta} θθ+βgθ

1.6 价值网络训练

  • 状态价值函数: V π ( S ) = E ( U t ∣ S t = s ) U t = 1 ( w i n ) U t = − 1 ( f a i l ) V_\pi(S)=E(U_t|S_t=s)\\U_t=1(win)\\U_t=-1(fail) Vπ(S)=E(UtSt=s)Ut=1(win)Ut=1(fail)
  • 神经网络近似状态价值函数: v ( s ; W ) ∼ V π ( s ) v(s;W)\sim V_\pi(s) v(s;W)Vπ(s)
  • 训练过程:
  1. 两个策略网络进行对弈直到游戏结束 u 1 = u 2 = u 3 = u T ( 赢 了 为 1 , 输 了 为 − 1 ) u_1=u_2=u_3=u_T(赢了为1,输了为-1) u1=u2=u3=uT(11)
  2. 损失函数: L = ∑ t = 1 T 1 2 [ v ( s t ; W ) − u t ] 2 L=\sum_{t=1}^T \frac{1}{2}[v(s_t;W)-u_t]^2 L=t=1T21[v(st;W)ut]2
  3. 进行参数更新: W ← W − α ⋅ ∂ L ∂ W W\gets W-\alpha\cdot \frac{\partial L}{\partial W} WWαWL

1.7 Monte Carlo Tree Search

每次蒙特卡洛树(MCTS)的搜索过程:

  1. Selection:假想Player依据当前状态执行一次动作 s t a t s_t\\a_t stat
  2. Expansion: 假想对手做一次动作,并更新状态: s t + 1 s_{t+1} st+1
  3. Evaluation: 使用状态价值网络评估得到价值分数: ν = v ( s t + 1 ; W ) \nu=v(s_{t+1};W) ν=v(st+1;W)假想对弈直到结束,得到奖励: r r r给假想动作进行打分: s c o r e ( a t ) = ν + r 2 score(a_t)=\frac{\nu+r}{2} score(at)=2ν+r
  4. Backup:对动作的分数进行更新。

1.7.1 Selection

在这里插入图片描述

  1. 观测到状态 s t s_t st
  2. 对于所有可选动作,计算其选择分数: s c o r e ( a ) = Q ( a ) + η ⋅ π ( a ∣ s t ; θ ) 1 + N ( a ) {\rm score} (a)=Q(a) + \eta\cdot\frac{\pi(a|s_t;\theta)}{1+N(a)} score(a)=Q(a)+η1+N(a)π(ast;θ)Q(a)为通过MRTS得到的动作价值,N(a)为在t时刻状态下已搜索过动作a的次数。
  3. 具有最高选择分数的动作被选中。

1.7.2 Expansion

在这里插入图片描述
由于状态转移函数未知,则用策略函数作为状态转移函数

  1. 依据策略函数和对手观测到的状态,随机采样得到对手的动作: a t ‘ ∼ π ( ⋅ ∣ s t ′ ; θ ) a_t^{‘}\sim \pi(\cdot|s_t^{'};\theta) atπ(st;θ)
  2. 对手的动作将导致新的状态生成: s t + 1 s_{t+1} st+1

1.7.3 Evaluation

在这里插入图片描述

  1. 重复对弈直至游戏结束,Player的动作: a k ∼ π ( ⋅ ∣ s k ; θ ) a_k\sim \pi(\cdot|s_k;\theta) akπ(sk;θ)Opponents的动作: a k ′ ∼ π ( ⋅ ∣ s k ′ ; θ ) a_k^{'}\sim \pi(\cdot|s_k^{'};\theta) akπ(sk;θ)
  2. 记录对局结束后的回报: w i n : r T = + 1 l o s e : r T = − 1 win:r_T=+1\\lose:r_T=-1 win:rT=+1lose:rT=1
  3. 计算 t+1时刻的状态价值: ν ( s t + 1 ; W ) \nu(s_{t+1};W) ν(st+1;W)
  4. t+1时刻状态的价值分数: V ( s t + 1 ) = 1 2 ν ( s t + 1 ; W ) + 1 2 r T V(s_{t+1})=\frac{1}{2}\nu(s_{t+1};W)+\frac{1}{2}r_T V(st+1)=21ν(st+1;W)+21rT

1.7.4 Backup

在这里插入图片描述

  1. 对选取t时刻动作后的过程重复多次: a t a_t at
  2. t时刻动作的每个子节点对应多条价值分数记录,取平均值更新Q值(在Selection中用到): Q ( a t ) = m e a n ( t h e r e c o r d e d V s ′ ) Q(a_t)=mean(the\quad recorded \quad V^{'}_s) Q(at)=mean(therecordedVs)
    在这里插入图片描述

1.8 使用MCTS进行决策

  • 动作a被选中的次数为: N ( a ) N(a) N(a)
  • 在进行MCTS后,被选中次数最多的动作被Player用于最后决策: a t = a r g m a x a N ( a ) a_t=\mathop{argmax}\limits_{a}N(a) at=aargmaxN(a)
  • 执行完该次决策后,Q值和N值被重置为0: Q ( a ) = 0 N ( a ) = 0 Q(a)=0\\N(a)=0 Q(a)=0N(a)=0

2. AlphaGo Zero

2.1 论文链接

查看原文

2.2 基于AlphaGo的改进内容

  • AlphaGo Zero未使用Behavior cloning(未使用人类经验)
  • 策略网络的训练过程中使用MCTS。

2.2 策略网络的训练

AlphaGo Zero使用MCTS训练策略网络

  1. 观测到状态: s t s_t st
  2. 通过策略网络进行预测: p = [ π ( a = 1 ∣ s t , θ ) , . . . , π ( a = 361 ∣ s t , θ ) ] ∈ R 361 p=[\pi(a=1|s_t,\theta),...,\pi(a=361|s_t,\theta)]\in R^{361} p=[π(a=1st,θ),...,π(a=361st,θ)]R361
  3. 通过MCTS进行预测: n = n o r m a l i z e [ N ( a = 1 ) , N ( a = 2 ) , . . . , N ( a = 361 ) ] ∈ R 361 n=normalize[N(a=1),N(a=2),...,N(a=361)]\in R^{361} n=normalize[N(a=1),N(a=2),...,N(a=361)]R361
  4. 计算损失: L = C r o s s E n t r o p y ( n , p ) L=CrossEntropy(n,p) L=CrossEntropy(n,p)
  5. 对网络参数进行更新: θ ← ∂ L ∂ θ \theta \gets \frac{\partial L}{\partial \theta} θθL

3. 代码实现

3.1 策略网络和状态价值网络实现

3.1.1 代码

# -*- coding: utf-8 -*-
# @Time : 2022/4/1 13:47
# @Author : CyrusMay WJ
# @FileName: resnet.py
# @Software: PyCharm
# @Blog :https://blog.csdn.net/Cyrus_May


import tensorflow as tf
import logging
import sys
import os
os.environ["PATH"] += os.pathsep + 'D:\software_root\Anoconda3\envs\AI\Graphviz\\bin' # 用于网络结构画图
class ResidualNet():

    def __init__(self,input_dim,output_dim,net_struct,l2_reg=0,logger=None):
        """
        :param input_dim:
        :param output_dim:
        :param net_struct: a list for residual network, net_struct[0] is the first CNN for inputs,
                            the rest is single block for residual connect. e.g. net_struct = [
                            {filters:64,kernel_size:(3,3), {filters:128,kernel_size:(3,3),
                             {filters:128,kernel_size:(3,3)}
                            ]
        :param logger:
        """
        self.logger=logger
        self.input_dim = input_dim
        self.output_dim=output_dim
        self.l2_reg = l2_reg
        self.__build_model(net_struct)

    def conv_layer(self,x,filters,kernel_size):
        x = tf.keras.layers.Conv2D(filters=filters,
                               kernel_size=kernel_size,
                               activation="linear",
                               padding="same",
                               data_format="channels_last",
                               kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                               bias_regularizer=tf.keras.regularizers.l2(self.l2_reg))(x)
        x = tf.keras.layers.BatchNormalization(axis=-1)(x)
        x = tf.keras.layers.LeakyReLU()(x)
        return x

    def residual_block(self,inputs,filters,kernel_size):
        x = self.conv_layer(inputs,filters,kernel_size)

        x = tf.keras.layers.Conv2D(filters=filters,
                               kernel_size=kernel_size,
                               activation="linear",
                               padding="same",
                               data_format="channels_last",
                               kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                               bias_regularizer=tf.keras.regularizers.l2(self.l2_reg))(x)
        x = tf.keras.layers.BatchNormalization(axis=-1)(x)

        if inputs.shape[-1] == filters:
            x = tf.keras.layers.add([inputs,x])
        else:
            inputs = tf.keras.layers.Conv2D(filters=filters,
                               kernel_size=(1,1),
                               activation="linear",
                               padding="same",
                               data_format="channels_last",
                               kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                               bias_regularizer=tf.keras.regularizers.l2(self.l2_reg))(inputs)
            x = tf.keras.layers.add([inputs, x])
        x = tf.keras.layers.LeakyReLU()(x)
        return x

    def policy_head(self,inputs):
        x = tf.keras.layers.Conv2D(filters=2,
                               kernel_size=(1,1),
                               activation="linear",
                               padding="same",
                               data_format="channels_last",
                               kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                               bias_regularizer=tf.keras.regularizers.l2(self.l2_reg))(inputs)
        x = tf.keras.layers.Flatten()(x)
        x = tf.keras.layers.Dense(units=self.output_dim,
                                  activation="linear",
                                  kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                                  bias_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                                  name="policy_head"
                                  )(x)
        return x

    def state_value_head(self,inputs):
        x = tf.keras.layers.Conv2D(filters=2,
                                   kernel_size=(1, 1),
                                   activation="linear",
                                   padding="same",
                                   data_format="channels_last",
                                   kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                                   bias_regularizer=tf.keras.regularizers.l2(self.l2_reg))(inputs)
        x = tf.keras.layers.Flatten()(x)
        x = tf.keras.layers.Dense(units=1,
                                  activation="linear",
                                  kernel_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                                  bias_regularizer=tf.keras.regularizers.l2(self.l2_reg),
                                  name="state_value_head"
                                  )(x)
        return x

    def __build_model(self,net_struct):
        input_layer = tf.keras.layers.Input(shape=self.input_dim,name="inputs")
        x = self.conv_layer(input_layer,net_struct[0]["filters"],net_struct[0]["kernel_size"])
        for i in range(1,len(net_struct)):
            x = self.residual_block(x,net_struct[i]["filters"],net_struct[i]["kernel_size"])
        v_output = self.state_value_head(x)
        p_output = self.policy_head(x)
        self.model = tf.keras.models.Model(inputs=input_layer,outputs=[p_output,v_output])
        tf.keras.utils.plot_model(self.model, to_file="./AlphZero.png")
        self.model.compile(optimizer=tf.optimizers.Adam(),
                           loss = {"policy_head":tf.nn.softmax_cross_entropy_with_logits,"state_value_head":"mean_squared_error"},
                           loss_weights={"policy_head":0.5,"state_value_head":0.5})


if __name__ == '__main__':
    logger = logging.getLogger(name="ResidualNet")
    logger.setLevel(logging.INFO)
    screen_handler = logging.StreamHandler(sys.stdout)
    screen_handler.setLevel(logging.INFO)
    formatter = logging.Formatter('%(asctime)s - %(module)s.%(funcName)s:%(lineno)d - %(levelname)s - %(message)s')
    screen_handler.setFormatter(formatter)
    logger.addHandler(screen_handler)

    residual_net = ResidualNet(logger=logger,input_dim=[19,19,17],
                               output_dim=19*19,
                               net_struct=[
                                {"filters":64,"kernel_size":(3,3)},
                                   {"filters": 128, "kernel_size": (3, 3)},
                                   {"filters": 128, "kernel_size": (3, 3)},
                                   {"filters": 64, "kernel_size": (3, 3)},
                                   {"filters": 64, "kernel_size": (3, 3)},
                              ])

3.1.2 结果

在这里插入图片描述

3.2 蒙特卡洛树搜索算法

参考我的另一篇博客

本文部分内容为参考B站学习视频书写的笔记!

by CyrusMay 2022 04 04

当时有多少的心愿
就有多少的残缺
————五月天(步步)————

  • 13
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值