mirror of
https://github.com/ruvnet/RuView
synced 2026-07-20 17:03:24 +00:00
fix: workflow-review findings — BN gamma init, ThresholdParams serde, init docs
Concurrent validation workflow (2 review lanes + adversarial verification, 13 agents): 5 confirmed findings, 3 refuted. Fixes: - wiflow_std: pin BatchNorm gamma to 1.0 (tch default draws Uniform(0,1) — silently halves activations in from-scratch training; loaded checkpoints unaffected, parity re-verified after the change) - wiflow_std: document the conv-init divergences vs the reference's effective kaiming_normal(fan_out) re-init (from-scratch dynamics only) - ieee80211bf: ThresholdParams deserialization validates via try_from so the <=100 invariant holds for untrusted payloads (+ rejection test) Benchmarks (release, ruvzen): GeometryEmbedding 1.84us/call (542k/s), MAE tokenization 7.38us/window (135k/s), 802.11bf FSM 8.9M events/s — nothing suspicious. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -88,6 +88,13 @@ fn serde_rejects_out_of_range_setup_id() {
|
||||
assert!(serde_json::from_str::<MeasurementSetupId>("127").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serde_rejects_out_of_range_threshold_params() {
|
||||
assert!(serde_json::from_str::<ThresholdParams>(r#"{"delta_percent":255}"#).is_err());
|
||||
let ok = serde_json::from_str::<ThresholdParams>(r#"{"delta_percent":100}"#).unwrap();
|
||||
assert_eq!(ok.delta_percent(), 100);
|
||||
}
|
||||
|
||||
// ---------- validation, no panics ----------
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -216,11 +216,30 @@ pub fn bandwidth_mhz(bw: Bandwidth) -> u16 {
|
||||
/// Threshold-based reporting parameters: a report is generated only when the
|
||||
/// measurement changes by at least `delta_percent` relative to the last
|
||||
/// reported measurement (normalized-change trigger).
|
||||
///
|
||||
/// Deserialization validates through [`ThresholdParams::new`] so the
|
||||
/// `delta_percent <= 100` invariant holds on every construction path,
|
||||
/// including untrusted wire/persisted payloads (same convention as
|
||||
/// [`MeasurementSetupId`]).
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(try_from = "RawThresholdParams")]
|
||||
pub struct ThresholdParams {
|
||||
delta_percent: u8,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct RawThresholdParams {
|
||||
delta_percent: u8,
|
||||
}
|
||||
|
||||
impl TryFrom<RawThresholdParams> for ThresholdParams {
|
||||
type Error = BfError;
|
||||
|
||||
fn try_from(raw: RawThresholdParams) -> Result<Self, Self::Error> {
|
||||
Self::new(raw.delta_percent)
|
||||
}
|
||||
}
|
||||
|
||||
impl ThresholdParams {
|
||||
pub fn new(delta_percent: u8) -> Result<Self, BfError> {
|
||||
if delta_percent > 100 {
|
||||
|
||||
@@ -6,6 +6,17 @@ use tch::{nn, nn::Module, Tensor};
|
||||
|
||||
use super::config::{CONV_BLOCK_DROPOUT, TCN_KERNEL};
|
||||
|
||||
/// BatchNorm config matching the reference: gamma = 1 (PyTorch default; the
|
||||
/// reference additionally pins BatchNorm1d weight=1/bias=0). tch-0.24's
|
||||
/// `BatchNormConfig::default()` would draw gamma from Uniform(0,1), silently
|
||||
/// halving activations on average in from-scratch training.
|
||||
pub(super) fn bn_cfg() -> nn::BatchNormConfig {
|
||||
nn::BatchNormConfig {
|
||||
ws_init: nn::Init::Const(1.0),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GroupedTemporalBlock (TCN level)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -53,19 +64,19 @@ impl GroupedTemporalBlock {
|
||||
};
|
||||
|
||||
let conv1_group = nn::conv1d(&vs / "conv1_group", c_in, c_in, k, grouped_cfg(groups));
|
||||
let bn1_group = nn::batch_norm1d(&vs / "bn1_group", c_in, Default::default());
|
||||
let bn1_group = nn::batch_norm1d(&vs / "bn1_group", c_in, bn_cfg());
|
||||
let conv1_pw = nn::conv1d(&vs / "conv1_pw", c_in, c_out, 1, pointwise_cfg);
|
||||
let bn1_pw = nn::batch_norm1d(&vs / "bn1_pw", c_out, Default::default());
|
||||
let bn1_pw = nn::batch_norm1d(&vs / "bn1_pw", c_out, bn_cfg());
|
||||
|
||||
let conv2_group = nn::conv1d(&vs / "conv2_group", c_out, c_out, k, grouped_cfg(groups));
|
||||
let bn2_group = nn::batch_norm1d(&vs / "bn2_group", c_out, Default::default());
|
||||
let bn2_group = nn::batch_norm1d(&vs / "bn2_group", c_out, bn_cfg());
|
||||
let conv2_pw = nn::conv1d(&vs / "conv2_pw", c_out, c_out, 1, pointwise_cfg);
|
||||
let bn2_pw = nn::batch_norm1d(&vs / "bn2_pw", c_out, Default::default());
|
||||
let bn2_pw = nn::batch_norm1d(&vs / "bn2_pw", c_out, bn_cfg());
|
||||
|
||||
let downsample = (c_in != c_out).then(|| {
|
||||
(
|
||||
nn::conv1d(&vs / "ds_conv", c_in, c_out, 1, pointwise_cfg),
|
||||
nn::batch_norm1d(&vs / "ds_bn", c_out, Default::default()),
|
||||
nn::batch_norm1d(&vs / "ds_bn", c_out, bn_cfg()),
|
||||
)
|
||||
});
|
||||
|
||||
@@ -145,11 +156,11 @@ impl ConvBlock {
|
||||
..Default::default()
|
||||
};
|
||||
let conv1 = nn::conv(&vs / "conv1", c_in, c_out, [1, 3], asym(stride_w));
|
||||
let bn1 = nn::batch_norm2d(&vs / "bn1", c_out, Default::default());
|
||||
let bn1 = nn::batch_norm2d(&vs / "bn1", c_out, bn_cfg());
|
||||
let conv2 = nn::conv(&vs / "conv2", c_out, c_out, [1, 3], asym(1));
|
||||
let bn2 = nn::batch_norm2d(&vs / "bn2", c_out, Default::default());
|
||||
let bn2 = nn::batch_norm2d(&vs / "bn2", c_out, bn_cfg());
|
||||
let conv3 = nn::conv(&vs / "conv3", c_out, c_out, [1, 3], asym(1));
|
||||
let bn3 = nn::batch_norm2d(&vs / "bn3", c_out, Default::default());
|
||||
let bn3 = nn::batch_norm2d(&vs / "bn3", c_out, bn_cfg());
|
||||
|
||||
let ds_conv = nn::conv(
|
||||
&vs / "ds_conv",
|
||||
@@ -162,7 +173,7 @@ impl ConvBlock {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let ds_bn = nn::batch_norm2d(&vs / "ds_bn", c_out, Default::default());
|
||||
let ds_bn = nn::batch_norm2d(&vs / "ds_bn", c_out, bn_cfg());
|
||||
|
||||
ConvBlock {
|
||||
conv1,
|
||||
@@ -228,9 +239,9 @@ impl AxialAttention {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let bn_qkv = nn::batch_norm1d(&vs / "bn_qkv", planes * 3, Default::default());
|
||||
let bn_similarity = nn::batch_norm2d(&vs / "bn_similarity", groups, Default::default());
|
||||
let bn_output = nn::batch_norm1d(&vs / "bn_output", planes, Default::default());
|
||||
let bn_qkv = nn::batch_norm1d(&vs / "bn_qkv", planes * 3, bn_cfg());
|
||||
let bn_similarity = nn::batch_norm2d(&vs / "bn_similarity", groups, bn_cfg());
|
||||
let bn_output = nn::batch_norm1d(&vs / "bn_output", planes, bn_cfg());
|
||||
|
||||
AxialAttention {
|
||||
qkv,
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
//!
|
||||
//! Idiomatic reimplementation of the DY2434 reference (Apache-2.0); see the
|
||||
//! [module docs](crate::wiflow_std) for provenance and the evidence grade.
|
||||
//! Weights are initialised from scratch (tch defaults; the axial-attention
|
||||
//! qkv conv mirrors the reference's `N(0, sqrt(1/in_planes))` init). The
|
||||
//! From-scratch init: BN gamma is pinned to 1 (see `layers::bn_cfg`); the
|
||||
//! axial-attention qkv conv uses `N(0, sqrt(1/in_planes))` per the
|
||||
//! reference's `attention.py` intent (note the reference's *effective* init
|
||||
//! differs — its `_initialize_weights` re-inits every `nn.Conv1d`, qkv
|
||||
//! included, with `kaiming_normal(fan_out)`); conv weights keep tch defaults
|
||||
//! (kaiming-uniform fan_in), which differ in scale from PyTorch's defaults.
|
||||
//! These divergences affect from-scratch training dynamics only — BN absorbs
|
||||
//! them at init, and loaded checkpoints overwrite everything. The
|
||||
//! retrained PyTorch checkpoint loads via [`WiFlowStdModel::load`] after
|
||||
//! key-remapped safetensors export
|
||||
//! (`benchmarks/wiflow-std/export_to_safetensors.py`); numerical parity with
|
||||
@@ -99,9 +105,9 @@ impl WiFlowStdModel {
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let dec_bn1 = nn::batch_norm2d(&root / "dec_bn1", mid, Default::default());
|
||||
let dec_bn1 = nn::batch_norm2d(&root / "dec_bn1", mid, super::layers::bn_cfg());
|
||||
let dec_conv2 = nn::conv2d(&root / "dec_conv2", mid, 2, 1, Default::default());
|
||||
let dec_bn2 = nn::batch_norm2d(&root / "dec_bn2", 2, Default::default());
|
||||
let dec_bn2 = nn::batch_norm2d(&root / "dec_bn2", 2, super::layers::bn_cfg());
|
||||
|
||||
Ok(WiFlowStdModel {
|
||||
vs,
|
||||
|
||||
Reference in New Issue
Block a user