#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LrSchedule {
Constant(f32),
Step {
base: f32,
step_size: usize,
gamma: f32,
},
Cosine { base: f32, min: f32, total: usize },
Warmup { base: f32, warmup: usize },
WarmupCosine {
base: f32,
min: f32,
warmup: usize,
total: usize,
},
}
impl LrSchedule {
pub fn lr_at(&self, step: usize) -> f32 {
match *self {
LrSchedule::Constant(lr) => lr,
LrSchedule::Step {
base,
step_size,
gamma,
} => {
let k = step.checked_div(step_size).unwrap_or(0);
base * gamma.powi(k as i32)
}
LrSchedule::Cosine { base, min, total } => cosine(base, min, step, total),
LrSchedule::Warmup { base, warmup } => {
if warmup > 0 && step < warmup {
base * (step as f32 + 1.0) / warmup as f32
} else {
base
}
}
LrSchedule::WarmupCosine {
base,
min,
warmup,
total,
} => {
if warmup > 0 && step < warmup {
base * (step as f32 + 1.0) / warmup as f32
} else {
cosine(base, min, step - warmup, total.saturating_sub(warmup))
}
}
}
}
}
fn cosine(base: f32, min: f32, step: usize, total: usize) -> f32 {
if total == 0 {
return min;
}
let t = (step.min(total)) as f32 / total as f32;
let factor = 0.5 * (1.0 + (std::f32::consts::PI * t).cos());
min + (base - min) * factor
}