Bandit算法学习[网站优化]03——Softmax 算法

news2024/11/27 15:33:44

Bandit算法学习[网站优化]03——Softmax 算法

参考资料

  1. White J. Bandit algorithms for website optimization[M]. " O’Reilly Media, Inc.", 2013.
  2. https://github.com/johnmyleswhite/BanditsBook

实验环境:jupyter python 3.7

项目地址:https://github.com/yijunquan-afk/bandit-learning

一、Softmax算法介绍

epsilon-Greedy算法有一个明显的问题:它完全随机地explore选项,而不考虑它们的优点。例如,在一个场景(称为场景A)中,可能有两只臂,其中一只臂有10%的时间奖励我们,另一只臂有13%的时间奖励我们。在场景B中,这两个手臂可能会在10%的时间和99%的时间奖励我们。在这两种情况下,epsilon-Greedy算法explore较差的臂的概率是完全相同的(epsilon/2),尽管相对而言,场景B中的较差臂比场景A中的较差臂差得多。

这对我们来说是一个问题,原因如下:

  • 如果两个臂之间的奖励率差异很小,我们需要花比10%更多的时间去explore,才能正确地确定哪一个实际上更好

  • 相比之下,如果两个臂之间的奖励率差异很大,我们需要explore不到10%的时间来正确估计这两个选项中更好的一个。出于这个原因,在这种情况下,你会因为探索一个毫无疑问的劣势选项而失去很多奖励。当我们第一次描述epsilon-Greedy算法时,我们说不会精确地设置epsilon=1.0,这样就不会把时间浪费在较差的选项上,但是,如果两臂之间的奖励率差异足够大,我们最终也会将时间浪费在较差的选项上,因为epsilon-Greedy算法总是随机地完全explore

因此,我们需要结构化的explore,而不是epsilon-Greedy算法提供的随意的explore

我们将描述的第一个考虑这种结构信息的算法称为Softmax算法。Softmax算法试图通过将有关可用臂的奖励率的信息明确纳入其探索时explore哪个臂的方法中,来处理奖励率不同的臂

可以根据估计值按比例选择每个臂,从而初步了解Softmax算法如何处理此问题。假设有两只臂,A和B。根据你过去的经验,这两只臂有两种不同的成功率: r A rA rA r B rB rB。有了这些假设,类似Softmax算法的最简单的实现:以概率为 r A / ( r A + r B ) rA/(rA+rB) rA/(rA+rB)选择臂 A和以概率为 r B / ( r A + r B ) rB/(rA+rB) rB/(rA+rB)选择臂B。代码编写如下:

def categorical_draw(probs):
    z = random.random()
    cum_prob = 0.0
    for i in range(len(probs)):
        prob = probs[i]
        cum_prob += prob
        if cum_prob > z:
            return i

    return len(probs) - 1


def select_arm(self):
    z = sum(self.values)
    probs = [v / z for v in self.values]
    return categorical_draw(probs)

实际上,上述算法并非实际使用的算法,我们需要对其进行两处更改。

首先,对 r A rA rA r B rB rB的估计进行指数化,计算出一个不同的奖励率比例。以 exp ⁡ ( r A ) / ( exp ⁡ ( r A ) + exp ⁡ ( r B ) ) \exp(rA)/(\exp(rA) + \exp(rB)) exp(rA)/(exp(rA)+exp(rB))的概率选择手臂A,以 exp ⁡ ( r B ) / ( exp ⁡ ( r A ) + exp ⁡ ( r B ) ) \exp(rB)/(\exp(rA) + \exp(rB)) exp(rB)/(exp(rA)+exp(rB))的概率选择手臂B。使用指数重构的优点是,如果使用负数作为成功率也不会影响,因为对 e x p exp exp的调用会把任何负数变成正数,并确保分母中的负数不能抵消分母中的任何正数。

更重要的是,这种指数化的技巧使我们非常接近完整的Softmax算法。事实上,如果你对标准Softmax算法所拥有的可配置参数之一进行硬编码,那么普通的指数重构就能给我们提供Softmax算法。这个额外的参数是与我们刚才介绍的指数化不同的一种缩放因子。这种新类型的比例因子通常被称为温度参数(temperature),这是基于物理学的类比,即系统在高温下往往表现得很随意,而在低温下则表现得更有结构性。事实上,完整的Softmax算法与物理学中一个叫做玻尔兹曼分布的概念密切相关,它被用来描述粒子群的行为方式。

