K臂赌博机(1)

2.1前提回顾

在第一节我们讲过强化学习和其他机器学习的最大的不同:强化学习的训练信号是用来评估给定动作好坏的,后者是通过给出正确动作例子来进行直接的指导。举个例子讲,前者就像你老师讲你这次月考考的还可以或者很垃圾,你得保持或努力。后者就是直接把答案给你抄。但问题就是,高考的时候你老师可没有正确答案了。所以我们要训练智能体反复实验以试探出好的动作。就像让你参加一次次考试,锻炼你考试的能力。

先给一个定义:强化学习关注智能体和环境交互过程中的学习,这是一种试错型学习。因为智能体也不知道哪个动作获得的奖励高,或者得到负奖励,只能一步步摸索。就像我们考试一样,在期中考试中并不知道这个题目的答案是多少,但等到后续考试遇到类似的题目我们就会求解。

2.2 问题定义

2.2.1问题描述:

你需要重复地在K个选项或动作中进行选择。每次做了一个选择之后,你都会得到一定奖励。奖励的多少由你选择的动作决定。你的目标是在一段时间之后将所有的奖励最大化。这个一段时间的所有的奖励指的就是我们上一节讲的期望。

这个就是"K臂赌博机"的原始形式,这个名字源于老虎机或者也可以叫"单臂赌博机"。与单臂赌博机不同的是,单臂赌博机的操作杆只有一个,而K臂赌博机有K个拉杆。每次拉动一个杆,都可以得到对应的赌博机的奖金r_i \in RR是每一个拉杆对应的奖励分布。R = \{r_1, r_2,r_3...r_n\},R表示你每次拉动这个杆获得的奖金是不同的,有n种奖金值。

智能体需要做的是什么呢?

智能体需要在奖励概率分布未知,即奖金未知的情况下,从第一个杆开始直到第K个杆依次拉动累计最大的奖励。这里有个问题,就是奖励的概率分布是未知的,智能体需要在"探索拉杆的获奖概率"和"根据已有经验选择奖励最多的拉杆"进行权衡。这个问题简单来讲就是,智能体已经知道采取某个动作能获得多少奖励,但是他要不要采用之前没有采取过的动作,这有可能带来更大的奖励。举例子来说就是你买股票之前只买固定的几只股票,每月收益1W,但你这次准备买几个其它的,有可能到2W,也有可能低于1W。这个问题我们会在下一节给出概念性知识。

2.2.2形式化描述:

K臂老虎机问题可以表示为一个二元组<A,R>问题,这是一个阉割版的强化学习的例子,因为并不存在环境中的状态S.

1.其中元组A = \{a_1, a_2, ... a_k\}为动作集合,其中一个动作表示拉动一个拉杆,共k个拉杆.用a_i表示拉动第i个拉杆.

2.R为奖励概率分布,拉动某个拉杆的动作a_i对应的奖励概率分布为R(r|a_i),不同的拉杆的奖励分布是不同的。

智能体的任务是为了获取最大的奖励。假设一个时间步(可能是1秒,2秒...)只能拉动一个拉杆,"智能体如何在规定时间T内获得最大化奖励“这个问题可以形式化为一个公式:

max\sum_{t=1}^T {r_t,r_t \sim R(\cdot|a_t)}

T表示总时间,t表示某时刻,a_t表示t时刻采取的动作,r_t表示奖励,\sim表示奖励r_t依赖于采取动作a_t的奖励概率分布R(\cdot|a_t)

2.3准备工作

考虑一下问题是什么?

问题是求解最大累计奖励。我们很难确定最大奖励的具体值是多少,但我们可以不断接近这个值,下面是一种方法.

首先给出以下定义:

对于每一个动作a,定义其期望奖励为Q(a) = E_{r \sim R(\cdot|a)} [r],于是,至少存在一个拉杆,他的期望奖励不小于其它任意一根拉杆,我们将这个拉杆的的期望奖励定义为Q^*(a) = max_{a\in A}Q(a),表示最大的那个期望奖励值.所以我们就可以将当前拉杆与最优拉杆的奖励期望差定义为
R(a) = Q^*(a) - Q(a).那么T次拉杆操作之后累计的期望差总量\sigma(a_t) = \sum_{t=1}^T R(a_t),那么求解最大累计问题,就可以重新定义为求解\sigma(a)的最小值.我们把\sigma(a)称为累计懊悔.

2.4 估计期望奖励

这个K臂赌博机中存在着一个问题,我们并不能知道具体的奖励概率分布信息. 但为了知道拉动哪一根杆可以获得更高的奖励,所以我们需要估计每根拉杆的期望奖励.由于只拉动一次存在随机性,所以我们多次拉动一根杠杆,然后计算得到的多次奖励的期望,其算法流程如下所示:

(1). 对于动作a \in A,初始化计数器N(a) = 0和奖励估值\hat{Q}(a) = 0,计数器和奖励估值都是有K个.

(2) for \:\:\: t \:= \: 1 \rightarrow T \: do:

(3)         选取某根拉杆,该动作记为a_t

(4)         得到奖励r_t

(5)         更新计数器N(a_t) = N(a_t) + 1

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

(7)end \: \:for

你们会觉得我敲,更新期望奖励估值的公式是什么啊,为甚这样写啊.接下来让我们推导一下,公式如下,第一行就是求均值,然后变换顺序.

为什么要这样进行估值的更新呢?因为这样可以进行增量式的期望更新.如果按照第一步求均值直接来,这个更新的时间复杂度和空间复杂度都为O(n),而现在就是O(1).

2.5 10臂老虎机例子(代码)

下面先简单写一个10臂老虎机的demo,其中每根拉杆的奖励服从伯努利分布,即每次拉杆有的p概率获得的奖励为1,(1-p)概率获得的奖励为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 总结

在本章我们对一个经典问题"多臂老虎机"进行了分析,并将其问题进行数据公式化,得到K臂老虎机问题可以表示为一个二元组<A,R>问题.然后又将其问题核心"求解奖励最大化"变为"最小化累计懊悔"\sigma(a).然后构建了一个伯努利多臂老虎机和多臂老虎机算法的基本框架.在下一章中,我们将会介绍一些多臂老虎机的策略以及实现这些策略.

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值