强化学习编程实战-1-一个及其简单的强化学习实例(多臂赌博机)

news2025/2/25 6:00:39

1.1 多臂赌博机

        一台拥有K个臂的机器,玩家每次可以摇动K个臂中的一个,摇动后,会吐出数量不等的金币,吐出金币的数量服从一定的概率分布,而且不同臂的概率分布不同。

        多臂赌博机的问题是:假设玩家共有N次摇地摇臂的机会,每次如何选择摇动才能使N轮之后得到的金币最多?

        对于这个问题,如果提前知道哪个臂吐的金币最多,那么可以每次都摇动那个臂。但是,问题使并不知道那个臂能获得最多金币?该采取什么策略?

 

1.1.1 eq?%5Cvarepsilon-greedy策略

        一个很直观的想法:既然不知道哪个臂吐的金币最多,那么可以先对每个臂都尝试几次(如都10次),找出那个臂吐出的金币最多,然后一直摇动它。

        其实这个最简单、最朴素的想法已经蕴含了算法学习最基本的两个过程:采集数据和学习。首先,对每个臂进行尝试就是采集数据,其次,学习就是利用这些数据知道哪个臂回吐出最多的金币。一个最简单的方法就是计算每个臂的平均吐钱数量,然后,一直摇那个臂。

        我们可以将这个算法用形式化的代数来表示。用s表示当前赌博机,用A表示可以选择的动作,即A={1,2,3}。其中a=1表示摇动第一个臂,依次类推。用回报eq?%5Cgamma表示摇动赌博机的摇臂后获得的金币的数目。用Q(a)表示摇动动作a获得的金币的平均回报,则eq?Q%28a%29%3D%5Cfrac%7B1%7D%7Bn%7D%5Csum_%7Bi%3D1%7D%5E%7Bn%7D%5Cgamma%20_%7Bi%7D,其中n为摇动动作的总次数。R(a)为摇动动作a的总回报。

        算法的伪代码

00b3aa0e16254d5eb84a42592496d306.png

1-3行:初始化每个动作的总回报R(a),以及摇动该动作的次数N(a)。

4-6行:每个臂都尝试eq?n_%7Ba%7D次,计算每个摇臂总的金币数。

7-9行:算出使得总回报最大的那个臂,然后一直摇动它。

这是一个简单、朴素的想法,但并不是一个好的算法。原因如下:缺点
(1)不应该以总回报最大为目的来选择当前哪个臂,而是应该选择当前平均回报最大的臂

(2)不应该只摇动当前平均回报最大的臂,因为它不一定使最好的那个臂。所以,我们除了关注当前平均回报最大的臂,还要保留一定的概率去摇动其他的臂,以便发现更好的臂。

以上两点对应着强化学习算法中最重要的概念:利用策略和探索策略平衡。

利用:exploitation,是利用当前的数据总结得到的最好的策略,采用该策略,我们可以得到较高的回报。

探索:exploration,该策略可以帮我们找到更好的臂,甚至找到最优的臂。

        强化学习算法在训练的过程中所用到的策略是平衡了利用和探索的策略,最常见的是eq?%5Cvarepsilon-greedy策略,公式表示:

0d853fb12db340eba68275ffa943a53f.png

        该策略,在每次选择摇动哪个臂时,应该以1-eq?%5Cvarepsilon的概率去摇动当前平均值最大的臂,以eq?%5Cvarepsilon的概率在所有动作中均匀随机地选择动作。这样可以在有限的次数中得到尽可能多的回报,同时不失去找到最好的臂的机会。

1d5a821d6895478c9aa3d02b25bef4a2.png

第1-4行:初始化总回报R(a),初始化每个动作的平均回报Q(a),每个动作的次数N(a).

第6行:在每次摇臂之前,采用eq?%5Cvarepsilon-greedy策略,选择要摇动的臂a。

第7行:动作a的次数N(a)+1.

第8行:根据动作a和环境返回的回报eq?%5Cgamma_%7B%7D(a),更新动作a的平均回报。

第9行:计算总的收益。

