use crate::error::Error;
use crate::neural_network::layers::activation::{Activation, ActivationInput};
use crate::neural_network::layers::recurrent::cell::{
RecurrentGroup, RnnCell, activate, activation_backward, record_cache,
};
use crate::neural_network::layers::recurrent::gate::FusedGates;
use crate::neural_network::layers::recurrent::rnn::{Rnn, recurrent_layer_traits};
use crate::neural_network::traits::{LayerBase, UnaryLayer};
use gemmkit_ndarray::dot;
use gemmkit_ndarray::{Activation as FusedActivation, Bias, Parallelism};
use ndarray::{Array2, ArrayView2};
#[derive(Debug)]
pub(crate) struct SimpleRnnCell {
activation: Activation,
}
impl RnnCell for SimpleRnnCell {
const CELL_TYPE: &'static str = "SimpleRNN";
const GATE_BIASES: &'static [f32] = &[0.0];
const STATE_COUNT: usize = 1;
const RECURRENT_GROUPS: &'static [RecurrentGroup] = &[RecurrentGroup {
first: 0,
count: 1,
operand: None,
}];
fn new(activation: Activation) -> Self {
Self { activation }
}
fn record_slots(&self) -> usize {
match self.activation.saves() {
ActivationInput::Output => 0,
ActivationInput::PreActivation => 1,
}
}
fn step(
&self,
gates: &FusedGates,
xw_t: ArrayView2<'_, f32>,
state: &mut [Array2<f32>],
record: Option<&mut Vec<Array2<f32>>>,
) -> Result<(), Error> {
let bias = gates.bias.as_slice().expect("bias must be contiguous");
let mut z = xw_t.to_owned();
let fused_act = match self.activation {
Activation::ReLU => Some(FusedActivation::Relu),
_ => None,
};
gemmkit_ndarray::gemm_fused(
1.0,
&state[0],
&gates.recurrent_kernel,
1.0,
&mut z,
Some(Bias::PerCol(bias)),
fused_act,
Parallelism::Rayon(0),
);
state[0] = match self.activation {
Activation::Linear | Activation::ReLU => z,
_ => {
let activated = activate(&self.activation, z, record.is_some())?;
if let (Some(record), Some(z)) = (record, activated.pre_activation) {
record.push(z);
}
activated.output
}
};
Ok(())
}
fn step_backward(
&self,
gates: &FusedGates,
_state_prev: &[Array2<f32>],
state_next: &[Array2<f32>],
record: &[Array2<f32>],
grad_state: &mut [Array2<f32>],
) -> Result<Array2<f32>, Error> {
let saved = match self.activation.saves() {
ActivationInput::Output => &state_next[0],
ActivationInput::PreActivation => &record[0],
};
let cache = record_cache(&self.activation, saved);
let d_z = activation_backward(&self.activation, &cache, grad_state[0].clone())?;
grad_state[0] = dot(&d_z, &gates.recurrent_kernel.t());
Ok(d_z)
}
}
pub struct SimpleRNN(
Rnn<SimpleRnnCell>,
);
impl SimpleRNN {
pub fn new(units: usize, activation: impl Into<Activation>) -> Result<Self, Error> {
Ok(Self(Rnn::new(units, activation)?))
}
pub fn with_random_state(mut self, random_state: u64) -> Self {
self.0 = self.0.with_random_state(random_state);
self
}
pub fn with_return_sequences(mut self, return_sequences: bool) -> Self {
self.0.return_sequences = return_sequences;
self
}
pub fn with_go_backwards(mut self, go_backwards: bool) -> Self {
self.0.go_backwards = go_backwards;
self
}
pub fn set_weights(
&mut self,
kernel: Array2<f32>,
recurrent_kernel: Array2<f32>,
bias: Array2<f32>,
) -> Result<(), Error> {
self.0.set_weights(kernel, recurrent_kernel, bias)
}
}
recurrent_layer_traits!(SimpleRNN);