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_cell_update_dropout(mut self, dropout_rate: f64, variational: bool) -> Self {
92 for cell in &mut self.cells {
93 *cell = cell
94 .clone()
95 .with_cell_update_dropout(dropout_rate, variational);
96 }
97 self
98 }
99
100 pub fn with_layer_dropout(mut self, layer_configs: Vec<LayerDropoutConfig>) -> Self {
101 for (i, config) in layer_configs.into_iter().enumerate() {
102 if i < self.cells.len() {
103 let mut cell = self.cells[i].clone();
104
105 if let Some((rate, variational)) = config.input_dropout {
106 cell = cell.with_input_dropout(rate, variational);
107 }
108 if let Some((rate, variational)) = config.recurrent_dropout {
109 cell = cell.with_recurrent_dropout(rate, variational);
110 }
111 if let Some(rate) = config.output_dropout {
112 cell = cell.with_output_dropout(rate);
113 }
114 if let Some((cell_rate, hidden_rate)) = config.zoneout {
115 cell = cell.with_zoneout(cell_rate, hidden_rate);
116 }
117 if let Some((rate, variational)) = config.cell_update_dropout {
118 cell = cell.with_cell_update_dropout(rate, variational);
119 }
120
121 self.cells[i] = cell;
122 }
123 }
124 self
125 }
126
127 pub fn train(&mut self) {
128 self.is_training = true;
129 for cell in &mut self.cells {
130 cell.train();
131 }
132 }
133
134 pub fn eval(&mut self) {
135 self.is_training = false;
136 for cell in &mut self.cells {
137 cell.eval();
138 }
139 }
140
141 pub fn from_cells(
143 cells: Vec<LSTMCell>,
144 input_size: usize,
145 hidden_size: usize,
146 num_layers: usize,
147 ) -> Self {
148 LSTMNetwork {
149 cells,
150 input_size,
151 hidden_size,
152 num_layers,
153 is_training: true,
154 }
155 }
156
157 pub fn get_cells(&self) -> &[LSTMCell] {
159 &self.cells
160 }
161
162 pub fn get_cells_mut(&mut self) -> &mut [LSTMCell] {
164 &mut self.cells
165 }
166
167 pub fn zero_state(&self, batch_size: usize) -> LSTMState {
169 let zeros = || {
170 (0..self.num_layers)
171 .map(|_| Array2::zeros((self.hidden_size, batch_size)))
172 .collect()
173 };
174 LSTMState {
175 h: zeros(),
176 c: zeros(),
177 }
178 }
179
180 pub fn reset_dropout_masks(&mut self) {
182 for cell in &mut self.cells {
183 cell.reset_dropout_masks();
184 }
185 }
186
187 pub fn forward(&mut self, input: &Array2<f64>, state: &LSTMState) -> (Array2<f64>, LSTMState) {
190 let (output, state, _) = self.forward_with_cache(input, state);
191 (output, state)
192 }
193
194 pub fn forward_with_cache(
196 &mut self,
197 input: &Array2<f64>,
198 state: &LSTMState,
199 ) -> (Array2<f64>, LSTMState, LSTMNetworkCache) {
200 assert_eq!(
201 state.h.len(),
202 self.num_layers,
203 "state must hold one entry per layer"
204 );
205 let mut layer_input = input.clone();
206 let mut next = LSTMState {
207 h: Vec::with_capacity(self.num_layers),
208 c: Vec::with_capacity(self.num_layers),
209 };
210 let mut cell_caches = Vec::with_capacity(self.num_layers);
211 let mut output_dropout_masks = Vec::with_capacity(self.num_layers);
212
213 let last = self.num_layers - 1;
214 for (l, cell) in self.cells.iter_mut().enumerate() {
215 let (hy, cy, cache) = cell.forward_with_cache(&layer_input, &state.h[l], &state.c[l]);
216 let mask = if l < last {
217 cell.output_dropout_mask(hy.raw_dim())
218 } else {
219 None
220 };
221 layer_input = masked(&hy, &mask);
222 next.h.push(hy);
223 next.c.push(cy);
224 cell_caches.push(cache);
225 output_dropout_masks.push(mask);
226 }
227
228 let cache = LSTMNetworkCache {
229 cell_caches,
230 output_dropout_masks,
231 };
232 (layer_input, next, cache)
233 }
234
235 pub fn forward_sequence(&mut self, sequence: &[Array2<f64>]) -> Vec<Array2<f64>> {
238 self.forward_sequence_with_cache(sequence)
239 .0
240 .into_iter()
241 .map(|(h, _)| h)
242 .collect()
243 }
244
245 pub fn forward_sequence_with_cache(
248 &mut self,
249 sequence: &[Array2<f64>],
250 ) -> (Vec<(Array2<f64>, Array2<f64>)>, Vec<LSTMNetworkCache>) {
251 let batch_size = sequence.first().map_or(1, |x| x.ncols());
252 let mut state = self.zero_state(batch_size);
253 self.reset_dropout_masks();
254
255 let mut outputs = Vec::with_capacity(sequence.len());
256 let mut caches = Vec::with_capacity(sequence.len());
257 for input in sequence {
258 let (output, next, cache) = self.forward_with_cache(input, &state);
259 outputs.push((output, next.c[self.num_layers - 1].clone()));
260 caches.push(cache);
261 state = next;
262 }
263 (outputs, caches)
264 }
265
266 pub fn backward_sequence(
273 &self,
274 d_outputs: &[Array2<f64>],
275 caches: &[LSTMNetworkCache],
276 ) -> (Vec<LSTMCellGradients>, Vec<Array2<f64>>) {
277 assert_eq!(
278 d_outputs.len(),
279 caches.len(),
280 "one output gradient per time step"
281 );
282 let mut gradients = self.zero_gradients();
283 let mut d_inputs = vec![Array2::zeros((0, 0)); caches.len()];
284 let batch_size = d_outputs.first().map_or(1, |d| d.ncols());
285 let mut dh_next = self.zero_state(batch_size);
286
287 let last = self.num_layers - 1;
288 for t in (0..caches.len()).rev() {
289 let mut d_above = d_outputs[t].clone();
290 for l in (0..self.num_layers).rev() {
291 let d_out = if l < last {
292 masked(&d_above, &caches[t].output_dropout_masks[l])
293 } else {
294 d_above
295 };
296 let dh = &d_out + &dh_next.h[l];
297 let (g, dx, dhx, dcx) =
298 self.cells[l].backward(&dh, &dh_next.c[l], &caches[t].cell_caches[l]);
299 gradients[l].accumulate(&g);
300 dh_next.h[l] = dhx;
301 dh_next.c[l] = dcx;
302 d_above = dx;
303 }
304 d_inputs[t] = d_above;
305 }
306 (gradients, d_inputs)
307 }
308
309 pub fn update_parameters<O: Optimizer>(
311 &mut self,
312 gradients: &[LSTMCellGradients],
313 optimizer: &mut O,
314 ) {
315 for (i, (cell, cell_gradients)) in self.cells.iter_mut().zip(gradients.iter()).enumerate() {
316 let prefix = format!("layer_{}", i);
317 cell.update_parameters(cell_gradients, optimizer, &prefix);
318 }
319 }
320
321 pub fn zero_gradients(&self) -> Vec<LSTMCellGradients> {
323 self.cells
324 .iter()
325 .map(|cell| cell.zero_gradients())
326 .collect()
327 }
328
329 pub fn forward_batch_sequences(
334 &mut self,
335 batch_sequences: &[Vec<Array2<f64>>],
336 ) -> Vec<Vec<(Array2<f64>, Array2<f64>)>> {
337 let padded = pad_batch(batch_sequences, self.input_size);
338 let (outputs, _) = self.forward_sequence_with_cache(&padded);
339 batch_sequences
340 .iter()
341 .enumerate()
342 .map(|(b, seq)| {
343 outputs
344 .iter()
345 .take(seq.len())
346 .map(|(h, c)| (column(h, b), column(c, b)))
347 .collect()
348 })
349 .collect()
350 }
351}
352
353pub fn pad_batch(batch_sequences: &[Vec<Array2<f64>>], features: usize) -> Vec<Array2<f64>> {
355 let max_len = batch_sequences.iter().map(Vec::len).max().unwrap_or(0);
356 (0..max_len)
357 .map(|t| {
358 let mut step = Array2::zeros((features, batch_sequences.len()));
359 for (b, seq) in batch_sequences.iter().enumerate() {
360 if let Some(x) = seq.get(t) {
361 step.column_mut(b).assign(&x.column(0));
362 }
363 }
364 step
365 })
366 .collect()
367}
368
369fn column(m: &Array2<f64>, b: usize) -> Array2<f64> {
370 m.column(b).to_owned().insert_axis(ndarray::Axis(1))
371}
372
373#[derive(Clone, Debug)]
375pub struct LayerDropoutConfig {
376 pub input_dropout: Option<(f64, bool)>, pub recurrent_dropout: Option<(f64, bool)>, pub output_dropout: Option<f64>, pub zoneout: Option<(f64, f64)>, pub cell_update_dropout: Option<(f64, bool)>, }
382
383impl Default for LayerDropoutConfig {
384 fn default() -> Self {
385 Self::new()
386 }
387}
388
389impl LayerDropoutConfig {
390 pub fn new() -> Self {
391 LayerDropoutConfig {
392 input_dropout: None,
393 recurrent_dropout: None,
394 output_dropout: None,
395 zoneout: None,
396 cell_update_dropout: None,
397 }
398 }
399
400 pub fn with_input_dropout(mut self, rate: f64, variational: bool) -> Self {
401 self.input_dropout = Some((rate, variational));
402 self
403 }
404
405 pub fn with_recurrent_dropout(mut self, rate: f64, variational: bool) -> Self {
406 self.recurrent_dropout = Some((rate, variational));
407 self
408 }
409
410 pub fn with_output_dropout(mut self, rate: f64) -> Self {
411 self.output_dropout = Some(rate);
412 self
413 }
414
415 pub fn with_zoneout(mut self, cell_rate: f64, hidden_rate: f64) -> Self {
416 self.zoneout = Some((cell_rate, hidden_rate));
417 self
418 }
419
420 pub fn with_cell_update_dropout(mut self, rate: f64, variational: bool) -> Self {
421 self.cell_update_dropout = Some((rate, variational));
422 self
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use super::*;
429 use ndarray::arr2;
430
431 #[test]
432 fn test_lstm_network_forward() {
433 let input_size = 3;
434 let hidden_size = 2;
435 let num_layers = 2;
436 let mut network = LSTMNetwork::new(input_size, hidden_size, num_layers);
437
438 let input = arr2(&[[0.5], [0.1], [-0.3]]);
439 let state = network.zero_state(1);
440
441 let (hy, next) = network.forward(&input, &state);
442 let cy = next.c[num_layers - 1].clone();
443
444 assert_eq!(hy.shape(), &[hidden_size, 1]);
445 assert_eq!(cy.shape(), &[hidden_size, 1]);
446 }
447
448 #[test]
449 fn test_lstm_network_with_dropout() {
450 let input_size = 3;
451 let hidden_size = 2;
452 let num_layers = 2;
453 let mut network = LSTMNetwork::new(input_size, hidden_size, num_layers)
454 .with_input_dropout(0.2, true) .with_recurrent_dropout(0.3, true) .with_output_dropout(0.1)
457 .with_zoneout(0.1, 0.1);
458
459 let input = arr2(&[[0.5], [0.1], [-0.3]]);
460 let state = network.zero_state(1);
461
462 network.train();
464 let (hy_train, next) = network.forward(&input, &state);
465 let cy_train = next.c[num_layers - 1].clone();
466
467 network.eval();
469 let (hy_eval, next) = network.forward(&input, &state);
470 let cy_eval = next.c[num_layers - 1].clone();
471
472 assert_eq!(hy_train.shape(), &[hidden_size, 1]);
473 assert_eq!(cy_train.shape(), &[hidden_size, 1]);
474 assert_eq!(hy_eval.shape(), &[hidden_size, 1]);
475 assert_eq!(cy_eval.shape(), &[hidden_size, 1]);
476 }
477
478 #[test]
479 fn test_layer_specific_dropout() {
480 let input_size = 3;
481 let hidden_size = 2;
482 let num_layers = 2;
483
484 let layer_configs = vec![
485 LayerDropoutConfig::new()
486 .with_input_dropout(0.2, true)
487 .with_recurrent_dropout(0.3, true),
488 LayerDropoutConfig::new()
489 .with_output_dropout(0.1)
490 .with_zoneout(0.1, 0.1),
491 ];
492
493 let mut network =
494 LSTMNetwork::new(input_size, hidden_size, num_layers).with_layer_dropout(layer_configs);
495
496 let input = arr2(&[[0.5], [0.1], [-0.3]]);
497 let state = network.zero_state(1);
498
499 let (hy, next) = network.forward(&input, &state);
500 let cy = next.c[num_layers - 1].clone();
501
502 assert_eq!(hy.shape(), &[hidden_size, 1]);
503 assert_eq!(cy.shape(), &[hidden_size, 1]);
504 }
505}