1use crate::{
4 optimizer::BaseOptimizer, Optimizer, OptimizerError, OptimizerResult, OptimizerState,
5 ParamGroup,
6};
7use parking_lot::RwLock;
8use std::collections::HashMap;
9use std::ops::Add;
10use std::sync::Arc;
11use torsh_core::error::Result;
12use torsh_tensor::Tensor;
13
14pub struct AdaGrad {
16 base: BaseOptimizer,
17 #[allow(dead_code)]
18 lr_decay: f32,
19 #[allow(dead_code)]
20 weight_decay: f32,
21 #[allow(dead_code)]
22 initial_accumulator_value: f32,
23 #[allow(dead_code)]
24 eps: f32,
25}
26
27impl AdaGrad {
28 pub fn new(
30 params: Vec<Arc<RwLock<Tensor>>>,
31 lr: Option<f32>,
32 lr_decay: Option<f32>,
33 weight_decay: Option<f32>,
34 initial_accumulator_value: Option<f32>,
35 eps: Option<f32>,
36 ) -> Self {
37 let lr = lr.unwrap_or(1e-2);
38 let lr_decay = lr_decay.unwrap_or(0.0);
39 let weight_decay = weight_decay.unwrap_or(0.0);
40 let initial_accumulator_value = initial_accumulator_value.unwrap_or(0.0);
41 let eps = eps.unwrap_or(1e-10);
42
43 let mut defaults = HashMap::new();
44 defaults.insert("lr".to_string(), lr);
45 defaults.insert("lr_decay".to_string(), lr_decay);
46 defaults.insert("weight_decay".to_string(), weight_decay);
47 defaults.insert(
48 "initial_accumulator_value".to_string(),
49 initial_accumulator_value,
50 );
51 defaults.insert("eps".to_string(), eps);
52
53 let param_group = ParamGroup::new(params, lr);
54
55 let base = BaseOptimizer {
56 param_groups: vec![param_group],
57 state: HashMap::new(),
58 optimizer_type: "AdaGrad".to_string(),
59 defaults,
60 };
61
62 Self {
63 base,
64 lr_decay,
65 weight_decay,
66 initial_accumulator_value,
67 eps,
68 }
69 }
70}
71
72impl Optimizer for AdaGrad {
73 fn step(&mut self) -> OptimizerResult<()> {
74 for group in &mut self.base.param_groups {
75 for param_arc in &group.params {
76 let mut param = param_arc.write();
77
78 if !param.has_grad() {
80 continue;
81 }
82
83 let grad = param
84 .grad()
85 .expect("gradient should exist after has_grad check");
86 let param_id = format!("{:p}", param_arc.as_ref());
87
88 let mut grad = grad;
90 if self.weight_decay != 0.0 {
91 let weight_decay_term = param
92 .mul_scalar(self.weight_decay)
93 .map_err(OptimizerError::TensorError)?;
94 grad = grad
95 .add(&weight_decay_term)
96 .map_err(OptimizerError::TensorError)?;
97 }
98
99 let needs_init = !self.base.state.contains_key(¶m_id);
101 let state = self
102 .base
103 .state
104 .entry(param_id.clone())
105 .or_insert_with(HashMap::new);
106
107 if needs_init {
108 let mut sum_of_squares = torsh_tensor::creation::zeros_like(¶m)?;
110 if self.initial_accumulator_value != 0.0 {
111 sum_of_squares
112 .add_scalar_(self.initial_accumulator_value)
113 .expect("adding initial accumulator value should succeed");
114 }
115 state.insert("sum_of_squares".to_string(), sum_of_squares);
116 state.insert(
117 "step".to_string(),
118 torsh_tensor::creation::zeros_like(¶m)?,
119 );
120 }
121
122 let mut sum_of_squares = state
123 .get("sum_of_squares")
124 .expect("sum_of_squares state should exist")
125 .clone();
126 let mut step_tensor = state.get("step").expect("step state should exist").clone();
127
128 step_tensor
130 .add_scalar_(1.0)
131 .map_err(OptimizerError::TensorError)?;
132 let step = step_tensor.to_vec().map_err(OptimizerError::TensorError)?[0] as f32;
133
134 let grad_squared = grad.mul_op(&grad).map_err(OptimizerError::TensorError)?;
137 sum_of_squares = sum_of_squares
138 .add(&grad_squared)
139 .map_err(OptimizerError::TensorError)?;
140
141 let clr = if self.lr_decay != 0.0 {
143 group.lr / (1.0 + (step - 1.0) * self.lr_decay)
144 } else {
145 group.lr
146 };
147
148 let std = sum_of_squares
150 .sqrt()
151 .map_err(OptimizerError::TensorError)?
152 .add_scalar(self.eps)
153 .map_err(OptimizerError::TensorError)?;
154
155 let update = grad
158 .div(&std)
159 .map_err(OptimizerError::TensorError)?
160 .mul_scalar(clr)
161 .map_err(OptimizerError::TensorError)?;
162 crate::param_update::sub_assign(&mut param, &update)
163 .map_err(OptimizerError::TensorError)?;
164
165 state.insert("sum_of_squares".to_string(), sum_of_squares);
167 state.insert("step".to_string(), step_tensor);
168 }
169 }
170
171 Ok(())
172 }
173
174 fn zero_grad(&mut self) {
175 self.base.zero_grad();
176 }
177
178 fn get_lr(&self) -> Vec<f32> {
179 self.base.get_lr()
180 }
181
182 fn set_lr(&mut self, lr: f32) {
183 self.base.set_lr(lr);
184 }
185
186 fn set_lrs(&mut self, lrs: &[f32]) {
187 self.base.set_lrs(lrs);
188 }
189
190 fn add_param_group(&mut self, params: Vec<Arc<RwLock<Tensor>>>, options: HashMap<String, f32>) {
191 self.base.add_param_group(params, options);
192 }
193
194 fn parameters(&self) -> Vec<Arc<RwLock<Tensor>>> {
195 self.base.parameters()
196 }
197
198 fn state_dict(&self) -> OptimizerResult<OptimizerState> {
199 self.base.state_dict()
200 }
201
202 fn load_state_dict(&mut self, state: OptimizerState) -> OptimizerResult<()> {
203 self.base.load_state_dict(state)
204 }
205}
206
207pub struct AdaGradBuilder {
209 lr: f32,
210 lr_decay: f32,
211 weight_decay: f32,
212 initial_accumulator_value: f32,
213 eps: f32,
214}
215
216impl AdaGradBuilder {
217 pub fn new() -> Self {
218 Self {
219 lr: 1e-2,
220 lr_decay: 0.0,
221 weight_decay: 0.0,
222 initial_accumulator_value: 0.0,
223 eps: 1e-10,
224 }
225 }
226
227 pub fn lr(mut self, lr: f32) -> Self {
228 self.lr = lr;
229 self
230 }
231
232 pub fn lr_decay(mut self, lr_decay: f32) -> Self {
233 self.lr_decay = lr_decay;
234 self
235 }
236
237 pub fn weight_decay(mut self, weight_decay: f32) -> Self {
238 self.weight_decay = weight_decay;
239 self
240 }
241
242 pub fn initial_accumulator_value(mut self, value: f32) -> Self {
243 self.initial_accumulator_value = value;
244 self
245 }
246
247 pub fn eps(mut self, eps: f32) -> Self {
248 self.eps = eps;
249 self
250 }
251
252 pub fn build(self, params: Vec<Arc<RwLock<Tensor>>>) -> AdaGrad {
253 AdaGrad::new(
254 params,
255 Some(self.lr),
256 Some(self.lr_decay),
257 Some(self.weight_decay),
258 Some(self.initial_accumulator_value),
259 Some(self.eps),
260 )
261 }
262}
263
264impl Default for AdaGradBuilder {
265 fn default() -> Self {
266 Self::new()
267 }
268}