# 创建自定义环境
创建自定义环境
此文档将展示如何创建一个自定义的强化学习环境,用以测试和训练强化学习算法以解决各种问题。强化学习工具箱的环境以 gym 为框架,因此在使用前可以对 gym 进行一定的学习从而更好地理解。
在创建一个强化学习环境时,通常需要实现以下几个核心功能。
- Rset:这个函数用于将环境重置为初始状态,并返回一个初始状态。这个函数通常在每个回合开始时调用;
- Step:这是环境的主要函数,用于执行智能体采取的动作并返回与动作相关的信息。它接受一个动作作为输入,并返回环境的新状态、奖励、是否终止等信息;
- CloseEnv:关闭环境;
- 状态/动作空间信息:这些信息用于定义状态空间和动作空间的特性。
# 示例
使用 Julia 构建 Car2D 环境
创建一个连续状态空间、离散动作空间的环境 Car2D,环境的说明如下:
- 描述:一个在二维平面上移动的小车,state 代表点当前坐标 [x,y],小车每次随机向上下左右移动一个单位,目标是到达原点附近;
- 状态:[x,y],小车坐标。初始时小车在横坐标 -10~10,纵坐标 -10~10 范围内的随机位置(小车坐标虽然是离散取值,但此处仍将其视作连续输入);
- 动作:[1,2,3,4],分别代表向上、下、左、右移动一个单位;
- 奖励:小车到达目的地(横纵坐标绝对值之和小于等于 1)获得 +10 奖励,每移动一次获得 -0.1 奖励;
- 结束:当 x 和 y 的绝对值之和小于等于 1 或者步数超过 200 后结束。
首先要创建环境文件“Car2D.jl”,以下是完整的文件代码,可以直接复制使用。
import TyReinforcementLearning: Reset, Step, ActionDims, ActionSize, StateSize ,CloseEnv
using TyReinforcementLearning
Base.@kwdef mutable struct Car2D <: CustomEnv
state::Vector{Float64} = [0, 0]
steps::Int64 = 0
stateInfo = Dict("frame" => "CustomEnv", "envtype" => "continuous", "size" => (2,), "OriginalLowerLimit" => [-Inf, -Inf], "OriginalUpperLimit" => [Inf, Inf], "dtype" => "float")
actionInfo = Dict("frame" => "CustomEnv", "envtype" => "discrete", "start" => 1, "number" => 4, "dtype" => "int")
end
function Reset(env::Car2D)
x = 20 * rand() - 10
y = 20 * rand() - 10
env.state = [x, y]
env.steps = 0
return env.state, nothing
end
function Step(env::Car2D, action::Int64)
env.steps += 1
x, y = env.state
if action == 1
y += 1
elseif action == 2
y -= 1
elseif action == 3
x -= 1
elseif action == 4
x += 1
else
error("Invalid action")
end
env.state = [x, y]
if env.steps == 1000
done = true
else
done = false
end
if sum(abs.(env.state)) <= 1
reward = 10
done = true
else
reward = -0.1
end
return env.state, reward, done, nothing, nothing
end
function ActionDims(env::Car2D)
return 4
end
function ActionSize(env::Car2D)
return (1,)
end
function StateSize(env::Car2D)
return (2,)
end
function CloseEnv(env::Car2D)
return nothing
end
以下对代码各个部分进行解释。
定义环境结构体,其中 state 为环境当前状态,steps 为环境自上次Reset以来执行的步数。
Base.@kwdef mutable struct Car2D
state::Vector{Float64} = [0, 0]
steps::Int64 = 0
end
定义环境重置函数,输入为自定义环境对象。这里一定要对输入的变量进行类型声明,防止与工具箱内部的函数冲突。输出环境初始状态、辅助信息。其中辅助信息不起实际用途,可定义为任意值。
function Reset(env::Car2D)
x = 20 * rand() - 10
y = 20 * rand() - 10
env.state = [x, y]
env.steps = 0
return env.state, nothing
end
定义环境执行函数,输入变量为自定义环境对象,动作。这里一定要对输入的变量进行类型声明,防止与工具箱内部的函数冲突。函数输出环境新状态、奖励、是否结束、是否截断、辅助信息。其中后两个变量不起实际用途,可定义为任意值。
function Step(env::Car2D, action::Int64)
env.steps += 1
x, y = env.state
if action == 1
y += 1
elseif action == 2
y -= 1
elseif action == 3
x -= 1
elseif action == 4
x += 1
else
error("Invalid action")
end
env.state = [x, y]
if env.steps == 1000
done = true
else
done = false
end
if sum(abs.(env.state)) <= 1
reward = 10
done = true
else
reward = -0.1
end
return env.state, reward, done, nothing, nothing
end
定义动作空间信息。环境动作空间为离散,因此要定义ActionDims函数(如果是连续的,则要定义ActionRange函数),共有 [1,2,3,4] 四个动作,因此返回的动作数目为 4。在 Julia 中,环境动作应当设定以 1 为开始的连续正整数。
function ActionDims(env::Car2D)
return 4
end
定义状态空间信息。环境状态空间为连续,因此要定义StateSize函数(当状态空间为离散时,推荐将其转化为连续输入进行处理)。状态向量长度为 2,因此返回的形状为(2,)。
function StateSize(env::Car2D)
return (2,)
end
在创建了环境文件“Car2D.jl”之后,便可使用其对合适的智能体进行训练。
首先重启命令行,在包加载之后使用 import 包名: 函数名 的方式对包内函数进行扩展。然后是导入环境,此处 include 的是环境文件,可以自行替换为本地文件的保存路径。此时便完整实现了环境的导入。
下面给出一个示例来对环境进行使用。
using Flux
using TyReinforcementLearning
include("Car2D.jl")
env = Car2D()
critic_net = BuildDefaultNet(64, StateSize(env)[1], ActionDims(env))
models = rlDQNModels(
criticNet=critic_net,
criticOptimizer=Flux.Adam(),
)
option = rlDQNAgentOptions(
stateSize=StateSize(env),
actionNum=ActionDims(env)
)
agent = rlDQNAgent(models,option)
train_options = rlTrainOptions(
max_episodes=1000,
learning_interval=10,
path=joinpath(pwd(), "result")
)
result = train!(agent, env, train_options)
plot_result(result)
使用 Julia 构建 CarContinuous 环境
创建一个连续状态空间、连续动作空间的环境 CarContinuous,环境的说明如下:
- 描述:一个在一维数轴上移动的小车,state 代表点当前坐标 [x],小车每次左右移动一定距离,目标是到达原点附近;
- 状态:[x],小车坐标。初始时小车在 -20~-10 与 10~20 的随机位置;
- 动作:[deta_x],小车移动的距离,取值范围为 [-2,2],正数代表向右移动,负数代表向左移动;
- 奖励:小车到达目的地(坐标绝对值小于 0.1)获得 +20 奖励,每移动一次获得 -0.1*abs(x)奖励;
- 结束:当 x 的绝对值小于 0.1 或者步数超过 200 后结束。
首先要创建环境文件“CarContinuous.jl”,以下是完整的文件代码,可以直接复制使用。
import TyReinforcementLearning: Reset, Step, ActionRange, ActionSize, StateSize, CloseEnv
using TyReinforcementLearning
Base.@kwdef mutable struct CarContinuous <: CustomEnv
state::Vector{Float64} = [0.0]
steps::Int64 = 0
stateInfo = Dict("frame" => "CustomEnv", "envtype" => "continuous", "size" => (1,), "OriginalLowerLimit" => [-Inf], "OriginalUpperLimit" => [Inf], "dtype" => "float")
actionInfo = Dict("frame" => "CustomEnv", "envtype" => "continuous", "size" => (1,), "OriginalLowerLimit" => [-2], "OriginalUpperLimit" => [2], "dtype" => "float")
end
function Reset(env::CarContinuous)
env.state = rand([-1,1]) .* [10 * rand() + 10]
env.steps = 0
return env.state, nothing
end
function Step(env::CarContinuous, action::AbstractFloat)
env.steps += 1
if abs(action) > 2
error("Invalid action")
end
env.state .+= action
if env.steps == 200
done = true
else
done = false
end
if abs(env.state[1]) < 0.1
reward = 20
done = true
else
reward = -0.1 * abs(env.state[1])
end
return env.state, reward, done, nothing, nothing
end
function ActionRange(env::CarContinuous)
return 2
end
function ActionSize(env::CarContinuous)
return (1,)
end
function StateSize(env::CarContinuous)
return (1,)
end
function CloseEnv(env::CarContinuous)
return nothing
end
环境定义情况与上一示例类似,下面列出几个不同点:
- 连续动作空间时,应使动作取值范围关于原点对称;
- 对于连续动作空间,应当对
ActionRange函数进行扩展,返回动作取值范围 [-a,a] 的边界 a。
下面给出一个示例来对环境进行使用。
using Flux
using TyReinforcementLearning
include("CarContinuous.jl")
env = CarContinuous()
actor_net = Flux.Chain(
Flux.Dense(StateSize(env)[1], 16, relu),
Flux.Dense(16, 1, tanh),
ScaleLayer(ActionRange(env))
) |> f64
critic_net = Flux.Chain(
Join(vcat,
ScaleLayer(1),
ScaleLayer(1)),
Flux.Dense(StateSize(env)[1] + 1, 16, relu),
Flux.Dense(16, 1)
) |> f64
models = rlDDPGModels(
actorNet=actor_net,
criticNet=critic_net,
actorOptimizer=Flux.Adam(0.0001),
criticOptimizer=Flux.Adam(0.0001)
)
option = rlDDPGAgentOptions(
stateSize=StateSize(env),
actionRange=ActionRange(env),
maxStepsPerEpisode=200,
)
agent = rlDDPGAgent(models, option)
train_options = rlTrainOptions(
max_episodes=200,
learning_interval=1,
batch_size=256,
path=joinpath(pwd(), "result")
)
result = train!(agent, env, train_options)
plot_result(result)
此外,还可以使用 gym 的自定义环境功能,将 python 代码文件放到本地 gym 文件夹中,并修改注册文件,实现BuildEnv函数通过 id 调用本地自定义环境。
# 另请参阅
BuildEnv| ActionDims| ActionRange| Reset| StateDims| StateSize| Step