1use crate::layers::lstm_cell::{masked, LSTMCell, LSTMCellCache, LSTMCellGradients};
2use crate::optimizers::Optimizer;
3use ndarray::Array2;
4
5#[derive(Clone, Debug, PartialEq)]
7pub struct LSTMState {
8 pub h: Vec<Array2<f64>>,
9 pub c: Vec<Array2<f64>>,
10}
11
12#[derive(Clone)]
14pub struct LSTMNetworkCache {
15 pub cell_caches: Vec<LSTMCellCache>,
16 pub output_dropout_masks: Vec<Option<Array2<f64>>>,
18}
19
20#[derive(Clone)]
26pub struct LSTMNetwork {
27 cells: Vec<LSTMCell>,
28 pub input_size: usize,
29 pub hidden_size: usize,
30 pub num_layers: usize,
31 pub is_training: bool,
32}
33
34impl LSTMNetwork {
35 pub fn new(input_size: usize, hidden_size: usize, num_layers: usize) -> Self {
40 let mut cells = Vec::new();
41
42 for i in 0..num_layers {
43 let layer_input_size = if i == 0 { input_size } else { hidden_size };
44 cells.push(LSTMCell::new(layer_input_size, hidden_size));
45 }
46
47 LSTMNetwork {
48 cells,
49 input_size,
50 hidden_size,
51 num_layers,
52 is_training: true,
53 }
54 }
55
56 pub fn with_input_dropout(mut self, dropout_rate: f64, variational: bool) -> Self {
57 for cell in &mut self.cells {
58 *cell = cell.clone().with_input_dropout(dropout_rate, variational);
59 }
60 self
61 }
62
63 pub fn with_recurrent_dropout(mut self, dropout_rate: f64, variational: bool) -> Self {
64 for cell in &mut self.cells {
65 *cell = cell
66 .clone()
67 .with_recurrent_dropout(dropout_rate, variational);
68 }
69 self
70 }
71
72 pub fn with_output_dropout(mut self, dropout_rate: f64) -> Self {
73 for (i, cell) in self.cells.iter_mut().enumerate() {
74 if i < self.num_layers - 1 {
75 *cell = cell.clone().with_output_dropout(dropout_rate);
76 }
77 }
78 self
79 }
80
81 pub fn with_zoneout(mut self, cell_zoneout_rate: f64, hidden_zoneout_rate: f64) -> Self {
82 for cell in &mut self.cells {
83 *cell = cell
84 .clone()
85 .with_zoneout(cell_zoneout_rate, hidden_zoneout_rate);
86 }
87 self
88 }
89
90 pub fn with_layer_dropout(mut self, layer_configs: Vec<LayerDropoutConfig>) -> Self {
91 for (i, config) in layer_configs.into_iter().enumerate() {
92 if i < self.cells.len() {
93 let mut cell = self.cells[i].clone();
94
95 if let Some((rate, variational)) = config.input_dropout {
96 cell = cell.with_input_dropout(rate, variational);
97 }
98 if let Some((rate, variational)) = config.recurrent_dropout {
99 cell = cell.with_recurrent_dropout(rate, variational);
100 }
101 if let Some(rate) = config.output_dropout {
102 cell = cell.with_output_dropout(rate);
103 }
104 if let Some((cell_rate, hidden_rate)) = config.zoneout {
105 cell = cell.with_zoneout(cell_rate, hidden_rate);
106 }
107
108 self.cells[i] = cell;
109 }
110 }
111 self
112 }
113
114 pub fn train(&mut self) {
115 self.is_training = true;
116 for cell in &mut self.cells {
117 cell.train();
118 }
119 }
120
121 pub fn eval(&mut self) {
122 self.is_training = false;
123 for cell in &mut self.cells {
124 cell.eval();
125 }
126 }
127
128 pub fn from_cells(
130 cells: Vec<LSTMCell>,
131 input_size: usize,
132 hidden_size: usize,
133 num_layers: usize,
134 ) -> Self {
135 LSTMNetwork {
136 cells,
137 input_size,
138 hidden_size,
139 num_layers,
140 is_training: true,
141 }
142 }
143
144 pub fn get_cells(&self) -> &[LSTMCell] {
146 &self.cells
147 }
148
149 pub fn get_cells_mut(&mut self) -> &mut [LSTMCell] {
151 &mut self.cells
152 }
153
154 pub fn zero_state(&self, batch_size: usize) -> LSTMState {
156 let zeros = || {
157 (0..self.num_layers)
158 .map(|_| Array2::zeros((self.hidden_size, batch_size)))
159 .collect()
160 };
161 LSTMState {
162 h: zeros(),
163 c: zeros(),
164 }
165 }
166
167 pub fn reset_dropout_masks(&mut self) {
169 for cell in &mut self.cells {
170 cell.reset_dropout_masks();
171 }
172 }
173
174 pub fn forward(&mut self, input: &Array2<f64>, state: &LSTMState) -> (Array2<f64>, LSTMState) {
177 let (output, state, _) = self.forward_with_cache(input, state);
178 (output, state)
179 }
180
181 pub fn forward_with_cache(
183 &mut self,
184 input: &Array2<f64>,
185 state: &LSTMState,
186 ) -> (Array2<f64>, LSTMState, LSTMNetworkCache) {
187 assert_eq!(
188 state.h.len(),
189 self.num_layers,
190 "state must hold one entry per layer"
191 );
192 let mut layer_input = input.clone();
193 let mut next = LSTMState {
194 h: Vec::with_capacity(self.num_layers),
195 c: Vec::with_capacity(self.num_layers),
196 };
197 let mut cell_caches = Vec::with_capacity(self.num_layers);
198 let mut output_dropout_masks = Vec::with_capacity(self.num_layers);
199
200 let last = self.num_layers - 1;
201 for (l, cell) in self.cells.iter_mut().enumerate() {
202 let (hy, cy, cache) = cell.forward_with_cache(&layer_input, &state.h[l], &state.c[l]);
203 let mask = if l < last {
204 cell.output_dropout_mask(hy.raw_dim())
205 } else {
206 None
207 };
208 layer_input = masked(&hy, &mask);
209 next.h.push(hy);
210 next.c.push(cy);
211 cell_caches.push(cache);
212 output_dropout_masks.push(mask);
213 }
214
215 let cache = LSTMNetworkCache {
216 cell_caches,
217 output_dropout_masks,
218 };
219 (layer_input, next, cache)
220 }
221
222 pub fn forward_sequence(&mut self, sequence: &[Array2<f64>]) -> Vec<Array2<f64>> {
225 self.forward_sequence_with_cache(sequence)
226 .0
227 .into_iter()
228 .map(|(h, _)| h)
229 .collect()
230 }
231
232 pub fn forward_sequence_with_cache(
235 &mut self,
236 sequence: &[Array2<f64>],
237 ) -> (Vec<(Array2<f64>, Array2<f64>)>, Vec<LSTMNetworkCache>) {
238 let batch_size = sequence.first().map_or(1, |x| x.ncols());
239 let mut state = self.zero_state(batch_size);
240 self.reset_dropout_masks();
241
242 let mut outputs = Vec::with_capacity(sequence.len());
243 let mut caches = Vec::with_capacity(sequence.len());
244 for input in sequence {
245 let (output, next, cache) = self.forward_with_cache(input, &state);
246 outputs.push((output, next.c[self.num_layers - 1].clone()));
247 caches.push(cache);
248 state = next;
249 }
250 (outputs, caches)
251 }
252
253 pub fn backward_sequence(
260 &self,
261 d_outputs: &[Array2<f64>],
262 caches: &[LSTMNetworkCache],
263 ) -> (Vec<LSTMCellGradients>, Vec<Array2<f64>>) {
264 assert_eq!(
265 d_outputs.len(),
266 caches.len(),
267 "one output gradient per time step"
268 );
269 let mut gradients = self.zero_gradients();
270 let mut d_inputs = vec![Array2::zeros((0, 0)); caches.len()];
271 let batch_size = d_outputs.first().map_or(1, |d| d.ncols());
272 let mut dh_next = self.zero_state(batch_size);
273
274 let last = self.num_layers - 1;
275 for t in (0..caches.len()).rev() {
276 let mut d_above = d_outputs[t].clone();
277 for l in (0..self.num_layers).rev() {
278 let d_out = if l < last {
279 masked(&d_above, &caches[t].output_dropout_masks[l])
280 } else {
281 d_above
282 };
283 let dh = &d_out + &dh_next.h[l];
284 let (g, dx, dhx, dcx) =
285 self.cells[l].backward(&dh, &dh_next.c[l], &caches[t].cell_caches[l]);
286 gradients[l].accumulate(&g);
287 dh_next.h[l] = dhx;
288 dh_next.c[l] = dcx;
289 d_above = dx;
290 }
291 d_inputs[t] = d_above;
292 }
293 (gradients, d_inputs)
294 }
295
296 pub fn update_parameters<O: Optimizer>(
298 &mut self,
299 gradients: &[LSTMCellGradients],
300 optimizer: &mut O,
301 ) {
302 for (i, (cell, cell_gradients)) in self.cells.iter_mut().zip(gradients.iter()).enumerate() {
303 let prefix = format!("layer_{}", i);
304 cell.update_parameters(cell_gradients, optimizer, &prefix);
305 }
306 }
307
308 pub fn zero_gradients(&self) -> Vec<LSTMCellGradients> {
310 self.cells
311 .iter()
312 .map(|cell| cell.zero_gradients())
313 .collect()
314 }
315
316 pub fn forward_batch_sequences(
321 &mut self,
322 batch_sequences: &[Vec<Array2<f64>>],
323 ) -> Vec<Vec<(Array2<f64>, Array2<f64>)>> {
324 let padded = pad_batch(batch_sequences, self.input_size);
325 let (outputs, _) = self.forward_sequence_with_cache(&padded);
326 batch_sequences
327 .iter()
328 .enumerate()
329 .map(|(b, seq)| {
330 outputs
331 .iter()
332 .take(seq.len())
333 .map(|(h, c)| (column(h, b), column(c, b)))
334 .collect()
335 })
336 .collect()
337 }
338}
339
340pub fn pad_batch(batch_sequences: &[Vec<Array2<f64>>], features: usize) -> Vec<Array2<f64>> {
342 let max_len = batch_sequences.iter().map(Vec::len).max().unwrap_or(0);
343 (0..max_len)
344 .map(|t| {
345 let mut step = Array2::zeros((features, batch_sequences.len()));
346 for (b, seq) in batch_sequences.iter().enumerate() {
347 if let Some(x) = seq.get(t) {
348 step.column_mut(b).assign(&x.column(0));
349 }
350 }
351 step
352 })
353 .collect()
354}
355
356fn column(m: &Array2<f64>, b: usize) -> Array2<f64> {
357 m.column(b).to_owned().insert_axis(ndarray::Axis(1))
358}
359
360#[derive(Clone, Debug)]
362pub struct LayerDropoutConfig {
363 pub input_dropout: Option<(f64, bool)>, pub recurrent_dropout: Option<(f64, bool)>, pub output_dropout: Option<f64>, pub zoneout: Option<(f64, f64)>, }
368
369impl Default for LayerDropoutConfig {
370 fn default() -> Self {
371 Self::new()
372 }
373}
374
375impl LayerDropoutConfig {
376 pub fn new() -> Self {
377 LayerDropoutConfig {
378 input_dropout: None,
379 recurrent_dropout: None,
380 output_dropout: None,
381 zoneout: None,
382 }
383 }
384
385 pub fn with_input_dropout(mut self, rate: f64, variational: bool) -> Self {
386 self.input_dropout = Some((rate, variational));
387 self
388 }
389
390 pub fn with_recurrent_dropout(mut self, rate: f64, variational: bool) -> Self {
391 self.recurrent_dropout = Some((rate, variational));
392 self
393 }
394
395 pub fn with_output_dropout(mut self, rate: f64) -> Self {
396 self.output_dropout = Some(rate);
397 self
398 }
399
400 pub fn with_zoneout(mut self, cell_rate: f64, hidden_rate: f64) -> Self {
401 self.zoneout = Some((cell_rate, hidden_rate));
402 self
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409 use ndarray::arr2;
410
411 #[test]
412 fn test_lstm_network_forward() {
413 let input_size = 3;
414 let hidden_size = 2;
415 let num_layers = 2;
416 let mut network = LSTMNetwork::new(input_size, hidden_size, num_layers);
417
418 let input = arr2(&[[0.5], [0.1], [-0.3]]);
419 let state = network.zero_state(1);
420
421 let (hy, next) = network.forward(&input, &state);
422 let cy = next.c[num_layers - 1].clone();
423
424 assert_eq!(hy.shape(), &[hidden_size, 1]);
425 assert_eq!(cy.shape(), &[hidden_size, 1]);
426 }
427
428 #[test]
429 fn test_lstm_network_with_dropout() {
430 let input_size = 3;
431 let hidden_size = 2;
432 let num_layers = 2;
433 let mut network = LSTMNetwork::new(input_size, hidden_size, num_layers)
434 .with_input_dropout(0.2, true) .with_recurrent_dropout(0.3, true) .with_output_dropout(0.1)
437 .with_zoneout(0.1, 0.1);
438
439 let input = arr2(&[[0.5], [0.1], [-0.3]]);
440 let state = network.zero_state(1);
441
442 network.train();
444 let (hy_train, next) = network.forward(&input, &state);
445 let cy_train = next.c[num_layers - 1].clone();
446
447 network.eval();
449 let (hy_eval, next) = network.forward(&input, &state);
450 let cy_eval = next.c[num_layers - 1].clone();
451
452 assert_eq!(hy_train.shape(), &[hidden_size, 1]);
453 assert_eq!(cy_train.shape(), &[hidden_size, 1]);
454 assert_eq!(hy_eval.shape(), &[hidden_size, 1]);
455 assert_eq!(cy_eval.shape(), &[hidden_size, 1]);
456 }
457
458 #[test]
459 fn test_layer_specific_dropout() {
460 let input_size = 3;
461 let hidden_size = 2;
462 let num_layers = 2;
463
464 let layer_configs = vec![
465 LayerDropoutConfig::new()
466 .with_input_dropout(0.2, true)
467 .with_recurrent_dropout(0.3, true),
468 LayerDropoutConfig::new()
469 .with_output_dropout(0.1)
470 .with_zoneout(0.1, 0.1),
471 ];
472
473 let mut network =
474 LSTMNetwork::new(input_size, hidden_size, num_layers).with_layer_dropout(layer_configs);
475
476 let input = arr2(&[[0.5], [0.1], [-0.3]]);
477 let state = network.zero_state(1);
478
479 let (hy, next) = network.forward(&input, &state);
480 let cy = next.c[num_layers - 1].clone();
481
482 assert_eq!(hy.shape(), &[hidden_size, 1]);
483 assert_eq!(cy.shape(), &[hidden_size, 1]);
484 }
485}