{"id":17874460,"url":"https://github.com/fl03/concision","last_synced_at":"2026-01-16T20:20:26.200Z","repository":{"id":92251837,"uuid":"553079696","full_name":"FL03/concision","owner":"FL03","description":"Concision is a complete machine-learning toolkit written in pure Rust and optimized for WebAssembly (WASM) operations.","archived":false,"fork":false,"pushed_at":"2024-05-18T20:25:23.000Z","size":1143,"stargazers_count":0,"open_issues_count":15,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-05-19T14:52:33.234Z","etag":null,"topics":["ai","data-science","machine-learning","math","rust","scsys","toolkit","wasm"],"latest_commit_sha":null,"homepage":"https://fl03.github.io/concision/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/FL03.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["FL03"]}},"created_at":"2022-10-17T17:23:07.000Z","updated_at":"2024-05-20T16:10:19.925Z","dependencies_parsed_at":"2023-11-28T19:28:01.504Z","dependency_job_id":"45883434-6f3f-4bc5-9400-abb0a997caf6","html_url":"https://github.com/FL03/concision","commit_stats":{"total_commits":33,"total_committers":3,"mean_commits":11.0,"dds":0.06060606060606055,"last_synced_commit":"9917c2dc9b605788f72cd05dd68ff6073105bf4f"},"previous_names":["scattered-systems/concision"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FL03%2Fconcision","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FL03%2Fconcision/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FL03%2Fconcision/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/FL03%2Fconcision/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/FL03","download_url":"https://codeload.github.com/FL03/concision/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244880359,"owners_count":20525507,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","data-science","machine-learning","math","rust","scsys","toolkit","wasm"],"created_at":"2024-10-28T11:08:59.315Z","updated_at":"2026-01-16T20:20:26.180Z","avatar_url":"https://github.com/FL03.png","language":"Rust","funding_links":["https://github.com/sponsors/FL03"],"categories":[],"sub_categories":[],"readme":"# concision (cnc)\n\n[![crates.io](https://img.shields.io/crates/v/concision?style=for-the-badge\u0026logo=rust)](https://crates.io/crates/concision)\n[![docs.rs](https://img.shields.io/docsrs/concision?style=for-the-badge\u0026logo=docs.rs)](https://docs.rs/concision)\n[![GitHub License](https://img.shields.io/github/license/FL03/concision?style=for-the-badge\u0026logo=github)](https://github.com/FL03/concision/blob/main/LICENSE)\n\n***\n\n_**Warning: The library still in development and is not yet ready for production use.**_\n\n**Note:** It is important to note that a primary consideration of the `concision` framework is ensuring compatibility in two key areas:\n\n- `autodiff`: the upcoming feature enabling rust to natively support automatic differentiation.\n- [`ndarray`](https://docs.rs/ndarray): The crate is designed around the `ndarray` crate, which provides a powerful N-dimensional array type for Rust\n\n## Overview\n\n### Goals\n\n- Provide a flexible and extensible framework for building neural network models in Rust.\n- Support both shallow and deep neural networks with a focus on modularity and reusability.\n- Enable easy integration with other libraries and frameworks in the Rust ecosystem.\n\n### Roadmap\n\n- Implement additional optimization algorithms (e.g., Adam, RMSProp).\n- Add support for convolutional and recurrent neural networks.\n- Expand the set of built-in layers and activation functions.\n- Improve documentation and provide more examples and tutorials.\n- Implement support for automatic differentiation using the `autodiff` crate.\n\n## Getting Started\n\n### Adding to your project\n\nTo use `concision` in your project, run the following command:\n\n```bash\ncargo add concision --features full\n```\n\nor add the following to your `Cargo.toml`:\n\n```toml\n[dependencies.concision]\nfeatures = [\"full\"]\nversion = \"0.3.x\"\n```\n\n### Examples\n\n#### **Example (1):** Simple Model\n\n```rust\n  use crate::activate::{ReLUActivation, SigmoidActivation};\n  use crate::{\n      DeepModelParams, Error, Forward, Model, ModelFeatures, Norm, Params, StandardModelConfig, Train,\n  };\n  #[cfg(feature = \"rand\")]\n  use concision_init::{\n      InitTensor,\n      rand_distr::{Distribution, StandardNormal},\n  };\n\n  use ndarray::prelude::*;\n  use ndarray::{Data, ScalarOperand};\n  use num_traits::{Float, FromPrimitive, NumAssign, Zero};\n\n  #[cfg_attr(feature = \"serde\", derive(serde::Deserialize, serde::Serialize))]\n  pub struct TestModel\u003cT = f64\u003e {\n      pub config: StandardModelConfig\u003cT\u003e,\n      pub features: ModelFeatures,\n      pub params: DeepModelParams\u003cT\u003e,\n  }\n\n  impl\u003cT\u003e TestModel\u003cT\u003e {\n      pub fn new(config: StandardModelConfig\u003cT\u003e, features: ModelFeatures) -\u003e Self\n      where\n          T: Clone + Zero,\n      {\n          let params = DeepModelParams::zeros(features);\n          TestModel {\n              config,\n              features,\n              params,\n          }\n      }\n      /// returns an immutable reference to the model configuration\n      pub const fn config(\u0026self) -\u003e \u0026StandardModelConfig\u003cT\u003e {\n          \u0026self.config\n      }\n      /// returns a reference to the model layout\n      pub const fn features(\u0026self) -\u003e ModelFeatures {\n          self.features\n      }\n      /// returns a reference to the model params\n      pub const fn params(\u0026self) -\u003e \u0026DeepModelParams\u003cT\u003e {\n          \u0026self.params\n      }\n      /// returns a mutable reference to the model params\n      pub const fn params_mut(\u0026mut self) -\u003e \u0026mut DeepModelParams\u003cT\u003e {\n          \u0026mut self.params\n      }\n      #[cfg(feature = \"rand\")]\n      /// consumes the current instance to initalize another with random parameters\n      pub fn init(self) -\u003e Self\n      where\n          StandardNormal: Distribution\u003cT\u003e,\n          T: Float,\n      {\n          let TestModel {\n              mut params,\n              config,\n              features,\n          } = self;\n          params.set_input(Params::\u003cT\u003e::lecun_normal((\n              features.input(),\n              features.hidden(),\n          )));\n          for layer in params.hidden_mut() {\n              *layer = Params::\u003cT\u003e::lecun_normal((features.hidden(), features.hidden()));\n          }\n          params.set_output(Params::\u003cT\u003e::lecun_normal((\n              features.hidden(),\n              features.output(),\n          )));\n          TestModel {\n              config,\n              features,\n              params,\n          }\n      }\n  }\n\n  impl\u003cT\u003e Model\u003cT\u003e for TestModel\u003cT\u003e {\n      type Config = StandardModelConfig\u003cT\u003e;\n\n      type Layout = ModelFeatures;\n\n      fn config(\u0026self) -\u003e \u0026StandardModelConfig\u003cT\u003e {\n          \u0026self.config\n      }\n\n      fn config_mut(\u0026mut self) -\u003e \u0026mut StandardModelConfig\u003cT\u003e {\n          \u0026mut self.config\n      }\n\n      fn layout(\u0026self) -\u003e \u0026ModelFeatures {\n          \u0026self.features\n      }\n\n      fn params(\u0026self) -\u003e \u0026DeepModelParams\u003cT\u003e {\n          \u0026self.params\n      }\n\n      fn params_mut(\u0026mut self) -\u003e \u0026mut DeepModelParams\u003cT\u003e {\n          \u0026mut self.params\n      }\n  }\n\n  impl\u003cA, S, D\u003e Forward\u003cArrayBase\u003cS, D, A\u003e\u003e for TestModel\u003cA\u003e\n  where\n      A: Float + FromPrimitive + ScalarOperand,\n      D: Dimension,\n      S: Data\u003cElem = A\u003e,\n      Params\u003cA\u003e: Forward\u003cArrayBase\u003cS, D, A\u003e, Output = Array\u003cA, D\u003e\u003e\n          + Forward\u003cArray\u003cA, D\u003e, Output = Array\u003cA, D\u003e\u003e,\n  {\n      type Output = Array\u003cA, D\u003e;\n\n      fn forward(\u0026self, input: \u0026ArrayBase\u003cS, D\u003e) -\u003e Self::Output {\n          // complete the first forward pass using the input layer\n          let mut output = self.params().input().forward(input).relu();\n          // complete the forward pass for each hidden layer\n          for layer in self.params().hidden() {\n              output = layer.forward(\u0026output).relu();\n          }\n\n          self.params().output().forward(\u0026output).sigmoid()\n      }\n  }\n\n  impl\u003cA, S, T\u003e Train\u003cArrayBase\u003cS, Ix1\u003e, ArrayBase\u003cT, Ix1\u003e\u003e for TestModel\u003cA\u003e\n  where\n      A: Float + FromPrimitive + NumAssign + ScalarOperand + core::fmt::Debug,\n      S: Data\u003cElem = A\u003e,\n      T: Data\u003cElem = A\u003e,\n  {\n      type Error = Error;\n      type Output = A;\n\n      fn train(\n          \u0026mut self,\n          input: \u0026ArrayBase\u003cS, Ix1\u003e,\n          target: \u0026ArrayBase\u003cT, Ix1\u003e,\n      ) -\u003e Result\u003cSelf::Output, Error\u003e {\n          if input.len() != self.layout().input() {\n              return Err(Error::InvalidInputFeatures(\n                  input.len(),\n                  self.layout().input(),\n              ));\n          }\n          if target.len() != self.layout().output() {\n              return Err(Error::InvalidTargetFeatures(\n                  target.len(),\n                  self.layout().output(),\n              ));\n          }\n          // get the learning rate from the model's configuration\n          let lr = self\n              .config()\n              .learning_rate()\n              .copied()\n              .unwrap_or(A::from_f32(0.01).unwrap());\n          // Normalize the input and target\n          let input = input / input.l2_norm();\n          let target_norm = target.l2_norm();\n          let target = target / target_norm;\n          // self.prev_target_norm = Some(target_norm);\n          // Forward pass to collect activations\n          let mut activations = Vec::new();\n          activations.push(input.to_owned());\n\n          let mut output = self.params().input().forward_then(\u0026input, |y| y.relu());\n          activations.push(output.to_owned());\n          // collect the activations of the hidden\n          for layer in self.params().hidden() {\n              output = layer.forward(\u0026output).relu();\n              activations.push(output.to_owned());\n          }\n\n          output = self.params().output().forward(\u0026output).sigmoid();\n          activations.push(output.to_owned());\n\n          // Calculate output layer error\n          let error = \u0026target - \u0026output;\n          let loss = error.pow2().mean().unwrap_or(A::zero());\n          #[cfg(feature = \"tracing\")]\n          tracing::trace!(\"Training loss: {loss:?}\");\n          let mut delta = error * output.sigmoid_derivative();\n          delta /= delta.l2_norm(); // Normalize the delta to prevent exploding gradients\n\n          // Update output weights\n          self.params_mut()\n              .output_mut()\n              .backward(activations.last().unwrap(), \u0026delta, lr);\n\n          let num_hidden = self.layout().layers();\n          // Iterate through hidden layers in reverse order\n          for i in (0..num_hidden).rev() {\n              // Calculate error for this layer\n              delta = if i == num_hidden - 1 {\n                  // use the output activations for the final hidden layer\n                  self.params().output().weights().dot(\u0026delta) * activations[i + 1].relu_derivative()\n              } else {\n                  // else; backpropagate using the previous hidden layer\n                  self.params().hidden()[i + 1].weights().t().dot(\u0026delta)\n                      * activations[i + 1].relu_derivative()\n              };\n              // Normalize delta to prevent exploding gradients\n              delta /= delta.l2_norm();\n              self.params_mut().hidden_mut()[i].backward(\u0026activations[i + 1], \u0026delta, lr);\n          }\n          /*\n              The delta for the input layer is computed using the weights of the first hidden layer\n              and the derivative of the activation function of the first hidden layer.\n          */\n          delta = self.params().hidden()[0].weights().dot(\u0026delta) * activations[1].relu_derivative();\n          delta /= delta.l2_norm(); // Normalize the delta to prevent exploding gradients\n          self.params_mut()\n              .input_mut()\n              .backward(\u0026activations[1], \u0026delta, lr);\n\n          Ok(loss)\n      }\n  }\n\n  impl\u003cA, S, T\u003e Train\u003cArrayBase\u003cS, Ix2\u003e, ArrayBase\u003cT, Ix2\u003e\u003e for TestModel\u003cA\u003e\n  where\n      A: Float + FromPrimitive + NumAssign + ScalarOperand + core::fmt::Debug,\n      S: Data\u003cElem = A\u003e,\n      T: Data\u003cElem = A\u003e,\n  {\n      type Error = Error;\n      type Output = A;\n\n      fn train(\n          \u0026mut self,\n          input: \u0026ArrayBase\u003cS, Ix2\u003e,\n          target: \u0026ArrayBase\u003cT, Ix2\u003e,\n      ) -\u003e Result\u003cSelf::Output, Self::Error\u003e {\n          if input.nrows() == 0 || target.nrows() == 0 {\n              return Err(anyhow::anyhow!(\"Input and target batches must be non-empty\").into());\n          }\n          if input.ncols() != self.layout().input() {\n              return Err(Error::InvalidInputFeatures(\n                  input.ncols(),\n                  self.layout().input(),\n              ));\n          }\n          if target.ncols() != self.layout().output() || target.nrows() != input.nrows() {\n              return Err(Error::InvalidTargetFeatures(\n                  target.ncols(),\n                  self.layout().output(),\n              ));\n          }\n          let batch_size = input.nrows();\n          let mut loss = A::zero();\n\n          for (i, (x, e)) in input.rows().into_iter().zip(target.rows()).enumerate() {\n              loss += match Train::\u003cArrayView1\u003cA\u003e, ArrayView1\u003cA\u003e\u003e::train(self, \u0026x, \u0026e) {\n                  Ok(l) =\u003e l,\n                  Err(err) =\u003e {\n                      #[cfg(not(feature = \"tracing\"))]\n                      eprintln!(\n                          \"Training failed for batch {}/{}: {:?}\",\n                          i + 1,\n                          batch_size,\n                          err\n                      );\n                      #[cfg(feature = \"tracing\")]\n                      tracing::error!(\n                          \"Training failed for batch {}/{}: {:?}\",\n                          i + 1,\n                          batch_size,\n                          err\n                      );\n                      return Err(err);\n                  }\n              };\n          }\n\n          Ok(loss)\n      }\n  }\n\n```\n\n## Contributing\n\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. View the [quickstart guide](QUICKSTART.md) for more information on setting up your environment to develop the `concision` framework.\n\nPlease make sure to update tests as appropriate.\n\n## License\n\n- [Apache-2.0](https://choosealicense.com/licenses/apache-2.0/)\n- [MIT](https://choosealicense.com/licenses/mit/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffl03%2Fconcision","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffl03%2Fconcision","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffl03%2Fconcision/lists"}