fix(cog-pose-estimation): emit frames under default config (ADR-159 A1)
pose_v1 has no confidence head, so infer() emits a constant 0.185 per frame. The config default_min_confidence was 0.3 and the runtime gates on confidence >= min_confidence, so a default install silently emitted ZERO pose.frame events while health reported healthy. - Add inference::MODEL_TYPICAL_CONFIDENCE (0.185, the validation PCK@50) as the single published per-frame confidence. - Pin default_min_confidence() to MODEL_TYPICAL_CONFIDENCE so a default install clears its own gate and emits. - Warn at run.started when min_confidence exceeds the model typical confidence (disclosed, not silent); document the trade-off in the config field, the JSON schema, and inference.rs. Failing-on-old test: default_config_emits_frames_with_real_model (with old 0.3 it panics: "default install would emit zero pose.frame events"). Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
parent
2e4461d64d
commit
98bf8c4726
|
|
@ -26,8 +26,8 @@
|
|||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1,
|
||||
"default": 0.3,
|
||||
"description": "Drop frames where the inferred pose confidence is below this threshold."
|
||||
"default": 0.185,
|
||||
"description": "Drop frames where the inferred pose confidence is below this threshold. pose_v1 has no confidence head, so every frame carries the model's published per-frame confidence (0.185 = validation PCK@50); the default is pinned to that value so a default install actually emits frames. Raising it above 0.185 suppresses ALL pose.frame events (the runtime warns when this happens)."
|
||||
}
|
||||
},
|
||||
"required": ["model_path"]
|
||||
|
|
|
|||
|
|
@ -23,6 +23,13 @@ pub struct CogConfig {
|
|||
pub poll_ms: u64,
|
||||
|
||||
/// Confidence threshold below which a frame's keypoints are not emitted.
|
||||
///
|
||||
/// Defaults to [`crate::inference::MODEL_TYPICAL_CONFIDENCE`] (0.185) — the
|
||||
/// model's published per-frame confidence. `pose_v1` has no confidence head,
|
||||
/// so every frame carries this same value; a default above it would silently
|
||||
/// suppress *all* `pose.frame` events while health still reports healthy.
|
||||
/// The runtime warns at `run.started` if this is raised above the model's
|
||||
/// typical confidence rather than dropping frames quietly.
|
||||
#[serde(default = "default_min_confidence")]
|
||||
pub min_confidence: f32,
|
||||
}
|
||||
|
|
@ -36,7 +43,9 @@ fn default_poll_ms() -> u64 {
|
|||
}
|
||||
|
||||
fn default_min_confidence() -> f32 {
|
||||
0.3
|
||||
// Pinned to the model's typical/published confidence so a default install
|
||||
// actually emits frames. See `min_confidence` doc and ADR-159 §A1.
|
||||
crate::inference::MODEL_TYPICAL_CONFIDENCE
|
||||
}
|
||||
|
||||
impl CogConfig {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,16 @@ pub const INPUT_SUBCARRIERS: usize = 56;
|
|||
pub const INPUT_TIMESTEPS: usize = 20;
|
||||
pub const OUTPUT_KEYPOINTS: usize = 17;
|
||||
|
||||
/// The model's typical self-reported confidence. `pose_v1` has **no confidence
|
||||
/// head** (the head emits 34 keypoint coordinates only), so per-frame confidence
|
||||
/// is not available from the network. This is the validation-set PCK@50 (18.5%)
|
||||
/// the training run reported, used as the published per-frame confidence floor.
|
||||
///
|
||||
/// Surfaced as a public constant so the runtime can warn when a configured
|
||||
/// `min_confidence` threshold exceeds it — otherwise a default install would
|
||||
/// silently emit zero `pose.frame` events while health reports healthy.
|
||||
pub const MODEL_TYPICAL_CONFIDENCE: f32 = 0.185;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CsiWindow {
|
||||
pub data: Vec<f32>, // length INPUT_SUBCARRIERS * INPUT_TIMESTEPS
|
||||
|
|
@ -283,12 +293,15 @@ impl InferenceEngine {
|
|||
let out = model.net.forward(&t)?; // [1, 34]
|
||||
let flat: Vec<f32> = out.flatten_all()?.to_vec1()?;
|
||||
// Confidence from pose_v1 is a published constant rather than per-frame —
|
||||
// the trained model didn't emit a confidence head. Use the validation-set
|
||||
// PCK@50 (18.5%) as the published self-reported confidence so downstream
|
||||
// consumers can gate display decisions on it.
|
||||
// the trained model has no confidence head (the head emits 34 keypoint
|
||||
// coordinates only), so a real per-frame value is genuinely unavailable.
|
||||
// We surface the validation-set PCK@50 (`MODEL_TYPICAL_CONFIDENCE`) as the
|
||||
// honest self-reported confidence. The runtime's `min_confidence` default
|
||||
// is pinned at or below this so a default install actually emits frames
|
||||
// (and warns if an operator raises the threshold above the model's reach).
|
||||
Ok(PoseOutput {
|
||||
keypoints: flat,
|
||||
confidence: 0.185,
|
||||
confidence: MODEL_TYPICAL_CONFIDENCE,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,18 @@ fn cmd_run(
|
|||
let cfg = CogConfig::load(&config_path)?;
|
||||
emit_event(&Event::run_started(COG_ID, &cfg));
|
||||
|
||||
// Disclosure: pose_v1 has no confidence head, so every frame carries the
|
||||
// same `MODEL_TYPICAL_CONFIDENCE`. A `min_confidence` above that silently
|
||||
// suppresses *all* pose.frame events. Warn loudly rather than drop quietly.
|
||||
if cfg.min_confidence > cog_pose_estimation::inference::MODEL_TYPICAL_CONFIDENCE {
|
||||
tracing::warn!(
|
||||
min_confidence = cfg.min_confidence,
|
||||
model_typical_confidence = cog_pose_estimation::inference::MODEL_TYPICAL_CONFIDENCE,
|
||||
"configured min_confidence exceeds the model's typical confidence; \
|
||||
no pose.frame events will be emitted until this is lowered"
|
||||
);
|
||||
}
|
||||
|
||||
let engine = InferenceEngine::with_adapter(adapter.as_deref())?;
|
||||
if engine.is_calibrated() {
|
||||
tracing::info!("per-room calibration adapter loaded");
|
||||
|
|
|
|||
|
|
@ -172,3 +172,56 @@ fn manifest_roundtrips() {
|
|||
assert_eq!(back.id, "pose-estimation");
|
||||
assert_eq!(back.version, "0.0.1");
|
||||
}
|
||||
|
||||
/// ADR-159 §A1 — the default-config min_confidence threshold must not silently
|
||||
/// suppress every `pose.frame`. With the old `default_min_confidence()=0.3` and
|
||||
/// the model's per-frame confidence pinned at 0.185, the runtime gate
|
||||
/// (`out.confidence >= cfg.min_confidence`) never fired, so a default install
|
||||
/// emitted ZERO frames while health reported healthy. This asserts the default
|
||||
/// install actually clears its own gate.
|
||||
#[test]
|
||||
fn default_config_emits_frames_with_real_model() {
|
||||
use cog_pose_estimation::config::CogConfig;
|
||||
|
||||
// A minimal config (only the required model_path) exercises every
|
||||
// `#[serde(default)]` path — i.e. the *default* install threshold.
|
||||
let cfg: CogConfig =
|
||||
serde_json::from_value(serde_json::json!({ "model_path": "pose_v1.safetensors" }))
|
||||
.expect("default config parse");
|
||||
|
||||
// Real model when present; stub otherwise. Either way the per-frame
|
||||
// confidence the runtime gates on must clear the default threshold,
|
||||
// OR (stub case) the gate must still let the model's typical confidence
|
||||
// through. We assert against the same value the runtime emits.
|
||||
let weights = std::path::Path::new("cog/artifacts/pose_v1.safetensors");
|
||||
let engine = if weights.exists() {
|
||||
InferenceEngine::with_weights(Some(weights)).expect("load real weights")
|
||||
} else {
|
||||
InferenceEngine::new().expect("engine init")
|
||||
};
|
||||
|
||||
// Core regression assertion (fails on the old `default_min_confidence()=0.3`):
|
||||
// the default threshold must not exceed the model's published per-frame
|
||||
// confidence (0.185), which is the exact value `infer()` emits for the real
|
||||
// model. With 0.3 the runtime gate `out.confidence >= min_confidence` never
|
||||
// fired → zero pose.frame events on a default install.
|
||||
assert!(
|
||||
cfg.min_confidence <= cog_pose_estimation::inference::MODEL_TYPICAL_CONFIDENCE,
|
||||
"default min_confidence {} exceeds model typical confidence {} — \
|
||||
a default install would emit zero pose.frame events",
|
||||
cfg.min_confidence,
|
||||
cog_pose_estimation::inference::MODEL_TYPICAL_CONFIDENCE
|
||||
);
|
||||
|
||||
// End-to-end: when the real model is loaded, the value it actually emits
|
||||
// must clear the default gate (i.e. the runtime would emit this frame).
|
||||
if engine.backend().starts_with("candle-") {
|
||||
let out = engine.infer(&SyntheticInput.as_window()).expect("infer");
|
||||
assert!(
|
||||
out.confidence >= cfg.min_confidence,
|
||||
"default install must emit: infer confidence {} < default min_confidence {}",
|
||||
out.confidence,
|
||||
cfg.min_confidence
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue