Files
rustytorch/crates/training/rtx-transformers/examples/normalization_demo.rs
T
2026-03-04 00:08:42 +00:00

427 lines
13 KiB
Rust

//! Advanced Normalization Techniques Demo
//!
//! This example demonstrates the comprehensive normalization framework
//! implemented in RTX-Transformers, showcasing all available normalization
//! techniques and their use cases.
use rtx_tensor::{Device, Shape, Tensor};
use rtx_transformers::{
Result,
layers::{
AdaLayerNorm,
DeepNorm,
DeepNormConfig,
// Layer trait
Layer,
// Individual normalization types
LayerNorm,
// Position wrappers
NormPosition,
NormWrapper,
NormalizationConfig,
// Unified framework
NormalizationType,
PostNorm,
PowerNorm,
PreNorm,
RMSNorm,
ScaleNorm,
UnifiedNorm,
normalization_factory,
power_norm_presets,
},
};
use tracing::{Level, info, warn};
#[tokio::main]
async fn main() -> Result<()> {
// Initialize logging
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
info!("🚀 RTX Advanced Normalization Techniques Demo");
// Setup device
let device = Device::cuda(0)?;
let d_model = 512;
let batch_size = 2;
let seq_len = 8;
// Create sample input tensor
let input = Tensor::randn(Shape::new(vec![batch_size, seq_len, d_model])?, &device)?;
info!("📊 Input tensor shape: {:?}", input.shape().dims());
// Demo 1: Individual Normalization Techniques
demo_individual_norms(&input, &device).await?;
// Demo 2: Unified Normalization Framework
demo_unified_framework(&input, &device).await?;
// Demo 3: PreNorm vs PostNorm Configurations
demo_norm_positions(&input, &device).await?;
// Demo 4: DeepNorm for Very Deep Networks
demo_deep_norm(&input, &device).await?;
// Demo 5: Adaptive Normalization with Conditioning
demo_adaptive_norm(&input, &device).await?;
// Demo 6: Performance Comparison
demo_performance_comparison(&input, &device).await?;
info!("✅ Demo completed successfully!");
Ok(())
}
/// Demonstrate individual normalization techniques
async fn demo_individual_norms(input: &Tensor, device: &Device) -> Result<()> {
info!("\n🔬 Demo 1: Individual Normalization Techniques");
let d_model = input.shape().dims()[2];
// 1. Standard LayerNorm
info!(" Testing LayerNorm...");
let layer_norm = LayerNorm::new(d_model, 1e-5, true, device)?;
let ln_output = layer_norm.forward(input)?;
info!(" LayerNorm output shape: {:?}", ln_output.shape().dims());
// 2. RMSNorm (used in modern LLMs)
info!(" Testing RMSNorm...");
let rms_norm = RMSNorm::new(d_model, 1e-6, device)?;
let rms_output = rms_norm.forward(input)?;
info!(" RMSNorm output shape: {:?}", rms_output.shape().dims());
// 3. ScaleNorm (efficient alternative)
info!(" Testing ScaleNorm...");
let scale_norm = ScaleNorm::new(d_model, 1e-8, device)?;
let scale_output = scale_norm.forward(input)?;
info!(
" ScaleNorm output shape: {:?}",
scale_output.shape().dims()
);
// 4. PowerNorm variants
info!(" Testing PowerNorm variants...");
// L1 norm (Manhattan)
let l1_norm = power_norm_presets::manhattan_norm(d_model, device)?;
let l1_output = l1_norm.forward(input)?;
info!(" L1 norm output shape: {:?}", l1_output.shape().dims());
// L2 norm (Euclidean, equivalent to RMSNorm)
let l2_norm = power_norm_presets::euclidean_norm(d_model, device)?;
let l2_output = l2_norm.forward(input)?;
info!(" L2 norm output shape: {:?}", l2_output.shape().dims());
// Max norm (L∞)
let max_norm = power_norm_presets::max_norm(d_model, device)?;
let max_output = max_norm.forward(input)?;
info!(" Max norm output shape: {:?}", max_output.shape().dims());
Ok(())
}
/// Demonstrate unified normalization framework
async fn demo_unified_framework(input: &Tensor, device: &Device) -> Result<()> {
info!("\n🎯 Demo 2: Unified Normalization Framework");
let d_model = input.shape().dims()[2];
// Create different normalization configurations
let configs = vec![
(
"Standard LayerNorm",
normalization_factory::layer_norm(d_model),
),
("Modern RMSNorm", normalization_factory::rms_norm(d_model)),
(
"Efficient ScaleNorm",
normalization_factory::scale_norm(d_model),
),
("L2 Normalization", normalization_factory::l2_norm(d_model)),
(
"Custom Power Norm",
normalization_factory::power_norm(d_model, 1.5),
),
];
for (name, config) in configs {
info!(" Testing {}...", name);
let config = config.with_device(0);
match UnifiedNorm::from_config(&config) {
Ok(norm) => {
let output = norm.forward(input)?;
info!(
" {} - Type: {}, Shape: {:?}, Complexity: {:.2}x",
name,
norm.norm_type(),
output.shape().dims(),
config.norm_type.relative_complexity()
);
info!(" Use case: {}", config.norm_type.recommended_use_cases());
}
Err(e) => warn!(" Failed to create {}: {}", name, e),
}
}
Ok(())
}
/// Demonstrate PreNorm vs PostNorm configurations
async fn demo_norm_positions(input: &Tensor, device: &Device) -> Result<()> {
info!("\n📍 Demo 3: PreNorm vs PostNorm Configurations");
let d_model = input.shape().dims()[2];
// Create a simple mock sublayer for demonstration
#[derive(Debug)]
struct MockSublayer {
device: Device,
}
impl Layer for MockSublayer {
fn forward(&self, input: &Tensor) -> Result<Tensor> {
// Simple identity + small perturbation
let perturbation = Tensor::randn(input.shape().clone(), &self.device)? * 0.1;
Ok((input + &perturbation)?)
}
fn layer_type(&self) -> &'static str {
"MockSublayer"
}
fn device(&self) -> &Device {
&self.device
}
fn parameters(&self) -> Vec<&Tensor> {
vec![]
}
fn parameters_mut(&mut self) -> Vec<&mut Tensor> {
vec![]
}
}
let sublayer = MockSublayer {
device: device.clone(),
};
// Test PreNorm configuration
info!(" Testing PreNorm (Norm -> Sublayer -> Residual)...");
let norm_pre = RMSNorm::new(d_model, 1e-6, device)?;
let prenorm_block = PreNorm::new(norm_pre, sublayer);
let prenorm_output = prenorm_block.forward(input)?;
info!(
" PreNorm output shape: {:?}",
prenorm_output.shape().dims()
);
// Test PostNorm configuration
info!(" Testing PostNorm (Sublayer -> Residual -> Norm)...");
let norm_post = RMSNorm::new(d_model, 1e-6, device)?;
let sublayer2 = MockSublayer {
device: device.clone(),
};
let postnorm_block = PostNorm::new(norm_post, sublayer2);
let postnorm_output = postnorm_block.forward(input)?;
info!(
" PostNorm output shape: {:?}",
postnorm_output.shape().dims()
);
// Test generic wrapper
info!(" Testing NormWrapper with different positions...");
for position in [NormPosition::Pre, NormPosition::Post] {
let norm = RMSNorm::new(d_model, 1e-6, device)?;
let sublayer = MockSublayer {
device: device.clone(),
};
let wrapper = NormWrapper::new(position, norm, sublayer);
let output = wrapper.forward(input)?;
info!(
" {:?} wrapper output shape: {:?}",
position,
output.shape().dims()
);
}
Ok(())
}
/// Demonstrate DeepNorm for very deep networks
async fn demo_deep_norm(input: &Tensor, device: &Device) -> Result<()> {
info!("\n🏗️ Demo 4: DeepNorm for Very Deep Networks");
let d_model = input.shape().dims()[2];
// Test different network depths
let depths = vec![10, 100, 1000];
for num_layers in depths {
info!(" Testing DeepNorm for {} layers...", num_layers);
let config = DeepNormConfig::new(num_layers, d_model);
let deep_norm = DeepNorm::new(config, device)?;
info!(" Alpha (residual scaling): {:.4}", deep_norm.alpha());
info!(" Beta (init scaling): {:.4}", deep_norm.beta());
let output = deep_norm.forward(input)?;
info!(
" DeepNorm-{} output shape: {:?}",
num_layers,
output.shape().dims()
);
// Demonstrate residual connection scaling
let sublayer_output = input * 0.1; // Mock sublayer output
let residual_output = deep_norm.forward_residual(input, &sublayer_output)?;
info!(
" Residual output shape: {:?}",
residual_output.shape().dims()
);
}
Ok(())
}
/// Demonstrate adaptive normalization with conditioning
async fn demo_adaptive_norm(input: &Tensor, device: &Device) -> Result<()> {
info!("\n🎛️ Demo 5: Adaptive Normalization with Conditioning");
let d_model = input.shape().dims()[2];
let condition_dim = 128;
let batch_size = input.shape().dims()[0];
let seq_len = input.shape().dims()[1];
// Create conditioning input
let condition = Tensor::randn(
Shape::new(vec![batch_size, seq_len, condition_dim])?,
device,
)?;
info!(" Testing AdaLayerNorm with conditioning...");
let ada_norm = AdaLayerNorm::new(d_model, condition_dim, 1e-5, device)?;
// Test with conditioning
let conditioned_output = ada_norm.forward_conditioned(input, &condition)?;
info!(
" Conditioned output shape: {:?}",
conditioned_output.shape().dims()
);
// Test without conditioning (fallback)
let unconditioned_output = ada_norm.forward(input)?;
info!(
" Unconditioned output shape: {:?}",
unconditioned_output.shape().dims()
);
// Test through unified framework
let config = normalization_factory::ada_layer_norm(d_model, condition_dim).with_device(0);
match UnifiedNorm::from_config(&config) {
Ok(unified_norm) => {
let unified_output = unified_norm.forward_conditioned(input, Some(&condition))?;
info!(
" Unified AdaLayerNorm output shape: {:?}",
unified_output.shape().dims()
);
}
Err(e) => warn!(" Failed to create unified AdaLayerNorm: {}", e),
}
Ok(())
}
/// Demonstrate performance comparison between different norms
async fn demo_performance_comparison(input: &Tensor, device: &Device) -> Result<()> {
info!("\n⚡ Demo 6: Performance and Complexity Comparison");
let d_model = input.shape().dims()[2];
let norm_types = vec![
NormalizationType::LayerNorm {
elementwise_affine: true,
},
NormalizationType::RMSNorm,
NormalizationType::ScaleNorm,
NormalizationType::L1Norm,
NormalizationType::L2Norm,
NormalizationType::MaxNorm,
NormalizationType::PowerNorm { power: 1.5 },
];
info!(" Normalization Performance Summary:");
info!(
" {:20} | {:12} | {:8} | {}",
"Type", "Complexity", "Cond.", "Use Case"
);
info!(" {:-<20} | {:-<12} | {:-<8} | {}", "", "", "", "");
for norm_type in norm_types {
let complexity = norm_type.relative_complexity();
let conditioning = if norm_type.requires_conditioning() {
"Yes"
} else {
"No"
};
let use_case = norm_type.recommended_use_cases();
info!(
" {:20} | {:8.2}x | {:8} | {}",
format!("{:?}", norm_type)
.split('{')
.next()
.unwrap_or("Unknown"),
complexity,
conditioning,
use_case.split(',').next().unwrap_or(use_case)
);
}
// Test actual forward passes for timing (simplified)
info!("\n Testing forward pass execution...");
let configs = vec![
("LayerNorm", normalization_factory::layer_norm(d_model)),
("RMSNorm", normalization_factory::rms_norm(d_model)),
("ScaleNorm", normalization_factory::scale_norm(d_model)),
];
for (name, config) in configs {
let config = config.with_device(0);
if let Ok(norm) = UnifiedNorm::from_config(&config) {
// Simple timing test (not precise but illustrative)
let start = std::time::Instant::now();
for _ in 0..10 {
let _ = norm.forward(input)?;
}
let duration = start.elapsed();
info!(" {} - 10 iterations: {:?}", name, duration);
}
}
Ok(())
}
/// Helper function to show tensor statistics (for debugging/analysis)
#[allow(dead_code)]
fn show_tensor_stats(tensor: &Tensor, name: &str) -> Result<()> {
let data = tensor.to_vec()?;
let mean = data.iter().sum::<f32>() / data.len() as f32;
let variance = data.iter().map(|x| (x - mean).powi(2)).sum::<f32>() / data.len() as f32;
let std_dev = variance.sqrt();
let min_val = data.iter().fold(f32::INFINITY, |a, &b| a.min(b));
let max_val = data.iter().fold(f32::NEG_INFINITY, |a, &b| a.max(b));
info!(
" {} stats: mean={:.6}, std={:.6}, min={:.6}, max={:.6}",
name, mean, std_dev, min_val, max_val
);
Ok(())
}