强化学习(RL)中的Q-learning在拿奖杯游戏的表现
RL中的Q-learning在拿奖杯游戏的表现
- 强化学习与Q-learning
- 拿奖杯游戏与建模
- 代码分析
- 运行结果
强化学习与Q-learning
随着知识的学习AI的面纱也被慢慢揭开,强化学习的详细解释其他blog上有详尽的入门解释。但是文章的主题是这方面,就说一说我的理解,下面的描述可能更针对对RL已经有一定了解的群体。总的来说就是对问题进行建模,对policy,value,q值,environment进行不同程度上的构建,然后经过不同算法的训练更新比如(时序差分,蒙特卡洛),其核心思想是bellman方程,最后经过收敛判断得出结果。当然对于大型的问题有更加复杂的解法,价值函数逼近,策略函数逼近等等。大一些的应用的问题代码量多,模型建立更加复杂(这里指数学模型,而不是RL里的model),但万变不离其中。当然后人是站在前面巨人的肩膀之上的,后面的创新肯定会搭高人类知识的阶梯。
下面再八股式地介绍一下Q-learning:
首先Q-learning可以被视为离轨策略的时序学习,下面解释一下什么是同轨学习和离轨学习
目标策略:用来学习并成为最优策略
行动策略:用来智能体的行动样本
同轨学习:目标策略和行动策略在同一个训练模型中产生
离轨学习:目标策略和行动策略不在同一个模型中产生,行动策略可能会单独产生
对于行动策略单独产生的迭代,由于与问题相关性更低,收敛的速度会更长,但效果在同等情况下会更好
下面是Q-learning算法
算法参数:步长α∈(0,1],取很小的ε>0
对所有的s∈S,a∈A(s),初始化Q(s,a),其中Q(终止状态) = 0
对每幕:初始化S对幕中的每一步循环:使用从Q得到的策略(ε-greedy),在S处选择A执行A,观察到R和S‘Q(S,A) <-- Q(S,A) + α[R+γmax{Q(S',A)} - Q(S,A)]S <-- S'直到S是终止状态
现在提出问题,为什么Q-learning是离轨策略?找了一些资料,网上貌似没有解答,个人认为是Q值的迭代中的max选择,这就是自己产生了行动策略,与模型无关。
直观来看,S,A,S’和A‘的动作关系如下图
在下面的奖杯游戏中马上就可以看到形象的例子:
拿奖杯游戏与建模
游戏和文章的灵感思路是从这里引出的,因为感觉这篇文章的博主讲的不是很清楚,关键部分跳过且感觉代码是copy的(没有冒犯的意思)。花了一些时间学习,看懂了模型和代码,想在此基础上再进一步,所以也吃水不忘挖井人。下面的一些图会引用原文章的示意图。
状态从上到下从左到右进行编号,我们的目标是从左下角(蓝色的点出发)花最小的代价走到终点,即红色的地方
黑色的state=5是障碍物,无法通过;绿色的state=7是骷髅,到达了会有负奖励。白色格子的路就是通道。
只有两个最终状态会有奖励reward,分别是+和-1.其他时候奖励都是0.
根据算法,我们要初始化状态矩阵s,q值矩阵qtable,还有一些参数
然后设定总的迭代次数,将上面的Q-learning伪代码实现。每次迭代中,我们都让代理人(agent)从state = 8出发,根据ε-greedy选择动作,再贪心地根据Q(S,A) <-- Q(S,A) + α[R+γmax{Q(S’,A)} - Q(S,A)]公式来更新当前的Q值,直到走到最终状态(最终状态包括3和7);依次重复以上动作,直至迭代完设定的次数。(当然我们可以根据q值变化大小的绝对上限来终止循环,为了简单还是设置了迭代次数)
下面是对代码的剖析:
代码分析
笔者在源码的基础上,加上了详细的注释,请食用
代码只要依次粘贴到python的一个文件中,运行即可,因为篇幅原因,笔者最后就不再重新整合到一起占用篇幅了
如果读者都嫌整合到一起复制粘贴麻烦,直接私聊我,我给你发源文件。
首先进行导包操作,设置好随机数以备后面的使用
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches # 图形类
plt.xkcd() # 设置画图风格(漫画)
np.random.seed(2022) # 随机数种子
定义一个代理人agent类,由他在环境中不断从状态中采取动作再到下一个动作,一直学习下去循环往复,直到迭代结束得出最终结果。
首先定义全局变量,最终状态terminal state
初始化变量,定义选择动作函数,和对Q值进行更新的learn函数
class Agent:terminal_states = (3, 7) # 终止状态# 3对应奖杯的位置,7对应骷髅的位置,从上往下,从左往右的顺序数,且从0开始def __init__(self, num_actions, num_states, exp_rate=0.2, lr=0.1, gamma=0.9):self.num_actions = num_actionsself.num_states = num_statesself.exp_rate = exp_rateself.lr = lrself.gamma = gammaself.q_table = np.zeros((self.num_states, self.num_actions))def choose_action(self, state): # ε-greedy# choose action with most expected valueif np.random.uniform(0, 1) <= self.exp_rate: # 如果随机数小于exp_rateaction = np.random.choice(self.num_actions) # 在动作集中随机选择一个动作else:# greedy action, choose the agent with highest q# if there are multiple max, we randomly choose oneqs = self.q_table[state, :] # 取出动作状态值函数action = np.random.choice(np.where(qs == qs.max())[0])return actiondef learn(self, s, a, r, s_):# s状态,a动作,r即时奖励,s_演化的下一个动作q_old = self.q_table[s, a]if s_ not in self.terminal_states:q_new = r + self.gamma * self.q_table[s_, :].max() # Q-learning取最大的动作状态值else:q_new = r# updateself.q_table[s, a] += self.lr * (q_new - q_old)
然后定义环境(网格世界)
定义坐标和状态的互相转化函数
设置好rewards矩阵,qtabel矩阵
然后定义了状态转移的get_transition_model函数,之后的状态转移就根据这个transition model进行转移
reset函数用来重置每幕中的agent位置,即每次都从出发点开始学习
step函数将agent和GridWord函数进行了整合,将封装好的函数集合成了整体,以便在最后的大循环中可以直接调用
class GridWorld:terminal_states = (3, 7) # 终止状态def __init__(self):"""Initialize the world with board data.0 0 0 1 1表示拿到奖杯0 2 0 -1 2表示障碍物无法通过0 0 0 0 -1表示遇见骷髅"""self.board = np.zeros((3, 4))self.board[1, 1] = 2self.board[0, 3] = 1self.board[1, 3] = -1self.num_rows, self.num_cols = self.board.shapeself.num_states = self.num_rows * self.num_colsself.num_actions = 4self.reward_table = self.get_reward_table()self.transition_model = self.get_transition_model()def get_state_from_pos(self, pos):"""Maps (x, y) position on the board to state index从位置坐标获得状态"""return pos[0] * self.num_cols + pos[1]def get_pos_from_state(self, state):"""Maps state index to (x, y) position on the board从状态获得位置坐标"""return state // self.num_cols, state % self.num_colsdef get_reward_table(self):"""Map each state to its corresponding reward.初始化 状态回报矩阵,state-rewrd matrix"""reward_table = np.zeros(self.num_states)for r in range(self.num_rows):for c in range(self.num_cols):s = self.get_state_from_pos((r, c))if s in self.terminal_states:reward_table[s] = self.board[r, c]return reward_tabledef get_transition_model(self, random_rate=.1):"""The transition_model describing the probability of environment going tonext state (s') given that we are currently at state (s) and take the action (a).P = P[s' | (s, a)] 当前状态s下采取行动a到达s'的概率"""transition_model = np.zeros((self.num_states, self.num_actions, self.num_states)) # 在当前状态下,采取行动,而可能又到达状态的概率for r in range(self.num_rows):for c in range(self.num_cols):s = self.get_state_from_pos((r, c)) # 获得当前状态possible_s_prime = np.zeros(self.num_actions)if self.board[r, c] == 0:for a in range(self.num_actions):new_r, new_c = r, cif a == 0: # 向上走new_r = max(r - 1, 0)elif a == 1: # 向右走new_c = min(c + 1, self.num_cols - 1)elif a == 2: # 向下走new_r = min(r + 1, self.num_rows - 1)elif a == 3: # 向左走new_c = max(c - 1, 0)if self.board[new_r, new_c] == 2: # 如果撞到障碍物,原地不动new_r, new_c = r, cs_prime = self.get_state_from_pos((new_r, new_c))possible_s_prime[a] = s_primeelse: # 如果是1、2、-1即不可以行走的地方,则原地不动possible_s_prime = np.ones(self.num_actions) * sfor a in range(self.num_actions):transition_model[s, a, int(possible_s_prime[a])] += 1 - random_ratetransition_model[s, a, int(possible_s_prime[(a + 1) % self.num_actions])] += random_rate / 2.0transition_model[s, a, int(possible_s_prime[(a - 1) % self.num_actions])] += random_rate / 2.0return transition_modeldef reset(self):start_pos = (2, 0) # 从左下角出发self.cur_state = self.get_state_from_pos(start_pos) # 当前状态self.epi_reward = 0return self.cur_statedef step(self, action):p = self.transition_model[self.cur_state, action]next_state = np.random.choice(self.num_states, p=p) # 根据transition_model的权重选择下一个状态reward = self.reward_table[next_state]self.epi_reward += rewardself.cur_state = next_statedone = Falseif self.cur_state in self.terminal_states:done = Truereturn self.cur_state, reward, done, [] # 返回下一状态,奖励,是否结束和信息(为空)
展示运行的结果函数
这个与文章的内容没什么关系,但是可视化还是很重要的,没有什么难点
且注释都很详细
def show_world(env, agent, episode, show_state=False, show_q_table=True):fig_size = (12, 8)num_rows, num_cols = env.num_rows, env.num_colsfig, ax0 = plt.subplots(1, 1, figsize=fig_size)a_shift = [(0, 0.3), (0.4, 0), (0, -.4), (-.3, 0)]ax0.axis('off') # 把横坐标关闭# plot the gridsfor i in range(num_cols + 1): # 按列画线if i == 0 or i == num_cols:ax0.plot([i, i], [0, num_rows], color='black')else:ax0.plot([i, i], [0, num_rows], alpha=0.7,color='grey', linestyle='dashed')for i in range(num_rows + 1): # 按行画线if i == 0 or i == num_rows:ax0.plot([0, num_cols], [i, i], color='black')else:ax0.plot([0, num_cols], [i, i], alpha=0.7,color='grey', linestyle='dashed')# plot the objects on the gridsfor i in range(num_rows):for j in range(num_cols):y = (num_rows - 1 - i)x = jif env.board[i, j] == 2:rect = patches.Rectangle((x, y), 1, 1, edgecolor='none', facecolor='black', alpha=0.6)ax0.add_patch(rect)elif env.board[i, j] == 1:rect = patches.Rectangle((x, y), 1, 1, edgecolor='none', facecolor='red', alpha=0.6)ax0.add_patch(rect)ax0.text(x + 0.4, y + 0.5, "r = +1")elif env.board[i, j] == -1:rect = patches.Rectangle((x, y), 1, 1, edgecolor='none', facecolor='green', alpha=0.6)ax0.add_patch(rect)ax0.text(x + 0.4, y + 0.5, "r = -1")else:# visualize the q tableif show_q_table:s = env.get_state_from_pos((i, j))qs = agent.q_table[s, :]for a in range(len(qs)):dx, dy = a_shift[a]c = 'k'q = qs[a]if q > 0:c = 'r'elif q < 0:c = 'g'ax0.text(x + dx + 0.3, y + dy + 0.5,"{:.2f}".format(qs[a]), c=c)if show_state:s = env.get_state_from_pos((i, j))ax0.text(x + 0.3, y + 0.1, "state = {}".format(str(s)))# draw the agenti, j = env.get_pos_from_state(env.cur_state)y = (num_rows - 1 - i)x = jax0.plot([x + 0.5], [y + 0.5], marker="o",linestyle='none', markersize=20, color='blue')ax0.set_title("episode: {}".format(str(episode)))plt.show(block=False)plt.pause(5)plt.close()
最后的使整个程序跑起来的代码如下
env = GridWorld() # 初始化
agent = Agent(4, 4 * 3)
num_episodes = 31for i in range(1,num_episodes):state = env.reset()if not i % 10:show_world(env, agent, i)while True:action = agent.choose_action(state) # 选择动作next_state, reward, done, info = env.step(action)agent.learn(state, action, reward, next_state) # 学习--更新q值state = next_state# show_world(env, agent, i + 1)if done:break
运行结果
最后策略的制定:根据每个状态的,采取不同动作所能获得的q值最大的动作去做动作,直到走到结束状态。
代码中一共设置了30次迭代,最后学出从上面走到终点的策略。直观上也很容易理解,有人说从右边走也行啊,但是到state=6的时候有更有可能会走进骷髅中,这是我们不希望看到的,所以从上面走是最佳策略。
强化学习(RL)中的Q-learning在拿奖杯游戏的表现相关推荐
- 强化学习入门 : 一文入门强化学习 (Sarsa、Q learning、Monte-carlo learning、Deep-Q-Network等)
最近博主在看强化学习的资料,找到这两个觉得特别适合入门,一个是"一文入门深度学习",一个是"莫烦PYTHON". 建议:看资料的时候可以多种资料一起参考,一边调 ...
- [强化学习实战]出租车调度-Q learning SARSA
出租车调度-Q learning & SARSA 案例分析 实验环境使用 同策时序差分学习调度 异策时序差分调度 资格迹学习调度 结论 代码链接 案例分析 本节考虑Gym库里出租车调度问题(T ...
- 强化学习(六) - 连续空间中的强化学习(RL in Continuous Spaces)及相关实例
强化学习(六) - 连续空间中的强化学习 6.1 连续空间中的强化学习 6.2 离散空间和连续空间 6.3 离散化 实例:小车上山 6.3.1 相关程序 6.3.2 程序注解 (1) 环境测试 (2) ...
- 强化学习RL学习笔记2-概述(2)
强化学习笔记专栏传送 上一篇:强化学习RL学习笔记1-概述(1) 下一篇:强化学习RL学习笔记3-gym了解与coding实践 目录 强化学习笔记专栏传送 前言 Major Components of ...
- 分层强化学习综述:Hierarchical reinforcement learning: A comprehensive survey
论文名称:Hierarchical reinforcement learning: A comprehensive survey 论文发表期刊:ACM Computing Surveys 期刊影响因子 ...
- 9个 强化学习现实生活中的应用
"大多数人类和动物的学习可以说属于无监督学习.有人说,如果智能是一块蛋糕,那么无监督学习就是蛋糕,监督学习是锦上添花,强化学习是锦上添花." 这似乎很有趣,对吧? 强化学习是最接近 ...
- 强化学习 最前沿之Hierarchical reinforcement learning(一)
强化学习-最前沿系列 深度强化学习作为当前发展最快的方向,可以说是百家争鸣的时代.针对特定问题,针对特定环境的文章也层出不穷.对于这么多的文章和方向,如果能撇一隅,往往也能够带来较多的启发. 本系列文 ...
- 深度强化学习之模仿学习(Imitation Learning)
上一部分研究的是奖励稀疏的情况,本节的问题在于如果连奖励都没有应该怎么办,没有奖励的原因是,一方面在某些任务中很难定量的评价动作的好坏,如自动驾驶,撞死人和撞死动物的奖励肯定不同,但分别为多少却并 ...
- 主要内容: 本文提出了一种基于(ppo)的微电网最优调度方法。 该方法采用强化学习(RL)来学习调度策略,并积累相应的调度知识。 同时,引入ppo模型,将微电网调度策略动作从离散动作空间扩展到连续动作
MATLAB代码:微电网 强化学习 关键词:微电网 强化学习 RL Reinforcement Learning 参考文档:<Optimal Scheduling of Microgrid Ba ...
最新文章
- 《计算机科学导论》一2.3 非位置化数字系统
- 关于微型计算机主板的描述错误的是,2016年9月计算机一级考试试题及答案「单选」...
- Build boost 1.66.0 with c++11
- codevs 1147 排座椅
- [FI] SAP 关于标准成本、计划成本、目标成本、实际成本
- 洛谷 P1309 瑞士轮 解题报告
- flash背景透明、置底、禁止放大 右键菜单
- 对属性可以赋值的位置
- RHEL/CentOS下编译安装Nginx
- Linux 块设备,Block Layer层架构演变
- goroutine调度详解,以及进程、线程、协程区别
- 让盘古分词支持最新的Lucene.Net 3.0.3
- linux下的screen工具配置(针对 string escape)
- 使用Ntdsutil.exe捕获系统状态数据
- IDEA中如何使用debug调试项目 一步一步详细教程
- ORACLE 8023学习总结
- ParticleDesigner 粒子编辑器使用
- 【报错】Cannot mix different versions of joi schemas(Postman)
- Android 屏幕旋转的处理
- 【SQL文档整理系列1】MySQL创建procedure(可以用来造测试数据)