第10行:玩家尝试N次之后,返回总的收益。

在多臂赌博机中,平衡利用和探索的策略还有玻尔兹曼策略和UCB策略。

 

1.1.2 玻尔兹曼策略

        e-greedy策略给对应值函数最大的那个动作一个比较大的概率,而其他动作,不管值函数的大小如何,被采样的概率都是相等。

        但这中概率的分配策略不太合理。按理说,非贪婪的动作也有好坏之分,那些对应值函数大的动作应该比那些对应值函数小的动作采样的概率大。于是玻尔兹曼策略根据值函数对动作的采样的概率进行了软处理,表示下式1-2为

d60a9ffda8cf42cbb36781d92faeb299.png

其中eq?%5Ctau为温度调节参数,可以用来调节探索和利用和比例。eq?%5Ctaueq?%5Ctau越小,玻尔兹曼越接近贪婪策略,利用所占的比例越大,探索越少。反之,探索越多。伪代码只需替换图1-3中的e-greedy策略为公式1-2.

 

1.1.3 UCB策略

UCB(Upper Confidence Boundn)置信上限。在统计学中常常用置信区间来表示不确定性。在这里,我们用置信区间来表示探索。

UCB策略公式

4edfe00e78e8460b9e819fbd557c3fd3.png

其中t为当前摇臂动作的总次数,N(a)为动作a的总次数。

下图为3种策略,总回报和摇动次数之后的关系。e-greedy策略回报最低,但却是形式最简单、最通用,可广泛用于各种任务的学习和探索训练中。

74bc4083f35b46ab9a875f9c7654e892.png

 

1.2 多臂赌博机代码实现

基于上文提到的三种学习策略。

①首先,我们先创建一个KB_Game类

②在类KB_Game中定义方法step(),用于模拟多臂赌博机如何给出回报。该方法的输入为动作,输出为回报。用正态分布来模拟玩家在每次摇动摇臂后得到的回报。step()方法实际上提供了多臂赌博机的模拟器。

③接下来,实现3种选择动作的策略方法choose_action().该方法的输入参数为测量类别policy,有3种,对应上文的三种策略e_greedy,ucb和boltzmann.另外还有一个参数字典**kwargs,对应传递相应的策略的所对应超参数,如e-greedy策略中的epsilon,UCB策略中的超参数c_ratio,以及玻尔兹曼中的温度‘temperature'.UCB算法的每一步是依次摇动每个臂,因此在程序中对应的代码为判断每个动作的次数,如果有等于零的,那么选择该动作。

④有了模拟器和动作选择策略,下面就可以通过交互进行学习训练。定义方法train().该方法的输入参数有play_total(表示训练的总次数),policy(训练的策略),**kwargs(相应策略的超参数字典).

⑤智能体通过学习的策略选择动作,然后将动作传给step()方法,相当于跟多臂赌博机进行了一次交互,从多臂赌博机中获得回报r,智能体根据立即回报更新每个动作的平均回报q,计算当前的累计回报并作相应的保存。

⑥在每次训练新的策略的时候,我们需要将类KB_Game中的成员变量进行重置,定义reset()方法进行重置,重置的变量有平均回报q,各动作的次数action_counts,当前的累积回报current_cumulative_reward,玩家尝试的次数counts,玩家尝试的历史counts_history、玩家累积回报的历史cumulative_rewards_history、动作a、回报reward。

 ⑦为了更直观比较3种不同策略的学习性能,需要画图展示,我们用方法plot()来实现。得到如下的结果。显然ucb比其他两个策略要好。

23a19cf0d2894248855a1a19c7c2c415.png

代码如下

