cv-per-patient-slice-maxpool-aggregation
SkillDev toolsAggregate per-slice CNN predictions into a single patient-level injury score by mean-pooling across TTA copies first, then max-pooling across slices — the worst-slice wins, which matches the medical reality that one bad slice is enough to grade the patient
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the cv-per-patient-slice-maxpool-aggregation skill
What this skill tells your AI
The instructions your AI receives, as published by wenmin-wu/ds-skills in skills/cv/per-patient-slice-maxpool-aggregation/SKILL.md and read by ahel’s review.
Overview
For per-patient binary or grade tasks (injury yes/no, severity 0-2-3) where the label depends on whether the worst slice is bad, average-pooling across slices washes out the signal: a single high-confidence injury slice gets diluted by 50 healthy ones. The right reduction is max across slices — but only after you've already averaged across TTA / multi-model copies, otherwise you collapse the noise floor. The order matters: mean(TTA) → max(slices) is the right composition; max(everything) overstates probability and mean(everything) understates it.
Quick Start
import numpy as np
def aggregate_patient(slice_preds, n_tta):
"""
slice_preds: (n_tta * n_slices, n_outputs) raw model outputs
Returns: (n_outputs,) patient-level score
"""
n_slices = slice_preds.shape[0] // n_tta
pred = slice_preds.reshape(n_tta, n_slices, -1)
pred = pred.mean(axis=0) # 1. average TTA copies per slice
pred = pred.max(axis=0) # 2. worst slice across the volume
return pred
patient_scores = np.zeros((len(patient_ids), 11), dtype='float32')
for i, pid in enumerate(patient_ids):
pdf = test_df.query('patient_id == @pid')
raw = model.predict(build_dataset(pdf.image_path.tolist()))
raw = np.concatenate(raw, axis=-1) # multi-head → flat
patient_scores[i] = aggregate_patient(raw, n_tta=4)
Workflow
- Run inference on every slice with N TTA copies (flips, rotations) →
(N * S, C)predictions - Reshape to
(N, S, C)and average over the TTA axis to denoise per-slice scores - Take the max over the slice axis to elevate the worst slice's score to the patient level
- For multi-head outputs (binary + multi-class), apply this aggregation per head, then concatenate
- Optionally clip the final scores to
[0.01, 0.99]if the metric penalizes log-loss extremes
Key Decisions
- Mean-then-max, not max-then-mean: max-then-mean averages already-saturated 1.0s and produces an unstable score; mean-then-max averages noise first then takes a clean argmax.
- vs. attention pooling: attention is better in theory but needs training-time supervision; mean→max is zero-parameter and competitive on injury-detection tasks.
- Don't pool across organs: the max is per-organ-head; pooling across organs would let a torn liver mask a healthy bowel.
- Per-series, then per-patient: if a patient has multiple CT series, max within each series, then mean across series — different series are independent observations of the same patient.
- Stride sampling earlier: sampling every Nth slice before the model is fine; max-pooling is robust to undersampling as long as N < typical organ-span-in-slices.
References
Signals
- GitHub stars
- 60
- Forks
- 4
- Last commit
- Apr 2026
Advanced
- Catalog kind
- skill
- Gateway key
cv-per-patient-slice-maxpool-aggregation- Source
- github.com/wenmin-wu/ds-skills