burn_train/learner/rl/
off_policy.rs1use crate::{
2 AgentEnvAsyncLoop, AgentEnvLoop, AsyncAgentEnvLoopConfig, EvaluationItem,
3 EventProcessorTraining, MultiAgentEnvLoop, RLComponents, RLComponentsTypes, RLEvent,
4 RLEventProcessorType, RLStrategy,
5};
6use burn_core::{self as burn};
7use burn_core::{config::Config, data::dataloader::Progress};
8use burn_rl::{
9 AsyncPolicy, Policy, PolicyLearner, SliceAccess, ToAction, ToObservation, TransitionBuffer,
10};
11
12#[derive(Config, Debug)]
14pub struct OffPolicyConfig {
15 #[config(default = 1)]
17 pub num_envs: usize,
18 #[config(default = 1)]
21 pub autobatch_size: usize,
22 #[config(default = 1024)]
24 pub replay_buffer_size: usize,
25 #[config(default = 1)]
27 pub train_interval: usize,
28 #[config(default = 1)]
30 pub train_steps: usize,
31 #[config(default = 10_000)]
33 pub eval_interval: usize,
34 #[config(default = 1)]
36 pub eval_episodes: usize,
37 #[config(default = 32)]
39 pub train_batch_size: usize,
40 #[config(default = 0)]
42 pub warmup_steps: usize,
43}
44
45pub struct OffPolicyStrategy {
47 config: OffPolicyConfig,
48}
49impl OffPolicyStrategy {
50 pub fn new(config: OffPolicyConfig) -> Self {
52 Self { config }
53 }
54}
55
56impl<RLC> RLStrategy<RLC> for OffPolicyStrategy
57where
58 RLC: RLComponentsTypes,
59 RLC::PolicyObs: SliceAccess,
60 RLC::PolicyAction: SliceAccess,
61{
62 fn train_loop(
63 &self,
64 training_components: RLComponents<RLC>,
65 learner_agent: &mut RLC::LearningAgent,
66 starting_epoch: usize,
67 env_init: RLC::EnvInit,
68 ) -> (RLC::Policy, RLEventProcessorType<RLC>) {
69 let mut event_processor = training_components.event_processor;
70 let mut checkpointer = training_components.checkpointer;
71 let num_steps_total = training_components.num_steps;
72
73 let inference_device = training_components.inference_device;
74 let mut env_runner = MultiAgentEnvLoop::<RLC>::new(
75 self.config.num_envs,
76 env_init.clone(),
77 AsyncPolicy::new(
78 self.config.num_envs.min(self.config.autobatch_size),
79 learner_agent.policy(),
80 ),
81 false,
82 false,
83 &inference_device,
84 );
85 let runner_config = AsyncAgentEnvLoopConfig {
86 eval: true,
87 deterministic: true,
88 id: 0,
89 };
90 let mut env_runner_valid = AgentEnvAsyncLoop::<RLC>::new(
91 env_init,
92 AsyncPolicy::new(1, learner_agent.policy()),
93 runner_config,
94 &inference_device,
95 None,
96 None,
97 );
98
99 let mut transition_buffer = TransitionBuffer::<RLC::PolicyObs, RLC::PolicyAction>::new(
100 self.config.replay_buffer_size,
101 &learner_agent.device(),
102 );
103
104 let mut valid_next = self.config.eval_interval + starting_epoch - 1;
105 let mut progress = Progress {
106 items_processed: starting_epoch,
107 items_total: num_steps_total,
108 unit: Some("steps".to_string()),
109 };
110
111 let mut intermediary_update: Option<<RLC::Policy as Policy>::PolicyState> = None;
112 while progress.items_processed < num_steps_total {
113 if training_components.interrupter.should_stop() {
114 if let Some(interruption) = training_components.interrupter.interruption() {
115 let reason = interruption.reason.as_deref().unwrap_or("reason unknown");
116 log::info!("Training interrupted: {reason}");
117 }
118 break;
119 }
120
121 let previous_steps = progress.items_processed;
122 let items = env_runner.run_steps(
123 self.config.train_interval,
124 &mut event_processor,
125 &training_components.interrupter,
126 &mut progress,
127 );
128
129 for item in &items {
130 let t = &item.transition;
131 let state: RLC::PolicyObs = t.state.clone().to_observation(&env_runner.device());
132 let next_state: RLC::PolicyObs =
133 t.next_state.clone().to_observation(&env_runner.device());
134 let action: RLC::PolicyAction = t.action.clone().to_action(&env_runner.device());
135 let reward = t.reward.try_to_vec_as::<f32>().unwrap()[0];
136 let done = t.done.try_to_vec_as::<f32>().unwrap()[0] > 0.5;
137 transition_buffer.push(state, next_state, action, reward, done);
138 }
139
140 if transition_buffer.len() >= self.config.train_batch_size
141 && progress.items_processed >= self.config.warmup_steps
142 {
143 if let Some(ref u) = intermediary_update {
144 env_runner.update_policy(u.clone());
145 }
146 for _ in 0..self.config.train_steps {
147 let batch = transition_buffer.sample(self.config.train_batch_size);
148 let train_item = learner_agent.train(batch);
149 intermediary_update = Some(learner_agent.policy().state());
150
151 training_components
152 .interrupter
153 .fail_on_error(event_processor.process_train(RLEvent::TrainStep(
154 EvaluationItem::new(train_item.item, progress.clone(), None),
155 )));
156 }
157 }
158
159 if valid_next > previous_steps && valid_next <= progress.items_processed {
160 training_components
161 .interrupter
162 .fail_on_error(event_processor.process_valid(
163 crate::AgentEvaluationEvent::Start(self.config.eval_episodes),
164 ));
165
166 env_runner_valid.update_policy(learner_agent.policy().state());
167 env_runner_valid.run_episodes(
168 self.config.eval_episodes,
169 &mut event_processor,
170 &training_components.interrupter,
171 &mut progress,
172 );
173
174 if let Some(checkpointer) = &mut checkpointer {
175 checkpointer.checkpoint(
176 &env_runner.policy(),
177 learner_agent,
178 valid_next,
179 &training_components.event_store,
180 );
181 }
182
183 valid_next += self.config.eval_interval;
184
185 training_components
186 .interrupter
187 .fail_on_error(event_processor.process_valid(crate::AgentEvaluationEvent::End));
188 }
189 }
190
191 (learner_agent.policy(), event_processor)
192 }
193}