增强式学习核心算法:基于策略的梯度下降法

上一节我们介绍了什么叫增强式学习,它的模型值得我们印刻在脑海里:


2849961-01d2ed0349ebc7e0.png
屏幕快照 2019-05-17 下午5.36.15.png

我们要打造一个Agent,也就是智能机器人,它运作在一个给定环境中。它每次与环境互动时都有给定种选择,同时它可以获得当前环境的状态,Agent如果在有限种选择中选择了“正确”的选择,那么环境就会给它一个正回馈,如果做出了错误选择 ,环境就会给它负反馈。

问题在于Agent如何根据当前环境状况让自己做出的选择以最大概率获得正回馈呢?这就是增强式学习的核心所在,我们必须给Agent一套原则或算法,让它懂得如何根据当前环境的变化做选择,而且这套算法要能够不断进化,随着算法运行得越多,算法能根据环境的回馈不断调整自己,然后算法能抽取出从当前环境状况找到最佳选择的规律。

我们注意到增强式学习与以往神经网络不同之处。以前的神经网络要想提升准确率,一个前提就是不断增加输入数据量。而增强式学习不同,它不需要输入更多数据,它只要增加与环境互动的次数,从互动结果中直接学习,不需要额外数据是增强式学习强大之处。

本节我们研究一种叫基于策略的学习法。假设在一个模拟环境中,Agent有5种选择,如果它没有学习能力,那么无论环境如何变化,它都只会在5种选择中随意选择一种。假设一次episode需要agent作出100次选择,那么我们预计每种选择大概有20次。当然20只是预测值,在具体一次episode中,某种选择肯定不是恰好20次。我们用下面代码模拟一下类似情况:

import  numpy as np
counts = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
for i in range(100):
    choice = np.random.choice([1, 2, 3, 4, 5])
    counts[choice] += 1
print(counts)

运行代码会发现,每一种选择都不可能正好是20次,当如果把上面代码运行多次,那么每种选择的次数评价起来会是20次左右。接下来我们进行一种简单的环境模拟,我们模拟两个agent从1到5中选择,每人选择100次然后加总,最后结果大的获胜:

