从多臂老虎机开始学习强化学习中的探索与利用

2023-11-11

从多臂老虎机开始学习强化学习中的探索与利用

\quad


\quad
\quad

多臂老虎机问题

在多臂老虎机(multi-armed bandit,MAB)问题中,有一个拥有 K K K根拉杆的老虎机,拉动每一根拉杆都对应一个关于奖励的概率分布 R R R。我们每次拉动其中一根拉杆,就可以从该拉杆对应的奖励概率分布中获得一个奖励 r r r。我们在各根拉杆的奖励概率分布未知的情况下,从头开始尝试,目标是在操作 T T T次拉杆后获得尽可能高的累积奖励。由于奖励的概率分布是未知的,因此我们需要在“探索拉杆的获奖概率”和“根据经验选择获奖最多的拉杆”中进行权衡。“采用怎样的操作策略才能使获得的累积奖励最高”便是多臂老虎机问题

multi-armed

与强化学习不同,多臂老虎机不存在状态信息,只有动作和奖励,算是最简单的“和环境交互中的学习”的一种形式。

在这个场景中,赌博机相当于环境,个体拉下某一单臂赌博机的拉杆表示执行了一个特定的行为,赌博机会给出一个即时奖励 R R R,随即该状态序结束。因此多臂赌博机中的一个完整状态序列就由一个行为和一个即时奖励构成,与状态无关。

形式化描述

多臂老虎机问题可以表示为一个元组 < A , R > <\mathcal{A},\mathcal{R}> <A,R>,其中:

  • A \mathcal{A} A为动作集合,其中一个动作表示拉动一个拉杆。若多臂老虎机一共有 K K K根拉杆,那动作空间就是集合 { a 1 , A 2 , ⋯   , a K } \{a_1,A_2,\cdots,a_K\} {a1,A2,,aK},我们用 a t ∈ A a_t\in\mathcal{A} atA表示任意一个动作;
  • R \mathcal{R} R为奖励概率分布,拉动每一根拉杆的动作 a a a都对应一个奖励概率分布 R ( r ∣ a ) \mathcal{R}(r|a) R(ra),不同拉杆的奖励分布通常是不同的。

假设每个时间步只能拉动一个拉杆,多臂老虎机的目标为最大化一段时间步内累积的奖励: max ⁡ ∑ t = 1 T r t , r t ∼ R ( ⋅ ∣ a t ) \max \sum_{t=1}^{T} r_{t}, r_{t} \sim \mathcal{R}\left(\cdot \mid a_{t}\right) maxt=1Trt,rtR(at) 。其中 a t a_t at表示在第 t t t时间步拉动某一拉杆的动作, r t r_t rt表示动作 a t a_t at获得的奖励。

估计期望奖励

为了方便描述问题,定义行为价值 Q ( a ) Q(a) Q(a)为采取行为 a a a获得的奖励期望:
Q ( a ) = E [ r ∣ a ] ) Q(a)=\mathbb{E}[r \mid a]) Q(a)=E[ra])
假设能够事先知道哪一个拉杆能够给出最大即时奖励,那可以每次只选择对应的那个拉杆。如果用 V ∗ V^* V表示这个最优价值, a ∗ a^* a表示能够带来最优价值的行为,那么:
V ∗ = Q ( a ∗ ) = max ⁡ a ∈ A Q ( a ) V^{*}=Q\left(a^{*}\right)=\max _{a \in A} Q(a) V=Q(a)=aAmaxQ(a)
事实上不可能事先知道拉下哪个拉杆能带来最高奖励,因此每一次拉杆获得的即时奖励都与最优价值 V ∗ V^* V存在一定的差距,定义这个差距为懊悔(regret)值:
l t = E [ V ∗ − Q ( a t ) ] l_{t}=\mathbb{E}\left[V^{*}-Q\left(a_{t}\right)\right] lt=E[VQ(at)]
每执行一次拉杆行为都会产生一个懊悔值 l t l_t lt,随着拉杆行为的持续进行,将所有的懊悔值加起来,形成一个总的懊悔值:
L t = E [ ∑ τ = 1 t ( V ∗ − Q ( a τ ) ) ] L_{t}=\mathbb{E}\left[\sum_{\tau=1}^{t}\left(V^{*}-Q\left(a_{\tau}\right)\right)\right] Lt=E[τ=1t(VQ(aτ))]
这样最大化累积奖励的问题就可以转化为最小化总懊悔值了。同时对分析问题较为简单、直观。上式也可用另一种方式重写。令 N t ( a ) N_t(a) Nt(a)为到 t t t时刻时已执行行为 a a a的次数, Δ a \Delta_a Δa为最优价值 V ∗ V^* V与行为 a a a对应的价值之间的差,则总懊悔值可以表示为:
L t = E [ ∑ τ = 1 t V ∗ − Q ( a τ ) ] = ∑ a ∈ A E [ N t ( a ) ] ( V ∗ − Q ( a ) ) = ∑ a ∈ A E [ N t ( a ) ] Δ a \begin{aligned} L_{t} &=\mathbb{E}\left[\sum_{\tau=1}^{t} V^{*}-Q\left(a_{\tau}\right)\right] \\ &=\sum_{a \in A} \mathbb{E}\left[N_{t}(a)\right]\left(V^{*}-Q(a)\right) \\ &=\sum_{a \in A} \mathbb{E}\left[N_{t}(a)\right] \Delta_{a} \end{aligned} Lt=E[τ=1tVQ(aτ)]=aAE[Nt(a)](VQ(a))=aAE[Nt(a)]Δa
把总懊悔值按行为分类统计可以看出,一个好的算法应该尽量减少执行那些价值差距较大的行为的次数。但个体无法知道这个差距具体有多少,可以使用蒙特卡罗评估来得到某行为的近似价值:
Q ^ t ( a ) = 1 N t ( a ) ∑ t = 1 T r t   1 ( a t = a ) ≈ Q ( a ) \hat{Q}_{t}(a)=\frac{1}{N_{t}(a)} \sum_{t=1}^{T} r_{t} \ 1\left(a_{t}=a\right) \approx Q(a) Q^t(a)=Nt(a)1t=1Trt 1(at=a)Q(a)
理论上 V ∗ V^* V Q ( a ) Q(a) Q(a)由环境动力学确定,因而都是静态的,随着交互次数 t t t的增多,可以认为蒙特卡罗评估得到的行为近似价值 Q ^ t ( a ) \hat Q_t(a) Q^t(a)越来越接近真实的行为价值 Q ( a ) Q(a) Q(a)

