Эпизодические и неэпизодические среды
Эпизодические среды
По умолчанию run(policy, env, stop_condition, hook)
выполняет env
пошагово до достижения конечного состояния и сообщает о завершении эпизода. Для этого в env
должна быть реализована функция RLBase.is_terminated(::YourEnvironment)
. Она вызывается после каждого шага по среде. Когда она возвращает значение true
, траектория регистрирует конечное состояние, вызывается функция RLBase.reset!(::YourEnvironment)
и среда возвращается в начальное состояние (или одно из них, если их несколько).
Для этого конечное состояние должно устанавливаться в значение 0, если оно было получено в результате обучения путем инициализации.
Неэпизодические среды
У неэпизодических сред, также называемых непрерывными задачами (Sutton & Barto, 2018), нет конечного состояния, поэтому они могут выполняться бесконечно или до достижения конечного условия (stop_condition
). Однако иногда может требоваться периодически сбрасывать среду, чтобы начать выполнение заново. Первым способом является реализация RLBase.is_terminated(::YourEnvironment)
для сброса среды согласно произвольному условию. Однако это может быть не самым лучшим вариантом, потому что последнее состояние (обратите внимание, что это не конечное состояние) будет инициализироваться со значением 0 во время обучения, хотя это и не истинное значение состояния.
Чтобы решить эту проблему, мы предоставляем условие ResetAfterNSteps(n)
в качестве аргумента вызова run(policy, env, stop_condition, hook, reset_condition = ResetIfEnvTerminated())
. Условие по умолчанию ResetIfEnvTerminated()
предполагает, что среда эпизодическая. Если изменить его на ResetAfterNSteps(n)
, проверка is_terminated
больше не будет проводиться, а вместо этого функция reset!
будет вызываться каждые n
шагов. Благодаря этому значение последнего состояния не будет умножаться на 0 во время инициализации, что позволяет изучить правильное значение.
Пользовательские условия сброса
Вместо использования встроенных условий reset_condition
можно задать пользовательское условие. Условие должно вызываться с помощью метода RLCore.check!(my_condition, policy, env)
. Например, вот как можно реализовать пользовательское условие, которое проверяет конечное состояние, но также производит сброс, если эпизод длится слишком долго:
using ReinforcementLearning
import ReinforcementLearning: RLCore
reset_n_steps = ResetAfterNSteps(10000)
struct MyCondition <: AbstractResetCondition end
function RLCore.check!(my_condition::MyCondition, policy, env)
terminal = is_terminated(env)
too_long = RLCore.check!(reset_n_steps, policy, env)
return terminal || too_long
end
env = RandomWalk1D()
agent = RandomPolicy()
stop_condition = StopIfEnvTerminated()
hook = EmptyHook()
run(agent, env, stop_condition, hook, MyCondition())
Чтобы избежать глобального значения reset_n_step
, можно использовать вызываемую структуру вместо функции.
mutable struct MyCondition1 <: AbstractResetCondition
reset_after
end
RLCore.check!(c::MyCondition1, policy, env) = is_terminated(env) || RLCore.check!(c.reset_after, policy, env)
run(agent, env, stop_condition, hook, MyCondition1(ResetAfterNSteps(10000)))