From f2a6ba3374e0031d9912b5d9164e41f463dc36e0 Mon Sep 17 00:00:00 2001 From: maxwellflitton Date: Thu, 25 Apr 2024 12:17:06 +0100 Subject: [PATCH 1/4] adding V2 but breaking due to dimensions not being passed through --- modules/core/Cargo.toml | 1 + modules/core/src/execution/compute.rs | 79 +++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/modules/core/Cargo.toml b/modules/core/Cargo.toml index 83cd289..dd52e8e 100644 --- a/modules/core/Cargo.toml +++ b/modules/core/Cargo.toml @@ -21,6 +21,7 @@ tensorflow-tests = [] [dependencies] regex = "1.9.3" ort = { version = "1.16.2", features = ["load-dynamic"], default-features = false } +ort-v2 = { version = "2.0.0-rc.1", package = "ort" } ndarray = "0.15.6" once_cell = "1.18.0" bytes = "1.5.0" diff --git a/modules/core/src/execution/compute.rs b/modules/core/src/execution/compute.rs index 6c796a3..1971fab 100644 --- a/modules/core/src/execution/compute.rs +++ b/modules/core/src/execution/compute.rs @@ -1,8 +1,13 @@ //! Defines the operations around performing computations on a loaded model. use crate::storage::surml_file::SurMlFile; use std::collections::HashMap; +use std::os::unix::process; use ndarray::{ArrayD, CowArray}; +use ort::tensor; use ort::{SessionBuilder, Value, session::Input}; +use ort_v2::Session as SessionV2; +use ort_v2::{Value as ValueV2, Input as InputV2}; +use ort_v2::Tensor; use super::onnx_environment::ENVIRONMENT; use crate::safe_eject; @@ -50,6 +55,28 @@ impl <'a>ModelComputation<'a> { buffer } + /// Creates a vector of dimensions for the input tensor from the loaded model. + /// + /// # Arguments + /// * `input_dims` - The input dimensions from the loaded model. + /// + /// # Returns + /// A vector of dimensions for the input tensor to be reshaped into from the loaded model. + fn process_input_dims_v2(input_dims: &InputV2) -> Vec { + let mut buffer = Vec::new(); + match input_dims.input_type.tensor_dimensions() { + Some(dims) => { + for dim in dims { + buffer.push(*dim as usize); + } + }, + None => { + buffer.push(1) + } + } + buffer + } + /// Creates a Vector that can be used manipulated with other operations such as normalisation from a hashmap of keys and values. /// /// # Arguments @@ -106,6 +133,55 @@ impl <'a>ModelComputation<'a> { return Ok(buffer) } + pub fn raw_compute_v2(&self, tensor: ArrayD, _dims: Option<(i32, i32)>) -> Result, SurrealError> { + let session = match SessionV2::builder() { + Ok(builder) => builder, + Err(_) => return Err(SurrealError::new( + String::from("Failed to create a session builder for the model (V2)").to_string(), + SurrealErrorStatus::Unknown + )) + }.commit_from_memory(&self.surml_file.model).map_err(|e| { + SurrealError::new( + format!("Failed to commit the model to the session (V2): {}", e).to_string(), + SurrealErrorStatus::Unknown + ) + })?; + let unwrapped_dims = ModelComputation::process_input_dims_v2(&session.inputs[0]); + let tensor = safe_eject!(tensor.into_shape(unwrapped_dims), SurrealErrorStatus::Unknown); + let input_values = match ort_v2::inputs![tensor] { + Ok(inputs) => inputs, + Err(e) => return Err(SurrealError::new( + format!("Failed to create input values for the model (V2): {}", e).to_string(), + SurrealErrorStatus::Unknown + )) + }; + // input_values. + + // let x = CowArray::from(tensor).into_dyn(); + // let input_values = safe_eject!(ValueV2::from_array(tensor), SurrealErrorStatus::Unknown); + let outputs = safe_eject!( + session.run(input_values), + SurrealErrorStatus::Unknown + ); + + let mut buffer: Vec = Vec::new(); + + match outputs[0].try_extract_tensor::() { + Ok(y) => { + for i in y.view().clone().into_iter() { + buffer.push(*i); + } + }, + Err(_) => { + for i in safe_eject!(outputs[0].try_extract_tensor::(), SurrealErrorStatus::Unknown).view().clone().into_iter() { + buffer.push(*i as f32); + } + } + }; + return Ok(buffer) + + } + /// Checks the header applying normalisers if present and then performs a raw computation on the loaded model. Will /// also apply inverse normalisers if present on the outputs. /// @@ -173,10 +249,13 @@ mod tests { input_values.insert(String::from("num_floors"), 2.0); let raw_input = model_computation.input_tensor_from_key_bindings(input_values).unwrap(); + let raw_input_two = raw_input.clone(); let output = model_computation.raw_compute(raw_input, Some((1, 2))).unwrap(); assert_eq!(output.len(), 1); assert_eq!(output[0], 985.57745); + let output = model_computation.raw_compute_v2(raw_input_two, Some((1, 2))).unwrap(); + println!("{:?}", output); } #[cfg(feature = "sklearn-tests")] From a1a664d16f5ce07736cb4658d196c4f62d32d5d0 Mon Sep 17 00:00:00 2001 From: maxwellflitton Date: Thu, 25 Apr 2024 14:40:42 +0100 Subject: [PATCH 2/4] adding support for v2 testing --- modules/core/README.md | 11 +++ modules/core/src/execution/compute.rs | 133 ++++++++++++++++++++++---- modules/core/src/lib.rs | 10 ++ 3 files changed, 133 insertions(+), 21 deletions(-) diff --git a/modules/core/README.md b/modules/core/README.md index 632458d..5788695 100644 --- a/modules/core/README.md +++ b/modules/core/README.md @@ -96,6 +96,17 @@ let data: ArrayD = ndarray::arr1(&x).into_dyn(); let output = compute_unit.raw_compute(data, None).unwrap(); ``` +### V2 Ort +Version 2 of Ort is now supported as well but this is in beta as V2 of Ort in general is also +in beta. Everything is the same apart from you adding `v2` to the end of your execution calls +like so: + +```rust +let output = compute_unit.buffered_compute_v2(&mut input_values).unwrap(); + +let output = compute_unit.raw_compute_v2(data, None).unwrap(); +``` + ## ONNX runtime assets We can find the ONNX assets with the following link: diff --git a/modules/core/src/execution/compute.rs b/modules/core/src/execution/compute.rs index 1971fab..d7ffc20 100644 --- a/modules/core/src/execution/compute.rs +++ b/modules/core/src/execution/compute.rs @@ -1,13 +1,9 @@ //! Defines the operations around performing computations on a loaded model. use crate::storage::surml_file::SurMlFile; use std::collections::HashMap; -use std::os::unix::process; use ndarray::{ArrayD, CowArray}; -use ort::tensor; use ort::{SessionBuilder, Value, session::Input}; use ort_v2::Session as SessionV2; -use ort_v2::{Value as ValueV2, Input as InputV2}; -use ort_v2::Tensor; use super::onnx_environment::ENVIRONMENT; use crate::safe_eject; @@ -62,19 +58,24 @@ impl <'a>ModelComputation<'a> { /// /// # Returns /// A vector of dimensions for the input tensor to be reshaped into from the loaded model. - fn process_input_dims_v2(input_dims: &InputV2) -> Vec { + fn process_input_dims_v2(input_dims: Option<&Vec>) -> Result, SurrealError> { + let input_dims = match input_dims { + Some(dims) => dims, + None => return Err(SurrealError::new( + String::from("src/execution/compute.rs 66: No input dimensions found for the model (V2)").to_string(), + SurrealErrorStatus::Unknown + )) + }; let mut buffer = Vec::new(); - match input_dims.input_type.tensor_dimensions() { - Some(dims) => { - for dim in dims { - buffer.push(*dim as usize); - } - }, - None => { - buffer.push(1) + for dim in input_dims { + if dim == &-1 { + buffer.push(1); + } + else { + buffer.push(*dim as usize); } } - buffer + Ok(buffer) } /// Creates a Vector that can be used manipulated with other operations such as normalisation from a hashmap of keys and values. @@ -146,8 +147,17 @@ impl <'a>ModelComputation<'a> { SurrealErrorStatus::Unknown ) })?; - let unwrapped_dims = ModelComputation::process_input_dims_v2(&session.inputs[0]); - let tensor = safe_eject!(tensor.into_shape(unwrapped_dims), SurrealErrorStatus::Unknown); + let unwrapped_dims = ModelComputation::process_input_dims_v2( + session.inputs[0].input_type.tensor_dimensions() + )?; + + let tensor = match tensor.into_shape(unwrapped_dims) { + Ok(tensor) => tensor, + Err(e) => return Err(SurrealError::new( + format!("Failed to reshape the input tensor for the model (V2): {}", e).to_string(), + SurrealErrorStatus::Unknown + )) + }; let input_values = match ort_v2::inputs![tensor] { Ok(inputs) => inputs, Err(e) => return Err(SurrealError::new( @@ -155,10 +165,6 @@ impl <'a>ModelComputation<'a> { SurrealErrorStatus::Unknown )) }; - // input_values. - - // let x = CowArray::from(tensor).into_dyn(); - // let input_values = safe_eject!(ValueV2::from_array(tensor), SurrealErrorStatus::Unknown); let outputs = safe_eject!( session.run(input_values), SurrealErrorStatus::Unknown @@ -205,6 +211,8 @@ impl <'a>ModelComputation<'a> { } } let tensor = self.input_tensor_from_key_bindings(input_values.clone())?; + // dims are None because dims is depcrecated as we are now getting the dims from the ONNX file but + // we will keep it here for now to keep the function signature the same and to see if we need it later let output = self.raw_compute(tensor, None)?; // if no normaliser is present, return the output @@ -228,6 +236,54 @@ impl <'a>ModelComputation<'a> { return Ok(buffer) } + /// Checks the header applying normalisers if present and then performs a raw computation on the loaded model. Will + /// also apply inverse normalisers if present on the outputs. + /// + /// # Notes + /// This function is fairly coupled and will consider breaking out the functions later on if needed. + /// + /// # Arguments + /// * `input_values` - A hashmap of keys and values that will be used to create the input tensor. + /// + /// # Returns + /// The computed output tensor from the loaded model. + pub fn buffered_compute_v2(&self, input_values: &mut HashMap) -> Result, SurrealError> { + // applying normalisers if present + for (key, value) in &mut *input_values { + let value_ref = value.clone(); + match self.surml_file.header.get_normaliser(&key.to_string())? { + Some(normaliser) => { + *value = normaliser.normalise(value_ref); + }, + None => {} + } + } + let tensor = self.input_tensor_from_key_bindings(input_values.clone())?; + // dims are None because dims is depcrecated as we are now getting the dims from the ONNX file but + // we will keep it here for now to keep the function signature the same and to see if we need it later + let output = self.raw_compute_v2(tensor, None)?; + + // if no normaliser is present, return the output + if self.surml_file.header.output.normaliser == None { + return Ok(output) + } + + // apply the normaliser to the output + let output_normaliser = match self.surml_file.header.output.normaliser.as_ref() { + Some(normaliser) => normaliser, + None => return Err(SurrealError::new( + String::from("No normaliser present for output which shouldn't happen as passed initial check for").to_string(), + SurrealErrorStatus::Unknown + )) + }; + let mut buffer = Vec::with_capacity(output.len()); + + for value in output { + buffer.push(output_normaliser.inverse_normalise(value)); + } + return Ok(buffer) + } + } @@ -254,8 +310,11 @@ mod tests { let output = model_computation.raw_compute(raw_input, Some((1, 2))).unwrap(); assert_eq!(output.len(), 1); assert_eq!(output[0], 985.57745); + + // testing the v2 let output = model_computation.raw_compute_v2(raw_input_two, Some((1, 2))).unwrap(); - println!("{:?}", output); + assert_eq!(output.len(), 1); + assert_eq!(output[0], 985.57745); } #[cfg(feature = "sklearn-tests")] @@ -272,6 +331,10 @@ mod tests { let output = model_computation.buffered_compute(&mut input_values).unwrap(); assert_eq!(output.len(), 1); + + // testing the v2 + let output = model_computation.buffered_compute_v2(&mut input_values).unwrap(); + assert_eq!(output.len(), 1); } #[cfg(feature = "onnx-tests")] @@ -287,10 +350,16 @@ mod tests { input_values.insert(String::from("num_floors"), 2.0); let raw_input = model_computation.input_tensor_from_key_bindings(input_values).unwrap(); + let raw_input_two = raw_input.clone(); let output = model_computation.raw_compute(raw_input, Some((1, 2))).unwrap(); assert_eq!(output.len(), 1); assert_eq!(output[0], 985.57745); + + // testing the v2 + let output = model_computation.raw_compute_v2(raw_input_two, Some((1, 2))).unwrap(); + assert_eq!(output.len(), 1); + assert_eq!(output[0], 985.57745); } #[cfg(feature = "onnx-tests")] @@ -307,6 +376,10 @@ mod tests { let output = model_computation.buffered_compute(&mut input_values).unwrap(); assert_eq!(output.len(), 1); + + // testing the v2 + let output = model_computation.buffered_compute_v2(&mut input_values).unwrap(); + assert_eq!(output.len(), 1); } #[cfg(feature = "torch-tests")] @@ -322,9 +395,14 @@ mod tests { input_values.insert(String::from("num_floors"), 2.0); let raw_input = model_computation.input_tensor_from_key_bindings(input_values).unwrap(); + let raw_input_two = raw_input.clone(); let output = model_computation.raw_compute(raw_input, None).unwrap(); assert_eq!(output.len(), 1); + + // testing V2 + let output = model_computation.raw_compute_v2(raw_input_two, None).unwrap(); + assert_eq!(output.len(), 1); } #[cfg(feature = "torch-tests")] @@ -341,6 +419,10 @@ mod tests { let output = model_computation.buffered_compute(&mut input_values).unwrap(); assert_eq!(output.len(), 1); + + // testing V2 + let output = model_computation.buffered_compute_v2(&mut input_values).unwrap(); + assert_eq!(output.len(), 1); } #[cfg(feature = "tensorflow-tests")] @@ -356,9 +438,14 @@ mod tests { input_values.insert(String::from("num_floors"), 2.0); let raw_input = model_computation.input_tensor_from_key_bindings(input_values).unwrap(); + let raw_input_two = raw_input.clone(); let output = model_computation.raw_compute(raw_input, None).unwrap(); assert_eq!(output.len(), 1); + + // testing v2 + let output = model_computation.raw_compute_v2(raw_input_two, None).unwrap(); + assert_eq!(output.len(), 1); } #[cfg(feature = "tensorflow-tests")] @@ -375,5 +462,9 @@ mod tests { let output = model_computation.buffered_compute(&mut input_values).unwrap(); assert_eq!(output.len(), 1); + + // testing v2 + let output = model_computation.buffered_compute_v2(&mut input_values).unwrap(); + assert_eq!(output.len(), 1); } } diff --git a/modules/core/src/lib.rs b/modules/core/src/lib.rs index 625d826..603dbb3 100644 --- a/modules/core/src/lib.rs +++ b/modules/core/src/lib.rs @@ -87,6 +87,16 @@ //! // None input can be a tuple of dimensions of the input data //! let output = compute_unit.raw_compute(data, None).unwrap(); //! ``` +//! +//! ### V2 Ort support +//! Version 2 of Ort is now supported as well but this is in beta as V2 of Ort in general is also +//! in beta. Everything is the same apart from you adding `v2` to the end of your execution calls +//! like so: +//! +//! ```rust +//! let output = compute_unit.buffered_compute_v2(&mut input_values).unwrap(); +//! let output = compute_unit.raw_compute_v2(data, None).unwrap(); +//! ``` pub mod storage; pub mod execution; pub mod errors; From 7b92854e7a1429b27b94253b605b7111f2e4076d Mon Sep 17 00:00:00 2001 From: maxwellflitton Date: Fri, 26 Apr 2024 13:05:40 +0100 Subject: [PATCH 3/4] updating doc tests --- modules/core/src/lib.rs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/core/src/lib.rs b/modules/core/src/lib.rs index 603dbb3..e0a8e9c 100644 --- a/modules/core/src/lib.rs +++ b/modules/core/src/lib.rs @@ -92,8 +92,30 @@ //! Version 2 of Ort is now supported as well but this is in beta as V2 of Ort in general is also //! in beta. Everything is the same apart from you adding `v2` to the end of your execution calls //! like so: -//! //! ```rust +//! use surrealml_core::storage::surml_file::SurMlFile; +//! use surrealml_core::execution::compute::ModelComputation; +//! use ndarray::ArrayD; +//! use std::collections::HashMap; +//! +//! +//! let mut file = SurMlFile::from_file("./stash/test.surml").unwrap(); +//! +//! let compute_unit = ModelComputation { +//! surml_file: &mut file, +//! }; +//! +//! // automatically map inputs and apply normalisers to the compute if this data was put in the header +//! let mut input_values = HashMap::new(); +//! input_values.insert(String::from("squarefoot"), 1000.0); +//! input_values.insert(String::from("num_floors"), 2.0); +//! +//! let output = compute_unit.buffered_compute(&mut input_values).unwrap(); +//! +//! // feed a raw ndarray into the model if no header was provided or if you want to bypass the header +//! let x = vec![1000.0, 2.0]; +//! let data: ArrayD = ndarray::arr1(&x).into_dyn(); +//! //! let output = compute_unit.buffered_compute_v2(&mut input_values).unwrap(); //! let output = compute_unit.raw_compute_v2(data, None).unwrap(); //! ``` From 4a7fec13e2bd1c6b15452ed4528cbf72b8a08c1e Mon Sep 17 00:00:00 2001 From: maxwellflitton Date: Fri, 26 Apr 2024 13:18:20 +0100 Subject: [PATCH 4/4] updating lib tests --- .github/workflows/surrealml_core_onnx_test.yml | 2 +- .../surrealml_core_tensorflow_test.yml | 2 +- .github/workflows/surrealml_core_test.yml | 2 +- .github/workflows/surrealml_core_torch_test.yml | 2 +- modules/core/stash/test.surml | Bin 433 -> 369 bytes 5 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/surrealml_core_onnx_test.yml b/.github/workflows/surrealml_core_onnx_test.yml index 03ab696..5e6d606 100644 --- a/.github/workflows/surrealml_core_onnx_test.yml +++ b/.github/workflows/surrealml_core_onnx_test.yml @@ -46,4 +46,4 @@ jobs: deactivate - name: Run Core Unit Tests - run: cd modules/core && cargo test --features onnx-tests + run: cd modules/core && cargo test --features onnx-tests --lib diff --git a/.github/workflows/surrealml_core_tensorflow_test.yml b/.github/workflows/surrealml_core_tensorflow_test.yml index 355e754..1652020 100644 --- a/.github/workflows/surrealml_core_tensorflow_test.yml +++ b/.github/workflows/surrealml_core_tensorflow_test.yml @@ -46,4 +46,4 @@ jobs: deactivate - name: Run Core Unit Tests - run: cd modules/core && cargo test --features tensorflow-tests + run: cd modules/core && cargo test --features tensorflow-tests --lib diff --git a/.github/workflows/surrealml_core_test.yml b/.github/workflows/surrealml_core_test.yml index be6e3d9..f6edc23 100644 --- a/.github/workflows/surrealml_core_test.yml +++ b/.github/workflows/surrealml_core_test.yml @@ -53,7 +53,7 @@ jobs: deactivate - name: Run Core Unit Tests - run: cd modules/core && cargo test --features sklearn-tests + run: cd modules/core && cargo test --features sklearn-tests --lib - name: Run HTTP Transfer Tests run: cargo test diff --git a/.github/workflows/surrealml_core_torch_test.yml b/.github/workflows/surrealml_core_torch_test.yml index 01b679f..38e78f9 100644 --- a/.github/workflows/surrealml_core_torch_test.yml +++ b/.github/workflows/surrealml_core_torch_test.yml @@ -55,4 +55,4 @@ jobs: deactivate - name: Run Core Unit Tests - run: cd modules/core && cargo test --features torch-tests + run: cd modules/core && cargo test --features torch-tests --lib diff --git a/modules/core/stash/test.surml b/modules/core/stash/test.surml index 61da29a225240cea28e447c93c4f81df8bbdf875..45bcd456f17700db03c0f62c8c6901d3c45c2484 100644 GIT binary patch delta 43 wcmdnU{E