称这个新的温度参数为 tau \text{tau} tau。我们引入 tau \text{tau} tau来产生以下新算法:

  • 在时间T处,选择两个臂之一,其概率计算如下:

    • exp ⁡ ( r A / tau ) / ( exp ⁡ ( r A / tau ) + exp ⁡ ( r B / tau ) ) \exp(rA/\text{tau})/(\exp(rA/\text{tau}) + \exp(rB/\text{tau})) exp(rA/tau)/(exp(rA/tau)+exp(rB/tau))
    • exp ⁡ ( r B / tau ) / ( exp ⁡ ( r A / tau ) + exp ⁡ ( r B / tau ) ) \exp(rB/\text{tau})/(\exp(rA/\text{tau}) + \exp(rB/\text{tau})) exp(rB/tau)/(exp(rA/tau)+exp(rB/tau))
  • 对于选择的任何一条臂,使用我们用于epsilon-Greedy算法的相同更新规则来更新对平均值的估计。

二、Softmax算法的应用

上述算法的实际编码如下:

import math
import random


def categorical_draw(probs):
    """  
    根据probs按比例以一定概率选择臂
    """
    z = random.random()
    cum_prob = 0.0
    for i in range(len(probs)):
        prob = probs[i]
        cum_prob += prob
        if cum_prob > z:
            return i

    return len(probs) - 1


class Softmax:
    def __init__(self, temperature, counts, values):
        self.temperature = temperature
        self.counts = counts
        self.values = values

    def initialize(self, n_arms):
        self.counts = [0 for col in range(n_arms)]
        self.values = [0.0 for col in range(n_arms)]
        return

    def select_arm(self):
        total = sum([math.exp(v / self.temperature) for v in self.values])
        probs = [math.exp(v / self.temperature) / total for v in self.values]
        return categorical_draw(probs)

    def update(self, chosen_arm, reward):
        """更新算法

        Args:
            chosen_arm: 最近选择的arm的索引
            reward: 选择该arm获得的奖励
        """        
        # 选择次数增加
        self.counts[chosen_arm] += 1
        n = self.counts[chosen_arm]

        value = self.values[chosen_arm]
        # 加权平均
        new_value = ((n-1)/float(n))*value + (1/float(n)) * reward
        self.values[chosen_arm] = new_value
        return
    

接下来让我们讨论一下温度参数 tau \text{tau} tau的作用。容易想到的是, tau \text{tau} tau允许我们沿着由两种极端方法来改变Softmax算法的行为。在一个极端,设置 tau = 0.0 \text{tau}=0.0 tau=0.0。这将完全确定地选择具有最高估计值的臂。在另一个极端,设置 tau = i n f \text{tau}=inf tau=inf,进行纯粹的随机探索。 tau \text{tau} tau参数之所以被称为温度参数,是因为它对臂的选择的影响就像传统物理学中的温度对原子的影响:在低温下,原子会有序地行动并产生固体,但在高温下,它们的行为是随机的,会产生气体。和原子一样,Softmax算法在低温下的行为是有序的,而在高温下基本上是随机的

三、衡量Softmax算法的表现

3.1 方法1:跟踪选择最佳arm的概率

image-20230105111108565

由上图可知,等待足够长的时间,Softmax算法开始100%地选择右臂。这比epsilon-Greedy算法有了很大的改进。

3.2 方法2:跟踪每个时间点的平均奖励

import numpy as np
import matplotlib.pyplot as plt

class BernoulliArm():
    def __init__(self, p):
        self.p = p
    
    def draw(self):
        if random.random() > self.p:
            return 0.0
        else:
            return 1.0

def get_cumulative_reward(algo, arms, num_sims, horizon):
    """测试算法框架

    Args:
        algo: 试图测试的bandit算法框架
        arms: 模拟绘制的臂的数组
        num_sims: 模拟的次数
        horizon: 每个算法在每次模拟期间被允许拉动臂的次数

    Returns:
        在每次试验中获得的平均累计奖励
    """
    result = []

    for sim in range(num_sims):
        cumulative_rewards = [0.0 for i in range(horizon)]
        algo.initialize(len(arms))
        for t in range(horizon):
            t = t + 1
            index =  t - 1

            # 选择臂
            chosen_arm = algo.select_arm()

            # 模拟拉动臂的结果
            reward = arms[chosen_arm].draw()

            # 记录算法收到的奖励金,然后调用更新
            if t == 1:
                cumulative_rewards[index] = reward
            else:
                cumulative_rewards[index] = cumulative_rewards[index - 1] + reward

            algo.update(chosen_arm, reward)
        result.append(cumulative_rewards)
    
    # 多次模拟求平均
    average = np.sum(np.array(result),axis=0) / num_sims
    
    return list(average)

