Files
rustytorch/crates/training/rtx-model-merging/src/config.rs
T
2026-03-04 00:08:42 +00:00

608 lines
17 KiB
Rust

//! Configuration system for model merging operations
use crate::error::{MergeError, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
/// Main configuration for model merging operations
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct MergeConfig {
/// Model loader configuration
pub loader: ModelLoaderConfig,
/// Validation configuration
pub validation: ValidationConfig,
/// Evaluation configuration
pub evaluation: EvaluationConfig,
/// Performance optimization settings
pub performance: PerformanceConfig,
/// Resource limits
pub resources: ResourceConfig,
}
/// Configuration for model loading
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelLoaderConfig {
/// Supported model formats
pub supported_formats: Vec<String>,
/// Memory mapping for large models
pub use_memory_mapping: bool,
/// Streaming threshold in MB
pub streaming_threshold_mb: usize,
/// Lazy loading enabled
pub lazy_loading: bool,
/// Cache loaded models
pub cache_models: bool,
/// Maximum cache size in MB
pub max_cache_size_mb: usize,
/// Parallel loading workers
pub parallel_workers: usize,
}
impl Default for ModelLoaderConfig {
fn default() -> Self {
Self {
supported_formats: vec![
"safetensors".to_string(),
"pytorch".to_string(),
"onnx".to_string(),
"huggingface".to_string(),
],
use_memory_mapping: true,
streaming_threshold_mb: 1024,
lazy_loading: true,
cache_models: true,
max_cache_size_mb: 8192,
parallel_workers: 4,
}
}
}
/// Configuration for model validation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationConfig {
/// Enable architecture validation
pub validate_architecture: bool,
/// Enable parameter compatibility checks
pub validate_parameters: bool,
/// Enable numerical stability checks
pub validate_numerical_stability: bool,
/// Enable memory usage validation
pub validate_memory_usage: bool,
/// Tolerance for numerical comparisons
pub numerical_tolerance: f32,
/// Maximum allowed parameter deviation
pub max_parameter_deviation: f32,
/// Validation timeout in seconds
pub timeout_seconds: u64,
}
impl Default for ValidationConfig {
fn default() -> Self {
Self {
validate_architecture: true,
validate_parameters: true,
validate_numerical_stability: true,
validate_memory_usage: true,
numerical_tolerance: 1e-6,
max_parameter_deviation: 0.1,
timeout_seconds: 300,
}
}
}
/// Configuration for performance evaluation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EvaluationConfig {
/// Enable performance benchmarking
pub enable_benchmarking: bool,
/// Benchmark datasets
pub benchmark_datasets: Vec<String>,
/// Evaluation metrics to compute
pub metrics: Vec<String>,
/// Evaluation batch size
pub batch_size: usize,
/// Number of evaluation samples
pub num_samples: usize,
/// Enable A/B testing
pub enable_ab_testing: bool,
/// Statistical significance threshold
pub significance_threshold: f32,
}
impl Default for EvaluationConfig {
fn default() -> Self {
Self {
enable_benchmarking: true,
benchmark_datasets: vec!["validation".to_string(), "test".to_string()],
metrics: vec![
"accuracy".to_string(),
"loss".to_string(),
"f1_score".to_string(),
"perplexity".to_string(),
],
batch_size: 32,
num_samples: 1000,
enable_ab_testing: true,
significance_threshold: 0.05,
}
}
}
/// Configuration for performance optimization
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceConfig {
/// Enable GPU acceleration
pub use_gpu: bool,
/// GPU device IDs to use
pub gpu_devices: Vec<usize>,
/// Enable mixed precision
pub mixed_precision: bool,
/// Enable parallel processing
pub parallel_processing: bool,
/// Number of worker threads
pub num_threads: usize,
/// Batch processing size
pub batch_size: usize,
/// Enable vectorized operations
pub vectorized_ops: bool,
/// Cache intermediate results
pub cache_intermediates: bool,
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
use_gpu: true,
gpu_devices: vec![0],
mixed_precision: true,
parallel_processing: true,
num_threads: std::thread::available_parallelism()
.map(std::num::NonZero::get)
.unwrap_or(4),
batch_size: 64,
vectorized_ops: true,
cache_intermediates: true,
}
}
}
/// Configuration for resource limits
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceConfig {
/// Maximum memory usage in MB
pub max_memory_mb: Option<usize>,
/// Maximum GPU memory usage in MB
pub max_gpu_memory_mb: Option<usize>,
/// Maximum disk usage for temporary files in MB
pub max_disk_usage_mb: Option<usize>,
/// Operation timeout in seconds
pub operation_timeout_sec: u64,
/// Enable resource monitoring
pub monitor_resources: bool,
/// Resource check interval in seconds
pub monitor_interval_sec: u64,
}
impl Default for ResourceConfig {
fn default() -> Self {
Self {
max_memory_mb: None, // No limit by default
max_gpu_memory_mb: None,
max_disk_usage_mb: Some(10240), // 10GB default
operation_timeout_sec: 3600, // 1 hour
monitor_resources: true,
monitor_interval_sec: 30,
}
}
}
/// Merge strategy configurations
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MergeStrategy {
/// TIES (Task-specific Interference Elimination) merging
Ties(TiesConfig),
/// DARE (Drop And REscale) merging
Dare(DareConfig),
/// SLERP (Spherical Linear Interpolation) merging
Slerp(SlerpConfig),
/// Task arithmetic merging
TaskArithmetic(TaskArithmeticConfig),
/// Fisher information weighted merging
Fisher(FisherConfig),
/// Model soup ensemble merging
ModelSoup(ModelSoupConfig),
/// Frankenmerging (layer-wise merging)
Frankenmerge(FrankenmergeConfig),
/// Progressive merging with validation
Progressive(ProgressiveConfig),
}
/// Configuration for TIES merging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TiesConfig {
/// Sign consistency threshold
pub sign_threshold: f32,
/// Magnitude threshold for parameter selection
pub magnitude_threshold: f32,
/// Rescaling method
pub rescale_method: RescaleMethod,
/// Enable sign voting
pub enable_sign_voting: bool,
/// Voting threshold
pub voting_threshold: f32,
/// Density parameter
pub density: f32,
}
impl Default for TiesConfig {
fn default() -> Self {
Self {
sign_threshold: 0.5,
magnitude_threshold: 0.1,
rescale_method: RescaleMethod::Magnitude,
enable_sign_voting: true,
voting_threshold: 0.5,
density: 0.8,
}
}
}
/// Configuration for DARE merging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DareConfig {
/// Drop probability
pub drop_probability: f32,
/// Rescaling factor
pub rescale_factor: f32,
/// Random seed for reproducibility
pub seed: Option<u64>,
/// Enable adaptive dropping
pub adaptive_dropping: bool,
/// Importance threshold
pub importance_threshold: f32,
}
impl Default for DareConfig {
fn default() -> Self {
Self {
drop_probability: 0.1,
rescale_factor: 1.0,
seed: None,
adaptive_dropping: false,
importance_threshold: 0.01,
}
}
}
/// Configuration for SLERP merging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SlerpConfig {
/// Interpolation parameter (0.0 to 1.0)
pub t: f32,
/// Enable quaternion-based interpolation
pub use_quaternions: bool,
/// Normalization method
pub normalization: NormalizationMethod,
/// Enable adaptive interpolation
pub adaptive_t: bool,
/// Parameter-specific interpolation weights
pub parameter_weights: Option<HashMap<String, f32>>,
}
impl Default for SlerpConfig {
fn default() -> Self {
Self {
t: 0.5,
use_quaternions: false,
normalization: NormalizationMethod::L2,
adaptive_t: false,
parameter_weights: None,
}
}
}
/// Configuration for task arithmetic
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TaskArithmeticConfig {
/// Task vector scaling factors
pub scaling_factors: Vec<f32>,
/// Enable sign preservation
pub preserve_signs: bool,
/// Magnitude clipping threshold
pub magnitude_clip: Option<f32>,
/// Enable selective arithmetic
pub selective_arithmetic: bool,
/// Parameter selection criteria
pub selection_criteria: Vec<String>,
}
impl Default for TaskArithmeticConfig {
fn default() -> Self {
Self {
scaling_factors: vec![1.0],
preserve_signs: true,
magnitude_clip: None,
selective_arithmetic: false,
selection_criteria: vec![],
}
}
}
/// Configuration for Fisher information merging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FisherConfig {
/// Fisher information computation method
pub fisher_method: FisherMethod,
/// Regularization strength
pub regularization: f32,
/// Enable diagonal approximation
pub diagonal_fisher: bool,
/// Number of samples for Fisher estimation
pub num_samples: usize,
/// Enable empirical Fisher
pub empirical_fisher: bool,
}
impl Default for FisherConfig {
fn default() -> Self {
Self {
fisher_method: FisherMethod::Empirical,
regularization: 1e-6,
diagonal_fisher: true,
num_samples: 1000,
empirical_fisher: true,
}
}
}
/// Configuration for model soup merging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModelSoupConfig {
/// Ensemble weights
pub weights: Vec<f32>,
/// Soup method
pub soup_method: SoupMethod,
/// Enable greedy soup construction
pub greedy_soup: bool,
/// Validation metric for greedy selection
pub validation_metric: String,
/// Maximum models in soup
pub max_models: usize,
}
impl Default for ModelSoupConfig {
fn default() -> Self {
Self {
weights: vec![],
soup_method: SoupMethod::Uniform,
greedy_soup: false,
validation_metric: "accuracy".to_string(),
max_models: 10,
}
}
}
/// Configuration for Frankenmerging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FrankenmergeConfig {
/// Layer assignment strategy
pub layer_assignment: LayerAssignmentStrategy,
/// Enable cross-architecture merging
pub cross_architecture: bool,
/// Layer compatibility checks
pub compatibility_checks: bool,
/// Enable layer interpolation
pub layer_interpolation: bool,
/// Interpolation weights per layer
pub layer_weights: HashMap<String, f32>,
}
impl Default for FrankenmergeConfig {
fn default() -> Self {
Self {
layer_assignment: LayerAssignmentStrategy::BestPerformance,
cross_architecture: false,
compatibility_checks: true,
layer_interpolation: false,
layer_weights: HashMap::new(),
}
}
}
/// Configuration for progressive merging
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProgressiveConfig {
/// Number of progressive steps
pub num_steps: usize,
/// Validation frequency
pub validation_frequency: usize,
/// Early stopping patience
pub early_stopping_patience: Option<usize>,
/// Validation metric threshold
pub metric_threshold: f32,
/// Enable rollback on failure
pub enable_rollback: bool,
}
impl Default for ProgressiveConfig {
fn default() -> Self {
Self {
num_steps: 10,
validation_frequency: 1,
early_stopping_patience: Some(3),
metric_threshold: 0.01,
enable_rollback: true,
}
}
}
/// Rescaling methods for TIES
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RescaleMethod {
/// Rescale by magnitude
Magnitude,
/// Rescale by sign consistency
SignConsistency,
/// No rescaling
None,
}
/// Normalization methods for SLERP
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum NormalizationMethod {
/// L2 normalization
L2,
/// L1 normalization
L1,
/// No normalization
None,
}
/// Fisher information computation methods
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum FisherMethod {
/// Empirical Fisher information
Empirical,
/// True Fisher information
True,
/// Diagonal Fisher approximation
Diagonal,
}
/// Model soup methods
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SoupMethod {
/// Uniform averaging
Uniform,
/// Weighted averaging
Weighted,
/// Greedy soup construction
Greedy,
}
/// Layer assignment strategies for Frankenmerging
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LayerAssignmentStrategy {
/// Assign based on best performance
BestPerformance,
/// Round-robin assignment
RoundRobin,
/// Random assignment
Random,
/// Performance-weighted assignment
PerformanceWeighted,
}
/// Load configuration from file
pub fn load_config<P: AsRef<std::path::Path>>(path: P) -> Result<MergeConfig> {
let content = std::fs::read_to_string(path.as_ref())
.map_err(|e| MergeError::io(path.as_ref().display().to_string(), e))?;
let extension = path
.as_ref()
.extension()
.and_then(|s| s.to_str())
.unwrap_or("");
match extension {
"toml" => toml::from_str(&content).map_err(MergeError::from),
"json" => serde_json::from_str(&content).map_err(MergeError::from),
_ => Err(MergeError::unsupported_format(format!(
"Configuration format: {extension}"
))),
}
}
/// Save configuration to file
pub fn save_config<P: AsRef<std::path::Path>>(config: &MergeConfig, path: P) -> Result<()> {
let extension = path
.as_ref()
.extension()
.and_then(|s| s.to_str())
.unwrap_or("");
let content = match extension {
"toml" => toml::to_string_pretty(config)
.map_err(|e| MergeError::internal(format!("TOML serialization error: {e}")))?,
"json" => serde_json::to_string_pretty(config).map_err(MergeError::from)?,
_ => {
return Err(MergeError::unsupported_format(format!(
"Configuration format: {extension}"
)));
}
};
std::fs::write(path.as_ref(), content)
.map_err(|e| MergeError::io(path.as_ref().display().to_string(), e))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::NamedTempFile;
#[test]
fn test_default_config() {
let config = MergeConfig::default();
assert!(config.loader.use_memory_mapping);
assert!(config.validation.validate_architecture);
assert!(config.performance.use_gpu);
}
#[test]
fn test_ties_config() {
let ties = TiesConfig::default();
assert_eq!(ties.sign_threshold, 0.5);
assert_eq!(ties.density, 0.8);
}
#[test]
fn test_config_serialization() -> Result<()> {
let config = MergeConfig::default();
let json_str = serde_json::to_string(&config)?;
let deserialized: MergeConfig = serde_json::from_str(&json_str)?;
assert_eq!(
config.loader.use_memory_mapping,
deserialized.loader.use_memory_mapping
);
Ok(())
}
#[test]
fn test_config_file_operations() -> Result<()> {
let config = MergeConfig::default();
let temp_file = NamedTempFile::new()
.map_err(|e| MergeError::internal(format!("Failed to create temp file: {}", e)))?;
let mut temp_path = temp_file.path().to_path_buf();
temp_path.set_extension("json");
save_config(&config, &temp_path)?;
let loaded_config = load_config(&temp_path)?;
assert_eq!(
config.loader.parallel_workers,
loaded_config.loader.parallel_workers
);
Ok(())
}
#[test]
fn test_merge_strategy_serialization() -> Result<()> {
let strategy = MergeStrategy::Ties(TiesConfig::default());
let json_str = serde_json::to_string(&strategy)?;
let deserialized: MergeStrategy = serde_json::from_str(&json_str)?;
match deserialized {
MergeStrategy::Ties(_) => (),
_ => panic!("Wrong strategy type"),
}
Ok(())
}
}