多臂老虎机

多臂老虎机

有n根拉杆的的老虎机,每根拉杆获得奖励(值为1)的概率各不相同。

期望奖励更新
Q k = 1 k ∑ i = 1 k r i = 1 k ( r k + ∑ i = 1 k − 1 r i ) = 1 k ( r k + k Q k − 1 − Q k − 1 ) = Q k − 1 + 1 k [ r k − Q k − 1 ] Q_k=\frac 1k \sum^{k}_{i=1}r_i\\ =\frac 1k (r_k+\sum^{k-1}_{i=1}r_i)\\ =\frac 1k( r_k+kQ_{k-1}-Q_{k-1})\\ =Q_{k-1}+\frac 1k[r_k-Q_{k-1}] Qk=k1i=1kri=k1(rk+i=1k1ri)=k1(rk+kQk1Qk1)=Qk1+k1[rkQk1]
累计懊悔:

​ 至少存在一根拉杆,它的期望奖励不小于拉动其他任意一根拉杆,设最优期望为 Q ∗ = m a x a ∈ A Q ( a ) Q^*=max_{a\in A}Q(a) Q=maxaAQ(a),在此基础上,引入懊悔概念,被定义为当前的收益与最优期望的插值,即 R ( a ) = Q ∗ − Q ( a ) R(a)=Q^*-Q(a) R(a)=QQ(a),累计懊悔 σ R = ∑ t = 1 T R ( a t ) \sigma_R=\sum^T_{t=1}R(a_t) σR=t=1TR(at),则多臂老虎机可等价为最小化累积期望。

基本框架

多臂老虎机的基本框架如下

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的数作为概率,获奖积1分,没获奖0分
        self.best_idx = np.argmax(self.probs)  # 获奖概率最大的拉杆
        self.best_prob = self.probs[self.best_idx]  # 最大获奖概率
        self.K = K

    def step(self, K):
        # 进行一步,根据选择的第k号拉杆获奖的概率进行返回
        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))


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  # ?问下gpt

    def run(self, num_steps):
        # 运行一定次数
        for _ in range(num_steps):
            k = self.run_one_step()
            self.counts[k] += 1
            self.actions.append(k)
            self.actions.append(k)
            self.update_regret(k)


def plot_results(solvers, solver_names):
    # 输入的solvers是一个列表,每个元素是一个特定策略,solver_names也是一个列表,存储每个策略的名称
    for idx, solver in enumerate(solvers):
"""enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般在循环中使用。此处将索引存在idx,具体的策略存在solver中"""
        time_list = range(len(solver.regrets))  # 生成一个包含从 0 到 solver.regrets 的大小减去 1 的整数序列。
        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()  # 用于添加图例到 Matplotlib 图表中的函数,即使用label标签标记的。
    plt.show()

​ 对于多臂老虎机的决策,有如下方法

ϵ \epsilon ϵ-贪婪算法

​ 这个算法设置一个随机概率 ϵ \epsilon ϵ,每次做决策时,有 1 − ϵ 1-\epsilon 1ϵ的概率按已有经验做出最优决策,在这个问题中,即在拉动过的杆中选出最优的,以概率 ϵ \epsilon ϵ随机选择一根拉杆。

​ 本例中先选择 ϵ = 0.01 , T = 5000 \epsilon =0.01,T=5000 ϵ=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)  # 调用父类的构造函数,用bandit作为参数
        self.epsilon = epsilon
        self.estimates = np.array([init_prob] * self.bandit.K)  # 初始化所有拉杆的期望奖励估值,设置为一样的?

    def run_one_step(self):
        # 以贪婪算法运行一步
        if np.random.rand() < 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  # 返回选择的杆的编号
      
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 ϵ随时间逐渐变小。即 ϵ t = 1 t \epsilon_t = \frac 1t ϵt=t1,一般使用随时间衰减的贪婪算法:

class DecayingEpsilonGreedy(Solver):
    """epsilon值随时间衰减的贪婪算法,需要继承Solver类"""

    def __init__(self, bandit, init_prob=1.0):
        super(DecayingEpsilonGreedy, self).__init__(bandit)  # 调用父类的构造函数,用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.rand() < 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(1)
