pub use crate::std::engine_std::EngineStdError;
pub use crate::std::layers_std::{DenseLayerDesc, LayerSpec};
pub use native_neural_network::activations::ActivationKind;
#[derive(Debug, Clone)]
pub struct NeuralNetworkStd {
pub layers: Vec<usize>,
pub weights: Vec<f32>,
pub biases: Vec<f32>,
}
impl NeuralNetworkStd {
pub fn from_parts(layers: Vec<usize>, weights: Vec<f32>, biases: Vec<f32>) -> Option<Self> {
if layers.len() < 2 {
return None;
}
let mut expected_w = 0usize;
let mut expected_b = 0usize;
for i in 0..layers.len() - 1 {
let in_sz = layers[i];
let out_sz = layers[i + 1];
expected_w = expected_w.saturating_add(in_sz.saturating_mul(out_sz));
expected_b = expected_b.saturating_add(out_sz);
}
if weights.len() != expected_w || biases.len() != expected_b {
return None;
}
Some(NeuralNetworkStd {
layers,
weights,
biases,
})
}
pub fn expected_weights_count(layers: &[usize]) -> Option<usize> {
native_neural_network::network::NeuralNetwork::expected_weights_count(layers)
}
pub fn expected_biases_count(layers: &[usize]) -> Option<usize> {
native_neural_network::network::NeuralNetwork::expected_biases_count(layers)
}
pub fn layer_count(&self) -> usize {
self.layers.len().saturating_sub(1)
}
pub fn build_layer_specs(
&self,
hidden_activation: ActivationKind,
output_activation: ActivationKind,
) -> Option<Vec<LayerSpec>> {
let layer_count = self.layer_count();
if layer_count == 0 {
return None;
}
let mut native_out = vec![
native_neural_network::layers::LayerSpec::Dense(
native_neural_network::layers::DenseLayerDesc {
input_size: 0,
output_size: 0,
weight_offset: 0,
bias_offset: 0,
activation: ActivationKind::Identity
}
);
layer_count
];
let mut w_off = 0usize;
let mut b_off = 0usize;
for (i, slot) in native_out.iter_mut().enumerate().take(layer_count) {
let in_size = self.layers[i];
let out_size = self.layers[i + 1];
let weight_len = in_size.checked_mul(out_size)?;
let activation = if i + 1 == layer_count {
output_activation
} else {
hidden_activation
};
*slot = native_neural_network::layers::LayerSpec::Dense(
native_neural_network::layers::DenseLayerDesc {
input_size: in_size,
output_size: out_size,
weight_offset: w_off,
bias_offset: b_off,
activation,
},
);
w_off = w_off.checked_add(weight_len)?;
b_off = b_off.checked_add(out_size)?;
}
if w_off != self.weights.len() || b_off != self.biases.len() {
return None;
}
Some(
native_out
.into_iter()
.map(crate::std::layers_std::LayerSpec::from)
.collect(),
)
}
pub fn forward(
&self,
input: &[f32],
output: &mut [f32],
scratch: &mut [f32],
hidden_activation: ActivationKind,
output_activation: ActivationKind,
) -> Result<(), EngineStdError> {
let specs = self
.build_layer_specs(hidden_activation, output_activation)
.ok_or(EngineStdError::InvalidPlan)?;
let plan_std = crate::std::layers_std::LayerPlanStd::new(
specs,
self.weights.clone(),
self.biases.clone(),
);
crate::std::engine_std::forward_plan(&plan_std, input, output, scratch)
}
}