use crate::error::Error;
use crate::neural_network::layers::activation::Activation;
use crate::neural_network::layers::recurrent::cell::{RecurrentGroup, RnnCell};
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, Ix2};
#[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 RECORD_SLOTS: usize = 0;
const RECURRENT_GROUPS: &'static [RecurrentGroup] = &[RecurrentGroup {
first: 0,
count: 1,
operand: None,
}];
fn new(activation: Activation) -> Self {
Self { activation }
}
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,
_ => self
.activation
.forward(&z.into_dyn())?
.into_dimensionality::<Ix2>()
.unwrap(),
};
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 d_z = {
let h_t = state_next[0].clone().into_dyn();
let grad_h = grad_state[0].clone().into_dyn();
self.activation
.backward(&h_t, &grad_h)?
.into_dimensionality::<Ix2>()
.unwrap()
};
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);