首页 > 其他分享 >动手学强化学习 第二章 多臂tiger机问题 阅读笔记

动手学强化学习 第二章 多臂tiger机问题 阅读笔记

时间:2023-02-25 09:45:19浏览次数:44  
标签:bandit self 多臂 tiger 奖励 np 第二章 hat 拉杆

第二章 多臂tiger机问题

第一节 简介

强化学习是一种试错型学习范式。

第二节 问题介绍

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

多臂tiger机问题可以表示为一个元组$<A,R>$,其中:

  • $A$为动作集合,其中一个动作表示拉动一根拉杆,若多臂tiger机一共有$K$根拉杆,那动作空间就是集合${a_1,...,a_i,...,a_K}$,我们用$a_t\in A$表示任意一个动作。
  • $R$为奖励概率分布,拉动每一根拉杆的动作$a$都对应一个奖励概率分布$R(r|a)$,拉动不同拉杆的奖励分布通常是不同的。

目标是最大化一段时间步$T$内累积的奖励:$\max\sum_{i=1}^{T}r_t,r_t~R(·|a_t)$,其中$a_t$表示在第$t$时间步拉动某一拉杆的动作,$r_t$表示动作$a_t$获得的奖励。

对于每一个动作$a$,我们定义其期望奖励为$Q(a)=E_{r~R(·|a)}[r]$。至少存在一根拉杆,它的期望奖励不小于拉动其他任意一根拉杆,我们将该最有期望奖励表示为$Q^*=\max_{a\in A}Q(a)$。懊悔被定义为拉动当前拉杆的动作$a$与最优拉杆的期望奖励差,即$R(a)=Q*-Q(a)$。累积懊悔是操作$T$次拉杆后累积的懊悔总量,即为:$\sigma_{R}=\sum_{t=1}R(a_t)$。所以问题从最大化累计奖励,变为最小化累积懊悔。

算法流程:

对于$\forall a\in A$,初始化计数器$N(a)=0$和期望奖励估值$\hat{Q}(a)=0$

for t=1→T do

​ 选取某根拉杆,该动作即为$a_t$

​ 得到奖励$r_t$

​ 更新计数器:$N(a_t)=N(a_t)+1$

​ *更新期望奖励估值:$\hat{Q}(a_t)=\hat{Q}(a_t)+\frac{1}{N(a_t)}[r_t-\hat{Q}(a_t)]$

end for

打*的那步是因为:

$Q_k=\frac{1}{k}\sum_{i=1}^{k}r_i$

$=\frac{1}{k}(r_k+\sum_{i=1}^{k-1}r_i)$

$=\frac{1}{k}(r_k+(k-1)Q_{k-1})$

$=\frac{1}{k}(r_k+kQ_{k-1}-Q_{k-1})$

$=Q_{k-1}+\frac{1}{k}(r_k-Q_{k-1})$

这样$O(1)$就求出新的均值了。

下面举个例子,有一个拉杆数为10的多臂tiger机,每根拉杆的奖励服从伯努利分布,即每次拉下拉杆有$p$的概率获得的奖励为1,有$1-p$的概率获得的奖励为0。显然1代表获奖,0代表没有获奖。于是代码如下:

import numpy as np
import matplotlib.pyplot as plt

class BernoulliBandit:
    # K代表拉杆个数
    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):
        # 玩家选择了k号拉杆后,根据拉动该拉杆获得奖励的概率返回1或0
        if np.random.rand() < self.probs[k]:
            return 1
        else:
            return 0
        
np.random.seed(42)
K = 10
bandit_10_arm = BernoulliBandit(K)
print(bandit_10_arm.best_idx, bandit_10_arm.best_prob)


# 解决框架
class Solver:
    def __init__(self, bandit):
        self.bandit = bandit  # 引入多臂tiger机
        self.counts = np.zeros(self.bandit.K)  # 每根拉杆的尝试次数
        self.regret = 0.  # 当前步的累积懊悔
        self.actions = []  # 每一步动作
        self.regrets = []  # 每一步的累积懊悔
        
    # k为本次序号
    def update_regret(self, k):
        self.regret += self.bandit.best_prob - self.bandit.probs[k]  # 懊悔是期望-期望,没问题
        self.regrets.append(self.regret)
        
    def run_one_step(self):
        # 返回当前动作选择哪一根拉杆,由每个具体的策略实现
        return NotImplementedError
    
    def run(self, num_steps):
        for _ in range(num_steps):
            k = self.run_one_step()
            self.counts[k] += 1
            self.actions.append(k)
            self.update_regret(k)

第三节 探索与利用的平衡

