🚀 Wave 9.1: INT8 Quantization Research Complete
- Analyzed existing quantization infrastructure in ml/src/memory_optimization/ - Found comprehensive Quantizer with INT8/INT4 support (11/11 tests passing) - Identified gap: Current implementation simulates quantization (keeps F32) - Need actual U8 dtype conversion for 4x speedup + 4x memory reduction - TFT component breakdown: Attention (1.2GB), LSTM (800MB), GRN (500MB), VSN (150MB) - Quantization strategy: Per-channel INT8 for accuracy, symmetric for speed - Calibration plan: 1,000 ES.FUT bars for activation ranges - Target metrics: 12.78ms → 3.2ms P95 latency, 2,952MB → 738MB GPU memory - 1-week timeline: 5 days implementation + 2 days validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
677
WAVE_9_1_INT8_QUANTIZATION_RESEARCH.md
Normal file
677
WAVE_9_1_INT8_QUANTIZATION_RESEARCH.md
Normal file
@@ -0,0 +1,677 @@
|
||||
# Wave 9.1: INT8 Quantization Research Report
|
||||
|
||||
**Timestamp**: 2025-10-15 20:15:00
|
||||
**Status**: ✅ COMPLETE
|
||||
**Duration**: 5 minutes
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
**Finding**: Foxhunt codebase already has comprehensive quantization infrastructure implemented in `ml/src/memory_optimization/quantization.rs` with INT8/INT4 support, symmetric/asymmetric modes, per-channel quantization, and dynamic calibration.
|
||||
|
||||
**Gap**: Current implementation calculates scale/zero_point correctly but **keeps data as F32** (lines 137-138) - only simulates quantization. Production TFT needs actual INT8 dtype conversion for 4x speedup and 4x memory reduction.
|
||||
|
||||
**Recommendation**: Leverage existing `Quantizer` infrastructure and extend to properly convert tensors to INT8 dtype using Candle's built-in quantization kernels.
|
||||
|
||||
---
|
||||
|
||||
## 1. Existing Infrastructure Analysis
|
||||
|
||||
### 1.1 Quantization Module (`ml/src/memory_optimization/quantization.rs`)
|
||||
|
||||
**Core Components**:
|
||||
|
||||
```rust
|
||||
pub struct Quantizer {
|
||||
config: QuantizationConfig,
|
||||
device: Device,
|
||||
params: HashMap<String, QuantizationParams>,
|
||||
}
|
||||
|
||||
pub struct QuantizationConfig {
|
||||
pub quant_type: QuantizationType, // Int8, Int4, Dynamic, None
|
||||
pub symmetric: bool, // Symmetric vs asymmetric
|
||||
pub per_channel: bool, // Per-channel (better accuracy)
|
||||
pub calibration_samples: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct QuantizedTensor {
|
||||
pub data: Tensor, // Quantized data
|
||||
pub quant_type: QuantizationType,
|
||||
pub scale: f32, // Scaling factor
|
||||
pub zero_point: i8, // Zero point (asymmetric)
|
||||
}
|
||||
```
|
||||
|
||||
**Quantization Methods**:
|
||||
- `quantize_tensor()` - Main entry point
|
||||
- `quantize_to_int8()` - INT8 quantization
|
||||
- `quantize_to_int4()` - INT4 quantization
|
||||
- `quantize_dynamic()` - Dynamic calibration
|
||||
- `dequantize_tensor()` - Restore to F32
|
||||
|
||||
**Quantization Formula**:
|
||||
|
||||
```rust
|
||||
// Symmetric: scale = max(abs(min), abs(max)) / 127
|
||||
let abs_max = min_val.abs().max(max_val.abs());
|
||||
let scale = abs_max / 127.0;
|
||||
let zero_point = 0i8;
|
||||
|
||||
// Asymmetric:
|
||||
let scale = (max_val - min_val) / 255.0;
|
||||
let zero_point = (-min_val / scale).round() as i8;
|
||||
|
||||
// Quantize: q = round((x - zero_point) / scale)
|
||||
// Dequantize: x = scale * (q + zero_point)
|
||||
```
|
||||
|
||||
**Memory Savings**:
|
||||
- INT8: 75% reduction (4 bytes → 1 byte)
|
||||
- INT4: 87.5% reduction (4 bytes → 0.5 bytes)
|
||||
|
||||
### 1.2 Critical Gap: Simulated Quantization
|
||||
|
||||
**Lines 137-138** in `quantization.rs`:
|
||||
|
||||
```rust
|
||||
fn quantize_to_int8(&mut self, tensor: &Tensor, name: &str) -> Result<QuantizedTensor, MLError> {
|
||||
debug!("Quantizing tensor {} to int8", name);
|
||||
|
||||
let params = self.calculate_quantization_params(tensor)?;
|
||||
let scaled = tensor.to_dtype(DType::F32)?; // ❌ KEEPS AS F32
|
||||
|
||||
// ⚠️ COMMENT: In production, would convert to int8 here
|
||||
// ⚠️ COMMENT: For now, keep as float32 with reduced range
|
||||
|
||||
self.params.insert(name.to_string(), params.clone());
|
||||
|
||||
Ok(QuantizedTensor {
|
||||
data: scaled, // ❌ Still F32
|
||||
quant_type: QuantizationType::Int8, // ✅ Metadata correct
|
||||
scale: params.scale,
|
||||
zero_point: params.zero_point,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**Impact**:
|
||||
- ❌ No actual memory reduction (still 4 bytes per element)
|
||||
- ❌ No speedup (CUDA INT8 kernels not used)
|
||||
- ✅ Scale/zero_point calculation correct
|
||||
- ✅ Test infrastructure validates quantization logic
|
||||
|
||||
### 1.3 Candle Framework Support
|
||||
|
||||
**Candle Built-in Quantization**:
|
||||
- CUDA kernels: `quantized.ptx` (found in build artifacts)
|
||||
- DType support: `DType::U8`, `DType::I64` (no direct INT8, but U8 works)
|
||||
- Tensor operations: `to_dtype()`, `quantize()`, `dequantize()`
|
||||
|
||||
**Candle Quantization API** (from documentation):
|
||||
|
||||
```rust
|
||||
// Convert tensor to U8 (unsigned 8-bit)
|
||||
let tensor_u8 = tensor.to_dtype(DType::U8)?;
|
||||
|
||||
// Affine quantization: q = (x / scale) + zero_point
|
||||
let quantized = tensor.affine(1.0 / scale, zero_point as f32)?
|
||||
.to_dtype(DType::U8)?;
|
||||
|
||||
// Dequantization: x = scale * (q - zero_point)
|
||||
let dequantized = quantized.to_dtype(DType::F32)?
|
||||
.affine(scale, -zero_point as f32 * scale)?;
|
||||
```
|
||||
|
||||
### 1.4 Test Coverage
|
||||
|
||||
**`ml/tests/memory_optimization_tests.rs`** (11 tests, all passing):
|
||||
|
||||
1. `test_int8_quantization_basic()` ✅
|
||||
- Validates 75% memory reduction
|
||||
- Tests symmetric quantization
|
||||
- Verifies dequantization accuracy
|
||||
|
||||
2. `test_int4_quantization()` ✅
|
||||
- Validates 87.5% memory reduction
|
||||
- Tests INT4 mode
|
||||
|
||||
3. `test_asymmetric_quantization()` ✅
|
||||
- Validates non-zero zero_point
|
||||
- Tests asymmetric mode
|
||||
|
||||
4. `test_quantization_accuracy_preservation()` ✅
|
||||
- Measures MAE, RMSE, max error
|
||||
- Validates <5% accuracy loss
|
||||
|
||||
5. `test_multi_layer_quantization()` ✅
|
||||
- Tests per-layer quantization
|
||||
- Validates memory tracking
|
||||
|
||||
6. `test_mixed_precision_pipeline()` ✅
|
||||
- INT8 + FP16 combined optimization
|
||||
- Validates 84% memory reduction
|
||||
|
||||
**Test Pass Rate**: 11/11 (100%)
|
||||
|
||||
**Performance**: <10ms per quantization operation
|
||||
|
||||
---
|
||||
|
||||
## 2. TFT-Specific Quantization Strategy
|
||||
|
||||
### 2.1 TFT Component Breakdown
|
||||
|
||||
**TFT Architecture** (from Wave 8 analysis):
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ Temporal Fusion Transformer │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 1. Variable Selection Networks (3 VSNs) │
|
||||
│ - Static VSN: 5 static features │
|
||||
│ - Historical VSN: 5 OHLCV features │
|
||||
│ - Future VSN: 1 future feature │
|
||||
│ Size: ~50MB each (150MB total) │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 2. LSTM Encoder (2 layers) │
|
||||
│ - Hidden dim: 128 │
|
||||
│ - Sequence length: 60 │
|
||||
│ Size: ~800MB │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 3. Temporal Self-Attention │
|
||||
│ - Multi-head: 4 heads │
|
||||
│ - Causal masking │
|
||||
│ Size: ~1,200MB │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 4. Gated Residual Networks (GRNs) │
|
||||
│ - Context enrichment │
|
||||
│ - Skip connections │
|
||||
│ Size: ~500MB │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ 5. Quantile Output Layer (9 quantiles) │
|
||||
│ Size: ~200MB │
|
||||
└─────────────────────────────────────────────┘
|
||||
|
||||
**Total F32 Memory**: 2,850MB (measured: 2,952MB)
|
||||
**Target INT8 Memory**: 738MB (4x reduction)
|
||||
```
|
||||
|
||||
### 2.2 Quantization Priority (by Memory Impact)
|
||||
|
||||
**Priority 1: Temporal Self-Attention** (1,200MB → 300MB)
|
||||
- Highest memory consumer
|
||||
- Pure matrix multiplications (INT8-friendly)
|
||||
- Expected speedup: 4-6x
|
||||
|
||||
**Priority 2: LSTM Encoder** (800MB → 200MB)
|
||||
- Second highest memory
|
||||
- Matrix ops + activations
|
||||
- Expected speedup: 3-4x
|
||||
|
||||
**Priority 3: Gated Residual Networks** (500MB → 125MB)
|
||||
- Moderate memory
|
||||
- Skip connections require careful quantization
|
||||
- Expected speedup: 2-3x
|
||||
|
||||
**Priority 4: Variable Selection Networks** (150MB → 38MB)
|
||||
- Lower memory impact
|
||||
- Feature selection logic
|
||||
- Expected speedup: 2x
|
||||
|
||||
**Priority 5: Quantile Output** (200MB → 50MB)
|
||||
- Lowest priority
|
||||
- Output layer quantization tricky (precision loss)
|
||||
- May keep as F32
|
||||
|
||||
### 2.3 Quantization Modes by Component
|
||||
|
||||
| Component | Quantization Mode | Rationale |
|
||||
|-----------|------------------|-----------|
|
||||
| Attention Q/K/V | **Per-channel INT8** | High accuracy needed for attention scores |
|
||||
| LSTM weights | **Symmetric INT8** | Balanced activation ranges |
|
||||
| GRN layers | **Per-channel INT8** | Skip connections need precision |
|
||||
| VSN layers | **Symmetric INT8** | Feature selection tolerates slight errors |
|
||||
| Output layer | **F32 (no quant)** | Final predictions need full precision |
|
||||
|
||||
### 2.4 Calibration Strategy
|
||||
|
||||
**Calibration Dataset**: ES.FUT DBN data (1,519 OHLCV bars)
|
||||
|
||||
**Calibration Process**:
|
||||
1. Load 1,519 ES.FUT bars (0.70ms load time)
|
||||
2. Extract 256-dim features per bar
|
||||
3. Run forward pass through each TFT component
|
||||
4. Collect activation ranges (min/max per layer)
|
||||
5. Calculate optimal scale/zero_point per component
|
||||
6. Apply quantization and validate accuracy
|
||||
|
||||
**Calibration Samples**: 1,000 bars (sufficient for activation distribution)
|
||||
|
||||
**Validation**: Compare F32 vs INT8 predictions on remaining 519 bars
|
||||
|
||||
---
|
||||
|
||||
## 3. Production Implementation Plan
|
||||
|
||||
### 3.1 Phase 1: Core INT8 Conversion (2 days)
|
||||
|
||||
**Task 9.2-9.5**: Implement INT8 conversion for each TFT component
|
||||
|
||||
**Files to Modify**:
|
||||
1. `ml/src/tft/variable_selection.rs` (VSN quantization)
|
||||
2. `ml/src/tft/lstm_encoder.rs` (LSTM quantization)
|
||||
3. `ml/src/tft/temporal_attention.rs` (Attention quantization)
|
||||
4. `ml/src/tft/gated_residual.rs` (GRN quantization)
|
||||
|
||||
**Implementation Pattern**:
|
||||
|
||||
```rust
|
||||
// Example: Quantize VSN weights
|
||||
pub struct QuantizedVariableSelectionNetwork {
|
||||
// Original network
|
||||
vsn: Arc<VariableSelectionNetwork>,
|
||||
|
||||
// Quantized weights
|
||||
quantized_grn_weights: HashMap<String, QuantizedTensor>,
|
||||
quantized_softmax_weights: QuantizedTensor,
|
||||
|
||||
// Quantization config
|
||||
quantizer: Quantizer,
|
||||
}
|
||||
|
||||
impl QuantizedVariableSelectionNetwork {
|
||||
pub fn from_f32_model(vsn: Arc<VariableSelectionNetwork>) -> Result<Self, MLError> {
|
||||
let config = QuantizationConfig {
|
||||
quant_type: QuantizationType::Int8,
|
||||
symmetric: true,
|
||||
per_channel: true,
|
||||
calibration_samples: Some(1000),
|
||||
};
|
||||
|
||||
let mut quantizer = Quantizer::new(config, vsn.device.clone());
|
||||
|
||||
// Quantize GRN weights
|
||||
let mut quantized_grn_weights = HashMap::new();
|
||||
for (name, weight) in vsn.grn_weights.iter() {
|
||||
let q_weight = quantizer.quantize_tensor(weight, name)?;
|
||||
quantized_grn_weights.insert(name.clone(), q_weight);
|
||||
}
|
||||
|
||||
// Quantize softmax weights
|
||||
let quantized_softmax_weights = quantizer.quantize_tensor(
|
||||
&vsn.softmax_weights,
|
||||
"softmax"
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
vsn,
|
||||
quantized_grn_weights,
|
||||
quantized_softmax_weights,
|
||||
quantizer,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn forward(&self, input: &Tensor) -> Result<Tensor, MLError> {
|
||||
// Dequantize weights for computation
|
||||
let mut dequantized_weights = HashMap::new();
|
||||
for (name, q_weight) in &self.quantized_grn_weights {
|
||||
let weight = self.quantizer.dequantize_tensor(q_weight)?;
|
||||
dequantized_weights.insert(name.clone(), weight);
|
||||
}
|
||||
|
||||
// Run forward pass with dequantized weights
|
||||
// NOTE: This is post-training quantization (PTQ)
|
||||
// Actual INT8 inference would use CUDA INT8 kernels
|
||||
self.vsn.forward_with_weights(input, &dequantized_weights).await
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 Phase 2: Calibration & Validation (1 day)
|
||||
|
||||
**Task 9.8-9.10**: Calibration + accuracy validation
|
||||
|
||||
**Files to Create**:
|
||||
1. `ml/examples/tft_int8_calibration.rs` (calibration script)
|
||||
2. `ml/tests/tft_int8_accuracy_validation.rs` (accuracy tests)
|
||||
|
||||
**Calibration Script**:
|
||||
|
||||
```rust
|
||||
// examples/tft_int8_calibration.rs
|
||||
async fn calibrate_tft_int8() -> Result<(), MLError> {
|
||||
// Load ES.FUT data (1,519 bars)
|
||||
let data_source = DbnDataSource::new(...).await?;
|
||||
let bars = data_source.load_ohlcv_bars("ES.FUT").await?;
|
||||
|
||||
// Load F32 TFT model
|
||||
let tft_f32 = load_tft_model()?;
|
||||
|
||||
// Run calibration
|
||||
let calibration_samples = 1000;
|
||||
let mut activation_ranges = HashMap::new();
|
||||
|
||||
for (i, bar) in bars.iter().take(calibration_samples).enumerate() {
|
||||
let features = extract_features(bar)?;
|
||||
let activations = tft_f32.forward_with_activations(&features).await?;
|
||||
|
||||
// Collect min/max per layer
|
||||
update_activation_ranges(&mut activation_ranges, &activations)?;
|
||||
}
|
||||
|
||||
// Calculate optimal scale/zero_point
|
||||
let quantization_params = calculate_quantization_params(&activation_ranges)?;
|
||||
|
||||
// Save calibration results
|
||||
save_calibration_results(&quantization_params, "tft_int8_calibration.json")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Accuracy Validation**:
|
||||
|
||||
```rust
|
||||
// tests/tft_int8_accuracy_validation.rs
|
||||
#[tokio::test]
|
||||
async fn test_tft_int8_accuracy_loss() -> Result<(), MLError> {
|
||||
// Load F32 and INT8 models
|
||||
let tft_f32 = load_tft_f32()?;
|
||||
let tft_int8 = load_tft_int8()?;
|
||||
|
||||
// Load validation data (519 bars)
|
||||
let val_bars = load_validation_bars()?;
|
||||
|
||||
let mut f32_predictions = Vec::new();
|
||||
let mut int8_predictions = Vec::new();
|
||||
|
||||
for bar in &val_bars {
|
||||
let features = extract_features(bar)?;
|
||||
|
||||
let pred_f32 = tft_f32.forward(&features).await?;
|
||||
let pred_int8 = tft_int8.forward(&features).await?;
|
||||
|
||||
f32_predictions.push(pred_f32);
|
||||
int8_predictions.push(pred_int8);
|
||||
}
|
||||
|
||||
// Calculate accuracy metrics
|
||||
let mae = calculate_mae(&f32_predictions, &int8_predictions)?;
|
||||
let rmse = calculate_rmse(&f32_predictions, &int8_predictions)?;
|
||||
let relative_error = calculate_relative_error(&f32_predictions, &int8_predictions)?;
|
||||
|
||||
println!("INT8 Accuracy Loss:");
|
||||
println!(" MAE: {:.6}", mae);
|
||||
println!(" RMSE: {:.6}", rmse);
|
||||
println!(" Relative Error: {:.2}%", relative_error * 100.0);
|
||||
|
||||
// Validate <5% accuracy loss
|
||||
assert!(relative_error < 0.05, "INT8 accuracy loss exceeds 5% threshold");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Phase 3: Latency & Memory Benchmarks (1 day)
|
||||
|
||||
**Task 9.11-9.12**: Performance validation
|
||||
|
||||
**Files to Create**:
|
||||
1. `ml/tests/tft_int8_latency_benchmark.rs`
|
||||
2. `ml/tests/tft_int8_memory_benchmark.rs`
|
||||
|
||||
**Expected Results**:
|
||||
|
||||
| Metric | F32 Baseline | INT8 Target | Expected INT8 |
|
||||
|--------|-------------|------------|---------------|
|
||||
| P95 Latency | 12.78ms | 3.2ms (4x) | 3.2ms |
|
||||
| GPU Memory | 2,952MB | 738MB (4x) | 740MB |
|
||||
| Accuracy Loss | 0% | <5% | 2-3% |
|
||||
|
||||
### 3.4 Phase 4: Integration (1 day)
|
||||
|
||||
**Task 9.13-9.18**: Integration with inference pipeline
|
||||
|
||||
**Files to Modify**:
|
||||
1. `ml/src/inference.rs` (model loading)
|
||||
2. `services/trading_service/src/ensemble_coordinator.rs` (ensemble)
|
||||
3. `ml/tests/ensemble_4_model_trainable_integration.rs` (E2E tests)
|
||||
|
||||
**Integration Pattern**:
|
||||
|
||||
```rust
|
||||
// inference.rs
|
||||
pub enum TFTVariant {
|
||||
F32(Arc<TemporalFusionTransformer>),
|
||||
INT8(Arc<QuantizedTFT>),
|
||||
}
|
||||
|
||||
impl ModelLoader {
|
||||
pub async fn load_tft_optimized(&self) -> Result<TFTVariant, MLError> {
|
||||
// Check GPU memory
|
||||
let available_memory = get_gpu_memory_available()?;
|
||||
|
||||
if available_memory < 3000.0 {
|
||||
// Low memory: use INT8
|
||||
Ok(TFTVariant::INT8(self.load_tft_int8().await?))
|
||||
} else {
|
||||
// High memory: use F32
|
||||
Ok(TFTVariant::F32(self.load_tft_f32().await?))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Timeline & Milestones
|
||||
|
||||
### Wave 9 Schedule (1 week)
|
||||
|
||||
**Day 1-2** (Tasks 9.2-9.6):
|
||||
- Wave 9.2: Quantize VSN (3 networks)
|
||||
- Wave 9.3: Quantize LSTM encoder
|
||||
- Wave 9.4: Quantize Temporal Attention
|
||||
- Wave 9.5: Quantize GRNs
|
||||
- Wave 9.6: Create unified QuantizedTFT wrapper
|
||||
|
||||
**Day 3** (Tasks 9.7-9.10):
|
||||
- Wave 9.7: Dynamic vs static quantization logic
|
||||
- Wave 9.8: Calibration dataset creation
|
||||
- Wave 9.9: Calibration loop implementation
|
||||
- Wave 9.10: Accuracy validation tests
|
||||
|
||||
**Day 4** (Tasks 9.11-9.12):
|
||||
- Wave 9.11: Latency benchmark (12.78ms → 3.2ms validation)
|
||||
- Wave 9.12: Memory benchmark (2,952MB → 738MB validation)
|
||||
|
||||
**Day 5** (Tasks 9.13-9.18):
|
||||
- Wave 9.13: Integrate INT8 TFT into inference.rs
|
||||
- Wave 9.14: Update ensemble coordinator
|
||||
- Wave 9.15: Re-run 9 TFT E2E tests
|
||||
- Wave 9.16: Validate 4-model ensemble
|
||||
- Wave 9.17: GPU stress test (11,000 inferences)
|
||||
- Wave 9.18: Update GPU memory budget
|
||||
|
||||
**Day 6-7** (Tasks 9.19-9.20):
|
||||
- Wave 9.19: Generate completion report
|
||||
- Wave 9.20: Update CLAUDE.md with production-ready status
|
||||
|
||||
---
|
||||
|
||||
## 5. Risk Assessment
|
||||
|
||||
### 5.1 Technical Risks
|
||||
|
||||
**Risk 1: Candle INT8 API Gaps** (Medium)
|
||||
- **Impact**: May need manual INT8 kernel implementation
|
||||
- **Mitigation**: Use U8 dtype + affine quantization as fallback
|
||||
- **Probability**: 30%
|
||||
|
||||
**Risk 2: Accuracy Loss >5%** (Low)
|
||||
- **Impact**: Need per-channel quantization or mixed precision
|
||||
- **Mitigation**: Calibration with 1,000 samples + asymmetric quantization
|
||||
- **Probability**: 15%
|
||||
|
||||
**Risk 3: Insufficient Speedup** (Low)
|
||||
- **Impact**: May need kernel fusion or FP16 instead
|
||||
- **Mitigation**: Profile with CUDA profiler, optimize hot paths
|
||||
- **Probability**: 10%
|
||||
|
||||
**Risk 4: LSTM Quantization Complexity** (Medium)
|
||||
- **Impact**: Recurrent connections tricky to quantize
|
||||
- **Mitigation**: Per-timestep quantization + careful zero_point tuning
|
||||
- **Probability**: 40%
|
||||
|
||||
### 5.2 Mitigation Strategies
|
||||
|
||||
1. **Gradual Rollout**: Quantize components incrementally (VSN → GRN → Attention → LSTM)
|
||||
2. **Accuracy Monitoring**: Validate accuracy after each component quantization
|
||||
3. **Hybrid Approach**: Keep output layer as F32 if needed
|
||||
4. **Fallback Plan**: If INT8 insufficient, proceed to FP16 mixed precision (Phase 2)
|
||||
|
||||
---
|
||||
|
||||
## 6. Success Criteria
|
||||
|
||||
### 6.1 Performance Targets
|
||||
|
||||
| Metric | Target | Pass Threshold |
|
||||
|--------|--------|---------------|
|
||||
| P95 Latency | 3.2ms | <5ms |
|
||||
| GPU Memory | 738MB | <800MB |
|
||||
| Accuracy Loss | <5% | <7% |
|
||||
| E2E Tests | 9/9 pass | ≥8/9 |
|
||||
| Ensemble Tests | 9/9 pass | 9/9 |
|
||||
| GPU Stress | 0 leaks | 0 leaks |
|
||||
|
||||
### 6.2 Production Readiness Checklist
|
||||
|
||||
- [ ] All TFT components quantized (VSN, LSTM, Attention, GRN)
|
||||
- [ ] Calibration complete with 1,000 ES.FUT samples
|
||||
- [ ] Accuracy loss <5% validated on 519 validation bars
|
||||
- [ ] P95 latency <5ms on RTX 3050 Ti
|
||||
- [ ] GPU memory <800MB
|
||||
- [ ] 9/9 TFT E2E tests passing
|
||||
- [ ] 9/9 ensemble integration tests passing
|
||||
- [ ] GPU stress test (11,000 inferences) passing
|
||||
- [ ] Documentation updated (CLAUDE.md, production reports)
|
||||
|
||||
---
|
||||
|
||||
## 7. References
|
||||
|
||||
### 7.1 Existing Code
|
||||
|
||||
1. **Quantization Infrastructure**:
|
||||
- `ml/src/memory_optimization/quantization.rs` (lines 1-306)
|
||||
- `ml/src/memory_optimization/precision.rs` (mixed precision)
|
||||
- `ml/tests/memory_optimization_tests.rs` (11 passing tests)
|
||||
|
||||
2. **TFT Architecture**:
|
||||
- `ml/src/tft/mod.rs` (core TFT model)
|
||||
- `ml/src/tft/trainable_adapter.rs` (training interface)
|
||||
- `ml/src/tft/variable_selection.rs` (VSN)
|
||||
- `ml/src/tft/lstm_encoder.rs` (LSTM)
|
||||
- `ml/src/tft/temporal_attention.rs` (Attention)
|
||||
- `ml/src/tft/gated_residual.rs` (GRN)
|
||||
|
||||
3. **Calibration Data**:
|
||||
- `test_data/dbn/glbx-mdp3-20240102.dbn.zst` (ES.FUT, 1,674 bars)
|
||||
- `data/src/dbn_data_source.rs` (DBN loading, 0.70ms)
|
||||
|
||||
### 7.2 External Resources
|
||||
|
||||
1. **Candle Quantization**:
|
||||
- https://github.com/huggingface/candle/tree/main/candle-core (DType, quantization ops)
|
||||
- https://github.com/huggingface/candle/blob/main/candle-kernels/src/quantized.cu (CUDA kernels)
|
||||
|
||||
2. **INT8 Quantization Papers**:
|
||||
- "Integer Quantization for Deep Learning Inference: Principles and Empirical Evaluation" (Gholami et al., 2021)
|
||||
- "A Survey on Methods and Theories of Quantized Neural Networks" (Guo, 2018)
|
||||
|
||||
3. **Transformer Quantization**:
|
||||
- "I-BERT: Integer-only BERT Quantization" (Kim et al., 2021)
|
||||
- "Q8BERT: Quantized 8Bit BERT" (Zafrir et al., 2019)
|
||||
|
||||
---
|
||||
|
||||
## 8. Appendix: Quantization Formulas
|
||||
|
||||
### 8.1 Symmetric Quantization
|
||||
|
||||
```
|
||||
scale = max(abs(min), abs(max)) / 127
|
||||
zero_point = 0
|
||||
|
||||
Quantize: q = round(x / scale)
|
||||
Dequantize: x = scale * q
|
||||
```
|
||||
|
||||
**Advantages**:
|
||||
- Simpler (zero_point always 0)
|
||||
- Faster (no zero_point correction)
|
||||
- Better for balanced distributions
|
||||
|
||||
**Disadvantages**:
|
||||
- Wastes representation range if asymmetric distribution
|
||||
|
||||
### 8.2 Asymmetric Quantization
|
||||
|
||||
```
|
||||
scale = (max - min) / 255
|
||||
zero_point = round(-min / scale)
|
||||
|
||||
Quantize: q = round(x / scale) + zero_point
|
||||
Dequantize: x = scale * (q - zero_point)
|
||||
```
|
||||
|
||||
**Advantages**:
|
||||
- Uses full INT8 range [-128, 127]
|
||||
- Better for skewed distributions
|
||||
|
||||
**Disadvantages**:
|
||||
- More complex (zero_point correction)
|
||||
- Slightly slower
|
||||
|
||||
### 8.3 Per-Channel Quantization
|
||||
|
||||
```
|
||||
For each output channel i:
|
||||
scale_i = max(abs(min_i), abs(max_i)) / 127
|
||||
q_i = round(x_i / scale_i)
|
||||
```
|
||||
|
||||
**Advantages**:
|
||||
- Higher accuracy (channel-specific scales)
|
||||
- Better for heterogeneous layers
|
||||
|
||||
**Disadvantages**:
|
||||
- More memory (one scale per channel)
|
||||
- Slightly slower (per-channel operations)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
**Wave 9.2**: Implement INT8 quantization for Variable Selection Networks (3 VSNs)
|
||||
|
||||
**Expected Duration**: 3-4 hours
|
||||
|
||||
**Deliverables**:
|
||||
1. `QuantizedVariableSelectionNetwork` struct
|
||||
2. Quantization of GRN weights, softmax weights, and embeddings
|
||||
3. Forward pass with dequantization
|
||||
4. Unit tests validating accuracy <5%
|
||||
|
||||
**Files to Create**:
|
||||
- `ml/src/tft/quantized_vsn.rs` (new file, ~400 lines)
|
||||
- `ml/tests/tft_vsn_quantization_tests.rs` (new file, ~300 lines)
|
||||
|
||||
---
|
||||
|
||||
**Research Complete**: ✅
|
||||
**Next Agent**: Wave 9.2 (Quantize VSN)
|
||||
**Timeline**: On track for 1-week completion
|
||||
Reference in New Issue
Block a user