本文用于记录毕设时期的一些学习细节
1、python学习
1.1 基于gym构建自己的环境(无参数版)
因为毕设做的是强化学习,所以需要自己根据模型去搭一个环境,我发现了可以在gym中注册自己的环境,只需根据他的要求去搭建就是了,步骤如下:
(参考了CSDN文章:gym创建自己的强化学习环境env )
- 在C:\Users\xxx\anaconda3\envs\pytorch\Lib\site-packages\gym\envs\classic_control下创建环境文件MyEnv.py
- 在C:\Users\xxx\anaconda3\envs\pytorch\Lib\site-packages\gym\envs_init_.py中注册
# 创建自己的环境
register(
id="MyEnv-v0",
entry_point="gym.envs.classic_control.MyEnv:MyEnv"
)
- 在C:\Users\xxx\anaconda3\envs\pytorch\Lib\site-packages\gym\envs\classic_control_init_.py中导入自己的环境
from gym.envs.classic_control.MyEnv import MyEnv # 创建自己的环境
- 开始写环境文件(step,reset,render这三个方法是gym.Env的抽象方法,必须重写)
import gym
from numpy import random
import time
class MyEnv(gym.Env):
def __init__(self):
self.viewer = None
# 参数声明
def step(self, action):
# 步进
return next_state, r, is_terminal, {}
def reset(self):
# 重置环境
return self.state
def close(self):
if self.viewer:
self.viewer.close()
self.viewer = None
def render(self, mode="human"):
from gym.envs.classic_control import rendering
width = 60
height = 40
edge_x = 0
edge_y = 0
if self.viewer is None:
self.viewer = rendering.Viewer(300, 200)
self.viewer.draw_circle(18, color=(0.8, 0.6, 0.4)).add_attr(
rendering.Transform(translation=(self.x[self.state - 1], self.y[self.state - 1])))
return self.viewer.render(return_rgb_array=mode == 'rgb_array')
- 调用自己的环境对象
(注意,因为继承了gym.env类,该类要求初始化类变量后一定要先调用reset方法一次后续才能调用step方法)
import gym
env = gym.make("MyEnv-v0")
env.reset()
env.step()
env.render()
1.2 基于gym构建自己的环境(自己设置参数版)
因为我想做的通用一点,所以给我的增加了初始化参数的选项,但出现了一些问题,再解决了问题后摸索出了怎么创建和调用。
前面1,2,3步骤同1.1节。
4. 开始写环境文件(step,reset,render这三个方法是gym.Env的抽象方法,必须重写)
import gym
import numpy as np
import math
import scipy.special as special_function
# from numpy import random
# import time
class MyEnv(gym.Env):
#1、初始化方法
def __init__(self,parameter_dict):
#初始化的参数也是自己写,只用一个parameter_dict是为了方便调试,直接用一个字典解决
print("当前调用__init__方法")
self.viewer = None
self.M = parameter_dict['M'] #微服务种类
self.I = parameter_dict['I'] #服务器数量
self.C = parameter_dict['C'] #单个服务器的核心数
self.MSCs = parameter_dict['MSCs'] #微服务链集合
self.MSCs_lambd = parameter_dict['MSCs_lambd'] #微服务链到达率集合
self.Ms_mu = parameter_dict['Ms_mu'] #微服务链处理率集合
self.Trans_time = parameter_dict['Trans_time'] #跨服去传输时延
self.delay_min = float("inf") #最大时延,初始化为无穷大
# 状态空间:M行I列,初始化为0
self.state = np.zeros((self.M,self.I))
#2、环境参数更新方法
def step(self, action):
#这块根据自己需要去写,下面的只是我的一些内容,有些用到的方法就没弄上来了
print("当前调用step方法")
#系统当前状态
state = self.state
print('当前状态state:', state)
#根据动作求出next_state
next_state = state.copy()
next_state[action] += 1 #每次只增加一个核心
#终止状态判断
is_terminal = False #终止状态初始为false
if next_state.sum() == self.I*self.C:
is_terminal = True
#奖励计算
r = self.reward(state,action,next_state,is_terminal)
#其他信息输出
info = None
#环境状态更新
self.state = next_state.copy()
#返回参数
return next_state, r, is_terminal, info
#3、环境重置方法
def reset(self):
print("当前调用reset方法")
#还原状态
self.state = np.zeros((self.M,self.I))
#返回参数
return self.state
#4、环境显示开启方法
def render(self, mode="human"):
print("当前调用render方法")
print(self.state)
return None
#5、环境显示关闭方法
def close(self):
print("当前调用close方法")
if self.viewer:
self.viewer.close()
self.viewer = None
- 调用自己的环境对象
(注意,因为继承了gym.env类,该类要求初始化类变量后一定要先调用reset方法一次后续才能调用step方法)
import gym
k={'M':3,'I':5,'C':6,'MSCs':[(0,1),(0,2),(2,0,1),(0,2,1)],'MSCs_lambd':(8,5,10,2),'Ms_mu':(20,16,25),'Trans_time':0.1}
#这里一定要是parameter_dic=k,不能是gym.make("MyEnv-v0",k),因为gym.make(id,**kwargs),第二个参数是**kwargs,必须要用=号的键值对,多个参数则用多个=号
env = gym.make("MyEnv-v0",parameter_dict=k)
env.reset()
env.step()
1.3 测试MyEnv过程的一些问题
1、我在使用1.2节中的方法设置好环境后,并使用步骤5生成了个类成员env,并进行测试,发现问题。
env并不是MyEnv类型,而是OrderEnforcing.MyEnv.MyEnv-v0类型,如图1所示。这是因为gym.make()方法创建的最后有一句:env = OrderEnforcing(env),而OrderEnforcing是gym.Wrapper的子类,在图2中我们也可以看到最后有两个类型为MyEnv的成员env和unwrapped(红箭头所指),这两个成员和我理想中初始化的MyEnv是一样的,可以看到里面的参数和我输入的参数是一样的。
图1 env的类型
图2 env中的成员env和unwrapped
- 后续,我为了测试代码,在命令行输入语句
env.state += 2,结果如下所示。一开始env.state和env.env.state同步变化,env.state、env.env.state、env.unwrapped.state三者是一样的,但在我输入env.state += 2,第一个和后面两个就不是同一个东西了。
#探讨三者关系
In[2] env.state
Out[2]:
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
In[3] env.env.state
Out[3]:
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
In[4] env.unwrapped.state
Out[4]:
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
#三者是相等的
In[5] env.state is env.env.state
Out[5]: True
#输入加2,后续的语句省略了In[number]
env.state += 2
env.state
Out[7]:
array([[2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2.]])
env.env.state
Out[8]:
array([[2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2.]])
#同步变化
env.state is env.env.state
Out[9]: True
#进行reset,按道理所有的state都会清0
env.reset()
当前调用reset方法
Out[10]:
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
env.state
Out[11]:
array([[2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2.],
[2., 2., 2., 2., 2.]])
env.env.state
Out[12]:
array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
#事实表明,env.state并没有清零,而且+2后与env.env.state不在相等
env.state is env.env.state
Out[13]: False
- 所以2中我发现我的state并不会变化,导致我很迷惑,在后面一步步编译查看
gym.make()的源码后,我发现了:
- 该方法创建的
env并不是我最初的那个MyEnv类型了 - 仔细看了看
env的变化,在env.state+=2语句前,env其实并没有state成员变量,见图2,这里应该是调用env.env或env.unwrapped的state成员变量。在那个语句之后,env出现state成员变量,所以在env.reset方法后,只修改了env.env和env.unwrapped的state成员变量,其实这样看来env.env和env.unwrapped应该引用了同一个类。 - 虽然不知道底层原理为什么会导致这样,会突然自己创建一个
state,也没搞懂env和env.env、env.unwrapped的关系,但是猜测是**类装饰器(wrapper)**的原因,这个点还不是很熟,一篇文章,可以看看,暂时不是很懂。
2、在看gym.make()的源码时,我了解到了两个修饰器Python中@property和@*.setter装饰器的详细用法,前者是为了让方法成为一个属性,不能更改,后者是为了让前者可以设置参数,后者一定要在前者后使用,详情见上述链接。