import numpy as np
import matplotlib.pyplot as plt
class KB_Game:
    def __init__(self,*args,**kwargs):
        self.q=np.array([0.0,0.0,0.0])          #每个臂的平均回报
        self.action_counts=np.array([0,0,0])    #摇动每个臂的次数
        self.current_cumulative_rewards=0.0     #当前累积回报总和
        self.actions=[1,2,3]                    #3个不同的摇臂
        self.counts=0                           #玩家玩游戏的次数
        self.counts_history=[]                  #玩家而玩游戏的次数记录
        self.cumulative_rewards_history=[] #累积回报的记录
        self.a=1                  #玩家当前动作,初始化为1,摇第一个摇臂
        self.reward=0             #当前回报,初始值为0

    def step(self,a):#模拟器
        r=0
        if a==1:
            r=np.random.normal(1,1)     #正态分布,均值为1,方差为1
        if a==2:
            r=np.random.normal(2,1)
        if a==3:
            r=np.random.normal(1.5,1)
        return r
    
    def choose_action(self,policy,**kwargs):  #动作策略
        action=0
        if policy=='e_greedy':
            if np.random.random()<kwargs['epsilon']:
                action=np.random.randint(1,4)
            else:
                action=np.argmax(self.q)+1
        if policy=='ucb':
            c_ratio=kwargs['c_ratio']
            if 0 in self.action_counts:
                action=np.where(self.action_counts==0)[0][0]+1
            else:
                value=self.q+c_ratio*np.sqrt(np.log(self.counts)/self.action_counts)
                action=np.argmax(value)+1
        if policy=='boltzmann':
            tau=kwargs['temperature']
            p=np.exp(self.q/tau)/(np.sum(np.exp(self.q/tau)))
            action=np.random.choice([1,2,3],p=p.ravel())
        return action
    
    def train(self,play_total,policy,**kwargs):
        reward_1=[]
        reward_2=[]
        reward_3=[]
        for i in range(play_total):
            action=0
            if policy=='e_greedy':
                action=self.choose_action(policy,epsilon=kwargs['epsilon'])
            if policy=='ucb':
                action=self.choose_action(policy,c_ratio=kwargs['c_ratio'])
            if policy=='boltzmann':
                action=self.choose_action(policy,temperature=kwargs['temperature'])
            self.a=action
            #print(self.a)
            #与环境交互一次
            self.r=self.step(self.a)
            self.counts+=1
            #更新值函数
            self.q[self.a-1]=(self.q[self.a-1]*self.action_counts[self.a-1]+self.r)/(self.action_counts[self.a-1]+1)
            self.action_counts[self.a-1]+=1
            reward_1.append([self.q[0]])
            reward_2.append([self.q[1]])
            reward_3.append([self.q[2]])
            self.current_cumulative_rewards+=self.r
            self.cumulative_rewards_history.append(self.current_cumulative_rewards)
            self.counts_history.append(i)
            
    def reset(self):
        self.q=np.array([0.0,0.0,0.0])          #每个臂的平均回报
        self.action_counts=np.array([0,0,0])    #摇动每个臂的次数
        self.current_cumulative_rewards=0.0     #当前累积回报总和
        self.counts=0                           #玩家玩游戏的次数
        self.counts_history=[]                  #玩家而玩游戏的次数记录
        self.cumulative_rewards_history=[] #累积回报的记录
        self.a=1                  #玩家当前动作,初始化为1,摇第一个摇臂
        self.reward=0             #当前回报,初始值为0
    
    def plot(self,colors,policy,style):
        plt.figure(1)       #创建画布
        plt.plot(self.counts_history,self.cumulative_rewards_history,colors,label=policy)
        plt.legend() #加上图列
        plt.xlabel('n',fontsize=18)
        plt.ylabel('total rewards',fontsize=18)
        

if __name__=='__main__':
    np.random.seed(0)
    k_gamble=KB_Game()
    total=2000
    k_gamble.train(play_total=total,policy='e_greedy',epsilon=0.05)
    k_gamble.plot(colors='r',policy='e_greedy',style='-.')
    k_gamble.reset()
    k_gamble.train(play_total=total,policy='boltzmann',temperature=1)
    k_gamble.plot(colors='b',policy='boltzmann',style='--')
    k_gamble.reset()
    k_gamble.train(play_total=total,policy='ucb',c_ratio=0.5)
    k_gamble.plot(colors='g',policy='ucb',style='-')
    plt.show()



 

 

 

 

 

 

 

 

 

        

 

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

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

