毕设记录

212 阅读6分钟

本文用于记录毕设时期的一些学习细节

1、python学习

1.1 基于gym构建自己的环境(无参数版)

因为毕设做的是强化学习,所以需要自己根据模型去搭一个环境,我发现了可以在gym中注册自己的环境,只需根据他的要求去搭建就是了,步骤如下:
(参考了CSDN文章:gym创建自己的强化学习环境env )

  1. 在C:\Users\xxx\anaconda3\envs\pytorch\Lib\site-packages\gym\envs\classic_control下创建环境文件MyEnv.py
  2. 在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"
)
  1. 在C:\Users\xxx\anaconda3\envs\pytorch\Lib\site-packages\gym\envs\classic_control_init_.py中导入自己的环境
from gym.envs.classic_control.MyEnv import MyEnv    # 创建自己的环境
  1. 开始写环境文件(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')

  1. 调用自己的环境对象
    注意,因为继承了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

  1. 调用自己的环境对象
    注意,因为继承了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,并进行测试,发现问题。

  1. env并不是MyEnv类型,而是OrderEnforcing.MyEnv.MyEnv-v0类型,如图1所示。这是因为gym.make()方法创建的最后有一句:env = OrderEnforcing(env),而OrderEnforcinggym.Wrapper的子类,在图2中我们也可以看到最后有两个类型为MyEnv的成员envunwrapped(红箭头所指),这两个成员和我理想中初始化的MyEnv是一样的,可以看到里面的参数和我输入的参数是一样的。

图1 env的类型

图2 env中的成员env和unwrapped
  1. 后续,我为了测试代码,在命令行输入语句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
  1. 所以2中我发现我的state并不会变化,导致我很迷惑,在后面一步步编译查看gym.make()的源码后,我发现了:
  • 该方法创建的env并不是我最初的那个MyEnv类型了
  • 仔细看了看env的变化,在env.state+=2语句前,env其实并没有state成员变量,见图2,这里应该是调用env.envenv.unwrappedstate成员变量。在那个语句之后,env出现state成员变量,所以在env.reset方法后,只修改了env.envenv.unwrappedstate成员变量,其实这样看来env.envenv.unwrapped应该引用了同一个类。
  • 虽然不知道底层原理为什么会导致这样,会突然自己创建一个state,也没搞懂envenv.envenv.unwrapped的关系,但是猜测是**类装饰器(wrapper)**的原因,这个点还不是很熟,一篇文章,可以看看,暂时不是很懂。

2、在看gym.make()的源码时,我了解到了两个修饰器Python中@property和@*.setter装饰器的详细用法,前者是为了让方法成为一个属性,不能更改,后者是为了让前者可以设置参数,后者一定要在前者后使用,详情见上述链接。