为了知道拉动哪一根拉杆能获得更高的奖励,我们需要估计拉动这根拉杆的期望奖励。由于只拉动一次拉杆获得的奖励存在随机性,所以需要多次拉动一根拉杆,然后计算得到的多次奖励的期望,其算法流程如下所示。


  • 对于 ∀ a ∈ A \forall a\in\mathcal{A} aA,初始化计数器 N ( a ) = 0 N(a)=0 N(a)=0和期望奖励估值 Q ^ ( a ) = 0 \hat Q(a)=0 Q^(a)=0
  • for t = 1 → T t=1\rightarrow T t=1T do
    • 选取某根拉杆,该动作记为 a t a_t at
    • 得到奖励 r t r_t rt
    • 更新计数器: N ( a t ) = N ( a t ) + 1 N\left(a_{t}\right)=N\left(a_{t}\right)+1 N(at)=N(at)+1
    • 更新期望奖励估值: Q ^ ( a t ) = Q ^ ( a t ) + 1 N ( a t ) [ r t − Q ^ ( a t ) ] \hat{Q}\left(a_{t}\right)=\hat{Q}\left(a_{t}\right)+\frac{1}{N\left(a_{t}\right)}\left[r_{t}-\hat{Q}\left(a_{t}\right)\right] Q^(at)=Q^(at)+N(at)1[rtQ^(at)]
  • end for

以上 for 循环中的第四步如此更新估值,是因为这样可以进行增量式的期望更新,公式如下。
Q k = 1 k ∑ i = 1 k r i = 1 k ( r k + ∑ i = 1 k − 1 r i ) = 1 k ( r k + ( k − 1 ) Q k − 1 ) = 1 k ( r k + k Q k − 1 − Q k − 1 ) = Q k − 1 + 1 k [ r k − Q k − 1 ] \begin{aligned} Q_{k} &=\frac{1}{k} \sum_{i=1}^{k} r_{i} \\ &=\frac{1}{k}\left(r_{k}+\sum_{i=1}^{k-1} r_{i}\right) \\ &=\frac{1}{k}\left(r_{k}+(k-1) Q_{k-1}\right) \\ &=\frac{1}{k}\left(r_{k}+k Q_{k-1}-Q_{k-1}\right) \\ &=Q_{k-1}+\frac{1}{k}\left[r_{k}-Q_{k-1}\right] \end{aligned} Qk=k1i=1kri=k1(rk+i=1k1ri)=k1(rk+(k1)Qk1)=k1(rk+kQk1Qk1)=Qk1+k1[rkQk1]
这样做是因为:如果将所有数求和再除以次数,其缺点是每次更新的时间复杂度和空间复杂度均为 O ( n ) O(n) O(n)。而采用增量式更新,时间复杂度和空间复杂度均为 O ( 1 ) O(1) O(1)

代码实现

实现一个拉杆数为10的多臂老虎机,其中拉动每根拉杆的奖励服从伯努利分布(奖励为1代表获奖, 奖励为0代表没有获奖)。