相关文章

2024上半年网络工程师考试《应用技术》试题二

试题二(20分) 阅读以下说明,回答问题,将解答填入对应的解答栏内。 某单位网络拓扑如下图所示.SW1、SW2为核心层交换机&#xff0c;PC网关配置在核心层&#xff0c;SW3-SW4为接入层交换机,行政部PC划为vlan10,销售部PC划为vlan20。 【问题1】(4分) 要求实现骨干链路冗余&…

golang线程池ants-实现架构

1、总体架构 ants协程池&#xff0c;在使用上有多种方式(使用方式参考这篇文章&#xff1a;golang线程池ants-四种使用方法)&#xff0c;但是在实现的核心就一个&#xff0c;如下架构图&#xff1a; 总的来说&#xff0c;就是三个数据结构&#xff1a; Pool、WorkerStack、goW…

Matplotlib Artist Axes

在简介里介绍了很多了&#xff0c;这里补充一点 Axes包含一个属性patch&#xff0c;是Axes对应的方框&#xff0c;可以用来设置Axes的相关属性 ax fig.add_subplot() rect ax.patch # a Rectangle instance rect.set_facecolor(green) Axes有以下方法 Axes helper metho…

五、保存数据到Excel、sqlite(爬虫及数据可视化)

五、保存数据到Excel、sqlite&#xff08;爬虫及数据可视化&#xff09; 1&#xff0c;保存数据到excel1.1 保存九九乘法表到excel&#xff08;1&#xff09;代码testXwlt.py&#xff08;2&#xff09;excel保存结果 1.2 爬取电影详情并保存到excel&#xff08;1&#xff09;代…

Java之网络面试经典题(一)

目录 ​编辑 一.Session和cookie Cookie Session 二.HTTP和HTTPS的区别 三.浅谈HTTPS为什么是安全的&#xff1f; 四.TCP和UDP 五.GET和Post的区别 六.forward 和 redirect 的区别&#xff1f; 本专栏全是博主自己收集的面试题&#xff0c;仅可参考&#xff0c;不能相…

数字信号处理及MATLAB仿真(3)——采样与量化

今天写主要来编的程序就是咱们AD变换的两个步骤。一个是采样&#xff0c;还有一个是量化。大家可以先看看&#xff0c;这一过程当中的信号是如何变化的。信号的变换图如下。 先说说采样&#xff0c;采样是将连续时间信号转换为离散时间信号的过程。在采样过程中&#xff0c;连续…

工作两年后,我如何看待设计模式

在软件工程中&#xff0c;设计模式是经过反复验证的最佳实践&#xff0c;用于解决在软件设计中经常遇到的一类问题。它们为开发者提供了一种通用的解决方案和语言&#xff0c;使得复杂的编程问题得以简化&#xff0c;代码结构更加清晰&#xff0c;可维护性大大提高。简而言之&a…

FreeRTOS的任务间通信

文章目录 4 FreeRTOS任务间通信4.1 队列4.1.1 队列的使用4.1.2 队列的创建&#xff0c;删除&#xff0c;复位4.1.3 队列的发送&#xff0c;接收&#xff0c;查询 4.2 邮箱&#xff08;mailbox&#xff09;4.2.1 任务中读写邮箱4.2.2 中断中读写邮箱 4.3 队列集4.3.1 队列集的创…

使用块的网络 VGG

一、AlexNet与VGG 1、深度学习追求更深更大&#xff0c;使用VGG将卷积层组合为块 2、VGG块&#xff1a;3*3卷积&#xff08;pad1&#xff0c;n层&#xff0c;m通道&#xff09;、2*2最大池化层 二、VGG架构 1、多个VGG块后接全连接层 2、不同次数的重复块得到不同的架构&a…

系统集成项目管理工程师第12章思维导图发布

今天发布系统集成项目管理工程师新版第12章脑图的图片版

01背包问题-队列分支限界法-C++

