强化学习学习笔记(一) 几种策略在多臂赌博机的实现
前言:
这系列笔记是基于郭宪编著的《深入浅出强化学习》而写作的,由于笔者是刚开始学习这方面的知识,不足之处在所难免,希望有问题的地方可以指出。
在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来,这次基本都是我自己总结了,就不标蓝了。
在这一篇文章中,我们主要介绍ε-greedy策略,玻尔兹曼策略,UCB策略在多臂赌博机中的实现。
正文:
1. 多臂赌博机
什么是多臂赌博机?就是一个有k个臂的机器,摇动其中一个臂后,它会爆出数量不等的金币,而爆出金币的数量是服从一定的概率分布的,而且这个概率分布也随不同的臂的变化而变化,也就是说有时候爆出的钱多,有时候钱少,短短几次测试是不能确定哪个臂是最好的。
2. ε-greedy策略
公式就不放上来了(其实是不会敲latex,也不想用其他人的图),这个方法以我来描述就是摇动当前爆金币最多的赌博机概率最高(概率=1-ε),然后随机选动作的概率较低(概率=ε)。
但是这个方法有2个问题,1、衡量变量的角度说,总回报其实不如平均回报合适。2、当前平均回报最好的臂,不一定是最好的那个臂,我们还得保留一定的概率去摇动其他的臂来发现更好的臂。
3. 玻尔兹曼策略
也没有公式,这一个策略与ε-greedy策略不同的是,这个策略根据对应的值函数对摇哪个臂的概率进行了软处理,也就是说平均爆金币多的机器更有可能选中。这个策略中引入了一个参数(temperature)此参数越小,这个策略就越接近贪婪策略(更可能选平均爆金币最多的),参数越大,就越接近均匀策略(更可能随便选)
4. UCB策略
还是没有公式,UCB的全称是Upper Confidence Bound(置信上界),这个策略的特点是当对一个臂的了解不够时(可以认为它的方差很大,现在只是运气不好),它会被选中。
5. 三种策略的多臂赌博机实现
主要需要注意的地方都写在注释里面了,使用了numpy和matploblib两个库,如果运行不了的话请注意。
样例:
1 "这里主要是进行一个多臂赌博机的强化学习练习,作为一个初始作品,希望能有一个好的开头" 2 import numpy as np 3 import matplotlib.pyplot as plt 4 5 6 class KB_Game: 7 def __init__(self, *args, **kwargs): 8 self.q = np.array([0.0, 0.0, 0.0]) # 每个臂的平均汇报回报 9 self.action_counts = np.array([0, 0, 0]) # 摇动每个臂的次数 10 self.current_cumulative_rewards = 0.0 # 累计回报总和 11 self.actions = [1, 2, 3] # 动作空间,1,2,3表示三个不同的摇臂 12 self.counts = 0 # 玩家玩游戏的次数 13 self.counts_history = [] # 玩家玩游戏的次数记录 14 self.current_cumulative_rewards_history = [] # 累积回报的记录 15 self.a = 1 # 玩家当前动作,初始值可以为动作空间中任意一个动作,这里是去摇动第一个臂 16 self.reward = 0 # 当前回报,初始值为0 17 18 def step(self, a): 19 r = 0 20 if a == 1: # 摇动摇臂1 21 r = np.random.normal(1, 1) # 回报符合均值为1,标准差为1的正态分布 22 if a == 2: 23 r = np.random.normal(2, 1) # 回报符合均值为2,标准差为1的正态分布 24 if a == 3: 25 r = np.random.normal(1.5, 1) # 回报符合均值为1.5,标准差为1的正态分布 26 return r 27 28 def choose_action(self, policy, **kwargs): # kwargs是一个dict,python自带的,这里传递的是策略对应的超参数, 29 action = 0 30 if policy == 'e_greedy': # e_greedy方法 31 if np.random.random() < kwargs['epsilon']: 32 # np.random.random()是0~1的随机数,kwargs['epsilon']对应的是epsilon(那个希腊符号) 33 action = np.random.randint(1, 4) # random.randint(1, 4)是返回1~4-1的一个随机整型 34 else: 35 action = np.argmax(self.q) + 1 # 返回平均回报最大的那个 36 if policy == 'ucb': # ucb策略可以看https://blog.csdn.net/songyunli1111/article/details/83384738 37 c_ratio = kwargs['c_ratio'] 38 if 0 in self.action_counts: # 在有哪一个没试过一次的情况下 39 action = np.where(self.action_counts == 0)[0][0] + 1 # 就去试一下那个臂 40 else: 41 value = self.q + c_ratio*np.sqrt(np.log(self.counts)/self.action_counts) # 算法实现 42 action = np.argmax(value) + 1 # 感觉这个怪怪的,直接找value最大的? 43 if policy == 'boltzmann': # 玻尔兹曼方法 44 tau = kwargs['temperature'] 45 p = np.exp(self.q/tau)/(np.sum(np.exp(self.q/tau))) # p是抽取每个臂的概率,一个不知道几维的数组 46 action = np.random.choice([1, 2, 3], p=p.ravel()) 47 # ravel函数是把p拉成一维数组,这里np.random.choice的前一个参数是选取范围,后一个是各个的概率 48 return action 49 50 # 这里play_total是它的训练的总次数,policy是训练方法(e_greedy之类),**kwargs是它的超参数(temperature之类) 51 def train(self, play_total, policy, **kwargs): 52 # 下面这几个reward我是不太明白,估计是在看到结果后,调出reward就能看到平均回报的变化,在这个程序里应该是没有用上的 53 reward_1 = [] 54 reward_2 = [] 55 reward_3 = [] 56 for i in range(play_total): # 总共玩多少次 57 action = 0 58 if policy == 'e_greedy': # 选择方法 59 action = self.choose_action(policy, epsilon=kwargs['epsilon']) 60 if policy == 'ucb': 61 action = self.choose_action(policy, c_ratio=kwargs['c_ratio']) 62 if policy == 'boltzmann': 63 action = self.choose_action(policy, temperature=kwargs['temperature']) 64 self.a = action 65 # print(self.a) 打印的是整型数字 66 # 与环境交互一次 67 self.reward = self.step(self.a) # 说实话我感觉应该写成reward,书上啥都有,这里都写成了r,那就改过来吧 68 self.counts += 1 69 # 更新值函数 70 self.q[self.a-1] = (self.q[self.a-1]*self.action_counts[self.a-1]+self.reward)/(self.action_counts[self.a-1]+1) 71 # 更新平均回报 72 self.action_counts[self.a-1] += 1 73 reward_1.append([self.q[0]]) 74 reward_2.append([self.q[1]]) 75 reward_3.append([self.q[2]]) 76 # 下面是对累计回报和历史累计回报进行更新 77 self.current_cumulative_rewards += self.reward 78 self.current_cumulative_rewards_history.append(self.current_cumulative_rewards) 79 self.counts_history.append(i) 80 81 def reset(self): # 只是把init函数里的actions删掉了而已,这个是用来重置KB_Game中的成员变量的函数 82 self.q = np.array([0.0, 0.0, 0.0]) 83 self.action_counts = np.array([0, 0, 0]) 84 self.current_cumulative_rewards = 0.0 85 self.counts = 0 86 self.counts_history = [] 87 self.current_cumulative_rewards_history = [] # self.cumulative_rewards_history = []书上是这玩意 88 self.a = 1 89 self.reward = 0 90 91 def plot(self, colors, policy): # 绘图函数 92 plt.figure(1) 93 plt.plot(self.counts_history, self.current_cumulative_rewards_history, colors, label=policy) 94 plt.legend() 95 plt.xlabel('n', fontsize=18) 96 plt.ylabel('total rewards', fontsize=18) 97 98 99 if __name__ == '__main__': 100 np.random.seed(0) # 要改成时间不同就不同的话就把0改掉就行了 101 k_gamble = KB_Game() 102 total = 2000 # 总共游玩的次数 103 # 下面是各个方法的使用了,就不一个个强调了 104 k_gamble.train(play_total=total, policy='e_greedy', epsilon=0.05) 105 k_gamble.plot(colors='r', policy='e_greedy') # 这书上的plot函数本来就没有style传进去,看来得改改,把style删了哈哈哈 106 k_gamble.reset() 107 k_gamble.train(play_total=total, policy='boltzmann', temperature=1) 108 k_gamble.plot(colors='b', policy='boltzmann') 109 k_gamble.reset() 110 k_gamble.train(play_total=total, policy='ucb', c_ratio=0.5) 111 k_gamble.plot(colors='g', policy='ucb') 112 plt.show()
图为跑出来的结果,看来boltzmann策略的参数得设置好,不然去赌博的时候怎么亏钱都不知道了(我才不会赌博呢)
后记:
第一次做强化学习的笔记,做完之后感到自己已经把python的知识忘光啦~,那应该怎么办呢?当然是重新拾起来啦!怎么拾起来呢?那就哪里不会搜哪里吧!配置环境就是这样搞定的,只要我访问的网页够多,就绝对有解决方案,如果找不到的话,那就一定是找的不够多!不过我还是挺乐在其中哈,不觉得这寻找解决方案的过程就像侦探追凶吗?当你关掉了几十个网页,长舒一口气时,感觉就像破案了一样快乐。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!