def draw_average_reward(arms, temperatures, num_sims=5000, times=250):
    result = []

    for temperature in temperatures:
        algo = Softmax(temperature, [], [])
        algo.initialize(n)
        cumulative_rewards = get_cumulative_reward(algo, arms, num_sims, times)

        average = list(
            map(lambda x: x / (cumulative_rewards.index(x) + 1),
                cumulative_rewards))
        result.append(average)

    i = 0
    for res in result:
        plt.plot(res, label='temperature = {0}'.format(temperatures[i]))
        i += 1

    plt.legend()
    plt.xlabel('Time')
    plt.ylabel('Average Reward')
    plt.title('Performance of the Softmax Algorithm')
    plt.show()


means = [0.1, 0.1, 0.1, 0.1, 0.9]
temperatures = [0.1, 0.2, 0.3, 0.4, 0.5]
n = len(means)
random.shuffle(means)
arms = list(map(lambda mu: BernoulliArm(mu), means))

draw_average_reward(arms, temperatures)

在这里插入图片描述

由平均奖励率图可知,奖励值被限制在0.9而不是1.0,因为测试中最好的arm相关的预期奖励率就为0.9。

3.3 方法3:跟踪每个时间点的累积奖励

def draw_cumulative_reward(arms, temperatures, num_sims=5000, times=250):
    result = []

    for temperature in temperatures:
        algo = Softmax(temperature, [], [])
        algo.initialize(n)
        cumulative_rewards = get_cumulative_reward(algo, arms, num_sims, times)

        result.append(cumulative_rewards)

    i = 0
    for res in result:
        plt.plot(res, label='temperature = {0}'.format(temperatures[i]))
        i += 1

    plt.legend()
    plt.xlabel('Time')
    plt.ylabel('Cumulative Reward')
    plt.title('Performance of the Softmax Algorithm')
    plt.show()

draw_cumulative_reward(arms, temperatures)

在这里插入图片描述

可以看到,累计奖励相较于epsilon-Greedy算法也有所提升。也可以看到,temperature因子设置为0.1比较好。

四、退火Softmax算法

和epsilon-Greedy算法一样,我们同样可以使用退火来优化Softmax算法。

退火的关键代码如下:

t = sum(self.counts) + 1
# 模拟退火
temperature = 1 / math.log(t + 0.0000001)

初始时,temperature被设置的十分大,Softmax算法近乎完全随机explore,但是,随着时间的推进,温度会越来越低。因为我们使用对数,这种下降并不是非常迅速。

具体的代码如下:

import math
import random


def categorical_draw(probs):
    """  
    根据probs按比例以一定概率选择臂
    """
    z = random.random()
    cum_prob = 0.0
    for i in range(len(probs)):
        prob = probs[i]
        cum_prob += prob
        if cum_prob > z:
            return i

    return len(probs) - 1


class AnnealingSoftmax:
    def __init__(self, counts, values):

        self.counts = counts
        self.values = values

    def initialize(self, n_arms):
        self.counts = [0 for col in range(n_arms)]
        self.values = [0.0 for col in range(n_arms)]
        return

    def select_arm(self):
        t = sum(self.counts) + 1
        # 模拟退火
        temperature = 1 / math.log(t + 0.0000001)
        total = sum([math.exp(v / temperature) for v in self.values])
        probs = [math.exp(v / temperature) / total for v in self.values]
        return categorical_draw(probs)

    def update(self, chosen_arm, reward):
        """更新算法

        Args:
            chosen_arm: 最近选择的arm的索引
            reward: 选择该arm获得的奖励
        """
        # 选择次数增加
        self.counts[chosen_arm] += 1
        n = self.counts[chosen_arm]

        value = self.values[chosen_arm]
        # 加权平均
        new_value = ((n - 1) / float(n)) * value + (1 / float(n)) * reward
        self.values[chosen_arm] = new_value
        return

运行结果如下:

