ganesh333 commited on
Commit
575e16b
·
verified ·
1 Parent(s): 32cf0f1

Upload folder using huggingface_hub

Browse files
Files changed (21) hide show
  1. LICENSE +11 -0
  2. README.md +127 -0
  3. config.json +14 -0
  4. data/Cicadidae.csv +189 -0
  5. data/Orthoptera.csv +144 -0
  6. data/README.txt +114 -0
  7. docs/deployment.md +16 -0
  8. evaluate.py +21 -0
  9. history.json +302 -0
  10. labels.json +34 -0
  11. metrics.json +551 -0
  12. model.h5 +3 -0
  13. model.pt +3 -0
  14. predict.py +22 -0
  15. requirements.txt +7 -0
  16. src/__init__.py +0 -0
  17. src/audio.py +104 -0
  18. src/inference.py +70 -0
  19. src/model.py +20 -0
  20. tests/test_model.py +37 -0
  21. train.py +151 -0
LICENSE ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This repository code is provided under the MIT License.
2
+
3
+ Copyright (c) 2026 Kernel MVP contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
10
+
11
+ Note: dataset audio is separately licensed/attributed as described in data/README.txt and is not relicensed by this code license.
README.md ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: cc-by-4.0
3
+ library_name: pytorch
4
+ pipeline_tag: audio-classification
5
+ datasets:
6
+ - InsectSet32
7
+ metrics:
8
+ - accuracy
9
+ - f1
10
+ tags:
11
+ - audio-classification
12
+ - insect
13
+ - bioacoustics
14
+ - experimental
15
+ - local-inference
16
+ ---
17
+
18
+ # Kernel Insect Sound Classifier (InsectCNN `full_model`, experimental 0.1.0)
19
+
20
+ A compact CNN (159k parameters) that classifies **32 sound-producing insect species** (9 Orthoptera, 23 Cicadidae) from audio. It was trained on [InsectSet32](https://zenodo.org/records/7072196).
21
+
22
+ > **This is not a grain-pest detector.** Do not use it to certify grain as clean or safe, to accept or reject lots, or to trigger treatment. It is a research baseline trained on a small dataset.
23
+
24
+ ## Model details
25
+
26
+ | | |
27
+ |---|---|
28
+ | Architecture | `InsectCNN`: 3 conv blocks (16/32/64 ch, BatchNorm, ReLU) + dense head (128) with dropout 0.25 (`src/model.py`) |
29
+ | Input | Mono audio → resampled to 16 kHz → 2 s windows, peak-normalised → 64-bin log-mel (n_fft 512, hop 160), per-record standardised (`src/audio.py`) |
30
+ | Inference | Softmax averaged over 5 evenly spaced 2 s windows (`eval_crops: 5` in `config.json`) |
31
+ | Output | 32 species scores, plus a hierarchical species → genus → group answer |
32
+ | Base model | None. Trained from scratch (no pretrained weights, not a fine-tune) |
33
+ | Framework | PyTorch. Weights stored as a state dict |
34
+
35
+ ### Files
36
+
37
+ - `model.h5`: the PyTorch state dict in a project-specific HDF5 layout (`kernel-pytorch-state-dict-hdf5-v1`). **It is not a Keras model.** `src.inference.load_model` reads it.
38
+ - `model.pt`: the same weights as a PyTorch state dict. Load it with `torch.load(..., weights_only=True)`.
39
+ - `config.json`, `labels.json`: architecture, preprocessing settings and class-index → species mapping.
40
+ - `metrics.json`, `history.json`: test metrics (with per-class report) and the training curve.
41
+ - `src/`, `predict.py`: model definition, audio frontend, loader and CLI.
42
+ - `train.py`, `evaluate.py`, `data/*.csv`: reproduction scripts and the official split annotations (the audio itself is not included).
43
+
44
+ ## Usage
45
+
46
+ This is a custom PyTorch model. It does **not** load with `transformers`, and the hosted inference widget and Inference API don't support it. Run it locally:
47
+
48
+ ```bash
49
+ pip install huggingface_hub
50
+ python -c "from huggingface_hub import snapshot_download; print(snapshot_download('ganesh333/kernel-insect-classifier', local_dir='kernel-insect-classifier'))"
51
+ cd kernel-insect-classifier
52
+ pip install -r requirements.txt
53
+ python predict.py path/to/recording.wav
54
+ ```
55
+
56
+ WAV works out of the box. FLAC, OGG and MP3 need `soundfile`, which `requirements.txt` includes. Inference is fully local; no audio is sent anywhere.
57
+
58
+ Python API:
59
+
60
+ ```python
61
+ from src.inference import predict_wav
62
+ r = predict_wav("recording.wav", model_dir=".")
63
+ print(r["identification"]) # {'level': 'species'|'genus'|'group'|'unknown', 'name': ..., 'confidence': ...}
64
+ print(r["predicted_species"], r["confidence"]) # raw top-1
65
+ print(r["top_k"], r["quality_pass"], r["quality_reasons"])
66
+ ```
67
+
68
+ **Hierarchical identification.** The model reports the most specific level whose summed probability is at least 0.6: species, then genus, then group (cicadas vs crickets/grasshoppers). Otherwise it reports `unknown`. The threshold was chosen on the validation split.
69
+
70
+ ## Training
71
+
72
+ Data: InsectSet32. Using the dataset's official `train`/`validation`/`test` partitions, 331 recordings were loaded (206 / 51 / 74).
73
+
74
+ ```bash
75
+ # place Cicadidae.csv, Orthoptera.csv, Cicadidae.zip, Orthoptera.zip in data/
76
+ python train.py --epochs 60 --random-crop --eval-crops 5 --output .
77
+ ```
78
+
79
+ - AdamW (lr 1e-3, weight decay 1e-4), batch size 16, class-weighted cross entropy (weights from train only), seed 42.
80
+ - Each epoch trains on a fresh random 2 s window per file.
81
+ - The checkpoint is chosen by best validation macro-F1 (0.548). The test split was scored once, after selection.
82
+ - About 4 minutes on a CPU.
83
+
84
+ ## Evaluation
85
+
86
+ Official test split (n = 74 recordings). 95% CIs from a file-level bootstrap:
87
+
88
+ | Metric | Value |
89
+ |---|---|
90
+ | Accuracy | 0.486 [0.38, 0.59] |
91
+ | Macro-F1 | 0.400 [0.29, 0.47] |
92
+ | Weighted-F1 | 0.474 |
93
+ | Session-clean accuracy / macro-F1 (n = 48) | 0.458 / 0.343 |
94
+
95
+ "Session-clean" counts only test files whose source recording session never appears in train or validation. See *Data leakage* below.
96
+
97
+ Accuracy among test files whose score at that level is at least 0.6:
98
+
99
+ | Level | Share of test files ≥ 0.6 | Accuracy on those (val / test) |
100
+ |---|---|---|
101
+ | Species | 43% | 77% / 72% |
102
+ | Genus | 77% | 90% / 95% |
103
+ | Group | 96% | 98% / 99% |
104
+
105
+ For comparison, the earlier center-crop baseline of the same architecture scored 0.392 accuracy and 0.329 macro-F1 on the same split. The CIs overlap, so with only 74 test files these differences are not conclusive.
106
+
107
+ ## Limitations
108
+
109
+ - **Small, imbalanced data.** Species have 4–22 recordings each, and `Roeselianaroeselii` has no test files. All scores are uncertain.
110
+ - **Data leakage.** The official split groups by file, not by source recording: 26 of the 74 test files share a source session with train or validation. Official-split scores are therefore optimistic; the session-clean numbers are the more honest estimate.
111
+ - **Label noise.** Five `MHV 318 P.capensis` source files are labelled `Platypleuracfcatenata`. They were kept as published.
112
+ - **Closed set.** Every input is mapped onto the 32 trained species. Silence, pure tones and white noise can receive high-confidence species predictions. Use `quality_reasons` and treat `genus`/`unknown` answers as exactly that.
113
+ - **Noise sensitivity.** Added background noise (SNR 0–20 dB) often changes the prediction.
114
+ - **Not calibrated.** The scores are not probabilities of grain infestation. Recording conditions differ from grain sacks and warehouses.
115
+ - **Not mobile-ready.** This is not a validated Android LiteRT/TFLite model. See `docs/deployment.md` before any on-device port.
116
+ - `model.pt` is a pickle-based file and may be flagged by the Hub's pickle scanner. It contains only tensors. Prefer `model.h5`, or load with `weights_only=True`.
117
+
118
+ ## Dataset attribution and citation
119
+
120
+ Trained on **InsectSet32** by Marius Faiß, Baudewijn Odé and Ed Baker (CC BY 4.0, [Zenodo 7072196](https://zenodo.org/records/7072196)). Orthoptera recordings: Baudewijn Odé. Cicadidae recordings: the Global Cicada Sound Collection on Bioacoustica ([doi.org/10.1093/database/bav054](https://doi.org/10.1093/database/bav054)). Copyright in individual recordings belongs to the recordists. See `data/README.txt`.
121
+
122
+ For academic use, please cite the dataset paper: Faiß, M. & Stowell, D. *Adaptive representations of sound for automatic insect recognition*, PLOS Computational Biology (2023), [doi.org/10.1371/journal.pcbi.1011541](https://doi.org/10.1371/journal.pcbi.1011541).
123
+
124
+ ## License
125
+
126
+ - Model weights and this card: **CC BY 4.0**, matching the training data's license. Keep the attribution above when you redistribute.
127
+ - Code (`src/`, `*.py`): **MIT**. See `LICENSE`.
config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "kernel-insect-cnn",
3
+ "architecture": "InsectCNN",
4
+ "num_classes": 32,
5
+ "sample_rate": 16000,
6
+ "clip_seconds": 2.0,
7
+ "feature": "64-bin log-mel spectrogram",
8
+ "eval_crops": 5,
9
+ "model_version": "0.1.0-experimental",
10
+ "framework": "PyTorch",
11
+ "hdf5_format": "kernel-pytorch-state-dict-hdf5-v1",
12
+ "task": "insect species classification",
13
+ "experimental": true
14
+ }
data/Cicadidae.csv ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,species,class_ID,data_set,original_file_name
2
+ Myopsaltamelanobasis_Myopsalta_melanobasis_Brigalow.wav,Myopsaltamelanobasis,9,test,Myopsalta_melanobasis_Brigalow.wav
3
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%236b.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%236b.wav
4
+ Platypleurasp13_MHV%201016%20P.sp13%20nr%20Hankey.wav,Platypleurasp13,27,validation,MHV%201016%20P.sp13%20nr%20Hankey.wav
5
+ Platypleuraintercapedinis_MHV%20952%20P.cf_.brunea%20Mtunzini%20%235.wav,Platypleuraintercapedinis,21,train,MHV%20952%20P.cf_.brunea%20Mtunzini%20%235.wav
6
+ Myopsaltaxerograsidia_Myopsalta_xerograsidia_Laura_River.wav,Myopsaltaxerograsidia,10,train,Myopsalta_xerograsidia_Laura_River.wav
7
+ Platypleuracfcatenata_MHV%20608%20P.catenata%20Robinson%20Pass.wav,Platypleuracfcatenata,15,test,MHV%20608%20P.catenata%20Robinson%20Pass.wav
8
+ Brevisianabrevis_MHV%20959%20B.brevis%20Morningside%20Ranch%20%232.wav,Brevisianabrevis,1,test,MHV%20959%20B.brevis%20Morningside%20Ranch%20%232.wav
9
+ Myopsaltaxerograsidia_Myopsalta_xerograsidia_Musselbrook.wav,Myopsaltaxerograsidia,10,test,Myopsalta_xerograsidia_Musselbrook.wav
10
+ Brevisianabrevis_MHV%20959%20B.brevis%20Morningside%20Ranch%20%233.wav,Brevisianabrevis,1,train,MHV%20959%20B.brevis%20Morningside%20Ranch%20%233.wav
11
+ Platypleuradeusta_MHV%20993%20P.duesta%20nr%20Piet%20Retief%20%232%20%28maybe%29.wav,Platypleuradeusta,17,train,MHV%20993%20P.duesta%20nr%20Piet%20Retief%20%232%20%28maybe%29.wav
12
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%236a.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%236a.wav
13
+ Platypleuraplumosa_MHV%201021%20P.plumosa%20nr%20Addo%20N.P..wav,Platypleuraplumosa,22,train,MHV%201021%20P.plumosa%20nr%20Addo%20N.P..wav
14
+ Platypleurasp10_MHV%20866%20P.sp10%20Van%20Ryns%20Pass%20%233.wav,Platypleurasp10,24,train,MHV%20866%20P.sp10%20Van%20Ryns%20Pass%20%233.wav
15
+ Platypleurachalybaea_MHV%20914%20P.chalybaea%20Kenton%20%235.wav,Platypleurachalybaea,16,test,MHV%20914%20P.chalybaea%20Kenton%20%235.wav
16
+ Myopsaltaleona_Myopsalta_leona_Kindon.wav,Myopsaltaleona,6,test,Myopsalta_leona_Kindon.wav
17
+ Brevisianabrevis_MHV%20959%20B.brevis%20Morningside%20Ranch%20%231.wav,Brevisianabrevis,1,validation,MHV%20959%20B.brevis%20Morningside%20Ranch%20%231.wav
18
+ Azanicadazuluensis_MHV%20937%20A.zuluensis%20Trafalgar.wav,Azanicadazuluensis,0,train,MHV%20937%20A.zuluensis%20Trafalgar.wav
19
+ Platypleurasp04_MHV%20597%20P.sp4%20Meiringspoort%20Pass%20%232_0.wav,Platypleurasp04,23,train,MHV%20597%20P.sp4%20Meiringspoort%20Pass%20%232_0.wav
20
+ Platypleurachalybaea_MHV%20914%20P.chalybaea%20Kenton%20%234.wav,Platypleurachalybaea,16,test,MHV%20914%20P.chalybaea%20Kenton%20%234.wav
21
+ Platypleuraintercapedinis_MHV%20945%20P.cf_.brunea%20Ingwe%20Lodge.wav,Platypleuraintercapedinis,21,validation,MHV%20945%20P.cf_.brunea%20Ingwe%20Lodge.wav
22
+ Platypleurasp12cfhirtipennis_MHV%20840%20P.sp12%20nr%20Laingsburg.wav,Platypleurasp12cfhirtipennis,26,train,MHV%20840%20P.sp12%20nr%20Laingsburg.wav
23
+ Platypleurasp10_MHV%20866%20P.sp10%20Van%20Ryns%20Pass%20%232.wav,Platypleurasp10,24,test,MHV%20866%20P.sp10%20Van%20Ryns%20Pass%20%232.wav
24
+ Platypleuracfcatenata_MHV%20286%20P.catenata%20Grabow%20%231.wav,Platypleuracfcatenata,15,train,MHV%20286%20P.catenata%20Grabow%20%231.wav
25
+ Platypleurasp11cfhirtipennis_MHV%20843%20P.sp11%20nr%20Karookloof%20%232.wav,Platypleurasp11cfhirtipennis,25,train,MHV%20843%20P.sp11%20nr%20Karookloof%20%232.wav
26
+ Platypleuraplumosa_MHV%201037%20P.plumosa%20Paniel%20Farm.wav,Platypleuraplumosa,22,train,MHV%201037%20P.plumosa%20Paniel%20Farm.wav
27
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%231.wav,Platypleurasp10,24,validation,MHV%20313%20P.sp10%20Die%20Koo%20%231.wav
28
+ Brevisianabrevis_MHV%20959%20B.brevis%20Morningside%20Ranch%20%234.wav,Brevisianabrevis,1,train,MHV%20959%20B.brevis%20Morningside%20Ranch%20%234.wav
29
+ Platypleuradeusta_MHV%20993%20P.duesta%20nr%20Piet%20Retief%20%231.wav,Platypleuradeusta,17,train,MHV%20993%20P.duesta%20nr%20Piet%20Retief%20%231.wav
30
+ Platypleurachalybaea_MHV%20914%20P.chalybaea%20Kenton%20%231.wav,Platypleurachalybaea,16,train,MHV%20914%20P.chalybaea%20Kenton%20%231.wav
31
+ Platypleuracfcatenata_MHV%20283%20P.catenata%20Sir%20Lowry%27s%20Pass%20%232.wav,Platypleuracfcatenata,15,train,MHV%20283%20P.catenata%20Sir%20Lowry%27s%20Pass%20%232.wav
32
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%237a.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%237a.wav
33
+ Platypleurasp12cfhirtipennis_MHV%20842%20P.sp12%20nr%20Maitjiesfontein.wav,Platypleurasp12cfhirtipennis,26,train,MHV%20842%20P.sp12%20nr%20Maitjiesfontein.wav
34
+ Platypleuracfcatenata_MHV%20286%20P.catenata%20Grabow%20%232.wav,Platypleuracfcatenata,15,validation,MHV%20286%20P.catenata%20Grabow%20%232.wav
35
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_Ceratodus.wav,Myopsaltamackinlayi,8,test,Myopsalta_mackinlayi_Ceratodus.wav
36
+ Platypleurasp11cfhirtipennis_MHV%20843%20P.sp11%20nr%20Karookloof%20%231.wav,Platypleurasp11cfhirtipennis,25,validation,MHV%20843%20P.sp11%20nr%20Karookloof%20%231.wav
37
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_6.1km_E_Kaimkillenbun.wav,Myopsaltamackinlayi,8,train,Myopsalta_mackinlayi_6.1km_E_Kaimkillenbun.wav
38
+ Kikihiamuta_08_Kikihia_muta_Rose_Saddle.wav,Kikihiamuta,5,train,08_Kikihia_muta_Rose_Saddle.wav
39
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%232.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%232.wav
40
+ Myopsaltalongicauda_Myopsalta_longicauda_Myall_Park.wav,Myopsaltalongicauda,7,test,Myopsalta_longicauda_Myall_Park.wav
41
+ Platypleurahaglundi_MHV%20975%20P.haglundi%20Morningside%20Ranch.wav,Platypleurahaglundi,19,test,MHV%20975%20P.haglundi%20Morningside%20Ranch.wav
42
+ Pycnasemiclara_MHV%20919%20P.semiclara%20nr%20Tzaneen.wav,Pycnasemiclara,29,train,MHV%20919%20P.semiclara%20nr%20Tzaneen.wav
43
+ Platypleurachalybaea_MHV%20914%20P.chalybaea%20Kenton%20%233.wav,Platypleurachalybaea,16,train,MHV%20914%20P.chalybaea%20Kenton%20%233.wav
44
+ Platypleurachalybaea_MHV%20914%20P.chalybaea%20Kenton%20%232.wav,Platypleurachalybaea,16,validation,MHV%20914%20P.chalybaea%20Kenton%20%232.wav
45
+ Kikihiamuta_10_Kikihia_muta_Fox_River.wav,Kikihiamuta,5,train,10_Kikihia_muta_Fox_River.wav
46
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%233.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%233.wav
47
+ Platypleuracfcatenata_MHV%20283%20P.catenata%20Sir%20Lowry%27s%20Pass%20%231.wav,Platypleuracfcatenata,15,train,MHV%20283%20P.catenata%20Sir%20Lowry%27s%20Pass%20%231.wav
48
+ Platypleurasp10_MHV%20866%20P.sp10%20Van%20Ryns%20Pass%20%234.wav,Platypleurasp10,24,train,MHV%20866%20P.sp10%20Van%20Ryns%20Pass%20%234.wav
49
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%237b.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%237b.wav
50
+ Platypleuracfcatenata_MHV%20286%20P.catenata%20Grabow%20%233.wav,Platypleuracfcatenata,15,validation,MHV%20286%20P.catenata%20Grabow%20%233.wav
51
+ Pycnasemiclara_MHV%20321%20P.plumosa%20Ghwarriepoort%20River%20%231_0.wav,Pycnasemiclara,29,validation,MHV%20321%20P.plumosa%20Ghwarriepoort%20River%20%231_0.wav
52
+ Myopsaltalongicauda_Myopsalta_longicauda_Possum_Park.wav,Myopsaltalongicauda,7,train,Myopsalta_longicauda_Possum_Park.wav
53
+ Platypleuraplumosa_MHV%201488%20P.plumosa%20Nanaga%20Rec%20%233.wav,Platypleuraplumosa,22,train,MHV%201488%20P.plumosa%20Nanaga%20Rec%20%233.wav
54
+ Platypleurasp04_MHV%20596%20P.sp4%2010km%20S%20De%20Rust%20%232.wav,Platypleurasp04,23,train,MHV%20596%20P.sp4%2010km%20S%20De%20Rust%20%232.wav
55
+ Pycnasemiclara_MHV%20930%20P.semiclara%20R614%20nr%20Wartberg.wav,Pycnasemiclara,29,test,MHV%20930%20P.semiclara%20R614%20nr%20Wartberg.wav
56
+ Platypleurasp12cfhirtipennis_MHV%20838%20P.sp12%20Swartberg%20Pass.wav,Platypleurasp12cfhirtipennis,26,validation,MHV%20838%20P.sp12%20Swartberg%20Pass.wav
57
+ Myopsaltaxerograsidia_Myopsalta_xerograsidia_8km_W_Alice_Springs.wav,Myopsaltaxerograsidia,10,train,Myopsalta_xerograsidia_8km_W_Alice_Springs.wav
58
+ Platypleurasp10_MHV%20600%20P.sp10%2045km%20N%20Montague.wav,Platypleurasp10,24,train,MHV%20600%20P.sp10%2045km%20N%20Montague.wav
59
+ Platypleurachalybaea_MHV%201022%20P.chalybaea%20Zuurberg%20Pass%20%232.wav,Platypleurachalybaea,16,train,MHV%201022%20P.chalybaea%20Zuurberg%20Pass%20%232.wav
60
+ Platypleurasp13_MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%231.wav,Platypleurasp13,27,train,MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%231.wav
61
+ Platypleuraplumosa_MHV%201488%20P.plumosa%20Nanaga%20Rec%20%232.wav,Platypleuraplumosa,22,train,MHV%201488%20P.plumosa%20Nanaga%20Rec%20%232.wav
62
+ Pycnasemiclara_MHV%20922%20P.semiclara%20Woodcliffe%20Farm.wav,Pycnasemiclara,29,test,MHV%20922%20P.semiclara%20Woodcliffe%20Farm.wav
63
+ Platypleurasp13_MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%233.wav,Platypleurasp13,27,train,MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%233.wav
64
+ Platypleurasp04_MHV%20596%20P.sp4%2010km%20S%20De%20Rust%20%231.wav,Platypleurasp04,23,validation,MHV%20596%20P.sp4%2010km%20S%20De%20Rust%20%231.wav
65
+ Myopsaltaxerograsidia_Myopsalta_xerograsidia_Herberton.wav,Myopsaltaxerograsidia,10,test,Myopsalta_xerograsidia_Herberton.wav
66
+ Platypleurasp13_MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%232.wav,Platypleurasp13,27,validation,MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%232.wav
67
+ Platypleurachalybaea_MHV%201022%20P.chalybaea%20Zuurberg%20Pass%20%231.wav,Platypleurachalybaea,16,train,MHV%201022%20P.chalybaea%20Zuurberg%20Pass%20%231.wav
68
+ Platypleuraplumosa_MHV%201488%20P.plumosa%20Nanaga%20Rec%20%231.wav,Platypleuraplumosa,22,train,MHV%201488%20P.plumosa%20Nanaga%20Rec%20%231.wav
69
+ Platypleuradivisa_MHV%20989%20P.maytenophila%20nr%20Jozini.wav,Platypleuradivisa,18,train,MHV%20989%20P.maytenophila%20nr%20Jozini.wav
70
+ Kikihiamuta_06_Kikihia_muta_totoptype_Queen_Charlotte_Sound.wav,Kikihiamuta,5,test,06_Kikihia_muta_totoptype_Queen_Charlotte_Sound.wav
71
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_Girraween.wav,Myopsaltamackinlayi,8,train,Myopsalta_mackinlayi_Girraween.wav
72
+ Platypleurahaglundi_MHV%20978%20P.haglundi%20nr%20Qudeni%20Forest.wav,Platypleurahaglundi,19,train,MHV%20978%20P.haglundi%20nr%20Qudeni%20Forest.wav
73
+ Platypleuracfcatenata_MHV%20288%20P.catenata%20Houwhoek%20Pass%20%232.wav,Platypleuracfcatenata,15,train,MHV%20288%20P.catenata%20Houwhoek%20Pass%20%232.wav
74
+ Platypleuracfcatenata_MHV%20288%20P.catenata%20Houwhoek%20Pass%20%233.wav,Platypleuracfcatenata,15,train,MHV%20288%20P.catenata%20Houwhoek%20Pass%20%233.wav
75
+ Platypleuraplumosa_MHV%20609%20P.plumosa%204km%20W%20De%20Rust%20%231.wav,Platypleuraplumosa,22,train,MHV%20609%20P.plumosa%204km%20W%20De%20Rust%20%231.wav
76
+ Myopsaltaleona_Myopsalta_leona_Eidsvold.wav,Myopsaltaleona,6,train,Myopsalta_leona_Eidsvold.wav
77
+ Platypleuraplumosa_MHV%201488%20P.plumosa%20Nanaga%20Rec%20%234.wav,Platypleuraplumosa,22,train,MHV%201488%20P.plumosa%20Nanaga%20Rec%20%234.wav
78
+ Platypleuracfcatenata_MHV%20900%20P.catenata%20Tradouws%20Pass%20%28maybe%20901%29.wav,Platypleuracfcatenata,15,train,MHV%20900%20P.catenata%20Tradouws%20Pass%20%28maybe%20901%29.wav
79
+ Azanicadazuluensis_MHV%20936%20A.zuluensis%20Mission%20Rock%20%232.wav,Azanicadazuluensis,0,train,MHV%20936%20A.zuluensis%20Mission%20Rock%20%232.wav
80
+ Platypleuradivisa_MHV%20954%20P.divisa%20nr%20Harrismith%20%28same%20as%20MHV%20990%29.wav,Platypleuradivisa,18,test,MHV%20954%20P.divisa%20nr%20Harrismith%20%28same%20as%20MHV%20990%29.wav
81
+ Platypleurasp12cfhirtipennis_MHV%20603%20P.sp12%205km%20W%20Prince%20Albert.wav,Platypleurasp12cfhirtipennis,26,test,MHV%20603%20P.sp12%205km%20W%20Prince%20Albert.wav
82
+ Myopsaltaleona_Myopsalta_leona_Cravens_Peak.wav,Myopsaltaleona,6,test,Myopsalta_leona_Cravens_Peak.wav
83
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_Coolbaggie_dusk.wav,Myopsaltamackinlayi,8,train,Myopsalta_mackinlayi_Coolbaggie_dusk.wav
84
+ Kikihiamuta_07_Kikihia_muta_Nelson.wav,Kikihiamuta,5,test,07_Kikihia_muta_Nelson.wav
85
+ Platypleuracfcatenata_MHV%20288%20P.catenata%20Houwhoek%20Pass%20%231.wav,Platypleuracfcatenata,15,validation,MHV%20288%20P.catenata%20Houwhoek%20Pass%20%231.wav
86
+ Platypleuraplumosa_MHV%20609%20P.plumosa%204km%20W%20De%20Rust%20%232.wav,Platypleuraplumosa,22,test,MHV%20609%20P.plumosa%204km%20W%20De%20Rust%20%232.wav
87
+ Platypleurasp13_MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%234.wav,Platypleurasp13,27,test,MHV%201486%20P.sp%2013%20nr%20Gamtoos%20River%20Bridge%20%234.wav
88
+ Kikihiamuta_05_Kikihia_muta_North_Island_form.wav,Kikihiamuta,5,validation,05_Kikihia_muta_North_Island_form.wav
89
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_Clandulla.wav,Myopsaltamackinlayi,8,test,Myopsalta_mackinlayi_Clandulla.wav
90
+ Azanicadazuluensis_MHV%20936%20A.zuluensis%20Mission%20Rock%20%231.wav,Azanicadazuluensis,0,test,MHV%20936%20A.zuluensis%20Mission%20Rock%20%231.wav
91
+ Kikihiamuta_09_Kikihia_muta_Westland_0.wav,Kikihiamuta,5,train,09_Kikihia_muta_Westland_0.wav
92
+ Pycnasemiclara_MHV%20925%20P.semiclara%20nr%20Grasskop%20%231.wav,Pycnasemiclara,29,train,MHV%20925%20P.semiclara%20nr%20Grasskop%20%231.wav
93
+ Platypleuradeusta_MHV%20990%20P.divisa%20nr%20Harrismith%20%28same%20as%20MHV%20990%29.wav,Platypleuradeusta,17,test,MHV%20990%20P.divisa%20nr%20Harrismith%20%28same%20as%20MHV%20990%29.wav
94
+ Myopsaltaxerograsidia_Myopsalta_xerograsidia_Cravens_Peak.wav,Myopsaltaxerograsidia,10,validation,Myopsalta_xerograsidia_Cravens_Peak.wav
95
+ Platypleuradivisa_MHV%20982%20P.divisa%20nr%20Barberton%20%28check%20ID%29.wav,Platypleuradivisa,18,train,MHV%20982%20P.divisa%20nr%20Barberton%20%28check%20ID%29.wav
96
+ Platypleuracapensis_MHV%20274%20P.capensis%20nr%20Arniston%20%232.wav,Platypleuracapensis,14,train,MHV%20274%20P.capensis%20nr%20Arniston%20%232.wav
97
+ Brevisianabrevis_MHV%201408%20Brevisiana%20brevis%20Dedza.wav,Brevisianabrevis,1,train,MHV%201408%20Brevisiana%20brevis%20Dedza.wav
98
+ Pycnasemiclara_MHV%20916%20P.semiclara%20nr%20Amajuba.wav,Pycnasemiclara,29,train,MHV%20916%20P.semiclara%20nr%20Amajuba.wav
99
+ Platypleuraplumosa_MHV%20329%20P.plumosa%20Steydtlerville%20%235a.wav,Platypleuraplumosa,22,train,MHV%20329%20P.plumosa%20Steydtlerville%20%235a.wav
100
+ Platypleurasp10_MHV%20891%20P.sp10%20Piekenierskloop%20Pass.wav,Platypleurasp10,24,train,MHV%20891%20P.sp10%20Piekenierskloop%20Pass.wav
101
+ Pycnasemiclara_MHV%20925%20P.semiclara%20nr%20Grasskop%20%232.wav,Pycnasemiclara,29,train,MHV%20925%20P.semiclara%20nr%20Grasskop%20%232.wav
102
+ Platypleuracfcatenata_MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%235.wav,Platypleuracfcatenata,15,train,MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%235.wav
103
+ Platypleuraplumosa_MHV%20501%20P.plumosa%20Fort%20Brown.wav,Platypleuraplumosa,22,test,MHV%20501%20P.plumosa%20Fort%20Brown.wav
104
+ Platypleuracapensis_MHV%20274%20P.capensis%20nr%20Arniston%20%231.wav,Platypleuracapensis,14,validation,MHV%20274%20P.capensis%20nr%20Arniston%20%231.wav
105
+ Platypleurasp12cfhirtipennis_MHV%20839%20P.sp12%20Swartberg%20Pass.wav,Platypleurasp12cfhirtipennis,26,test,MHV%20839%20P.sp12%20Swartberg%20Pass.wav
106
+ Myopsaltaleona_Myopsalta_leona_Wyalong.wav,Myopsaltaleona,6,train,Myopsalta_leona_Wyalong.wav
107
+ Platypleurasp04_MHV%20598%20P.sp4%20nr%20Maitjiesfontein.wav,Platypleurasp04,23,train,MHV%20598%20P.sp4%20nr%20Maitjiesfontein.wav
108
+ Platypleuracfcatenata_MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%234.wav,Platypleuracfcatenata,15,train,MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%234.wav
109
+ Platypleuradivisa_MHV%20983%20P.divisa%20nr%20Piet%20Retief.wav,Platypleuradivisa,18,train,MHV%20983%20P.divisa%20nr%20Piet%20Retief.wav
110
+ Platypleuraplumosa_MHV%20329%20P.plumosa%20Steydtlerville%20%235b.wav,Platypleuraplumosa,22,test,MHV%20329%20P.plumosa%20Steydtlerville%20%235b.wav
111
+ Platypleurahaglundi_MHV%20973%20P.haglundi%20nr%20Barberton%20%231.wav,Platypleurahaglundi,19,test,MHV%20973%20P.haglundi%20nr%20Barberton%20%231.wav
112
+ Azanicadazuluensis_MHV%20935%20A.zuluensis%20Haga%20Haga%20%235.wav,Azanicadazuluensis,0,validation,MHV%20935%20A.zuluensis%20Haga%20Haga%20%235.wav
113
+ Platypleuracfcatenata_MHV%20903%20P.catenata%20nrWitrivier.wav,Platypleuracfcatenata,15,train,MHV%20903%20P.catenata%20nrWitrivier.wav
114
+ Platypleuraplumosa_MHV%20321%20P.plumosa%20Ghwarriepoort%20River%20%231.wav,Platypleuraplumosa,22,validation,MHV%20321%20P.plumosa%20Ghwarriepoort%20River%20%231.wav
115
+ Platypleurasp04_MHV%20597%20P.sp4%20Meiringspoort%20Pass%20%231.wav,Platypleurasp04,23,test,MHV%20597%20P.sp4%20Meiringspoort%20Pass%20%231.wav
116
+ Myopsaltamelanobasis_Myopsalta_melanobasis_Maclagan.wav,Myopsaltamelanobasis,9,train,Myopsalta_melanobasis_Maclagan.wav
117
+ Platypleurahirtipennis_MHV%20486%20P.hirtipennis%20Coombs%20View%20%231.wav,Platypleurahirtipennis,20,validation,MHV%20486%20P.hirtipennis%20Coombs%20View%20%231.wav
118
+ Platypleuracapensis_MHV%201489%20P.capensis%20nr%20R102%20%231.wav,Platypleuracapensis,14,train,MHV%201489%20P.capensis%20nr%20R102%20%231.wav
119
+ Platypleurasp12cfhirtipennis_MHV%20602%20P.sp12%2020km%20SE%20Prince%20Albert.wav,Platypleurasp12cfhirtipennis,26,train,MHV%20602%20P.sp12%2020km%20SE%20Prince%20Albert.wav
120
+ Platypleuracfcatenata_MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%231.wav,Platypleuracfcatenata,15,train,MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%231.wav
121
+ Platypleurasp12cfhirtipennis_MHV%20898%20P.sp12%20nr%20Ganskop.wav,Platypleurasp12cfhirtipennis,26,train,MHV%20898%20P.sp12%20nr%20Ganskop.wav
122
+ Platypleurahaglundi_MHV%20976%20P.haglundi%20Blyde%20River%20Canyon.wav,Platypleurahaglundi,19,train,MHV%20976%20P.haglundi%20Blyde%20River%20Canyon.wav
123
+ Myopsaltaleona_Myopsalta_leona_Willowvale.wav,Myopsaltaleona,6,train,Myopsalta_leona_Willowvale.wav
124
+ Platypleuradeusta_MHV%20991%20P.duesta%20Woodcliffe%20Farm.wav,Platypleuradeusta,17,train,MHV%20991%20P.duesta%20Woodcliffe%20Farm.wav
125
+ Platypleurahaglundi_MHV%20973%20P.haglundi%20nr%20Barberton%20%232.wav,Platypleurahaglundi,19,validation,MHV%20973%20P.haglundi%20nr%20Barberton%20%232.wav
126
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_Brigalow_Research_Station.wav,Myopsaltamackinlayi,8,train,Myopsalta_mackinlayi_Brigalow_Research_Station.wav
127
+ Platypleuraplumosa_MHV%20321%20P.plumosa%20Ghwarriepoort%20River%20%232.wav,Platypleuraplumosa,22,train,MHV%20321%20P.plumosa%20Ghwarriepoort%20River%20%232.wav
128
+ Platypleuraplumosa_MHV%20540%20P.plumosa%20Fish%20River%20Bridge.wav,Platypleuraplumosa,22,train,MHV%20540%20P.plumosa%20Fish%20River%20Bridge.wav
129
+ Platypleuracfcatenata_MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%233.wav,Platypleuracfcatenata,15,train,MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%233.wav
130
+ Platypleurahirtipennis_MHV%201028%20P.hirtipennis%20nr%20Patterson.wav,Platypleurahirtipennis,20,train,MHV%201028%20P.hirtipennis%20nr%20Patterson.wav
131
+ Platypleurasp04_MHV%20597%20P.sp4%20Meiringspoort%20Pass%20%232.wav,Platypleurasp04,23,test,MHV%20597%20P.sp4%20Meiringspoort%20Pass%20%232.wav
132
+ Platypleurahirtipennis_MHV%20486%20P.hirtipennis%20Coombs%20View%20%232.wav,Platypleurahirtipennis,20,test,MHV%20486%20P.hirtipennis%20Coombs%20View%20%232.wav
133
+ Platypleuracfcatenata_MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%232.wav,Platypleuracfcatenata,15,train,MHV%20318%20P.capensis%20Prince%20Alberts%20Pass%20%232.wav
134
+ Platypleuraplumosa_MHV%20610%20P.plumosa%2040km%20E%20Prince%20Albert.wav,Platypleuraplumosa,22,train,MHV%20610%20P.plumosa%2040km%20E%20Prince%20Albert.wav
135
+ Myopsaltaleona_Myopsalta_leona_9km_NW_Grenfell.wav,Myopsaltaleona,6,train,Myopsalta_leona_9km_NW_Grenfell.wav
136
+ Platypleuraplumosa_MHV%20329%20P.plumosa%20Steydtlerville%20%233.wav,Platypleuraplumosa,22,validation,MHV%20329%20P.plumosa%20Steydtlerville%20%233.wav
137
+ Platypleurasp04_MHV%20595%20P.sp4%20nr%20Van%20Wyk%27s%20Dorp.wav,Platypleurasp04,23,train,MHV%20595%20P.sp4%20nr%20Van%20Wyk%27s%20Dorp.wav
138
+ Platypleuracfcatenata_MHV%20393%20P.catenata%20Jefferies%20Bay%20%233.wav,Platypleuracfcatenata,15,test,MHV%20393%20P.catenata%20Jefferies%20Bay%20%233.wav
139
+ Platypleuradeusta_MHV%20514%20P.duesta%20Nico%20Malan%20Pass%20%231.wav,Platypleuradeusta,17,validation,MHV%20514%20P.duesta%20Nico%20Malan%20Pass%20%231.wav
140
+ Platypleurasp10_MHV%20847%20P.sp10%20nr%20Kagga%20Kama%20%232.wav,Platypleurasp10,24,test,MHV%20847%20P.sp10%20nr%20Kagga%20Kama%20%232.wav
141
+ Platypleurasp13_MHV%201487%20P.sp%2013%20nr%20Thornhill%20%235.wav,Platypleurasp13,27,train,MHV%201487%20P.sp%2013%20nr%20Thornhill%20%235.wav
142
+ Platypleuradivisa_MHV%20515%20P.divisa%20S%20of%20Whittlesey%20%231.wav,Platypleuradivisa,18,validation,MHV%20515%20P.divisa%20S%20of%20Whittlesey%20%231.wav
143
+ Platypleurahirtipennis_MHV%20488%20P.hirtipennis%20Fish%20River%20Mouth%20%233.wav,Platypleurahirtipennis,20,train,MHV%20488%20P.hirtipennis%20Fish%20River%20Mouth%20%233.wav
144
+ Platypleurahirtipennis_MHV%20488%20P.hirtipennis%20Fish%20River%20Mouth%20%232.wav,Platypleurahirtipennis,20,train,MHV%20488%20P.hirtipennis%20Fish%20River%20Mouth%20%232.wav
145
+ Myopsaltaxerograsidia_Myopsalta_xerograsidia_Surat.wav,Myopsaltaxerograsidia,10,train,Myopsalta_xerograsidia_Surat.wav
146
+ Platypleurasp13_MHV%201487%20P.sp%2013%20nr%20Thornhill%20%234.wav,Platypleurasp13,27,test,MHV%201487%20P.sp%2013%20nr%20Thornhill%20%234.wav
147
+ Platypleuracapensis_MHV%20277%20P.capensis%204km%20W%20Die%20Dam%20%231.wav,Platypleuracapensis,14,train,MHV%20277%20P.capensis%204km%20W%20Die%20Dam%20%231.wav
148
+ Platypleuracfcatenata_MHV%20393%20P.catenata%20Jefferies%20Bay%20%232.wav,Platypleuracfcatenata,15,train,MHV%20393%20P.catenata%20Jefferies%20Bay%20%232.wav
149
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%238a.wav,Platypleurasp10,24,train,MHV%20313%20P.sp10%20Die%20Koo%20%238a.wav
150
+ Platypleuraplumosa_MHV%20329%20P.plumosa%20Steydtlerville%20%232.wav,Platypleuraplumosa,22,train,MHV%20329%20P.plumosa%20Steydtlerville%20%232.wav
151
+ Pycnasemiclara_MHV%20931%20P.semiclara%20Coffee%20Bay%20%232.wav,Pycnasemiclara,29,train,MHV%20931%20P.semiclara%20Coffee%20Bay%20%232.wav
152
+ Platypleuradeusta_MHV%20514%20P.duesta%20Nico%20Malan%20Pass%20%232.wav,Platypleuradeusta,17,test,MHV%20514%20P.duesta%20Nico%20Malan%20Pass%20%232.wav
153
+ Platypleuracapensis_MHV%20277%20P.capensis%204km%20W%20Die%20Dam%20%233.wav,Platypleuracapensis,14,test,MHV%20277%20P.capensis%204km%20W%20Die%20Dam%20%233.wav
154
+ Platypleurasp10_MHV%20847%20P.sp10%20nr%20Kagga%20Kama%20%231.wav,Platypleurasp10,24,test,MHV%20847%20P.sp10%20nr%20Kagga%20Kama%20%231.wav
155
+ Platypleurasp11cfhirtipennis_MHV%20845%20P.sp11%20Leeuwenshof%20%232.wav,Platypleurasp11cfhirtipennis,25,train,MHV%20845%20P.sp11%20Leeuwenshof%20%232.wav
156
+ Myopsaltamackinlayi_Myopsalta_mackinlayi_Boomer_Range.wav,Myopsaltamackinlayi,8,validation,Myopsalta_mackinlayi_Boomer_Range.wav
157
+ Platypleuradivisa_MHV%20515%20P.divisa%20S%20of%20Whittlesey%20%232.wav,Platypleuradivisa,18,test,MHV%20515%20P.divisa%20S%20of%20Whittlesey%20%232.wav
158
+ Platypleuradeusta_MHV%20994%20P.duesta%20Fort%20Nottingham.wav,Platypleuradeusta,17,train,MHV%20994%20P.duesta%20Fort%20Nottingham.wav
159
+ Platypleurahirtipennis_MHV%20488%20P.hirtipennis%20Fish%20River%20Mouth%20%231.wav,Platypleurahirtipennis,20,test,MHV%20488%20P.hirtipennis%20Fish%20River%20Mouth%20%231.wav
160
+ Myopsaltamelanobasis_Myopsalta_melanobasis_Southwood.wav,Myopsaltamelanobasis,9,train,Myopsalta_melanobasis_Southwood.wav
161
+ Platypleurasp04_MHV%20906%20P.sp4%20nr%20Barrydale.wav,Platypleurasp04,23,train,MHV%20906%20P.sp4%20nr%20Barrydale.wav
162
+ Platypleurasp12cfhirtipennis_MHV%20601%20P.sp12%2015km%20S%20Ladismith.wav,Platypleurasp12cfhirtipennis,26,validation,MHV%20601%20P.sp12%2015km%20S%20Ladismith.wav
163
+ Platypleuradeusta_MHV%20514%20P.duesta%20Nico%20Malan%20Pass%20%233.wav,Platypleuradeusta,17,train,MHV%20514%20P.duesta%20Nico%20Malan%20Pass%20%233.wav
164
+ Platypleuracfcatenata_MHV%20393%20P.catenata%20Jefferies%20Bay%20%231.wav,Platypleuracfcatenata,15,train,MHV%20393%20P.catenata%20Jefferies%20Bay%20%231.wav
165
+ Platypleurasp10_MHV%20313%20P.sp10%20Die%20Koo%20%234a.wav,Platypleurasp10,24,validation,MHV%20313%20P.sp10%20Die%20Koo%20%234a.wav
166
+ Platypleuraplumosa_MHV%20329%20P.plumosa%20Steydtlerville%20%231.wav,Platypleuraplumosa,22,test,MHV%20329%20P.plumosa%20Steydtlerville%20%231.wav
167
+ Myopsaltalongicauda_Myopsalta_longicauda_Stockingbingal.wav,Myopsaltalongicauda,7,train,Myopsalta_longicauda_Stockingbingal.wav
168
+ Platypleurasp13_MHV%201487%20P.sp%2013%20nr%20Thornhill%20%233.wav,Platypleurasp13,27,train,MHV%201487%20P.sp%2013%20nr%20Thornhill%20%233.wav
169
+ Platypleurasp13_MHV%201490%20P.sp%2013%20Gamtoos%20Pass%20%232.wav,Platypleurasp13,27,train,MHV%201490%20P.sp%2013%20Gamtoos%20Pass%20%232.wav
170
+ Pycnasemiclara_MHV%20923%20P.semiclara%20nr%20Donnybrook.wav,Pycnasemiclara,29,validation,MHV%20923%20P.semiclara%20nr%20Donnybrook.wav
171
+ Platypleurasp12cfhirtipennis_MHV%20606%20P.sp12%2020km%20S%20Ladismith.wav,Platypleurasp12cfhirtipennis,26,train,MHV%20606%20P.sp12%2020km%20S%20Ladismith.wav
172
+ Platypleuracfcatenata_MHV%20835%20P.catenata%20Swartberg%20Pass.wav,Platypleuracfcatenata,15,test,MHV%20835%20P.catenata%20Swartberg%20Pass.wav
173
+ Platypleuraintercapedinis_MHV%20947%20P.cf_.brunea%20Morningside%20Ranch%20%231.wav,Platypleuraintercapedinis,21,test,MHV%20947%20P.cf_.brunea%20Morningside%20Ranch%20%231.wav
174
+ Myopsaltamelanobasis_Myopsalta_melanobasis_20km_S_Taroom.wav,Myopsaltamelanobasis,9,test,Myopsalta_melanobasis_20km_S_Taroom.wav
175
+ Platypleurasp13_MHV%201487%20P.sp%2013%20nr%20Thornhill%20%232.wav,Platypleurasp13,27,train,MHV%201487%20P.sp%2013%20nr%20Thornhill%20%232.wav
176
+ Myopsaltalongicauda_Myopsalta_longicauda_Geham.wav,Myopsaltalongicauda,7,validation,Myopsalta_longicauda_Geham.wav
177
+ Platypleuraplumosa_MHV%20329%20P.plumosa%20Steydtlerville%20%234.wav,Platypleuraplumosa,22,train,MHV%20329%20P.plumosa%20Steydtlerville%20%234.wav
178
+ Platypleurasp12cfhirtipennis_MHV%20605%20P.sp12%2030km%20SW%20Ladismith.wav,Platypleurasp12cfhirtipennis,26,train,MHV%20605%20P.sp12%2030km%20SW%20Ladismith.wav
179
+ Platypleuradeusta_MHV%20992%20P.duesta%20nr%20Grasskop.wav,Platypleuradeusta,17,train,MHV%20992%20P.duesta%20nr%20Grasskop.wav
180
+ Myopsaltaleona_Myopsalta_leona_4km_W_Binjour.wav,Myopsaltaleona,6,validation,Myopsalta_leona_4km_W_Binjour.wav
181
+ Platypleurasp13_MHV%201490%20P.sp%2013%20Gamtoos%20Pass%20%231.wav,Platypleurasp13,27,train,MHV%201490%20P.sp%2013%20Gamtoos%20Pass%20%231.wav
182
+ Platypleurasp11cfhirtipennis_MHV%20859%20P.sp11%20Trawal%20%232.wav,Platypleurasp11cfhirtipennis,25,test,MHV%20859%20P.sp11%20Trawal%20%232.wav
183
+ Platypleuraintercapedinis_MHV%20947%20P.cf_.brunea%20Morningside%20Ranch%20%232.wav,Platypleuraintercapedinis,21,test,MHV%20947%20P.cf_.brunea%20Morningside%20Ranch%20%232.wav
184
+ Myopsaltamelanobasis_Myopsalta_melanobasis_14.6km_W_Drillham_Ck.wav,Myopsaltamelanobasis,9,validation,Myopsalta_melanobasis_14.6km_W_Drillham_Ck.wav
185
+ Platypleurasp13_MHV%201487%20P.sp%2013%20nr%20Thornhill%20%231.wav,Platypleurasp13,27,test,MHV%201487%20P.sp%2013%20nr%20Thornhill%20%231.wav
186
+ Platypleuracfcatenata_MHV%20607%20P.catenata%2020km%20W%20Swellendam%20R324.wav,Platypleuracfcatenata,15,test,MHV%20607%20P.catenata%2020km%20W%20Swellendam%20R324.wav
187
+ Platypleuracapensis_MHV%20277%20P.capensis%204km%20W%20Die%20Dam%20%234.wav,Platypleuracapensis,14,train,MHV%20277%20P.capensis%204km%20W%20Die%20Dam%20%234.wav
188
+ Platypleuraintercapedinis_MHV%20948%20P.cf_.brunea%20Morning%20Sun%20Ranch.wav,Platypleuraintercapedinis,21,train,MHV%20948%20P.cf_.brunea%20Morning%20Sun%20Ranch.wav
189
+ Platypleuracfcatenata_MHV%201017%20P.catenata%20nr%20Hankey%20%2817th%20Jan%202008%20rec%237%29.wav,Platypleuracfcatenata,15,train,MHV%201017%20P.catenata%20nr%20Hankey%20%2817th%20Jan%202008%20rec%237%29.wav
data/Orthoptera.csv ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ file_name,species,class_ID,data_set,original_file_name
2
+ Tettigoniaviridissima_200726_0062.M.wav,Tettigoniaviridissima,31,validation,200726_0062.M.wav
3
+ Grylluscampestris_GRYCAM_20210728_115826.M.wav,Grylluscampestris,4,train,GRYCAM_20210728_115826.M.wav
4
+ Grylluscampestris_dat008-019.M.wav,Grylluscampestris,4,train,dat008-019.M.wav
5
+ Pseudochorthippusparallelus_dat003-016.M.wav,Pseudochorthippusparallelus,28,train,dat003-016.M.wav
6
+ Nemobiussylvestris_dat017-001.wav,Nemobiussylvestris,11,train,dat017-001.wav
7
+ Grylluscampestris_dat003-004.M.wav,Grylluscampestris,4,validation,dat003-004.M.wav
8
+ Nemobiussylvestris_MixPre-304.M.wav,Nemobiussylvestris,11,test,MixPre-304.M.wav
9
+ Pholidopteragriseoaptera_dat006-002.M.wav,Pholidopteragriseoaptera,13,train,dat006-002.M.wav
10
+ Chorthippusbiguttulus_CHOBIG_20190906_163909.M.wav,Chorthippusbiguttulus,2,train,CHOBIG_20190906_163909.M.wav
11
+ Chorthippusbrunneus_dat024-016.wav,Chorthippusbrunneus,3,train,dat024-016.wav
12
+ Pseudochorthippusparallelus_VOC_150705-0325.M.wav,Pseudochorthippusparallelus,28,test,VOC_150705-0325.M.wav
13
+ Chorthippusbiguttulus_Take91.wav,Chorthippusbiguttulus,2,test,Take91.wav
14
+ Grylluscampestris_Take11.M.wav,Grylluscampestris,4,train,Take11.M.wav
15
+ Pseudochorthippusparallelus_Take00.M.wav,Pseudochorthippusparallelus,28,train,Take00.M.wav
16
+ Tettigoniaviridissima_MixPre-198.wav,Tettigoniaviridissima,31,test,MixPre-198.wav
17
+ Nemobiussylvestris_Take23.wav,Nemobiussylvestris,11,train,Take23.wav
18
+ Tettigoniaviridissima_dat118-003.M.wav,Tettigoniaviridissima,31,train,dat118-003.M.wav
19
+ Pholidopteragriseoaptera_dat022-004.wav,Pholidopteragriseoaptera,13,test,dat022-004.wav
20
+ Pholidopteragriseoaptera_dat097-023.wav,Pholidopteragriseoaptera,13,test,dat097-023.wav
21
+ Pholidopteragriseoaptera_dat003-037.M.wav,Pholidopteragriseoaptera,13,validation,dat003-037.M.wav
22
+ Grylluscampestris_dat010-007.M.wav,Grylluscampestris,4,test,dat010-007.M.wav
23
+ Tettigoniaviridissima_Take23.M.wav,Tettigoniaviridissima,31,train,Take23.M.wav
24
+ Chorthippusbiguttulus_Take45.M.wav,Chorthippusbiguttulus,2,train,Take45.M.wav
25
+ Nemobiussylvestris_Take20.wav,Nemobiussylvestris,11,train,Take20.wav
26
+ Nemobiussylvestris_Take21-1.wav,Nemobiussylvestris,11,train,Take21-1.wav
27
+ Grylluscampestris_dat010-005.M.wav,Grylluscampestris,4,train,dat010-005.M.wav
28
+ Oecanthuspellucens_Take05.wav,Oecanthuspellucens,12,train,Take05.wav
29
+ Chorthippusbrunneus_dat005-044.M.wav,Chorthippusbrunneus,3,train,dat005-044.M.wav
30
+ Chorthippusbrunneus_NoID_20200809_160342.M.wav,Chorthippusbrunneus,3,train,NoID_20200809_160342.M.wav
31
+ Tettigoniaviridissima_Take05.M.wav,Tettigoniaviridissima,31,train,Take05.M.wav
32
+ Nemobiussylvestris_Take21.wav,Nemobiussylvestris,11,train,Take21.wav
33
+ Tettigoniaviridissima_dat017-007.M.wav,Tettigoniaviridissima,31,train,dat017-007.M.wav
34
+ Chorthippusbiguttulus_dat028-023.wav,Chorthippusbiguttulus,2,train,dat028-023.wav
35
+ Nemobiussylvestris_20170828_205431.M.wav,Nemobiussylvestris,11,train,20170828_205431.M.wav
36
+ Chorthippusbiguttulus_Take07.M.wav,Chorthippusbiguttulus,2,test,Take07.M.wav
37
+ Tettigoniaviridissima_dat029-004.M.wav,Tettigoniaviridissima,31,train,dat029-004.M.wav
38
+ Roeselianaroeselii_dat025-009.M.wav,Roeselianaroeselii,30,train,dat025-009.M.wav
39
+ Pseudochorthippusparallelus_dat023-006.wav,Pseudochorthippusparallelus,28,train,dat023-006.wav
40
+ Chorthippusbiguttulus_dat179-001.wav,Chorthippusbiguttulus,2,train,dat179-001.wav
41
+ Roeselianaroeselii_dat025-010.M.wav,Roeselianaroeselii,30,train,dat025-010.M.wav
42
+ Pholidopteragriseoaptera_dat003-029.M.wav,Pholidopteragriseoaptera,13,train,dat003-029.M.wav
43
+ Pseudochorthippusparallelus_CHOPAR_20190626_194920.M.wav,Pseudochorthippusparallelus,28,train,CHOPAR_20190626_194920.M.wav
44
+ Pholidopteragriseoaptera_PHOGRI_20190914_155550.M.wav,Pholidopteragriseoaptera,13,train,PHOGRI_20190914_155550.M.wav
45
+ Roeselianaroeselii_Ultramic_20130809 190241.M.wav,Roeselianaroeselii,30,train,Ultramic_20130809 190241.M.wav
46
+ Grylluscampestris_dat008-018.M.wav,Grylluscampestris,4,train,dat008-018.M.wav
47
+ Grylluscampestris_dat008-008.wav,Grylluscampestris,4,train,dat008-008.wav
48
+ Roeselianaroeselii_dat006-019.M.wav,Roeselianaroeselii,30,validation,dat006-019.M.wav
49
+ Pseudochorthippusparallelus_dat023-005.wav,Pseudochorthippusparallelus,28,validation,dat023-005.wav
50
+ Grylluscampestris_dat008-009.wav,Grylluscampestris,4,train,dat008-009.wav
51
+ Chorthippusbiguttulus_dat001-018.M.wav,Chorthippusbiguttulus,2,train,dat001-018.M.wav
52
+ Grylluscampestris_dat003-005.M.wav,Grylluscampestris,4,test,dat003-005.M.wav
53
+ Tettigoniaviridissima_TETVIR_20200808_183034.M.wav,Tettigoniaviridissima,31,train,TETVIR_20200808_183034.M.wav
54
+ Pholidopteragriseoaptera_Ultramic_20130915 203136.M.wav,Pholidopteragriseoaptera,13,train,Ultramic_20130915 203136.M.wav
55
+ Pseudochorthippusparallelus_20170718_205835.M.wav,Pseudochorthippusparallelus,28,validation,20170718_205835.M.wav
56
+ Chorthippusbrunneus_No_ID_20170802_132433.M.wav,Chorthippusbrunneus,3,train,No_ID_20170802_132433.M.wav
57
+ Roeselianaroeselii_dat006-020.M.wav,Roeselianaroeselii,30,train,dat006-020.M.wav
58
+ Pseudochorthippusparallelus_dat001-044.M.wav,Pseudochorthippusparallelus,28,train,dat001-044.M.wav
59
+ Chorthippusbrunneus_dat011-004.wav,Chorthippusbrunneus,3,validation,dat011-004.wav
60
+ Pseudochorthippusparallelus_dat044-019.wav,Pseudochorthippusparallelus,28,train,dat044-019.wav
61
+ Grylluscampestris_Take32.M.wav,Grylluscampestris,4,validation,Take32.M.wav
62
+ Pseudochorthippusparallelus_dat004-016.M.wav,Pseudochorthippusparallelus,28,test,dat004-016.M.wav
63
+ Roeselianaroeselii_dat015-001.M.wav,Roeselianaroeselii,30,train,dat015-001.M.wav
64
+ Oecanthuspellucens_dat168-015.wav,Oecanthuspellucens,12,test,dat168-015.wav
65
+ Chorthippusbiguttulus_file0224.M.wav,Chorthippusbiguttulus,2,train,file0224.M.wav
66
+ Chorthippusbrunneus_dat003-020.M.wav,Chorthippusbrunneus,3,train,dat003-020.M.wav
67
+ Chorthippusbrunneus_dat022-008.wav,Chorthippusbrunneus,3,train,dat022-008.wav
68
+ Chorthippusbiguttulus_dat185-002.wav,Chorthippusbiguttulus,2,train,dat185-002.wav
69
+ Chorthippusbiguttulus_dat005-056.M.wav,Chorthippusbiguttulus,2,train,dat005-056.M.wav
70
+ Roeselianaroeselii_dat006-006.M.wav,Roeselianaroeselii,30,train,dat006-006.M.wav
71
+ Oecanthuspellucens_Take73.M.wav,Oecanthuspellucens,12,validation,Take73.M.wav
72
+ Tettigoniaviridissima_dat003-039.M.wav,Tettigoniaviridissima,31,train,dat003-039.M.wav
73
+ Chorthippusbiguttulus_NoID_20210729_115824.M.wav,Chorthippusbiguttulus,2,train,NoID_20210729_115824.M.wav
74
+ Pseudochorthippusparallelus_dat026-038.M.wav,Pseudochorthippusparallelus,28,test,dat026-038.M.wav
75
+ Grylluscampestris_dat010-006.M.wav,Grylluscampestris,4,train,dat010-006.M.wav
76
+ Chorthippusbiguttulus_Take22.wav,Chorthippusbiguttulus,2,validation,Take22.wav
77
+ Grylluscampestris_dat010-004.wav,Grylluscampestris,4,train,dat010-004.wav
78
+ Chorthippusbiguttulus_dat001-020.M.wav.M.wav,Chorthippusbiguttulus,2,validation,dat001-020.M.wav.M.wav
79
+ Tettigoniaviridissima_dat110-002.M.wav,Tettigoniaviridissima,31,train,dat110-002.M.wav
80
+ Pholidopteragriseoaptera_20200705_063934.M.wav,Pholidopteragriseoaptera,13,validation,20200705_063934.M.wav
81
+ Pseudochorthippusparallelus_PSEPAR_20210729_164449.M.wav,Pseudochorthippusparallelus,28,train,PSEPAR_20210729_164449.M.wav
82
+ Nemobiussylvestris_dat044-015.wav,Nemobiussylvestris,11,validation,dat044-015.wav
83
+ Grylluscampestris_dat008-012.M.wav,Grylluscampestris,4,train,dat008-012.M.wav
84
+ Chorthippusbrunneus_dat124-003.M.wav,Chorthippusbrunneus,3,validation,dat124-003.M.wav
85
+ Tettigoniaviridissima_Take70.M.wav,Tettigoniaviridissima,31,test,Take70.M.wav
86
+ Roeselianaroeselii_Metrioptera roeselii Echt.M.wav,Roeselianaroeselii,30,train,Metrioptera roeselii Echt.M.wav
87
+ Oecanthuspellucens_Take40.M.wav,Oecanthuspellucens,12,train,Take40.M.wav
88
+ Oecanthuspellucens_Take38.M.wav,Oecanthuspellucens,12,train,Take38.M.wav
89
+ Nemobiussylvestris_NoID_20210724_144844.M.wav,Nemobiussylvestris,11,train,NoID_20210724_144844.M.wav
90
+ Oecanthuspellucens_dat213-002.wav,Oecanthuspellucens,12,validation,dat213-002.wav
91
+ Chorthippusbrunneus_dat001-040.M.wav,Chorthippusbrunneus,3,train,dat001-040.M.wav
92
+ Oecanthuspellucens_dat116-008.M.wav,Oecanthuspellucens,12,test,dat116-008.M.wav
93
+ Roeselianaroeselii_dat006-015.M.wav,Roeselianaroeselii,30,train,dat006-015.M.wav
94
+ Nemobiussylvestris_No_ID_20170828_205643.M.wav,Nemobiussylvestris,11,train,No_ID_20170828_205643.M.wav
95
+ Grylluscampestris_dat008-010.wav,Grylluscampestris,4,train,dat008-010.wav
96
+ Grylluscampestris_dat008-014.M.wav,Grylluscampestris,4,train,dat008-014.M.wav
97
+ Chorthippusbiguttulus_CHOBIG_20190906_164243.M.wav,Chorthippusbiguttulus,2,test,CHOBIG_20190906_164243.M.wav
98
+ Tettigoniaviridissima_Ultramic_20130809 212842.M.wav,Tettigoniaviridissima,31,test,Ultramic_20130809 212842.M.wav
99
+ Chorthippusbrunneus_dat001-021.M.wav,Chorthippusbrunneus,3,test,dat001-021.M.wav
100
+ Grylluscampestris_dat010-003.wav,Grylluscampestris,4,train,dat010-003.wav
101
+ Oecanthuspellucens_dat213-001.wav,Oecanthuspellucens,12,test,dat213-001.wav
102
+ Oecanthuspellucens_dat174-004.wav,Oecanthuspellucens,12,train,dat174-004.wav
103
+ Pholidopteragriseoaptera_dat001-054.M.wav,Pholidopteragriseoaptera,13,train,dat001-054.M.wav
104
+ Grylluscampestris_dat008-016.M.wav,Grylluscampestris,4,train,dat008-016.M.wav
105
+ Oecanthuspellucens_Take03.M.wav,Oecanthuspellucens,12,validation,Take03.M.wav
106
+ Roeselianaroeselii_Metrioptera roeselii Vjjl.M.wav,Roeselianaroeselii,30,train,Metrioptera roeselii Vjjl.M.wav
107
+ Pseudochorthippusparallelus_dat097-022.M.wav,Pseudochorthippusparallelus,28,train,dat097-022.M.wav
108
+ Chorthippusbiguttulus_dat001-053.M.wav,Chorthippusbiguttulus,2,train,dat001-053.M.wav
109
+ Pholidopteragriseoaptera_dat148-009.wav,Pholidopteragriseoaptera,13,train,dat148-009.wav
110
+ Pholidopteragriseoaptera_dat169-001.M.wav,Pholidopteragriseoaptera,13,train,dat169-001.M.wav
111
+ Oecanthuspellucens_dat174-005.wav,Oecanthuspellucens,12,train,dat174-005.wav
112
+ Oecanthuspellucens_Take39.M.wav,Oecanthuspellucens,12,train,Take39.M.wav
113
+ Grylluscampestris_dat008-011.M.wav,Grylluscampestris,4,test,dat008-011.M.wav
114
+ Grylluscampestris_Take02.M.wav,Grylluscampestris,4,test,Take02.M.wav
115
+ Pseudochorthippusparallelus_dat005-060.M.wav,Pseudochorthippusparallelus,28,train,dat005-060.M.wav
116
+ Chorthippusbiguttulus_dat030-001.wav,Chorthippusbiguttulus,2,validation,dat030-001.wav
117
+ Grylluscampestris_file0045.M.wav,Grylluscampestris,4,train,file0045.M.wav
118
+ Tettigoniaviridissima_Take14.wav,Tettigoniaviridissima,31,test,Take14.wav
119
+ Nemobiussylvestris_dat004-037.M.wav,Nemobiussylvestris,11,train,dat004-037.M.wav
120
+ Pseudochorthippusparallelus_dat027-012.M.wav,Pseudochorthippusparallelus,28,test,dat027-012.M.wav
121
+ Pholidopteragriseoaptera_NoID_20190907_210359.M.wav,Pholidopteragriseoaptera,13,train,NoID_20190907_210359.M.wav
122
+ Nemobiussylvestris_No_ID_20170828_205759.M.wav,Nemobiussylvestris,11,train,No_ID_20170828_205759.M.wav
123
+ Chorthippusbrunneus_dat003-009.M.wav,Chorthippusbrunneus,3,validation,dat003-009.M.wav
124
+ Pholidopteragriseoaptera_NoID_20200810_181548.M.wav,Pholidopteragriseoaptera,13,train,NoID_20200810_181548.M.wav
125
+ Pseudochorthippusparallelus_VOC_190626-1033.wav,Pseudochorthippusparallelus,28,validation,VOC_190626-1033.wav
126
+ Chorthippusbiguttulus_dat031-007.wav,Chorthippusbiguttulus,2,train,dat031-007.wav
127
+ Chorthippusbrunneus_Take92.wav,Chorthippusbrunneus,3,train,Take92.wav
128
+ Nemobiussylvestris_Take16.wav,Nemobiussylvestris,11,test,Take16.wav
129
+ Grylluscampestris_dat008-017.M.wav,Grylluscampestris,4,train,dat008-017.M.wav
130
+ Chorthippusbiguttulus_dat003-040.M.wav,Chorthippusbiguttulus,2,test,dat003-040.M.wav
131
+ Oecanthuspellucens_Take47.M.wav,Oecanthuspellucens,12,train,Take47.M.wav
132
+ Chorthippusbrunneus_CHOBRU_20180914_141921.M.wav,Chorthippusbrunneus,3,train,CHOBRU_20180914_141921.M.wav
133
+ Tettigoniaviridissima_dat001-006.M.wav,Tettigoniaviridissima,31,test,dat001-006.M.wav
134
+ Pholidopteragriseoaptera_dat005-041.M.wav,Pholidopteragriseoaptera,13,validation,dat005-041.M.wav
135
+ Tettigoniaviridissima_20170629_201050.M.wav,Tettigoniaviridissima,31,validation,20170629_201050.M.wav
136
+ Chorthippusbiguttulus_dat149-002.wav,Chorthippusbiguttulus,2,test,dat149-002.wav
137
+ Nemobiussylvestris_Take17.wav,Nemobiussylvestris,11,test,Take17.wav
138
+ Pholidopteragriseoaptera___20150723_231036.M.wav,Pholidopteragriseoaptera,13,train,__20150723_231036.M.wav
139
+ Oecanthuspellucens_dat026-008.M.wav,Oecanthuspellucens,12,train,dat026-008.M.wav
140
+ Nemobiussylvestris_dat004-031.M.wav,Nemobiussylvestris,11,train,dat004-031.M.wav
141
+ Roeselianaroeselii_ROEROE_20200811_113904.M.wav,Roeselianaroeselii,30,train,ROEROE_20200811_113904.M.wav
142
+ Tettigoniaviridissima_dat020-004.M.wav,Tettigoniaviridissima,31,train,dat020-004.M.wav
143
+ Nemobiussylvestris_dat016-020.wav,Nemobiussylvestris,11,train,dat016-020.wav
144
+ Nemobiussylvestris_dat145-001.wav,Nemobiussylvestris,11,train,dat145-001.wav
data/README.txt ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #############################################
2
+
3
+ Description:
4
+
5
+ This dataset contains recordings of 32 sound producing insect species with a total 335 files and a length of 57 minutes. The dataset was compiled for training neural networks to automatically identify insect species while comparing adaptive, waveform-based frontends to conventional mel-spectrogram methods for audio feature extraction. This work will be submitted for publication in the future and the dataset can be used to replicate the results or for similar research. The scripts for audio processing and the machine learning implementations that were used will be published on Github (github.com/mariusfaiss/Adaptive-Representations-of-Sound-for-Automatic-Insect-Recognition).
6
+
7
+ Roughly half of the recordings (147) are of nine species belonging to the order Orthoptera. These recordings stem from a dataset that was originally compiled by Baudewijn Odé (unpublished). The remaining recordings (188) are of 23 species in the family Cicadidae. These recordings were selected from the Global Cicada Sound Collection hosted on Bioacoustica (doi.org/10.1093/database/bav054), including recordings published in doi.org/10.3897/BDJ.3.e5792 & doi.org/10.11646/zootaxa.4340.1. Many recordings from this collection included speech annotations in the beginning of the recordings, therefore the last ten seconds of audio were extracted and used in this dataset. 
8
+
9
+ All files were manually inspected and files with strong noise interference or with sounds of multiple species were removed. Between species, the number of files ranges from four to 22 files and the length from 40 seconds to almost nine minutes of audio material for a single species. The files range in length from less than one second to several minutes. All original files were available with sample rates of at least 44.1 kHz or higher but were resampled to 44.1 kHz mono WAV files for consistency.
10
+
11
+ #############################################
12
+
13
+ CSV:
14
+
15
+ The annotation files for both parts of the dataset include information for all recordings:
16
+
17
+ file_name: The file name used in this dataset, with the species name attached
18
+ species: The name of the species in the recording (see species list below)
19
+ class_ID: A numeric class identifier relating to each species (0-31, see below)
20
+ data_set: The data-subset the file was included in for training, testing or validating a neural network
21
+ original_file_name: The original file name from the source dataset, without the species name
22
+
23
+ #############################################
24
+
25
+ Baudewijn Odé - Orthoptera:
26
+
27
+ Species n min
28
+ Chorthippus biguttulus 20 3:43
29
+ Chorthippus brunneus 13 2:15
30
+ Gryllus campestris 22 3:38
31
+ Nemobius sylvestris 18 8:54
32
+ Oecanthus pellucens 14 4:27
33
+ Pholidoptera griseoaptera 15 1:54
34
+ Pseudochorthippus parallelus 17 2:01
35
+ Roeseliana roeselii 12 1:03
36
+ Tettigonia viridissima 16 1:34
37
+
38
+ #############################################
39
+
40
+ Ed Baker - Cicadidae:
41
+
42
+ Species n min
43
+ Azanicada zuluensis 4 0:40
44
+ Brevisiana brevis 5 0:50
45
+ Kikihia muta 6 1:00
46
+ Myopsalta leona 7 1:10
47
+ Myopsalta longicauda 4 0:40
48
+ Myopsalta mackinlayi 7 1:08
49
+ Myopsalta melanobasis 5 0:43
50
+ Myopsalta xerograsidia 6 1:00
51
+ Platypleura capensis 6 1:00
52
+ Platypleura cfcatenata 22 3:34
53
+ Platypleura chalybaea 7 1:10
54
+ Platypleura deusta 9 1:23
55
+ Platypleura divisa 6 1:00
56
+ Platypleura haglundi 5 0:50
57
+ Platypleura hirtipennis 6 0:54
58
+ Platypleura intercapedinis 5 0:50
59
+ Platypleura plumosa 19 3:09
60
+ Platypleura sp04 8 1:20
61
+ Platypleura sp10 16 2:24
62
+ Platypleura sp11 cfhirtipennis 4 0:40
63
+ Platypleura sp12 cfhirtipennis 10 1:40
64
+ Platypleura sp13 12 2:00
65
+ Pycna semiclara 9 1:30
66
+
67
+ #############################################
68
+
69
+ class_ID list:
70
+
71
+ Azanicadazuluensis 0
72
+ Brevisianabrevis 1
73
+ Chorthippusbiguttulus 2
74
+ Chorthippusbrunneus 3
75
+ Grylluscampestris 4
76
+ Kikihiamuta 5
77
+ Myopsaltaleona 6
78
+ Myopsaltalongicauda 7
79
+ Myopsaltamackinlayi 8
80
+ Myopsaltamelanobasis 9
81
+ Myopsaltaxerograsidia 10
82
+ Nemobiussylvestris 11
83
+ Oecanthuspellucens 12
84
+ Pholidopteragriseoaptera 13
85
+ Platypleuracapensis 14
86
+ Platypleuracfcatenata 15
87
+ Platypleurachalybaea 16
88
+ Platypleuradeusta 17
89
+ Platypleuradivisa 18
90
+ Platypleurahaglundi 19
91
+ Platypleurahirtipennis 20
92
+ Platypleuraintercapedinis 21
93
+ Platypleuraplumosa 22
94
+ Platypleurasp04 23
95
+ Platypleurasp10 24
96
+ Platypleurasp11cfhirtipennis 25
97
+ Platypleurasp12cfhirtipennis 26
98
+ Platypleurasp13 27
99
+ Pseudochorthippusparallelus 28
100
+ Pycnasemiclara 29
101
+ Roeselianaroeselii 30
102
+ Tettigoniaviridissima 31
103
+
104
+ #############################################
105
+
106
+ Copyright (c) is held by the individual recordists.
107
+
108
+ These data are published under the Creative Commons Attribution licence (CC BY):
109
+ https://creativecommons.org/licenses/by/4.0/
110
+ This licence allows to to re-use the data for almost any purpose (follow the link for more information), as long as you give credit to the original source.
111
+
112
+ For academic reuse, we ask that you do this as a citation to the research paper, given above.
113
+
114
+
docs/deployment.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local deployment
2
+
3
+ ## Desktop / local service integration
4
+ The supported entry point is `src.inference.predict_wav`. Install requirements and keep `model.h5`, `config.json`, `labels.json`, and `src/` together. There is no network dependency in the inference code.
5
+
6
+ ## Android / Kernel integration notes
7
+ Kernel's supplied architecture requires all live inference to remain on-device. This package is a Python/PyTorch training artifact, not yet a drop-in Android artifact. Before shipping:
8
+ 1. Freeze the audio frontend and compare it against the training implementation using a golden WAV fixture.
9
+ 2. Convert the model to an Android-supported runtime (e.g. LiteRT/TFLite) and test numerical parity; do not rename `.pt`/`.h5` to `.tflite`.
10
+ 3. Benchmark on the actual iQOO 15: latency, thermal/battery, noise, sample-rate path and memory.
11
+ 4. Validate with field-collected grain-pest recordings and verified physical inspections.
12
+ 5. Keep outputs experimental until false-positive, false-negative, uncertainty and cross-device performance are evaluated.
13
+ 6. Store model version, audio quality and human-verified outcome separately. Never treat a Clean prediction as proof of pest-free grain.
14
+
15
+ ## Suggested production architecture
16
+ Microphone -> signal-quality gate -> exactly matched local preprocessing -> local model -> experimental result UI -> local SQLite record. GCP services, if enabled, should sync only authorized metadata and must not receive raw audio or perform inference.
evaluate.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Re-score an existing CNN model dir on the official test split (and its session-clean subset)."""
2
+ import argparse, json
3
+ from pathlib import Path
4
+ from src.inference import load_model
5
+ from train import load_rows, evaluate_test, ROOT
6
+
7
+ p=argparse.ArgumentParser()
8
+ p.add_argument('--model-dir',default=str(ROOT)); p.add_argument('--data-dir',default=str(ROOT/'data'))
9
+ p.add_argument('--eval-crops',type=int,default=None,help='defaults to eval_crops in the model config (1 if absent)')
10
+ p.add_argument('--name',default=None,help='label for compare_models.py; result saved to eval/<name>.json')
11
+ a=p.parse_args()
12
+ mdir=Path(a.model_dir) if Path(a.model_dir).exists() else ROOT/a.model_dir
13
+ model,labels,cfg=load_model(mdir)
14
+ k=a.eval_crops if a.eval_crops is not None else int(cfg.get('eval_crops',1))
15
+ ev=evaluate_test(model,load_rows(a.data_dir),k,labels)
16
+ res={'model_dir':str(mdir),'eval_crops':k,**ev}
17
+ name=a.name or f"{mdir.resolve().name}_k{k}"
18
+ out=Path(__file__).resolve().parent/'eval'; out.mkdir(exist_ok=True)
19
+ (out/f'{name}.json').write_text(json.dumps(res,indent=2))
20
+ summary={s:{m:v for m,v in (ev[s] or {}).items() if m!='report'} for s in ev}
21
+ print(json.dumps({'name':name,'eval_crops':k,**summary},indent=2))
history.json ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "epoch": 1,
4
+ "train_loss": 3.549590092438918,
5
+ "validation_macro_f1": 0.0729895104895105
6
+ },
7
+ {
8
+ "epoch": 2,
9
+ "train_loss": 3.2208462678469143,
10
+ "validation_macro_f1": 0.07673611111111112
11
+ },
12
+ {
13
+ "epoch": 3,
14
+ "train_loss": 2.91882507617657,
15
+ "validation_macro_f1": 0.07138480392156864
16
+ },
17
+ {
18
+ "epoch": 4,
19
+ "train_loss": 2.692603404705341,
20
+ "validation_macro_f1": 0.08596299533799534
21
+ },
22
+ {
23
+ "epoch": 5,
24
+ "train_loss": 2.4573555176074686,
25
+ "validation_macro_f1": 0.2512648809523809
26
+ },
27
+ {
28
+ "epoch": 6,
29
+ "train_loss": 2.263241639504066,
30
+ "validation_macro_f1": 0.1829410173160173
31
+ },
32
+ {
33
+ "epoch": 7,
34
+ "train_loss": 2.1775223842033973,
35
+ "validation_macro_f1": 0.26106150793650795
36
+ },
37
+ {
38
+ "epoch": 8,
39
+ "train_loss": 2.0498767632704515,
40
+ "validation_macro_f1": 0.2673363095238095
41
+ },
42
+ {
43
+ "epoch": 9,
44
+ "train_loss": 1.9059001757548406,
45
+ "validation_macro_f1": 0.2243371212121212
46
+ },
47
+ {
48
+ "epoch": 10,
49
+ "train_loss": 1.7863830052889311,
50
+ "validation_macro_f1": 0.3174107142857143
51
+ },
52
+ {
53
+ "epoch": 11,
54
+ "train_loss": 1.6624305523358858,
55
+ "validation_macro_f1": 0.271875
56
+ },
57
+ {
58
+ "epoch": 12,
59
+ "train_loss": 1.6673363172090971,
60
+ "validation_macro_f1": 0.210123556998557
61
+ },
62
+ {
63
+ "epoch": 13,
64
+ "train_loss": 1.578295845251817,
65
+ "validation_macro_f1": 0.304265873015873
66
+ },
67
+ {
68
+ "epoch": 14,
69
+ "train_loss": 1.431467129633977,
70
+ "validation_macro_f1": 0.3928571428571429
71
+ },
72
+ {
73
+ "epoch": 15,
74
+ "train_loss": 1.4111314874428968,
75
+ "validation_macro_f1": 0.4458333333333333
76
+ },
77
+ {
78
+ "epoch": 16,
79
+ "train_loss": 1.416034460067749,
80
+ "validation_macro_f1": 0.4265625
81
+ },
82
+ {
83
+ "epoch": 17,
84
+ "train_loss": 1.321886534874256,
85
+ "validation_macro_f1": 0.3136160714285714
86
+ },
87
+ {
88
+ "epoch": 18,
89
+ "train_loss": 1.3062187295693617,
90
+ "validation_macro_f1": 0.4526785714285714
91
+ },
92
+ {
93
+ "epoch": 19,
94
+ "train_loss": 1.2392334662950957,
95
+ "validation_macro_f1": 0.3239583333333333
96
+ },
97
+ {
98
+ "epoch": 20,
99
+ "train_loss": 1.1840643057456384,
100
+ "validation_macro_f1": 0.33348214285714284
101
+ },
102
+ {
103
+ "epoch": 21,
104
+ "train_loss": 1.0871062324597285,
105
+ "validation_macro_f1": 0.41403769841269844
106
+ },
107
+ {
108
+ "epoch": 22,
109
+ "train_loss": 1.034065645474654,
110
+ "validation_macro_f1": 0.41443452380952384
111
+ },
112
+ {
113
+ "epoch": 23,
114
+ "train_loss": 1.09239622262808,
115
+ "validation_macro_f1": 0.28385416666666663
116
+ },
117
+ {
118
+ "epoch": 24,
119
+ "train_loss": 1.0812750504567072,
120
+ "validation_macro_f1": 0.40788690476190476
121
+ },
122
+ {
123
+ "epoch": 25,
124
+ "train_loss": 0.9923067643092229,
125
+ "validation_macro_f1": 0.4029017857142857
126
+ },
127
+ {
128
+ "epoch": 26,
129
+ "train_loss": 0.949020443054346,
130
+ "validation_macro_f1": 0.37656249999999997
131
+ },
132
+ {
133
+ "epoch": 27,
134
+ "train_loss": 0.9958093166351318,
135
+ "validation_macro_f1": 0.4095238095238095
136
+ },
137
+ {
138
+ "epoch": 28,
139
+ "train_loss": 0.984066536793342,
140
+ "validation_macro_f1": 0.3671875
141
+ },
142
+ {
143
+ "epoch": 29,
144
+ "train_loss": 0.9425493799723111,
145
+ "validation_macro_f1": 0.3669642857142857
146
+ },
147
+ {
148
+ "epoch": 30,
149
+ "train_loss": 0.888027026103093,
150
+ "validation_macro_f1": 0.4125
151
+ },
152
+ {
153
+ "epoch": 31,
154
+ "train_loss": 0.8223837109712454,
155
+ "validation_macro_f1": 0.41770833333333335
156
+ },
157
+ {
158
+ "epoch": 32,
159
+ "train_loss": 0.8049154189916757,
160
+ "validation_macro_f1": 0.41488095238095235
161
+ },
162
+ {
163
+ "epoch": 33,
164
+ "train_loss": 0.7919926161949451,
165
+ "validation_macro_f1": 0.3965029761904762
166
+ },
167
+ {
168
+ "epoch": 34,
169
+ "train_loss": 0.8035265940886277,
170
+ "validation_macro_f1": 0.3972470238095238
171
+ },
172
+ {
173
+ "epoch": 35,
174
+ "train_loss": 0.6834720992124997,
175
+ "validation_macro_f1": 0.390625
176
+ },
177
+ {
178
+ "epoch": 36,
179
+ "train_loss": 0.8087582496496347,
180
+ "validation_macro_f1": 0.3942708333333333
181
+ },
182
+ {
183
+ "epoch": 37,
184
+ "train_loss": 0.8021313754411844,
185
+ "validation_macro_f1": 0.5049107142857143
186
+ },
187
+ {
188
+ "epoch": 38,
189
+ "train_loss": 0.7348064826085017,
190
+ "validation_macro_f1": 0.44494047619047616
191
+ },
192
+ {
193
+ "epoch": 39,
194
+ "train_loss": 0.8007802000412574,
195
+ "validation_macro_f1": 0.4438988095238095
196
+ },
197
+ {
198
+ "epoch": 40,
199
+ "train_loss": 0.6960412997465867,
200
+ "validation_macro_f1": 0.41562499999999997
201
+ },
202
+ {
203
+ "epoch": 41,
204
+ "train_loss": 0.6792902808922988,
205
+ "validation_macro_f1": 0.4413690476190476
206
+ },
207
+ {
208
+ "epoch": 42,
209
+ "train_loss": 0.7683078875908484,
210
+ "validation_macro_f1": 0.44999999999999996
211
+ },
212
+ {
213
+ "epoch": 43,
214
+ "train_loss": 0.6988282685096447,
215
+ "validation_macro_f1": 0.4438988095238095
216
+ },
217
+ {
218
+ "epoch": 44,
219
+ "train_loss": 0.6402304653938,
220
+ "validation_macro_f1": 0.44218749999999996
221
+ },
222
+ {
223
+ "epoch": 45,
224
+ "train_loss": 0.611903557410607,
225
+ "validation_macro_f1": 0.44955357142857144
226
+ },
227
+ {
228
+ "epoch": 46,
229
+ "train_loss": 0.5640731900930405,
230
+ "validation_macro_f1": 0.48177083333333337
231
+ },
232
+ {
233
+ "epoch": 47,
234
+ "train_loss": 0.5864904935543354,
235
+ "validation_macro_f1": 0.5484374999999999
236
+ },
237
+ {
238
+ "epoch": 48,
239
+ "train_loss": 0.6681630175847274,
240
+ "validation_macro_f1": 0.3572916666666667
241
+ },
242
+ {
243
+ "epoch": 49,
244
+ "train_loss": 0.6659798106321921,
245
+ "validation_macro_f1": 0.45362103174603174
246
+ },
247
+ {
248
+ "epoch": 50,
249
+ "train_loss": 0.5747443850223835,
250
+ "validation_macro_f1": 0.54375
251
+ },
252
+ {
253
+ "epoch": 51,
254
+ "train_loss": 0.48586812156897324,
255
+ "validation_macro_f1": 0.4979166666666666
256
+ },
257
+ {
258
+ "epoch": 52,
259
+ "train_loss": 0.5781429868478042,
260
+ "validation_macro_f1": 0.4869047619047619
261
+ },
262
+ {
263
+ "epoch": 53,
264
+ "train_loss": 0.6054044022009923,
265
+ "validation_macro_f1": 0.4695684523809524
266
+ },
267
+ {
268
+ "epoch": 54,
269
+ "train_loss": 0.5589048977081592,
270
+ "validation_macro_f1": 0.5058779761904761
271
+ },
272
+ {
273
+ "epoch": 55,
274
+ "train_loss": 0.6121608293973483,
275
+ "validation_macro_f1": 0.49069940476190477
276
+ },
277
+ {
278
+ "epoch": 56,
279
+ "train_loss": 0.556478527876047,
280
+ "validation_macro_f1": 0.4575892857142857
281
+ },
282
+ {
283
+ "epoch": 57,
284
+ "train_loss": 0.567545989385018,
285
+ "validation_macro_f1": 0.45401785714285714
286
+ },
287
+ {
288
+ "epoch": 58,
289
+ "train_loss": 0.5471948671799439,
290
+ "validation_macro_f1": 0.5364583333333334
291
+ },
292
+ {
293
+ "epoch": 59,
294
+ "train_loss": 0.47601646872667164,
295
+ "validation_macro_f1": 0.38139880952380956
296
+ },
297
+ {
298
+ "epoch": 60,
299
+ "train_loss": 0.4983831655520659,
300
+ "validation_macro_f1": 0.49114583333333334
301
+ }
302
+ ]
labels.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "0": "Azanicadazuluensis",
3
+ "1": "Brevisianabrevis",
4
+ "2": "Chorthippusbiguttulus",
5
+ "3": "Chorthippusbrunneus",
6
+ "4": "Grylluscampestris",
7
+ "5": "Kikihiamuta",
8
+ "6": "Myopsaltaleona",
9
+ "7": "Myopsaltalongicauda",
10
+ "8": "Myopsaltamackinlayi",
11
+ "9": "Myopsaltamelanobasis",
12
+ "10": "Myopsaltaxerograsidia",
13
+ "11": "Nemobiussylvestris",
14
+ "12": "Oecanthuspellucens",
15
+ "13": "Pholidopteragriseoaptera",
16
+ "14": "Platypleuracapensis",
17
+ "15": "Platypleuracfcatenata",
18
+ "16": "Platypleurachalybaea",
19
+ "17": "Platypleuradeusta",
20
+ "18": "Platypleuradivisa",
21
+ "19": "Platypleurahaglundi",
22
+ "20": "Platypleurahirtipennis",
23
+ "21": "Platypleuraintercapedinis",
24
+ "22": "Platypleuraplumosa",
25
+ "23": "Platypleurasp04",
26
+ "24": "Platypleurasp10",
27
+ "25": "Platypleurasp11cfhirtipennis",
28
+ "26": "Platypleurasp12cfhirtipennis",
29
+ "27": "Platypleurasp13",
30
+ "28": "Pseudochorthippusparallelus",
31
+ "29": "Pycnasemiclara",
32
+ "30": "Roeselianaroeselii",
33
+ "31": "Tettigoniaviridissima"
34
+ }
metrics.json ADDED
@@ -0,0 +1,551 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "dataset_files_loaded": 331,
3
+ "split_counts": {
4
+ "train": 206,
5
+ "validation": 51,
6
+ "test": 74
7
+ },
8
+ "num_classes": 32,
9
+ "test_accuracy": 0.4864864864864865,
10
+ "test_macro_f1": 0.40037202380952375,
11
+ "test_weighted_f1": 0.47425997425997424,
12
+ "test_accuracy_ci95": [
13
+ 0.3780405405405406,
14
+ 0.5945945945945946
15
+ ],
16
+ "test_macro_f1_ci95": [
17
+ 0.2855325728085446,
18
+ 0.46932641432641425
19
+ ],
20
+ "test_report": {
21
+ "Azanicadazuluensis": {
22
+ "precision": 1.0,
23
+ "recall": 1.0,
24
+ "f1-score": 1.0,
25
+ "support": 1.0
26
+ },
27
+ "Brevisianabrevis": {
28
+ "precision": 0.0,
29
+ "recall": 0.0,
30
+ "f1-score": 0.0,
31
+ "support": 1.0
32
+ },
33
+ "Chorthippusbiguttulus": {
34
+ "precision": 0.75,
35
+ "recall": 0.6,
36
+ "f1-score": 0.6666666666666666,
37
+ "support": 5.0
38
+ },
39
+ "Chorthippusbrunneus": {
40
+ "precision": 0.5,
41
+ "recall": 1.0,
42
+ "f1-score": 0.6666666666666666,
43
+ "support": 1.0
44
+ },
45
+ "Grylluscampestris": {
46
+ "precision": 1.0,
47
+ "recall": 1.0,
48
+ "f1-score": 1.0,
49
+ "support": 4.0
50
+ },
51
+ "Kikihiamuta": {
52
+ "precision": 1.0,
53
+ "recall": 0.5,
54
+ "f1-score": 0.6666666666666666,
55
+ "support": 2.0
56
+ },
57
+ "Myopsaltaleona": {
58
+ "precision": 0.0,
59
+ "recall": 0.0,
60
+ "f1-score": 0.0,
61
+ "support": 2.0
62
+ },
63
+ "Myopsaltalongicauda": {
64
+ "precision": 0.0,
65
+ "recall": 0.0,
66
+ "f1-score": 0.0,
67
+ "support": 1.0
68
+ },
69
+ "Myopsaltamackinlayi": {
70
+ "precision": 0.0,
71
+ "recall": 0.0,
72
+ "f1-score": 0.0,
73
+ "support": 2.0
74
+ },
75
+ "Myopsaltamelanobasis": {
76
+ "precision": 0.0,
77
+ "recall": 0.0,
78
+ "f1-score": 0.0,
79
+ "support": 2.0
80
+ },
81
+ "Myopsaltaxerograsidia": {
82
+ "precision": 1.0,
83
+ "recall": 0.5,
84
+ "f1-score": 0.6666666666666666,
85
+ "support": 2.0
86
+ },
87
+ "Nemobiussylvestris": {
88
+ "precision": 1.0,
89
+ "recall": 0.6666666666666666,
90
+ "f1-score": 0.8,
91
+ "support": 3.0
92
+ },
93
+ "Oecanthuspellucens": {
94
+ "precision": 1.0,
95
+ "recall": 1.0,
96
+ "f1-score": 1.0,
97
+ "support": 3.0
98
+ },
99
+ "Pholidopteragriseoaptera": {
100
+ "precision": 1.0,
101
+ "recall": 0.5,
102
+ "f1-score": 0.6666666666666666,
103
+ "support": 2.0
104
+ },
105
+ "Platypleuracapensis": {
106
+ "precision": 0.25,
107
+ "recall": 1.0,
108
+ "f1-score": 0.4,
109
+ "support": 1.0
110
+ },
111
+ "Platypleuracfcatenata": {
112
+ "precision": 0.3333333333333333,
113
+ "recall": 0.25,
114
+ "f1-score": 0.2857142857142857,
115
+ "support": 4.0
116
+ },
117
+ "Platypleurachalybaea": {
118
+ "precision": 0.0,
119
+ "recall": 0.0,
120
+ "f1-score": 0.0,
121
+ "support": 2.0
122
+ },
123
+ "Platypleuradeusta": {
124
+ "precision": 0.0,
125
+ "recall": 0.0,
126
+ "f1-score": 0.0,
127
+ "support": 2.0
128
+ },
129
+ "Platypleuradivisa": {
130
+ "precision": 0.3333333333333333,
131
+ "recall": 0.5,
132
+ "f1-score": 0.4,
133
+ "support": 2.0
134
+ },
135
+ "Platypleurahaglundi": {
136
+ "precision": 0.4,
137
+ "recall": 1.0,
138
+ "f1-score": 0.5714285714285714,
139
+ "support": 2.0
140
+ },
141
+ "Platypleurahirtipennis": {
142
+ "precision": 0.0,
143
+ "recall": 0.0,
144
+ "f1-score": 0.0,
145
+ "support": 2.0
146
+ },
147
+ "Platypleuraintercapedinis": {
148
+ "precision": 0.0,
149
+ "recall": 0.0,
150
+ "f1-score": 0.0,
151
+ "support": 2.0
152
+ },
153
+ "Platypleuraplumosa": {
154
+ "precision": 0.25,
155
+ "recall": 0.25,
156
+ "f1-score": 0.25,
157
+ "support": 4.0
158
+ },
159
+ "Platypleurasp04": {
160
+ "precision": 0.4,
161
+ "recall": 1.0,
162
+ "f1-score": 0.5714285714285714,
163
+ "support": 2.0
164
+ },
165
+ "Platypleurasp10": {
166
+ "precision": 0.6666666666666666,
167
+ "recall": 0.6666666666666666,
168
+ "f1-score": 0.6666666666666666,
169
+ "support": 3.0
170
+ },
171
+ "Platypleurasp11cfhirtipennis": {
172
+ "precision": 0.0,
173
+ "recall": 0.0,
174
+ "f1-score": 0.0,
175
+ "support": 1.0
176
+ },
177
+ "Platypleurasp12cfhirtipennis": {
178
+ "precision": 0.0,
179
+ "recall": 0.0,
180
+ "f1-score": 0.0,
181
+ "support": 2.0
182
+ },
183
+ "Platypleurasp13": {
184
+ "precision": 0.5,
185
+ "recall": 0.3333333333333333,
186
+ "f1-score": 0.4,
187
+ "support": 3.0
188
+ },
189
+ "Pseudochorthippusparallelus": {
190
+ "precision": 0.6666666666666666,
191
+ "recall": 1.0,
192
+ "f1-score": 0.8,
193
+ "support": 4.0
194
+ },
195
+ "Pycnasemiclara": {
196
+ "precision": 1.0,
197
+ "recall": 0.5,
198
+ "f1-score": 0.6666666666666666,
199
+ "support": 2.0
200
+ },
201
+ "Roeselianaroeselii": {
202
+ "precision": 0.0,
203
+ "recall": 0.0,
204
+ "f1-score": 0.0,
205
+ "support": 0.0
206
+ },
207
+ "Tettigoniaviridissima": {
208
+ "precision": 0.75,
209
+ "recall": 0.6,
210
+ "f1-score": 0.6666666666666666,
211
+ "support": 5.0
212
+ },
213
+ "accuracy": 0.4864864864864865,
214
+ "macro avg": {
215
+ "precision": 0.43125,
216
+ "recall": 0.4333333333333333,
217
+ "f1-score": 0.40037202380952375,
218
+ "support": 74.0
219
+ },
220
+ "weighted avg": {
221
+ "precision": 0.5137387387387388,
222
+ "recall": 0.4864864864864865,
223
+ "f1-score": 0.47425997425997424,
224
+ "support": 74.0
225
+ }
226
+ },
227
+ "test_session_clean": {
228
+ "count": 48,
229
+ "accuracy": 0.4583333333333333,
230
+ "macro_f1": 0.34285714285714286,
231
+ "weighted_f1": 0.48298611111111106,
232
+ "accuracy_ci95": [
233
+ 0.3125,
234
+ 0.6041666666666666
235
+ ],
236
+ "macro_f1_ci95": [
237
+ 0.21914404761904757,
238
+ 0.4203331677380589
239
+ ]
240
+ },
241
+ "best_validation_macro_f1": 0.5484374999999999,
242
+ "eval_crops": 5,
243
+ "random_crop": true,
244
+ "init_from": null,
245
+ "lr": 0.001,
246
+ "epochs": 60,
247
+ "history": [
248
+ {
249
+ "epoch": 1,
250
+ "train_loss": 3.549590092438918,
251
+ "validation_macro_f1": 0.0729895104895105
252
+ },
253
+ {
254
+ "epoch": 2,
255
+ "train_loss": 3.2208462678469143,
256
+ "validation_macro_f1": 0.07673611111111112
257
+ },
258
+ {
259
+ "epoch": 3,
260
+ "train_loss": 2.91882507617657,
261
+ "validation_macro_f1": 0.07138480392156864
262
+ },
263
+ {
264
+ "epoch": 4,
265
+ "train_loss": 2.692603404705341,
266
+ "validation_macro_f1": 0.08596299533799534
267
+ },
268
+ {
269
+ "epoch": 5,
270
+ "train_loss": 2.4573555176074686,
271
+ "validation_macro_f1": 0.2512648809523809
272
+ },
273
+ {
274
+ "epoch": 6,
275
+ "train_loss": 2.263241639504066,
276
+ "validation_macro_f1": 0.1829410173160173
277
+ },
278
+ {
279
+ "epoch": 7,
280
+ "train_loss": 2.1775223842033973,
281
+ "validation_macro_f1": 0.26106150793650795
282
+ },
283
+ {
284
+ "epoch": 8,
285
+ "train_loss": 2.0498767632704515,
286
+ "validation_macro_f1": 0.2673363095238095
287
+ },
288
+ {
289
+ "epoch": 9,
290
+ "train_loss": 1.9059001757548406,
291
+ "validation_macro_f1": 0.2243371212121212
292
+ },
293
+ {
294
+ "epoch": 10,
295
+ "train_loss": 1.7863830052889311,
296
+ "validation_macro_f1": 0.3174107142857143
297
+ },
298
+ {
299
+ "epoch": 11,
300
+ "train_loss": 1.6624305523358858,
301
+ "validation_macro_f1": 0.271875
302
+ },
303
+ {
304
+ "epoch": 12,
305
+ "train_loss": 1.6673363172090971,
306
+ "validation_macro_f1": 0.210123556998557
307
+ },
308
+ {
309
+ "epoch": 13,
310
+ "train_loss": 1.578295845251817,
311
+ "validation_macro_f1": 0.304265873015873
312
+ },
313
+ {
314
+ "epoch": 14,
315
+ "train_loss": 1.431467129633977,
316
+ "validation_macro_f1": 0.3928571428571429
317
+ },
318
+ {
319
+ "epoch": 15,
320
+ "train_loss": 1.4111314874428968,
321
+ "validation_macro_f1": 0.4458333333333333
322
+ },
323
+ {
324
+ "epoch": 16,
325
+ "train_loss": 1.416034460067749,
326
+ "validation_macro_f1": 0.4265625
327
+ },
328
+ {
329
+ "epoch": 17,
330
+ "train_loss": 1.321886534874256,
331
+ "validation_macro_f1": 0.3136160714285714
332
+ },
333
+ {
334
+ "epoch": 18,
335
+ "train_loss": 1.3062187295693617,
336
+ "validation_macro_f1": 0.4526785714285714
337
+ },
338
+ {
339
+ "epoch": 19,
340
+ "train_loss": 1.2392334662950957,
341
+ "validation_macro_f1": 0.3239583333333333
342
+ },
343
+ {
344
+ "epoch": 20,
345
+ "train_loss": 1.1840643057456384,
346
+ "validation_macro_f1": 0.33348214285714284
347
+ },
348
+ {
349
+ "epoch": 21,
350
+ "train_loss": 1.0871062324597285,
351
+ "validation_macro_f1": 0.41403769841269844
352
+ },
353
+ {
354
+ "epoch": 22,
355
+ "train_loss": 1.034065645474654,
356
+ "validation_macro_f1": 0.41443452380952384
357
+ },
358
+ {
359
+ "epoch": 23,
360
+ "train_loss": 1.09239622262808,
361
+ "validation_macro_f1": 0.28385416666666663
362
+ },
363
+ {
364
+ "epoch": 24,
365
+ "train_loss": 1.0812750504567072,
366
+ "validation_macro_f1": 0.40788690476190476
367
+ },
368
+ {
369
+ "epoch": 25,
370
+ "train_loss": 0.9923067643092229,
371
+ "validation_macro_f1": 0.4029017857142857
372
+ },
373
+ {
374
+ "epoch": 26,
375
+ "train_loss": 0.949020443054346,
376
+ "validation_macro_f1": 0.37656249999999997
377
+ },
378
+ {
379
+ "epoch": 27,
380
+ "train_loss": 0.9958093166351318,
381
+ "validation_macro_f1": 0.4095238095238095
382
+ },
383
+ {
384
+ "epoch": 28,
385
+ "train_loss": 0.984066536793342,
386
+ "validation_macro_f1": 0.3671875
387
+ },
388
+ {
389
+ "epoch": 29,
390
+ "train_loss": 0.9425493799723111,
391
+ "validation_macro_f1": 0.3669642857142857
392
+ },
393
+ {
394
+ "epoch": 30,
395
+ "train_loss": 0.888027026103093,
396
+ "validation_macro_f1": 0.4125
397
+ },
398
+ {
399
+ "epoch": 31,
400
+ "train_loss": 0.8223837109712454,
401
+ "validation_macro_f1": 0.41770833333333335
402
+ },
403
+ {
404
+ "epoch": 32,
405
+ "train_loss": 0.8049154189916757,
406
+ "validation_macro_f1": 0.41488095238095235
407
+ },
408
+ {
409
+ "epoch": 33,
410
+ "train_loss": 0.7919926161949451,
411
+ "validation_macro_f1": 0.3965029761904762
412
+ },
413
+ {
414
+ "epoch": 34,
415
+ "train_loss": 0.8035265940886277,
416
+ "validation_macro_f1": 0.3972470238095238
417
+ },
418
+ {
419
+ "epoch": 35,
420
+ "train_loss": 0.6834720992124997,
421
+ "validation_macro_f1": 0.390625
422
+ },
423
+ {
424
+ "epoch": 36,
425
+ "train_loss": 0.8087582496496347,
426
+ "validation_macro_f1": 0.3942708333333333
427
+ },
428
+ {
429
+ "epoch": 37,
430
+ "train_loss": 0.8021313754411844,
431
+ "validation_macro_f1": 0.5049107142857143
432
+ },
433
+ {
434
+ "epoch": 38,
435
+ "train_loss": 0.7348064826085017,
436
+ "validation_macro_f1": 0.44494047619047616
437
+ },
438
+ {
439
+ "epoch": 39,
440
+ "train_loss": 0.8007802000412574,
441
+ "validation_macro_f1": 0.4438988095238095
442
+ },
443
+ {
444
+ "epoch": 40,
445
+ "train_loss": 0.6960412997465867,
446
+ "validation_macro_f1": 0.41562499999999997
447
+ },
448
+ {
449
+ "epoch": 41,
450
+ "train_loss": 0.6792902808922988,
451
+ "validation_macro_f1": 0.4413690476190476
452
+ },
453
+ {
454
+ "epoch": 42,
455
+ "train_loss": 0.7683078875908484,
456
+ "validation_macro_f1": 0.44999999999999996
457
+ },
458
+ {
459
+ "epoch": 43,
460
+ "train_loss": 0.6988282685096447,
461
+ "validation_macro_f1": 0.4438988095238095
462
+ },
463
+ {
464
+ "epoch": 44,
465
+ "train_loss": 0.6402304653938,
466
+ "validation_macro_f1": 0.44218749999999996
467
+ },
468
+ {
469
+ "epoch": 45,
470
+ "train_loss": 0.611903557410607,
471
+ "validation_macro_f1": 0.44955357142857144
472
+ },
473
+ {
474
+ "epoch": 46,
475
+ "train_loss": 0.5640731900930405,
476
+ "validation_macro_f1": 0.48177083333333337
477
+ },
478
+ {
479
+ "epoch": 47,
480
+ "train_loss": 0.5864904935543354,
481
+ "validation_macro_f1": 0.5484374999999999
482
+ },
483
+ {
484
+ "epoch": 48,
485
+ "train_loss": 0.6681630175847274,
486
+ "validation_macro_f1": 0.3572916666666667
487
+ },
488
+ {
489
+ "epoch": 49,
490
+ "train_loss": 0.6659798106321921,
491
+ "validation_macro_f1": 0.45362103174603174
492
+ },
493
+ {
494
+ "epoch": 50,
495
+ "train_loss": 0.5747443850223835,
496
+ "validation_macro_f1": 0.54375
497
+ },
498
+ {
499
+ "epoch": 51,
500
+ "train_loss": 0.48586812156897324,
501
+ "validation_macro_f1": 0.4979166666666666
502
+ },
503
+ {
504
+ "epoch": 52,
505
+ "train_loss": 0.5781429868478042,
506
+ "validation_macro_f1": 0.4869047619047619
507
+ },
508
+ {
509
+ "epoch": 53,
510
+ "train_loss": 0.6054044022009923,
511
+ "validation_macro_f1": 0.4695684523809524
512
+ },
513
+ {
514
+ "epoch": 54,
515
+ "train_loss": 0.5589048977081592,
516
+ "validation_macro_f1": 0.5058779761904761
517
+ },
518
+ {
519
+ "epoch": 55,
520
+ "train_loss": 0.6121608293973483,
521
+ "validation_macro_f1": 0.49069940476190477
522
+ },
523
+ {
524
+ "epoch": 56,
525
+ "train_loss": 0.556478527876047,
526
+ "validation_macro_f1": 0.4575892857142857
527
+ },
528
+ {
529
+ "epoch": 57,
530
+ "train_loss": 0.567545989385018,
531
+ "validation_macro_f1": 0.45401785714285714
532
+ },
533
+ {
534
+ "epoch": 58,
535
+ "train_loss": 0.5471948671799439,
536
+ "validation_macro_f1": 0.5364583333333334
537
+ },
538
+ {
539
+ "epoch": 59,
540
+ "train_loss": 0.47601646872667164,
541
+ "validation_macro_f1": 0.38139880952380956
542
+ },
543
+ {
544
+ "epoch": 60,
545
+ "train_loss": 0.4983831655520659,
546
+ "validation_macro_f1": 0.49114583333333334
547
+ }
548
+ ],
549
+ "seed": 42,
550
+ "limitations": "Small imbalanced research dataset; results are not evidence of grain-pest detection."
551
+ }
model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6a31529c5c7e27767246b99617c8f54ca36165fcb172814a9e50a8e168fc0ca7
3
+ size 653312
model.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:768f84773266fd847e97dd5b0546a250b1bf507046df851f93c9002ae92a55fb
3
+ size 643418
predict.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse, json, sys
2
+ from pathlib import Path
3
+ from src.inference import predict_wav
4
+
5
+ ROOT=Path(__file__).resolve().parent
6
+ def resolve(p):
7
+ """Use the path as given if it exists, else relative to this project folder (so the script works from any cwd)."""
8
+ p=Path(p)
9
+ return p if p.exists() or p.is_absolute() else ROOT/p
10
+
11
+ p=argparse.ArgumentParser(description='Run the local experimental Kernel insect classifier')
12
+ p.add_argument('wav'); p.add_argument('--model-dir',default='.')
13
+ p.add_argument('--device',default='cpu')
14
+ a=p.parse_args()
15
+ wav=Path(a.wav); model_dir=resolve(a.model_dir)
16
+ if not wav.is_file(): sys.exit(f"Audio file not found: {wav.resolve()}\nPass the path to a real recording (.wav, or .flac/.ogg/.mp3 with soundfile installed), e.g. python predict.py C:/path/to/clip.wav --model-dir full_model")
17
+ if not (model_dir/'model.h5').is_file(): sys.exit(f"No model.h5 in {model_dir}. Use --model-dir . (baseline), full_model or finetuned_cnn.")
18
+ try:
19
+ print(json.dumps(predict_wav(wav,model_dir,a.device),indent=2))
20
+ except (ValueError,RuntimeError) as e:
21
+ # soundfile raises LibsndfileError (a RuntimeError) for unreadable files.
22
+ sys.exit(f"Could not read {wav} as audio ({e}). Convert it first, e.g. ffmpeg -i input.mp3 -ac 1 output.wav")
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ numpy>=1.24
2
+ scipy>=1.10
3
+ pandas>=2.0
4
+ scikit-learn>=1.3
5
+ torch>=2.1
6
+ h5py>=3.9
7
+ soundfile>=0.12 # FLAC/OGG/MP3 input; WAV works without it
src/__init__.py ADDED
File without changes
src/audio.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Deterministic 16 kHz waveform -> log-mel features; standard-library/scipy implementation."""
2
+ import numpy as np
3
+ from scipy.io import wavfile
4
+ from scipy.signal import resample_poly, stft
5
+ from scipy.fft import dct
6
+
7
+ def to_float(y):
8
+ """Scale PCM samples to float32 in [-1,1]; must run before mixing to mono, which drops the integer dtype."""
9
+ y=np.asarray(y)
10
+ if y.dtype==np.uint8: return (y.astype(np.float32)-128.)/128.
11
+ if np.issubdtype(y.dtype,np.integer):
12
+ return y.astype(np.float32)/max(abs(np.iinfo(y.dtype).min),np.iinfo(y.dtype).max)
13
+ return y.astype(np.float32)
14
+
15
+ def read_audio(path):
16
+ """(sample_rate, mono float32 waveform) from WAV via scipy, or FLAC/OGG/MP3/... via the optional soundfile package."""
17
+ path=str(path)
18
+ if path.lower().endswith(('.wav','.wave')):
19
+ try:
20
+ sr,y=wavfile.read(path); y=to_float(y)
21
+ except ValueError:
22
+ # Compressed or unusual WAV encodings scipy cannot parse; libsndfile often can.
23
+ sr,y=_read_soundfile(path)
24
+ else: sr,y=_read_soundfile(path)
25
+ if y.ndim==2: y=y.mean(axis=1)
26
+ return int(sr),y.astype(np.float32)
27
+
28
+ def _read_soundfile(path):
29
+ try: import soundfile as sf
30
+ except ImportError:
31
+ raise RuntimeError(f"Reading {path} needs the soundfile package (pip install soundfile), "
32
+ "or convert it first: ffmpeg -i input.mp3 -ac 1 output.wav") from None
33
+ y,sr=sf.read(path,dtype='float32',always_2d=False)
34
+ return sr,y
35
+
36
+ SR=16000
37
+ DURATION=2.0
38
+ N_MELS=64
39
+ N_FFT=512
40
+ HOP=160
41
+
42
+ def _hz_to_mel(hz): return 2595.0*np.log10(1.0+np.asarray(hz)/700.0)
43
+ def _mel_to_hz(mel): return 700.0*(10.0**(np.asarray(mel)/2595.0)-1.0)
44
+ def mel_filterbank(sr=SR, n_fft=N_FFT, n_mels=N_MELS):
45
+ points=_mel_to_hz(np.linspace(_hz_to_mel(0),_hz_to_mel(sr/2),n_mels+2))
46
+ bins=np.floor((n_fft+1)*points/sr).astype(int)
47
+ fb=np.zeros((n_mels,n_fft//2+1),dtype=np.float32)
48
+ for m in range(1,n_mels+1):
49
+ left,center,right=bins[m-1:m+2]
50
+ if center>left:
51
+ fb[m-1,left:center]=np.arange(left,center)-left
52
+ fb[m-1,left:center]/=(center-left)
53
+ if right>center:
54
+ fb[m-1,center:right]=(right-np.arange(center,right))/(right-center)
55
+ return fb
56
+
57
+ _FB=mel_filterbank()
58
+ def resample(y, sr):
59
+ y=np.asarray(y,dtype=np.float32).reshape(-1)
60
+ if sr != SR:
61
+ import math
62
+ g=math.gcd(int(sr),SR)
63
+ y=resample_poly(y,SR//g,int(sr)//g).astype(np.float32)
64
+ return y
65
+
66
+ def crop_offsets(n_samples, k):
67
+ """k evenly spaced 2 s crop starts covering a 16 kHz waveform; k=1 gives the center crop."""
68
+ n=int(SR*DURATION)
69
+ if n_samples<=n or k<=1: return [None]
70
+ return [int(round(o)) for o in np.linspace(0,n_samples-n,k)]
71
+
72
+ def prepare_waveform(y, sr, offset=None):
73
+ y=resample(y,sr)
74
+ n=int(SR*DURATION)
75
+ if len(y)>n:
76
+ # Center crop by default: deterministic inference and evaluation.
77
+ start=(len(y)-n)//2 if offset is None else min(max(0,int(offset)),len(y)-n)
78
+ y=y[start:start+n]
79
+ elif len(y)<n:
80
+ y=np.pad(y,(0,n-len(y)))
81
+ peak=float(np.max(np.abs(y))) if len(y) else 0.
82
+ if peak>1e-8: y=y/peak
83
+ return y
84
+
85
+ def logmel(y, sr=SR, offset=None):
86
+ y=prepare_waveform(y,sr,offset)
87
+ _,_,z=stft(y,fs=SR,nperseg=N_FFT,noverlap=N_FFT-HOP,
88
+ nfft=N_FFT,boundary='zeros',padded=True)
89
+ power=(np.abs(z)**2).astype(np.float32)
90
+ mel=_FB @ power
91
+ feat=np.log(np.maximum(mel,1e-8))
92
+ # Per-record normalization stabilizes recorder gain; retain a consistent transformation.
93
+ feat=(feat-feat.mean())/(feat.std()+1e-5)
94
+ return feat.astype(np.float32)[None,:,:]
95
+
96
+ def audio_quality(y):
97
+ y=np.asarray(y,dtype=np.float32)
98
+ if y.size==0: return False, ['empty_audio']
99
+ rms=float(np.sqrt(np.mean(y*y)))
100
+ peak=float(np.max(np.abs(y)))
101
+ reasons=[]
102
+ if rms<0.002: reasons.append('low_level_or_silence')
103
+ if peak>0.999: reasons.append('possible_clipping')
104
+ return len(reasons)==0, reasons
src/inference.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load and run the packaged model locally. No network calls are made."""
2
+ import json, h5py, numpy as np, torch
3
+ from pathlib import Path
4
+ from .model import InsectCNN
5
+ from .audio import logmel, audio_quality, resample, crop_offsets, read_audio, SR
6
+
7
+ # Labels are 'GenusSpecies' with no separator; longest prefix first so Pseudochorthippus != Chorthippus.
8
+ ORTHOPTERA=['Pseudochorthippus','Chorthippus','Gryllus','Nemobius','Oecanthus','Pholidoptera','Roeseliana','Tettigonia']
9
+ CICADIDAE=['Azanicada','Brevisiana','Kikihia','Myopsalta','Platypleura','Pycna']
10
+ # Chosen on the validation split for full_model (species ~77%, genus ~90%, group ~98% correct above it).
11
+ DEFAULT_THRESHOLD=0.6
12
+
13
+ def genus_of(species):
14
+ return next((g for g in ORTHOPTERA+CICADIDAE if species.startswith(g)),species)
15
+
16
+ def group_of(species):
17
+ return 'Orthoptera (crickets/grasshoppers)' if genus_of(species) in ORTHOPTERA else 'Cicadidae (cicadas)'
18
+
19
+ def _summed(probs, labels, keyf):
20
+ out={}
21
+ for i,p in enumerate(probs): k=keyf(labels[str(i)]); out[k]=out.get(k,0.)+float(p)
22
+ return sorted(out.items(),key=lambda kv:-kv[1])
23
+
24
+ def identify(probs, labels, threshold=DEFAULT_THRESHOLD):
25
+ """Most specific taxonomic level whose summed probability reaches the threshold, else 'unknown'."""
26
+ top=int(np.argmax(probs))
27
+ levels=[('species',[(labels[str(top)],float(probs[top]))]),
28
+ ('genus',_summed(probs,labels,genus_of)),('group',_summed(probs,labels,group_of))]
29
+ for level,ranked in levels:
30
+ if ranked[0][1]>=threshold: return {'level':level,'name':ranked[0][0],'confidence':ranked[0][1]}
31
+ return {'level':'unknown','name':'Uncertain - possibly a species outside the 32 trained classes','confidence':levels[-1][1][0][1]}
32
+
33
+ def load_model(model_dir, device='cpu'):
34
+ model_dir=Path(model_dir)
35
+ cfg=json.loads((model_dir/'config.json').read_text())
36
+ labels=json.loads((model_dir/'labels.json').read_text())
37
+ net=InsectCNN(cfg['num_classes'])
38
+ h5_path=model_dir/'model.h5'
39
+ with h5py.File(h5_path,'r') as h:
40
+ state={}
41
+ for key in h['state_dict']:
42
+ arr=np.asarray(h['state_dict'][key])
43
+ state[key]=torch.from_numpy(arr.copy())
44
+ net.load_state_dict(state)
45
+ net.to(device).eval()
46
+ return net, labels, cfg
47
+
48
+ def predict_wav(path, model_dir, device='cpu'):
49
+ net,labels,cfg=load_model(model_dir,device)
50
+ # Integer PCM is scaled to [-1,1] before mixing to mono so the quality check sees true levels.
51
+ sr,y=read_audio(path)
52
+ ok,reasons=audio_quality(y)
53
+ # eval_crops>1 averages over evenly spaced 2 s windows; older bundles use the center crop.
54
+ y16=resample(y,sr)
55
+ offs=crop_offsets(len(y16),int(cfg.get('eval_crops',1)))
56
+ x=torch.from_numpy(np.stack([logmel(y16,SR,o) for o in offs])).to(device)
57
+ with torch.no_grad():
58
+ probs=torch.softmax(net(x),dim=1).mean(dim=0).cpu().numpy()
59
+ order=np.argsort(probs)[::-1][:5]
60
+ thr=float(cfg.get('confidence_threshold',DEFAULT_THRESHOLD))
61
+ return {'identification':identify(probs,labels,thr),
62
+ 'genus_scores':[{'genus':g,'score':v} for g,v in _summed(probs,labels,genus_of)[:3]],
63
+ 'group_scores':[{'group':g,'score':v} for g,v in _summed(probs,labels,group_of)],
64
+ 'predicted_species':labels[str(int(order[0]))],
65
+ 'confidence':float(probs[order[0]]),
66
+ 'top_k':[{'species':labels[str(int(i))],'score':float(probs[i])} for i in order],
67
+ 'quality_pass':bool(ok),'quality_reasons':reasons,
68
+ 'model_version':cfg['model_version'],
69
+ 'experimental':True,
70
+ 'warning':'Research insect-species classifier only. Not trained for grain-pest detection or grain safety.'}
src/model.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Small CNN for 32-way insect acoustic species classification."""
2
+ import torch
3
+ from torch import nn
4
+
5
+ class InsectCNN(nn.Module):
6
+ def __init__(self, num_classes=32):
7
+ super().__init__()
8
+ self.features = nn.Sequential(
9
+ nn.Conv2d(1, 16, 3, padding=1), nn.BatchNorm2d(16), nn.ReLU(),
10
+ nn.MaxPool2d(2),
11
+ nn.Conv2d(16, 32, 3, padding=1), nn.BatchNorm2d(32), nn.ReLU(),
12
+ nn.MaxPool2d(2),
13
+ nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), nn.ReLU(),
14
+ nn.AdaptiveAvgPool2d((4, 4)),
15
+ )
16
+ self.classifier = nn.Sequential(nn.Flatten(), nn.Dropout(0.25),
17
+ nn.Linear(64*4*4, 128), nn.ReLU(),
18
+ nn.Dropout(0.25), nn.Linear(128, num_classes))
19
+ def forward(self, x):
20
+ return self.classifier(self.features(x))
tests/test_model.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest, json
2
+ from pathlib import Path
3
+ import numpy as np, torch
4
+ from src.model import InsectCNN
5
+ from src.inference import load_model
6
+ from src.inference import identify, genus_of
7
+ from src.audio import logmel, crop_offsets, resample, SR
8
+ ROOT=Path(__file__).resolve().parents[1]
9
+ class ModelPackageTests(unittest.TestCase):
10
+ def test_forward_shape(self):
11
+ m=InsectCNN(32).eval()
12
+ with torch.no_grad(): out=m(torch.zeros(2,1,64,201))
13
+ self.assertEqual(tuple(out.shape),(2,32))
14
+ def test_hdf5_load(self):
15
+ m,labels,cfg=load_model(ROOT)
16
+ self.assertEqual(len(labels),32)
17
+ self.assertEqual(cfg['num_classes'],32)
18
+ def test_center_crop_default_unchanged(self):
19
+ y=np.random.default_rng(0).standard_normal(44100*5).astype(np.float32)
20
+ y16=resample(y,44100)
21
+ np.testing.assert_allclose(logmel(y,44100),logmel(y16,SR,(len(y16)-2*SR)//2),atol=1e-5)
22
+ self.assertEqual(crop_offsets(len(y16),1),[None])
23
+ offs=crop_offsets(len(y16),5)
24
+ self.assertEqual((offs[0],offs[-1]),(0,len(y16)-2*SR))
25
+ def test_finetuned_bundle_loads(self):
26
+ d=ROOT/'finetuned_cnn'
27
+ if not (d/'model.h5').exists(): self.skipTest('finetuned_cnn not trained')
28
+ m,labels,cfg=load_model(d)
29
+ self.assertEqual(len(labels),32)
30
+ def test_identify_levels(self):
31
+ labels={str(i):n for i,n in enumerate(['Myopsaltaleona','Myopsaltamackinlayi','Chorthippusbrunneus','Pseudochorthippusparallelus'])}
32
+ self.assertEqual(genus_of('Pseudochorthippusparallelus'),'Pseudochorthippus')
33
+ self.assertEqual(identify(np.array([.7,.1,.1,.1]),labels)['level'],'species')
34
+ r=identify(np.array([.4,.35,.15,.1]),labels); self.assertEqual((r['level'],r['name']),('genus','Myopsalta'))
35
+ self.assertEqual(identify(np.array([.15,.15,.35,.35]),labels,0.6)['name'],'Orthoptera (crickets/grasshoppers)')
36
+ self.assertEqual(identify(np.array([.3,.2,.3,.2]),labels,0.6)['level'],'unknown')
37
+ if __name__=='__main__': unittest.main()
train.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, io, re, json, zipfile, argparse, random
2
+ from pathlib import Path
3
+ from urllib.parse import unquote
4
+ import numpy as np, pandas as pd, torch, h5py
5
+ from scipy.io import wavfile
6
+ from sklearn.metrics import accuracy_score, classification_report, f1_score
7
+ from src.audio import logmel, resample, crop_offsets, SR, DURATION
8
+ from src.model import InsectCNN
9
+
10
+ ROOT=Path(__file__).resolve().parent
11
+
12
+ def load_rows(data_dir=ROOT/'data'):
13
+ """Return dicts with the 16 kHz waveform, label, split and source session of every CSV row found in the zips."""
14
+ rows=[]
15
+ for csv,zipname in [('Cicadidae.csv','Cicadidae.zip'),('Orthoptera.csv','Orthoptera.zip')]:
16
+ df=pd.read_csv(Path(data_dir)/csv)
17
+ with zipfile.ZipFile(Path(data_dir)/zipname) as z:
18
+ wavs=[n for n in z.namelist() if n.lower().endswith('.wav') and '/._' not in n]
19
+ lookup={}
20
+ for n in wavs: lookup.setdefault(n.split('/')[-1],n)
21
+ for _,r in df.iterrows():
22
+ fn=str(r['file_name'])
23
+ member=lookup.get(fn)
24
+ if not member:
25
+ matches=[n for n in wavs if n.endswith('/'+fn)]
26
+ if matches: member=matches[0]
27
+ if not member:
28
+ print('MISSING',fn); continue
29
+ sr,y=wavfile.read(io.BytesIO(z.read(member)))
30
+ if y.ndim==2: y=y.astype(np.float32).mean(axis=1)
31
+ if np.issubdtype(y.dtype,np.integer):
32
+ y=y.astype(np.float32)/max(abs(np.iinfo(y.dtype).min),np.iinfo(y.dtype).max)
33
+ else: y=y.astype(np.float32)
34
+ rows.append({'wave':resample(y,sr),'label':int(r['class_ID']),'species':str(r['species']),
35
+ 'split':str(r['data_set']).lower(),'file_name':fn,
36
+ 'session':session_key(str(r['species']),str(r['original_file_name']))})
37
+ return rows
38
+
39
+ def session_key(species, original_file_name):
40
+ """Group files cut from the same source recording/tape (e.g. 'MHV 313 ... #6a/#6b', 'dat008-019')."""
41
+ s=unquote(original_file_name)
42
+ for p in [r'(MHV\s*\d+)',r'(dat\d+)',r'([A-Z]+_\d{8})',r'(\d{6})_']:
43
+ m=re.match(p,s)
44
+ if m: return species+'|'+m.group(1)
45
+ return species+'|'+s.rsplit('.',1)[0]
46
+
47
+ def features(waves, offsets):
48
+ return torch.from_numpy(np.stack([logmel(w,SR,o) for w,o in zip(waves,offsets)]))
49
+
50
+ def predict_probs(model, rows, idx, eval_crops=1):
51
+ """Per-file class probabilities, averaged over eval_crops evenly spaced 2 s windows."""
52
+ model.eval(); out=[]
53
+ with torch.no_grad():
54
+ for i in idx:
55
+ offs=crop_offsets(len(rows[i]['wave']),eval_crops)
56
+ out.append(torch.softmax(model(features([rows[i]['wave']]*len(offs),offs)),dim=1).mean(dim=0).numpy())
57
+ return np.stack(out) if out else np.empty((0,32))
58
+
59
+ def evaluate_split(model, rows, idx, eval_crops=1, names=None, n_boot=1000, seed=42):
60
+ y=np.array([rows[i]['label'] for i in idx]); pred=np.argmax(predict_probs(model,rows,idx,eval_crops),axis=1)
61
+ res={'count':int(len(idx)),'accuracy':float(accuracy_score(y,pred)),
62
+ 'macro_f1':float(f1_score(y,pred,average='macro',zero_division=0)),
63
+ 'weighted_f1':float(f1_score(y,pred,average='weighted',zero_division=0))}
64
+ # Bootstrap over files: with ~74 test files the point estimates are very uncertain.
65
+ rng=np.random.default_rng(seed); acc=[]; mf1=[]
66
+ for _ in range(n_boot):
67
+ b=rng.integers(0,len(y),len(y))
68
+ acc.append(accuracy_score(y[b],pred[b])); mf1.append(f1_score(y[b],pred[b],average='macro',zero_division=0))
69
+ res['accuracy_ci95']=[float(np.percentile(acc,2.5)),float(np.percentile(acc,97.5))]
70
+ res['macro_f1_ci95']=[float(np.percentile(mf1,2.5)),float(np.percentile(mf1,97.5))]
71
+ if names:
72
+ res['report']=classification_report(y,pred,labels=list(range(32)),target_names=[names[str(i)] for i in range(32)],output_dict=True,zero_division=0)
73
+ return res
74
+
75
+ def evaluate_test(model, rows, eval_crops=1, names=None):
76
+ """Official test split, plus the subset whose source session never occurs in train/validation."""
77
+ split=np.array([r['split'] for r in rows]); test=np.where(split=='test')[0]
78
+ seen={r['session'] for r in rows if r['split']!='test'}
79
+ clean=np.array([i for i in test if rows[i]['session'] not in seen])
80
+ return {'test':evaluate_split(model,rows,test,eval_crops,names),
81
+ 'test_session_clean':evaluate_split(model,rows,clean,eval_crops) if len(clean) else None}
82
+
83
+ def main():
84
+ ap=argparse.ArgumentParser()
85
+ ap.add_argument('--epochs',type=int,default=16); ap.add_argument('--seed',type=int,default=42)
86
+ ap.add_argument('--output',default='.'); ap.add_argument('--data-dir',default=str(ROOT/'data'))
87
+ ap.add_argument('--init-from',default=None,help='model dir to warm-start from (fine-tuning)')
88
+ ap.add_argument('--lr',type=float,default=1e-3)
89
+ ap.add_argument('--random-crop',action='store_true',help='train on a fresh random 2 s window per file each epoch')
90
+ ap.add_argument('--eval-crops',type=int,default=1,help='average over K evenly spaced windows for validation/test')
91
+ args=ap.parse_args()
92
+ random.seed(args.seed); np.random.seed(args.seed); torch.manual_seed(args.seed)
93
+ torch.set_num_threads(max(1,min(4,os.cpu_count() or 1)))
94
+ rows=load_rows(args.data_dir)
95
+ if not rows: raise RuntimeError('No audio rows loaded. Check data/ zip and CSV files.')
96
+ y=np.array([r['label'] for r in rows]); split=np.array([r['split'] for r in rows])
97
+ train=np.where(split=='train')[0]; val=np.where(split=='validation')[0]; test=np.where(split=='test')[0]
98
+ # Dataset-provided partitions are preserved; no random file-level split. Test is only scored once, after selection.
99
+ waves=[r['wave'] for r in rows]; yt=torch.tensor(y,dtype=torch.long)
100
+ if args.init_from:
101
+ from src.inference import load_model
102
+ model,_,_=load_model(args.init_from)
103
+ else:
104
+ model=InsectCNN(32)
105
+ n=int(SR*DURATION)
106
+ xcenter=features(waves,[None]*len(waves)) if not args.random_crop else None
107
+ counts=np.bincount(y[train],minlength=32)
108
+ weights=np.array([len(train)/(32*max(1,c)) for c in counts],dtype=np.float32)
109
+ criterion=torch.nn.CrossEntropyLoss(weight=torch.tensor(weights))
110
+ opt=torch.optim.AdamW(model.parameters(),lr=args.lr,weight_decay=1e-4)
111
+ best=-1; best_state=None; history=[]
112
+ for epoch in range(args.epochs):
113
+ model.train(); perm=np.random.permutation(train); losses=[]
114
+ for start in range(0,len(perm),16):
115
+ idx=perm[start:start+16]
116
+ if args.random_crop:
117
+ offs=[np.random.randint(0,len(waves[i])-n+1) if len(waves[i])>n else None for i in idx]
118
+ xb=features([waves[i] for i in idx],offs)
119
+ else: xb=xcenter[idx]
120
+ opt.zero_grad(); logits=model(xb); loss=criterion(logits,yt[idx]); loss.backward(); opt.step(); losses.append(float(loss.item()))
121
+ pv=np.argmax(predict_probs(model,rows,val,args.eval_crops),axis=1) if len(val) else np.array([])
122
+ score=f1_score(y[val],pv,average='macro',zero_division=0) if len(val) else -np.mean(losses)
123
+ history.append({'epoch':epoch+1,'train_loss':float(np.mean(losses)),'validation_macro_f1':float(score)})
124
+ print(f"epoch {epoch+1}/{args.epochs}: loss={np.mean(losses):.4f} val_macro_f1={score:.4f}",flush=True)
125
+ if score>best:
126
+ best=score; best_state={k:v.detach().cpu().clone() for k,v in model.state_dict().items()}
127
+ model.load_state_dict(best_state); model.eval()
128
+ ids=sorted(set(int(i) for i in y))
129
+ names={str(i):next((r['species'] for r in rows if r['label']==i),f'class_{i}') for i in ids}
130
+ ev=evaluate_test(model,rows,args.eval_crops,names); t=ev['test']
131
+ metrics={'dataset_files_loaded':len(rows),'split_counts':{s:int((split==s).sum()) for s in ['train','validation','test']},
132
+ 'num_classes':32,'test_accuracy':t['accuracy'],'test_macro_f1':t['macro_f1'],'test_weighted_f1':t['weighted_f1'],
133
+ 'test_accuracy_ci95':t['accuracy_ci95'],'test_macro_f1_ci95':t['macro_f1_ci95'],
134
+ 'test_report':t['report'],'test_session_clean':ev['test_session_clean'],
135
+ 'best_validation_macro_f1':float(best),'eval_crops':args.eval_crops,'random_crop':args.random_crop,
136
+ 'init_from':args.init_from,'lr':args.lr,'epochs':args.epochs,
137
+ 'history':history,'seed':args.seed,'limitations':'Small imbalanced research dataset; results are not evidence of grain-pest detection.'}
138
+ out=Path(args.output); out.mkdir(parents=True,exist_ok=True)
139
+ torch.save(model.state_dict(),out/'model.pt')
140
+ with h5py.File(out/'model.h5','w') as h:
141
+ h.attrs['format']='kernel-pytorch-state-dict-hdf5-v1'
142
+ h.attrs['model_architecture']='InsectCNN'
143
+ h.attrs['model_version']='0.1.0-experimental'
144
+ sd=h.create_group('state_dict')
145
+ for k,v in model.state_dict().items(): sd.create_dataset(k,data=v.detach().cpu().numpy())
146
+ (out/'labels.json').write_text(json.dumps(names,indent=2))
147
+ (out/'config.json').write_text(json.dumps({'model_type':'kernel-insect-cnn','architecture':'InsectCNN','num_classes':32,'sample_rate':16000,'clip_seconds':2.0,'feature':'64-bin log-mel spectrogram','eval_crops':args.eval_crops,'model_version':'0.1.0-experimental','framework':'PyTorch','hdf5_format':'kernel-pytorch-state-dict-hdf5-v1','task':'insect species classification','experimental':True},indent=2))
148
+ (out/'metrics.json').write_text(json.dumps(metrics,indent=2))
149
+ (out/'history.json').write_text(json.dumps(history,indent=2))
150
+ print(json.dumps({k:v for k,v in metrics.items() if k not in ('test_report','history')},indent=2))
151
+ if __name__=='__main__': main()