而具体的策略该如何设计呢?在多臂tiger机问题中,一个经典的问题就是探索与利用的平衡问题。探索(exploration)是指尝试拉动更多可能的拉杆,这跟拉杆不一定会获得最大的奖励,但这种方案能够摸清楚所有拉杆的获奖情况。利用(exploitation)是指拉动已知期望奖励最大的那根拉杆,由于已知的信息仅仅来自有限次的交互观测,所以当前的最优拉杆不一定是全局最优的。在多臂tiger机问题中,设计策略时就需要平衡探索和利用的次数,使得累积奖励最大化。

一个比较常用的思路是在开始时做比较多的探索,在对每根拉杆有比较准确的估计后,再进行利用。经典算法比如:$\epsilon-$贪婪算法、上置信界算法和汤普森采样算法等。

第四节 $\epsilon-$探索算法

完全贪婪算法即在每一时刻采取期望奖励估值最大的动作(这里指拉动拉杆),即纯粹地利用没有探索。在此基础上诞生了$\epsilon-Greedy$算法,具体是在完全贪婪算法上添加了噪声,每次以概率$1-\epsilon$选择以往经验中期望奖励估值最大的那根拉杆(利用),以概率$\epsilon$随机选择一根拉杆(探索)。

随着探索次数不断增加,我们对各个动作的奖励估计得越来越准,此时我们就没有必要花大力气进行探索。所以在具体实现中,令$\epsilon$随时间衰减,即探索的概率将会不断降低。但是却不让$\epsilon$不会在有限的步数内衰减至0,因为基于优先步数观测得完全贪婪算法仍然是一个局部信息的贪婪算法,永远距离最优解有一个固定的差距。

具体实现,这里依然是10臂tiger机,设置$\epsilon=0.01,T=5000$:

class EpsilonGreedy(Solver):  # 继承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)  # 初始化拉动所有拉杆的期望奖励估值,因为这个1会在无穷次之后的贡献变为0,而如果初始化为0,则探索的时候发现别的没有当前高,就不会探索了
        
    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)
        # counts[k]多算1,因为没算这次呢 然后是之前推的公式
        self.estimates[k] += 1. / (self.counts[k] + 1) * (r - self.estimates[k])
        return k

通过实验可知,无论$\epsilon$取值多少,累积懊悔都是线性增长的。随着$\epsilon$的增大,累积懊悔增长的速率也会增大。

接下来尝试反比例衰减,即$\epsilon_{t}=\frac{1}{t}$。代码如下:

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
    
    
np.random.seed(42)
decaying_epsilon_greedy_solver = DecayingEpsilonGreedy(bandit_10_arm)
decaying_epsilon_greedy_solver.run(5000)
print(decaying_epsilon_greedy_solver.regret)

经过实验发现,反比例衰减能够使累积懊悔与时间步的关系变成次线性的(对数形式增长),明显优于固定值的贪婪算法。

第五节 上置信界算法

对于一台双臂tiger机,第一根拉杆只被拉动过一次,得到的奖励为0,第二根拉杆被拉动过很多次,我们对它的奖励分布已经有了大致的把握。这里基于不确定性,因为第一根拉杆的不确定性很高,很具有探索价值,因为探索之后我们可能发现它的期望价值很高。我们在此引入不确定性度量$U(a)$,它会随着一个动作被尝试次数的增加而减小。我们可以使用一种基于不确定性的策略来综合考虑现有的期望奖励估值和不确定性。其核心问题是如何估计不确定性。

上置信界算法(upper confidence bound,UCB)算法是一种经典的基于不确定性的策略算法,它的思想用到了一个非常著名的数学原理:霍夫定不等式。霍夫定不等式:令$X_1,X_2,...,X_n$为$n$个独立同分布的随机变量,取值范围为$[0,1]$,其经验期望为$\bar{x_n}=\frac{1}{n}\sum_{j=1}^{n}X_j$,则有:$P(E[X]\ge \bar{x_t}+u)\le e{-2nu2}$,其中$u\ge0$。

将此不等式运用到多臂tiger机问题中,将$\hat{Q}(a_t)$代入$\bar{x_t}$,不等式中的参数$u=\hat{U}(a_t)$代表不确定性度量。给定一个概率$p=e{-2N(a_t)U(a_t)2}$,根据上述不等式,$Q(a_t)\lt \hat{Q}(a_t)+\hat{U}(a_t)$至少以概率$1-p$成立。当$p$很小时,$Q(a_t)\lt \hat{Q}(a_t)+\hat{U}(a_t)$就以很大概率成立,$\hat{Q}(a_t)+\hat{U}(a_t)$便是期望奖励上界。于是上置信界算法便选取期望奖励上届最大的动作,即$a_t=\arg\max_{a\in A}[\hat{Q}(a)+\hat{U}(a)]$。而这个$\hat{U}(a)$是怎么来的呢,是根据$p=e{-2N(a_t)U(a_t)2}$得到的,即给定概率$p$,可以求出$\hat{U}(a)=\sqrt{\frac{-\log p}{2N(a_t)}}$。