0-1背包问题-队列分支限界法 问题描述&#xff1a; 给定n种物品和一个背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包中的物品&#xff0c;使得装入背包中物品的总价值最大&#xff1f;对于给定的n种物品的重量和价值&#xff0c;以及背包的容量&…

如何选择一家适合自己的商城源码?

商城源码的选择取决于多个因素&#xff0c;包括商城的功能需求、稳定性、易用性、可定制性以及价格等。启山智软作为在市场上被广泛认可且表现优异的商城源码提供商&#xff0c;具有以下的特点和优势&#xff1a; 特点①&#xff1a;国内知名的B2B2C开源商城源码系统&#xff…

Go语言--工程管理、临时/永久设置GOPATH、main函数以及init函数

工作区 Go 代码必须放在工作区中。工作区其实就是一个对应于特定工程的目录&#xff0c;它应包含3个子目录:src 目录、pkg目录和bin 目录。 src 目录:用于以代码包的形式组织并保存 Go源码文件。(比如:.go.chs等)pkg 目录:用于存放经由 go install 命令构建安装后的代码包(包…

东芝TB6560AHQ/AFG步进电机驱动IC:解锁卓越的电机控制性能

作为一名工程师&#xff0c;一直在寻找可靠且高效的组件来应用于你的项目中。东芝的TB6560AHQ/AFG步进电机驱动IC能够提供精准且多功能的电机控制&#xff0c;完全符合现代应用的高要求&#xff0c;保证高性能和易用性。在这篇文章中&#xff0c;我们将探讨TB6560AHQ/AFG的主要…

陈志泊主编《数据库原理及应用教程第4版微课版》的实验题目参考答案实验2

实验目的 1&#xff0e;掌握在SQL Server中使用对象资源管理器和SQL命令创建数据库与修改数据库的方法。 2&#xff0e;掌握在SQL Server中使用对象资源管理器或者SQL命令创建数据表和修改数据表的方 法&#xff08;以SQL命令为重点&#xff09;。 实验设备 操作系统:Win11…

DEPTHAI 2.27.0 发布!

小伙伴们大家好&#xff0c;我们发布了DepthAI 2.27.0版本&#xff0c;本次对DepthAI库有了一些小更新&#xff0c;以下是更新内容。 功能 设置DEPTHAI_ENABLE_FEEDBACK_CRASHDUMP时自动故障转储收集&#xff1b; 漏洞修补 修复深度超出ImageAlign节点时生成PointCloud的问…

Matplotlib Artist 1 概览

Matplotlib API中有三层 matplotlib.backend_bases.FigureCanvas&#xff1a;绘制区域matplotlib.backend_bases.Renderer&#xff1a;控制如何在FigureCanvas上绘制matplotlib.artist.Artist&#xff1a;控制render如何进行绘制 开发者95%的时间都是在使用Artist。Artist有两…

Java开源ERP系统Axelor汉化方法初探

Axelor简介 汉化过程介绍 定义语言和本地化 导出多语言记录 导入翻译 验证翻译 调整翻译 Axelor简介 2024年6月份Axelor ERP发布了8.1版本&#xff0c;适配JDK11及PostgreSQL12及以上版本&#xff08;7及以前版本适配JDK8及PostgreSQL10&#xff09;数据库。v8版本较之前…

景区气象站:守护旅行安全的智能向导

在繁忙的现代社会&#xff0c;人们越来越渴望逃离城市的喧嚣&#xff0c;寻找一处宁静的自然之地放松身心。景区&#xff0c;作为大自然与人类文明交织的瑰宝&#xff0c;吸引了无数游客前来探访。然而&#xff0c;多变的天气往往给游客的旅行带来不确定性。 景区气象站&#x…

Python + OpenCV 开启图片、写入储存图片

这篇教学会介绍OpenCV 里imread()、imshow()、waitKey() 方法&#xff0c;透过这些方法&#xff0c;在电脑中使用不同的色彩模式开启图片并显示图片。 imread() 开启图片 使用imread() 方法&#xff0c;可以开启图片&#xff0c;imread() 有两个参数&#xff0c;第一个参数为档…