def  simulate_game(policy):
    player_1_choices = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
    player_1_total = 0
    player_2_choices = {1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
    player_2_total = 0
    for i in range(100):
        player_1_choice = np.random.choice([1,2,3,4,5], p = policy)
        player_1_choices[player_1_choice] += 1
        player_1_total += player_1_choice
        player_2_choice = np.random.choice([1,2,3,4,5], p = policy)
        player_2_choices[player_2_choice] += 1
        player_2_total += player_2_choice
    if player_1_total > player_2_total:
        winner_choices = player_1_choices
        loser_choices = player_2_choices
    else:
        winner_choices = player_2_choices
        loser_choices = player_1_choices
    return (winner_choices, loser_choices)

policy = [0.2, 0.2, 0.2, 0.2, 0.2]
print(simulate_game(policy))

多运行几次上面代码就会看到,winner就是能多次挑选到大数值的player,例如我运行后结果如下:
({1: 21, 2: 14, 3: 19, 4: 26, 5: 20}, {1: 20, 2: 25, 3: 21, 4: 18, 5: 16})
不难理解在这样的环境里,选1是坏选择,选5才是好选择。如果代码中的Player拥有学习能力的话,它就能从反馈中发现每种选择的好坏,例如它会发现自己选择1多的时候负反馈也多,选5多的时候正反馈也多,于是它就会主动增加选择5的几率。

代码中的player可以基于如下政策调整比率。首先随机选择,然后根据结果调整。如果一次模拟下来发现自己的选择得到了胜利,那么它下次就就增加本次选择中选取次数最多的那个数值,如果失败了,它下次就减少选取次数最多的那个数值,如此反复足够多后,player就会不自觉的多选择5少选择1,这就是基于政策的学习算法核心所在。

上面展示的学习算法还不足以应用到围棋这么复杂的情景。首先上面模拟中player并没有根据环境的当前状态去做选择,同时如何对“环境”进行分析是一个非常棘手的问题。在围棋中所谓“环境”就是棋盘上棋子的分布,如何把棋盘布局与落子方式关联起来就需要使用神经网络进行计算。

我们已经知道,在神经网络中,我们通过修改连接链路的权重来改进网络的输出结果,链路的修改方法就是梯度下降法。以前我们总是修改权重,使得网络的输出与给定结果尽可能的接近,现在不一样,我们要在给定策略条件下,通过分析当前棋盘情况去修改链路权重,这种做法就叫基于政策的梯度下降法。

接下来我们将通过代码的方式,逐步实现整个AlphaGo系统,在模块实现时我们会突出相应的学习算法,很多抽象或难理解的概念无法用语言来表述清楚,但是落实到代码上时反而能变得具体和生动,从而更好理解,因此对计算机技术而言,实践永远是最好的学习方法!

首先,我们定义AlphaGo网络的基本结构:

from keras.models import Sequential
from keras.layers.core import Dense, Flatten
from keras.layers.convolutional import Conv2D

def  alphago_model(input_shape, is_policy_net = False,
                  num_filters = 192, first_kernel_size = 5, other_kernel_size = 3):
    model = Sequential()
    #第1到12层都是是卷积层,第一层将棋盘分解成5*5小块进行解析,其余层将棋盘分解成3*3小块解析
    model.add(Conv2D(num_filters, first_kernel_size, input_shape = input_shape, padding = 'same',
                    data_format = 'channels_first', activation = 'relu'))
    for i in range(2, 12):
        model.add(Conv2D(num_filters, other_kernel_size, padding = 'same',
                        data_format = 'channels_first', activation = 'relu'))
    if is_policy_net:
        #进入这里表明构建的网络将执行基于策略的梯度下降法,具体算法内容后面会在实现时具体分析
        model.add(Conv2D(filters = 1, kernel_size = 1, padding = 'same',
                        data_format = 'channels_first', activation = 'softmax'))
        model.add(Flatten())
        return model
    else:
        #增强式学习处理基于策略的梯度下降法外还有基于数值的下降法,后面实现时我们再深入分析
        model.add(Conv2D(num_filters, other_kernel_size, padding = 'same',
                        data_format = 'channels_first', activation = 'relu'))
        model.add(Conv2D(filters = 1, kernel_size = 1, padding = 'same',
                        data_format = 'channels_first', activation = 'relu'))
        model.add(Flatten())
        model.add(Dense(256, activation = 'relu'))
        model.add(Dense(1, activation = 'tanh'))
        return model

model = alphago_model(input_shape = (19, 19, 19) )
model.summary()

上面代码运行后输出如下:

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d_59 (Conv2D)           (None, 192, 19, 19)       91392     
_________________________________________________________________
conv2d_60 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_61 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_62 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_63 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_64 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_65 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_66 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_67 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_68 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_69 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_70 (Conv2D)           (None, 192, 19, 19)       331968    
_________________________________________________________________
conv2d_71 (Conv2D)           (None, 1, 19, 19)         193       
_________________________________________________________________
flatten_5 (Flatten)          (None, 361)               0         
_________________________________________________________________
dense_7 (Dense)              (None, 256)               92672     
_________________________________________________________________
dense_8 (Dense)              (None, 1)                 257       
=================================================================
Total params: 3,836,162
Trainable params: 3,836,162
Non-trainable params: 0

我们看到整个网络有三百多万参数,训练这个网络将需要非常庞大的算力支持才行。接下来我们看看适用于AlphaGo得棋盘编码,它跟我们前面提到的编码很相像,主不过它更复杂一些,它将拥有48层,每一层记录当前棋盘的特定信息。这种编码之所以有那么多层,是因为我们希望能从棋盘上判断某种特定策略是否可行,例如一种策略叫”梯形围捕“,例如下面这种情况:

2849961-48df370954d28d00.png
屏幕快照 2019-06-06 下午5.21.22.png

最左边的棋盘中白子只剩下右边一个自由点,如果被黑棋堵上就会被吃掉。当如果它直接堵住右边只有点的话,黑棋可以继续围堵,使得白棋始终只有一个自由点,如此下去最后白棋走到边缘处无路可走,最后被黑棋全部吃掉,这种情况就是”梯级围堵“。但如果白棋已经有一个棋子在梯形路径右上角,那么梯形围捕就会失败,如下图:

2849961-0c9c0bc5c35e829f.png
屏幕快照 2019-06-06 下午5.25.36.png

所以棋盘编码就需要记录当前是否具备梯形围捕的条件,如果有的话,AlphaGo就可以对对方进行梯形围捕。在前面编码中,我们使用三层来记录拥有1,2,或3个以上自由点的棋子,在48层编码中,我们使用8层来记录拥有1到8个以上自由点的棋子,我们总结一下48层编码中的不同层作用:2层分别记录不同颜色棋子在棋盘中的分布:

作用 层数 说明
记录棋子颜色 3 其中2层分布记录不同颜色棋子分布,1层记录空白位置
填充1 1 这一层全部用1来填满
填充0 1 这一层全部用0填满
有效性 1 这一层记录所有能有效落子的位置
落子记录 8 这8层记录若干步前的棋盘状况
自由点 8 记录这次落子衔接起来的棋子中对应的自由点
经过当前步后的自由点数 8 当前落子后所有棋子自由点的变化
对方被吃掉的棋子数 8 当前落子后对方有多少棋子被吃掉
叫吃 8 落子后,本方有多少棋子可能会被对方下一步吃掉
梯形围捕 1 当前落子或被梯形围捕吗
梯形逃逸 1 当前落子能否破坏梯形围捕
当前落子方棋子颜色 1 如果当前落子方是白棋用1填充,如果是黑棋 用0填充

为了方便起见,我们不用亲自实现上面编码,到时候我们之间加载相应类,这样我们可以把精力集中到算法的设计上。下一节我们将深入研究具体的学习算法实现。

请关注公众号,让我们共同学习进步


2849961-2e72d19648903e81.jpg
qrcode_for_gh_00f6e6bb0b6c_258.jpg

新书上架,请诸位朋友多多支持:
2849961-d0b793872009c378.jpeg
WechatIMG1.jpeg

转载于:https://www.jianshu.com/p/ddd27696e8b6

猜你喜欢

转载自blog.csdn.net/weixin_33860553/article/details/91270414