commit
7759a6615d
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/Cargo.lock
|
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "neuramethyst"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ndarray = "^0.15"
|
||||||
|
rand = "^0.8"
|
@ -0,0 +1,45 @@
|
|||||||
|
pub trait Activation {
|
||||||
|
fn eval(&self, input: f64) -> f64;
|
||||||
|
|
||||||
|
fn eval_f32(&self, input: f32) -> f32 {
|
||||||
|
self.eval(input as f64) as f32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derivate(&self, at: f64) -> f64;
|
||||||
|
|
||||||
|
fn derivate_f32(&self, at: f32) -> f32 {
|
||||||
|
self.derivate(at as f64) as f32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct Relu;
|
||||||
|
impl Activation for Relu {
|
||||||
|
#[inline(always)]
|
||||||
|
fn eval(&self, input: f64) -> f64 {
|
||||||
|
input.max(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn eval_f32(&self, input: f32) -> f32 {
|
||||||
|
input.max(0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn derivate(&self, input: f64) -> f64 {
|
||||||
|
if input > 0.0 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn derivate_f32(&self, input: f32) -> f32 {
|
||||||
|
if input > 0.0 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
mod dense;
|
||||||
|
pub use dense::NeuraDenseLayer;
|
||||||
|
|
||||||
|
pub trait NeuraLayer {
|
||||||
|
type Input;
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn eval(&self, input: &Self::Input) -> Self::Output;
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
use super::NeuraLayer;
|
||||||
|
use crate::{activation::Activation, utils::multiply_matrix_vector};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
pub struct NeuraDenseLayer<Act: Activation, const INPUT_LEN: usize, const OUTPUT_LEN: usize> {
|
||||||
|
weights: [[f64; INPUT_LEN]; OUTPUT_LEN],
|
||||||
|
bias: [f64; OUTPUT_LEN],
|
||||||
|
activation: Act,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Act: Activation, const INPUT_LEN: usize, const OUTPUT_LEN: usize>
|
||||||
|
NeuraDenseLayer<Act, INPUT_LEN, OUTPUT_LEN>
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
weights: [[f64; INPUT_LEN]; OUTPUT_LEN],
|
||||||
|
bias: [f64; OUTPUT_LEN],
|
||||||
|
activation: Act,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
weights,
|
||||||
|
bias,
|
||||||
|
activation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_rng(rng: &mut impl Rng, activation: Act) -> Self {
|
||||||
|
let mut weights = [[0.0; INPUT_LEN]; OUTPUT_LEN];
|
||||||
|
|
||||||
|
let multiplier = std::f64::consts::SQRT_2 / (INPUT_LEN as f64).sqrt();
|
||||||
|
|
||||||
|
for i in 0..OUTPUT_LEN {
|
||||||
|
for j in 0..INPUT_LEN {
|
||||||
|
weights[i][j] = rng.gen::<f64>() * multiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
weights,
|
||||||
|
// Biases are zero-initialized, as this shouldn't cause any issues during training
|
||||||
|
bias: [0.0; OUTPUT_LEN],
|
||||||
|
activation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Act: Activation, const INPUT_LEN: usize, const OUTPUT_LEN: usize> NeuraLayer
|
||||||
|
for NeuraDenseLayer<Act, INPUT_LEN, OUTPUT_LEN>
|
||||||
|
{
|
||||||
|
type Input = [f64; INPUT_LEN];
|
||||||
|
|
||||||
|
type Output = [f64; OUTPUT_LEN];
|
||||||
|
|
||||||
|
fn eval(&self, input: &Self::Input) -> Self::Output {
|
||||||
|
let mut result = multiply_matrix_vector(&self.weights, input);
|
||||||
|
|
||||||
|
for i in 0..OUTPUT_LEN {
|
||||||
|
result[i] = self.activation.eval(result[i] + self.bias[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::activation::Relu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_from_rng() {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let layer: NeuraDenseLayer<_, 64, 32> = NeuraDenseLayer::from_rng(&mut rng, Relu);
|
||||||
|
let mut input = [0.0; 64];
|
||||||
|
for x in 0..64 {
|
||||||
|
input[x] = rng.gen();
|
||||||
|
}
|
||||||
|
assert!(layer.eval(&input).len() == 32);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
pub mod activation;
|
||||||
|
pub mod layer;
|
||||||
|
mod utils;
|
@ -0,0 +1,16 @@
|
|||||||
|
pub fn multiply_matrix_vector<const WIDTH: usize, const HEIGHT: usize>(
|
||||||
|
matrix: &[[f64; WIDTH]; HEIGHT],
|
||||||
|
vector: &[f64; WIDTH],
|
||||||
|
) -> [f64; HEIGHT] {
|
||||||
|
let mut result = [0.0; HEIGHT];
|
||||||
|
|
||||||
|
for i in 0..HEIGHT {
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for k in 0..WIDTH {
|
||||||
|
sum += matrix[i][k] * vector[k];
|
||||||
|
}
|
||||||
|
result[i] = sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
Loading…
Reference in new issue