use crate::activations::Activation;
use arrayfire::{constant, gt, matmul, mul, randu, sum, Array, Dim4, MatProp};
pub struct DenseLayer {
pub activation: Activation,
pub biases: Array<f32>,
pub weights: Array<f32>,
}
impl DenseLayer {
pub fn new(from: u64, size: u64, activation: Activation) -> DenseLayer {
if size == 0 {
panic!("All dense layer sizes must be >0.");
}
return DenseLayer {
activation,
biases: (randu::<f32>(Dim4::new(&[size, 1, 1, 1])) * 2f32) - 1f32,
weights: ((randu::<f32>(Dim4::new(&[size, from, 1, 1])) * 2f32) - 1f32)
/ (from as f32).sqrt(),
};
}
pub fn new_constant(from: u64, size: u64, activation: Activation, val: f32) -> DenseLayer {
if size == 0 {
panic!("All dense layer sizes must be >0.");
}
return DenseLayer {
activation,
biases: constant(val, Dim4::new(&[size, 1, 1, 1])),
weights: constant(val, Dim4::new(&[size, from, 1, 1])),
};
}
pub fn forepropagate(&self, a: &Array<f32>, ones: &Array<f32>) -> (Array<f32>, Array<f32>) {
let weighted_inputs: Array<f32> = matmul(&self.weights, &a, MatProp::NONE, MatProp::NONE);
let bias_matrix: Array<f32> = matmul(&self.biases, &ones, MatProp::NONE, MatProp::NONE);
let input = weighted_inputs + bias_matrix;
let activation = self.activation.run(&input);
return (activation, input);
}
pub fn backpropagate(
&mut self,
partial_error: &Array<f32>, z: &Array<f32>, a: &Array<f32>, learning_rate: f32,
l2: Option<f32>,
training_set_length: usize,
) -> Array<f32> {
let error = self.activation.derivative(z) * partial_error;
let bias_error = sum(&error, 1);
let weight_error = matmul(&error, a, MatProp::NONE, MatProp::TRANS);
let nxt_partial_error = matmul(&self.weights, &error, MatProp::TRANS, MatProp::NONE);
let batch_len = z.dims().get()[1] as f32;
if let Some(lambda) = l2 {
self.weights = ((1f32 - (learning_rate * lambda / training_set_length as f32))
* &self.weights)
- (learning_rate * weight_error / batch_len)
} else {
self.weights = &self.weights - (learning_rate * weight_error / batch_len);
}
self.biases = &self.biases - (learning_rate * bias_error / batch_len);
return nxt_partial_error;
}
}
pub struct DropoutLayer {
pub p: f32,
mask: Array<f32>,
}
impl DropoutLayer {
pub fn new(p: f32) -> DropoutLayer {
DropoutLayer {
p,
mask: Array::<f32>::new_empty(Dim4::new(&[1, 1, 1, 1])),
}
}
pub fn forepropagate(&mut self, z: &Array<f32>, ones: &Array<f32>) -> Array<f32> {
let z_dims = z.dims();
let z_dim_arr = z_dims.get();
let mask_dims = Dim4::new(&[z_dim_arr[0], 1, 1, 1]);
self.mask = matmul(
>(&randu::<f32>(mask_dims), &self.p, false).cast::<f32>(),
ones,
MatProp::NONE,
MatProp::NONE,
);
return mul(z, &self.mask, false);
}
pub fn backpropagate(&self, partial_error: &Array<f32>) -> Array<f32> {
return mul(partial_error, &self.mask, false);
}
}