refactor(ml): delete legacy SimpleNeuralNetwork and TrainingPipeline dead code
Remove 520 lines of ndarray-based placeholder code that never performed real gradient descent. Production training uses Candle-based trainers in ml::trainers/. Deleted: SimpleNeuralNetwork, TrainingPipeline, NetworkConfig, ActivationType, TrainingMetrics, NetworkInterface, MockNetwork, and 9 associated tests. Kept: DeviceCapabilities, TrainingConfig, sub-module re-exports. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
//! GPU Performance Benchmarks for HFT ML Inference
|
||||
//!
|
||||
//! Validates sub-100μs inference requirements with real workloads
|
||||
//! Validates sub-100us inference requirements with real workloads
|
||||
|
||||
#[cfg(any(test, feature = "benchmarks"))]
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -14,59 +14,3 @@ use tokio::runtime::Runtime;
|
||||
use crate::liquid::network::LiquidNetworkConfig;
|
||||
#[cfg(any(test, feature = "benchmarks"))]
|
||||
use crate::liquid::training::{ActivationType, LiquidTrainer, LiquidTrainingConfig};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::training::{TrainingConfig, TrainingPipeline};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_gpu_infrastructure_initialization() {
|
||||
let infrastructure = TrainingPipeline::new(TrainingConfig::default());
|
||||
|
||||
assert!(infrastructure.device_capabilities().performance_score > 0.0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_network_creation() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let infrastructure = TrainingPipeline::new(TrainingConfig::default());
|
||||
|
||||
let network_config = crate::training::NetworkConfig {
|
||||
input_dim: 10,
|
||||
hidden_dims: vec![],
|
||||
output_dim: 3,
|
||||
activation: crate::training::ActivationType::ReLU,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
|
||||
let network = infrastructure.create_network(network_config).await?;
|
||||
// Network creation successful
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_inference_timing() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let infrastructure = TrainingPipeline::new(TrainingConfig::default());
|
||||
|
||||
let network_config = crate::training::NetworkConfig {
|
||||
input_dim: 25,
|
||||
hidden_dims: vec![],
|
||||
output_dim: 3,
|
||||
activation: crate::training::ActivationType::ReLU,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
|
||||
let network = infrastructure.create_network(network_config).await?;
|
||||
let input = vec![0.5f32; 25];
|
||||
|
||||
let start = Instant::now();
|
||||
let result = network.inference_hft(&input).await?;
|
||||
let inference_time = start.elapsed();
|
||||
|
||||
assert_eq!(result.len(), 3);
|
||||
|
||||
println!("Inference time: {:?}", inference_time);
|
||||
// Note: Actual performance depends on hardware
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,36 +17,7 @@ pub mod unified_trainer; // NEW: Unified training trait for all models // NEW: M
|
||||
|
||||
// NO RE-EXPORTS - Use explicit imports: unified_data_loader::{...}
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::time::Instant;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use ndarray::Array1;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::time::Duration;
|
||||
use tracing::info;
|
||||
|
||||
// Import CommonError for consistent error handling
|
||||
use common::error::CommonError;
|
||||
|
||||
/// Activation function types
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum ActivationType {
|
||||
ReLU,
|
||||
Sigmoid,
|
||||
Tanh,
|
||||
LeakyReLU,
|
||||
}
|
||||
|
||||
/// Network configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NetworkConfig {
|
||||
pub input_dim: usize,
|
||||
pub hidden_dims: Vec<usize>,
|
||||
pub output_dim: usize,
|
||||
pub dropout_rate: f64,
|
||||
pub activation: ActivationType,
|
||||
}
|
||||
|
||||
/// Training configuration
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -72,162 +43,6 @@ impl Default for TrainingConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Simple neural network implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SimpleNeuralNetwork {
|
||||
pub config: NetworkConfig,
|
||||
pub weights: Vec<Array1<f64>>,
|
||||
pub biases: Vec<Array1<f64>>,
|
||||
pub is_trained: bool,
|
||||
}
|
||||
|
||||
impl SimpleNeuralNetwork {
|
||||
/// Create a new neural network with the specified configuration
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `CommonError` if:
|
||||
/// - Configuration is invalid
|
||||
/// - Weight initialization fails
|
||||
/// - Memory allocation fails
|
||||
/// - Layer dimensions are incompatible
|
||||
pub fn new(config: NetworkConfig) -> Result<Self, CommonError> {
|
||||
let mut weights = Vec::new();
|
||||
let mut biases = Vec::new();
|
||||
|
||||
let mut layer_dims = vec![config.input_dim];
|
||||
layer_dims.extend(&config.hidden_dims);
|
||||
layer_dims.push(config.output_dim);
|
||||
|
||||
for i in 0..layer_dims.len() - 1 {
|
||||
let input_size = layer_dims[i];
|
||||
let output_size = layer_dims[i + 1];
|
||||
|
||||
// Initialize weights with random values
|
||||
let weight = Array1::from_vec(
|
||||
(0..input_size * output_size)
|
||||
.map(|_| fastrand::f64() * 2.0 - 1.0)
|
||||
.collect(),
|
||||
);
|
||||
weights.push(weight);
|
||||
|
||||
// Initialize biases to zero
|
||||
let bias = Array1::zeros(output_size);
|
||||
biases.push(bias);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
config,
|
||||
weights,
|
||||
biases,
|
||||
is_trained: false,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn forward(&self, input: &Array1<f64>) -> Result<Array1<f64>, CommonError> {
|
||||
let mut current = input.clone();
|
||||
|
||||
for (i, (weight, bias)) in self.weights.iter().zip(&self.biases).enumerate() {
|
||||
// Simple matrix multiplication (simplified)
|
||||
let output_size = bias.len();
|
||||
let mut output = Array1::zeros(output_size);
|
||||
|
||||
for j in 0..output_size {
|
||||
let mut sum = bias[j];
|
||||
for k in 0..current.len() {
|
||||
sum += current[k] * weight[k * output_size + j];
|
||||
}
|
||||
output[j] = sum;
|
||||
}
|
||||
|
||||
// Apply activation if not the last layer
|
||||
if i < self.weights.len() - 1 {
|
||||
current = self.apply_activation(&output)?;
|
||||
} else {
|
||||
current = output;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
pub fn apply_activation(&self, input: &Array1<f64>) -> Result<Array1<f64>, CommonError> {
|
||||
let result = match self.config.activation {
|
||||
ActivationType::ReLU => input.mapv(|x| x.max(0.0)),
|
||||
ActivationType::Sigmoid => input.mapv(|x| 1.0 / (1.0 + (-x).exp())),
|
||||
ActivationType::Tanh => input.mapv(|x| x.tanh()),
|
||||
ActivationType::LeakyReLU => input.mapv(|x| if x > 0.0 { x } else { x * 0.01 }),
|
||||
};
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn predict_fast(&self, input: &[f64]) -> Result<Vec<f64>, CommonError> {
|
||||
if !self.is_trained {
|
||||
return Err(CommonError::validation(
|
||||
"Model must be trained before prediction".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let input_array = Array1::from_vec(input.to_vec());
|
||||
let output = self.forward(&input_array)?;
|
||||
Ok(output.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/// Training metrics
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TrainingMetrics {
|
||||
pub train_losses: Vec<f64>,
|
||||
pub validation_losses: Vec<f64>,
|
||||
pub train_accuracies: Vec<f64>,
|
||||
pub validation_accuracies: Vec<f64>,
|
||||
pub best_validation_accuracy: f64,
|
||||
pub best_epoch: usize,
|
||||
pub final_train_loss: f64,
|
||||
pub final_validation_loss: f64,
|
||||
pub early_stopped: bool,
|
||||
pub training_time_seconds: f64,
|
||||
start_time: Option<Instant>,
|
||||
}
|
||||
|
||||
impl TrainingMetrics {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
start_time: Some(Instant::now()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_epoch_results(
|
||||
&mut self,
|
||||
train_loss: f64,
|
||||
val_loss: f64,
|
||||
train_acc: f64,
|
||||
val_acc: f64,
|
||||
epoch: usize,
|
||||
) {
|
||||
self.train_losses.push(train_loss);
|
||||
self.validation_losses.push(val_loss);
|
||||
self.train_accuracies.push(train_acc);
|
||||
self.validation_accuracies.push(val_acc);
|
||||
|
||||
if val_acc > self.best_validation_accuracy {
|
||||
self.best_validation_accuracy = val_acc;
|
||||
self.best_epoch = epoch;
|
||||
}
|
||||
|
||||
self.final_train_loss = train_loss;
|
||||
self.final_validation_loss = val_loss;
|
||||
}
|
||||
|
||||
pub fn complete_training(&mut self, early_stopped: bool) {
|
||||
self.early_stopped = early_stopped;
|
||||
if let Some(start) = self.start_time {
|
||||
self.training_time_seconds = start.elapsed().as_secs_f64();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Device capabilities for performance scoring
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceCapabilities {
|
||||
@@ -246,125 +61,6 @@ impl DeviceCapabilities {
|
||||
}
|
||||
}
|
||||
|
||||
/// Network interface trait
|
||||
#[async_trait]
|
||||
pub trait NetworkInterface {
|
||||
async fn inference_hft(&self, input: &[f32]) -> Result<Vec<f32>, CommonError>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Mock network for testing only - isolated from production
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MockNetwork {
|
||||
config: NetworkConfig,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl MockNetwork {
|
||||
pub fn new(config: NetworkConfig) -> Self {
|
||||
Self { config }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[async_trait]
|
||||
impl NetworkInterface for MockNetwork {
|
||||
async fn inference_hft(&self, _input: &[f32]) -> Result<Vec<f32>, CommonError> {
|
||||
// Mock inference - just return zeros of expected output size
|
||||
Ok(vec![0.0; self.config.output_dim])
|
||||
}
|
||||
}
|
||||
|
||||
/// Training pipeline
|
||||
#[derive(Debug)]
|
||||
pub struct TrainingPipeline {
|
||||
pub config: TrainingConfig,
|
||||
models: HashMap<String, SimpleNeuralNetwork>,
|
||||
device_caps: DeviceCapabilities,
|
||||
statistics: HashMap<String, f64>,
|
||||
}
|
||||
|
||||
impl TrainingPipeline {
|
||||
pub fn new(config: TrainingConfig) -> Self {
|
||||
let mut statistics = HashMap::new();
|
||||
statistics.insert("total_models".to_string(), 0.0);
|
||||
statistics.insert("trained_models".to_string(), 0.0);
|
||||
|
||||
Self {
|
||||
config,
|
||||
models: HashMap::new(),
|
||||
device_caps: DeviceCapabilities::cpu_default(),
|
||||
statistics,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn device_capabilities(&self) -> &DeviceCapabilities {
|
||||
&self.device_caps
|
||||
}
|
||||
|
||||
pub fn register_model(
|
||||
&mut self,
|
||||
name: String,
|
||||
model: SimpleNeuralNetwork,
|
||||
) -> Result<(), CommonError> {
|
||||
self.models.insert(name, model);
|
||||
if let Some(total_models) = self.statistics.get_mut("total_models") {
|
||||
*total_models += 1.0;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub async fn create_network(&self, config: NetworkConfig) -> Result<MockNetwork, CommonError> {
|
||||
let network = MockNetwork::new(config);
|
||||
Ok(network)
|
||||
}
|
||||
|
||||
pub async fn train_all_models(
|
||||
&mut self,
|
||||
_training_data: &[(Array1<f64>, Array1<f64>)],
|
||||
) -> Result<HashMap<String, TrainingMetrics>, CommonError> {
|
||||
let mut results = HashMap::new();
|
||||
|
||||
for (name, model) in &mut self.models {
|
||||
info!("Training model: {}", name);
|
||||
|
||||
let mut metrics = TrainingMetrics::new();
|
||||
|
||||
// SIMPLIFIED: Basic training loop - full implementation requires actual model training
|
||||
// TODO: Implement proper gradient descent, backpropagation, and loss calculation
|
||||
tracing::warn!(
|
||||
"train_all: using placeholder training loop. Use model-specific trainers for production."
|
||||
);
|
||||
for epoch in 0..self.config.epochs.min(3) {
|
||||
// Placeholder metrics - real implementation needs actual training
|
||||
let train_loss = 1.0 / (epoch as f64 + 1.0);
|
||||
let val_loss = train_loss * 1.1;
|
||||
let train_acc = 0.5 + 0.3 * epoch as f64 / self.config.epochs as f64;
|
||||
let val_acc = train_acc * 0.9;
|
||||
|
||||
metrics.add_epoch_results(train_loss, val_loss, train_acc, val_acc, epoch);
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
|
||||
model.is_trained = true;
|
||||
metrics.complete_training(false);
|
||||
|
||||
results.insert(name.clone(), metrics);
|
||||
if let Some(trained_models) = self.statistics.get_mut("trained_models") {
|
||||
*trained_models += 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn get_statistics(&self) -> &HashMap<String, f64> {
|
||||
&self.statistics
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -380,173 +76,10 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_creation() -> Result<(), CommonError> {
|
||||
let config = NetworkConfig {
|
||||
input_dim: 5,
|
||||
hidden_dims: vec![10, 8],
|
||||
output_dim: 3,
|
||||
activation: ActivationType::ReLU,
|
||||
dropout_rate: 0.1,
|
||||
};
|
||||
|
||||
let network = SimpleNeuralNetwork::new(config.clone())?;
|
||||
assert_eq!(network.config.input_dim, 5);
|
||||
assert_eq!(network.config.output_dim, 3);
|
||||
assert!(!network.is_trained);
|
||||
assert_eq!(network.weights.len(), 3); // 2 hidden + 1 output
|
||||
assert_eq!(network.biases.len(), 3);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_forward_pass() -> Result<(), CommonError> {
|
||||
let config = NetworkConfig {
|
||||
input_dim: 3,
|
||||
hidden_dims: vec![5],
|
||||
output_dim: 2,
|
||||
activation: ActivationType::ReLU,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
|
||||
let network = SimpleNeuralNetwork::new(config)?;
|
||||
let input = Array1::from(vec![1.0, 2.0, 3.0]);
|
||||
let output = network.forward(&input)?;
|
||||
|
||||
assert_eq!(output.len(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_activation_functions() -> Result<(), CommonError> {
|
||||
let input = Array1::from(vec![-2.0, -1.0, 0.0, 1.0, 2.0]);
|
||||
|
||||
// Test ReLU
|
||||
let relu_config = NetworkConfig {
|
||||
input_dim: 5,
|
||||
hidden_dims: vec![],
|
||||
output_dim: 5,
|
||||
activation: ActivationType::ReLU,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
let relu_network = SimpleNeuralNetwork::new(relu_config)?;
|
||||
let relu_output = relu_network.apply_activation(&input)?;
|
||||
|
||||
// ReLU should clip negative values to 0
|
||||
assert!(relu_output[0] >= 0.0);
|
||||
assert!(relu_output[1] >= 0.0);
|
||||
|
||||
// Test Sigmoid
|
||||
let sigmoid_config = NetworkConfig {
|
||||
input_dim: 5,
|
||||
hidden_dims: vec![],
|
||||
output_dim: 5,
|
||||
activation: ActivationType::Sigmoid,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
let sigmoid_network = SimpleNeuralNetwork::new(sigmoid_config)?;
|
||||
let sigmoid_output = sigmoid_network.apply_activation(&input)?;
|
||||
|
||||
// Sigmoid output should be between 0 and 1
|
||||
for &val in &sigmoid_output {
|
||||
assert!(val >= 0.0 && val <= 1.0);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_training_pipeline() -> Result<(), CommonError> {
|
||||
// Create a simple training dataset
|
||||
let mut training_data = Vec::new();
|
||||
for i in 0..100 {
|
||||
let x = i as f64 * 0.1;
|
||||
let input = Array1::from(vec![x, x * x]);
|
||||
let output = Array1::from(vec![x * 2.0]); // Simple linear relationship
|
||||
training_data.push((input, output));
|
||||
}
|
||||
|
||||
let config = TrainingConfig {
|
||||
epochs: 10,
|
||||
learning_rate: 0.01,
|
||||
batch_size: 10,
|
||||
validation_split: 0.2,
|
||||
early_stopping_patience: Some(5),
|
||||
random_seed: Some(42),
|
||||
};
|
||||
|
||||
let mut pipeline = TrainingPipeline::new(config);
|
||||
|
||||
// Create and register a simple model
|
||||
let model_config = NetworkConfig {
|
||||
input_dim: 2,
|
||||
hidden_dims: vec![4],
|
||||
output_dim: 1,
|
||||
activation: ActivationType::ReLU,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
|
||||
let model = SimpleNeuralNetwork::new(model_config)?;
|
||||
pipeline.register_model("test_model".to_string(), model)?;
|
||||
|
||||
// Train the model
|
||||
let results = pipeline.train_all_models(&training_data).await?;
|
||||
|
||||
assert_eq!(results.len(), 1);
|
||||
assert!(results.contains_key("test_model"));
|
||||
|
||||
let stats = pipeline.get_statistics();
|
||||
assert_eq!(stats.get("total_models").unwrap(), &1.0);
|
||||
assert_eq!(stats.get("trained_models").unwrap(), &1.0);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_fast_inference() -> Result<(), CommonError> {
|
||||
let config = NetworkConfig {
|
||||
input_dim: 4,
|
||||
hidden_dims: vec![8],
|
||||
output_dim: 2,
|
||||
activation: ActivationType::ReLU,
|
||||
dropout_rate: 0.0,
|
||||
};
|
||||
|
||||
let mut network = SimpleNeuralNetwork::new(config)?;
|
||||
|
||||
// Test prediction before training (should fail)
|
||||
let input = vec![1.0, 2.0, 3.0, 4.0];
|
||||
let result = network.predict_fast(&input).await;
|
||||
assert!(result.is_err());
|
||||
|
||||
// Mock training by setting is_trained to true
|
||||
network.is_trained = true;
|
||||
|
||||
// Test prediction after "training"
|
||||
let result = network.predict_fast(&input).await?;
|
||||
assert_eq!(result.len(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_training_metrics() {
|
||||
let mut metrics = TrainingMetrics::new();
|
||||
|
||||
// Add some epoch results
|
||||
metrics.add_epoch_results(0.8, 0.9, 0.7, 0.6, 0);
|
||||
metrics.add_epoch_results(0.6, 0.7, 0.8, 0.75, 1);
|
||||
metrics.add_epoch_results(0.5, 0.6, 0.85, 0.8, 2);
|
||||
|
||||
assert_eq!(metrics.train_losses.len(), 3);
|
||||
assert_eq!(metrics.best_validation_accuracy, 0.8);
|
||||
assert_eq!(metrics.best_epoch, 2);
|
||||
assert_relative_eq!(metrics.final_train_loss, 0.5, epsilon = 1e-10);
|
||||
assert_relative_eq!(metrics.final_validation_loss, 0.6, epsilon = 1e-10);
|
||||
|
||||
metrics.complete_training(false);
|
||||
assert!(!metrics.early_stopped);
|
||||
assert!(metrics.training_time_seconds >= 0.0);
|
||||
fn test_device_capabilities_cpu_default() {
|
||||
let caps = DeviceCapabilities::cpu_default();
|
||||
assert_eq!(caps.performance_score, 1.0);
|
||||
assert_eq!(caps.memory_gb, 8.0);
|
||||
assert!(caps.compute_units > 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user