【五】gym搭建自己的环境之寻宝游戏,详细定义自己myenv.py文件以及算法实现
相关文章:
相关文章:
【四】gym搭建自己的环境,全网最详细版本,3分钟你就学会了!
【五】gym搭建自己的环境____详细定义自己myenv.py文件
【六】gym搭建自己环境升级版设计,动态障碍------强化学习
环境文件下载地址:丨汀/MyEnv {目前暂未更新}
gym搭建自己的环境之详细定义自己myenv.py文件
1.模板化环境编程(统一环境代码框架)
通过上篇文章我们已经可以注册搭建自己环境了,下面开始详细构建自己的myenv.py文件,还有疑问请看文章【四】。
- 首先需要定义自己的环境myenv.py,其代码框架如下:
import gym
"""
gym.Env是gym的环境基类,自定义的环境就是根据自己的需要重写其中的方法;
必须要重写的方法有:
__init__():构造函数
reset():初始化环境
step():环境动作,即环境对agent的反馈
render():如果要进行可视化则实现
"""
class MyEnv(gym.Env):
- 函数reset()作用:智能体需要一次次地尝试累积经验,然后从经验中学到好的动作。一次尝试称之为一条轨迹或一个episode. 每次尝试都要到达终止状态. 一次尝试结束后,智能体需要从头开始,这就需要智能体具有重新初始化的功能。
- 一个仿真环境必不可少的两部分是物理引擎和图像引擎。物理引擎模拟环境中物体的运动规律;图像引擎用来显示环境中的物体图像
· render()函数作用:起到图像引擎作用,对于强化学习算法渲染函数可以没有,但是加入图像引擎可以方便调试代码时直观显示当前环境中物体的状态。
·step()函数作用:起到物理引擎,其输入是动作a,输出是:下一步状态,立即回报,是否终止,调试项;该函数中,一般利用智能体的运动学模型和动力学模型计算下一步的状态和立即回报,并判断是否达到终止状态。
from gym import spaces, core
# core.Env是gym的环境基类,自定义的环境就是根据自己的需要重写其中的方法;
#同上
class MyEnv(core.Env):
def __init__(self):
self.action_space = spaces.Box(low=-1, high=1, shape=(1, )) # 动作空间
self.observation_space = spaces.Box(low=-1, high=1, shape=(1, )) # 状态空间
# 其他成员
def reset(self):
...
obs = self.get_observation()
return obs
def step(self, action):
...
reward = self._get_reward()
done = self._get_done()
obs = self._get_observation(action)
info = {} # 用于记录训练过程中的环境信息,便于观察训练状态
return obs, reward, done, info
# 根据需要设计相关辅助函数
def _get_observation(self, action):
...
return obs
def _get_reward(self):
...
return reward
def _get_done(self):
...
return done
2.项目环境搭建
- 背景介绍:机器人在一个二维迷宫中走动寻找电池,迷宫中有障碍物、大山、电池。大山机器人是无法走的,游戏终止条件是:机器人设计障碍物里或者找到电池;如何最佳的策略,让机器人尽快地找到电池获得奖励呢。下面将进行解答:
2.1 状态空间代码:
self.states = range(0,16) #状态空间
self.terminate_states = dict() #终止状态为字典格式
self.terminate_states[11] = 1
self.terminate_states[12] = 1
self.terminate_states[15] = 1
self.actions = ['n','e','s','w']
self.rewards = dict(); #回报的数据结构为字典
self.rewards['8_s'] = -1.0
self.rewards['13_w'] = -1.0
self.rewards['7_s'] = -1.0
self.rewards['10_e'] = -1.0
self.rewards['14_e'] = 1.0
self.t = dict(); #状态转移的数据格式为字典
self.t['1_s'] = 5
self.t['1_e'] = 2
self.t['2_w'] = 1
self.t['2_e'] = 3
self.t['3_s'] = 6
self.t['3_w'] = 2
self.t['3_e'] = 4
self.t['4_w'] = 3
self.t['4_s'] = 7
self.t['5_s'] = 8
self.t['5_n'] = 1
self.t['6_n'] = 3
self.t['6_s'] = 10
self.t['6_e'] = 7
self.t['7_w'] = 6
self.t['7_n'] = 4
self.t['7_s'] = 11
self.t['8_n'] = 5
self.t['8_e'] = 9
self.t['8_s'] = 12
self.t['9_w'] = 8
self.t['9_e'] = 10
self.t['9_s'] = 13
self.t['10_w'] = 9
self.t['10_n'] = 6
self.t['10_e'] = 11
self.t['10_s'] = 14
self.t['10_w'] = 9
self.t['13_n'] = 9
self.t['13_e'] = 14
self.t['13_w'] = 12
self.t['14_n'] = 10
self.t['14_e'] = 15
self.t['14_w'] = 13
2.2 step函数创建:
动作空间:需要注意的是输出的顺序不要弄错了,对于调试信息,可以为空,但不能缺少,否则会报错,常用{}来代替。
简单阐释:状态转移根据当前状态和动作得到下一步状态,然后判断是否达到终止条件is_terminal决定游戏进程。reward只有到达目的或者障碍是才有其余情况为
def step(self, action):
#系统当前状态
state = self.state
if state in self.terminate_states:
return state, 0, True, {}
key = "%d_%s"%(state, action) #将状态和动作组成字典的键值
#状态转移
if key in self.t:
next_state = self.t[key]
else:
next_state = state
self.state = next_state
is_terminal = False
if next_state in self.terminate_states:
is_terminal = True
if key not in self.rewards:
r = 0.0
else:
r = self.rewards[key]
return next_state, r, is_terminal,{}
2.3 render函数的建立:
def render(self, mode='human'): #可视化画图
from gym.envs.classic_control import rendering
screen_width = 600
screen_height = 600
if self.viewer is None:
self.viewer = rendering.Viewer(screen_width, screen_height)#调用rendering中的画图函数,#创建600*600的窗口
# 创建网格世界,一共包括10条直线,事先算好每条直线的起点和终点坐标,然后绘制这些直线,代码如下:
#创建网格世界
self.line1 = rendering.Line((100,100),(500,100))
self.line2 = rendering.Line((100, 200), (500, 200))
self.line3 = rendering.Line((100, 300), (500, 300))
self.line4 = rendering.Line((100, 400), (500, 400))
self.line5 = rendering.Line((100, 500), (500, 500))
self.line6 = rendering.Line((100, 100), (100, 500))
self.line7 = rendering.Line((200, 100), (200, 500))
self.line8 = rendering.Line((300, 100), (300, 500))
self.line9 = rendering.Line((400, 100), (400, 500))
self.line10 = rendering.Line((500, 100), (500, 500))
#创建大山
self.mountain = rendering.make_circle(40)
self.circletrans = rendering.Transform(translation=(250,350))
self.mountain.add_attr(self.circletrans)
self.mountain.set_color(0,1,1)
#创建第一个障碍物
self.obstacle_1 = rendering.make_circle(35)
self.circletrans = rendering.Transform(translation=(450, 250))
self.obstacle_1.add_attr(self.circletrans)
self.obstacle_1.set_color(0, 0, 0)
#创建第二个障碍物
self.obstacle_2 = rendering.make_circle(35)
self.circletrans = rendering.Transform(translation=(150, 150))
self.obstacle_2.add_attr(self.circletrans)
self.obstacle_2.set_color(0, 0, 0)
#创建电池
self.Battery = rendering.make_circle(35)
self.circletrans = rendering.Transform(translation=(450, 150))
self.Battery.add_attr(self.circletrans)
self.Battery.set_color(0, 1, 0.5)
#创建机器人
self.robot= rendering.make_circle(30)
self.robotrans = rendering.Transform()
self.robot.add_attr(self.robotrans)
self.robot.set_color(1, 0.8, 0)
# 创建完之后,给11条直线设置颜色,并将这些创建的对象添加到几何中代码如下:
self.line1.set_color(0, 0, 0)
self.line2.set_color(0, 0, 0)
self.line3.set_color(0, 0, 0)
self.line4.set_color(0, 0, 0)
self.line5.set_color(0, 0, 0)
self.line6.set_color(0, 0, 0)
self.line7.set_color(0, 0, 0)
self.line8.set_color(0, 0, 0)
self.line9.set_color(0, 0, 0)
self.line10.set_color(0, 0, 0)
# 添加组件到Viewer中
self.viewer.add_geom(self.line1)
self.viewer.add_geom(self.line2)
self.viewer.add_geom(self.line3)
self.viewer.add_geom(self.line4)
self.viewer.add_geom(self.line5)
self.viewer.add_geom(self.line6)
self.viewer.add_geom(self.line7)
self.viewer.add_geom(self.line8)
self.viewer.add_geom(self.line9)
self.viewer.add_geom(self.line10)
self.viewer.add_geom(self.mountain)
self.viewer.add_geom(self.obstacle_1)
self.viewer.add_geom(self.obstacle_2)
self.viewer.add_geom(self.Battery)
self.viewer.add_geom(self.robot)
# 接下来,开始设置机器人的位置。机器人的位置根据其当前所处的状态不同,所在的位置不同。我们事先计算出每个状态处机器人位置的中心坐标,并存储到两个向量中,并在类初始化中给出
self.x=[150,250,350,450] * 4
self.y=[450] * 4 + [350] * 4 + [250] * 4 + [150] * 4
"""为了让结果可视化,我们需要自己渲染结果,比如我打算设置一个600×600的窗口,
那么,每一格的中心的横坐标为[150, 250, 350, 450]重复4次(因为是一个1×16的list,每4个为环境的一行),
相应地,纵坐标为150,250,350,450分别重复4次。"""
# 根据这两个向量和机器人当前的状态,我们就可以设置机器人当前的圆心坐标了即:
if self.state is None:
return None
self.robotrans.set_translation(self.x[self.state-1], self.y[self.state- 1])
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
2.4 reset()函数的建立:
reset()函数常常用随机的方法初始化机器人的状态,即:
def reset(self):
self.state = self.states[int(random.random() * len(self.states))] #随机初始化机器人状态在[1-16之间随便选]
return self.state
关闭窗口:
def close(self):
if self.viewer:
self.viewer.close()
下面对创建网格世界进行详细阐释:比如需要创建下面的往网格,则需要10条线,3行4列(本来需要7条),但是有空缺,则8,9,10,需要每个创建从开始到结束的坐标
2.5 环境生成效果图:
2.6 完整代码和文件创建位置:
通过上篇文章我们已经可以注册搭建自己环境了,下面开始详细构建自己的myenv.py文件,还有疑问请看文章【四】。
- 路径H:\Anaconda3-2020.02\envs\tf2\Lib\site-packages\gym\envs\classic_control下的__init__.py文件
- 拷贝在这个文件夹中因为要使用rendering模块
from gym.envs.classic_control.cartpole import CartPoleEnv
from gym.envs.classic_control.mountain_car import MountainCarEnv
from gym.envs.classic_control.continuous_mountain_car import Continuous_MountainCarEnv
from gym.envs.classic_control.pendulum import PendulumEnv
from gym.envs.classic_control.acrobot import AcrobotEnv
#MyEnv_1:
from gym.envs.classic_control.myenv_1.myenv_1 import MyEnv_1
- 路径H:\Anaconda3-2020.02\envs\tf2\Lib\site-packages\gym\envs下的__init__.py文件
register(
id='MyEnv_1-v0',
entry_point='gym.envs.classic_control:MyEnv_1',
max_episode_steps=200,
reward_threshold=195.0,
- 测试程序
import gym
import time
env = gym.make('MyEnv_1-v0')
env.reset()
env.render()
time.sleep(10)
env.close()
sys.exit()
- 完整的环境py文件
3.基于Q-learning和Epsilon-greedy训练
理论知识见:【五】强化学习之Sarsa、Qlearing详细讲解----PaddlePaddlle【PARL】框架{飞桨}_丨汀、的博客-CSDN博客
这份代码没有考虑大山的存在。具体代码见码云:丨汀/MyEnv - Gitee.com
import gym
import numpy as np
import time
import sys
num_episodes = 5000# 共进行5000场游戏
max_number_of_steps = 10# 每场游戏最大步数
# 以栈的方式记录成绩
goal_average_steps = 100 # 平均分
num_consecutive_iterations = 100 # 栈的容量
last_time_steps = np.zeros(num_consecutive_iterations) # 只存储最近100场的得分(可以理解为是一个容量为100的栈)
env = gym.make('GridWorld-v0')
# q_table是一个256*2的二维数组
# 离散化后的状态共有4^4=256中可能的取值,每种状态会对应一个行动
# q_table[s][a]就是当状态为s时作出行动a的有利程度评价值
# 我们的AI模型要训练学习的就是这个映射关系表
# 这里的4*4=16是棋盘上棋子的位置数量,第二个参数的4为每个位置对应的4个方向的可能操作。
# q_table的纵坐标是state可能出现的情况之和,横坐标为对应每种state可以做出的action
# 而取值是每种action对于每种state有利程度的评价值
# q_table = np.loadtxt("q_table.txt", delimiter=",")
q_table = np.random.uniform(low=-1, high=1, size=(4 * 4, 4))
#在这之间随机取值
# 根据本次的行动及其反馈(下一个时间步的状态),返回下一次的最佳行动
# epsilon_coefficient为贪心策略中的ε,取值范围[0,1],取值越大,行为越随机
# 当epsilon_coefficient取值为0时,将完全按照q_table行动。故可作为训练模型与运用模型的开关值。
def get_action(state, action, observation, reward, episode, epsilon_coefficient=0.0):
# print(observation)
next_state = observation
epsilon = epsilon_coefficient * (0.99 ** episode) # ε-贪心策略中的ε
if epsilon <= np.random.uniform(0, 1):
next_action = np.argmax(q_table[next_state])
else:
next_action = np.random.choice([0, 1, 2, 3])
# -------------------------------------训练学习,更新q_table----------------------------------
alpha = 0.2 # 学习系数α
gamma = 0.99 # 报酬衰减系数γ
q_table[state, action] = (1 - alpha) * q_table[state, action] + alpha * (
reward + gamma * q_table[next_state, next_action])
# -------------------------------------------------------------------------------------------
return next_action, next_state
timer = time.time()#返回时间单位是秒
for episode in range(num_episodes):
env.reset() # 初始化本场游戏的环境
episode_reward = 0 # 初始化本场游戏的得分
q_table_cache = q_table # 创建q_table还原点,如若训练次数超次,则不作本次训练记录。
for t in range(max_number_of_steps):
env.render() # 更新并渲染游戏画面
state = env.state
action = np.argmax(q_table[state])
observation, reward, done, info = env.step(action) # 进行活动,#并获取本次行动的反馈结果
action, state = get_action(state, action, observation, reward, episode, 0.5) # 作出下一次行动的决策
episode_reward += reward
if done:
np.savetxt("q_table.txt", q_table, delimiter=",")
print('已完成 %d 次训练,本次训练共进行 %d 步数。episode_reward:%d,平均分: %f' % (episode, t + 1, reward, last_time_steps.mean()))
last_time_steps = np.hstack((last_time_steps[1:], [reward])) # 更新最近100场游戏的得分stack
break
q_table = q_table_cache # 超次还原q_table
print('已完成 %d 次训练,本次训练共进行 %d 步数。episode_reward:%d,平均分: %f' % (episode, t + 1, reward, last_time_steps.mean()))
last_time_steps = np.hstack((last_time_steps[1:], [reward])) # 更新最近100场游戏的得分stack;np.hstack():在水平方向上平铺
if (last_time_steps.mean() >= goal_average_steps):
np.savetxt("q_table.txt", q_table, delimiter=",")
print('用时 %d s,训练 %d 次后,模型到达测试标准!' % (time.time() - timer, episode))
env.close()
sys.exit()
env.close()
sys.exit()
结果如下:
......
已完成 113 次训练,本次训练共进行 4 步数。episode_reward:100,平均分: 96.000000
已完成 113 次训练,本次训练共进行 4 步数。episode_reward:100,平均分: 96.000000
已完成 114 次训练,本次训练共进行 2 步数。episode_reward:100,平均分: 96.000000
已完成 114 次训练,本次训练共进行 2 步数。episode_reward:100,平均分: 96.000000
已完成 115 次训练,本次训练共进行 5 步数。episode_reward:100,平均分: 96.000000
已完成 115 次训练,本次训练共进行 5 步数。episode_reward:100,平均分: 98.000000
【五】gym搭建自己的环境之寻宝游戏,详细定义自己myenv.py文件以及算法实现的更多相关文章
- 一文教你如何在ubuntu上快速搭建STM32 CubeIDE环境(图文超详细+文末有附件)
在快速ubuntu上安装cubeide你值得拥有:适合对linux系统还不是很熟悉的同学: 文章目录 1 下载 cubeide 2 找到软件 3 安装 4 附件 5 总结 1 下载 cubeide 登 ...
- 十分钟上手-搭建vue开发环境(新手教程)
想写一些关于vue的文章已经很久了,因为这个框架已经火了很久,在公司里用的框架都比较老旧,但怎么也得跟上前端发展变化的潮流,这不,开始使用vue开发项目了,一遍开发一边踩坑中,今天要记录的是五分钟搭建 ...
- docker 搭建 web 服务环境
docker容器虽然早就听说过,但是本人还真的没去用过,刚好看到相关的文章,就分享了下,有机会可以实践下...... 做过开发的人对开发环境的安装.配置应该都不会太陌生,不管你做什么开发,对开发环境都 ...
- Sublime Text3搭建完美开发环境(Python+PHP+Javascript+nodejs+C++)
一.Sublime配置(如已安装Package Control可跳过) sublime下载地址:http://www.sublimetext.com/3 安装Package Control插件: 直接 ...
- 一文带你趟过mac搭建appium测试环境的遇到的坑
做UI自动化,最难的一步就是在环境搭建上,怎么去搭建一个UI自动化测试的环境,会难住很多人,在Mac上搭建appium如何搭建呢,本文带着大家去领略如何在mac上搭建appium测试环境.下面就是详细 ...
- 单机搭建Android开发环境(五)
前文介绍了Android系统开发环境的搭建,本文将简单介绍Android应用开发环境的搭建. 基于Android Studio搭建应用开发环境,相比使用Eclipse简单得多.Android Stud ...
- 五分钟用Docker快速搭建Go开发环境
挺早以前在我写过一篇用 `Docker`搭建LNMP开发环境的文章:[用Docker搭建Laravel开发环境](http://mp.weixin.qq.com/s?__biz=MzUzNTY5MzU ...
- 自定义搭建PHP开发环境
学习了一段时间php了,因为之前是刚接触php,所以用的是集成安装包(wamp).现在想进一步了解apache.mysql.php之间的关系以及提升自己所以进行自定义搭建PHP开发环境.废话不多说,请 ...
- [BI项目记]-搭建代码管理环境之客户端
前面已经介绍了如何搭建代码管理环境的服务器端安装和配置,这里介绍对于客户端的几个场景. 首先对于开发人员来说,可以直接使用Visual Studio来连接,这里主要演示Visual Studio 20 ...
- 第二章 搭建Android开发环境--读书笔记
俗话说,工欲善其事,必先利其器,对于Android驱动开发来说,首先我们要做的就是搭建Android开发环境,我们首先要配置Linux驱动的开发环境,接着还得配置开发Android应用程序以及Andr ...
随机推荐
- 【Django-Vue】手机号是否存在接口 多方式登录接口 腾讯云短信介绍和申请 api与sdk
目录 昨日回顾 今日内容 0 登录注册功能设计 1 短信登录接口 视图类 2 多方式密码登录接口 视图类 序列化类 路由 3 腾讯云短信介绍和申请 3.1api与sdk 补充 练习 昨日回顾 # 你的 ...
- Mysql--数据的导入导出以及备份
一.导入导出 1.1.into outfile(只导出数据) 注意:mysql 5.7+版本,secure_file_priv 的值默认为NULL,即不允许导入或导出,需在 /etc/my.cnf 添 ...
- ME21N 采购订单批导
1业务场景 事务代码:ME21N创建采购订单 可以通过BAPI_PO_CREATE1批量创建 2代码实现 1.抬头 2.行项目 3.增强 抬头增强字段放在BAPI_TE_MEPOHEADER结构中的C ...
- Educational Codeforces Round 96 (Rated for Div. 2) (A - C题个人题解)
因为火锅导致错过的上分机会,赛后发现人均AC5题 1430A. Number of Apartments 暴力搜索 #include<bits/stdc++.h> using namesp ...
- Educational Codeforces Round 102 Personal Editorial(A~C,max Rating 1500)
1473A. Replacing Elements Rating 800 对数组排序,一旦数组中最大的数即a[n-1]是一个小于或等于d的数,直接输出YES即可,否则运用数组中最小的两个数加和替换最大 ...
- Java | 个人学习指南笔记
前言:由于作者已经有C语言,C++和Python语言的基础了,所以在文章的编写时会以这几门编程语言作对比.本文学习自 C语言中文网的 Java 教程,部分内容引用自这.引用内容仅作学习使用. 第1章: ...
- 【转载】内存基本概念-watermark&lowmem_reserve
概述 当系统内存短缺的情况下仍去申请内存,可能会触发系统对内存的回收,那什么时候应该进行回收,回收到什么标准又可以停止回收,参考依据是什么?即本文将介绍的watermark(内存水位线),当检查wat ...
- 30 秒使用 Sealos 搭建个人密码管理器 Vaultwarden
我与 LastPass 的曲折恋情 超过 8 年网龄的我,注册过很多网站帐号,每个网站的密码我都用不同的复杂密码.一开始我全靠脑力记忆这些密码,后来渐渐觉得记起来很困难,就记录在笔记本上.但是随着时间 ...
- distributor和gateway联合实现出中继的负载均衡+故障转移
概述 freeswitch是一款简单好用的VOIP开源软交换平台. 在之前的文章,我们介绍过distributor模块实现多线路分发的配置方法,但是当线路发生故障时,distributor并不会自动跳 ...
- shell脚本(4)-格式化输入
一.read命令 1.概念: 默认接受键盘的输入,回车符代表输入结束 2.read命令选项 -p:打印信息 -t:限定时间 -s:不回显 -n:输入字符个数 3.举例说明 (1)模拟登录 [root@ ...