动手学强化学习(一):多臂老虎机 Multi-armed Bandit
更多Ai资讯:公主号AiCharm

强化学习关注智能体和环境交互过程中的学习,这是一种试错型学习(trial-and-error learning)范式。在正式学习强化学习之前,我们需要先了解多臂老虎机问题,它可以被看作简化版的强化学习问题。与强化学习不同,多臂老虎机不存在状态信息,只有动作和奖励,算是最简单的“和环境交互中的学习”的一种形式。多臂老虎机中的探索与利用(exploration vs. exploitation)问题一直以来都是一个特别经典的问题,理解它能够帮助我们学习强化学习。
在多臂老虎机(multi-armed bandit,MAB)问题(见图 2-1)中,有一个拥有 根拉杆的老虎机,拉动每一根拉杆都对应一个关于奖励的概率分布 。我们每次拉动其中一根拉杆,就可以从该拉杆对应的奖励概率分布中获得一个奖励 。我们在各根拉杆的奖励概率分布未知的情况下,从头开始尝试,目标是在操作 次拉杆后获得尽可能高的累积奖励。由于奖励的概率分布是未知的,因此我们需要在“探索拉杆的获奖概率”和“根据经验选择获奖最多的拉杆”中进行权衡。“采用怎样的操作策略才能使获得的累积奖励最高”便是多臂老虎机问题。如果是你,会怎么做呢?

