1. Define Multi-Output Targets for Restraint Responses
Why the Output Schema Must Be Fixed First
In a multi-output restraint-response model, the target tensor is not a single column but a structured contract: every output channel has a fixed position, a physical unit, a valid range, and a defined meaning. If this contract is left implicit, three failure modes appear almost immediately. First, column order drifts between the feature builder and the loss function, so gradients are applied to the wrong head. Second, unit mismatches (for example, force in N versus kN) silently rescale the loss and bias the model toward whichever channel has the largest numeric magnitude. Third, partially labeled samples either crash the loss or, worse, inject zeros that the model learns as real restraint values. This case defines the schema before any model code exists, so that later data pipelines, loss masking, and evaluation all reference one authoritative specification.
Anatomy of a Restraint Response Target
A restraint response is the measured reaction of a restraint system to an imposed load or displacement. In a typical vehicle-occupant or structural restraint dataset, each sample may carry several response channels recorded under different conditions. The schema must capture, per channel: a stable identifier, the physical quantity, the unit, the sign convention, the expected range, and whether the channel is always present or conditionally available.
Sign conventions deserve explicit attention because restraint responses are directional. Define a coordinate frame once — for example, +X forward, +Y left, +Z up — and state that tensile restraint force is positive while compressive is negative. Every downstream consumer (loss, metrics, plots) must inherit this convention. If a channel is later re-expressed in another frame, that transformation is documented as a separate, explicit step.
Defining the Output Dimensions
The output dimension is the number of channels the model predicts simultaneously. For this case we fix a concrete, reproducible schema of six channels covering the most common restraint response families. The table below is the authoritative contract; it is the artifact that model code, data loaders, and evaluation scripts will import or mirror.
| Index | Channel ID | Physical quantity | Unit | Sign convention | Expected range | Availability |
|---|---|---|---|---|---|---|
| 0 | belt_force_peak | Peak seatbelt restraint force | N | Tension positive | 0 – 12000 | Always |
| 1 | belt_force_avg | Mean seatbelt force over event | N | Tension positive | 0 – 8000 | Always |
| 2 | pretensioner_stroke | Pretensioner retraction stroke | mm | Retraction positive | 0 – 120 | Conditional |
| 3 | load_limiter_force | Load-limiter plateau force | N | Tension positive | 0 – 6000 | Conditional |
| 4 | chest_accel_peak | Peak chest acceleration | g | Forward positive | 0 – 80 | Always |
| 5 | head_excursion | Head forward excursion | mm | Forward positive | 0 – 400 | Conditional |
Two design decisions are embedded here. First, units are heterogeneous (N, mm, g), which means the loss cannot treat raw values as comparable. Second, three channels are conditional: they exist only for configurations that include the corresponding hardware (for example, a pretensioner is not fitted on every restraint variant). This is precisely why missing-label handling must be part of the schema, not an afterthought.
Missing-Label Handling as Part of the Contract
The schema distinguishes three states for every channel on every sample: present (a real measured value), absent (the hardware or condition does not apply), and unknown (applicable but not recorded). Conflating these is the most common source of corrupted training. The contract specifies:
- Absent and unknown labels are stored as
NaNin the target tensor, never as0. - A parallel boolean validity mask of shape
[num_samples, num_outputs]marks which entries are present. - The loss is computed only over valid entries; masked entries contribute zero loss and zero gradient.
- Evaluation reports per-channel coverage so that a channel with 5% availability is not silently averaged into an aggregate score.
Encoding absence as NaN rather than zero is deliberate: zero is a physically meaningful restraint force and would be indistinguishable from a genuine measurement. The mask carries the truth; the value carries only real data.
-1 or 0 as a missing sentinel. Both collide with valid physical values (a compressive force can be negative; a released belt reads zero). Use NaN plus an explicit mask.Normalization and the Modeling Contract
Because channels span different units and magnitudes, the schema also fixes a normalization policy: each channel is standardized using statistics computed only on its valid entries, and the mean/scale are stored alongside the schema. This keeps the loss balanced across heads and makes the mask semantics consistent — normalization statistics are never computed over NaN entries. The schema therefore has two parts: the channel definition table above, and a companion normalization record (per-channel mean and standard deviation) that is versioned with it.
Encoding the Schema in Code
The schema should exist as a single source of truth that other modules import. The following Python module is self-contained and runnable; it defines the channels, builds a target tensor and mask from raw records, and demonstrates the masked loss on a small example. It uses only the standard library plus NumPy.
import numpy as np
# Authoritative channel order — index position IS the output dimension.
CHANNELS = [
{"id": "belt_force_peak", "unit": "N", "sign": "tension+", "always": True},
{"id": "belt_force_avg", "unit": "N", "sign": "tension+", "always": True},
{"id": "pretensioner_stroke", "unit": "mm", "sign": "retraction+","always": False},
{"id": "load_limiter_force", "unit": "N", "sign": "tension+", "always": False},
{"id": "chest_accel_peak", "unit": "g", "sign": "forward+", "always": True},
{"id": "head_excursion", "unit": "mm", "sign": "forward+", "always": False},
]
NUM_OUTPUTS = len(CHANNELS)
INDEX = {c["id"]: i for i, c in enumerate(CHANNELS)}
def build_targets(records):
"""records: list of dicts mapping channel_id -> float or None.
Returns y [N, NUM_OUTPUTS] float32 with NaN for missing, and mask [N, NUM_OUTPUTS] bool."""
n = len(records)
y = np.full((n, NUM_OUTPUTS), np.nan, dtype=np.float32)
mask = np.zeros((n, NUM_OUTPUTS), dtype=bool)
for r, rec in enumerate(records):
for cid, val in rec.items():
j = INDEX[cid]
if val is not None:
y[r, j] = val
mask[r, j] = True
return y, mask
def masked_mse(y_true, y_pred, mask):
"""Mean squared error over valid entries only."""
diff = (y_pred - y_true) ** 2
diff = np.where(mask, diff, 0.0)
denom = mask.sum()
return float(diff.sum() / denom) if denom else 0.0
# --- Example (illustrative values, not measured data) ---
records = [
{"belt_force_peak": 4820.0, "belt_force_avg": 3110.0,
"chest_accel_peak": 42.5}, # no pretensioner/limiter/head
{"belt_force_peak": 5310.0, "belt_force_avg": 3405.0,
"pretensioner_stroke": 78.0, "load_limiter_force": 4200.0,
"chest_accel_peak": 47.1, "head_excursion": 210.0},
]
y, mask = build_targets(records)
print("y shape:", y.shape, "| mask shape:", mask.shape)
print("row0:", y[0], "| mask0:", mask[0].astype(int))
y_pred = np.where(mask, y + 100.0, 0.0) # pretend predictions offset by +100
print("masked MSE:", masked_mse(y, y_pred, mask))
print("coverage per channel:", mask.mean(axis=0).round(2))
Running this prints a target tensor of shape (2, 6) with NaN in the three conditional slots of the first sample, a mask of matching shape, and a masked MSE computed only over the nine valid entries. The coverage vector [1.0, 1.0, 0.5, 0.5, 1.0, 0.5] immediately reveals that half the samples lack the conditional channels — the exact signal that later informs loss masking and split design.
Verification Checklist
Before any model code is written, confirm the schema passes these checks:
- Dimension lock:
NUM_OUTPUTSequals the number of rows in the channel table, andINDEXorder matches the table order. - No sentinel leakage: missing entries are
NaN; a scan ofyfinds no-1or spurious0in conditional slots. - Mask consistency:
mask == ~np.isnan(y)holds elementwise. - Unit audit: each channel's unit and sign convention are recorded and match the source data dictionary.
- Normalization record: per-channel mean/scale exist and were computed on valid entries only.
If a downstream loss produces a gradient on a masked entry, or a coverage report shows a channel at 0%, the schema contract has been violated and should be corrected at the source rather than patched in the training loop.