Документация Engee

Эпизодические и неэпизодические среды

Эпизодические среды

По умолчанию 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)))