多臂老虎机问题是概率论中一个经典问题,也属于强化学习的范畴。设想,一个赌徒面前有N个老虎机,事先他不知道每台老虎机的真实盈利情况,他如何根据每次玩老虎机的结果来选择下次拉哪台或者是否停止赌博,来最大化自己的从头到尾的收益。
关于多臂老虎机问题名字的来源,是因为老虎机[2]在以前是有一个操控杆,就像一只手臂(arm),而玩老虎机的结果往往是口袋被掏空,就像遇到了土匪(bandit)一样,而在多臂老虎机问题中,我们面对的是多个老虎机。
多臂老虎机问题可以表示为一个元组 ⟨ A , R ⟩ \langle\mathcal{A}, \mathcal{R}\rangle ⟨A,R⟩ ,其中:
在商业中,多臂老虎机问题有着广泛的应用,包括广告展示,医学试验和金融等领域[3].比如在推荐系统中,我们有N个物品,事先不知道用户U对N个物品的反应,我们需要每次推荐给用户某个物品,来最大化用户的价值(或者说尽量使得用户U转化),比如用户的购买。
对于每一个动作 a a a ,我们定义其期望奖励为 Q ( a ) = E r ∼ R ( ⋅ ∣ a ) [ r ] Q(a)=\mathbb{E}_{r \sim \mathcal{R}(\cdot \mid a)}[r] Q(a)=Er∼R(⋅∣a)[r] 。于是,至少存在一根拉杆,它的期望奖励不小于拉动其他任意一根拉杆,我们将该最优期望奖励表 示为 Q ∗ = max a ∈ A Q ( a ) Q^{*}=\max _{a \in \mathcal{A}} Q(a) Q∗=maxa∈AQ(a) 。为了更加直观、方便地观察拉动一根拉杆的期望奖励离最优拉杆期玍奖励的差距,我们引入懊悔 (regret) 概念。懊悔定义为拉动当前拉 杆的动作 a a a 与最优拉杆的期望奖励差,即 R ( a ) = Q ∗ − Q ( a ) R(a)=Q^{*}-Q(a) R(a)=Q∗−Q(a) 。累积懊悔 (cumulative regret) 即操作 T T T 次拉杆后累积的㥪悔总量,对于一次完整的 T T T 步决策 { a 1 , a 2 , … , a T } \left\{a_{1}, a_{2}, \ldots, a_{T}\right\} {a1,a2,…,aT} ,累积懊悔为 σ R = ∑ t = 1 T R ( a t ) \sigma_{R}=\sum_{t=1}^{T} R\left(a_{t}\right) σR=∑t=1TR(at) 。MAB 问题的目标为最大化累积奖励,等价于最小化累积懊悔。
为了知道拉动哪一根拉杆能获得更高的奖励,我们需要估计拉动这根拉杆的期望奖励。由于只拉动一次拉杆获得的奖励存在随机性,所以需要多次拉动一根拉杆,然 后计算得到的多次奖励的期望,其算法流程如下所示。
如果将所有数求和再除以次数,其缺点是每次更新的时间复杂度和空间复杂度均为
O
(
n
)
O(n)
O(n) 。而采用增量式更新,时间复杂度和空间复杂度均为
O
(
1
)
O(1)
O(1) 。
下面我们编写代码来实现一个拉杆数为 10 的多臂老虎机。其中拉动每根拉杆的奖励服从伯努利分布 (Bernoulli distribution),即每次拉下拉杆有
p
p
p 的概率获得的奖励 为 1 . 有
1
−
p
1-p
1−p 的概率获得的奖励为 0 奖励为 1 代表获奖, 奖励为 0 代表没有获奖。
本文所有代码均在jupyter notebook运行
# 导入需要使用的库,其中numpy是支持数组和矩阵运算的科学计算库,而matplotlib是绘图库
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号拉杆后,根据拉动该老虎机的k号拉杆获得奖励的概率返回1(获奖)或0(未
# 获奖)
if np.random.rand() < self.probs[k]:
return 1
else:
return 0
np.random.seed(1) # 设定随机种子,使实验具有可重复性
K = 10
bandit_10_arm = BernoulliBandit(K)
print("随机生成了一个%d臂伯努利老虎机" % K)
print("获奖概率最大的拉杆为%d号,其获奖概率为%.4f" %
(bandit_10_arm.best_idx, bandit_10_arm.best_prob))
# 随机生成了一个10臂伯努利老虎机
# 获奖概率最大的拉杆为1号,其获奖概率为0.7203
随机生成了一个10臂伯努利老虎机 获奖概率最大的拉杆为1号,其获奖概率为0.7203
接下来我们用一个 Solver 基础类来实现上述的多臂老虎机的求解方案。根据前文的算法流程,我们需要实现下列函数功能:根据策略选择动作、根据动作获取奖励、更新期望奖励估值、更新累积懊悔和计数。在下面的 MAB 算法基本框架中,我们将根据策略选择动作、根据动作获取奖励和更新期望奖励估值放在 run_one_step() 函数中,由每个继承 Solver 类的策略具体实现。而更新累积懊悔和计数则直接放在主循环 run() 中。
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)
在上面的算法框架中,还没有一个策略告诉我们应该采取哪个动作,即拉动哪根拉杆,所以接下来我们将学习如何设计一个策略。例如,一个最简单的策略就是一直采取第一个动作,但这就非常依赖运气的好坏。如果运气绝佳,可能拉动的刚好是能获得最大期望奖励的拉杆,即最优拉杆;但如果运气很糟糕,获得的就有可能是最小的期望奖励。在多臂老虎机问题中,一个经典的问题就是探索与利用的平衡问题。探索(exploration)是指尝试拉动更多可能的拉杆,这根拉杆不一定会获得最大的奖励,但这种方案能够摸清楚所有拉杆的获奖情况。例如,对于一个 10 臂老虎机,我们要把所有的拉杆都拉动一下才知道哪根拉杆可能获得最大的奖励。利用(exploitation)是指拉动已知期望奖励最大的那根拉杆,由于已知的信息仅仅来自有限次的交互观测,所以当前的最优拉杆不一定是全局最优的。例如,对于一个 10 臂老虎机,我们只拉动过其中 3 根拉杆,接下来就一直拉动这 3 根拉杆中期望奖励最大的那根拉杆,但很有可能期望奖励最大的拉杆在剩下的 7 根当中,即使我们对 10 根拉杆各自都尝试了 20 次,发现 5 号拉杆的经验期望奖励是最高的,但仍然存在着微小的概率—另一根 6 号拉杆的真实期望奖励是比 5 号拉杆更高的。
于是在多臂老虎机问题中,设计策略时就需要平衡探索和利用的次数,使得累积奖励最大化。一个比较常用的思路是在开始时做比较多的探索,在对每根拉杆都有比较准确的估计后,再进行利用。目前已有一些比较经典的算法来解决这个问题,例如ϵ-贪婪算法、上置信界算法和汤普森采样算法等,我们接下来将分别介绍这几种算法。
完全贪婪算法即在每一时刻采取期望奖励估值最大的动作(拉动拉杆),这就是纯粹的利用,而没有探索,所以我们通常需要对完全贪婪算法进行一些修改,其中比较经典的一种方法为ϵ -贪婪( ϵ-Greedy)算法。 ϵ-贪婪算法在完全贪婪算法的基础上添加了噪声,每次以概率 选择以往经验中期望奖励估值最大的那根拉杆(利用),以概率 随机选择一根拉杆(探索),公式如下:
\begin{align*}at = \begin{cases}\arg\max{a\in\mathcal{A}} \hat{Q}(a), &\text{采样概率: 1-ϵ}\ \text{从 A \mathcal{A} A中随机选择}, &\text{采样概率: }\end{cases}\end{align*}
随着探索次数的不断增加,我们对各个动作的奖励估计得越来越准,此时我们就没必要继续花大力气进行探索。所以在 -贪婪算法的具体实现中,我们可以令 随时间衰减,即探索的概率将会不断降低。但是请注意, ϵ不会在有限的步数内衰减至 0,因为基于有限步数观测的完全贪婪算法仍然是一个局部信息的贪婪算法,永远距离最优解有一个固定的差距。
我们接下来编写代码来实现一个 ϵ-贪婪算法,并用它去解决 2.4节生成的 10 臂老虎机的问题。设置ϵ=0.01,以及T=5000。
class EpsilonGreedy(Solver):
""" epsilon贪婪算法,继承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
为了更加直观地展示,可以把每一时间步的累积函数绘制出来。于是我们定义了以下绘图函数,方便之后调用。
def plot_results(solvers, solver_names):
"""生成累积懊悔随时间变化的图像。输入solvers是一个列表,列表中的每个元素是一种特定的策略。
而solver_names也是一个列表,存储每个策略的名称"""
for idx, solver in enumerate(solvers):
time_list = range(len(solver.regrets))
plt.plot(time_list, solver.regrets, label=solver_names[idx])
plt.xlabel('Time steps')
plt.ylabel('Cumulative regrets')
plt.title('%d-armed bandit' % solvers[0].bandit.K)
plt.legend()
plt.show()
np.random.seed(1)
epsilon_greedy_solver = EpsilonGreedy(bandit_10_arm, epsilon=0.01)
epsilon_greedy_solver.run(5000)
print('epsilon-贪婪算法的累积懊悔为:', epsilon_greedy_solver.regret)
plot_results([epsilon_greedy_solver], ["EpsilonGreedy"])
# epsilon-贪婪算法的累积懊悔为:25.526630933945313

通过上面的实验可以发现,在经历了开始的一小段时间后,ϵ-贪婪算法的累积懊悔几乎是线性增长的。这是 ϵ=0.01时的结果,因为一旦做出了随机拉杆的探索,那么产生的懊悔值是固定的。其他不同的ϵ取值又会带来怎样的变化呢?我们继续使用该 10 臂老虎机,我们尝试不同的参数 { 0.0001 , 0.01 , 0.1 , 0.25 , 0.5 } \left\{0.0001, 0.01,0.1,0.25,0.5\right\} {0.0001,0.01,0.1,0.25,0.5} ,查看相应的实验结果(另见彩插图 1)。
np.random.seed(0)
epsilons = [1e-4, 0.01, 0.1, 0.25, 0.5]
epsilon_greedy_solver_list = [
EpsilonGreedy(bandit_10_arm, epsilon=e) for e in epsilons
]
epsilon_greedy_solver_names = ["epsilon={}".format(e) for e in epsilons]
for solver in epsilon_greedy_solver_list:
solver.run(5000)
plot_results(epsilon_greedy_solver_list, epsilon_greedy_solver_names)

通过实验结果可以发现,基本上无论 ϵ \epsilon ϵ 取值多少,累积懊悔都是线性增长的。在这个例子中,随着 ϵ \epsilon ϵ 的增大,累积懊悔增长的速率也会增大。接下来我们尝试 ϵ \epsilon ϵ 值随 时间衰减的 ϵ \epsilon ϵ-贪婪算法,采取的具体衰减形式为反比例衰减,公式为 ϵ t = 1 t \epsilon_{t}=\frac{1}{t} ϵt=t1 。
class DecayingEpsilonGreedy(Solver):
""" epsilon值随时间衰减的epsilon-贪婪算法,继承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: # 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
np.random.seed(1)
decaying_epsilon_greedy_solver = DecayingEpsilonGreedy(bandit_10_arm)
decaying_epsilon_greedy_solver.run(5000)
print('epsilon值衰减的贪婪算法的累积懊悔为:', decaying_epsilon_greedy_solver.regret)
plot_results([decaying_epsilon_greedy_solver], ["DecayingEpsilonGreedy"])
# epsilon值衰减的贪婪算法的累积懊悔为:10.114334931260183