decaysing_epsilon_greedy_solver = DecayingEpsilonGreedy(bandit_10_arm)
decaysing_epsilon_greedy_solver.run(5000)
print('epsilon值衰减的-贪婪算法的累计懊悔为:', decaysing_epsilon_greedy_solver.regret)
plot_results([decaysing_epsilon_greedy_solver], ["DecayingEpsilonGreedy"])

在这里插入图片描述

上置信界算法(UCB)

​ 在探索时,我们总会有这样的一种想法,如果第1根拉杆只被拉动过1次,但奖励为0,第2根被拉动过很多次,我们对它的奖励分布已经有了比较确定的把握,或许我们更愿意尝试拉动第一根拉杆,可能他的收益更高。为了定量的描述这种“潜藏“的更优解,引入了不确定性度量 U ( a ) U(a) U(a),它会随着一个动作被尝试次数的增加而减小。

​ 那么我们可以使用一种基于不确定性的策略来综合考虑现有的期望奖励估值和不确定性,其核心问题是如何估计不确定性。

霍夫丁不等式

​ 令 X 1 , ⋯   , X n X_1,\cdots,X_n X1,,Xn n n n个独立同分布的随机变量,取值范围为 [ 0 , 1 ] [0,1] [0,1],若经验期望为 x ‾ = 1 x ∑ j = 1 n X j \overline{x}=\frac 1x\sum^n_{j=1}X_j x=x1j=1nXj,则有
P ( E [ X ] ≥ x ‾ t + u ) ≤ e − 2 n u 2 P(E[X]\ge \overline{x}_t +u)\le e^{-2nu^2} P(E[X]xt+u)e2nu2

期望的奖励上界

​ 在这个例子中,经验期望就是 Q ^ ( a t ) \hat{Q}(a_t) Q^(at),将 u = U ^ ( a t ) u=\hat{U}(a_t) u=U^(at)代表不确定性度量,给定一个概率 p = e − 2 N ( a t ) U ( a t ) 2 p=e^{-2N(a_t)U(a_t)^2} p=e2N(at)U(at)2,其中 N ( a t ) N(a_t) N(at)表示拉动某一编号杆的次数。根据霍夫丁不等式, Q ( a t ) < Q ^ ( a t ) + U ^ ( a t ) Q(a_t)<\hat{Q}(a_t)+\hat{U}(a_t) Q(at)<Q^(at)+U^(at)至少以概率 1 − p 1-p 1p成立,当 p p p很小时,以很大概率成立,那么我们可以认为 Q ^ ( a t ) + U ^ ( a t ) \hat{Q}(a_t)+\hat{U}(a_t) Q^(at)+U^(at)便是期望的奖励上界。

​ 此时,UCB算法选取上界最大的动作,即 a t = a r g   m a x a ∈ A [ Q ^ ( a ) + U ^ ( a ) ] a_t = arg\ max_{a\in A}[\hat{Q}(a)+\hat{U}(a)] at=arg maxaA[Q^(a)+U^(a)]

​ 其中 U ^ ( a t ) = − l o g   p 2 N ( a t ) \hat{U}(a_t)=\sqrt{\cfrac{-log\ p}{2N(a_t)}} U^(at)=2N(at)log p ,也就是说,设定一个概率 p p p后,就可计算了。

​ 总的来说,UCB算法在每次决策前,都会估计每根杆的期望上界,使得拉动每根杆的期望奖励只有一个较小的概率 p p p超过上界,并选取最优可能获得最大期望奖励的拉杆。

UCB算法的代码实现

​ 容易发现的是,随着时间的增长,我们将对期望有着越来越确定的把握,那么 p p p的设置也应该是随时间增长减少的,则设 p = 1 t p=\frac 1t p=t1

​ 对于 U ^ ( a t ) \hat{U}(a_t) U^(at),我们在分母加上常数1,避免出现分母为0的情况,则 U ^ ( a t ) = l o g t 2 ( N ( a t ) + 1 ) \hat{U}(a_t)=\sqrt{\frac{log t}{2(N(a_t)+1)}} U^(at)=2(N(at)+1)logt

​ 为了控制不确定性的比重,引入系数 c c c,此时, a t = a r g   m a x a ∈ A [ Q ^ ( a ) + c ⋅ U ^ ( a ) ] a_t = arg\ max_{a\in A}[\hat{Q}(a)+c\cdot \hat{U}(a)] at=arg maxaA[Q^(a)+cU^(a)]

