从多臂老虎机开始学习强化学习中的探索与利用
\quad
\quad
\quad
多臂老虎机问题
在多臂老虎机(multi-armed bandit,MAB)问题中,有一个拥有 K K K根拉杆的老虎机,拉动每一根拉杆都对应一个关于奖励的概率分布 R R R。我们每次拉动其中一根拉杆,就可以从该拉杆对应的奖励概率分布中获得一个奖励 r r r。我们在各根拉杆的奖励概率分布未知的情况下,从头开始尝试,目标是在操作 T T T次拉杆后获得尽可能高的累积奖励。由于奖励的概率分布是未知的,因此我们需要在“探索拉杆的获奖概率”和“根据经验选择获奖最多的拉杆”中进行权衡。“采用怎样的操作策略才能使获得的累积奖励最高”便是多臂老虎机问题。
与强化学习不同,多臂老虎机不存在状态信息,只有动作和奖励,算是最简单的“和环境交互中的学习”的一种形式。
在这个场景中,赌博机相当于环境,个体拉下某一单臂赌博机的拉杆表示执行了一个特定的行为,赌博机会给出一个即时奖励 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} at∈A表示任意一个动作;
- R \mathcal{R} R为奖励概率分布,拉动每一根拉杆的动作 a a a都对应一个奖励概率分布 R ( r ∣ a ) \mathcal{R}(r|a) R(r∣a),不同拉杆的奖励分布通常是不同的。
假设每个时间步只能拉动一个拉杆,多臂老虎机的目标为最大化一段时间步内累积的奖励: 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) max∑t=1Trt,rt∼R(⋅∣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[r∣a])
假设能够事先知道哪一个拉杆能够给出最大即时奖励,那可以每次只选择对应的那个拉杆。如果用
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∗)=a∈AmaxQ(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[V∗−Q(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[τ=1∑t(V∗−Q(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[τ=1∑tV∗−Q(aτ)]=a∈A∑E[Nt(a)](V∗−Q(a))=a∈A∑E[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=1∑Trt 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} ∀a∈A,初始化计数器 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=1→T 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[rt−Q^(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=1∑kri=k1(rk+i=1∑k−1ri)=k1(rk+(k−1)Qk−1)=k1(rk+kQk−1−Qk−1)=Qk−1+k1[rk−Qk−1]
这样做是因为:如果将所有数求和再除以次数,其缺点是每次更新的时间复杂度和空间复杂度均为
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=n1∑j=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}≤e−2nu2
现在我们将霍夫丁不等式运用于多臂老虎机问题中。将
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=e−2Nt(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
1−p成立。当
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=a∈Aargmax[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}
e−2Nt(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=arga∈AmaxQ^(a)+c⋅U^(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的次数,则汤普森采样过程如下:
- 取出每一个候选对应的参数 a 和 b;
- 为每个候选用 a 和 b 作为参数,用贝塔分布产生一个随机数;
- 按照随机数排序,输出最大值对应的候选;
- 观察用户反馈,如果用户点击则将对应候选的 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