burn_optim/optim/
grads.rs1use burn_core as burn;
2
3use burn::{
4 Tensor,
5 tensor::{Device, Gradients, container::TensorContainer},
6};
7
8use burn::module::{AutodiffModule, ParamId};
9
10use super::visitor::{GradientsParamsChangeDevice, GradientsParamsConverter};
11
12#[derive(Default, Debug)]
14pub struct GradientsParams {
15 container: TensorContainer<ParamId>,
16}
17
18impl GradientsParams {
19 pub fn new() -> Self {
21 Self::default()
22 }
23
24 pub fn from_grads<M: AutodiffModule>(grads: Gradients, module: &M) -> Self {
29 let mut grads = grads;
30 Self::from_module(&mut grads, module)
31 }
32
33 pub fn from_module<M: AutodiffModule>(grads: &mut Gradients, module: &M) -> Self {
35 let mut grads_params = GradientsParams::new();
36 let mut visitor = GradientsParamsConverter::<M>::new(grads, &mut grads_params, None);
37 module.visit(&mut visitor);
38 grads_params
39 }
40
41 pub fn from_params<M: AutodiffModule>(
43 grads: &mut Gradients,
44 module: &M,
45 params: &[ParamId],
46 ) -> Self {
47 let mut grads_params = GradientsParams::new();
48 let mut visitor =
49 GradientsParamsConverter::<M>::new(grads, &mut grads_params, Some(params.to_vec()));
50 module.visit(&mut visitor);
51 grads_params
52 }
53
54 pub fn get<const D: usize>(&self, id: ParamId) -> Option<Tensor<D>> {
61 self.container.get(&id)
62 }
63
64 pub fn remove<const D: usize>(&mut self, id: ParamId) -> Option<Tensor<D>> {
66 self.container.remove(&id)
67 }
68
69 pub fn register<const D: usize>(&mut self, id: ParamId, value: Tensor<D>) {
75 self.container.register(id, value)
77 }
78
79 pub fn len(&self) -> usize {
81 self.container.len()
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.len() == 0
87 }
88
89 pub fn to_device<M: AutodiffModule>(mut self, device: &Device, module: &M) -> Self {
91 let mut visitor = GradientsParamsChangeDevice::<M>::new(device, &mut self);
92 module.visit(&mut visitor);
93 self
94 }
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100 use burn::module::{Module, list_param_ids};
101 use burn::tensor::Distribution;
102 use burn_nn::{Linear, LinearConfig};
103
104 #[test]
105 fn test_convert_grads() {
106 let device = Device::default().autodiff();
107 let layer_1 = layer(&device);
108 let mut layer_2 = layer_1.clone();
109 layer_2 = layer_2.fork(&device);
110 let loss_1 = layer_1.forward(random_tensor(&device));
111 let loss_2 = layer_2.forward(random_tensor(&device));
112 let grads_1 = GradientsParams::from_grads(loss_1.backward(), &layer_1);
113 let grads_2 = GradientsParams::from_grads(loss_2.backward(), &layer_2);
114
115 let param_ids_1 = list_param_ids(&layer_1);
116 let param_ids_2 = list_param_ids(&layer_2);
117
118 assert_eq!(param_ids_1, param_ids_2);
119 assert_eq!(grads_1.len(), param_ids_1.len());
120 assert_eq!(grads_2.len(), param_ids_2.len());
121 }
122
123 fn layer(device: &Device) -> Linear {
124 LinearConfig::new(20, 20).init(device)
125 }
126
127 fn random_tensor(device: &Device) -> Tensor<2> {
128 Tensor::<2>::random([2, 20], Distribution::Default, device)
129 }
130}