class BernouliBandit:
    def __init__(self, K):
        self.probs = np.random.uniform(size=K)  # 随机生成K个0~1的数,作为拉动每根拉杆的获奖概率
        self.best_idx = np.argmax(self.probs)  # 获奖概率最大的拉杆
        self.best_prob = self.probs[self.best_idx]  # 最大的获奖概率
        self.K = K

    def step(self, k):
        if np.random.rand() < self.probs[k]:
            return 1
        else:
            return 0

使用一个Solver类来实现多臂老虎机的求解方案。

class Solver:
    def __init__(self, bandit):
        self.bandit = bandit
        self.counts = np.zeros(self.bandit.K)  # 每根拉杆的尝试次数
        self.regret = 0  # 当前步的累积懊悔
        self.actions = []  # 维护一个列表,记录每一步的动作
        self.regrets = []  # 维护一个列表,记录每一步的累积懊悔

    def update_regret(self, k):
        # 计算累积懊悔并保存,k为本次动作选择的拉杆的编号
        self.regret += self.bandit.best_prob - self.bandit.probs[k]
        self.regrets.append(self.regret)

    def run_one_step(self):
        # 返回当前动作选择哪一根拉杆,由每个具体的策略实现
        raise NotImplementedError

    def run(self, num_steps):
        # 运行一定次数,num_steps为总运行次数
        for _ in range(num_steps):
            k = self.run_one_step()
            self.counts[k] += 1
            self.actions.append(k)
            self.update_regret(k)

在上面的框架中,还没有一个策略告诉我们应该采取哪个动作,即拉动哪根拉杆。一个最简单的策略就是一直采取第一个动作,但这就非常依赖运气的好坏。如果运气绝佳,可能拉动的刚好是能获得最大期望奖励的拉杆,即最优拉杆;但如果运气很糟糕,获得的就有可能是最小的期望奖励。但这中极大程度依靠运气的策略显然不行,所以接下来需要考虑的是如何设计一个选择动作的策略。

策略中的探索与利用

在多臂老虎机,甚至强化学习问题中,一个经典的问题就是探索与利用的平衡问题。探索与利用是一对矛盾:探索尝试不同的行为继而收集更多的信息利用则是做出当前信息下的最佳决定。探索可能会牺牲一些短期利益,通过搜集更多信息而获得较为长期准确的利益估计;利用则侧重于对根据已掌握的信息而做到短期利益最大化。

例如,对于一个 10 臂老虎机,我们只拉动过其中 3 根拉杆,接下来就一直拉动这 3 根拉杆中期望奖励最大的那根拉杆,但很有可能期望奖励最大的拉杆在剩下的 7 根当中,即使我们对 10 根拉杆各自都尝试了 20 次,发现 5 号拉杆的经验期望奖励是最高的,但仍然存在着微小的概率—另一根 6 号拉杆的真实期望奖励是比 5 号拉杆更高的。

探索不能无止境地进行,否则就牺牲了太多地短期利益进而导致整体利益受损;同时也不能太看重短期利益而忽视一些未探索地可能会带来巨大利益地行为。因此如何平衡探索和利用是强化学习领域的一个课题。

总结:探索与利用就是基于目前策略获取已知最优收益还是尝试不同的决策

  • Exploitation:执行能够获得已知最优收益的决策
  • Exploration:尝试更多可能的决策,不一定会是最优收益

另外,根据探索过程中使用的数据结构,可以将探索分为:依据状态行为空间的探索和参数化搜索。

  • 状态行为空间探索:针对当前的每一个状态,以一定的算法尝试之前该状态下没有尝试过的行为;
  • 参数化搜索:直接针对参数化的策略函数,表现为尝试不同的参数设置,进而得到具体的行为。

一个比较常用的思路是在开始时做比较多的探索,在对每根拉杆都有比较准确的估计后,再进行利用。目前已有一些比较经典的算法来解决这个问题,例如 ϵ \epsilon ϵ-贪婪算法上置信界算法汤普森采样算法等,接下来将分别介绍这几种算法。

ϵ \epsilon ϵ-greedy

