强化学习之多臂老虎机(Multi-Armed-Bandit)问题

一、问题背景

假设有一个老虎机有 n n 个握把,每个握把 i i Pi P i 的概率可以中奖,每次可以选择一个握把进行尝试,老虎机只会返回是否中奖。尝试者不知道中奖概率,需要通过有策略的尝试,选出中奖概率最高的那一个握把。

二、问题抽象

共有 n n 个arm,每个arm的成功概率为 Pi P i 玩家可以对每个arm进行尝试,每次尝试后,若成功则得到1,若失败则得到0。玩家不知道每个arm的概率,需要调整尝试策略(policy),最终找到成功概率最大的arm

三、算法

采用最简单的 ϵgreedy ϵ − g r e e d y 策略,通过尝试调整每一个arm对应的估计成功概率(value)。即有两种操作,explore与exploit,exploit选择当前value最大的arm尝试,explore随机选择一个arm尝试。在每次尝试完成后,均根据中奖结果更新对应arm的value。更新value的策略如下:

Qk+1(a)=Qk(a)+1k+1(Rk(a)Qk(a)) Q k + 1 ( a ) = Q k ( a ) + 1 k + 1 ( R k ( a ) − Q k ( a ) )

其中,a表示尝试(action), Qk(a) Q k ( a ) 表示尝试的估计成功率(value),k表示该尝试被试过的次数, Rk(a) R k ( a ) 表示该次尝试的结果(reward)。
经过许多次尝试后,即可得到预期的结果,对应部分的代码如下

while i <= self.training_epochs:
    if random.uniform(0, 1) < self.explore_rate:
        index, reward = self.explore()
    else:
        index, reward = self.exploit()
    self.times[index] += 1
    self.values[index] += 1.0 / (self.times[index]+1) * (reward - self.values[index])
    i += 1

四、代码实现

import numpy as np
import random

class Bandit:

    def __init__(self, arms_prob):
        self.arms_prob = arms_prob
        self.size = len(self.arms_prob)

    def play(self, i):
        if not 0 <= i < self.size:
            return -1
        else:
            if random.uniform(0, 1) < self.arms_prob[i]:
                return 1
            else:
                return 0

class Model:
    def __init__(self, bandit, explore_rate=0.2, training_epochs=10000):
        self.bandit = bandit
        self.explore_rate = explore_rate
        self.training_epochs = training_epochs
        self.values = np.zeros(bandit.size)
        self.times = np.zeros(bandit.size)
        self.size = bandit.size
        self.result = 0

    def explore(self):
        index = random.randint(0, self.size - 1)
        return index, self.bandit.play(index)

    def exploit(self):
        index = self.values.argmax(axis=0)
        return index, self.bandit.play(index)

    def train(self):
        i = 1
        while i <= self.training_epochs:
            if random.uniform(0, 1) < self.explore_rate:
                index, reward = self.explore()
            else:
                index, reward = self.exploit()
            assert reward >= 0 and index >= 0
            self.times[index] += 1
            self.values[index] += 1.0 / (self.times[index]+1) * (reward - self.values[index])
            i += 1
            print("Round %d, choose %d, reward %d " % (i, index, reward))
            print(repr(self.values))

        self.result = self.values.argmax(axis=0)

bandit = Bandit([0.5, 0.6, 0.8, 0.9, 0.3, 0.95, 0.96, 0.45, 0.93, 0.22, 0.65])
model = Model(bandit, explore_rate=0.15, training_epochs=30000)
model.train()
print("Best choice is %d." % model.result)

五、测试结果

原先设置的各个握把的成功率为:

[0.5, 0.6, 0.8, 0.9, 0.3, 0.95, 0.96, 0.45 0.93, 0.22, 0.65]

设置explore_rate(explore的概率)为0.15,进行30000次尝试后,得到的各个握把估计成功率为:

[0.511, 0.577, 0.807, 0.888, 0.282, 0.949, 0.959, 0.510, 0.945, 0.238, 0.625]

可以观察到,已经与估计值的差距已经比较接近,最终选择的最大值均为第7个握把(实际概率0.96,估计概率0.959),训练结果较为理想。

你可能感兴趣的:(强化学习)