prop/docs/plans/2026-04-01-algorithm-refinement-ml-training-design.md
Graham McIntire 69b5caf876
Normalize ML features to prevent NaN gradient explosion
Raw features had vastly different scales (pressure ~1013, sin/cos ~[-1,1])
causing gradient explosion. Normalize all atmospheric features to ~[0,1]
using known physical bounds. Add Polaris dep for optimizer.
2026-04-01 09:08:33 -05:00

87 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Algorithm Refinement & ML Training Design
## Goal
Refine the propagation scoring algorithm using 57K+ historical QSOs matched to HRRR atmospheric conditions, then train the Axon ML model on the refined algorithm's output for fast inference from HRRR forecast data.
## Data Assets
- 57,488 tropospheric QSOs (1992-2024) with distance as ground truth
- 54M HRRR grid profiles (2016-2026) at 0.125° resolution
- 326K surface observations
- 58K terrain profiles (97% BLOCKED — confirms atmospheric propagation)
- 3M propagation_scores (current algorithm output)
- Band distribution: 53K at 10G, 3.8K at 24G, 800 at 47G, 141 at 75G, sparse above
## Design Decisions
- **Target variable**: Hybrid — refine algorithm from QSO data, then train ML on refined scores
- **QSO-HRRR matching**: Endpoint average — HRRR conditions at both QSO endpoints, averaged
- **Algorithm scope**: Full re-examination — weights, thresholds, add/remove factors
- **ML deployment**: ML primary scorer with algorithm fallback for missing data
## Implementation Plan
### Stage 1: Analysis Mix Task (`mix propagation.analyze`)
Build QSO↔HRRR analysis dataset:
- For each QSO, find nearest HRRR grid point to pos1 and pos2 (round to 0.125°)
- Find HRRR profile at nearest hour to qso_timestamp
- Average atmospheric conditions across both endpoints
- Output: CSV or in-memory dataset with columns:
- qso_id, band, distance_km, month, utc_hour
- avg_surface_temp_c, avg_surface_dewpoint_c, avg_surface_pressure_mb
- avg_min_refractivity_gradient, avg_hpbl_m, avg_pwat_mm
- avg_surface_refractivity, ducting_at_either_endpoint
- terrain_verdict (BLOCKED/CLEAR/FRESNEL_PARTIAL)
Run correlation analysis:
- Spearman rank correlation of each variable with distance_km, stratified by band
- Bin each variable into deciles, compute median distance per bin (natural breakpoint curves)
- Check for interaction effects (e.g., gradient × time_of_day, hpbl × month)
- Look for currently-missing predictive factors
- Compare factor distributions for short (<100km) vs long (>300km) contacts
Output: printed report with findings and recommended changes.
### Stage 2: Update algo.md
Document all findings with data backing:
- Which factors gained/lost predictive power
- New threshold curves derived from data
- Revised weights with justification
- Any new factors added or old factors removed
- Before/after comparison of scoring behavior
### Stage 3: Update scorer.ex + band_config.ex
Implement revised algorithm:
- Updated scoring functions with new thresholds
- Updated weights in band_config
- New factors if analysis warrants
- All changes match algo.md documentation
- Run `mix test` to verify no regressions (update tests for new behavior)
### Stage 4: Recompute Propagation Scores
Run refined algorithm across HRRR grid:
- Either `mix propagation_grid` or a targeted recompute task
- Generates fresh propagation_scores with refined algorithm
- These become ML training data
### Stage 5: Training Mix Task (`mix propagation.train`)
Build training pipeline:
- Query propagation_scores joined to hrrr_profiles by lat/lon/valid_time
- Features: raw atmospheric inputs + cyclical temporal + log frequency
- Target: refined composite score normalized to 0-1
- 80/10/10 train/val/test split, stratified by band and score quartile
- MSE loss, Adam optimizer, early stopping on validation loss
- Save trained weights to priv/models/propagation_v1.nx
- Print evaluation metrics (RMSE, R², per-band performance)
### Stage 6: Integrate ML into Grid Worker
- model.ex: `predict_score/2` for inference
- PropagationGridWorker: try ML prediction first, fall back to Scorer.composite_score/2
- Graceful degradation when model file missing or inputs incomplete