class UCB(Solver):
    # UCB算法,继承Solver类
    def __init__(self, bandit, coef, init_prob=1.0):
        super(UCB, self).__init__(bandit)
        self.total_count = 0  # 时间计数t
        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)))  # 计算上置信界
        # np.log()默认以e为底,以其他为底可使用换底公式
        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 = 0.7  # 不确定性权重的参数
UCB_solver = UCB(bandit_10_arm, coef)
UCB_solver.run(5000)
print("上置信界算法的累积懊悔为:", UCB_solver.regret)
plot_results([UCB_solver], ["UCB"])

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/626619.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

oracle10g dbca和netca报错

oracle10g dbca和netca报错 [oraclecqnew database]$ netcaOracle Net Services Configuration: Warning: Cannot convert string "-b&h-lucida-medium-r-normal-sans-*-140-*-*-p-*-iso8859-1" to type FontStruct Configuring Listener:LISTENER不影响使用&am…

一键监控多台服务器磁盘使用情况的神奇脚本!

在当今这个数据为王的时代&#xff0c;服务器的磁盘空间使用情况成为了系统管理员日常关注的重要指标之一。磁盘空间不足可能导致服务中断&#xff0c;数据丢失&#xff0c;甚至整个系统崩溃。因此&#xff0c;及时监控磁盘空间&#xff0c;预防潜在风险&#xff0c;成为了每个…

上下左右翻转照片以及标注信息扩充数据集

目录 前言&#xff1a; 示例项目数据结构&#xff1a; 源代码&#xff1a; 运行代码后生成的项目结构&#xff1a; 效果&#xff1a; 前言&#xff1a; 使用yolo训练模型时&#xff0c;遇到数据集很小的情况&#xff08;一两百张&#xff09;&#xff0c;训练出来的模型效…

2024年电工杯数学建模B题思路 中国电机工程学会杯建模思路分析

文章目录 1 赛题思路2 比赛日期和时间3 竞赛信息4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

四天学会JS高阶(学好vue的关键)——作用域解构箭头函数(理论+实战)(第一天)

一、作用域 提到作用域&#xff08;作用域又分为局部作用域和全局作用域&#xff09;&#xff0c;就要想到变量。因为作用域规定了变量能够被访问的范围&#xff08;也就是作用域是为变量而服务的&#xff09;&#xff0c;为了避免全局变量污染这一情况&#xff0c;所以需要使…

关闭 Visual Studio Code 项目中 的eslint的语法校验 lintOnSave: false;; 项目运行起来之后 自动打开浏览器 端口

1、在 vue.config.js 配置 一个属性 lintOnSave: false 2、配置两个属性 open: true, // 自动打开浏览器 port: 3000 // 端口 port 端口号根据自己的项目实际开发来 配置

C++类细节,反汇编,面试题02

文章目录 2. 虚函数vs纯虚函数3. 重写vs重载vs隐藏3.1. 为什么C可以重载&#xff1f; 4. struct vs union4.1. 为什么要内存对齐&#xff1f; 5. static作用6. 空类vs空结构体6.1. 八个默认函数&#xff1a;6.2. 为什么空类占用1字节 7. const作用7.1 指针常量vs常量指针vs常量…

用友网络的危与机:2023年亏损约10亿元,王文京面临严肃拷问

“企业在新的产业浪潮来临时&#xff0c;应该主动推进新阶段的产品和业务创新&#xff0c;这样才能够在新的浪潮成为主流的时候&#xff0c;走到行业前面&#xff0c;否则就会从产业发展的潮流中掉下来”。用友网络创始人王文京&#xff0c;曾用“冲浪理论”形容一家企业成功的…

国内好用的测试用例管理工具有哪些?

目前市面上的测试用例管理工具有很多&#xff0c;但由于针对的项目、领域、目标用户&#xff0c;功能也并不一致&#xff0c;所以选择一款适合的测试管理平台并不轻松。做好这件事&#xff0c;首先要需求明确你用测试管理工具干什么&#xff1f;最终想要达到什么目标&#xff1…

C语言学习(八)typedef 虚拟内存 malloc/free

目录 一、typedef 类型重定义&#xff08;一&#xff09;使用&#xff08;二&#xff09;define和typedef的区别1. 编译处理的阶段不同2. 功能不同 二、虚拟内存&#xff08;一&#xff09;虚拟内存分布&#xff08;二&#xff09;内存分布1. 静态分配2. 动态分配 三、malloc/f…

