burn_train/learner/
base.rs1use crate::checkpoint::{
2 AsyncCheckpointer, Checkpointer, CheckpointingAction, CheckpointingStrategy,
3};
4use crate::metric::store::EventStoreClient;
5use crate::{
6 CloneEarlyStoppingStrategy, LearnerModel, TrainOutput, TrainStep, TrainingError,
7 TrainingModelInput, TrainingModelOutput,
8};
9use burn_core::store::ModuleRecord;
10use burn_core::tensor::Device;
11use burn_optim::lr_scheduler::LrSchedulerRecord;
12use burn_optim::lr_scheduler::module_lr_scheduler::{ModuleLearningRate, ModuleLrScheduler};
13use burn_optim::{GradientsParams, ModuleOptimizer, MultiGradientsParams, OptimizerRecord};
14use std::marker::PhantomData;
15use std::sync::atomic::{AtomicBool, Ordering};
16use std::sync::{Arc, Mutex};
17
18pub struct Learner<M: LearnerModel> {
20 pub(crate) model: M,
21 optim: ModuleOptimizer,
22 lr_scheduler: ModuleLrScheduler,
23 lr_module: ModuleLearningRate,
24}
25
26impl<M: LearnerModel> Clone for Learner<M> {
27 fn clone(&self) -> Self {
28 Self {
29 model: self.model.clone(),
30 optim: self.optim.clone(),
31 lr_scheduler: self.lr_scheduler.clone(),
32 lr_module: self.lr_module.clone(),
33 }
34 }
35}
36
37impl<M: LearnerModel> Learner<M> {
38 pub fn new(
40 model: M,
41 optim: ModuleOptimizer,
42 lr_scheduler: impl Into<ModuleLrScheduler>,
43 ) -> Self {
44 Self {
45 model,
46 optim,
47 lr_scheduler: lr_scheduler.into(),
48 lr_module: 0.0.into(),
49 }
50 }
51}
52
53impl<M: LearnerModel> Learner<M> {
54 pub fn fork(&mut self, device: &Device) {
56 self.model = self.model().fork(device);
57 }
58
59 pub fn model(&self) -> M {
61 self.model.clone()
62 }
63
64 pub fn lr_current(&self) -> ModuleLearningRate {
66 self.lr_module.clone()
67 }
68
69 pub fn lr_step(&mut self) {
71 self.lr_module = self.lr_scheduler.step();
72 }
73
74 pub fn train_step(&self, item: TrainingModelInput<M>) -> TrainOutput<TrainingModelOutput<M>> {
84 TrainStep::step(&self.model, item)
85 }
86
87 pub fn optimizer_step(&mut self, grads: GradientsParams) {
95 self.model = self
96 .model()
97 .optimize(&mut self.optim, self.lr_module.clone(), grads);
98 }
99
100 pub fn optimizer_step_multi(&mut self, grads: MultiGradientsParams) {
108 self.model = self
109 .model()
110 .optimize_multi(&mut self.optim, self.lr_module.clone(), grads);
111 }
112
113 pub fn load_model(&mut self, record: ModuleRecord) {
115 self.model = self.model.clone().load_record(record);
116 }
117
118 pub fn load_optim(&mut self, record: OptimizerRecord) {
123 self.optim = self.optim.clone().load_record(record);
124 }
125
126 pub fn load_scheduler(&mut self, record: LrSchedulerRecord) {
128 self.lr_scheduler = self.lr_scheduler.clone().load_record(record);
129 }
130}
131
132pub struct LearningCheckpointer<M: LearnerModel> {
134 model: AsyncCheckpointer<ModuleRecord>,
135 optim: AsyncCheckpointer<OptimizerRecord>,
136 lr_scheduler: AsyncCheckpointer<LrSchedulerRecord>,
137 strategy: Box<dyn CheckpointingStrategy>,
138 _phantom: PhantomData<M>,
139}
140
141impl<M: LearnerModel> LearningCheckpointer<M> {
142 pub fn new(
144 model: AsyncCheckpointer<ModuleRecord>,
145 optim: AsyncCheckpointer<OptimizerRecord>,
146 lr_scheduler: AsyncCheckpointer<LrSchedulerRecord>,
147 strategy: Box<dyn CheckpointingStrategy>,
148 ) -> Self {
149 Self {
150 model,
151 optim,
152 lr_scheduler,
153 strategy,
154 _phantom: PhantomData,
155 }
156 }
157
158 pub fn checkpoint(&mut self, learner: &Learner<M>, epoch: usize, store: &EventStoreClient) {
160 let actions = self.strategy.checkpointing(epoch, store);
161
162 for action in actions {
163 match action {
164 CheckpointingAction::Delete(epoch) => {
165 self.model
166 .delete(epoch)
167 .expect("Can delete model checkpoint.");
168 self.optim
169 .delete(epoch)
170 .expect("Can delete optimizer checkpoint.");
171 self.lr_scheduler
172 .delete(epoch)
173 .expect("Can delete learning rate scheduler checkpoint.");
174 }
175 CheckpointingAction::Save => {
176 self.model
177 .save(epoch, learner.model.clone().into_record())
178 .expect("Can save model checkpoint.");
179 self.optim
180 .save(epoch, learner.optim.to_record())
181 .expect("Can save optimizer checkpoint.");
182 self.lr_scheduler
183 .save(epoch, learner.lr_scheduler.to_record())
184 .expect("Can save learning rate scheduler checkpoint.");
185 }
186 }
187 }
188 }
189
190 pub fn load_checkpoint(&self, mut learner: Learner<M>, epoch: usize) -> Learner<M> {
197 let record = self
198 .model
199 .restore(epoch)
200 .expect("Can load model checkpoint.");
201 learner.load_model(record);
202
203 let record = self
204 .optim
205 .restore(epoch)
206 .expect("Can load optimizer checkpoint.");
207 learner.load_optim(record);
208
209 let record = self
210 .lr_scheduler
211 .restore(epoch)
212 .expect("Can load learning rate scheduler checkpoint.");
213 learner.load_scheduler(record);
214
215 learner
216 }
217}
218
219pub(crate) type EarlyStoppingStrategyRef = Box<dyn CloneEarlyStoppingStrategy>;
221
222#[derive(Clone, Debug, PartialEq, Eq)]
224pub struct Interruption {
225 pub reason: Option<String>,
227}
228
229#[derive(Clone, Default)]
230pub struct Interrupter {
235 state: Arc<AtomicBool>,
236 message: Arc<Mutex<Option<String>>>,
237 error: Arc<Mutex<Option<Arc<TrainingError>>>>,
238}
239
240impl Interrupter {
241 pub fn new() -> Self {
243 Self::default()
244 }
245
246 pub fn stop(&self, reason: Option<&str>) {
250 self.state.store(true, Ordering::Relaxed);
251 reason.inspect(|r| {
252 let mut message = self.message.lock().unwrap();
253 *message = Some(String::from(*r));
254 });
255 }
256
257 pub fn fail(&self, error: impl Into<TrainingError>) {
263 let error = error.into();
264 log::error!("Stopped by an error: {error}");
265 let mut slot = self.error.lock().unwrap();
266 if slot.is_none() {
267 *slot = Some(Arc::new(error));
268 }
269 self.state.store(true, Ordering::Relaxed);
270 }
271
272 pub fn fail_on_error<E: Into<TrainingError>>(&self, result: Result<(), E>) {
274 if let Err(error) = result {
275 self.fail(error);
276 }
277 }
278
279 pub fn reset(&self) {
281 self.state.store(false, Ordering::Relaxed);
282 self.error.lock().unwrap().take();
283 }
284
285 pub fn should_stop(&self) -> bool {
287 self.state.load(Ordering::Relaxed)
288 }
289
290 pub fn interruption(&self) -> Option<Interruption> {
292 (self.should_stop() && self.error().is_none()).then(|| Interruption {
293 reason: self.get_message(),
294 })
295 }
296
297 pub fn error(&self) -> Option<Arc<TrainingError>> {
299 self.error.lock().unwrap().clone()
300 }
301
302 pub fn get_message(&self) -> Option<String> {
304 let message = self.message.lock().unwrap();
305 message.clone()
306 }
307}