Downloads · 30 days
0
resoajoe/alarm-nano
alarm-nano is a audio classification model from resoajoe. Use it for the audio classification task on the model card, and read the license before you ship it in a product. The card lists the license as mit.
46,834 parameters. 187 KB of ONNX. Is a medical-equipment alarm sounding in this one-second window?
Downloads · 30 days
0
Access
Public
Updated Sep 3, 2026
Repo size
388 KB
Likes
0
Public
Click a slice to open those files.
.pt196 KB · 48%
From the Hugging Face model README
46,834 parameters. 187 KB of ONNX. Is a medical-equipment alarm sounding in this one-second window?
Runs on a CPU thread. The same 47K convolutional architecture as this project's vision models — a log-spectrogram is an image.
Domain measured / deployment domain tested: measured on synthesised IEC 60601-1-8 alarms against real ESC-50 negatives; deployment domain: no ward or bedside microphone recording tested. (Fifth line of the card standard, added 2026-09-02: a number is only as good as the domain it was measured in.)
What it is for: healthcare OPERATIONS. Alarm-fatigue auditing, unattended-alarm detection, "how long did that alarm sound before anyone came", acoustic load surveys in wards. It detects that a device is sounding, and nothing else.
It is NOT a diagnostic device, and must not be used as one. The distinction is the whole point:
Positives are synthesised, not recorded. IEC 60601-1-8 specifies medical alarm signals as melodic pulse bursts: 3 pulses (medium priority) or 5 (high), fundamental 150–1000 Hz, pulse duration 75–200 ms, 1–4 harmonics. All of that can be generated exactly, which makes the label free and perfectly accurate and the training set effectively infinite.
Negatives are real. ESC-50 — 2,000 recordings across 50 environmental sound classes — split by the dataset's own 5 folds (folds 1–3 train, 4–5 test) so clips from the same source recording never cross the boundary.
Positives are mixed into negatives at a known SNR, so every example carries an exact difficulty label.
Held-out ESC-50 folds: 0.853 accuracy (chance 0.500).
Detection by SNR — a single headline number would just report the SNR mix you sampled:
| SNR | −20 dB | −15 | −10 | −5 | 0 | +5 | +10 |
|---|---|---|---|---|---|---|---|
| detection rate | 0.419 | 0.583 | 0.770 | 0.911 | 0.964 | 0.993 | 0.977 |
False alarms across all 50 ESC-50 classes: 0.099.
Tested against noise it never trained on:
| background | false alarm | accuracy |
|---|---|---|
| white | 0.077 | 0.958 |
| pink | 0.067 | 0.942 |
| brown | 0.003 | 0.995 |
| HVAC-shaped | 0.000 | 0.988 |
| a real office room, never heard | 0.067 | 0.925 |
ESC-50 contains sounds that are alarms, just not medical ones. Their false-alarm rates matter far more than the 50-class average, so they are reported separately rather than averaged away:
| class | false alarm |
|---|---|
| car_horn | 0.346 |
| church_bells | 0.227 |
| siren | 0.190 |
| clock_tick | 0.118 |
| can_opening | 0.050 |
| clock_alarm | 0.040 |
A harmonic pulse train is a harmonic pulse train. This model will fire on car horns roughly a third of the time. In any deployment where those sounds occur, that is the binding limitation.
Other limits:
The first version used one room's noise floor as its entire negative class. It scored 0.012 false alarms in-domain and 0.987 on white noise — it had learned "unexpected high-frequency energy", which is what an alarm looks like against that particular quiet floor.
v1 beat v2 on every in-domain metric: 0.012 false alarms vs 0.099, and 1.000 detection at −5 dB vs 0.911. v1 was also useless. A narrow model outscores a robust one on exactly the numbers you would put in a README, which is why this card leads with out-of-distribution probes.
import numpy as np, onnxruntime as ort, cv2
from scipy import signal
SR = 16000
sess = ort.InferenceSession("alarm.onnx", providers=["CPUExecutionProvider"])
def spec(x): # x: 1 second of mono float audio at 16 kHz
f, t, S = signal.stft(x, SR, nperseg=256, noverlap=192)
P = np.log10(np.abs(S) + 1e-8)
P = (P - P.mean()) / (P.std() + 1e-8) # per-window normalise: removes absolute level so
return cv2.resize(P.astype(np.float32), # the model reads STRUCTURE, not loudness
(64, 64), interpolation=cv2.INTER_AREA)
x = ... # np.float32, shape (16000,), range about [-1, 1]
logits = sess.run(None, {"spectrogram": spec(x)[None, None]})[0][0]
print("alarm" if logits.argmax() == 1 else "no alarm")
The per-window normalisation is not optional. Training normalised every window to zero mean and unit variance, so the model is deliberately blind to absolute level. Skip it and the input distribution no longer matches training.
ONNX vs PyTorch, identical weights, both CPU, 256 inputs:
Relative, not absolute — an absolute 1e-3 check flagged a bit-accurate export of a sibling model as broken.
train_alarm.py (synthesis + the one-room v1) and alarm_esc50.py (v2) are both included.
Measured on a Jetson AGX Orin. ONNX Runtime sizes its intra-op thread pool to the CPU core count, and those workers spin-wait between inferences. Running two 47K-parameter models this way left ~18 threads busy-waiting at ~10.6% of a core each — about 1.9 cores burned continuously to run inferences that take 0.28 ms. A model this small cannot use intra-op parallelism at all.
so = ort.SessionOptions()
so.intra_op_num_threads = 1
so.inter_op_num_threads = 1
so.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL
so.add_session_config_entry("session.intra_op.allow_spinning", "0")
sess = ort.InferenceSession("alarm.onnx", sess_options=so, providers=["CPUExecutionProvider"])
Measured effect on the same workload: idle CPU 192% → 16.5% of one core, active 231% → 88%, thread count 45 → 22, throughput unchanged. On edge hardware this is the difference between "runs alongside everything else" and "saturates the machine".
Every margin quoted here is against a stated baseline, because a margin without one is not a measurement. The baseline is the best single-threshold classifier over ten cheap statistics, fitted optimistically:
mean · std · lapvar · hf (high-frequency energy ratio) · grad (Sobel magnitude) ·
entropy · centre_edge · radial_slope · row_fft_peak · col_fft_peak
The last four are spatially aware, added after an earlier six-statistic baseline — all global aggregates — was found to systematically overstate model value on spatially structured tasks. A baseline that cannot see where anything is loses to a CNN by default. On one test task that flaw inflated an apparent margin from +0.060 to +0.261.
Two questions are asked with it, and they disagree:
Where this card quotes a single scalar figure without qualification, it is the in-sample one.
Positives are synthesised from a published standard. Negatives are ESC-50, a public dataset of environmental recordings. No patients, no clinical recordings, and no personal data are involved anywhere in this work. No audio of identifiable people was used in training.