# seed( ) 用于指定随机数生成时所用算法开始的整数值,如果使用相同的seed( )值,
# 则每次生成的随即数都相同
random.seed(1)
means = [0.1, 0.1, 0.1, 0.1, 0.9]
n = len(means)
random.shuffle(means)
arms = list(map(lambda mu:BernoulliArm(mu), means))

algo = AnnealingSoftmax([], [])
algo.initialize(n)
cumulative_rewards = get_cumulative_reward(algo, arms, 5000, 250)

plt.plot(cumulative_rewards, label='annealing')


plt.legend()
plt.xlabel('Time')
plt.ylabel('Average Reward')
plt.title('Performance of the Epsilon Greedy Algorithm')
plt.show()

在这里插入图片描述

虽然上图的累计奖励并没有选择temperature=0.1时多,但是我们要知道,我们并不知道最佳的temperature因子应该设置为多少,而是需要一次次的尝试,这在实际的应用中是十分费时的,而退火算法可以帮助我们进一步简化因子选择的过程。

五、进一步探索算法

5.1 不同臂的奖励概率近似时

means = [0.1, 0.1, 0.1, 0.1, 0.12]
n = len(means)
random.shuffle(means)
arms = list(map(lambda mu:BernoulliArm(mu), means))
draw_cumulative_reward(arms, temperatures)

在这里插入图片描述

可以看到,累计奖励近似于单臂的奖励概率 × \times × Time,与epsilon-Greedy算法无显著差异。

5.2 修改退火规则

将退火规则设置为 1 / t 1/t 1/t,运行结果如下:

image-20230105154824165

区别不大。

使前100轮的温度为0.5,然后接下来的150轮的温度为0.1,运行结果如下:

可见有明显的转折。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【实践】百度APP Feed流业务架构变迁思考和升级实践

省时查报告-专业、及时、全面的行研报告库省时查方案-专业、及时、全面的营销策划方案库【免费下载】2022年11月份热门报告盘点罗振宇2023年跨年演讲PPT原稿吴晓波2022年年终秀演讲PPT原稿《底层逻辑》高清配图‍基于深度学习的个性化推荐系统实时化改造与升级.pdf推荐技术在vi…

图像数字识别、数字分割(OCR识别,毕业设计)

基本图像处理流程 这是我在测试图像处理中使用的原始图像。它有一些眩光点,但是图像相当干净。让我们逐步完成获取此源图像的过程,并尝试将其分解为单个数字。 影像准备 在开始图像处理流程之前,我们决定先调整一些图像属性,然后…

【数据结构】LeetCode升级版的环形链表,复制带随机指针的链表

目录 一、升级版的环形链表 1、题目说明 2、题目解析 二、复制带随机指针的链表 1、题目说明 2、题目解析 一、升级版的环形链表 1、题目说明 题目链接:升级版的环形链表 给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环&am…

桌面客户端性能提升,优化使用资源消耗

十二月末,MQTT X 团队发布了 1.9.1-beta.1 版本,这也是 MQTT X 的首个公共测试版。我们希望能够通过测试版本,让更多用户参与到 MQTT X 的测试中来,和我们一起打造一个更加稳定的版本,进而帮助用户轻松使用 MQTT X 完成…

LabVIEW传递接收C/C++DLL指针

LabVIEW传递接收C/CDLL指针传递指针C和C函数通常在其函数原型中接收指针。指针基本上是一个表示内存地址的整数值。要将指向DLL的指针(即值的内存地址)从LabVIEW传递到DLL,必须配置调用库函数节点,以通过引用而不是值传递数据。不…

【Linux】vim文本编辑器的使用

目录 一、为什么要学vim 1.原因 2.简单介绍 3.准备工作 二、vim最小集 1.各模式功能 2.编写代码示例 三、vim指令集 1.命令模式 1.1光标移动 1.2复制(剪切)粘贴 1.3 撤销 1.4 替换 / 删除/大小写切换 2.底行模式 2.1本文件内操作 2.2文件…

KITTI数据集可视化(二):点云多种视图与标注展示的可视化代码解析

如有错误,恳请指出。 文章目录1. 在图像上绘制2d、3d标注框2. 在图像上绘制Lidar投影3. Lidar绘制前视图(FOV)4. Lidar绘制前视图(FOV)3d box5. Lidar绘制鸟瞰图(BEV)6. Lidar绘制鸟瞰图(BEV)2d box7. Lidar绘制全景图(RV)8. Lidar绘制全景图(RV)2d box在对KITTI数据…

立创eda专业版学习笔记(2)(从原理图导入变更失败)