完全贪婪算法在每一时刻采取期望奖励估值最大的动作,这就是纯粹的利用,而没有探索,所以我们通常需要对完全贪婪算法进行一些修改,其中比较经典的一种方法为 ϵ \epsilon ϵ-greedy算法。 ϵ \epsilon ϵ-greedy算法在完全贪婪算法的基础上添加了噪声,每次以概率 1 − ϵ 1-\epsilon 1ϵ选择以往经验中期望奖励估值最大的那根拉杆,以概率 ϵ \epsilon ϵ随机选择一根拉杆,公式如下:
a t = { arg ⁡ max ⁡ a Q ^ ( a ) 采样概率:  1 − ϵ 从 A 中随机选择 采样概率:  ϵ a_{t}=\left\{\begin{array}{ll} \arg \max _{a} \hat{Q}(a) & \text {采样概率: } 1-\epsilon \\ \text{从}\mathcal{A}\text {中随机选择} &\text{采样概率: }\epsilon \end{array}\right. at={argmaxaQ^(a)A中随机选择采样概率1ϵ采样概率ϵ
随着探索次数的不断增加,我们对各个动作的奖励估计得越来越准,此时我们就没必要继续花大力气进行探索。所以在 ϵ \epsilon ϵ-greedy算法的具体实现中,我们可以令 ϵ \epsilon ϵ随时间衰减,即探索的概率将会不断降低。但是请注意, ϵ \epsilon ϵ不会在有限的步数内衰减至 0,因为基于有限步数观测的完全贪婪算法仍然是一个局部信息的贪婪算法,永远距离最优解有一个固定的差距。

class EpsilonGreedy(Solver):
    def __init__(self, bandit, epsilon=0.01, init_prob=1.0):
        super(EpsilonGreedy, self).__init__(bandit)
        self.epsilon = epsilon
        # 初始化拉动所有拉杆的期望奖励估值
        self.estimates = np.array([init_prob] * self.bandit.K)

    def run_one_step(self):
        if np.random.random() < self.epsilon:
            k = np.random.randint(0, self.bandit.K)  # 随机选择一根拉杆
        else:
            k = np.argmax(self.estimates)  # 选择期望奖励估值最大的拉杆
        r = self.bandit.step(k)  # 得到本次动作的奖励
        self.estimates[k] += 1. / (self.counts[k] + 1)(r - self.estimates[k])
        return k

ϵ \epsilon ϵ值随时间衰减的 ϵ \epsilon ϵ-贪婪算法,采取的具体衰减形式为反比例衰减,公式为 ϵ t = 1 t \epsilon_t=\frac{1}{t} ϵt=t1

class DecayingEpsilonGreedy(Solver):
    def __init__(self, bandit, init_prob=1.0):
        super(DecayingEpsilonGreedy, self).__init__(bandit)
        self.estimates = np.array([init_prob] * self.bandit.K)
        self.total_count = 0

    def run_one_step(self):
        self.total_count += 1
        if np.random.random() < 1 / self.total_count:
            k = np.random.randint(0, self.bandit.K)
        else:
            k = np.argmax(self.estimates)
        r = self.bandit.step(k)
        self.estimates[k] += 1. / (self.counts[k] + 1) * (r - self.estimates[k])
        return k

随时间做反比例衰减的 ϵ \epsilon ϵ-贪婪算法能够使累积懊悔与时间步的关系变成次线性(sublinear)的,这明显优于固定 ϵ \epsilon ϵ值的 ϵ \epsilon ϵ-贪婪算法。

上置信界算法

设想这样一种情况:对于一台双臂老虎机,其中第一根拉杆只被拉动过一次,得到的奖励为0;第二根拉杆被拉动过很多次,我们对它的奖励分布已经有了大致的把握。这时你会怎么做?或许你会进一步尝试拉动第一根拉杆,从而更加确定其奖励分布。这种思路主要是基于不确定性,因为此时第一根拉杆只被拉动过一次,它的不确定性很高。一根拉杆的不确定性越大,它就越具有探索的价值,因为探索之后我们可能发现它的期望奖励很大。我们在此引入不确定性度量 U ( a ) U(a) U(a),它会随着一个动作被尝试次数的增加而减小。我们可以使用一种基于不确定性的策略来综合考虑现有的期望奖励估值不确定性,其核心问题是如何估计不确定性

上置信界(upper confidence bound,UCB)算法是一种经典的基于不确定性的策略算法,它的思想用到了一个非常著名的数学原理:霍夫丁不等式(Hoeffding’s inequality)。在霍夫丁不等式中,令 X 1 , ⋯   , X n X_1,\cdots,X_n X1,,Xn n n n个独立同分布的随机变量,取值范围为 [ 0 , 1 ] [0,1] [0,1],其经验期望为 x ˉ n = 1 n ∑ j = 1 n X j \bar x_n=\frac{1}{n}\sum_{j=1}^{n}X_j xˉn=n1j=1nXj,则有
P { E [ X ] ≥ x ˉ t + u } ≤ e − 2 n u 2 \mathbb{P}\left\{\mathbb{E}[X] \geq \bar{x}_{t}+u\right\} \leq e^{-2 n u^{2}} P{E[X]xˉt+u}e2nu2
现在我们将霍夫丁不等式运用于多臂老虎机问题中。将 Q ^ t ( a ) \hat Q_t(a) Q^t(a)代入 x ˉ t \bar x_t xˉt,不等式中的参数 u = U ^ t ( a ) u=\hat U_t(a) u=U^t(a)代表不确定性度量。给定一个概率
p = e − 2 N t ( a ) U a ( a ) 2 p=e^{-2N_t(a)U_a(a)^2} p=e2Nt(a)Ua(a)2
根据上述不等式, Q t ( a ) < Q ^ t ( a ) + U ^ t ( a ) Q_{t}(a)<\hat{Q}_{t}(a)+\hat{U}_{t}(a) Qt(a)<Q^t(a)+U^t(a)至少以概率 1 − p 1-p 1p成立。当 p p p很小时, Q t ( a ) < Q ^ t ( a ) + U ^ t ( a ) Q_{t}(a)<\hat{Q}_{t}(a)+\hat{U}_{t}(a) Qt(a)<Q^t(a)+U^t(a)就以很大概率成立, Q ^ t ( a ) + U ^ t ( a ) \hat{Q}_{t}(a)+\hat{U}_{t}(a) Q^t(a)+U^t(a)便是期望奖励上界。此时,上置信界算法便选取期望奖励上界最大的动作,即 a = argmax ⁡ a ∈ A [ Q ^ ( a ) + U ^ ( a ) ] a=\underset{a \in \mathcal{A}}{\operatorname{argmax}}[\hat{Q}(a)+\hat{U}(a)] a=aAargmax[Q^(a)+U^(a)]

那其中 U ^ t ( a ) \hat U_t(a) U^t(a)具体是什么呢?根据等式 e − 2 N t ( a ) U a ( a ) 2 e^{-2N_t(a)U_a(a)^2} e2Nt(a)Ua(a)2,解之即得
U ^ t ( a ) = − log ⁡ p 2 N t ( a ) \hat{U}_{t}(a)=\sqrt{\frac{-\log p}{2 N_{t}(a)}} U^t(a)=2Nt(a)logp

因此,设定一个概率 p p p后,就可以计算相应的不确定性度量 U ^ t ( a ) \hat U_t(a) U^t(a)了。更直观地说,UCB 算法在每次选择拉杆前,先估计每根拉杆的期望奖励的上界,使得拉动每根拉杆的期望奖励只有一个较小的概率 p p p超过这个上界,接着选出期望奖励上界最大的拉杆,从而选择最有可能获得最大期望奖励的拉杆。

不确定性度量 U ^ t ( a ) \hat U_t(a) U^t(a)的理解:如果一个行动被选择次数越多意味着它的行动价值估计的确定性就越小,比如说,如果某个行动被选择次数还是0的话,那就意味着该行动在exploring中应该以最高优先度被选择。

代码实现:在具体的实现过程中,设置 p = 1 t p=\frac{1}{t} p=t1,并且在分母中为拉动每根拉杆的次数加上常数 1,以免出现分母为 0 的情形,即此时
U ^ t ( a ) = log ⁡ t 2 ( N t ( a ) + 1 ) \hat{U}_{t}(a)=\sqrt{\frac{\log t}{2\left(N_{t}(a)+1\right)}} U^t(a)=2(Nt(a)+1)logt

同时,我们设定一个系数 c c c来控制不确定性的比重,此时
a = arg ⁡ max ⁡ a ∈ A Q ^ ( a ) + c ⋅ U ^ ( a ) 0 a=\arg \max _{a \in \mathcal{A}} \hat{Q}(a)+c \cdot \hat{U}(a)_{0} a=argaAmaxQ^(a)+cU^(a)0

class UCB(Solver):
    def __init__(self, bandit, coef, init_prob=1.0):
        super(UCB, self).__init__(bandit)
        self.total_count = 0
        self.estimates = np.array([init_prob] * self.bandit.K)
        self.coef = coef

    def run_one_step(self):
        self.total_count += 1
        # 计算上置信界
        ucb = self.estimates + self.coef * np.sqrt(np.log(self.total_count) / (2 * (self.counts + 1)))
        k = np.argmax(ucb)  # 选出上置信界最大的拉杆
        r = self.bandit.step(k)
        self.estimates[k] += 1. / (self.counts[k] + 1) * (r - self.estimates[k])
        return k

UCB通常表现得更好,但是它的扩展性要比 ϵ \epsilon ϵ-greedy方法要差,即相比来说更难以推广应用到多臂老虎机以外的更通用的强化学习问题。其中一个困难在于非平稳环境的跟踪,另一个困难在于具有很大状态空间的问题的处理。在这些更复杂的问题场景中,UCB方法通常变得不切实际。

汤普森采样算法

MAB 中还有一种经典算法——汤普森采样(Thompson sampling),先假设拉动每根拉杆的奖励服从一个特定的概率分布,然后根据拉动每根拉杆的期望奖励来进行选择。但是由于计算所有拉杆的期望奖励的代价比较高,汤普森采样算法使用采样的方式,即根据当前每个动作 a a a的奖励概率分布进行一轮采样,得到一组各根拉杆的奖励样本,再选择样本中奖励最大的动作。可以看出,汤普森采样是一种计算所有拉杆的最高奖励概率的蒙特卡洛采样方法

了解了汤普森采样算法的基本思路后,我们需要解决另一个问题:怎样得到当前每个动作 a a a的奖励概率分布并且在过程中进行更新?在实际情况中,我们通常用Beta分布对当前每个动作的奖励概率分布进行建模。具体来说,若某拉杆被选择了 k k k次,其中 m 1 m_1 m1次奖励为 1, m 2 m_2 m2次奖励为 0,则该拉杆的奖励服从参数为 ( m 1 + 1 , m 2 + 1 ) (m_1+1, m_2+1) (m1+1,m2+1)的 Beta 分布。

Beta分布特点:

  • a+b的值越大,分布曲线越窄,分布越集中,产生的随机数越靠近中心位置。
  • a/(a+b)的值越大,分布的中心位置越靠近1,否则越靠近0。这样产生的随机数也更容易靠近1或0。

汤普森采样的背后原理就是Beta分布,你把贝塔分布的 a 参数看成是每根拉杆奖励为1的次数,把分布的 b 参数看成是每根拉杆奖励为0的次数,则汤普森采样过程如下:

  1. 取出每一个候选对应的参数 a 和 b;
  2. 为每个候选用 a 和 b 作为参数,用贝塔分布产生一个随机数;
  3. 按照随机数排序,输出最大值对应的候选;
  4. 观察用户反馈,如果用户点击则将对应候选的 a 加 1,否则 b 加 1;

那么为什么汤普森采样有效呢?

  • 如果一个候选被选中的次数很多,也就是 a+b 很大了,它的分布会很窄,换句话说这个候选的收益已经非常确定了,就是说不管分布中心接近0还是1都几乎比较确定了。用它产生随机数,基本上就在中心位置附近,接近平均收益。
  • 如果一个候选不但 a+b 很大,即分布很窄,而且 a/(a+b) 也很大,接近 1,那就确定这是个好的候选项,平均收益很好,每次选择很占优势,就进入利用阶段。反之则有可能平均分布比较接近与0,几乎再无出头之日
  • 如果一个候选的 a+b 很小,分布很宽,也就是没有被选择太多次,说明这个候选是好是坏还不太确定,那么分布就是跳跃的,这次可能好,下次就可能坏,也就是还有机会存在,没有完全抛弃。那么用它产生随机数就有可能得到一个较大的随机数,在排序时被优先输出,这就起到了前面说的探索作用。

代码实现:

class ThompsonSampling(Solver):
    def __init__(self, bandit):
        super(ThompsonSampling, self).__init__(bandit)
        self._a = np.ones(self.bandit.K)  # 表示每根拉杆奖励为1的次数
        self._b = np.ones(self.bandit.K)  # 表示每根拉杆奖励为0的次数

    def run_one_step(self):
        samples = np.random.beta(self._a, self._b)  # 按照Beta分布采样一组奖励样本
        k = np.argmax(samples)  # 选出采样奖励最大的拉杆
        r = self.bandit.step(k)

        self._a[k] += r  # 更新Beta分布的第一个参数
        self._b[k] += (1 - r)  # 更新Beta分布的第二个参数
        return k

总结

  • ϵ \epsilon ϵ-greedy算法的累积懊悔是随时间线性增长的,而 ϵ \epsilon ϵ-衰减greedy算法、上置信界算法、汤普森采样算法的累积懊悔都是随时间次线性增长的(具体为对数形式增长)。
  • ϵ \epsilon ϵ-greedy更容易推广应用到多臂老虎机以外的更通用的强化学习问题。

参考文献

  • 《动手学强化学习》,张伟楠、沈键、余勇;
  • https://www.cnblogs.com/gczr/p/11220187.html
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从多臂老虎机开始学习强化学习中的探索与利用 的相关文章

  • 如何在多进程系统中实现锁定?

    我们正在并行运行许多詹金斯项目 我们使用 python 并且选择使用 pyenv 管理虚拟环境 不幸的是 pyenv 有一个众所周知的竞争条件 https github com yyuu pyenv issues 174 为了解决这个问题
  • 当我有自定义身份验证模型时,如何登录 Django Rest 可浏览 API?

    我有一个自定义用户模型 如下所示account models py from django contrib auth modles import AbstractUser from django db models signals impo
  • 稀有对象的 python 类型注释,例如 psycopg2 对象

    我了解内置类型 但是我如何指定稀有对象 例如数据库连接对象 def get connection and cursor gt tuple psycopg2 extensions cursor psycopg2 extensions conn
  • 反编译Python 3.9.2的PYC文件[重复]

    这个问题在这里已经有答案了 目前 我有一个 3 9 2 版本的 python 的 PYC 文件 P S 这适用于所有 3 9 及更高版本 我正在尝试反编译 PYC 文件 但它显示错误 因为 uncompyle6 或者更确切地说 新版本 de
  • 在Python中从大文件中搜索单词列表

    我是新蟒蛇 我有一个单词列表和一个非常大的文件 我想删除文件中包含单词列表中的单词的行 单词列表按排序给出 并且可以在初始化期间输入 我正在努力寻找解决这个问题的最佳方法 我现在正在进行线性搜索 这花费了太多时间 有什么建议么 您可以使用i
  • 无法在 selenium 和 requests 之间传递 cookie,以便使用后者进行抓取

    我用 python 结合 selenium 编写了一个脚本来登录网站 然后从driver to requests这样我就可以继续使用requests进行进一步的活动 I used item soup select one div class
  • 创建上下文后将 jar 文件添加到 pyspark

    我正在笔记本上使用 pyspark 并且不处理 SparkSession 的创建 我需要加载一个包含一些我想在处理 rdd 时使用的函数的 jar 您可以使用 jars 轻松完成此操作 但在我的特定情况下我无法做到这一点 有没有办法访问sp
  • 使用 Paramiko 进行 DSA 密钥转发?

    我正在使用 Paramiko 在远程服务器上执行 bash 脚本 在其中一些脚本中 存在与其他服务器的 ssh 连接 如果我只使用 bash 不使用 Python 我的 DSA 密钥将被第一个远程服务器上的 bash 脚本转发并使用 以连接
  • 如何确保 re.findall() 停止在正确的位置?

    这是我的代码 a import re re findall r lt title gt lt title gt a 结果是 title aaa
  • AttributeError:“模块”对象没有属性[重复]

    这个问题在这里已经有答案了 我有两个 python 模块 a py import b def hello print hello print a py print hello print b hi b py import a def hi
  • 从 Flask 运行 NPM 构建

    我有一个 React 前端 我想在与我的 python 后端 API 相同的源上提供服务 我正在尝试使用 Flask 来实现此目的 但我遇到了 Flask 找不到我的静态文件的问题 我的前端构建是用生成的npm run build in s
  • 在骨架图像中查找线 OpenCV python

    我有以下图片 我想找到一些线来进行一些计算 平均长度等 我尝试使用HoughLinesP 但它找不到线 我能怎么做 这是我的代码 sk skeleton mask rows cols sk shape imgOut np zeros row
  • Python 中维基百科 API 中的 DisambiguationError 和 GuessedAtParserWarning

    我想获得维基百科与搜索词相关的可能且可接受的名称列表 在这种情况下是 电晕 当输入以下内容时 print wikipedia summary Corona 这给出了以下输出 home virej local lib python3 8 si
  • 在Raspberry pi上升级skimage版本

    我已经使用 Raspberry Pi 2 上的 synaptic 包管理器安装了 python 包 然而 skimage 模块版本 0 6 是 synaptic 中最新的可用版本 有人可以指导我如何将其升级到0 11 因为旧版本中缺少某些功
  • XPath:通过当前节点属性选择当前和下一个节点的文本

    首先 这是从我之前的问题 https stackoverflow com questions 5202187 xpath select current and next nodes text by current node attribut
  • minizinc python 安装

    我通过 anaconda 提示符在 python 上安装了 minizinc 就像其他软件包一样 pip install minizinc 该软件包表示已成功安装 我可以导入该模块 但是 我正在遵循基本示例https minizinc py
  • 如何给URL添加变量?

    我正在尝试从网站收集数据 我有一个 Excel 文件 其中包含该网站的所有不同扩展名 F i www example com example2 我有一个脚本可以成功从网站中提取 HTML 但现在我想为所有扩展自动执行此操作 然而 当我说 s
  • Django 管理器链接

    我想知道是否有可能 如果可以的话 如何 将多个管理器链接在一起以生成受两个单独管理器影响的查询集 我将解释我正在研究的具体示例 我有多个抽象模型类 用于为其他模型提供小型的特定功能 其中两个模型是DeleteMixin 和GlobalMix
  • 如何从namedtuple实例列表创建pandas DataFrame(带有索引或多索引)?

    简单的例子 from collections import namedtuple import pandas Price namedtuple Price ticker date price a Price GE 2010 01 01 30
  • 如何使用 python 定位和读取 Data Matrix 代码

    我正在尝试读取微管底部的数据矩阵条形码 我试过libdmtx http libdmtx sourceforge net 它有 python 绑定 当矩阵的点是方形时工作得相当好 但当矩阵的点是圆形时工作得更糟 如下所示 另一个复杂问题是在某

随机推荐

  • Qt跨线程信号和槽的连接

    Qt支持三种类型的信号 槽连接 1 直接连接 当signal发射时 slot立即调用 此slot在发射signal的那个线程中被执行 不一定是接收对象生存的那个线程 2 队列连接 当控制权回到对象属于的那个线程的事件循环时 slot被调用
  • 压测以及python的自省

    经过两个季度的开发 数据库收敛的项目一期终于到了最后阶段 这周完成最后的功能测试之后即将部署到测试环境进行压测 并进行运维文档的完善 下午小组会上 heng哥分享了python类和自省机制的相关内容 他用了苏格拉底那句经典的 The une
  • Java多线程,Android多线程

    目录 一 线程的概念 二 线程创建的方式及特点 三 线程创建方式 1 继承Thread类 2 实现Runnable接口 3 实现Callable接口 我觉得了解即可 4 AsyncTask异步任务 被弃用 5 AsyncTask替代方案 四
  • 最全的计算机网络思维导图

    计算机网络思维导图 概述 应用层 传输层 网络层 链路层 物理层 概述 应用层 传输层 网络层 链路层 物理层
  • ASP.NET 2.0数据操作(一)

    创建数据访问层的过程 第一步 在网站中添加新项 数据集 Visual Studio会问我们是否将DataSet添加到App Code文件夹中 选择 Yes 第二步 向数据集中添加TableAdapter控件 完成上步后 Visual Stu
  • PPT添加页码

    点击 插入 点击 幻灯片编号 点击 幻灯片编号 点击 全部应用 右下角出现编号
  • ue测试php,用户体验测试(UE测试)

    GO语言练习 channel 缓冲机制 1 代码 2 运行 3 解析 1 代码 buffer go package main import fmt time func readThre Web调试工具 Fiddler介绍 Fiddler 教
  • JDBC连接数据库案例

    基于 MySQL 8 0 3 JDK 1 8 数据库的创建 创建学生数据库 CREATE DATABASE Student 创建学生表 CREATE TABLE STU ID INT PRIMARY KEY AUTO INCREMENT N
  • 014.Solidity入门——01数据类型

    数据类型是编写智能合约的基础 Solidity支持多种数据类型 包括基本数据类型 数组 结构体 枚举 映射等 基本数据类型包括 bool 布尔型 true或false int uint 整型 可以表示正负整数 int 或非负整数 uint
  • Oracle获取字符串长度

    Oracle中常用的字符串长度获取方法 有两个 lengthb string 和length string b是byte字节的意思 其中 lengthb string 计算string所占的字节长度 返回字符串的长度 单位是字节 lengt
  • matlab求解全局最优(初步介绍)

    这里可以看到全局优化的一些经典算法举例 matlab两个工具箱的比较 最左上角是求解器的选项 可以在此选择不同的算法求解 不同的求解器需要输入的参数也各不相同
  • Udacity Deep Learning课程作业(六)

    来到课程最后一次小作业 训练完word2vec模型后 作业六基于Text8 zip语料训练一个LSTM模型 用perplexity评价训练得到语言模型的质量 越低越好 LSTM Problem 1 num nodes 64 graph tf
  • 【Untiy2D独立/合作开发】特别篇:如何实现快速丢掉物品

    学习目标 一天两更累的一批 那么今天就来实现如何快速丢掉物品而不是只能拖拽物品这样能快速处理背包物品 学习内容 首先先去EventHandler上新建一个事件 public static event Action DropItemSelec
  • 【unity3D】如何修改相机的默认视角

    未来的游戏开发程序媛 现在的努力学习菜鸡 本专栏是我关于游戏开发的学习笔记 本篇是unity的如何修改相机的默认视角 如何修改相机的默认视角 Game窗口运行的话视角是这样的 此时Scene窗口的视角是这样的 可以观察到人物变化 但是我现在
  • html select 添加js,封装html的select标签的js操作实例

    复制代码 代码如下 function BindSelect id dataList fieldtext fieldValue 绑定某一个数据源 fieldtext为需要绑定的文本字段 fieldValue为需要绑定的value字段 var
  • 【docx4j】docx4j操作docx,实现替换内容、转换pdf、html等操作

    主要是想要用此功插件操作docx 主要的操作就是操作段落等信息 另外 也想实现替换docx的内容 实现根据模板动态生成内容的效果 也想用此插件实现docx转换pdf word的格式其实可以用xml来表现 docx4j也应该是基于xml来操作
  • Linux下的Jenkins安装教程

    当前环境 CentOS 7 8 Java 11 注意当前jenkins支持的Java版本最低为Java11 FinalShell 3 9 操作环境 安装Jenkins PS 不建议使用Docker安装Jenkins 因为使用Jenkins的
  • 设计模式学习(理论+实践)

    设计模式学习 理论 实践 例举一些Demo来帮助我们理解设计模式 https gitee com lipeng gzmu design pattern demo tree master
  • Ubuntu20.04下安装显卡驱动

    环境配置 系统 Ubuntu 20 04 CPU i5 GPU Geforce 960M Ubuntu安装显卡驱动 1 查看当前显卡安装情况 使用glxinfo查看 https dri freedesktop org wiki glxinf
  • 从多臂老虎机开始学习强化学习中的探索与利用

    从多臂老虎机开始学习强化学习中的探索与利用 quad 目录 从多臂老虎机开始学习强化学习中的探索与利用 多臂老虎机问题 形式化描述 估计期望奖励 代码实现 策略中的探索与利用 epsilo