使用OpenAI Gym构建强化学习环境
如果你曾经从事过深度学习(DL)或机器学习(ML),你就会知道监督学习和无监督学习是使用的两种关键技术。
强化学习(RL)之所以脱颖而出,是因为它被用来在现场环境中训练模型。
许多标准的OpenAI RL环境可以使你建立一个项目。它们包括CartPole-v0和SpaceInvaders-v0。
然而,它们可能是有局限性的,因为其中一些环境是为解决特定任务而建立的。
本教程将向你展示如何使用OpenAI Gym构建一个自定义的RL环境。具体来说,我们将建立一个RL模型来自动调节温度,使其达到一个最佳范围。
我们将使用OpenAI Gym完成这项任务,这是一个强化学习工具包,使你能够开发和比较RL算法。
前提条件
要跟上本教程,你需要熟悉。
- 强化学习和它的算法。
- 机器学习建模。
- 谷歌Colab或Jupyter笔记本。
目标
在我们开始之前,有几件事我们需要注意。
-
首先,我们希望我们的最佳温度在37到39摄氏度之间。
-
淋浴的长度将是60秒。如果你与其他标准RL环境合作过,你知道它们有一个情节长度。在这种情况下,我们的情节长度将是60秒。这意味着模型将试图在60秒内进入该最佳温度范围。
-
我们的模型将执行三个动作;调高、离开和调低。我们可以把我们的温度调高,调低,或者让它保持原状。
让我们从安装我们的依赖性开始。
安装和导入所需的依赖项
我们将安装四个关键的依赖项。
TensorFlow
允许我们对深度学习模型进行训练和推理。OpenAI Gym
允许我们建立我们的环境。Keras
是一个高水平的API,使我们能够建立深度学习模型。Keras-rl2
为我们提供几个预定义的代理来建立RL模型。
!pip install tensorflow==2.3.0
!pip install gym
!pip install keras
!pip install keras-rl2
下一步是将依赖关系导入我们的笔记本。
import numpy as np
from gym import Env
from gym.spaces import Box, Discrete
import random
在上面的代码中,我们已经导入了。
- 来自OpenAI Gym的
Env
类。这个占位符类允许我们在它上面建立我们的自定义环境。 Discrete
和Box
空间来自gym.spaces
。它们允许我们定义我们可以在环境中采取的行动和当前状态。numpy
来帮助我们进行数学运算。random
允许我们测试我们的随机环境。
用OpenAI Gym构建自定义RL环境
我们首先创建一个CustomEnv
类。当我们把Env
传递给CustomEnv
类时,我们继承了OpenAI Gym环境类中的方法和属性。
class CustomEnv(Env):
我们已经在CustomEnv
类中实现了四个不同的函数。我们创建了__init__
函数来初始化行动、观察和情节长度。
Discrete
空间接收一个固定的非负值范围。对于我们的情况,它需要三个行动; , , 。down (0)
stay(1)
up (2)
observation_space
将持有我们当前温度的数组。接下来,我们将我们的起始温度设置为38
度加上一个随机整数。最后,我们将淋浴长度设置为60
秒。
与
Discrete
空间不同,Box
空间更加灵活,允许我们在0
和100
之间传递多个值。此外,你可以用它来保存图像、音频和数据帧。
def __init__(self):
self.action_space = Discrete(3)
self.observation_space = Box(low=np.array([0]), high=np.array([100]))
self.state = 38 + random.randint(-3,3)
self.shower_length = 60
step
函数定义了我们在采取行动后所做的事情。我们已经将我们的动作值设置为-1
。理想情况下,这意味着。
- 如果我们将行动(0)与
-1
一起应用,我们会得到一个-1
的值。这个动作将使温度降低1
。 - 如果我们将动作(1)与
-1
一起应用,我们会得到一个0
的值。这个动作将保持当前的温度。 - 如果我们将动作(2)与
-1
一起应用,我们会得到一个1
的值。这个动作将增加温度,1
。
我们也在减少淋浴的长度,1
。
在计算奖励时,如果温度在其最佳范围内,即37
,和39
,我们给出的奖励是1
。
如果温度不在最佳范围内,我们给出的奖励是-1
。我们的模型会一直尝试用这个函数收敛,使温度在最佳范围内。
def step(self, action):
self.state += action -1
self.shower_length -= 1
# Calculating the reward
if self.state >=37 and self.state <=39:
reward =1
else:
reward = -1
# Checking if shower is done
if self.shower_length <= 0:
done = True
else:
done = False
# Setting the placeholder for info
info = {}
# Returning the step information
return self.state, reward, done, info
我们使用render
函数来显示我们的结果。然而,我们不会在本教程中使用它。但是,这是你编写可视化代码的地方。
def render(self):
# This is where you would write the visualization code
我们使用reset
函数来重置我们的环境或更新每个情节。它重设了淋浴温度和时间。
def reset(self):
self.state = 38 + random.randint(-3,3)
self.shower_length = 60
return self.state
让我们把我们的类存储在一个名为env
的自定义变量内。
env = CustomEnv()
让我们来玩玩我们的环境。
episodes = 20 #20 shower episodes
for episode in range(1, episodes+1):
state = env.reset()
done = False
score = 0
while not done:
action = env.action_space.sample()
n_state, reward, done, info = env.step(action)
score+=reward
print('Episode:{} Score:{}'.format(episode, score))
输出。
Episode:1 Score:-44
Episode:2 Score:-58
Episode:3 Score:-26
Episode:4 Score:-56
Episode:5 Score:-28
Episode:6 Score:-60
Episode:7 Score:-38
Episode:8 Score:2
Episode:9 Score:-22
Episode:10 Score:-34
Episode:11 Score:-60
Episode:12 Score:-10
Episode:13 Score:-24
Episode:14 Score:-18
Episode:15 Score:22
Episode:16 Score:-12
Episode:17 Score:-28
Episode:18 Score:-40
Episode:19 Score:22
Episode:20 Score:-30
在运行了20
不同的淋浴后,我们得到不同的奖励值。记住,如果淋浴不在37
和39
度之间的最佳范围内,我们得到的奖励是-1
。
大多数奖励表明,我们远远超出了最佳温度范围。最好的奖励是22
,这表明我们采取的一些步骤可能是在这个最佳范围内。
让我们继续使用Keras来建立一个深度学习模型。
使用Keras创建一个深度学习模型
我们首先从TensorFlow中导入所有必要的依赖关系。
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.optimizers import Adam
下一步涉及到定义我们的states
和actions
。
states = env.observation_space.shape
actions = env.action_space.n
当你运行上面的代码时,它会给你我们的状态的形状和行动的数量。现在我们来建立我们的模型。
def build_model(states, actions):
model = Sequential()
model.add(Dense(24, activation='relu', input_shape=states))
model.add(Dense(24, activation='relu'))
model.add(Dense(actions, activation='linear'))
return model
在模型中,我们将温度(input_shape=states
)传递给我们的深度学习模型的输入,然后返回三个不同的动作。
model = build_model(states, actions)
当你运行下面的代码时,它将给我们的模型一个总结。
model.summary()
然后,我们可以将这个模型传递给Keras-RL
模型。
用Keras-RL构建代理
我们首先从Keras-RL中导入必要的依赖项。
from rl.agents import DQNAgent
from rl.policy import BoltzmannQPolicy
from rl.memory import SequentialMemory
然后,我们使用上面一节中创建的模型建立一个DQNagent
。我们使用Boltzmann Q策略。
它在q
值上建立了一个概率规律,并返回一个根据这个规律随机选择的行动。
DQN代理使用Sequential memory
来存储各种状态、行动和奖励。
def build_agent(model, actions):
policy = BoltzmannQPolicy()
memory = SequentialMemory(limit=50000, window_length=1)
dqn = DQNAgent(model=model, memory=memory, policy=policy,
nb_actions=actions, nb_steps_warmup=10, target_model_update=1e-2)
return dqn
dqn = build_agent(model, actions)
dqn.compile(Adam(lr=1e-3), metrics=['mae'])
dqn.fit(env, nb_steps=60000, visualize=False, verbose=1)
在上面的代码中,我们的自定义RL环境现在可以训练我们的dqn
模型来设置正确的最佳温度。
我们训练代理60000步,但你可以训练更长时间以产生更好的结果。你可以使用nb_steps
参数来改变它。
如果你碰巧遇到这个属性错误,Sequential
对象没有属性_compile_time_distribution_strategy
,请确保在build_model
函数之后包括del model
,然后你可以重新运行单元。
在60000
步之后,我们得到的奖励是-0.3908
。在最初的10000
步中,我们开始的奖励是-0.6412
。最后减少到-0.3908
。
该模型并不完美,但当我们增加训练步骤的数量时,你会得到更好的结果(正的奖励)。
正值意味着温度在其最佳温度之内。你可以尝试在创建模型时加入一些随机数字,看看你的代理在训练后会有什么表现。
测试我们的自定义RL环境
训练完我们的模型后,我们就可以去测试它了。让我们写下以下代码。
results = dqn.test(env, nb_episodes=150, visualize=False)
print(np.mean(results.history['episode_reward']))
经测试,我们的平均奖励值为59
。这是一个高奖励。我们的模型表现良好。然而,当你在你的模型中加入一些噪音时,情况可能就不是这样了。
这是一个理想的例子,可能不代表真实情况,即你的朋友随机调整淋浴温度。试着做实验,看看你能得到什么。
总结
在本教程中,我们使用OpenAI Gym和Keras构建了一个自定义的RL环境。