从实验结果图中可以发现,随时间做反比例衰减的 ϵ \epsilon ϵ -贪婪算法能够使累积懊悔与时间步的关系变成次线性(sublinear)的,这明显优于固定 值的 ϵ \epsilon ϵ -贪婪算法。
设想这样一种情况: 对于一台双臂老虎机,其中第一根拉杆只被拉动过一次,得到的奖励为 0 ;第二根拉杆被拉动过很多次,我们对它的奖励分布已经有了大致的把 握。这时你会怎么做? 或许你会进一步尝试拉动第一根拉杆,从而更加确定其奖励分布。这种思路主要是基于不确定性,因为此时第一根拉杆只被拉动过一次,它的 不确定性很高。一根拉杆的不确定性越大,它就越具有探索的价值,因为探索之后我们可能发现它的期望奖励很大。我们在此引入不确定性度量
U
(
a
)
U(a)
U(a) ,它会随着一个 动作被尝试次数的增加而减小。我们可以使用一种基于不确定性的策略来综合考虑现有的期望奖励估值和不确定性,其核心问题是如何估计不确定性。
上置信界 (upper confidence bound,UCB) 算法是一种经典的基于不确定性的策略算法,它的思想用到了一个非常著名的数学原理: 霍夫丁不等式 (Hoeffding’s inequality)。在霍夫丁不等式中,令
X
1
,
…
,
X
n
X_{1}, \ldots, 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
t
(
a
)
2
p=e^{-2 N_{t}(a) U_{t}(a)^{2}}
p=e−2Nt(a)Ut(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
t
(
a
)
2
e^{-2 N_{t}(a) U_{t}(a)^{2}}
e−2Nt(a)Ut(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 超过这个上界,接着选出期望奖励上界最大的拉杆,从而选择最有可能获得最大期望奖励的拉杆。
我们编写代码来实现 UCB 算法,并且仍然使用 2.2.4 节定义的 10 臂老虎机来观察实验结果。在具体的实现过程中,设置
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
)
a=\arg \max _{a \in \mathcal{A}} \hat{Q}(a)+c \cdot \hat{U}(a)
a=argmaxa∈AQ^(a)+c⋅U^(a) 。
class UCB(Solver):
""" 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
np.random.seed(1)
coef = 1 # 控制不确定性比重的系数
UCB_solver = UCB(bandit_10_arm, coef)
UCB_solver.run(5000)
print('上置信界算法的累积懊悔为:', UCB_solver.regret)
plot_results([UCB_solver], ["UCB"])
# 上置信界算法的累积懊悔为: 70.45281214197854

MAB 中还有一种经典算法——汤普森采样(Thompson sampling),先假设拉动每根拉杆的奖励服从一个特定的概率分布,然后根据拉动每根拉杆的期望奖励来进行选择。但是由于计算所有拉杆的期望奖励的代价比较高,汤普森采样算法使用采样的方式,即根据当前每个动作a的奖励概率分布进行一轮采样,得到一组各根拉杆的奖励样本,再选择样本中奖励最大的动作。可以看出,汤普森采样是一种计算所有拉杆的最高奖励概率的蒙特卡洛采样方法。
了解了汤普森采样算法的基本思路后,我们需要解决另一个问题:怎样得到当前每个动作a的奖励概率分布并且在过程中进行更新?在实际情况中,我们通常用 Beta 分布对当前每个动作的奖励概率分布进行建模。具体来说,若某拉杆被选择了k次,其中次奖励m1为 1,m2次奖励为 0,则该拉杆的奖励服从参数为的 (m1+1,m2+1)Beta 分布。

编写代码来实现汤普森采样算法,并且仍然使用 2.4 节定义的 10 臂老虎机来观察实验结果。
class ThompsonSampling(Solver):
""" 汤普森采样算法,继承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
np.random.seed(1)
thompson_sampling_solver = ThompsonSampling(bandit_10_arm)
thompson_sampling_solver.run(5000)
print('汤普森采样算法的累积懊悔为:', thompson_sampling_solver.regret)
plot_results([thompson_sampling_solver], ["ThompsonSampling"])
# 汤普森采样算法的累积懊悔为:57.19161964443925

通过实验我们可以得到以下结论: ϵ \epsilon ϵ -贪婪算法的累积懊悔是随时间线性增长的,而另外 3 种算法( -衰减贪婪算法、上置信界算法、汤普森采样算法)的累积懊悔都是随时间次线性增长的(具体为对数形式增长)。
探索与利用是与环境做交互学习的重要问题,是强化学习试错法中的必备技术,而多臂老虎机问题是研究探索与利用技术理论的最佳环境。了解多臂老虎机的探索与利用问题,对接下来我们学习强化学习环境探索有很重要的帮助。对于多臂老虎机各种算法的累积懊悔理论分析,有兴趣的同学可以自行查阅相关资料。 -贪婪算法、上置信界算法和汤普森采样算法在多臂老虎机问题中十分常用,其中上置信界算法和汤普森采样方法均能保证对数的渐进最优累积懊悔。
多臂老虎机问题与强化学习的一大区别在于其与环境的交互并不会改变环境,即多臂老虎机的每次交互的结果和以往的动作无关,所以可看作无状态的强化学习(stateless reinforcement learning)。
更多Ai资讯:公主号AiCharm

相关资源来自:伯禹学习平台-动手学强化学习

目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总
深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal
我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or
如何学习ruby的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/
在previousquestion中我想出了如何在多个服务器上启动经过密码验证的sshsession来运行单个命令。现在我需要能够执行“sudo”命令。问题是,net-ssh-multi没有分配sudo需要运行的伪终端(pty),导致以下错误:[127.0.0.1:stderr]sudo:sorry,youmusthaveattytorunsudo根据documentation,可以通过调用channel对象的方法来分配伪终端,但是,以下代码不起作用:它会生成上面的“notty”错误:require'net/ssh'require'net/ssh/multi'Net::SSH::Mul
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG
文章目录1、自相关函数ACF2、偏自相关函数PACF3、ARIMA(p,d,q)的阶数判断4、代码实现1、引入所需依赖2、数据读取与处理3、一阶差分与绘图4、ACF5、PACF1、自相关函数ACF自相关函数反映了同一序列在不同时序的取值之间的相关性。公式:ACF(k)=ρk=Cov(yt,yt−k)Var(yt)ACF(k)=\rho_{k}=\frac{Cov(y_{t},y_{t-k})}{Var(y_{t})}ACF(k)=ρk=Var(yt)Cov(yt,yt−k)其中分子用于求协方差矩阵,分母用于计算样本方差。求出的ACF值为[-1,1]。但对于一个平稳的AR模型,求出其滞
写在之前Shader变体、Shader属性定义技巧、自定义材质面板,这三个知识点任何一个单拿出来都是一套知识体系,不能一概而论,本文章目的在于将学习和实际工作中遇见的问题进行总结,类似于网络笔记之用,方便后续回顾查看,如有以偏概全、不祥不尽之处,还望海涵。1、Shader变体先看一段代码......Properties{ [KeywordEnum(on,off)]USL_USE_COL("IsUseColorMixTex?",int)=0 [Toggle(IS_RED_ON)]_IsRed("IsRed?",int)=0}......//中间省略,后续会有完整代码 #pragmamulti_c
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它