同样随时间反比例递减,设置$p=\frac{1}{t}$,并且在分母中为拉动每根拉杆的次数加上常数1,以免出现分母为0的情形,即此时$\hat{U}(a_t)=\sqrt{\frac{-\log p}{2(N(a_t)+1)}}$,同时设定一个常数$c$来控制不确定性比重,此时$a_t=\arg\max_{a\in A}[\hat{Q}(a)+c\times \hat{U}(a)]$。

实现如下:

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.sort(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


np.random.seed(42)
coef = 1  # 权重c
UCB_solver = UCB(bandit_10_arm, coef)
UCB_solver.run(5000)
print(UCB_solver.regret)

发现累积懊悔随时间次线性增长(对数形式增长)。

第六节 汤普森采样算法

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

在实际情况中,我们通常用$Beta$分布对当前每个动作的奖励概率分布进行建模,具体来说,若某拉杆被选择了$k$次,其中$m_1$次奖励为1,$m_2$次奖励为0,则该拉杆的奖励服从参数为$(m_1+1,m_2+1)$的$Beta$分布。

实现如下:

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
        self._b[k] += (1 - r)
        return k
    
np.random.seed(42)
thompson_sampling_solver = ThompsonSampling(bandit_10_arm)
thompson_sampling_solver.run(5000)
print(thompson_sampling_solver.regret)

发现累积懊悔随时间次线性增长(对数形式增长)。

第七节 小结

探索与利用是强化学习试错法中的必备技术,其中反比例衰减的$\epsilon-$贪婪算法、上置信界算法和汤普森采样方法均能保证对数的渐进最优累积懊悔。

多臂tiger机问题与强化学习的一大区别在于其与环境的交互并不会改变环境,即多臂tiger机的每次交互的结果和以往的动作无关,所以可看作无状态的强化学习。下一章讨论有状态的环境下的强化学习即马尔可夫决策过程。

标签:bandit,self,多臂,tiger,奖励,np,第二章,hat,拉杆
From: https://www.cnblogs.com/fansoflight/p/17153776.html

相关文章

  • 第二章 数据是用二进制数表示的
    第二章观后感想:     第二章主要讲解了二进制数、移位运算、逻辑运算。     计算机内部是由IC这种电子部件构成的,IC的一个引脚,只能表示两个状态。IC的......
  • 第二章、操作系统基础操作
    1.操作系统的启动异常和中断启动:OS的启动加载是通过一步步来执行实现的,主要涉及到BIOS(基本I/O处理系统,检查外设硬件能否正常工作)、DISK(存放OS)、Bootloader(加......
  • 《程序是怎样跑起来的》第二章读后感
    在第二章中,主要讲述的是关于二进制的,内容有以下:       IC的一个引脚,只能表示两个状态。IC的这个特性,决定了计算机的信息数据只能用二进制数来处理。由于1位(......
  • C++ primer 5th 第二章 变量和基本类型 阅读笔记
    第二章变量和基本类型第一节基本内置类型C++标准规定了算术类型尺寸的最小值,同时允许编译器赋予这些类型更大的尺寸。比如:类型含义最小尺寸bool布尔类型......
  • 第二章 物理层
    一、物理层基本概念媒传输媒体导引型传输媒体双绞线同轴电缆光纤非导引型传输媒体微波通信(2-40GHz)物理层协议特性机械特性:指明接口所用接线器的形状和......
  • 第二章:电阻电路的等效变换和分析
    !https://zhuanlan.zhihu.com/p/608595705第二章:电阻电路的等效变换和分析1.星三角变换2.桥型电路外部接入电压源\[U_1=U_2\impliesI_{12}=0\]外部接入电流源......
  • 资料分析第二章 高频考点
    第一节增长相关题型1增长率A》一般增长率题型特征:。。。的增长率/曾速/增幅.。。。。。,增长最快的是a>基础公式增长率 =增长......
  • 第一卷《社会工程学:信息的艺术》第二章:强制搜索微信
    第二章 上一章讲了强制搜索QQ,这一章不废话,直接聊微信。 添加微信方式,演示微信xyybpl2333 1、微信号搜索 2、手机号通讯录 3、二维码 4、群聊 5、绑定......
  • 第二章:泉映明月轮,月照迷梦人
    岳阳下得山来,正是一片梦见树林。”名字恰是应景,在这儿碰碰运气,看看有无破解之法。”找了一处空地,搭上营火,解下行囊,“如此稳固的幻境,怕是要大留了。”收恰好一切,岳阳便开始......
  • 第二章 前缀和、差分和离散化
    整体和部分的性质至关重要。通常辅助其余算法。前缀和与二维前缀和整体可分为若干部分。B3612求区间和模板。P1719最大加权矩形枚举左右边界,用每行的区间和跑最大......