Training, ensembling, and inference code for the BirdCLEF 2026 Kaggle competition. The project centers on a CNN/SED audio classifier and augments it with Perch-based branches, prototype scoring, site/time priors, and OOF-fitted calibration.
Competition: https://www.kaggle.com/competitions/birdclef-2026
The repository is split into an importable library under src/ and runnable
workflow entrypoints under scripts/.
Kaggle audio/metadata
-> src.data: labels, folds, waveform loading, mel spectrograms, augmentation
-> src.model: timm CNN encoder + frequency pooling + attentive SED head
-> src.training: fold-safe training, metrics, EMA, schedulers, thresholds
-> outputs/<timestamp>: checkpoints, logs, OOF arrays, ONNX exports
-> scripts.onnx: checkpoint ranking, ONNX export, model/head merging
-> src.perch + src.ensemble: Perch heads, native labels, prototypes, priors
-> scripts.inference: local/Kaggle-style submission generation
Main prediction branches:
cnn: log-mel windows passed through exported CNN/SED ONNX models.perch: frozen Perch v2 embeddings passed through trained BirdCLEF heads.perch_native: Perch's native label logits mapped onto the competition taxonomy.perch_proto: nearest-prototype scoring in Perch embedding space.site_prior: a site/date/time prior fitted from labeled soundscape windows.
The ensemble can use fixed blend weights or a per-class logistic stacker fitted from fold-safe soundscape OOF predictions.
src/
config.py Paths and default training/inference settings
model.py CNN/SED model, ASL loss, optional Perch distillation
data/ Audio IO, mel features, augmentations, datasets, labels
training/ Fold training loop, metrics, schedulers, EMA, thresholds
perch/ Perch ONNX extractor, embedding data helpers, head model
ensemble/ OOF generation, calibration, pseudo labels, priors
inference_utils.py Shared ONNX/session/windowing utilities
scripts/
training/ Train folds, train Perch heads, grid search
onnx/ Export, rank, and merge ONNX models
perch/ Extract embeddings and build prototype banks
ensemble/ Build OOF arrays, calibrators, pseudo labels
inference/ Submission and hybrid inference entrypoints
notebooks/ Exploration and Kaggle notebooks
tests/ Local smoke tests
Use python -m ... from the repository root so imports resolve consistently.
Most reusable logic belongs in src/; executable workflows belong in
scripts/.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtDownload the Kaggle competition data and unpack it here:
data/birdclef-2026/
train.csv
taxonomy.csv
sample_submission.csv
train_audio/
train_soundscapes/
train_soundscapes_labels.csv
test_soundscapes/
Perch workflows also need:
data/perch_v2.onnx
data/perch_labels.csv
src/perch/extractor.py can download perch_v2.onnx when used without an
explicit model path. The Perch labels file should match the official Perch label
asset used by the model.
Train the CNN/SED folds:
python -m scripts.training.train_all_folds \
--bs 128 --lr 3e-4 --wd 1e-3 \
--cpu-friendly \
--no-pseudo-labelsExport checkpoints and build a weighted CNN ensemble:
RUN=20260525_230046
python -m scripts.onnx.export --run "$RUN"
python -m scripts.onnx.rank_checkpoints \
--run "$RUN" --one-per-fold --top-k 5 \
--out-specs "outputs/$RUN/cnn_weighted_fold5.txt"
python -m scripts.onnx.merge_models \
--run "$RUN" \
--model-specs "outputs/$RUN/cnn_weighted_fold5.txt"Train Perch heads and build prototype banks:
python -m scripts.perch.extract_embeddings --perch-path data/perch_v2.onnx
for FOLD in 0 1 2 3 4; do
python -m scripts.training.train_perch_head --fold "$FOLD" --bs 512 --lr 3e-4 --wd 1e-3
done
python -m scripts.onnx.merge_perch_heads --dir outputs/perch_head
python -m scripts.perch.build_perch_prototypes \
--out outputs/perch_prototypes/perch_proto_all.npz \
--fold-prefix outputs/perch_prototypes/perch_protoCreate soundscape OOF arrays and fit a calibrator:
RUN=20260525_230046
python -m scripts.ensemble.soundscape_oof \
--out-prefix "outputs/$RUN/soundscape_full" \
--cnn-models-file "outputs/$RUN/cnn_weighted_fold5.txt" \
--perch-heads outputs/perch_head/perch_head_fold0.onnx outputs/perch_head/perch_head_fold1.onnx outputs/perch_head/perch_head_fold2.onnx outputs/perch_head/perch_head_fold3.onnx outputs/perch_head/perch_head_fold4.onnx \
--perch-native \
--perch-prototypes outputs/perch_prototypes/perch_proto_fold0.npz outputs/perch_prototypes/perch_proto_fold1.npz outputs/perch_prototypes/perch_proto_fold2.npz outputs/perch_prototypes/perch_proto_fold3.npz outputs/perch_prototypes/perch_proto_fold4.npz \
--site-time-prior
python -m scripts.ensemble.fit_ensemble_calibrator \
--targets "outputs/$RUN/soundscape_full_targets.npy" \
--branch "cnn:outputs/$RUN/soundscape_full_cnn_probs.npy" \
--branch "perch:outputs/$RUN/soundscape_full_perch_probs.npy" \
--input-space prob \
--fallback-weights cnn:0.55,perch:0.45 \
--output "outputs/$RUN/hybrid_calibrator.npz"Run hybrid inference:
RUN=20260525_230046
python -m scripts.inference.hybrid_perch_cnn_inference \
--cnn-models "outputs/$RUN/merged_model/fused_model.onnx" \
--perch-heads outputs/perch_head/perch_head_ensemble.onnx \
--perch-native \
--perch-prototypes outputs/perch_prototypes/perch_proto_all.npz \
--site-time-prior \
--calibrator "outputs/$RUN/hybrid_calibrator.npz" \
--out submission.csvpython -m tests.test_perch
python -m tests.test_train_cpuThe CPU training smoke test expects a small usable copy of the competition data
under data/birdclef-2026/.
- Outputs are timestamped under
outputs/; replaceRUN=...with your run directory name, or use--run latestwhere a command supports it. - Fold-aware OOF commands expect fold-specific filenames containing
fold0throughfold4. - Pseudo labels are mined with
scripts.ensemble.generate_pseudo_labelsand can be passed back into CNN training with--pseudo-labels. - Kaggle submission notebooks should stay self-contained; they cannot rely on
importing local
src/orscripts/modules unless those files are packaged as notebook inputs.