出师不利啊,刚想把用一个原理图生成pcb板就出来这个,第一眼我是有点懵的。后来发现其实是我没搞清楚软件的基本逻辑。 原本,在一个板子的下面有一个原理图,原理图有1页,图标是这个样子 我本来是想新建一个pcb板&#x…

Spring MVC 返回数据

默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图(xxx.html),⽽现在都是前后端分离的,后端只需要返回给前端数据即可,这个时候我们就需要使⽤ResponseBody 注解了。 1.返回静态界面 创建前端页面index.…

spring boot配置多数据源(静态和动态数据源)

背景在开发过程中,很多时候都会有垮数据库操作数据的情况,需要同时配置多套数据源,即多个数据库,保证不同的业务在不同的数据库执行操作,通过mapper来灵活的切换数据源。本文以sqlserver和mysql混合数据源配置为例。配…

美团开放平台SDK自动生成技术与实践

总第549篇2023年 第001篇美团开放平台为整个美团提供了20业务场景的开放API,为了使开发者能够快速且安全的接入美团开放平台,美团开放平台提供了多种语言的SDK来提高开发者的接入效率。本文介绍了美团开放平台如何自动生成SDK代码的相关技术实现方案&…

【学习】深度强化学习、模型压缩

文章目录一、deep reinforcement learningPolicy-based Approach——Learning an Actor作为actor的神经网络small model网络可以被修剪一、deep reinforcement learning 强化学习场景 监督学习和强化学习之间: 训练一个聊天机器人-强化学习:让两个代…

基于c语言tftp服务器与客户端实现

开发环境:ubuntu 所用知识点:c,socket, tcp/ip协议 A)本实验主要实现tftp协议的服务器与客户端。 服务器实现功能有: 1)接收处理客户端请求,上传下下载文件 2)进行用户验证 3)对传输数据进行加密解密处理 4)生成日志文…

TensorRT学习笔记--Ubuntu20.04安装TensorRT 8.2.5

目录 前言 1--查看本机环境配置 2--下载并安装Tensor RT 3--实例测试 3-1--验证Onnx模型的可用性 3-2--将Onnx模型转换为推理引擎engine 3-3--基于Tensor RT使用engine模型进行推理 4--参考 前言 推荐结合官方文档 3.2.3节中的Tar File Installation安装教程进行安装&a…

【docker09】镜像发布到docker私有库

镜像发布到docker私有库 1.Docker Registry 官方Docker Hub地址:https://hub.docker.com/,中国大陆访问太慢,并且具有被阿里云取代的趋势,不太主流Dockerhub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像…

PDF如何转换成excel文档?这个方法很实用

PDF如何转换成excel文档?PDF文件是我们经常使用的文件之一,我们在很多工作场景都能接触到PDF文件,不过PDF文件并不能适用于各种情况,比如我们想对文件内的数据进行更改,我们就需要把PDF文件转换成excel表格再进行修改&…

js 生成条形码

简介&#xff1a; 通过js生成条形码 效果展示&#xff1a; 示例代码&#xff1a; <!-- Created by IntelliJ IDEA. User: songsir Date: 2018/11/26 Time: 10:49 --> <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8&…

分享一下我3个月收割大厂offer的一些经验总结

前几天&#xff0c;有位老粉私信我&#xff0c;说看到某95后学弟晒出阿里P7的工资单&#xff0c;他是真酸了…想狠补下技术&#xff0c;努力冲一把大厂。 为了帮到他&#xff0c;也为了大家能在最短的时间内做面试复习&#xff0c;我把软件测试面试系列都汇总在这一篇文章了。…

对抗搜索 学习笔记

先来看一道有意思题 situation 大意&#xff1a; 两个人玩井字棋&#xff0c;要求把所有位置填满后结束游戏。一方希望两者的连起来的线之差最多&#xff0c;一方希望最少。现在给定初始局面&#xff08;已经存在一些棋子&#xff09;以及先手&#xff0c;求最后的两者的连起…

SAP FICO预制凭证界面隐藏过账按钮

会计凭证一旦过账了就不能再进行修改&#xff0c;但其也提供了类似国内财务软件同样的预制功能&#xff0c;预制凭证过账之前不会更新会计系统。预制凭证虽然不更新科目余额&#xff0c;但同样会生成凭证编号&#xff0c;其凭证内容可以随意更改&#xff0c;也可以删除。一旦过…