1#![allow(dead_code)]
4
5use crate::{
6 adam::{Adam, AdamW},
7 scheduler::LRScheduler,
8 sgd::SGD,
9};
10use trustformers_core::{errors::Result, tensor::Tensor, traits::Optimizer};
11
12pub struct BERTOptimizer {
14 base_optimizer: AdamW,
15 warmup_scheduler: Box<dyn LRScheduler>,
16 layer_wise_decay: f32,
17 weight_decay_exclusions: Vec<String>,
18 current_step: usize,
19 warmup_steps: usize,
20 total_steps: usize,
21}
22
23impl BERTOptimizer {
24 pub fn new(
25 learning_rate: f32,
26 warmup_steps: usize,
27 total_steps: usize,
28 layer_wise_decay: f32,
29 ) -> Result<Self> {
30 let base_optimizer = AdamW::new(learning_rate, (0.9, 0.999), 1e-6, 0.01);
31
32 let warmup_scheduler = Box::new(BERTWarmupScheduler::new(
34 learning_rate,
35 warmup_steps,
36 total_steps,
37 ));
38
39 let weight_decay_exclusions = vec![
41 "bias".to_string(),
42 "LayerNorm".to_string(),
43 "layer_norm".to_string(),
44 "ln".to_string(),
45 ];
46
47 Ok(Self {
48 base_optimizer,
49 warmup_scheduler,
50 layer_wise_decay,
51 weight_decay_exclusions,
52 current_step: 0,
53 warmup_steps,
54 total_steps,
55 })
56 }
57
58 fn get_layer_wise_lr(&self, param_name: &str, base_lr: f32) -> f32 {
60 if let Some(layer_num) = self.extract_layer_number(param_name) {
62 let decay_factor = self.layer_wise_decay.powi(layer_num as i32);
63 base_lr * decay_factor
64 } else {
65 base_lr
66 }
67 }
68
69 fn extract_layer_number(&self, param_name: &str) -> Option<usize> {
70 if param_name.contains("layer.") {
72 let parts: Vec<&str> = param_name.split('.').collect();
73 for i in 0..parts.len() {
74 if parts[i] == "layer" && i + 1 < parts.len() {
75 if let Ok(layer_num) = parts[i + 1].parse::<usize>() {
76 return Some(layer_num);
77 }
78 }
79 }
80 }
81 None
82 }
83
84 fn should_exclude_weight_decay(&self, param_name: &str) -> bool {
85 self.weight_decay_exclusions
86 .iter()
87 .any(|exclusion| param_name.contains(exclusion))
88 }
89}
90
91impl Optimizer for BERTOptimizer {
92 fn update(&mut self, parameter: &mut Tensor, grad: &Tensor) -> Result<()> {
93 self.base_optimizer.update(parameter, grad)
94 }
95
96 fn zero_grad(&mut self) {
97 self.base_optimizer.zero_grad()
98 }
99
100 fn step(&mut self) {
101 self.base_optimizer.step();
102 self.warmup_scheduler.step();
103 self.current_step += 1;
104 }
105
106 fn get_lr(&self) -> f32 {
107 self.base_optimizer.get_lr()
108 }
109
110 fn set_lr(&mut self, lr: f32) {
111 self.base_optimizer.set_lr(lr)
112 }
113}
114
115struct BERTWarmupScheduler {
117 base_lr: f32,
118 warmup_steps: usize,
119 total_steps: usize,
120 current_step: usize,
121}
122
123impl BERTWarmupScheduler {
124 fn new(base_lr: f32, warmup_steps: usize, total_steps: usize) -> Self {
125 Self {
126 base_lr,
127 warmup_steps,
128 total_steps,
129 current_step: 0,
130 }
131 }
132}
133
134impl LRScheduler for BERTWarmupScheduler {
135 fn step(&mut self) {
136 self.current_step += 1;
137 }
138
139 fn get_lr(&self, step: usize) -> f32 {
140 if step < self.warmup_steps {
141 self.base_lr * (step as f32 / self.warmup_steps as f32)
143 } else {
144 let progress =
146 (step - self.warmup_steps) as f32 / (self.total_steps - self.warmup_steps) as f32;
147 self.base_lr * (1.0 - progress).max(0.0)
148 }
149 }
150}
151
152pub struct GANOptimizer {
154 generator_optimizer: Adam,
155 discriminator_optimizer: Adam,
156 spectral_norm: bool,
157 gradient_penalty_weight: f32,
158 ttur: bool, d_steps_per_g_step: usize,
160 current_d_steps: usize,
161}
162
163impl GANOptimizer {
164 pub fn new(g_lr: f32, d_lr: f32, spectral_norm: bool, gradient_penalty_weight: f32) -> Self {
165 let generator_optimizer = Adam::new(g_lr, (0.0, 0.999), 1e-8, 0.0);
166 let discriminator_optimizer = Adam::new(d_lr, (0.0, 0.999), 1e-8, 0.0);
167
168 Self {
169 generator_optimizer,
170 discriminator_optimizer,
171 spectral_norm,
172 gradient_penalty_weight,
173 ttur: d_lr != g_lr,
174 d_steps_per_g_step: if d_lr > g_lr { 5 } else { 1 },
175 current_d_steps: 0,
176 }
177 }
178
179 pub fn step_discriminator(
180 &mut self,
181 d_params: &mut [Tensor],
182 d_grads: &[Tensor],
183 ) -> Result<()> {
184 let mut modified_grads = d_grads.to_vec();
186 if self.gradient_penalty_weight > 0.0 {
187 self.apply_gradient_penalty(&mut modified_grads)?;
188 }
189
190 if self.spectral_norm {
192 self.apply_spectral_norm(d_params)?;
193 }
194
195 for (param, grad) in d_params.iter_mut().zip(modified_grads.iter()) {
196 self.discriminator_optimizer.update(param, grad)?;
197 }
198 self.discriminator_optimizer.step();
199 self.current_d_steps += 1;
200 Ok(())
201 }
202
203 pub fn step_generator(&mut self, g_params: &mut [Tensor], g_grads: &[Tensor]) -> Result<()> {
204 if self.current_d_steps >= self.d_steps_per_g_step {
206 for (param, grad) in g_params.iter_mut().zip(g_grads.iter()) {
207 self.generator_optimizer.update(param, grad)?;
208 }
209 self.generator_optimizer.step();
210 self.current_d_steps = 0;
211 }
212 Ok(())
213 }
214
215 fn apply_gradient_penalty(&self, gradients: &mut [Tensor]) -> Result<()> {
216 for grad in gradients.iter_mut() {
218 let grad_norm = self.compute_gradient_norm(grad)?;
219 if grad_norm > 1.0 {
220 let penalty = (grad_norm - 1.0).powi(2) * self.gradient_penalty_weight;
221 *grad = grad.add_scalar(penalty)?;
222 }
223 }
224 Ok(())
225 }
226
227 fn apply_spectral_norm(&self, parameters: &mut [Tensor]) -> Result<()> {
228 for param in parameters.iter_mut() {
230 if param.shape().len() >= 2 {
231 let spectral_norm = self.compute_spectral_norm(param)?;
233 if spectral_norm > 1.0 {
234 *param = param.div_scalar(spectral_norm)?;
235 }
236 }
237 }
238 Ok(())
239 }
240
241 fn compute_gradient_norm(&self, grad: &Tensor) -> Result<f32> {
242 let sum_squares = grad.pow(2.0)?.sum(None, false)?;
244 let norm_tensor = sum_squares.sqrt()?;
245 let norm_data = norm_tensor.data()?;
247 Ok(norm_data[0].sqrt())
248 }
249
250 fn compute_spectral_norm(&self, weight: &Tensor) -> Result<f32> {
251 let weight_data = weight.data()?;
253 let len = weight_data.len();
254
255 if len == 0 {
257 return Ok(0.0);
258 }
259 if len == 1 {
260 return Ok(weight_data[0].abs());
261 }
262
263 if len <= 4 {
265 let frobenius_norm: f32 = weight_data.iter().map(|x| x * x).sum::<f32>().sqrt();
266 return Ok(frobenius_norm);
267 }
268
269 let sqrt_len = (len as f32).sqrt() as usize;
271 let rows = sqrt_len.max(1);
272 let cols = len.div_ceil(rows); let mut v: Vec<f32> = (0..cols).map(|i| ((i % 7) as f32) / 7.0 - 0.5).collect();
276 let mut v_norm = v.iter().map(|x| x * x).sum::<f32>().sqrt();
277 if v_norm > 0.0 {
278 for val in &mut v {
279 *val /= v_norm;
280 }
281 }
282
283 for _ in 0..5 {
285 let mut new_v = vec![0.0; rows];
287
288 for i in 0..rows {
290 for j in 0..cols {
291 let idx = i * cols + j;
292 if idx < len && j < v.len() {
293 new_v[i] += weight_data[idx] * v[j];
294 }
295 }
296 }
297
298 v_norm = new_v.iter().map(|x| x * x).sum::<f32>().sqrt();
300 if v_norm > 1e-8 {
301 for item in &mut new_v {
302 *item /= v_norm;
303 }
304 v = new_v;
306 } else {
307 break;
308 }
309 }
310
311 Ok(v_norm.max(1e-8)) }
314}
315
316pub struct RLOptimizer {
318 policy_optimizer: Adam,
319 value_optimizer: Adam,
320 clip_grad_norm: Option<f32>,
321 entropy_coeff: f32,
322 value_loss_coeff: f32,
323 max_grad_norm: f32,
324}
325
326impl RLOptimizer {
327 pub fn new(
328 policy_lr: f32,
329 value_lr: f32,
330 entropy_coeff: f32,
331 value_loss_coeff: f32,
332 max_grad_norm: f32,
333 ) -> Self {
334 let policy_optimizer = Adam::new(policy_lr, (0.9, 0.999), 1e-8, 0.0);
335 let value_optimizer = Adam::new(value_lr, (0.9, 0.999), 1e-8, 0.0);
336
337 Self {
338 policy_optimizer,
339 value_optimizer,
340 clip_grad_norm: Some(max_grad_norm),
341 entropy_coeff,
342 value_loss_coeff,
343 max_grad_norm,
344 }
345 }
346
347 pub fn step_policy(&mut self, params: &mut [Tensor], grads: &[Tensor]) -> Result<()> {
348 let mut modified_grads = grads.to_vec();
349
350 if let Some(max_norm) = self.clip_grad_norm {
352 self.clip_gradients(&mut modified_grads, max_norm)?;
353 }
354
355 self.apply_entropy_regularization(&mut modified_grads)?;
357
358 for (param, grad) in params.iter_mut().zip(modified_grads.iter()) {
359 self.policy_optimizer.update(param, grad)?;
360 }
361 self.policy_optimizer.step();
362 Ok(())
363 }
364
365 pub fn step_value(&mut self, params: &mut [Tensor], grads: &[Tensor]) -> Result<()> {
366 let mut modified_grads = grads.to_vec();
367
368 for grad in modified_grads.iter_mut() {
370 *grad = grad.mul_scalar(self.value_loss_coeff)?;
371 }
372
373 if let Some(max_norm) = self.clip_grad_norm {
375 self.clip_gradients(&mut modified_grads, max_norm)?;
376 }
377
378 for (param, grad) in params.iter_mut().zip(modified_grads.iter()) {
379 self.value_optimizer.update(param, grad)?;
380 }
381 self.value_optimizer.step();
382 Ok(())
383 }
384
385 fn clip_gradients(&self, gradients: &mut [Tensor], max_norm: f32) -> Result<()> {
386 let mut total_norm_sq: f32 = 0.0;
388 for grad in gradients.iter() {
389 let grad_norm_sq_tensor = grad.pow(2.0)?.sum(None, false)?;
390 let grad_norm_sq_data = grad_norm_sq_tensor.data()?;
391 total_norm_sq += grad_norm_sq_data[0];
392 }
393
394 let total_norm = total_norm_sq.sqrt();
395
396 if total_norm > max_norm {
397 let clip_factor = max_norm / total_norm;
398 for grad in gradients.iter_mut() {
399 *grad = grad.mul_scalar(clip_factor)?;
400 }
401 }
402
403 Ok(())
404 }
405
406 fn apply_entropy_regularization(&self, gradients: &mut [Tensor]) -> Result<()> {
407 for grad in gradients.iter_mut() {
409 let entropy_bonus = grad.mul_scalar(self.entropy_coeff)?;
410 *grad = grad.sub(&entropy_bonus)?;
411 }
412 Ok(())
413 }
414}
415
416pub struct MetaOptimizer {
418 meta_optimizer: Adam,
419 inner_optimizer: SGD,
420 inner_steps: usize,
421 inner_lr: f32,
422 meta_lr: f32,
423 first_order: bool, }
425
426impl MetaOptimizer {
427 pub fn new(meta_lr: f32, inner_lr: f32, inner_steps: usize, first_order: bool) -> Self {
428 let meta_optimizer = Adam::new(meta_lr, (0.9, 0.999), 1e-8, 0.0);
429 let inner_optimizer = SGD::new(inner_lr, 0.0, 0.0, false);
430
431 Self {
432 meta_optimizer,
433 inner_optimizer,
434 inner_steps,
435 inner_lr,
436 meta_lr,
437 first_order,
438 }
439 }
440
441 pub fn meta_step(&mut self, params: &mut [Tensor], meta_grads: &[Tensor]) -> Result<()> {
442 for (param, grad) in params.iter_mut().zip(meta_grads.iter()) {
443 self.meta_optimizer.update(param, grad)?;
444 }
445 self.meta_optimizer.step();
446 Ok(())
447 }
448
449 pub fn inner_loop(
450 &mut self,
451 mut params: Vec<Tensor>,
452 task_grads: &[Vec<Tensor>],
453 ) -> Result<Vec<Tensor>> {
454 for step in 0..self.inner_steps {
456 if step < task_grads.len() {
457 let grads = &task_grads[step];
458 for (param, grad) in params.iter_mut().zip(grads.iter()) {
459 self.inner_optimizer.update(param, grad)?;
460 }
461 self.inner_optimizer.step();
462 }
463 }
464 Ok(params)
465 }
466
467 pub fn compute_meta_gradients(
468 &self,
469 original_params: &[Tensor],
470 adapted_params: &[Tensor],
471 meta_loss_grads: &[Tensor],
472 ) -> Result<Vec<Tensor>> {
473 if self.first_order {
474 Ok(meta_loss_grads.to_vec())
476 } else {
477 self.compute_second_order_grads(original_params, adapted_params, meta_loss_grads)
479 }
480 }
481
482 fn compute_second_order_grads(
483 &self,
484 _original_params: &[Tensor],
485 _adapted_params: &[Tensor],
486 meta_loss_grads: &[Tensor],
487 ) -> Result<Vec<Tensor>> {
488 Ok(meta_loss_grads.to_vec())
491 }
492}
493
494pub fn create_bert_optimizer(
496 learning_rate: f32,
497 warmup_steps: usize,
498 total_steps: usize,
499) -> Result<BERTOptimizer> {
500 BERTOptimizer::new(learning_rate, warmup_steps, total_steps, 0.95)
501}
502
503pub fn create_gan_optimizer(g_lr: f32, d_lr: f32, use_spectral_norm: bool) -> GANOptimizer {
504 GANOptimizer::new(g_lr, d_lr, use_spectral_norm, 10.0)
505}
506
507pub fn create_ppo_optimizer(learning_rate: f32, entropy_coeff: f32) -> RLOptimizer {
508 RLOptimizer::new(learning_rate, learning_rate, entropy_coeff, 0.5, 0.5)
509}
510
511pub fn create_maml_optimizer(meta_lr: f32, inner_lr: f32, inner_steps: usize) -> MetaOptimizer {
512 MetaOptimizer::new(meta_lr, inner_lr, inner_steps, false)
513}