jake kalstad 2 ani în urmă
comite
6865b038bc
7 a modificat fișierele cu 328 adăugiri și 0 ștergeri
  1. 1 0
      .gitignore
  2. 3 0
      .vscode/settings.json
  3. 167 0
      Cargo.lock
  4. 10 0
      Cargo.toml
  5. 1 0
      readme.md
  6. 75 0
      src/main.rs
  7. 71 0
      src/tensor.rs

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+/target

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+    "git-blame.gitWebUrl": ""
+}

+ 167 - 0
Cargo.lock

@@ -0,0 +1,167 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "getrandom"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.147"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
+
+[[package]]
+name = "libm"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
+
+[[package]]
+name = "matrixmultiply"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
+[[package]]
+name = "ndarray"
+version = "0.15.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32"
+dependencies = [
+ "matrixmultiply",
+ "num-complex",
+ "num-integer",
+ "num-traits",
+ "rawpointer",
+]
+
+[[package]]
+name = "ndarray-rand"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65608f937acc725f5b164dcf40f4f0bc5d67dc268ab8a649d3002606718c4588"
+dependencies = [
+ "ndarray",
+ "rand",
+ "rand_distr",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_distr"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31"
+dependencies = [
+ "num-traits",
+ "rand",
+]
+
+[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
+name = "rustygrad"
+version = "0.1.0"
+dependencies = [
+ "ndarray",
+ "ndarray-rand",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

+ 10 - 0
Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "rustygrad"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+ndarray = "*"
+ndarray-rand = "*"

+ 1 - 0
readme.md

@@ -0,0 +1 @@
+Creating a minimally viable neural network with rust, built to learn and experiment - only a fool would use this in production.

+ 75 - 0
src/main.rs

@@ -0,0 +1,75 @@
+mod tensor;
+
+use tensor::Tensor;
+use ndarray_rand::{rand, RandomExt};
+use ndarray_rand::rand_distr::Uniform;
+use std::f64::consts::E;
+
+fn sigmoid(x: f64) -> f64 {
+    1.0 / (1.0 + E.powf(-x))
+}
+
+#[derive(Debug)]
+struct NeuralNetwork {
+    input_size: usize,
+    hidden_size: usize,
+    output_size: usize,
+    weights_ih: Tensor,
+    weights_ho: Tensor,
+    bias_h: Tensor,
+    bias_o: Tensor,
+}
+
+impl NeuralNetwork {
+    fn new(input_size: usize, hidden_size: usize, output_size: usize) -> Self {
+        let weights_ih = Tensor::new(vec![input_size, hidden_size]);
+        let weights_ho = Tensor::new(vec![hidden_size, output_size]);
+        let bias_h = Tensor::new(vec![1, hidden_size]);
+        let bias_o = Tensor::new(vec![1, output_size]);
+
+        NeuralNetwork {
+            input_size,
+            hidden_size,
+            output_size,
+            weights_ih,
+            weights_ho,
+            bias_h,
+            bias_o,
+        }
+    }
+
+    fn forward(&self, input: &Tensor) -> Tensor {
+        let hidden = (input.matmul(&self.weights_ih)).add(&self.bias_h);
+        let hidden_activated = hidden.apply(sigmoid);
+        let output = hidden_activated.matmul(&self.weights_ho).add(&self.bias_o);
+        output
+    }
+}
+
+fn main() {
+    let tensor1 = crate::tensor::Tensor::new(vec![2, 3]);
+    let tensor2 = crate::tensor::Tensor::new(vec![2, 1]);
+    let result1 = tensor1.add(&tensor2);
+    tensor1.print();
+    tensor2.print();
+    result1.print();
+
+    let tensor1 = crate::tensor::Tensor::new(vec![2, 3]);
+    let tensor2 = crate::tensor::Tensor::new(vec![3, 2]);
+    let result2 = tensor1.matmul(&tensor2);
+
+    tensor1.print();
+    tensor2.print();
+    result2.print();
+
+    let input_size = 2;
+    let hidden_size = 3;
+    let output_size = 1;
+
+    let nn = NeuralNetwork::new(input_size, hidden_size, output_size);
+    let input = Tensor::new(vec![1, input_size]);
+    let output = nn.forward(&input);
+
+    println!("In: {:?}\n Out:  {:?}\n", input, output);
+    println!("NN: {:?}\n", nn);
+}

+ 71 - 0
src/tensor.rs

@@ -0,0 +1,71 @@
+use ndarray::{Array, ArrayD};
+use ndarray_rand::rand_distr::StandardNormal;
+use ndarray_rand::RandomExt;
+
+// Define a Tensor struct
+#[derive(Debug)]
+pub struct Tensor {
+    data: ArrayD<f64>,
+}
+
+impl Tensor {
+    // Create a new tensor with random values
+    pub fn new(shape: Vec<usize>) -> Self {
+        let data = ArrayD::random(shape, StandardNormal);
+        Tensor { data }
+    }
+
+    pub fn apply(&self, func: fn(f64) -> f64) -> Tensor {
+        let applied_data = self.data.mapv(func);
+        Tensor { data: applied_data }
+    }
+    
+    // Create a new tensor with zeros
+    pub fn zeros(shape: Vec<usize>) -> Self {
+        let data = Array::from_elem(shape, 0.0);
+        Tensor { data }
+    }
+
+    // Create a new tensor with ones
+    pub fn ones(shape: Vec<usize>) -> Self {
+        let data = Array::from_elem(shape, 1.0);
+        Tensor { data }
+    }
+
+    // Perform element-wise addition
+    pub fn add(&self, other: &Tensor) -> Self {
+        let result_data = &self.data + &other.data;
+        Tensor { data: result_data }
+    }
+
+    // Perform element-wise multiplication
+    pub fn multiply(&self, other: &Tensor) -> Self {
+        let result_data = &self.data * &other.data;
+        Tensor { data: result_data }
+    }
+
+    // Perform matrix multiplication
+    pub fn matmul(&self, other: &Tensor) -> Self {
+        let dim_lhs = self.data.shape().to_vec();
+        let dim_rhs = other.data.shape().to_vec();
+
+        let lhs = self.data
+            .to_owned()
+            .into_shape((dim_lhs[0], dim_lhs[1]))
+            .expect("Invalid dimensions");
+
+        let rhs = other.data
+            .to_owned()
+            .into_shape((dim_rhs[0], dim_rhs[1]))
+            .expect("Invalid dimensions");
+
+        let result_data = lhs.dot(&rhs);
+        Tensor { data: result_data.into_dimensionality().expect("Dimensions!") }
+    }
+
+    // Print the tensor's data
+    pub fn print(&self) {
+        println!("{:?}", self.data);
+    }
+}
+