用sunoAI写粤语歌的方法,博主已经亲自实践可行

粤语歌还是很好听的&#xff0c;那么如何使用suno进行粤语歌的创作呢&#xff1f; 本文和大家进行分享下如何进行粤语歌曲的创作。 访问地址如下&#xff08;电脑端/手机端一个地址&#xff09;&#xff1a; ​https://suno3.cn/#/?i8NCBS8_WXTT 在微信浏览器中也可以直接…

RT-DETR改进教程|加入SCNet中的SCConv[CVPR2020]自校准卷积模块!

⭐⭐ RT-DETR改进专栏|包含主干、模块、注意力机制、检测头等前沿创新 ⭐⭐ 一、 论文介绍 论文链接&#xff1a;http://mftp.mmcheng.net/Papers/20cvprSCNet.pdf 代码链接&#xff1a;https://gitcode.com/MCG-NKU/SCNet/ 文章摘要&#xff1a; CNN的最新进展主要致力于设计更…

文字转成活码的3步操作,手机扫码即可查看文本信息

现在经常会通过二维码的方式来传递通知的文字信息&#xff0c;只需要分享文字生成二维码的图片到微信群或者印刷出来&#xff0c;其他人就可以通过扫码来查看文字内容&#xff0c;有利于其他人更快速的获取信息。 目前文本静态码无法通过微信来扫码展示&#xff0c;那么想要解…

服务器被后台爆破怎么处理

服务器后台遭受密码爆破攻击是网络安全中常见的威胁之一&#xff0c;这种攻击通过不断尝试不同的用户名和密码组合来破解系统登录凭证&#xff0c;一旦成功&#xff0c;攻击者便能非法访问系统资源。 本文将介绍如何识别此类攻击&#xff0c;并采取有效措施进行防御&#xff0c…

ETL-kettle数据转换及组件使用详解

目录 一、txt文本转换成excel 1、新建、转换 2、构建流程图 3、配置数据流图中的各个组件 3.1、配置文件文本输入组件 3.2、 配置Excel输出组件 4、保存执行 二、excel转换成mysql &#xff08;1&#xff09;在MySQL数据库中创建数据库&#xff0c;这个根据自身情况。我…

(python)cryptography-安全的加密

前言 cryptography 是一个广泛使用的 Python 加密库&#xff0c;提供了各种加密、哈希和签名算法的实现。它支持多种加密算法&#xff0c;如 AES、RSA、ECC 等&#xff0c;以及哈希函数&#xff08;如 SHA-256、SHA-384 等&#xff09;和数字签名算法(如 DSA、ECDSA 等). 目录 …

一本书打通SLAM在智能汽车/自动驾驶领域应用

自动驾驶技术已成为当今数字化时代汽车行业的热点话题之一。随着技术的不断成熟&#xff0c;越来越多的车辆采用激光SLAM&#xff08;即时定位与地图构建&#xff09;和视觉SLAM技术&#xff0c;实现更高层次的智能网联汽车。SLAM技术在智能网联汽车中的应用是非常重要的&#…

[XYCTF新生赛]-PWN:baby_gift解析(函数调用前需清空eax)

查看保护 查看ida 这里有一处栈溢出&#xff0c;并且从汇编上看&#xff0c;程序将rbp0x20处设置为了rdi&#xff0c;让我们可以控制rdi的值。而程序没有可利用的pop。 完整exp&#xff1a; from pwn import* pprocess(./babygift) premote(gz.imxbt.cn,20833) printf_plt0x4…

如何通过ETL工具对数据进行去重

在数据处理流程中&#xff0c;数据去重是一个至关重要的环节&#xff0c;它能够确保数据分析的准确性和效率。ETL&#xff08;Extract, Transform, Load&#xff09;工具作为数据集成的重要组成部分&#xff0c;提供了强大的功能来帮助用户实现数据的抽取、转换和加载&#xff…

【Unity Shader入门精要 第7章】基础纹理(一)

1. 纹理映射 每一张纹理可以看作拥有一个属于自己的2D坐标空间&#xff0c;其横轴用U表示&#xff0c;纵轴用V表示&#xff0c;因此也称为UV坐标空间。 UV空间的坐标范围为[0&#xff0c;0]到[1&#xff0c;1]&#xff0c;在Unity中&#xff0c;UV空间也是从左下到右上&#…