2.1前提回顾
在第一节我们讲过强化学习和其他机器学习的最大的不同:强化学习的训练信号是用来评估给定动作好坏的,后者是通过给出正确动作例子来进行直接的指导。举个例子讲,前者就像你老师讲你这次月考考的还可以或者很垃圾,你得保持或努力。后者就是直接把答案给你抄。但问题就是,高考的时候你老师可没有正确答案了。所以我们要训练智能体反复实验以试探出好的动作。就像让你参加一次次考试,锻炼你考试的能力。
先给一个定义:强化学习关注智能体和环境交互过程中的学习,这是一种试错型学习。因为智能体也不知道哪个动作获得的奖励高,或者得到负奖励,只能一步步摸索。就像我们考试一样,在期中考试中并不知道这个题目的答案是多少,但等到后续考试遇到类似的题目我们就会求解。
2.2 问题定义
2.2.1问题描述:
你需要重复地在K个选项或动作中进行选择。每次做了一个选择之后,你都会得到一定奖励。奖励的多少由你选择的动作决定。你的目标是在一段时间之后将所有的奖励最大化。这个一段时间的所有的奖励指的就是我们上一节讲的期望。
这个就是"K臂赌博机"的原始形式,这个名字源于老虎机或者也可以叫"单臂赌博机"。与单臂赌博机不同的是,单臂赌博机的操作杆只有一个,而K臂赌博机有个拉杆。每次拉动一个杆,都可以得到对应的赌博机的奖金
。
是每一个拉杆对应的奖励分布。
,
表示你每次拉动这个杆获得的奖金是不同的,有n种奖金值。
智能体需要做的是什么呢?
智能体需要在奖励概率分布未知,即奖金未知的情况下,从第一个杆开始直到第K个杆依次拉动累计最大的奖励。这里有个问题,就是奖励的概率分布是未知的,智能体需要在"探索拉杆的获奖概率"和"根据已有经验选择奖励最多的拉杆"进行权衡。这个问题简单来讲就是,智能体已经知道采取某个动作能获得多少奖励,但是他要不要采用之前没有采取过的动作,这有可能带来更大的奖励。举例子来说就是你买股票之前只买固定的几只股票,每月收益1W,但你这次准备买几个其它的,有可能到2W,也有可能低于1W。这个问题我们会在下一节给出概念性知识。
2.2.2形式化描述:
臂老虎机问题可以表示为一个二元组
问题,这是一个阉割版的强化学习的例子,因为并不存在环境中的状态
.
1.其中元组为动作集合,其中一个动作表示拉动一个拉杆,共
个拉杆.用
表示拉动第
个拉杆.
2.为奖励概率分布,拉动某个拉杆的动作
对应的奖励概率分布为
,不同的拉杆的奖励分布是不同的。
智能体的任务是为了获取最大的奖励。假设一个时间步(可能是1秒,2秒...)只能拉动一个拉杆,"智能体如何在规定时间内获得最大化奖励“这个问题可以形式化为一个公式:
表示总时间,
表示某时刻,
表示
时刻采取的动作,
表示奖励,
表示奖励
依赖于采取动作
的奖励概率分布
。
2.3准备工作
考虑一下问题是什么?
问题是求解最大累计奖励。我们很难确定最大奖励的具体值是多少,但我们可以不断接近这个值,下面是一种方法.
首先给出以下定义:
对于每一个动作,定义其期望奖励为
,于是,至少存在一个拉杆,他的期望奖励不小于其它任意一根拉杆,我们将这个拉杆的的期望奖励定义为
,表示最大的那个期望奖励值.所以我们就可以将当前拉杆与最优拉杆的奖励期望差定义为
.那么
次拉杆操作之后累计的期望差总量
,那么求解最大累计问题,就可以重新定义为求解
的最小值.我们把
称为累计懊悔.
2.4 估计期望奖励
这个臂赌博机中存在着一个问题,我们并不能知道具体的奖励概率分布信息. 但为了知道拉动哪一根杆可以获得更高的奖励,所以我们需要估计每根拉杆的期望奖励.由于只拉动一次存在随机性,所以我们多次拉动一根杠杆,然后计算得到的多次奖励的期望,其算法流程如下所示:
(1). 对于动作,初始化计数器
和奖励估值
,计数器和奖励估值都是有
个.
(2)
(3) 选取某根拉杆,该动作记为
(4) 得到奖励
(5) 更新计数器
(6) 更新期望奖励估值:
(7)
你们会觉得我敲,更新期望奖励估值的公式是什么啊,为甚这样写啊.接下来让我们推导一下,公式如下,第一行就是求均值,然后变换顺序.
为什么要这样进行估值的更新呢?因为这样可以进行增量式的期望更新.如果按照第一步求均值直接来,这个更新的时间复杂度和空间复杂度都为,而现在就是
.
2.5 10臂老虎机例子(代码)
下面先简单写一个10臂老虎机的demo,其中每根拉杆的奖励服从伯努利分布,即每次拉杆有的概率获得的奖励为1,
概率获得的奖励为0,奖励为1代表获奖,奖励为0代表没有获奖.
# -*- coding: utf-8 -*-
"""
@File : 10laohuji.py
@Author : Administrator
@Time : 2024-07-06 10:47
@IDE : PyCharm
@Version :
@comment : ···
"""
import numpy as np
import matplotlib.pyplot as plt
class BernoulliBandit:
"""先生成老虎机,K表示拉杆个数"""
def __init__(self, K):
"""
:param K:拉杆个数
"""
#生成K个数字,作为拉动每根拉杆的获奖情况
self.probs = np.random.uniform(size=K)
#获奖概率最大的拉杆
self.best_idx = np.argmax(self.probs)
#最大的获奖概率
self.best_prob = self.probs[self.best_idx]
def step(self, K):
"""
当玩家选择了k号拉杆后,根据拉动该老虎机的k号拉杆获得奖励的概率返回1(获奖)或0(未获奖)
:param K:第K号拉杆
:return: 获奖与否
"""
if np.random.rand() < self.probs[K]:
return 1
else:
return 0
if __name__ == '__main__':
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))
运行结果:
这时,我们设置了一个伯努利多臂老虎机,接下来我们使用一个Solver基础类来实现上述的多臂老虎机的求解方案,根据我们前文所讲的,我们需要实现以下函数功能:
1.根据策略选择动作
2.根据动作获取奖励
3.更新期望奖励估值
4.更新累计懊悔和计数
接下来设计一个框架,将前三个函数放到run_one_step()函数里面,由每个继承Solver类的策略具体实现,而4则放到主循环函数run()里面.
# -*- coding: utf-8 -*-
"""
@File : Solver.py
@Author : Administrator
@Time : 2024-07-06 11:07
@IDE : PyCharm
@Version :
@comment : ···
"""
import numpy.random as np
class Solver:
"""多臂老虎机算法基本框架"""
def __init__(self, bandit):
"""
:param 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)
这只是一个多臂老虎机算法的基本框架,我们后续的一些策略都可以使用这个框架来进行.
2.6 总结
在本章我们对一个经典问题"多臂老虎机"进行了分析,并将其问题进行数据公式化,得到臂老虎机问题可以表示为一个二元组
问题.然后又将其问题核心"求解奖励最大化"变为"最小化累计懊悔"
.然后构建了一个伯努利多臂老虎机和多臂老虎机算法的基本框架.在下一章中,我们将会介绍一些多臂老虎机的策略以及实现这些策略.