diff --git a/.gitignore b/.gitignore index ab340499a..c8a54dce6 100644 Binary files a/.gitignore and b/.gitignore differ diff --git a/Tutorials/Engine team first Git Commit/Onboarding_Task_KFW.ipynb b/Tutorials/Engine team first Git Commit/Onboarding_Task_KFW.ipynb index a585b81fe..23b869f6f 100644 --- a/Tutorials/Engine team first Git Commit/Onboarding_Task_KFW.ipynb +++ b/Tutorials/Engine team first Git Commit/Onboarding_Task_KFW.ipynb @@ -708,17 +708,27 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "id": "a61a7da9", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\librosa\\util\\files.py:10: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", + " from pkg_resources import resource_filename\n", + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ "TensorFlow Version: 2.10.0\n", "TensorFlow Hub Version: 0.12.0\n", - "Librosa Version: 0.11.0\n", + "Librosa Version: 0.9.2\n", "Audiomentations Version: 0.42.0\n", "Protobuf Version: 3.19.4\n", "Available GPUs: []\n" @@ -852,7 +862,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "id": "ca008cb3-eb26-4c52-9ba5-b58d40096de1", "metadata": {}, "outputs": [ @@ -882,7 +892,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Downloading project_echo_bucket_1: 100%|█████████████████████████████████████████████| 353/353 [00:50<00:00, 7.01it/s]\n" + "Downloading project_echo_bucket_1: 100%|█████████████████████████████████████████████| 353/353 [00:43<00:00, 8.11it/s]\n" ] }, { @@ -897,7 +907,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Downloading project_echo_bucket_2: 100%|███████████████████████████████████████████| 7161/7161 [11:13<00:00, 10.63it/s]\n" + "Downloading project_echo_bucket_2: 100%|███████████████████████████████████████████| 7161/7161 [06:16<00:00, 19.03it/s]\n" ] }, { @@ -912,7 +922,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Downloading project_echo_bucket_3: 100%|███████████████████████████████████████████| 7536/7536 [12:01<00:00, 10.44it/s]\n" + "Downloading project_echo_bucket_3: 100%|███████████████████████████████████████████| 7536/7536 [08:41<00:00, 14.45it/s]\n" ] }, { @@ -927,21 +937,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "Downloading project_echo_birdclef: 100%|█████████████████████████████████████████████| 524/524 [01:11<00:00, 7.29it/s]" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "All files have been downloaded successfully!\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "\n" + "Downloading project_echo_birdclef: 91%|████████████████████████████████████████▉ | 476/524 [01:25<00:12, 3.76it/s]" ] } ], diff --git a/src/Components/docker-compose.yml b/src/Components/docker-compose.yml index e56098976..6497b25af 100644 --- a/src/Components/docker-compose.yml +++ b/src/Components/docker-compose.yml @@ -22,8 +22,8 @@ services: echo_engine: build: - context: ./Engine - dockerfile: Engine.Dockerfile + context: ../Prototypes/engine/torch_impl + dockerfile: light_engine.Dockerfile image: ts-echo-engine container_name: ts-echo-engine-cont networks: diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/README_EfficientNetV2_Engine_Integration.md b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/README_EfficientNetV2_Engine_Integration.md new file mode 100644 index 000000000..9b27b0278 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/README_EfficientNetV2_Engine_Integration.md @@ -0,0 +1,652 @@ +# EfficientNetV2 Engine Integration Progress + +## Overview + +This work integrates the EfficientNetV2 model selected from the Sprint 1 benchmarking work into a reusable inference workflow for Project Echo. The main goal of this part is to create a functioning model inference pipeline that can load a saved model, preprocess audio, return species predictions, and support future Engine and MQTT integration. + +At this stage, the focus is not on maximising model accuracy. The priority is to prove that the selected model can be trained, saved, reloaded, and used in a structured Engine-style inference flow. + +--- + +## Branch + +Work was completed on the branch: + +```text +EE/WKF/efficientnetv2_integration +``` + +--- + +## Work Completed So Far + +### 1. EfficientNetV2 Training and Model Saving + +A training notebook was created to retrain the selected EfficientNetV2 model using the available Project Echo dataset. + +The notebook performs the following steps: + +1. Loads the audio dataset from the local data folder. +2. Extracts Mel spectrogram features from audio files. +3. Trains the EfficientNetV2 model using the extracted features. +4. Evaluates the trained model quickly on test data. +5. Saves the trained model and supporting configuration files. + +The following output files are generated: + +```text +_trained_models/ + efficientnetv2_project_echo.pt + class_mapping.json + preprocess_config.json + training_metrics.json +``` + +### Purpose + +This step creates the saved model file required for Engine-side inference. + +--- + +### 2. Saved Model Loading Test + +After training, the saved model was loaded again to confirm that it can be reused outside the training process. + +This test confirms: + +1. The `.pt` model file loads successfully. +2. The model architecture can be rebuilt using the saved checkpoint information. +3. The saved class mapping can be used to convert predicted class indexes into species labels. +4. A sample audio file can be passed through the loaded model. +5. The model returns a predicted label and confidence value. + +### Purpose + +This proves that the saved model is usable for inference and is not only valid inside the training notebook. + +--- + +### 3. Reusable EfficientNetV2 Predictor + +A reusable Python predictor file was created: + +```text +efficientnetv2_predictor.py +``` + +This file contains reusable functions for model inference: + +```text +load_model() +load_class_mapping() +load_preprocess_config() +preprocess_audio() +predict_audio() +``` + +The predictor handles: + +1. Loading the saved EfficientNetV2 model. +2. Loading the class mapping file. +3. Loading the preprocessing configuration file. +4. Loading and padding/trimming audio. +5. Extracting Mel spectrogram features. +6. Running model inference. +7. Returning the predicted label, class index, confidence value, and top predictions. + +### Purpose + +This separates inference logic from the notebook, making the code reusable by the real Engine pipeline later. + +--- + +### 4. Single Audio Inference Test + +The reusable predictor was tested using a real dataset audio file: + +```text +Acanthiza chrysorrhoa/region_3.650-4.900.mp3 +``` + +The model predicted: + +```text +Predicted label: Acanthiza chrysorrhoa +Confidence: 0.9337 +``` + +The top prediction matched the actual folder label. + +### Purpose + +This confirms that the reusable predictor can correctly process a real audio file and return a valid prediction. + +--- + +### 5. Dataset-Based Validation + +A dataset validation script was created: + +```text +validate_efficientnetv2_dataset_inference.py +``` + +This script validates the saved EfficientNetV2 model using labelled dataset audio files. + +It checks: + +1. The saved model loads correctly. +2. The class mapping loads correctly. +3. The preprocessing config loads correctly. +4. Dataset audio files can be preprocessed. +5. The model returns predicted labels and confidence values. +6. Predictions can be compared with actual dataset folder labels. + +The validation tested 50 audio files. + +Result: + +```text +Total files tested: 50 +Correct predictions: 47 +Validation accuracy: 0.94 +``` + +The validation output was saved as: + +```text +efficientnetv2_dataset_validation_results.csv +``` + +### Purpose + +This answers the dataset validation part of the task. It proves that the model can run inference on labelled dataset audio and return labels and confidence values. + +--- + +### 6. JSON-Style Inference Validation + +A JSON-style validation script was created: + +```text +validate_efficientnetv2_json_inference.py +``` + +This script simulates the type of structured input that the Engine may receive before or through MQTT. + +Example input: + +```json +{ + "device_id": "sensor_001", + "audio_path": "path/to/audio.mp3", + "timestamp": "2026-04-27T15:30:00", + "source": "dataset_validation" +} +``` + +The script: + +1. Reads the JSON-style message. +2. Extracts the audio path. +3. Validates that the audio file exists. +4. Runs preprocessing and inference. +5. Returns a structured JSON-style prediction response. + +Example output includes: + +```json +{ + "device_id": "sensor_001", + "model": "EfficientNetV2", + "prediction": { + "label": "Acanthiza chrysorrhoa", + "class_index": 0, + "confidence": 0.9337119460105896 + }, + "status": "success" +} +``` + +The result was saved as: + +```text +efficientnetv2_json_inference_result.json +``` + +### Purpose + +This proves that the model can work with a structured JSON-style Engine input, which is the step before real MQTT integration. + +--- + +### 7. MQTT Message Handler Validation + +A simulated MQTT message handler validation script was created: + +```text +validate_efficientnetv2_mqtt_message_handler.py +``` + +This script does not require a live MQTT broker. Instead, it simulates how an MQTT payload would arrive as bytes. + +The simulated MQTT payload is: + +```text +MQTT payload bytes → decode JSON → extract audio path → run predictor → return prediction response +``` + +The script validates: + +1. MQTT-style payload bytes can be decoded. +2. JSON fields are read correctly. +3. The audio path is extracted correctly. +4. The EfficientNetV2 model loads successfully. +5. Audio preprocessing runs before inference. +6. Label, confidence, class index, and top predictions are returned. +7. The response is saved as JSON. + +The result was saved as: + +```text +efficientnetv2_mqtt_message_handler_result.json +``` + +### Purpose + +This prepares the model inference flow for real MQTT integration later. + +--- + +### 8. ONNX Export for Saved EfficientNetV2 Model + +An ONNX export script was created: + +```text +export_saved_efficientnetv2_to_onnx.py +``` + +This script exports the saved EfficientNetV2 model from the current training and inference workflow. Unlike the earlier reference export script, this version loads the trained checkpoint from: + +```text +_trained_models/efficientnetv2_project_echo.pt +``` + +The script rebuilds the EfficientNetV2 architecture using the saved checkpoint information, loads the trained weights, and exports the model to ONNX using the expected input shape: + +```text +[1, 1, 128, 313] +``` + +The ONNX model was saved as: + +```text +_trained_models/efficientnetv2_project_echo.onnx +``` + +Output summary: + +```text +Loading saved EfficientNetV2 model... +Model loaded successfully. +Model name: efficientnetv2_rw_s +Number of classes: 123 +Exporting to ONNX... +ONNX model saved to: +_trained_models/efficientnetv2_project_echo.onnx +Checking ONNX model... +Opset: 13 +ONNX export completed successfully. +``` + +### Purpose + +This step prepares the trained EfficientNetV2 model for conversion into a deployment-friendly format. ONNX acts as the intermediate format between PyTorch and TensorFlow/TFLite. + +--- + +### 9. TFLite Conversion + +A conversion script was created: + +```text +convert_saved_efficientnetv2_onnx_to_tflite.py +``` + +This script converts the exported ONNX model into a TensorFlow SavedModel and then into a TFLite model. + +The conversion flow is: + +```text +Saved PyTorch EfficientNetV2 model + ↓ +ONNX model + ↓ +TensorFlow SavedModel + ↓ +TFLite model +``` + +The following outputs were generated: + +```text +_trained_models/ + efficientnetv2_project_echo.onnx + efficientnetv2_project_echo_saved_model/ + efficientnetv2_project_echo.tflite +``` + +Output summary: + +```text +Loading ONNX model... +Converting ONNX to TensorFlow SavedModel... +TensorFlow SavedModel saved to: +_trained_models/efficientnetv2_project_echo_saved_model +Converting TensorFlow SavedModel to TFLite... +TFLite model saved to: +_trained_models/efficientnetv2_project_echo.tflite +TFLite conversion completed successfully. File size: 85.19 MB +``` + +### Purpose + +This step validates that the selected EfficientNetV2 model can be converted into a TFLite format. This is important for production or edge deployment, because TFLite models are generally more suitable for lightweight inference than PyTorch models. + +--- + +### 10. TFLite Inference Validation + +A TFLite validation script was created: + +```text +validate_efficientnetv2_tflite_inference.py +``` + +This script validates the converted TFLite model using the same class mapping and preprocessing configuration used by the PyTorch predictor. + +The script performs: + +1. Single audio PyTorch vs TFLite comparison. +2. TFLite input and output shape inspection. +3. TFLite prediction on a real dataset audio file. +4. Dataset-based TFLite validation using 50 labelled audio files. +5. CSV and JSON output export. + +TFLite input details confirmed: + +```text +Input shape: [1, 1, 128, 313] +Input dtype: float32 +``` + +TFLite output details confirmed: + +```text +Output shape: [1, 123] +Output dtype: float32 +``` + +Single audio comparison used: + +```text +Acanthiza chrysorrhoa/region_3.650-4.900.mp3 +``` + +PyTorch result: + +```text +Predicted label: Acanthiza chrysorrhoa +Confidence: 0.9337119460105896 +``` + +TFLite result: + +```text +Predicted label: Acanthiza chrysorrhoa +Confidence: 0.9337119460105896 +``` + +Comparison summary: + +```text +PyTorch label: Acanthiza chrysorrhoa +TFLite label: Acanthiza chrysorrhoa +Labels match: True +PyTorch confidence: 0.9337119460105896 +TFLite confidence: 0.9337119460105896 +``` + +Dataset-based TFLite validation result: + +```text +Total files tested: 50 +Correct predictions: 47 +TFLite validation accuracy: 0.94 +``` + +The outputs were saved as: + +```text +efficientnetv2_tflite_single_audio_comparison.json +efficientnetv2_tflite_dataset_validation_results.csv +``` + +### Purpose + +This confirms that the converted TFLite model can run inference successfully and gives matching results with the PyTorch model for the tested sample audio. It also confirms that TFLite dataset validation matches the previous PyTorch dataset validation result of 47 correct predictions out of 50. + +--- + +### 11. Engine-Side TFLite Message Path Validation + +A safe copy of the real Engine file was created: + +```text +light_echo_engine_efficientnetv2_tflite.py +``` + +This copied file was used so the original Engine file could remain unchanged while testing the EfficientNetV2 TFLite integration. + +The copied Engine file was updated to: + +1. Load the existing Engine configuration and credentials. +2. Load the EfficientNetV2 TFLite model. +3. Load the class mapping file. +4. Load the preprocessing configuration file. +5. Preprocess raw audio bytes using the EfficientNetV2 settings. +6. Run local TFLite inference inside the Engine file. +7. Add a new `EfficientNetV2_TFLite_Mode` path inside `on_message()`. + +The Engine copy successfully loaded the EfficientNetV2 TFLite model. + +Model loading output: + +```text +EfficientNetV2 TFLite model loaded successfully. +EfficientNetV2 input shape: [ 1 1 128 313] +EfficientNetV2 output shape: [ 1 123] +``` + +The local audio-bytes inference function was tested using: + +```text +Acanthiza chrysorrhoa/region_3.650-4.900.mp3 +``` + +Local audio-bytes inference result: + +```text +Predicted class: Acanthiza chrysorrhoa +Predicted probability: 93.37 +Sample rate: 32000 +Processed audio length: 160000 +``` + +A fake MQTT message was then created and passed directly into `on_message()` to simulate the Engine receiving an MQTT audio message. + +Fake MQTT message test result: + +```text +Recieved audio message, processing via engine model... +2026-04-30T10:00:00 +EfficientNetV2_TFLite_Mode +Predicted class : Acanthiza chrysorrhoa +Predicted probability : 93.37 +``` + +Top prediction: + +```text +Acanthiza chrysorrhoa +Confidence: 0.9337119460105896 +``` + +The final API send step failed locally because `ts-api-cont` is a Docker service name and is not available when running from Jupyter. + +Local API error: + +```text +HTTPConnectionPool(host='ts-api-cont', port=9000): Max retries exceeded with url: /engine/event +``` + +This does not affect the model inference result because the prediction was already completed successfully before the API send step. + +### Purpose + +This validates that the copied real Engine message flow can call the EfficientNetV2 TFLite inference path from an MQTT-style payload. It confirms that raw audio from the message can be decoded, preprocessed, passed through the local TFLite model, and converted into a predicted species label with confidence. + +--- + +### 12. Prototype Docker Engine Validation + +The EfficientNetV2 TFLite Engine file was also tested inside the Docker-based system environment using the prototype Engine path. + +The Docker Compose Engine build context was temporarily pointed to the prototype Engine folder: + +```text +src/Prototypes/engine/torch_impl +``` + +The Engine Dockerfile was updated to run: + +```text +light_echo_engine_efficientnetv2_tflite.py +``` + +A smaller Engine-specific requirements file was created to avoid installing unnecessary training and benchmarking packages inside the Engine container. This requirements file only includes the packages needed for Engine-side inference, such as TensorFlow, Librosa, NumPy, Paho MQTT, Requests, PyMongo, Google Cloud Storage, Pandas, scikit-learn, SoundFile, Geopy, and DiskCache. + +After rebuilding the Engine container, the EfficientNetV2 TFLite model loaded successfully inside Docker. + +Docker Engine output: + +```text +Python Version : 3.9.23 +TensorFlow Version : 2.10.0 +Librosa Version : 0.9.2 +Echo Engine configuration successfully loaded +Echo Engine credentials successfully loaded +Found echo store database names: ['EchoNet'] +INFO: Created TensorFlow Lite XNNPACK delegate for CPU. +EfficientNetV2 TFLite model loaded successfully. +EfficientNetV2 input shape: [ 1 1 128 313] +EfficientNetV2 output shape: [ 1 123] +Engine started. +``` + +This confirms that the EfficientNetV2 TFLite model, class mapping, preprocessing configuration, TensorFlow Lite interpreter, and required Docker dependencies are available inside the Engine container. + +The Engine then attempted to connect to the MQTT broker: + +```text +DEBUG: Attempting to connect to Broker at: mqtt-broker:1883 +CONNECTION ERROR: [Errno -2] Name or service not known +``` + +This MQTT connection issue is related to Docker Compose service naming or MQTT broker networking. It is not caused by the EfficientNetV2 model or the TFLite inference code, because the model had already loaded successfully before the MQTT connection step. + +### Purpose + +This step validates that the EfficientNetV2 TFLite inference setup can run inside a Docker Engine container. It confirms the Docker-level model loading and dependency setup. The remaining blocker is the MQTT broker connection inside the wider system environment, which should be handled through Docker Compose service/network configuration. + +## Validation Flow Completed + +The completed validation flow is: + +```text +Training notebook + ↓ +Saved EfficientNetV2 PyTorch model + ↓ +Reusable predictor file + ↓ +Single audio inference test + ↓ +Dataset validation + ↓ +JSON-style validation + ↓ +MQTT message handler validation + ↓ +ONNX export + ↓ +TensorFlow SavedModel conversion + ↓ +TFLite conversion + ↓ +TFLite inference validation + ↓ +Engine-side EfficientNetV2 TFLite inference path + ↓ +Fake MQTT message validation through on_message() + ↓ +Prototype Docker Engine model loading validation +``` + +## Current Status + +Completed: + +```text +EfficientNetV2 training and saving +Saved model loading test +Reusable predictor implementation +Single audio inference validation +Dataset validation +JSON-style inference validation +MQTT message handler validation +ONNX export +TensorFlow SavedModel conversion +TFLite conversion +TFLite inference validation +Engine-side EfficientNetV2 TFLite inference path +Fake MQTT message validation through on_message() +Prototype Docker Engine model loading validation +Engine-specific Docker requirements setup +EfficientNetV2 TFLite model successfully loaded inside Docker +``` + +Not completed yet: + +```text +Resolve MQTT broker hostname/service-name issue in Docker Compose +Send a real MQTT audio message through the Docker Engine container +Confirm the prediction result is sent successfully to the API/database +Decide whether to migrate the validated prototype changes into the real Components Engine path +``` + +## Important Notes + +The current work focuses on creating a functioning Engine-compatible inference pipeline. Model accuracy can be improved later by increasing training epochs, tuning parameters, adding better validation sampling, or using more production-like audio. + +The dataset validation currently tested 50 files. The first validation scan mainly covered the first dataset folders, so future validation can be improved by randomly sampling files across more species classes. + +The TFLite model was successfully generated with a file size of 85.19 MB. Because model artifacts such as `.pt`, `.onnx`, `.tflite`, and TensorFlow SavedModel folders are large, the team should confirm whether these should be committed directly to GitHub or managed using Git LFS or external storage. + +The current Engine-side integration was tested using a safe copied Engine file, `light_echo_engine_efficientnetv2_tflite.py`, so the original `light_echo_engine.py` remains unchanged. + +The copied Engine successfully loaded the EfficientNetV2 TFLite model and processed a fake MQTT-style message through `on_message()`. The model inference completed successfully, but the final API send step failed locally because `ts-api-cont` is only available inside the Docker/system network. + +The prototype Docker Engine validation also confirmed that the EfficientNetV2 TFLite model loads successfully inside the Docker Engine container with the expected input shape `[1, 1, 128, 313]` and output shape `[1, 123]`. The Engine reached the MQTT connection stage, but the broker hostname `mqtt-broker` could not be resolved, which is a Docker Compose networking/service-name issue rather than a model integration issue. + +--- diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/class_mapping.json b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/class_mapping.json new file mode 100644 index 000000000..3b64304cb --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/class_mapping.json @@ -0,0 +1,377 @@ +{ + "classes": [ + "Acanthiza chrysorrhoa", + "Acanthiza lineata", + "Acanthiza nana", + "Acanthiza pusilla", + "Acanthiza reguloides", + "Acanthiza uropygialis", + "Acanthorhynchus tenuirostris", + "Accipiter cirrocephalus", + "Aidemosyne modesta", + "Alauda arvensis", + "Anhinga novaehollandiae", + "Anthochaera phrygia", + "Antigone rubicunda", + "Artamus cinereus", + "Artamus cyanopterus", + "Artamus minor", + "Artamus superciliosus", + "Barnardius zonarius", + "Callocephalon fimbriatum", + "Calyptorhynchus banksii", + "Calyptorhynchus lathami", + "Capra Hircus", + "Carduelis carduelis", + "Carterornis leucotis", + "Cervus Unicolour", + "Ceyx azureus", + "Chenonetta jubata", + "Chlamydera nuchalis", + "Cincloramphus mathewsi", + "Cinclosoma punctatum", + "Cisticola exilis", + "Climacteris picumnus", + "Colluricincla harmonica", + "Conopophila albogularis", + "Cophixalus exiguus", + "Cophixalus infacetus", + "Cophixalus ornatus", + "Coracina novaehollandiae", + "Coracina papuensis", + "Corcorax melanorhamphos", + "Cormobates leucophaea", + "Corvus mellori", + "Coturnix pectoralis", + "Cygnus atratus", + "Dama Dama", + "Daphoenositta chrysoptera", + "Dasyurus maculatus", + "Dicaeum hirundinaceum", + "Egretta novaehollandiae", + "Elseyornis melanops", + "Entomyzon cyanotis", + "Eurostopodus argus", + "Eurostopodus mystacalis", + "Eurystomus orientalis", + "Falco berigora", + "Falco cenchroides", + "Falco peregrinus", + "Falcunculus frontatus", + "Felis Catus", + "Fulica atra", + "Gallinula tenebrosa", + "Geopelia cuneata", + "Gerygone mouki", + "Haliastur sphenurus", + "Irediparra gallinacea", + "Lalage leucomela", + "Litoria inermis", + "Manorina melanophrys", + "Megapodius reinwardt", + "Melithreptus brevirostris", + "Melithreptus gularis", + "Melithreptus lunatus", + "Microeca flavigaster", + "Neophema pulchella", + "Nesoptilotis leucotis", + "Pachycephala simplex", + "Pardalotus rubricatus", + "Parvipsitta pusilla", + "Pelecanus conspicillatus", + "Petrochelidon ariel", + "Petrochelidon nigricans", + "Petroica boodang", + "Petroica goodenovii", + "Petroica phoenicea", + "Petroica rosea", + "Pezoporus wallicus", + "Phaps elegans", + "Philemon citreogularis", + "Philemon corniculatus", + "Phylidonyris niger", + "Pitta iris", + "Pitta versicolor", + "Platycercus elegans", + "Plectorhyncha lanceolata", + "Psophodes cristatus", + "Ptilinopus regina", + "Pycnoptilus floccosus", + "Ramsayornis fasciatus", + "Ranoidea caerulea", + "Rattus Norvegicus", + "Rhinella marina", + "Rhipidura albiscapa", + "Rhipidura leucophrys", + "Rhipidura rufifrons", + "Rhipidura rufiventris", + "Scythrops novaehollandiae", + "Spilopelia chinensis", + "Stizoptera bichenovii", + "Stomiopera unicolor", + "Strepera versicolor", + "Sus Scrofa", + "Symposiachrus trivirgatus", + "Tregellasia capito", + "Trichosurus vulpecula", + "Uperoleia altissima", + "Uperoleia mimula", + "Vanellus miles", + "Vulpes vulpes", + "brant", + "jabwar", + "sheowl", + "spodov", + "wiltur" + ], + "label_to_index": { + "Acanthiza chrysorrhoa": 0, + "Acanthiza lineata": 1, + "Acanthiza nana": 2, + "Acanthiza pusilla": 3, + "Acanthiza reguloides": 4, + "Acanthiza uropygialis": 5, + "Acanthorhynchus tenuirostris": 6, + "Accipiter cirrocephalus": 7, + "Aidemosyne modesta": 8, + "Alauda arvensis": 9, + "Anhinga novaehollandiae": 10, + "Anthochaera phrygia": 11, + "Antigone rubicunda": 12, + "Artamus cinereus": 13, + "Artamus cyanopterus": 14, + "Artamus minor": 15, + "Artamus superciliosus": 16, + "Barnardius zonarius": 17, + "Callocephalon fimbriatum": 18, + "Calyptorhynchus banksii": 19, + "Calyptorhynchus lathami": 20, + "Capra Hircus": 21, + "Carduelis carduelis": 22, + "Carterornis leucotis": 23, + "Cervus Unicolour": 24, + "Ceyx azureus": 25, + "Chenonetta jubata": 26, + "Chlamydera nuchalis": 27, + "Cincloramphus mathewsi": 28, + "Cinclosoma punctatum": 29, + "Cisticola exilis": 30, + "Climacteris picumnus": 31, + "Colluricincla harmonica": 32, + "Conopophila albogularis": 33, + "Cophixalus exiguus": 34, + "Cophixalus infacetus": 35, + "Cophixalus ornatus": 36, + "Coracina novaehollandiae": 37, + "Coracina papuensis": 38, + "Corcorax melanorhamphos": 39, + "Cormobates leucophaea": 40, + "Corvus mellori": 41, + "Coturnix pectoralis": 42, + "Cygnus atratus": 43, + "Dama Dama": 44, + "Daphoenositta chrysoptera": 45, + "Dasyurus maculatus": 46, + "Dicaeum hirundinaceum": 47, + "Egretta novaehollandiae": 48, + "Elseyornis melanops": 49, + "Entomyzon cyanotis": 50, + "Eurostopodus argus": 51, + "Eurostopodus mystacalis": 52, + "Eurystomus orientalis": 53, + "Falco berigora": 54, + "Falco cenchroides": 55, + "Falco peregrinus": 56, + "Falcunculus frontatus": 57, + "Felis Catus": 58, + "Fulica atra": 59, + "Gallinula tenebrosa": 60, + "Geopelia cuneata": 61, + "Gerygone mouki": 62, + "Haliastur sphenurus": 63, + "Irediparra gallinacea": 64, + "Lalage leucomela": 65, + "Litoria inermis": 66, + "Manorina melanophrys": 67, + "Megapodius reinwardt": 68, + "Melithreptus brevirostris": 69, + "Melithreptus gularis": 70, + "Melithreptus lunatus": 71, + "Microeca flavigaster": 72, + "Neophema pulchella": 73, + "Nesoptilotis leucotis": 74, + "Pachycephala simplex": 75, + "Pardalotus rubricatus": 76, + "Parvipsitta pusilla": 77, + "Pelecanus conspicillatus": 78, + "Petrochelidon ariel": 79, + "Petrochelidon nigricans": 80, + "Petroica boodang": 81, + "Petroica goodenovii": 82, + "Petroica phoenicea": 83, + "Petroica rosea": 84, + "Pezoporus wallicus": 85, + "Phaps elegans": 86, + "Philemon citreogularis": 87, + "Philemon corniculatus": 88, + "Phylidonyris niger": 89, + "Pitta iris": 90, + "Pitta versicolor": 91, + "Platycercus elegans": 92, + "Plectorhyncha lanceolata": 93, + "Psophodes cristatus": 94, + "Ptilinopus regina": 95, + "Pycnoptilus floccosus": 96, + "Ramsayornis fasciatus": 97, + "Ranoidea caerulea": 98, + "Rattus Norvegicus": 99, + "Rhinella marina": 100, + "Rhipidura albiscapa": 101, + "Rhipidura leucophrys": 102, + "Rhipidura rufifrons": 103, + "Rhipidura rufiventris": 104, + "Scythrops novaehollandiae": 105, + "Spilopelia chinensis": 106, + "Stizoptera bichenovii": 107, + "Stomiopera unicolor": 108, + "Strepera versicolor": 109, + "Sus Scrofa": 110, + "Symposiachrus trivirgatus": 111, + "Tregellasia capito": 112, + "Trichosurus vulpecula": 113, + "Uperoleia altissima": 114, + "Uperoleia mimula": 115, + "Vanellus miles": 116, + "Vulpes vulpes": 117, + "brant": 118, + "jabwar": 119, + "sheowl": 120, + "spodov": 121, + "wiltur": 122 + }, + "index_to_label": { + "0": "Acanthiza chrysorrhoa", + "1": "Acanthiza lineata", + "2": "Acanthiza nana", + "3": "Acanthiza pusilla", + "4": "Acanthiza reguloides", + "5": "Acanthiza uropygialis", + "6": "Acanthorhynchus tenuirostris", + "7": "Accipiter cirrocephalus", + "8": "Aidemosyne modesta", + "9": "Alauda arvensis", + "10": "Anhinga novaehollandiae", + "11": "Anthochaera phrygia", + "12": "Antigone rubicunda", + "13": "Artamus cinereus", + "14": "Artamus cyanopterus", + "15": "Artamus minor", + "16": "Artamus superciliosus", + "17": "Barnardius zonarius", + "18": "Callocephalon fimbriatum", + "19": "Calyptorhynchus banksii", + "20": "Calyptorhynchus lathami", + "21": "Capra Hircus", + "22": "Carduelis carduelis", + "23": "Carterornis leucotis", + "24": "Cervus Unicolour", + "25": "Ceyx azureus", + "26": "Chenonetta jubata", + "27": "Chlamydera nuchalis", + "28": "Cincloramphus mathewsi", + "29": "Cinclosoma punctatum", + "30": "Cisticola exilis", + "31": "Climacteris picumnus", + "32": "Colluricincla harmonica", + "33": "Conopophila albogularis", + "34": "Cophixalus exiguus", + "35": "Cophixalus infacetus", + "36": "Cophixalus ornatus", + "37": "Coracina novaehollandiae", + "38": "Coracina papuensis", + "39": "Corcorax melanorhamphos", + "40": "Cormobates leucophaea", + "41": "Corvus mellori", + "42": "Coturnix pectoralis", + "43": "Cygnus atratus", + "44": "Dama Dama", + "45": "Daphoenositta chrysoptera", + "46": "Dasyurus maculatus", + "47": "Dicaeum hirundinaceum", + "48": "Egretta novaehollandiae", + "49": "Elseyornis melanops", + "50": "Entomyzon cyanotis", + "51": "Eurostopodus argus", + "52": "Eurostopodus mystacalis", + "53": "Eurystomus orientalis", + "54": "Falco berigora", + "55": "Falco cenchroides", + "56": "Falco peregrinus", + "57": "Falcunculus frontatus", + "58": "Felis Catus", + "59": "Fulica atra", + "60": "Gallinula tenebrosa", + "61": "Geopelia cuneata", + "62": "Gerygone mouki", + "63": "Haliastur sphenurus", + "64": "Irediparra gallinacea", + "65": "Lalage leucomela", + "66": "Litoria inermis", + "67": "Manorina melanophrys", + "68": "Megapodius reinwardt", + "69": "Melithreptus brevirostris", + "70": "Melithreptus gularis", + "71": "Melithreptus lunatus", + "72": "Microeca flavigaster", + "73": "Neophema pulchella", + "74": "Nesoptilotis leucotis", + "75": "Pachycephala simplex", + "76": "Pardalotus rubricatus", + "77": "Parvipsitta pusilla", + "78": "Pelecanus conspicillatus", + "79": "Petrochelidon ariel", + "80": "Petrochelidon nigricans", + "81": "Petroica boodang", + "82": "Petroica goodenovii", + "83": "Petroica phoenicea", + "84": "Petroica rosea", + "85": "Pezoporus wallicus", + "86": "Phaps elegans", + "87": "Philemon citreogularis", + "88": "Philemon corniculatus", + "89": "Phylidonyris niger", + "90": "Pitta iris", + "91": "Pitta versicolor", + "92": "Platycercus elegans", + "93": "Plectorhyncha lanceolata", + "94": "Psophodes cristatus", + "95": "Ptilinopus regina", + "96": "Pycnoptilus floccosus", + "97": "Ramsayornis fasciatus", + "98": "Ranoidea caerulea", + "99": "Rattus Norvegicus", + "100": "Rhinella marina", + "101": "Rhipidura albiscapa", + "102": "Rhipidura leucophrys", + "103": "Rhipidura rufifrons", + "104": "Rhipidura rufiventris", + "105": "Scythrops novaehollandiae", + "106": "Spilopelia chinensis", + "107": "Stizoptera bichenovii", + "108": "Stomiopera unicolor", + "109": "Strepera versicolor", + "110": "Sus Scrofa", + "111": "Symposiachrus trivirgatus", + "112": "Tregellasia capito", + "113": "Trichosurus vulpecula", + "114": "Uperoleia altissima", + "115": "Uperoleia mimula", + "116": "Vanellus miles", + "117": "Vulpes vulpes", + "118": "brant", + "119": "jabwar", + "120": "sheowl", + "121": "spodov", + "122": "wiltur" + } +} \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.onnx b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.onnx new file mode 100644 index 000000000..674f6ddf2 Binary files /dev/null and b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.onnx differ diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.pt b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.pt new file mode 100644 index 000000000..34cf79dfe Binary files /dev/null and b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.pt differ diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.tflite b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.tflite new file mode 100644 index 000000000..2059399c3 Binary files /dev/null and b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/efficientnetv2_project_echo.tflite differ diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/preprocess_config.json b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/preprocess_config.json new file mode 100644 index 000000000..a4cb71f4c --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/preprocess_config.json @@ -0,0 +1,15 @@ +{ + "target_sr": 32000, + "duration_s": 5.0, + "n_mels": 128, + "hop_length": 512, + "fmin": 20, + "fmax": 14000, + "feature_type": "mel_spectrogram_db", + "normalisation": "per_sample_standardisation", + "input_shape": [ + 1, + 128, + "time" + ] +} \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/training_metrics.json b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/training_metrics.json new file mode 100644 index 000000000..a81c07508 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/_trained_models/training_metrics.json @@ -0,0 +1,29 @@ +{ + "model": "EfficientNetV2", + "model_name": "efficientnetv2_rw_s", + "test_accuracy": 0.8724018475750578, + "f1_macro": 0.843222466600867, + "train_size": 6927, + "test_size": 1732, + "num_classes": 123, + "epochs": 3, + "batch_size": 16, + "learning_rate": 0.0001, + "training_history": [ + { + "epoch": 1, + "train_loss": 2.679424368855033, + "train_accuracy": 0.42226071892594197 + }, + { + "epoch": 2, + "train_loss": 0.8290824525599289, + "train_accuracy": 0.8049660747798469 + }, + { + "epoch": 3, + "train_loss": 0.25007590935144036, + "train_accuracy": 0.9487512631730908 + } + ] +} \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/convert_saved_efficientnetv2_onnx_to_tflite.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/convert_saved_efficientnetv2_onnx_to_tflite.py new file mode 100644 index 000000000..fbea4305e --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/convert_saved_efficientnetv2_onnx_to_tflite.py @@ -0,0 +1,68 @@ +""" +Convert the saved Project Echo EfficientNetV2 ONNX model to TFLite. + +This script converts: +PyTorch saved model -> ONNX -> TensorFlow SavedModel -> TFLite + +The ONNX model should already be created by: +export_saved_efficientnetv2_to_onnx.py +""" + +import pathlib +import onnx +import tensorflow as tf +from onnx_tf.backend import prepare + + +BASE_DIR = pathlib.Path(__file__).resolve().parent +MODEL_DIR = BASE_DIR / "_trained_models" + +ONNX_MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.onnx" +SAVED_MODEL_DIR = MODEL_DIR / "efficientnetv2_project_echo_saved_model" +TFLITE_MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.tflite" + + +def convert_onnx_to_saved_model(): + print("Loading ONNX model...") + onnx_model = onnx.load(ONNX_MODEL_PATH) + + print("Converting ONNX to TensorFlow SavedModel...") + tf_rep = prepare(onnx_model) + tf_rep.export_graph(str(SAVED_MODEL_DIR)) + + print("TensorFlow SavedModel saved to:") + print(SAVED_MODEL_DIR) + + +def convert_saved_model_to_tflite(): + print("Converting TensorFlow SavedModel to TFLite...") + + converter = tf.lite.TFLiteConverter.from_saved_model(str(SAVED_MODEL_DIR)) + + # Basic float32 TFLite conversion first. + # Quantisation can be added later if needed. + tflite_model = converter.convert() + + with open(TFLITE_MODEL_PATH, "wb") as f: + f.write(tflite_model) + + print("TFLite model saved to:") + print(TFLITE_MODEL_PATH) + + +def main(): + if not ONNX_MODEL_PATH.exists(): + raise FileNotFoundError(f"ONNX model not found: {ONNX_MODEL_PATH}") + + convert_onnx_to_saved_model() + convert_saved_model_to_tflite() + + if TFLITE_MODEL_PATH.exists(): + size_mb = TFLITE_MODEL_PATH.stat().st_size / (1024 * 1024) + print(f"TFLite conversion completed successfully. File size: {size_mb:.2f} MB") + else: + raise RuntimeError("TFLite file was not created.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_dataset_validation_results.csv b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_dataset_validation_results.csv new file mode 100644 index 000000000..658734a3a --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_dataset_validation_results.csv @@ -0,0 +1,51 @@ +audio_path,actual_label,predicted_label,confidence,correct +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_11.250-13.250.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9770649671554565,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_11.750-13.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9973547458648682,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_12.800-14.800.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9857408404350281,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_13.250-15.250.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9886628985404968,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_14.750-16.050.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.6044153571128845,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_16.750-18.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9494422674179077,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_17.600-19.600.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9919753670692444,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_17.750-19.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9930353760719299,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_17.750-19.750.wav,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9931343793869019,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_18.500-20.500.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.2879418432712555,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_22.750-24.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9764229655265808,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_24.750-26.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9784281849861145,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_28.750-30.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.8369612097740173,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_3.650-4.900.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9337119460105896,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_4.650-6.650.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9372029304504395,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_41.750-43.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.7937591671943665,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_43.750-45.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9964704513549805,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_44.000-46.000.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.5156345367431641,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_53.750-55.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9752287864685059,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_55.750-57.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9251630902290344,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_57.750-59.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.897212564945221,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_59.750-61.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9712151885032654,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_61.750-63.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9747024774551392,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_63.750-65.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9932301044464111,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_65.750-67.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9752557873725891,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_70.000-72.000.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9092298150062561,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_72.650-74.650.mp3,Acanthiza chrysorrhoa,Acanthorhynchus tenuirostris,0.3176240622997284,False +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_72.650-74.650.wav,Acanthiza chrysorrhoa,Acanthorhynchus tenuirostris,0.3172307312488556,False +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_74.000-76.000.mp3,Acanthiza chrysorrhoa,Rhipidura albiscapa,0.36642369627952576,False +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_75.750-77.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9858733415603638,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_75.750-77.750.wav,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9857967495918274,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_84.500-86.500.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9440171122550964,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_9.250-11.250.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9827945828437805,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_95.750-97.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9877018332481384,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_0.700-2.700.mp3,Acanthiza lineata,Acanthiza lineata,0.9033298492431641,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_10.050-12.050.mp3,Acanthiza lineata,Acanthiza lineata,0.9026768803596497,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_12.050-14.050.mp3,Acanthiza lineata,Acanthiza lineata,0.7625439763069153,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_16.000-18.000.mp3,Acanthiza lineata,Acanthiza lineata,0.947208821773529,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_18.000-20.000.mp3,Acanthiza lineata,Acanthiza lineata,0.8150545358657837,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_18.050-20.050.mp3,Acanthiza lineata,Acanthiza lineata,0.9321537017822266,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_2.700-4.700.mp3,Acanthiza lineata,Acanthiza lineata,0.9598342776298523,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_2.700-4.700.wav,Acanthiza lineata,Acanthiza lineata,0.9597043991088867,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_20.050-22.050.mp3,Acanthiza lineata,Acanthiza lineata,0.9585906863212585,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_22.050-24.050.mp3,Acanthiza lineata,Acanthiza lineata,0.8344961404800415,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_22.900-24.900.mp3,Acanthiza lineata,Acanthiza lineata,0.979550302028656,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_24.050-26.050.mp3,Acanthiza lineata,Acanthiza lineata,0.845700740814209,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_26.900-28.900.mp3,Acanthiza lineata,Acanthiza lineata,0.9725697636604309,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_30.050-32.050.mp3,Acanthiza lineata,Acanthiza lineata,0.9205486178398132,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_32.000-34.000.mp3,Acanthiza lineata,Acanthiza lineata,0.2848418056964874,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_34.000-36.000.mp3,Acanthiza lineata,Acanthiza lineata,0.9507287740707397,True diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_json_inference_result.json b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_json_inference_result.json new file mode 100644 index 000000000..055164dfe --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_json_inference_result.json @@ -0,0 +1,40 @@ +{ + "device_id": "sensor_001", + "input_timestamp": "2026-04-27T15:30:00", + "processed_timestamp": "2026-04-27T19:13:45", + "model": "EfficientNetV2", + "input_audio": "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3", + "prediction": { + "label": "Acanthiza chrysorrhoa", + "class_index": 0, + "confidence": 0.9337119460105896 + }, + "top_predictions": [ + { + "index": 0, + "label": "Acanthiza chrysorrhoa", + "confidence": 0.9337119460105896 + }, + { + "index": 47, + "label": "Dicaeum hirundinaceum", + "confidence": 0.02223811112344265 + }, + { + "index": 3, + "label": "Acanthiza pusilla", + "confidence": 0.007062176708132029 + }, + { + "index": 5, + "label": "Acanthiza uropygialis", + "confidence": 0.006129274610430002 + }, + { + "index": 103, + "label": "Rhipidura rufifrons", + "confidence": 0.0059699914418160915 + } + ], + "status": "success" +} \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_mqtt_message_handler_result.json b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_mqtt_message_handler_result.json new file mode 100644 index 000000000..4c9851c04 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_mqtt_message_handler_result.json @@ -0,0 +1,40 @@ +{ + "device_id": "sensor_001", + "input_timestamp": "2026-04-27T15:30:00", + "processed_timestamp": "2026-04-27T19:18:01", + "model": "EfficientNetV2", + "input_audio": "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3", + "prediction": { + "label": "Acanthiza chrysorrhoa", + "class_index": 0, + "confidence": 0.9337119460105896 + }, + "top_predictions": [ + { + "index": 0, + "label": "Acanthiza chrysorrhoa", + "confidence": 0.9337119460105896 + }, + { + "index": 47, + "label": "Dicaeum hirundinaceum", + "confidence": 0.02223811112344265 + }, + { + "index": 3, + "label": "Acanthiza pusilla", + "confidence": 0.007062176708132029 + }, + { + "index": 5, + "label": "Acanthiza uropygialis", + "confidence": 0.006129274610430002 + }, + { + "index": 103, + "label": "Rhipidura rufifrons", + "confidence": 0.0059699914418160915 + } + ], + "status": "success" +} \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_predictor.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_predictor.py new file mode 100644 index 000000000..2df0cad25 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_predictor.py @@ -0,0 +1,255 @@ +""" +EfficientNetV2 predictor utilities for Project Echo Engine integration. + +This file loads a saved EfficientNetV2 model, class mapping, and preprocessing +configuration, then runs inference on a single audio file. + +Expected saved files: + _trained_models/ + efficientnetv2_project_echo.pt + class_mapping.json + preprocess_config.json + training_metrics.json + +Main functions: + load_model() + load_class_mapping() + load_preprocess_config() + preprocess_audio() + predict_audio() +""" + +from __future__ import annotations + +import json +from pathlib import Path +from typing import Any, Dict, Optional, Tuple + +import librosa +import numpy as np +import torch +import timm + + +def load_class_mapping(class_mapping_path: str | Path) -> Dict[str, Any]: + """ + Load the class mapping JSON file. + + The expected format is: + { + "classes": [...], + "label_to_index": {...}, + "index_to_label": {"0": "...", "1": "..."} + } + """ + class_mapping_path = Path(class_mapping_path) + + if not class_mapping_path.exists(): + raise FileNotFoundError(f"Class mapping file not found: {class_mapping_path}") + + with open(class_mapping_path, "r", encoding="utf-8") as f: + return json.load(f) + + +def load_preprocess_config(preprocess_config_path: str | Path) -> Dict[str, Any]: + """ + Load the preprocessing configuration JSON file. + + This should contain the same audio preprocessing settings used during training. + """ + preprocess_config_path = Path(preprocess_config_path) + + if not preprocess_config_path.exists(): + raise FileNotFoundError(f"Preprocessing config file not found: {preprocess_config_path}") + + with open(preprocess_config_path, "r", encoding="utf-8") as f: + return json.load(f) + + +def load_model( + model_path: str | Path, + device: Optional[str] = None +) -> Tuple[torch.nn.Module, str]: + """ + Load the saved EfficientNetV2 model. + + Args: + model_path: Path to efficientnetv2_project_echo.pt + device: Optional device string, e.g. "cpu" or "cuda" + + Returns: + model: Loaded PyTorch model in eval mode + device: Device used for inference + """ + model_path = Path(model_path) + + if not model_path.exists(): + raise FileNotFoundError(f"Model file not found: {model_path}") + + if device is None: + device = "cuda" if torch.cuda.is_available() else "cpu" + + checkpoint = torch.load(model_path, map_location=device) + + required_keys = ["model_state_dict", "model_name", "num_classes", "in_chans"] + missing_keys = [key for key in required_keys if key not in checkpoint] + if missing_keys: + raise KeyError(f"Model checkpoint is missing required keys: {missing_keys}") + + model = timm.create_model( + checkpoint["model_name"], + pretrained=False, + num_classes=checkpoint["num_classes"], + in_chans=checkpoint["in_chans"], + ) + + model.load_state_dict(checkpoint["model_state_dict"]) + model = model.to(device) + model.eval() + + return model, device + + +def load_audio_fixed( + audio_path: str | Path, + target_sr: int, + duration_s: float +) -> Tuple[np.ndarray, int]: + """ + Load audio as mono, resample it, and crop/pad to a fixed duration. + """ + audio_path = Path(audio_path) + + if not audio_path.exists(): + raise FileNotFoundError(f"Audio file not found: {audio_path}") + + y, sr = librosa.load(str(audio_path), sr=target_sr, mono=True) + + target_len = int(duration_s * target_sr) + + if len(y) < target_len: + y = np.pad(y, (0, target_len - len(y))) + else: + y = y[:target_len] + + return y, target_sr + + +def preprocess_audio( + audio_path: str | Path, + preprocess_config: Dict[str, Any] +) -> torch.Tensor: + """ + Convert an audio file into the Mel spectrogram tensor expected by EfficientNetV2. + + Output shape: + [1, 1, n_mels, time] + """ + required_keys = ["target_sr", "duration_s", "n_mels", "hop_length", "fmin", "fmax"] + missing_keys = [key for key in required_keys if key not in preprocess_config] + if missing_keys: + raise KeyError(f"Preprocessing config is missing required keys: {missing_keys}") + + y, sr = load_audio_fixed( + audio_path, + target_sr=preprocess_config["target_sr"], + duration_s=preprocess_config["duration_s"], + ) + + mel = librosa.feature.melspectrogram( + y=y, + sr=sr, + n_mels=preprocess_config["n_mels"], + hop_length=preprocess_config["hop_length"], + fmin=preprocess_config["fmin"], + fmax=preprocess_config["fmax"], + ) + + mel_db = librosa.power_to_db(mel, ref=np.max).astype(np.float32) + + # Same per-sample normalisation used in the training notebook. + mel_db = (mel_db - np.mean(mel_db)) / (np.std(mel_db) + 1e-6) + + # Shape: [batch, channel, n_mels, time] + tensor = torch.tensor(mel_db, dtype=torch.float32).unsqueeze(0).unsqueeze(0) + + return tensor + + +@torch.no_grad() +def predict_audio( + audio_path: str | Path, + model: torch.nn.Module, + class_mapping: Dict[str, Any], + preprocess_config: Dict[str, Any], + device: Optional[str] = None, +) -> Dict[str, Any]: + """ + Predict the species/class for one audio file. + + Returns: + { + "audio_path": "...", + "predicted_index": int, + "predicted_label": str, + "confidence": float, + "top_predictions": [ + {"index": int, "label": str, "confidence": float}, + ... + ] + } + """ + if device is None: + device = "cuda" if torch.cuda.is_available() else "cpu" + + if "index_to_label" not in class_mapping: + raise KeyError("class_mapping must contain 'index_to_label'.") + + index_to_label = class_mapping["index_to_label"] + + x = preprocess_audio(audio_path, preprocess_config) + x = x.to(device) + + outputs = model(x) + probs = torch.softmax(outputs, dim=1).detach().cpu().numpy()[0] + + pred_index = int(np.argmax(probs)) + confidence = float(probs[pred_index]) + pred_label = index_to_label[str(pred_index)] + + # Top 5 predictions, or fewer if there are fewer than 5 classes. + top_k = min(5, len(probs)) + top_indices = np.argsort(probs)[::-1][:top_k] + + top_predictions = [ + { + "index": int(idx), + "label": index_to_label[str(int(idx))], + "confidence": float(probs[idx]), + } + for idx in top_indices + ] + + return { + "audio_path": str(audio_path), + "predicted_index": pred_index, + "predicted_label": pred_label, + "confidence": confidence, + "top_predictions": top_predictions, + } + + +def load_predictor_bundle( + model_path: str | Path, + class_mapping_path: str | Path, + preprocess_config_path: str | Path, + device: Optional[str] = None, +) -> Tuple[torch.nn.Module, Dict[str, Any], Dict[str, Any], str]: + """ + Convenience function to load model, class mapping, and preprocessing config together. + """ + model, device = load_model(model_path, device=device) + class_mapping = load_class_mapping(class_mapping_path) + preprocess_config = load_preprocess_config(preprocess_config_path) + + return model, class_mapping, preprocess_config, device diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_tflite_dataset_validation_results.csv b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_tflite_dataset_validation_results.csv new file mode 100644 index 000000000..49653afe3 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_tflite_dataset_validation_results.csv @@ -0,0 +1,51 @@ +audio_path,actual_label,predicted_label,confidence,correct +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_11.250-13.250.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9770646095275879,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_11.750-13.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9973547458648682,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_12.800-14.800.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9857407212257385,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_13.250-15.250.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9886627793312073,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_14.750-16.050.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.6044183373451233,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_16.750-18.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9494422674179077,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_17.600-19.600.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9919752478599548,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_17.750-19.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9930353164672852,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_17.750-19.750.wav,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9931344985961914,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_18.500-20.500.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.2879422903060913,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_22.750-24.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9764230847358704,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_24.750-26.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.978428304195404,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_28.750-30.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.8369632363319397,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_3.650-4.900.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9337119460105896,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_4.650-6.650.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9372023940086365,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_41.750-43.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.7937611937522888,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_43.750-45.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9964703321456909,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_44.000-46.000.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.5156335234642029,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_53.750-55.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9752292633056641,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_55.750-57.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9251631498336792,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_57.750-59.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.8972120881080627,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_59.750-61.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9712159633636475,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_61.750-63.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9747023582458496,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_63.750-65.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9932301044464111,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_65.750-67.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9752556681632996,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_70.000-72.000.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9092291593551636,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_72.650-74.650.mp3,Acanthiza chrysorrhoa,Acanthorhynchus tenuirostris,0.3176233470439911,False +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_72.650-74.650.wav,Acanthiza chrysorrhoa,Acanthorhynchus tenuirostris,0.3172329366207123,False +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_74.000-76.000.mp3,Acanthiza chrysorrhoa,Rhipidura albiscapa,0.36641833186149597,False +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_75.750-77.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9858735799789429,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_75.750-77.750.wav,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9857966303825378,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_84.500-86.500.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9440176486968994,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_9.250-11.250.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9827941656112671,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_95.750-97.750.mp3,Acanthiza chrysorrhoa,Acanthiza chrysorrhoa,0.9877020716667175,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_0.700-2.700.mp3,Acanthiza lineata,Acanthiza lineata,0.9033257365226746,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_10.050-12.050.mp3,Acanthiza lineata,Acanthiza lineata,0.9026774764060974,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_12.050-14.050.mp3,Acanthiza lineata,Acanthiza lineata,0.7625426650047302,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_16.000-18.000.mp3,Acanthiza lineata,Acanthiza lineata,0.9472092390060425,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_18.000-20.000.mp3,Acanthiza lineata,Acanthiza lineata,0.8150554895401001,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_18.050-20.050.mp3,Acanthiza lineata,Acanthiza lineata,0.93215411901474,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_2.700-4.700.mp3,Acanthiza lineata,Acanthiza lineata,0.9598336219787598,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_2.700-4.700.wav,Acanthiza lineata,Acanthiza lineata,0.9597044587135315,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_20.050-22.050.mp3,Acanthiza lineata,Acanthiza lineata,0.9585901498794556,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_22.050-24.050.mp3,Acanthiza lineata,Acanthiza lineata,0.8344961404800415,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_22.900-24.900.mp3,Acanthiza lineata,Acanthiza lineata,0.9795499444007874,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_24.050-26.050.mp3,Acanthiza lineata,Acanthiza lineata,0.8457003235816956,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_26.900-28.900.mp3,Acanthiza lineata,Acanthiza lineata,0.9725702404975891,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_30.050-32.050.mp3,Acanthiza lineata,Acanthiza lineata,0.920547604560852,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_32.000-34.000.mp3,Acanthiza lineata,Acanthiza lineata,0.2848412096500397,True +C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza lineata\region_34.000-36.000.mp3,Acanthiza lineata,Acanthiza lineata,0.9507286548614502,True diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_tflite_single_audio_comparison.json b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_tflite_single_audio_comparison.json new file mode 100644 index 000000000..56972d201 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/efficientnetv2_tflite_single_audio_comparison.json @@ -0,0 +1,80 @@ +{ + "sample_audio": "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3", + "pytorch_result": { + "audio_path": "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3", + "predicted_index": 0, + "predicted_label": "Acanthiza chrysorrhoa", + "confidence": 0.9337119460105896, + "top_predictions": [ + { + "index": 0, + "label": "Acanthiza chrysorrhoa", + "confidence": 0.9337119460105896 + }, + { + "index": 47, + "label": "Dicaeum hirundinaceum", + "confidence": 0.02223811112344265 + }, + { + "index": 3, + "label": "Acanthiza pusilla", + "confidence": 0.007062176708132029 + }, + { + "index": 5, + "label": "Acanthiza uropygialis", + "confidence": 0.006129274610430002 + }, + { + "index": 103, + "label": "Rhipidura rufifrons", + "confidence": 0.0059699914418160915 + } + ] + }, + "tflite_result": { + "audio_path": "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3", + "predicted_index": 0, + "predicted_label": "Acanthiza chrysorrhoa", + "confidence": 0.9337119460105896, + "top_predictions": [ + { + "index": 0, + "label": "Acanthiza chrysorrhoa", + "confidence": 0.9337119460105896 + }, + { + "index": 47, + "label": "Dicaeum hirundinaceum", + "confidence": 0.022238129749894142 + }, + { + "index": 3, + "label": "Acanthiza pusilla", + "confidence": 0.007062216755002737 + }, + { + "index": 5, + "label": "Acanthiza uropygialis", + "confidence": 0.00612929742783308 + }, + { + "index": 103, + "label": "Rhipidura rufifrons", + "confidence": 0.005970010533928871 + } + ], + "raw_output_shape": [ + 1, + 123 + ], + "tflite_input_shape": [ + 1, + 1, + 128, + 313 + ] + }, + "labels_match": true +} \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/export_saved_efficientnetv2_to_onnx.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/export_saved_efficientnetv2_to_onnx.py new file mode 100644 index 000000000..a78fdbdeb --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/export_saved_efficientnetv2_to_onnx.py @@ -0,0 +1,84 @@ +""" +Export the saved Project Echo EfficientNetV2 model to ONNX. + +This script uses the EfficientNetV2 model trained and saved in the current +Engine integration workflow. +""" + +import pathlib + +import torch +import timm +import onnx + + +BASE_DIR = pathlib.Path(__file__).resolve().parent +MODEL_DIR = BASE_DIR / "_trained_models" + +MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.pt" +ONNX_OUTPUT_PATH = MODEL_DIR / "efficientnetv2_project_echo.onnx" + +DEVICE = "cpu" + + +def load_saved_model_for_export(model_path): + checkpoint = torch.load(model_path, map_location=DEVICE) + + model = timm.create_model( + checkpoint["model_name"], + pretrained=False, + num_classes=checkpoint["num_classes"], + in_chans=checkpoint["in_chans"] + ) + + model.load_state_dict(checkpoint["model_state_dict"]) + model = model.to(DEVICE) + model.eval() + + return model, checkpoint + + +def export_to_onnx(): + print("Loading saved EfficientNetV2 model...") + model, checkpoint = load_saved_model_for_export(MODEL_PATH) + + print("Model loaded successfully.") + print("Model name:", checkpoint["model_name"]) + print("Number of classes:", checkpoint["num_classes"]) + + # Shape must match our preprocessing: + # batch, channel, n_mels, time_bins + dummy_input = torch.randn(1, 1, 128, 313, device=DEVICE) + + print("Exporting to ONNX...") + torch.onnx.export( + model, + dummy_input, + ONNX_OUTPUT_PATH, + export_params=True, + opset_version=13, + do_constant_folding=True, + input_names=["input"], + output_names=["output"], + dynamic_axes={ + "input": {0: "batch_size"}, + "output": {0: "batch_size"} + }, + dynamo=False, + ) + + print("ONNX model saved to:") + print(ONNX_OUTPUT_PATH) + + print("Checking ONNX model...") + onnx_model = onnx.load(ONNX_OUTPUT_PATH) + onnx.checker.check_model(onnx_model) + + for opset in onnx_model.opset_import: + print("Opset:", opset.version) + + print("ONNX export completed successfully.") + + +if __name__ == "__main__": + export_to_onnx() \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/project_echo_train_save_efficientnetv2.ipynb b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/project_echo_train_save_efficientnetv2.ipynb new file mode 100644 index 000000000..8bac49b11 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/project_echo_train_save_efficientnetv2.ipynb @@ -0,0 +1,1519 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "29342f53", + "metadata": {}, + "source": [ + "# Project Echo - Train and Save EfficientNetV2 Model\n", + "\n", + "**Goal:** reload the audio dataset, extract Mel spectrogram features, train the selected EfficientNetV2 model, evaluate it, and save the model files needed for the Engine pipeline.\n", + "\n", + "This notebook produces:\n", + "\n", + "- `efficientnetv2_project_echo.pt`\n", + "- `class_mapping.json`\n", + "- `preprocess_config.json`\n", + "- `training_metrics.json`\n", + "- optional feature cache under `_feature_cache`\n", + "\n", + "Run the cells in order.\n" + ] + }, + { + "cell_type": "markdown", + "id": "e581ab78", + "metadata": {}, + "source": [ + "## 1. Install required packages\n", + "\n", + "Run this once if your environment does not already have these packages.\n", + "\n", + "`librosa==0.9.2` is used because newer versions previously caused kernel issues in the Project Echo benchmark work.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a2c42171", + "metadata": {}, + "outputs": [], + "source": [ + "# Run this only if needed.\n", + "# If packages are already installed, you can skip this cell.\n", + "\n", + "!pip install librosa==0.9.2 timm scikit-learn tqdm soundfile\n" + ] + }, + { + "cell_type": "markdown", + "id": "e2825f6b", + "metadata": {}, + "source": [ + "## 2. Imports\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "7b3aac4a", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import json\n", + "import time\n", + "import pathlib\n", + "import warnings\n", + "\n", + "warnings.filterwarnings(\"ignore\")\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import librosa\n", + "\n", + "from tqdm import tqdm\n", + "\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.preprocessing import LabelEncoder\n", + "from sklearn.metrics import accuracy_score, f1_score, classification_report, confusion_matrix\n", + "\n", + "import torch\n", + "from torch import nn\n", + "from torch.utils.data import Dataset, DataLoader\n", + "\n", + "import timm\n" + ] + }, + { + "cell_type": "markdown", + "id": "b0f94d60", + "metadata": {}, + "source": [ + "## 3. Configuration\n", + "\n", + "Change `DATA_ROOT` to your local Project Echo dataset path.\n", + "\n", + "Expected structure:\n", + "\n", + "```text\n", + "data_files/\n", + " class_or_species_name_1/\n", + " audio1.wav\n", + " class_or_species_name_2/\n", + " audio2.wav\n", + "```\n", + "\n", + "If your dataset has another structure, this notebook still scans all subfolders and uses the **parent folder name** as the label.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "286240ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data root: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\n", + "Feature cache: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\_feature_cache\n", + "Model output folder: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\n" + ] + } + ], + "source": [ + "# EDIT THIS PATH IF NEEDED\n", + "DATA_ROOT = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\"\n", + "\n", + "VALID_EXTS = {\".wav\", \".mp3\", \".flac\", \".ogg\"}\n", + "\n", + "# Same feature settings used in previous benchmark work\n", + "TARGET_SR = 32000\n", + "DURATION_S = 5.0\n", + "N_MELS = 128\n", + "HOP_LENGTH = 512\n", + "FMIN = 20\n", + "FMAX = 14000\n", + "\n", + "TEST_SIZE = 0.2\n", + "RANDOM_STATE = 42\n", + "\n", + "# Training settings\n", + "EPOCHS = 3 # Start with 3 to confirm it works. Increase later, e.g. 10.\n", + "BATCH_SIZE = 16\n", + "LEARNING_RATE = 1e-4\n", + "\n", + "DATA_ROOT = pathlib.Path(DATA_ROOT)\n", + "\n", + "CACHE_DIR = DATA_ROOT / \"_feature_cache\"\n", + "CACHE_DIR.mkdir(exist_ok=True)\n", + "\n", + "MODEL_DIR = pathlib.Path(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\"\n", + ")\n", + "MODEL_DIR.mkdir(exist_ok=True)\n", + "\n", + "print(\"Data root:\", DATA_ROOT)\n", + "print(\"Feature cache:\", CACHE_DIR)\n", + "print(\"Model output folder:\", MODEL_DIR)\n" + ] + }, + { + "cell_type": "markdown", + "id": "4b5594c3", + "metadata": {}, + "source": [ + "## 4. Scan dataset\n", + "\n", + "This scans all audio files and creates a table with file path and label.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "95534e8e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Found 8659 audio files\n", + "Found 123 classes\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
pathlabel
0C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Acanthiza chrysorrhoa
1C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Acanthiza chrysorrhoa
2C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Acanthiza chrysorrhoa
3C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Acanthiza chrysorrhoa
4C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Acanthiza chrysorrhoa
\n", + "
" + ], + "text/plain": [ + " path label\n", + "0 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Acanthiza chrysorrhoa\n", + "1 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Acanthiza chrysorrhoa\n", + "2 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Acanthiza chrysorrhoa\n", + "3 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Acanthiza chrysorrhoa\n", + "4 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Acanthiza chrysorrhoa" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "label\n", + "Rhipidura leucophrys 649\n", + "Colluricincla harmonica 610\n", + "Phylidonyris niger 543\n", + "Dasyurus maculatus 517\n", + "Rhipidura albiscapa 445\n", + "Acanthiza pusilla 249\n", + "Litoria inermis 214\n", + "Cisticola exilis 214\n", + "Barnardius zonarius 205\n", + "Cophixalus infacetus 174\n", + "Philemon citreogularis 171\n", + "Acanthiza reguloides 160\n", + "Cincloramphus mathewsi 150\n", + "Acanthiza nana 147\n", + "brant 135\n", + "sheowl 128\n", + "Petroica goodenovii 126\n", + "Haliastur sphenurus 119\n", + "Philemon corniculatus 112\n", + "spodov 107\n", + "Vulpes vulpes 106\n", + "Rattus Norvegicus 105\n", + "Acanthorhynchus tenuirostris 103\n", + "Melithreptus gularis 101\n", + "Rhipidura rufifrons 93\n", + "Accipiter cirrocephalus 89\n", + "Carterornis leucotis 79\n", + "jabwar 78\n", + "wiltur 76\n", + "Cinclosoma punctatum 74\n", + "Name: count, dtype: int64" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def scan_audio_dataset(root, exts):\n", + " rows = []\n", + " root = pathlib.Path(root)\n", + "\n", + " if not root.exists():\n", + " raise FileNotFoundError(f\"DATA_ROOT does not exist: {root}\")\n", + "\n", + " ignored_folders = {\"_feature_cache\", \"_trained_models\", \"__pycache__\"}\n", + "\n", + " for path in root.rglob(\"*\"):\n", + " if path.is_file() and path.suffix.lower() in exts:\n", + " if any(part in ignored_folders for part in path.parts):\n", + " continue\n", + "\n", + " label = path.parent.name\n", + "\n", + " rows.append({\n", + " \"path\": str(path),\n", + " \"label\": label\n", + " })\n", + "\n", + " df = pd.DataFrame(rows)\n", + "\n", + " if df.empty:\n", + " raise ValueError(\n", + " \"No audio files found. Check DATA_ROOT and VALID_EXTS.\"\n", + " )\n", + "\n", + " return df\n", + "\n", + "\n", + "files_df = scan_audio_dataset(DATA_ROOT, VALID_EXTS)\n", + "\n", + "print(f\"Found {len(files_df)} audio files\")\n", + "print(f\"Found {files_df['label'].nunique()} classes\")\n", + "display(files_df.head())\n", + "display(files_df[\"label\"].value_counts().head(30))\n" + ] + }, + { + "cell_type": "markdown", + "id": "f407c00b", + "metadata": {}, + "source": [ + "## 5. Feature extraction functions\n", + "\n", + "Each audio file is loaded, cropped or padded to 5 seconds, converted to a Mel spectrogram, and saved as `.npy` in `_feature_cache`.\n", + "\n", + "This means if the notebook is restarted, already extracted features do not need to be extracted again.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9faad307", + "metadata": {}, + "outputs": [], + "source": [ + "def load_audio_fixed(path, target_sr=TARGET_SR, duration_s=DURATION_S):\n", + " y, sr = librosa.load(path, sr=target_sr, mono=True)\n", + "\n", + " target_len = int(duration_s * target_sr)\n", + "\n", + " if len(y) < target_len:\n", + " y = np.pad(y, (0, target_len - len(y)))\n", + " else:\n", + " y = y[:target_len]\n", + "\n", + " return y, target_sr\n", + "\n", + "\n", + "def melspec(y, sr):\n", + " S = librosa.feature.melspectrogram(\n", + " y=y,\n", + " sr=sr,\n", + " n_mels=N_MELS,\n", + " hop_length=HOP_LENGTH,\n", + " fmin=FMIN,\n", + " fmax=FMAX\n", + " )\n", + "\n", + " S_db = librosa.power_to_db(S, ref=np.max).astype(np.float32)\n", + " return S_db\n", + "\n", + "\n", + "def feature_path_for(audio_path):\n", + " audio_path = pathlib.Path(audio_path)\n", + "\n", + " try:\n", + " safe_name = str(audio_path.relative_to(DATA_ROOT))\n", + " except ValueError:\n", + " safe_name = audio_path.name\n", + "\n", + " safe_name = safe_name.replace(\"\\\\\", \"_\").replace(\"/\", \"_\").replace(\":\", \"\")\n", + " return CACHE_DIR / f\"{safe_name}.npy\"\n", + "\n", + "\n", + "def extract_feature_cached(audio_path):\n", + " fp = feature_path_for(audio_path)\n", + "\n", + " if fp.exists():\n", + " return np.load(fp)\n", + "\n", + " y, sr = load_audio_fixed(audio_path)\n", + " feat = melspec(y, sr)\n", + "\n", + " np.save(fp, feat)\n", + " return feat\n" + ] + }, + { + "cell_type": "markdown", + "id": "a32039ba", + "metadata": {}, + "source": [ + "## 6. Extract features\n", + "\n", + "Run this cell to recreate all feature files. This may take time depending on the dataset size.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "34a556fb", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Extracting and caching features: 100%|█████████████████████████████████████████████| 8659/8659 [16:32<00:00, 8.72it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Feature extraction completed.\n", + "Successful files: 8659\n", + "Failed files: 0\n", + "Cache folder: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\_feature_cache\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "X_paths = files_df[\"path\"].tolist()\n", + "y_labels = files_df[\"label\"].tolist()\n", + "\n", + "failed_files = []\n", + "\n", + "for p in tqdm(X_paths, desc=\"Extracting and caching features\"):\n", + " try:\n", + " _ = extract_feature_cached(p)\n", + " except Exception as e:\n", + " failed_files.append((p, str(e)))\n", + "\n", + "print(\"Feature extraction completed.\")\n", + "print(\"Successful files:\", len(X_paths) - len(failed_files))\n", + "print(\"Failed files:\", len(failed_files))\n", + "print(\"Cache folder:\", CACHE_DIR)\n", + "\n", + "if failed_files:\n", + " failed_df = pd.DataFrame(failed_files, columns=[\"path\", \"error\"])\n", + " display(failed_df.head(20))\n" + ] + }, + { + "cell_type": "markdown", + "id": "a571194e", + "metadata": {}, + "source": [ + "## 7. Remove failed files if any\n", + "\n", + "If any files failed during feature extraction, this cell removes them from training.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "580e7d03", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Files remaining for training: 8659\n", + "Classes remaining: 123\n" + ] + } + ], + "source": [ + "if failed_files:\n", + " failed_paths = {p for p, _ in failed_files}\n", + " files_df = files_df[~files_df[\"path\"].isin(failed_paths)].reset_index(drop=True)\n", + "\n", + "X_paths = files_df[\"path\"].tolist()\n", + "y_labels = files_df[\"label\"].tolist()\n", + "\n", + "print(\"Files remaining for training:\", len(X_paths))\n", + "print(\"Classes remaining:\", files_df[\"label\"].nunique())\n" + ] + }, + { + "cell_type": "markdown", + "id": "c086e283", + "metadata": {}, + "source": [ + "## 8. Encode labels and split train/test data\n", + "\n", + "The class mapping saved later must match this label encoder.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "4e52c511", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Train files: 6927\n", + "Test files: 1732\n", + "Number of classes: 123\n", + "Classes:\n", + "0 -> Acanthiza chrysorrhoa\n", + "1 -> Acanthiza lineata\n", + "2 -> Acanthiza nana\n", + "3 -> Acanthiza pusilla\n", + "4 -> Acanthiza reguloides\n", + "5 -> Acanthiza uropygialis\n", + "6 -> Acanthorhynchus tenuirostris\n", + "7 -> Accipiter cirrocephalus\n", + "8 -> Aidemosyne modesta\n", + "9 -> Alauda arvensis\n", + "10 -> Anhinga novaehollandiae\n", + "11 -> Anthochaera phrygia\n", + "12 -> Antigone rubicunda\n", + "13 -> Artamus cinereus\n", + "14 -> Artamus cyanopterus\n", + "15 -> Artamus minor\n", + "16 -> Artamus superciliosus\n", + "17 -> Barnardius zonarius\n", + "18 -> Callocephalon fimbriatum\n", + "19 -> Calyptorhynchus banksii\n", + "20 -> Calyptorhynchus lathami\n", + "21 -> Capra Hircus\n", + "22 -> Carduelis carduelis\n", + "23 -> Carterornis leucotis\n", + "24 -> Cervus Unicolour\n", + "25 -> Ceyx azureus\n", + "26 -> Chenonetta jubata\n", + "27 -> Chlamydera nuchalis\n", + "28 -> Cincloramphus mathewsi\n", + "29 -> Cinclosoma punctatum\n", + "30 -> Cisticola exilis\n", + "31 -> Climacteris picumnus\n", + "32 -> Colluricincla harmonica\n", + "33 -> Conopophila albogularis\n", + "34 -> Cophixalus exiguus\n", + "35 -> Cophixalus infacetus\n", + "36 -> Cophixalus ornatus\n", + "37 -> Coracina novaehollandiae\n", + "38 -> Coracina papuensis\n", + "39 -> Corcorax melanorhamphos\n", + "40 -> Cormobates leucophaea\n", + "41 -> Corvus mellori\n", + "42 -> Coturnix pectoralis\n", + "43 -> Cygnus atratus\n", + "44 -> Dama Dama\n", + "45 -> Daphoenositta chrysoptera\n", + "46 -> Dasyurus maculatus\n", + "47 -> Dicaeum hirundinaceum\n", + "48 -> Egretta novaehollandiae\n", + "49 -> Elseyornis melanops\n", + "50 -> Entomyzon cyanotis\n", + "51 -> Eurostopodus argus\n", + "52 -> Eurostopodus mystacalis\n", + "53 -> Eurystomus orientalis\n", + "54 -> Falco berigora\n", + "55 -> Falco cenchroides\n", + "56 -> Falco peregrinus\n", + "57 -> Falcunculus frontatus\n", + "58 -> Felis Catus\n", + "59 -> Fulica atra\n", + "60 -> Gallinula tenebrosa\n", + "61 -> Geopelia cuneata\n", + "62 -> Gerygone mouki\n", + "63 -> Haliastur sphenurus\n", + "64 -> Irediparra gallinacea\n", + "65 -> Lalage leucomela\n", + "66 -> Litoria inermis\n", + "67 -> Manorina melanophrys\n", + "68 -> Megapodius reinwardt\n", + "69 -> Melithreptus brevirostris\n", + "70 -> Melithreptus gularis\n", + "71 -> Melithreptus lunatus\n", + "72 -> Microeca flavigaster\n", + "73 -> Neophema pulchella\n", + "74 -> Nesoptilotis leucotis\n", + "75 -> Pachycephala simplex\n", + "76 -> Pardalotus rubricatus\n", + "77 -> Parvipsitta pusilla\n", + "78 -> Pelecanus conspicillatus\n", + "79 -> Petrochelidon ariel\n", + "80 -> Petrochelidon nigricans\n", + "81 -> Petroica boodang\n", + "82 -> Petroica goodenovii\n", + "83 -> Petroica phoenicea\n", + "84 -> Petroica rosea\n", + "85 -> Pezoporus wallicus\n", + "86 -> Phaps elegans\n", + "87 -> Philemon citreogularis\n", + "88 -> Philemon corniculatus\n", + "89 -> Phylidonyris niger\n", + "90 -> Pitta iris\n", + "91 -> Pitta versicolor\n", + "92 -> Platycercus elegans\n", + "93 -> Plectorhyncha lanceolata\n", + "94 -> Psophodes cristatus\n", + "95 -> Ptilinopus regina\n", + "96 -> Pycnoptilus floccosus\n", + "97 -> Ramsayornis fasciatus\n", + "98 -> Ranoidea caerulea\n", + "99 -> Rattus Norvegicus\n", + "100 -> Rhinella marina\n", + "101 -> Rhipidura albiscapa\n", + "102 -> Rhipidura leucophrys\n", + "103 -> Rhipidura rufifrons\n", + "104 -> Rhipidura rufiventris\n", + "105 -> Scythrops novaehollandiae\n", + "106 -> Spilopelia chinensis\n", + "107 -> Stizoptera bichenovii\n", + "108 -> Stomiopera unicolor\n", + "109 -> Strepera versicolor\n", + "110 -> Sus Scrofa\n", + "111 -> Symposiachrus trivirgatus\n", + "112 -> Tregellasia capito\n", + "113 -> Trichosurus vulpecula\n", + "114 -> Uperoleia altissima\n", + "115 -> Uperoleia mimula\n", + "116 -> Vanellus miles\n", + "117 -> Vulpes vulpes\n", + "118 -> brant\n", + "119 -> jabwar\n", + "120 -> sheowl\n", + "121 -> spodov\n", + "122 -> wiltur\n" + ] + } + ], + "source": [ + "le = LabelEncoder()\n", + "y = le.fit_transform(y_labels)\n", + "\n", + "class_counts = pd.Series(y).value_counts()\n", + "min_class_count = class_counts.min()\n", + "\n", + "if min_class_count < 2:\n", + " raise ValueError(\n", + " \"At least one class has fewer than 2 files. \"\n", + " \"Stratified train/test split requires at least 2 files per class.\"\n", + " )\n", + "\n", + "X_train, X_test, y_train, y_test = train_test_split(\n", + " X_paths,\n", + " y,\n", + " test_size=TEST_SIZE,\n", + " random_state=RANDOM_STATE,\n", + " stratify=y\n", + ")\n", + "\n", + "print(\"Train files:\", len(X_train))\n", + "print(\"Test files:\", len(X_test))\n", + "print(\"Number of classes:\", len(le.classes_))\n", + "print(\"Classes:\")\n", + "for i, label in enumerate(le.classes_):\n", + " print(i, \"->\", label)\n" + ] + }, + { + "cell_type": "markdown", + "id": "36259d32", + "metadata": {}, + "source": [ + "## 9. PyTorch dataset\n", + "\n", + "EfficientNetV2 receives input as `[batch, channels, n_mels, time]`.\n", + "\n", + "Here, channels = 1 because the Mel spectrogram is single-channel.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b983bb1e", + "metadata": {}, + "outputs": [], + "source": [ + "class MelDataset(Dataset):\n", + " def __init__(self, paths, labels):\n", + " self.paths = list(paths)\n", + " self.labels = np.array(labels)\n", + "\n", + " def __len__(self):\n", + " return len(self.paths)\n", + "\n", + " def __getitem__(self, idx):\n", + " mel = extract_feature_cached(self.paths[idx])\n", + "\n", + " # Normalise each sample\n", + " mel = (mel - np.mean(mel)) / (np.std(mel) + 1e-6)\n", + "\n", + " # Shape: [1, n_mels, time]\n", + " mel = torch.tensor(mel, dtype=torch.float32).unsqueeze(0)\n", + "\n", + " label = torch.tensor(self.labels[idx], dtype=torch.long)\n", + "\n", + " return mel, label\n" + ] + }, + { + "cell_type": "markdown", + "id": "4ac4a27c", + "metadata": {}, + "source": [ + "## 10. Train EfficientNetV2\n", + "\n", + "This uses the EfficientNetV2 model selected from Sprint 1 benchmark work.\n", + "\n", + "Start with 3 epochs first to check the full pipeline. After confirming it works, increase `EPOCHS`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ffaebc18", + "metadata": {}, + "outputs": [], + "source": [ + "def train_efficientnetv2(X_train, y_train, epochs=EPOCHS, batch_size=BATCH_SIZE, lr=LEARNING_RATE):\n", + " device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n", + " print(\"Using device:\", device)\n", + "\n", + " num_classes = len(np.unique(y_train))\n", + "\n", + " model = timm.create_model(\n", + " \"efficientnetv2_rw_s\",\n", + " pretrained=True,\n", + " num_classes=num_classes,\n", + " in_chans=1\n", + " )\n", + "\n", + " model = model.to(device)\n", + "\n", + " train_ds = MelDataset(X_train, y_train)\n", + " train_loader = DataLoader(\n", + " train_ds,\n", + " batch_size=batch_size,\n", + " shuffle=True,\n", + " num_workers=0\n", + " )\n", + "\n", + " criterion = nn.CrossEntropyLoss()\n", + " optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=1e-4)\n", + "\n", + " history = []\n", + "\n", + " for epoch in range(epochs):\n", + " model.train()\n", + "\n", + " running_loss = 0.0\n", + " correct = 0\n", + " total = 0\n", + "\n", + " for x, labels in tqdm(train_loader, desc=f\"Epoch {epoch+1}/{epochs}\"):\n", + " x = x.to(device)\n", + " labels = labels.to(device)\n", + "\n", + " optimizer.zero_grad()\n", + "\n", + " outputs = model(x)\n", + " loss = criterion(outputs, labels)\n", + "\n", + " loss.backward()\n", + " optimizer.step()\n", + "\n", + " running_loss += loss.item() * x.size(0)\n", + "\n", + " preds = torch.argmax(outputs, dim=1)\n", + " correct += (preds == labels).sum().item()\n", + " total += labels.size(0)\n", + "\n", + " epoch_loss = running_loss / total\n", + " epoch_acc = correct / total\n", + "\n", + " history.append({\n", + " \"epoch\": epoch + 1,\n", + " \"train_loss\": float(epoch_loss),\n", + " \"train_accuracy\": float(epoch_acc)\n", + " })\n", + "\n", + " print(f\"Epoch {epoch+1}/{epochs} | Loss: {epoch_loss:.4f} | Train Acc: {epoch_acc:.4f}\")\n", + "\n", + " return model, device, history\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "7282371c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using device: cpu\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 1/3: 100%|█████████████████████████████████████████████████████████████████████| 433/433 [31:39<00:00, 4.39s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1/3 | Loss: 2.6794 | Train Acc: 0.4223\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 2/3: 100%|█████████████████████████████████████████████████████████████████████| 433/433 [30:06<00:00, 4.17s/it]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 2/3 | Loss: 0.8291 | Train Acc: 0.8050\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Epoch 3/3: 100%|█████████████████████████████████████████████████████████████████████| 433/433 [30:04<00:00, 4.17s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 3/3 | Loss: 0.2501 | Train Acc: 0.9488\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "efficientnet_model, device, training_history = train_efficientnetv2(\n", + " X_train,\n", + " y_train,\n", + " epochs=EPOCHS,\n", + " batch_size=BATCH_SIZE,\n", + " lr=LEARNING_RATE\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "7a7b5455", + "metadata": {}, + "source": [ + "## 11. Evaluate on test set\n", + "\n", + "This checks whether the trained model can produce labels and confidence values correctly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "e9bb483a", + "metadata": {}, + "outputs": [], + "source": [ + "@torch.no_grad()\n", + "def predict_model(model, paths, batch_size=BATCH_SIZE):\n", + " model.eval()\n", + "\n", + " dummy_labels = np.zeros(len(paths), dtype=int)\n", + " ds = MelDataset(paths, dummy_labels)\n", + " loader = DataLoader(ds, batch_size=batch_size, shuffle=False, num_workers=0)\n", + "\n", + " all_probs = []\n", + "\n", + " for x, _ in tqdm(loader, desc=\"Predicting\"):\n", + " x = x.to(device)\n", + "\n", + " outputs = model(x)\n", + " probs = torch.softmax(outputs, dim=1)\n", + "\n", + " all_probs.append(probs.cpu().numpy())\n", + "\n", + " probs = np.concatenate(all_probs, axis=0)\n", + " preds = np.argmax(probs, axis=1)\n", + "\n", + " return preds, probs\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "7dce2e58", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Predicting: 100%|████████████████████████████████████████████████████████████████████| 109/109 [02:26<00:00, 1.34s/it]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Test Accuracy: 0.8724018475750578\n", + "F1 Macro: 0.843222466600867\n", + "\n", + " precision recall f1-score support\n", + "\n", + " Acanthiza chrysorrhoa 0.80 0.57 0.67 7\n", + " Acanthiza lineata 0.75 1.00 0.86 6\n", + " Acanthiza nana 0.90 0.97 0.93 29\n", + " Acanthiza pusilla 0.90 0.70 0.79 50\n", + " Acanthiza reguloides 0.76 0.91 0.83 32\n", + " Acanthiza uropygialis 0.83 0.77 0.80 13\n", + "Acanthorhynchus tenuirostris 0.64 0.76 0.70 21\n", + " Accipiter cirrocephalus 0.74 0.94 0.83 18\n", + " Aidemosyne modesta 1.00 1.00 1.00 3\n", + " Alauda arvensis 1.00 0.80 0.89 5\n", + " Anhinga novaehollandiae 0.00 0.00 0.00 2\n", + " Anthochaera phrygia 0.86 0.86 0.86 7\n", + " Antigone rubicunda 1.00 1.00 1.00 5\n", + " Artamus cinereus 1.00 0.83 0.91 6\n", + " Artamus cyanopterus 0.83 1.00 0.91 5\n", + " Artamus minor 0.75 0.86 0.80 7\n", + " Artamus superciliosus 1.00 0.67 0.80 3\n", + " Barnardius zonarius 0.91 0.78 0.84 41\n", + " Callocephalon fimbriatum 1.00 1.00 1.00 7\n", + " Calyptorhynchus banksii 0.86 1.00 0.92 6\n", + " Calyptorhynchus lathami 1.00 0.75 0.86 4\n", + " Capra Hircus 1.00 0.82 0.90 11\n", + " Carduelis carduelis 0.77 0.83 0.80 12\n", + " Carterornis leucotis 0.71 0.94 0.81 16\n", + " Cervus Unicolour 1.00 1.00 1.00 3\n", + " Ceyx azureus 1.00 1.00 1.00 1\n", + " Chenonetta jubata 0.50 0.67 0.57 3\n", + " Chlamydera nuchalis 1.00 0.50 0.67 4\n", + " Cincloramphus mathewsi 0.96 0.87 0.91 30\n", + " Cinclosoma punctatum 1.00 0.93 0.97 15\n", + " Cisticola exilis 0.97 0.79 0.87 43\n", + " Climacteris picumnus 1.00 0.50 0.67 4\n", + " Colluricincla harmonica 0.85 0.89 0.87 122\n", + " Conopophila albogularis 0.62 1.00 0.77 5\n", + " Cophixalus exiguus 1.00 0.86 0.92 7\n", + " Cophixalus infacetus 1.00 1.00 1.00 35\n", + " Cophixalus ornatus 1.00 1.00 1.00 1\n", + " Coracina novaehollandiae 0.83 0.83 0.83 6\n", + " Coracina papuensis 1.00 1.00 1.00 1\n", + " Corcorax melanorhamphos 1.00 1.00 1.00 3\n", + " Cormobates leucophaea 0.57 0.40 0.47 10\n", + " Corvus mellori 0.86 0.86 0.86 7\n", + " Coturnix pectoralis 1.00 1.00 1.00 3\n", + " Cygnus atratus 0.88 1.00 0.93 7\n", + " Dama Dama 1.00 0.75 0.86 8\n", + " Daphoenositta chrysoptera 1.00 0.67 0.80 9\n", + " Dasyurus maculatus 0.99 1.00 1.00 103\n", + " Dicaeum hirundinaceum 0.73 0.80 0.76 10\n", + " Egretta novaehollandiae 1.00 1.00 1.00 2\n", + " Elseyornis melanops 1.00 1.00 1.00 4\n", + " Entomyzon cyanotis 1.00 0.50 0.67 2\n", + " Eurostopodus argus 1.00 0.50 0.67 2\n", + " Eurostopodus mystacalis 0.80 0.80 0.80 5\n", + " Eurystomus orientalis 0.78 0.70 0.74 10\n", + " Falco berigora 0.60 0.60 0.60 10\n", + " Falco cenchroides 1.00 0.50 0.67 2\n", + " Falco peregrinus 0.90 1.00 0.95 9\n", + " Falcunculus frontatus 1.00 0.33 0.50 3\n", + " Felis Catus 0.75 1.00 0.86 9\n", + " Fulica atra 0.00 0.00 0.00 3\n", + " Gallinula tenebrosa 0.80 0.67 0.73 6\n", + " Geopelia cuneata 1.00 1.00 1.00 1\n", + " Gerygone mouki 0.71 0.71 0.71 7\n", + " Haliastur sphenurus 0.78 0.75 0.77 24\n", + " Irediparra gallinacea 1.00 1.00 1.00 5\n", + " Lalage leucomela 0.50 0.40 0.44 5\n", + " Litoria inermis 1.00 1.00 1.00 43\n", + " Manorina melanophrys 0.73 0.85 0.79 13\n", + " Megapodius reinwardt 1.00 0.33 0.50 3\n", + " Melithreptus brevirostris 1.00 0.60 0.75 5\n", + " Melithreptus gularis 0.68 0.85 0.76 20\n", + " Melithreptus lunatus 1.00 1.00 1.00 2\n", + " Microeca flavigaster 0.80 0.80 0.80 5\n", + " Neophema pulchella 1.00 0.50 0.67 2\n", + " Nesoptilotis leucotis 1.00 1.00 1.00 3\n", + " Pachycephala simplex 1.00 1.00 1.00 1\n", + " Pardalotus rubricatus 1.00 1.00 1.00 4\n", + " Parvipsitta pusilla 0.71 0.77 0.74 13\n", + " Pelecanus conspicillatus 1.00 1.00 1.00 1\n", + " Petrochelidon ariel 1.00 1.00 1.00 2\n", + " Petrochelidon nigricans 1.00 1.00 1.00 10\n", + " Petroica boodang 1.00 1.00 1.00 3\n", + " Petroica goodenovii 0.96 0.96 0.96 25\n", + " Petroica phoenicea 1.00 0.60 0.75 5\n", + " Petroica rosea 1.00 1.00 1.00 5\n", + " Pezoporus wallicus 0.83 1.00 0.91 10\n", + " Phaps elegans 1.00 0.40 0.57 5\n", + " Philemon citreogularis 0.79 0.88 0.83 34\n", + " Philemon corniculatus 0.87 0.59 0.70 22\n", + " Phylidonyris niger 1.00 0.99 1.00 109\n", + " Pitta iris 1.00 0.75 0.86 4\n", + " Pitta versicolor 1.00 1.00 1.00 6\n", + " Platycercus elegans 0.75 0.75 0.75 4\n", + " Plectorhyncha lanceolata 0.90 0.90 0.90 10\n", + " Psophodes cristatus 1.00 1.00 1.00 6\n", + " Ptilinopus regina 1.00 1.00 1.00 1\n", + " Pycnoptilus floccosus 1.00 1.00 1.00 2\n", + " Ramsayornis fasciatus 1.00 0.80 0.89 5\n", + " Ranoidea caerulea 1.00 1.00 1.00 3\n", + " Rattus Norvegicus 0.95 1.00 0.98 21\n", + " Rhinella marina 1.00 1.00 1.00 4\n", + " Rhipidura albiscapa 0.86 0.87 0.86 89\n", + " Rhipidura leucophrys 0.83 0.91 0.86 130\n", + " Rhipidura rufifrons 0.76 1.00 0.86 19\n", + " Rhipidura rufiventris 0.80 0.80 0.80 5\n", + " Scythrops novaehollandiae 1.00 1.00 1.00 2\n", + " Spilopelia chinensis 0.67 1.00 0.80 2\n", + " Stizoptera bichenovii 1.00 1.00 1.00 12\n", + " Stomiopera unicolor 0.81 1.00 0.90 13\n", + " Strepera versicolor 1.00 1.00 1.00 4\n", + " Sus Scrofa 0.89 0.89 0.89 9\n", + " Symposiachrus trivirgatus 0.67 0.67 0.67 3\n", + " Tregellasia capito 0.67 1.00 0.80 4\n", + " Trichosurus vulpecula 1.00 1.00 1.00 2\n", + " Uperoleia altissima 1.00 1.00 1.00 14\n", + " Uperoleia mimula 1.00 1.00 1.00 10\n", + " Vanellus miles 1.00 0.75 0.86 12\n", + " Vulpes vulpes 0.91 1.00 0.95 21\n", + " brant 0.83 0.93 0.88 27\n", + " jabwar 0.77 0.62 0.69 16\n", + " sheowl 0.74 0.65 0.69 26\n", + " spodov 0.95 0.95 0.95 21\n", + " wiltur 0.58 0.73 0.65 15\n", + "\n", + " accuracy 0.87 1732\n", + " macro avg 0.88 0.83 0.84 1732\n", + " weighted avg 0.88 0.87 0.87 1732\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "y_pred, y_probs = predict_model(efficientnet_model, X_test)\n", + "\n", + "acc = accuracy_score(y_test, y_pred)\n", + "f1_macro = f1_score(y_test, y_pred, average=\"macro\")\n", + "\n", + "print(\"Test Accuracy:\", acc)\n", + "print(\"F1 Macro:\", f1_macro)\n", + "print()\n", + "print(classification_report(y_test, y_pred, target_names=le.classes_, zero_division=0))\n" + ] + }, + { + "cell_type": "markdown", + "id": "27e894b2", + "metadata": {}, + "source": [ + "## 12. Show prediction examples\n", + "\n", + "It shows predicted label and confidence.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "ded10be8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
audio_pathtrue_labelpredicted_labelconfidencecorrect
0C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Barnardius zonariusBarnardius zonarius0.995623True
1C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Rhipidura albiscapaRhipidura albiscapa0.981187True
2C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Colluricincla harmonicaColluricincla harmonica0.989218True
3C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Petroica goodenoviiPetroica goodenovii0.976628True
4C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Falco peregrinusFalco peregrinus0.954945True
5C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Cophixalus infacetusCophixalus infacetus0.996565True
6C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Sus ScrofaSus Scrofa0.549493True
7C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Petroica phoeniceaPetroica phoenicea0.957906True
8C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Phylidonyris nigerPhylidonyris niger0.987102True
9C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy...Colluricincla harmonicaColluricincla harmonica0.997531True
\n", + "
" + ], + "text/plain": [ + " audio_path true_label \\\n", + "0 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Barnardius zonarius \n", + "1 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Rhipidura albiscapa \n", + "2 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Colluricincla harmonica \n", + "3 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Petroica goodenovii \n", + "4 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Falco peregrinus \n", + "5 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Cophixalus infacetus \n", + "6 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Sus Scrofa \n", + "7 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Petroica phoenicea \n", + "8 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Phylidonyris niger \n", + "9 C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototy... Colluricincla harmonica \n", + "\n", + " predicted_label confidence correct \n", + "0 Barnardius zonarius 0.995623 True \n", + "1 Rhipidura albiscapa 0.981187 True \n", + "2 Colluricincla harmonica 0.989218 True \n", + "3 Petroica goodenovii 0.976628 True \n", + "4 Falco peregrinus 0.954945 True \n", + "5 Cophixalus infacetus 0.996565 True \n", + "6 Sus Scrofa 0.549493 True \n", + "7 Petroica phoenicea 0.957906 True \n", + "8 Phylidonyris niger 0.987102 True \n", + "9 Colluricincla harmonica 0.997531 True " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "example_rows = []\n", + "\n", + "for i in range(min(10, len(X_test))):\n", + " true_idx = int(y_test[i])\n", + " pred_idx = int(y_pred[i])\n", + " confidence = float(np.max(y_probs[i]))\n", + "\n", + " example_rows.append({\n", + " \"audio_path\": X_test[i],\n", + " \"true_label\": le.classes_[true_idx],\n", + " \"predicted_label\": le.classes_[pred_idx],\n", + " \"confidence\": confidence,\n", + " \"correct\": true_idx == pred_idx\n", + " })\n", + "\n", + "examples_df = pd.DataFrame(example_rows)\n", + "display(examples_df)\n" + ] + }, + { + "cell_type": "markdown", + "id": "2de46a64", + "metadata": {}, + "source": [ + "## 13. Save model, class mapping, preprocessing config, and metrics\n", + "\n", + "These files are needed by the Engine pipeline.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "8cf75e99", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Saved model to: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo.pt\n", + "Saved class mapping to: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\class_mapping.json\n", + "Saved preprocessing config to: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\preprocess_config.json\n", + "Saved metrics to: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\training_metrics.json\n" + ] + } + ], + "source": [ + "model_path = MODEL_DIR / \"efficientnetv2_project_echo.pt\"\n", + "label_map_path = MODEL_DIR / \"class_mapping.json\"\n", + "preprocess_path = MODEL_DIR / \"preprocess_config.json\"\n", + "metrics_path = MODEL_DIR / \"training_metrics.json\"\n", + "\n", + "# Save model checkpoint\n", + "torch.save({\n", + " \"model_state_dict\": efficientnet_model.state_dict(),\n", + " \"model_name\": \"efficientnetv2_rw_s\",\n", + " \"num_classes\": len(le.classes_),\n", + " \"in_chans\": 1,\n", + " \"target_sr\": TARGET_SR,\n", + " \"duration_s\": DURATION_S,\n", + " \"n_mels\": N_MELS,\n", + " \"hop_length\": HOP_LENGTH,\n", + " \"fmin\": FMIN,\n", + " \"fmax\": FMAX\n", + "}, model_path)\n", + "\n", + "# Save class mapping\n", + "class_mapping = {\n", + " \"classes\": le.classes_.tolist(),\n", + " \"label_to_index\": {label: int(i) for i, label in enumerate(le.classes_)},\n", + " \"index_to_label\": {str(i): label for i, label in enumerate(le.classes_)}\n", + "}\n", + "\n", + "with open(label_map_path, \"w\", encoding=\"utf-8\") as f:\n", + " json.dump(class_mapping, f, indent=4)\n", + "\n", + "# Save preprocessing configuration\n", + "preprocess_config = {\n", + " \"target_sr\": TARGET_SR,\n", + " \"duration_s\": DURATION_S,\n", + " \"n_mels\": N_MELS,\n", + " \"hop_length\": HOP_LENGTH,\n", + " \"fmin\": FMIN,\n", + " \"fmax\": FMAX,\n", + " \"feature_type\": \"mel_spectrogram_db\",\n", + " \"normalisation\": \"per_sample_standardisation\",\n", + " \"input_shape\": [1, N_MELS, \"time\"]\n", + "}\n", + "\n", + "with open(preprocess_path, \"w\", encoding=\"utf-8\") as f:\n", + " json.dump(preprocess_config, f, indent=4)\n", + "\n", + "# Save training metrics\n", + "training_metrics = {\n", + " \"model\": \"EfficientNetV2\",\n", + " \"model_name\": \"efficientnetv2_rw_s\",\n", + " \"test_accuracy\": float(acc),\n", + " \"f1_macro\": float(f1_macro),\n", + " \"train_size\": len(X_train),\n", + " \"test_size\": len(X_test),\n", + " \"num_classes\": len(le.classes_),\n", + " \"epochs\": EPOCHS,\n", + " \"batch_size\": BATCH_SIZE,\n", + " \"learning_rate\": LEARNING_RATE,\n", + " \"training_history\": training_history\n", + "}\n", + "\n", + "with open(metrics_path, \"w\", encoding=\"utf-8\") as f:\n", + " json.dump(training_metrics, f, indent=4)\n", + "\n", + "print(\"Saved model to:\", model_path)\n", + "print(\"Saved class mapping to:\", label_map_path)\n", + "print(\"Saved preprocessing config to:\", preprocess_path)\n", + "print(\"Saved metrics to:\", metrics_path)\n" + ] + }, + { + "cell_type": "markdown", + "id": "028c1f81", + "metadata": {}, + "source": [ + "## 14. Test loading the saved model\n", + "\n", + "This confirms the `.pt` file can be loaded again. This is important before connecting it to the Engine pipeline.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "b42eee1e", + "metadata": {}, + "outputs": [], + "source": [ + "def load_saved_efficientnetv2(model_path, device=\"cpu\"):\n", + " checkpoint = torch.load(model_path, map_location=device)\n", + "\n", + " model = timm.create_model(\n", + " checkpoint[\"model_name\"],\n", + " pretrained=False,\n", + " num_classes=checkpoint[\"num_classes\"],\n", + " in_chans=checkpoint[\"in_chans\"]\n", + " )\n", + "\n", + " model.load_state_dict(checkpoint[\"model_state_dict\"])\n", + " model = model.to(device)\n", + " model.eval()\n", + "\n", + " return model, checkpoint\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "9a074415", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Predicting: 100%|████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 9.05it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded model works.\n", + "Sample audio: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Barnardius zonarius\\region_64.600-66.600.mp3\n", + "Predicted label: Barnardius zonarius\n", + "Confidence: 0.9956234097480774\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "loaded_model, checkpoint = load_saved_efficientnetv2(model_path, device=device)\n", + "\n", + "sample_path = X_test[0]\n", + "sample_pred, sample_probs = predict_model(loaded_model, [sample_path])\n", + "\n", + "pred_index = int(sample_pred[0])\n", + "pred_label = le.classes_[pred_index]\n", + "confidence = float(np.max(sample_probs[0]))\n", + "\n", + "print(\"Loaded model works.\")\n", + "print(\"Sample audio:\", sample_path)\n", + "print(\"Predicted label:\", pred_label)\n", + "print(\"Confidence:\", confidence)\n" + ] + }, + { + "cell_type": "markdown", + "id": "bbe8959d", + "metadata": {}, + "source": [ + "## 15. Final output check\n", + "\n", + "After running the notebook, check that these files exist:\n", + "\n", + "```text\n", + "_trained_models/\n", + " efficientnetv2_project_echo.pt\n", + " class_mapping.json\n", + " preprocess_config.json\n", + " training_metrics.json\n", + "```\n", + "\n", + "**Load the selected Sprint 1 model into the real Engine pipeline.**\n" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "8420a8c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "efficientnetv2_project_echo.pt exists: True\n", + "class_mapping.json exists: True\n", + "preprocess_config.json exists: True\n", + "training_metrics.json exists: True\n" + ] + } + ], + "source": [ + "for file_path in [model_path, label_map_path, preprocess_path, metrics_path]:\n", + " print(file_path.name, \"exists:\", pathlib.Path(file_path).exists())\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.25" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/test.ipynb b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/test.ipynb new file mode 100644 index 000000000..a13a0d93f --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/test.ipynb @@ -0,0 +1,856 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "4a48ec59-1447-49b4-9ff9-52b73fa598a8", + "metadata": {}, + "outputs": [], + "source": [ + "import os" + ] + }, + { + "cell_type": "markdown", + "id": "f7fbe7c7-cbef-447f-a935-0645f2a021c4", + "metadata": {}, + "source": [ + "## Validate with dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fd378503-ecb1-4dc0-a55e-b8628d53fd1e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Starting EfficientNetV2 dataset validation...\n", + "\n", + "Loading model and configuration files...\n", + "Model loaded successfully.\n", + "Device: cpu\n", + "Number of classes: 123\n", + "\n", + "Scanning dataset files...\n", + "Found 50 audio files for validation.\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9771 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9974 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9857 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9887 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.6044 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9494 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9920 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9930 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9931 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.2879 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9764 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9784 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.8370 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9337 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9372 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.7938 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9965 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.5156 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9752 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9252 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.8972 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9712 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9747 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9932 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9753 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9092 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthorhynchus tenuirostris | Confidence: 0.3176 | Correct: False\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthorhynchus tenuirostris | Confidence: 0.3172 | Correct: False\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Rhipidura albiscapa | Confidence: 0.3664 | Correct: False\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9859 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9858 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9440 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9828 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | Predicted: Acanthiza chrysorrhoa | Confidence: 0.9877 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9033 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9027 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.7625 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9472 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.8151 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9322 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9598 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9597 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9586 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.8345 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9796 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.8457 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9726 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9205 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.2848 | Correct: True\n", + "Actual: Acanthiza lineata | Predicted: Acanthiza lineata | Confidence: 0.9507 | Correct: True\n", + "\n", + "Validation summary\n", + "------------------\n", + "Total files tested: 50\n", + "Correct predictions: 47\n", + "Validation accuracy: 0.94\n", + "\n", + "Validation results saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\efficientnetv2_dataset_validation_results.csv\n" + ] + } + ], + "source": [ + "\n", + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + ")\n", + "\n", + "%run validate_efficientnetv2_dataset_inference.py" + ] + }, + { + "cell_type": "markdown", + "id": "565322c6-7b41-4f6f-97d7-ac428ff4cff4", + "metadata": {}, + "source": [ + "## Validate with json format" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5266705e-ec41-4dea-a641-42ae85681d8c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Starting EfficientNetV2 JSON-style inference validation...\n", + "\n", + "Loading model and configuration files...\n", + "Model loaded successfully.\n", + "Device: cpu\n", + "Number of classes: 123\n", + "\n", + "Incoming JSON-style message:\n", + "{\n", + " \"device_id\": \"sensor_001\",\n", + " \"audio_path\": \"C:\\\\Deakin\\\\ProjectEcho\\\\Project-Echo\\\\src\\\\Prototypes\\\\data\\\\data_files\\\\Acanthiza chrysorrhoa\\\\region_3.650-4.900.mp3\",\n", + " \"timestamp\": \"2026-04-27T15:30:00\",\n", + " \"source\": \"dataset_validation\"\n", + "}\n", + "\n", + "Running inference...\n", + "\n", + "Inference response:\n", + "{\n", + " \"device_id\": \"sensor_001\",\n", + " \"input_timestamp\": \"2026-04-27T15:30:00\",\n", + " \"processed_timestamp\": \"2026-04-27T19:13:45\",\n", + " \"model\": \"EfficientNetV2\",\n", + " \"input_audio\": \"C:\\\\Deakin\\\\ProjectEcho\\\\Project-Echo\\\\src\\\\Prototypes\\\\data\\\\data_files\\\\Acanthiza chrysorrhoa\\\\region_3.650-4.900.mp3\",\n", + " \"prediction\": {\n", + " \"label\": \"Acanthiza chrysorrhoa\",\n", + " \"class_index\": 0,\n", + " \"confidence\": 0.9337119460105896\n", + " },\n", + " \"top_predictions\": [\n", + " {\n", + " \"index\": 0,\n", + " \"label\": \"Acanthiza chrysorrhoa\",\n", + " \"confidence\": 0.9337119460105896\n", + " },\n", + " {\n", + " \"index\": 47,\n", + " \"label\": \"Dicaeum hirundinaceum\",\n", + " \"confidence\": 0.02223811112344265\n", + " },\n", + " {\n", + " \"index\": 3,\n", + " \"label\": \"Acanthiza pusilla\",\n", + " \"confidence\": 0.007062176708132029\n", + " },\n", + " {\n", + " \"index\": 5,\n", + " \"label\": \"Acanthiza uropygialis\",\n", + " \"confidence\": 0.006129274610430002\n", + " },\n", + " {\n", + " \"index\": 103,\n", + " \"label\": \"Rhipidura rufifrons\",\n", + " \"confidence\": 0.0059699914418160915\n", + " }\n", + " ],\n", + " \"status\": \"success\"\n", + "}\n", + "\n", + "JSON inference result saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\efficientnetv2_json_inference_result.json\n" + ] + } + ], + "source": [ + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + ")\n", + "\n", + "%run validate_efficientnetv2_json_inference.py" + ] + }, + { + "cell_type": "markdown", + "id": "0eb9e882-ad12-45e0-a3cc-60bb84524adb", + "metadata": {}, + "source": [ + "## Validate with MQTT message" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "622c62f1-fed7-459f-bf45-1d9ac9d447d8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Starting EfficientNetV2 MQTT message handler validation...\n", + "\n", + "Loading model and configuration files...\n", + "Model loaded successfully.\n", + "Device: cpu\n", + "Number of classes: 123\n", + "\n", + "Simulated MQTT payload:\n", + "b'{\"device_id\": \"sensor_001\", \"audio_path\": \"C:\\\\\\\\Deakin\\\\\\\\ProjectEcho\\\\\\\\Project-Echo\\\\\\\\src\\\\\\\\Prototypes\\\\\\\\data\\\\\\\\data_files\\\\\\\\Acanthiza chrysorrhoa\\\\\\\\region_3.650-4.900.mp3\", \"timestamp\": \"2026-04-27T15:30:00\", \"source\": \"mqtt_simulation\"}'\n", + "\n", + "Running MQTT-style message handler...\n", + "\n", + "MQTT-style inference response:\n", + "{\n", + " \"device_id\": \"sensor_001\",\n", + " \"input_timestamp\": \"2026-04-27T15:30:00\",\n", + " \"processed_timestamp\": \"2026-04-27T19:18:01\",\n", + " \"model\": \"EfficientNetV2\",\n", + " \"input_audio\": \"C:\\\\Deakin\\\\ProjectEcho\\\\Project-Echo\\\\src\\\\Prototypes\\\\data\\\\data_files\\\\Acanthiza chrysorrhoa\\\\region_3.650-4.900.mp3\",\n", + " \"prediction\": {\n", + " \"label\": \"Acanthiza chrysorrhoa\",\n", + " \"class_index\": 0,\n", + " \"confidence\": 0.9337119460105896\n", + " },\n", + " \"top_predictions\": [\n", + " {\n", + " \"index\": 0,\n", + " \"label\": \"Acanthiza chrysorrhoa\",\n", + " \"confidence\": 0.9337119460105896\n", + " },\n", + " {\n", + " \"index\": 47,\n", + " \"label\": \"Dicaeum hirundinaceum\",\n", + " \"confidence\": 0.02223811112344265\n", + " },\n", + " {\n", + " \"index\": 3,\n", + " \"label\": \"Acanthiza pusilla\",\n", + " \"confidence\": 0.007062176708132029\n", + " },\n", + " {\n", + " \"index\": 5,\n", + " \"label\": \"Acanthiza uropygialis\",\n", + " \"confidence\": 0.006129274610430002\n", + " },\n", + " {\n", + " \"index\": 103,\n", + " \"label\": \"Rhipidura rufifrons\",\n", + " \"confidence\": 0.0059699914418160915\n", + " }\n", + " ],\n", + " \"status\": \"success\"\n", + "}\n", + "\n", + "MQTT message handler result saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\efficientnetv2_mqtt_message_handler_result.json\n" + ] + } + ], + "source": [ + "\n", + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + ")\n", + "\n", + "%run validate_efficientnetv2_mqtt_message_handler.py" + ] + }, + { + "cell_type": "markdown", + "id": "2fab61df-4e79-41cb-bc26-71eb2353e897", + "metadata": {}, + "source": [ + "## Validate export to ONNX" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8fae31db-3543-4945-b93b-c7ee84040b29", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading saved EfficientNetV2 model...\n", + "Model loaded successfully.\n", + "Model name: efficientnetv2_rw_s\n", + "Number of classes: 123\n", + "Exporting to ONNX...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\export_saved_efficientnetv2_to_onnx.py:54: DeprecationWarning: You are using the legacy TorchScript-based ONNX export. Starting in PyTorch 2.9, the new torch.export-based ONNX exporter will be the default. To switch now, set dynamo=True in torch.onnx.export. This new exporter supports features like exporting LLMs with DynamicCache. We encourage you to try it and share feedback to help improve the experience. Learn more about the new export logic: https://pytorch.org/docs/stable/onnx_dynamo.html. For exporting control flow: https://pytorch.org/tutorials/beginner/onnx/export_control_flow_model_to_onnx_tutorial.html.\n", + " torch.onnx.export(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ONNX model saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo.onnx\n", + "Checking ONNX model...\n", + "Opset: 13\n", + "ONNX export completed successfully.\n" + ] + } + ], + "source": [ + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + ")\n", + "\n", + "%run export_saved_efficientnetv2_to_onnx.py" + ] + }, + { + "cell_type": "markdown", + "id": "8ea5afee-b6d4-438c-9834-50b69ef02339", + "metadata": {}, + "source": [ + "## Validate TFLite Conversion" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "42dcb04a-b59e-42f3-b8cb-44c8e8591fdc", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\tensorflow_addons\\utils\\tfa_eol_msg.py:23: UserWarning: \n", + "\n", + "TensorFlow Addons (TFA) has ended development and introduction of new features.\n", + "TFA has entered a minimal maintenance and release mode until a planned end of life in May 2024.\n", + "Please modify downstream libraries to take dependencies from other repositories in our TensorFlow community (e.g. Keras, Keras-CV, and Keras-NLP). \n", + "\n", + "For more information see: https://github.com/tensorflow/addons/issues/2807 \n", + "\n", + " warnings.warn(\n", + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\tensorflow_addons\\utils\\ensure_tf_install.py:53: UserWarning: Tensorflow Addons supports using Python ops for all Tensorflow versions above or equal to 2.12.0 and strictly below 2.15.0 (nightly versions are not supported). \n", + " The versions of TensorFlow you are currently using is 2.10.0 and is not supported. \n", + "Some things might work, some things might not.\n", + "If you were to encounter a bug, do not file an issue.\n", + "If you want to make sure you're using a tested and supported configuration, either change the TensorFlow version or the TensorFlow Addons's version. \n", + "You can find the compatibility matrix in TensorFlow Addon's readme:\n", + "https://github.com/tensorflow/addons\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading ONNX model...\n", + "Converting ONNX to TensorFlow SavedModel...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:absl:Found untraced functions such as gen_tensor_dict while saving (showing 1 of 1). These functions will not be directly callable after loading.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "INFO:tensorflow:Assets written to: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo_saved_model\\assets\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:tensorflow:Assets written to: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo_saved_model\\assets\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "TensorFlow SavedModel saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo_saved_model\n", + "Converting TensorFlow SavedModel to TFLite...\n", + "TFLite model saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo.tflite\n", + "TFLite conversion completed successfully. File size: 85.19 MB\n" + ] + } + ], + "source": [ + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + ")\n", + "\n", + "%run convert_saved_efficientnetv2_onnx_to_tflite.py" + ] + }, + { + "cell_type": "markdown", + "id": "60ea86b9-8f46-4f85-87d5-69e5a72c3250", + "metadata": {}, + "source": [ + "## Validate TFLite Inference" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "28dfd0d7-b9fb-4a46-9578-0fe728895c78", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "Single audio PyTorch vs TFLite validation\n", + "======================================================================\n", + "PyTorch model loaded.\n", + "TFLite model loaded.\n", + "Device: cpu\n", + "Number of classes: 123\n", + "\n", + "TFLite input details:\n", + "{'name': 'serving_default_input:0', 'index': 0, 'shape': array([ 1, 1, 128, 313]), 'shape_signature': array([ -1, 1, 128, 313]), 'dtype': , 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}}\n", + "\n", + "TFLite output details:\n", + "{'name': 'PartitionedCall:0', 'index': 1189, 'shape': array([ 1, 123]), 'shape_signature': array([ -1, 123]), 'dtype': , 'quantization': (0.0, 0), 'quantization_parameters': {'scales': array([], dtype=float32), 'zero_points': array([], dtype=int32), 'quantized_dimension': 0}, 'sparsity_parameters': {}}\n", + "\n", + "Sample audio:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3\n", + "\n", + "PyTorch prediction:\n", + "{\n", + " \"audio_path\": \"C:\\\\Deakin\\\\ProjectEcho\\\\Project-Echo\\\\src\\\\Prototypes\\\\data\\\\data_files\\\\Acanthiza chrysorrhoa\\\\region_3.650-4.900.mp3\",\n", + " \"predicted_index\": 0,\n", + " \"predicted_label\": \"Acanthiza chrysorrhoa\",\n", + " \"confidence\": 0.9337119460105896,\n", + " \"top_predictions\": [\n", + " {\n", + " \"index\": 0,\n", + " \"label\": \"Acanthiza chrysorrhoa\",\n", + " \"confidence\": 0.9337119460105896\n", + " },\n", + " {\n", + " \"index\": 47,\n", + " \"label\": \"Dicaeum hirundinaceum\",\n", + " \"confidence\": 0.02223811112344265\n", + " },\n", + " {\n", + " \"index\": 3,\n", + " \"label\": \"Acanthiza pusilla\",\n", + " \"confidence\": 0.007062176708132029\n", + " },\n", + " {\n", + " \"index\": 5,\n", + " \"label\": \"Acanthiza uropygialis\",\n", + " \"confidence\": 0.006129274610430002\n", + " },\n", + " {\n", + " \"index\": 103,\n", + " \"label\": \"Rhipidura rufifrons\",\n", + " \"confidence\": 0.0059699914418160915\n", + " }\n", + " ]\n", + "}\n", + "\n", + "TFLite prediction:\n", + "{\n", + " \"audio_path\": \"C:\\\\Deakin\\\\ProjectEcho\\\\Project-Echo\\\\src\\\\Prototypes\\\\data\\\\data_files\\\\Acanthiza chrysorrhoa\\\\region_3.650-4.900.mp3\",\n", + " \"predicted_index\": 0,\n", + " \"predicted_label\": \"Acanthiza chrysorrhoa\",\n", + " \"confidence\": 0.9337119460105896,\n", + " \"top_predictions\": [\n", + " {\n", + " \"index\": 0,\n", + " \"label\": \"Acanthiza chrysorrhoa\",\n", + " \"confidence\": 0.9337119460105896\n", + " },\n", + " {\n", + " \"index\": 47,\n", + " \"label\": \"Dicaeum hirundinaceum\",\n", + " \"confidence\": 0.022238129749894142\n", + " },\n", + " {\n", + " \"index\": 3,\n", + " \"label\": \"Acanthiza pusilla\",\n", + " \"confidence\": 0.007062216755002737\n", + " },\n", + " {\n", + " \"index\": 5,\n", + " \"label\": \"Acanthiza uropygialis\",\n", + " \"confidence\": 0.00612929742783308\n", + " },\n", + " {\n", + " \"index\": 103,\n", + " \"label\": \"Rhipidura rufifrons\",\n", + " \"confidence\": 0.005970010533928871\n", + " }\n", + " ],\n", + " \"raw_output_shape\": [\n", + " 1,\n", + " 123\n", + " ],\n", + " \"tflite_input_shape\": [\n", + " 1,\n", + " 1,\n", + " 128,\n", + " 313\n", + " ]\n", + "}\n", + "\n", + "Comparison summary:\n", + "PyTorch label: Acanthiza chrysorrhoa\n", + "TFLite label: Acanthiza chrysorrhoa\n", + "Labels match: True\n", + "PyTorch confidence: 0.9337119460105896\n", + "TFLite confidence: 0.9337119460105896\n", + "\n", + "Single audio comparison saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\efficientnetv2_tflite_single_audio_comparison.json\n", + "\n", + "======================================================================\n", + "Dataset-based TFLite validation\n", + "======================================================================\n", + "Found 50 files for TFLite validation.\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9771 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9974 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9857 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9887 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.6044 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9494 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9920 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9930 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9931 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.2879 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9764 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9784 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.8370 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9337 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9372 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.7938 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9965 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.5156 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9752 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9252 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.8972 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9712 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9747 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9932 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9753 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9092 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthorhynchus tenuirostris | Confidence: 0.3176 | Correct: False\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthorhynchus tenuirostris | Confidence: 0.3172 | Correct: False\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Rhipidura albiscapa | Confidence: 0.3664 | Correct: False\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9859 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9858 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9440 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9828 | Correct: True\n", + "Actual: Acanthiza chrysorrhoa | TFLite Predicted: Acanthiza chrysorrhoa | Confidence: 0.9877 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9033 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9027 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.7625 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9472 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.8151 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9322 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9598 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9597 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9586 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.8345 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9795 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.8457 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9726 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9205 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.2848 | Correct: True\n", + "Actual: Acanthiza lineata | TFLite Predicted: Acanthiza lineata | Confidence: 0.9507 | Correct: True\n", + "\n", + "TFLite validation summary\n", + "-------------------------\n", + "Total files tested: 50\n", + "Correct predictions: 47\n", + "TFLite validation accuracy: 0.94\n", + "\n", + "TFLite dataset validation results saved to:\n", + "C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\efficientnetv2_tflite_dataset_validation_results.csv\n" + ] + } + ], + "source": [ + "\n", + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + ")\n", + "\n", + "%run validate_efficientnetv2_tflite_inference.py" + ] + }, + { + "cell_type": "markdown", + "id": "01bc2ac7-64c3-4d31-ac36-081bf3cbfcee", + "metadata": {}, + "source": [ + "## Validate TFLite-ReaL Engine intergration without MQTT" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a2526339-d8b0-4f09-b12c-1185813a8645", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "helpers.melspectrogram_to_cam import skipped: No module named 'helpers'\n", + "Python Version : 3.9.25\n", + "TensorFlow Version : 2.10.0\n", + "Librosa Version : 0.9.2\n", + "Echo Engine configuration successfully loaded\n", + "Echo Engine credentials successfully loaded\n", + "Failed to establish database connection\n", + "EfficientNetV2 TFLite model loaded successfully.\n", + "EfficientNetV2 input shape: [ 1 1 128 313]\n", + "EfficientNetV2 output shape: [ 1 123]\n", + "Predicted class: Acanthiza chrysorrhoa\n", + "Predicted probability: 93.37\n", + "Sample rate: 32000\n", + "Processed audio length: 160000\n", + "Top predictions: [{'index': 0, 'label': 'Acanthiza chrysorrhoa', 'confidence': 0.9337119460105896}, {'index': 47, 'label': 'Dicaeum hirundinaceum', 'confidence': 0.022238129749894142}, {'index': 3, 'label': 'Acanthiza pusilla', 'confidence': 0.007062216755002737}, {'index': 5, 'label': 'Acanthiza uropygialis', 'confidence': 0.00612929742783308}, {'index': 103, 'label': 'Rhipidura rufifrons', 'confidence': 0.005970010533928871}]\n" + ] + } + ], + "source": [ + "\n", + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\"\n", + ")\n", + "\n", + "from light_echo_engine_efficientnetv2_tflite import EchoEngine\n", + "\n", + "engine = EchoEngine()\n", + "\n", + "AUDIO_PATH = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3\"\n", + "\n", + "with open(AUDIO_PATH, \"rb\") as f:\n", + " audio_bytes = f.read()\n", + "\n", + "predicted_class, predicted_probability, processed_audio, sample_rate, top_predictions = (\n", + " engine.efficientnetv2_tflite_predict_from_audio_bytes(audio_bytes)\n", + ")\n", + "\n", + "print(\"Predicted class:\", predicted_class)\n", + "print(\"Predicted probability:\", predicted_probability)\n", + "print(\"Sample rate:\", sample_rate)\n", + "print(\"Processed audio length:\", len(processed_audio))\n", + "print(\"Top predictions:\", top_predictions)" + ] + }, + { + "cell_type": "markdown", + "id": "9d652ef9-af62-4d98-9a2d-0551ecfd8d16", + "metadata": {}, + "source": [ + "## Validate TFLite-ReaL Engine intergration with fake MQTT" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9cb2b9d4-4499-48ef-b15d-9a6359dd92c4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "helpers.melspectrogram_to_cam import skipped: No module named 'helpers'\n", + "Python Version : 3.9.25\n", + "TensorFlow Version : 2.10.0\n", + "Librosa Version : 0.9.2\n", + "Echo Engine configuration successfully loaded\n", + "Echo Engine credentials successfully loaded\n", + "Failed to establish database connection\n", + "EfficientNetV2 TFLite model loaded successfully.\n", + "EfficientNetV2 input shape: [ 1 1 128 313]\n", + "EfficientNetV2 output shape: [ 1 123]\n", + "Recieved audio message, processing via engine model...\n", + "2026-04-30T10:00:00\n", + "EfficientNetV2_TFLite_Mode\n", + "Predicted class : Acanthiza chrysorrhoa\n", + "Predicted probability : 93.37\n", + "Top predictions : [{'index': 0, 'label': 'Acanthiza chrysorrhoa', 'confidence': 0.9337119460105896}, {'index': 47, 'label': 'Dicaeum hirundinaceum', 'confidence': 0.022238129749894142}, {'index': 3, 'label': 'Acanthiza pusilla', 'confidence': 0.007062216755002737}, {'index': 5, 'label': 'Acanthiza uropygialis', 'confidence': 0.00612929742783308}, {'index': 103, 'label': 'Rhipidura rufifrons', 'confidence': 0.005970010533928871}]\n", + "An error occurred: HTTPConnectionPool(host='ts-api-cont', port=9000): Max retries exceeded with url: /engine/event (Caused by NameResolutionError(\"HTTPConnection(host='ts-api-cont', port=9000): Failed to resolve 'ts-api-cont' ([Errno 11001] getaddrinfo failed)\"))\n" + ] + } + ], + "source": [ + "import json\n", + "import base64\n", + "\n", + "os.chdir(\n", + " r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\"\n", + ")\n", + "\n", + "from light_echo_engine_efficientnetv2_tflite import EchoEngine\n", + "\n", + "engine = EchoEngine()\n", + "\n", + "AUDIO_PATH = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3\"\n", + "\n", + "with open(AUDIO_PATH, \"rb\") as f:\n", + " audio_bytes = f.read()\n", + "\n", + "audio_base64 = base64.b64encode(audio_bytes).decode(\"utf-8\")\n", + "\n", + "test_audio_event = {\n", + " \"timestamp\": \"2026-04-30T10:00:00\",\n", + " \"audioFile\": \"EfficientNetV2_TFLite_Mode\",\n", + " \"audioClip\": audio_base64,\n", + " \"sensorId\": \"test_sensor_001\",\n", + " \"microphoneLLA\": [-37.8136, 144.9631, 0],\n", + " \"animalEstLLA\": [-37.8136, 144.9631, 0],\n", + " \"animalTrueLLA\": [-37.8136, 144.9631, 0],\n", + " \"animalLLAUncertainty\": 0\n", + "}\n", + "\n", + "\n", + "class FakeMQTTMessage:\n", + " def __init__(self, payload):\n", + " self.payload = payload\n", + "\n", + "\n", + "fake_msg = FakeMQTTMessage(\n", + " payload=json.dumps(test_audio_event).encode(\"utf-8\")\n", + ")\n", + "\n", + "engine.on_message(None, None, fake_msg)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8de1190b-5a20-44ce-98da-e6c31f305263", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "python 3.9.25 (main, Nov 3 2025, 22:44:01) [MSC v.1929 64 bit (AMD64)]\n", + "numpy==1.23.5\n", + "tensorflow==2.10.0\n", + "librosa==0.9.2\n", + "soundfile==0.13.1\n", + "paho-mqtt==2.1.0\n", + "requests==2.32.5\n", + "pymongo==4.17.0\n", + "google-cloud-storage==3.9.0\n", + "geopy==2.4.1\n", + "pandas==2.3.3\n", + "scikit-learn==1.6.1\n", + "diskcache==5.6.3\n" + ] + } + ], + "source": [ + "import sys\n", + "\n", + "packages = [\n", + " (\"numpy\", \"numpy\"),\n", + " (\"tensorflow\", \"tensorflow\"),\n", + " (\"librosa\", \"librosa\"),\n", + " (\"soundfile\", \"soundfile\"),\n", + " (\"paho-mqtt\", \"paho.mqtt\"),\n", + " (\"requests\", \"requests\"),\n", + " (\"pymongo\", \"pymongo\"),\n", + " (\"google-cloud-storage\", \"google.cloud.storage\"),\n", + " (\"geopy\", \"geopy\"),\n", + " (\"pandas\", \"pandas\"),\n", + " (\"scikit-learn\", \"sklearn\"),\n", + " (\"diskcache\", \"diskcache\"),\n", + "]\n", + "\n", + "print(\"python\", sys.version)\n", + "\n", + "for package_name, import_name in packages:\n", + " try:\n", + " module = __import__(import_name, fromlist=[\"dummy\"])\n", + " version = getattr(module, \"__version__\", \"installed\")\n", + " print(f\"{package_name}=={version}\")\n", + " except Exception as e:\n", + " print(f\"{package_name}: NOT FOUND ({e})\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (projectecho)", + "language": "python", + "name": "projectecho" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.25" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/test_efficientnetv2_predictor.ipynb b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/test_efficientnetv2_predictor.ipynb new file mode 100644 index 000000000..8e74616c5 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/test_efficientnetv2_predictor.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0df6bc7e", + "metadata": {}, + "source": [ + "# Test EfficientNetV2 Predictor in Jupyter\n", + "\n", + "Use this notebook to test `efficientnetv2_predictor.py`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e4d242d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current folder: C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\n" + ] + } + ], + "source": [ + "import os\n", + "\n", + "PROJECT_FOLDER = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\"\n", + "\n", + "os.chdir(PROJECT_FOLDER)\n", + "print(\"Current folder:\", os.getcwd())" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "235196ae", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\librosa\\util\\files.py:10: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.\n", + " from pkg_resources import resource_filename\n", + "C:\\Users\\krish\\.conda\\envs\\projectecho\\lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Predictor functions imported successfully.\n" + ] + } + ], + "source": [ + "from efficientnetv2_predictor import (\n", + " load_predictor_bundle,\n", + " predict_audio\n", + ")\n", + "\n", + "print(\"Predictor functions imported successfully.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e822c15a", + "metadata": {}, + "outputs": [], + "source": [ + "MODEL_PATH = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\efficientnetv2_project_echo.pt\"\n", + "\n", + "CLASS_MAPPING_PATH = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\class_mapping.json\"\n", + "\n", + "PREPROCESS_CONFIG_PATH = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\engine\\torch_impl\\Integrate_EfficientNetV2_Engine\\_trained_models\\preprocess_config.json\"\n", + "\n", + "# Change this to one real audio file from your dataset\n", + "AUDIO_PATH = r\"C:\\Deakin\\ProjectEcho\\Project-Echo\\src\\Prototypes\\data\\data_files\\Acanthiza chrysorrhoa\\region_3.650-4.900.mp3\"" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "645fba0c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Model and config files loaded successfully.\n", + "Device: cpu\n", + "Number of classes: 123\n" + ] + } + ], + "source": [ + "model, class_mapping, preprocess_config, device = load_predictor_bundle(\n", + " MODEL_PATH,\n", + " CLASS_MAPPING_PATH,\n", + " PREPROCESS_CONFIG_PATH\n", + ")\n", + "\n", + "print(\"Model and config files loaded successfully.\")\n", + "print(\"Device:\", device)\n", + "print(\"Number of classes:\", len(class_mapping[\"classes\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "7f7cd914", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'audio_path': 'C:\\\\Deakin\\\\ProjectEcho\\\\Project-Echo\\\\src\\\\Prototypes\\\\data\\\\data_files\\\\Acanthiza chrysorrhoa\\\\region_3.650-4.900.mp3',\n", + " 'predicted_index': 0,\n", + " 'predicted_label': 'Acanthiza chrysorrhoa',\n", + " 'confidence': 0.9337119460105896,\n", + " 'top_predictions': [{'index': 0,\n", + " 'label': 'Acanthiza chrysorrhoa',\n", + " 'confidence': 0.9337119460105896},\n", + " {'index': 47,\n", + " 'label': 'Dicaeum hirundinaceum',\n", + " 'confidence': 0.02223811112344265},\n", + " {'index': 3,\n", + " 'label': 'Acanthiza pusilla',\n", + " 'confidence': 0.007062176708132029},\n", + " {'index': 5,\n", + " 'label': 'Acanthiza uropygialis',\n", + " 'confidence': 0.006129274610430002},\n", + " {'index': 103,\n", + " 'label': 'Rhipidura rufifrons',\n", + " 'confidence': 0.0059699914418160915}]}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result = predict_audio(\n", + " AUDIO_PATH,\n", + " model,\n", + " class_mapping,\n", + " preprocess_config,\n", + " device=device\n", + ")\n", + "\n", + "result" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.25" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_dataset_inference.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_dataset_inference.py new file mode 100644 index 000000000..dfbd43cc9 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_dataset_inference.py @@ -0,0 +1,189 @@ +""" +Dataset validation script for EfficientNetV2 inference. + +This script validates the saved EfficientNetV2 model using labelled dataset audio files. +It checks that: +1. The saved model can be loaded. +2. The class mapping can be loaded. +3. The preprocessing configuration can be loaded. +4. Dataset audio files can be preprocessed. +5. The model returns predicted labels and confidence values. +6. Predictions can be compared with the actual dataset folder labels. + +This is dataset-based validation only. +""" + +import json +import pathlib +import pandas as pd + +from efficientnetv2_predictor import ( + load_model, + load_class_mapping, + load_preprocess_config, + predict_audio +) + + +# ========================= +# Paths +# ========================= + +BASE_DIR = pathlib.Path(__file__).resolve().parent + +MODEL_DIR = BASE_DIR / "_trained_models" + +MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.pt" +CLASS_MAPPING_PATH = MODEL_DIR / "class_mapping.json" +PREPROCESS_CONFIG_PATH = MODEL_DIR / "preprocess_config.json" + +DATA_ROOT = pathlib.Path( + r"C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files" +) + +VALID_EXTS = {".wav", ".mp3", ".flac", ".ogg"} + +# Change this number if needed. +# Use 1 for a smoke test. +# Use 20, 50, or 100 for better dataset validation. +MAX_FILES = 50 + + +# ========================= +# Dataset scanning +# ========================= + +def scan_dataset(data_root, valid_exts, max_files=None): + """ + Scan dataset folder and collect audio files. + + Assumption: + Each class/species is stored as a folder name. + + Example: + data_files/ + Acanthiza chrysorrhoa/ + region_3.650-4.900.mp3 + """ + + rows = [] + + for audio_path in data_root.rglob("*"): + if audio_path.is_file() and audio_path.suffix.lower() in valid_exts: + if "_feature_cache" in audio_path.parts: + continue + if "_trained_models" in audio_path.parts: + continue + + actual_label = audio_path.parent.name + + rows.append({ + "audio_path": str(audio_path), + "actual_label": actual_label + }) + + if max_files is not None and len(rows) >= max_files: + break + + return rows + + +# ========================= +# Validation +# ========================= + +def validate_dataset_inference(): + print("Starting EfficientNetV2 dataset validation...") + + print("\nLoading model and configuration files...") + + model, device = load_model(MODEL_PATH) + class_mapping = load_class_mapping(CLASS_MAPPING_PATH) + preprocess_config = load_preprocess_config(PREPROCESS_CONFIG_PATH) + + print("Model loaded successfully.") + print("Device:", device) + print("Number of classes:", len(class_mapping["classes"])) + + print("\nScanning dataset files...") + dataset_rows = scan_dataset( + DATA_ROOT, + VALID_EXTS, + max_files=MAX_FILES + ) + + if len(dataset_rows) == 0: + raise RuntimeError("No audio files found. Please check DATA_ROOT path.") + + print(f"Found {len(dataset_rows)} audio files for validation.") + + results = [] + + for row in dataset_rows: + audio_path = row["audio_path"] + actual_label = row["actual_label"] + + try: + prediction = predict_audio( + audio_path=audio_path, + model=model, + class_mapping=class_mapping, + preprocess_config=preprocess_config, + device=device + ) + + predicted_label = prediction["predicted_label"] + confidence = prediction["confidence"] + + is_correct = actual_label == predicted_label + + results.append({ + "audio_path": audio_path, + "actual_label": actual_label, + "predicted_label": predicted_label, + "confidence": confidence, + "correct": is_correct + }) + + print( + f"Actual: {actual_label} | " + f"Predicted: {predicted_label} | " + f"Confidence: {confidence:.4f} | " + f"Correct: {is_correct}" + ) + + except Exception as e: + results.append({ + "audio_path": audio_path, + "actual_label": actual_label, + "predicted_label": None, + "confidence": None, + "correct": False, + "error": str(e) + }) + + print(f"Error processing {audio_path}: {e}") + + results_df = pd.DataFrame(results) + + total_files = len(results_df) + correct_count = results_df["correct"].sum() + accuracy = correct_count / total_files if total_files > 0 else 0 + + print("\nValidation summary") + print("------------------") + print("Total files tested:", total_files) + print("Correct predictions:", int(correct_count)) + print("Validation accuracy:", round(accuracy, 4)) + + output_path = BASE_DIR / "efficientnetv2_dataset_validation_results.csv" + results_df.to_csv(output_path, index=False) + + print("\nValidation results saved to:") + print(output_path) + + return results_df + + +if __name__ == "__main__": + validate_dataset_inference() \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_json_inference.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_json_inference.py new file mode 100644 index 000000000..81cd0dbd7 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_json_inference.py @@ -0,0 +1,148 @@ +""" +JSON-style validation script for EfficientNetV2 inference. + +This script simulates how the Engine may receive an MQTT/JSON message. +It validates that the saved EfficientNetV2 predictor can: +1. Read an incoming JSON-style payload. +2. Extract the audio file path. +3. Run preprocessing and inference. +4. Return a structured JSON-style prediction response. + +This is not connected to a live MQTT broker yet. +""" + +import json +import pathlib +from datetime import datetime + +from efficientnetv2_predictor import ( + load_model, + load_class_mapping, + load_preprocess_config, + predict_audio +) + + +# ========================= +# Paths +# ========================= + +BASE_DIR = pathlib.Path(__file__).resolve().parent +MODEL_DIR = BASE_DIR / "_trained_models" + +MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.pt" +CLASS_MAPPING_PATH = MODEL_DIR / "class_mapping.json" +PREPROCESS_CONFIG_PATH = MODEL_DIR / "preprocess_config.json" + + +# ========================= +# Example JSON/MQTT-style message +# ========================= + +SAMPLE_MESSAGE = { + "device_id": "sensor_001", + "audio_path": r"C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_3.650-4.900.mp3", + "timestamp": "2026-04-27T15:30:00", + "source": "dataset_validation" +} + + +# ========================= +# JSON validation functions +# ========================= + +def validate_input_message(message): + """ + Validate that the incoming JSON-style message contains the required fields. + """ + + required_fields = ["device_id", "audio_path", "timestamp"] + + missing_fields = [] + + for field in required_fields: + if field not in message: + missing_fields.append(field) + + if missing_fields: + raise ValueError(f"Missing required fields: {missing_fields}") + + audio_path = pathlib.Path(message["audio_path"]) + + if not audio_path.exists(): + raise FileNotFoundError(f"Audio file not found: {audio_path}") + + return True + + +def run_json_inference(message, model, device, class_mapping, preprocess_config): + """ + Run EfficientNetV2 inference from a JSON-style input message. + """ + + validate_input_message(message) + + prediction = predict_audio( + audio_path=message["audio_path"], + model=model, + class_mapping=class_mapping, + preprocess_config=preprocess_config, + device=device + ) + + response = { + "device_id": message["device_id"], + "input_timestamp": message["timestamp"], + "processed_timestamp": datetime.now().isoformat(timespec="seconds"), + "model": "EfficientNetV2", + "input_audio": prediction["audio_path"], + "prediction": { + "label": prediction["predicted_label"], + "class_index": prediction["predicted_index"], + "confidence": prediction["confidence"] + }, + "top_predictions": prediction.get("top_predictions", []), + "status": "success" + } + + return response + + +def main(): + print("Starting EfficientNetV2 JSON-style inference validation...") + + print("\nLoading model and configuration files...") + model, device = load_model(MODEL_PATH) + class_mapping = load_class_mapping(CLASS_MAPPING_PATH) + preprocess_config = load_preprocess_config(PREPROCESS_CONFIG_PATH) + + print("Model loaded successfully.") + print("Device:", device) + print("Number of classes:", len(class_mapping["classes"])) + + print("\nIncoming JSON-style message:") + print(json.dumps(SAMPLE_MESSAGE, indent=4)) + + print("\nRunning inference...") + response = run_json_inference( + message=SAMPLE_MESSAGE, + model=model, + device=device, + class_mapping=class_mapping, + preprocess_config=preprocess_config + ) + + print("\nInference response:") + print(json.dumps(response, indent=4)) + + output_path = BASE_DIR / "efficientnetv2_json_inference_result.json" + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(response, f, indent=4) + + print("\nJSON inference result saved to:") + print(output_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_mqtt_message_handler.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_mqtt_message_handler.py new file mode 100644 index 000000000..94fbde89f --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_mqtt_message_handler.py @@ -0,0 +1,172 @@ +""" +MQTT-style message handler validation for EfficientNetV2 inference. + +This script validates the part of the Engine flow that would handle an MQTT message. +It does not require a live MQTT broker. + +It simulates: +1. Receiving an MQTT payload as bytes. +2. Decoding the payload into JSON. +3. Extracting the audio file path. +4. Running EfficientNetV2 preprocessing and inference. +5. Returning a structured prediction response. + +Live MQTT broker integration can be added separately after this handler is verified. +""" + +import json +import pathlib +from datetime import datetime + +from efficientnetv2_predictor import ( + load_model, + load_class_mapping, + load_preprocess_config, + predict_audio +) + + +# ========================= +# Paths +# ========================= + +BASE_DIR = pathlib.Path(__file__).resolve().parent +MODEL_DIR = BASE_DIR / "_trained_models" + +MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.pt" +CLASS_MAPPING_PATH = MODEL_DIR / "class_mapping.json" +PREPROCESS_CONFIG_PATH = MODEL_DIR / "preprocess_config.json" + + +# ========================= +# Example MQTT-style payload +# ========================= + +SAMPLE_MQTT_MESSAGE = { + "device_id": "sensor_001", + "audio_path": r"C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_3.650-4.900.mp3", + "timestamp": "2026-04-27T15:30:00", + "source": "mqtt_simulation" +} + +# MQTT payload usually arrives as bytes +SAMPLE_MQTT_PAYLOAD = json.dumps(SAMPLE_MQTT_MESSAGE).encode("utf-8") + + +# ========================= +# Validation functions +# ========================= + +def decode_mqtt_payload(payload): + """ + Decode an MQTT payload from bytes into a Python dictionary. + """ + + if isinstance(payload, bytes): + payload = payload.decode("utf-8") + + message = json.loads(payload) + return message + + +def validate_mqtt_message(message): + """ + Validate required fields in the MQTT-style message. + """ + + required_fields = ["device_id", "audio_path", "timestamp"] + missing_fields = [] + + for field in required_fields: + if field not in message: + missing_fields.append(field) + + if missing_fields: + raise ValueError(f"Missing required fields: {missing_fields}") + + audio_path = pathlib.Path(message["audio_path"]) + + if not audio_path.exists(): + raise FileNotFoundError(f"Audio file not found: {audio_path}") + + return True + + +def handle_mqtt_message(payload, model, device, class_mapping, preprocess_config): + """ + Simulate the Engine's MQTT message handler. + + Input: + payload: MQTT message payload as bytes or string + + Output: + structured prediction response as dictionary + """ + + message = decode_mqtt_payload(payload) + validate_mqtt_message(message) + + prediction = predict_audio( + audio_path=message["audio_path"], + model=model, + class_mapping=class_mapping, + preprocess_config=preprocess_config, + device=device + ) + + response = { + "device_id": message["device_id"], + "input_timestamp": message["timestamp"], + "processed_timestamp": datetime.now().isoformat(timespec="seconds"), + "model": "EfficientNetV2", + "input_audio": prediction["audio_path"], + "prediction": { + "label": prediction["predicted_label"], + "class_index": prediction["predicted_index"], + "confidence": prediction["confidence"] + }, + "top_predictions": prediction.get("top_predictions", []), + "status": "success" + } + + return response + + +def main(): + print("Starting EfficientNetV2 MQTT message handler validation...") + + print("\nLoading model and configuration files...") + model, device = load_model(MODEL_PATH) + class_mapping = load_class_mapping(CLASS_MAPPING_PATH) + preprocess_config = load_preprocess_config(PREPROCESS_CONFIG_PATH) + + print("Model loaded successfully.") + print("Device:", device) + print("Number of classes:", len(class_mapping["classes"])) + + print("\nSimulated MQTT payload:") + print(SAMPLE_MQTT_PAYLOAD) + + print("\nRunning MQTT-style message handler...") + response = handle_mqtt_message( + payload=SAMPLE_MQTT_PAYLOAD, + model=model, + device=device, + class_mapping=class_mapping, + preprocess_config=preprocess_config + ) + + print("\nMQTT-style inference response:") + print(json.dumps(response, indent=4)) + + output_path = BASE_DIR / "efficientnetv2_mqtt_message_handler_result.json" + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(response, f, indent=4) + + print("\nMQTT message handler result saved to:") + print(output_path) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_tflite_inference.py b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_tflite_inference.py new file mode 100644 index 000000000..f9f8c6140 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/Integrate_EfficientNetV2_Engine/validate_efficientnetv2_tflite_inference.py @@ -0,0 +1,370 @@ +""" +TFLite validation script for EfficientNetV2 inference. + +This script validates the converted EfficientNetV2 TFLite model using the same +audio preprocessing and class mapping used by the PyTorch predictor. + +It performs: +1. Single audio validation +2. PyTorch vs TFLite output comparison +3. Dataset-based TFLite validation +4. CSV result export + +This confirms whether the converted TFLite model can be used as a production-ready +model candidate after PyTorch -> ONNX -> TensorFlow SavedModel -> TFLite conversion. +""" + +import json +import pathlib + +import numpy as np +import pandas as pd +import tensorflow as tf +import torch + +from efficientnetv2_predictor import ( + load_model, + load_class_mapping, + load_preprocess_config, + preprocess_audio, + predict_audio, +) + + +# ========================= +# Paths +# ========================= + +BASE_DIR = pathlib.Path(__file__).resolve().parent +MODEL_DIR = BASE_DIR / "_trained_models" + +PYTORCH_MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.pt" +TFLITE_MODEL_PATH = MODEL_DIR / "efficientnetv2_project_echo.tflite" +CLASS_MAPPING_PATH = MODEL_DIR / "class_mapping.json" +PREPROCESS_CONFIG_PATH = MODEL_DIR / "preprocess_config.json" + +DATA_ROOT = pathlib.Path( + r"C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files" +) + +VALID_EXTS = {".wav", ".mp3", ".flac", ".ogg"} + +# Change this if you want to validate more files. +MAX_FILES = 50 + +# Use one known sample first. +SAMPLE_AUDIO_PATH = pathlib.Path( + r"C:\Deakin\ProjectEcho\Project-Echo\src\Prototypes\data\data_files\Acanthiza chrysorrhoa\region_3.650-4.900.mp3" +) + + +# ========================= +# TFLite helper functions +# ========================= + +def load_tflite_interpreter(tflite_model_path): + """ + Load the TFLite model interpreter. + """ + + if not pathlib.Path(tflite_model_path).exists(): + raise FileNotFoundError(f"TFLite model not found: {tflite_model_path}") + + interpreter = tf.lite.Interpreter(model_path=str(tflite_model_path)) + interpreter.allocate_tensors() + + input_details = interpreter.get_input_details() + output_details = interpreter.get_output_details() + + return interpreter, input_details, output_details + + +def adapt_to_tflite_input(x_torch, input_shape, input_dtype): + """ + Convert PyTorch-style input tensor to the shape expected by the TFLite model. + + PyTorch preprocessing returns shape: + [1, 1, 128, 313] -> NCHW + + TFLite may expect: + [1, 1, 128, 313] -> NCHW + or: + [1, 128, 313, 1] -> NHWC + """ + + x = x_torch.detach().cpu().numpy().astype(np.float32) + + if tuple(x.shape) == tuple(input_shape): + return x.astype(input_dtype) + + x_nhwc = np.transpose(x, (0, 2, 3, 1)) + + if tuple(x_nhwc.shape) == tuple(input_shape): + return x_nhwc.astype(input_dtype) + + raise ValueError( + f"Could not adapt input shape. " + f"TFLite expects {input_shape}, but prepared NCHW={x.shape}, NHWC={x_nhwc.shape}" + ) + + +def run_tflite_prediction(audio_path, interpreter, input_details, output_details, class_mapping, preprocess_config): + """ + Run one TFLite prediction using the same preprocessing config. + """ + + input_index = input_details[0]["index"] + input_shape = tuple(input_details[0]["shape"]) + input_dtype = input_details[0]["dtype"] + + x_torch = preprocess_audio(audio_path, preprocess_config) + x_tflite = adapt_to_tflite_input(x_torch, input_shape, input_dtype) + + interpreter.set_tensor(input_index, x_tflite) + interpreter.invoke() + + output = interpreter.get_tensor(output_details[0]["index"]) + + logits_or_probs = output[0].astype(np.float32) + + # Apply softmax because exported model usually returns logits. + exp_values = np.exp(logits_or_probs - np.max(logits_or_probs)) + probs = exp_values / np.sum(exp_values) + + pred_index = int(np.argmax(probs)) + confidence = float(probs[pred_index]) + + index_to_label = class_mapping["index_to_label"] + pred_label = index_to_label[str(pred_index)] + + top_indices = np.argsort(probs)[::-1][:5] + + top_predictions = [ + { + "index": int(i), + "label": str(index_to_label[str(int(i))]), + "confidence": float(probs[i]) + } + for i in top_indices + ] + + return { + "audio_path": str(audio_path), + "predicted_index": int(pred_index), + "predicted_label": str(pred_label), + "confidence": float(confidence), + "top_predictions": top_predictions, + "raw_output_shape": [int(v) for v in output.shape], + "tflite_input_shape": [int(v) for v in input_shape], + } + + +# ========================= +# Dataset scanning +# ========================= + +def scan_dataset(data_root, valid_exts, max_files=None): + """ + Scan dataset folder and collect audio files. + + Assumption: + folder name = actual species label + """ + + rows = [] + + for audio_path in data_root.rglob("*"): + if audio_path.is_file() and audio_path.suffix.lower() in valid_exts: + if "_feature_cache" in audio_path.parts: + continue + if "_trained_models" in audio_path.parts: + continue + + rows.append({ + "audio_path": str(audio_path), + "actual_label": audio_path.parent.name + }) + + if max_files is not None and len(rows) >= max_files: + break + + return rows + + +# ========================= +# Validation functions +# ========================= + +def single_audio_comparison(): + """ + Compare PyTorch and TFLite prediction on one sample audio file. + """ + + print("\n" + "=" * 70) + print("Single audio PyTorch vs TFLite validation") + print("=" * 70) + + device = "cuda" if torch.cuda.is_available() else "cpu" + + pytorch_model, device = load_model(PYTORCH_MODEL_PATH, device=device) + class_mapping = load_class_mapping(CLASS_MAPPING_PATH) + preprocess_config = load_preprocess_config(PREPROCESS_CONFIG_PATH) + + interpreter, input_details, output_details = load_tflite_interpreter(TFLITE_MODEL_PATH) + + print("PyTorch model loaded.") + print("TFLite model loaded.") + print("Device:", device) + print("Number of classes:", len(class_mapping["classes"])) + + print("\nTFLite input details:") + print(input_details[0]) + + print("\nTFLite output details:") + print(output_details[0]) + + pytorch_result = predict_audio( + audio_path=SAMPLE_AUDIO_PATH, + model=pytorch_model, + class_mapping=class_mapping, + preprocess_config=preprocess_config, + device=device + ) + + tflite_result = run_tflite_prediction( + audio_path=SAMPLE_AUDIO_PATH, + interpreter=interpreter, + input_details=input_details, + output_details=output_details, + class_mapping=class_mapping, + preprocess_config=preprocess_config + ) + + print("\nSample audio:") + print(SAMPLE_AUDIO_PATH) + + print("\nPyTorch prediction:") + print(json.dumps(pytorch_result, indent=4)) + + print("\nTFLite prediction:") + print(json.dumps(tflite_result, indent=4)) + + label_match = pytorch_result["predicted_label"] == tflite_result["predicted_label"] + + print("\nComparison summary:") + print("PyTorch label:", pytorch_result["predicted_label"]) + print("TFLite label:", tflite_result["predicted_label"]) + print("Labels match:", label_match) + print("PyTorch confidence:", pytorch_result["confidence"]) + print("TFLite confidence:", tflite_result["confidence"]) + + output_path = BASE_DIR / "efficientnetv2_tflite_single_audio_comparison.json" + + comparison = { + "sample_audio": str(SAMPLE_AUDIO_PATH), + "pytorch_result": pytorch_result, + "tflite_result": tflite_result, + "labels_match": label_match + } + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(comparison, f, indent=4) + + print("\nSingle audio comparison saved to:") + print(output_path) + + +def dataset_tflite_validation(): + """ + Validate the TFLite model using multiple labelled dataset audio files. + """ + + print("\n" + "=" * 70) + print("Dataset-based TFLite validation") + print("=" * 70) + + class_mapping = load_class_mapping(CLASS_MAPPING_PATH) + preprocess_config = load_preprocess_config(PREPROCESS_CONFIG_PATH) + + interpreter, input_details, output_details = load_tflite_interpreter(TFLITE_MODEL_PATH) + + dataset_rows = scan_dataset(DATA_ROOT, VALID_EXTS, max_files=MAX_FILES) + + if len(dataset_rows) == 0: + raise RuntimeError("No audio files found. Please check DATA_ROOT.") + + print(f"Found {len(dataset_rows)} files for TFLite validation.") + + results = [] + + for row in dataset_rows: + audio_path = row["audio_path"] + actual_label = row["actual_label"] + + try: + result = run_tflite_prediction( + audio_path=audio_path, + interpreter=interpreter, + input_details=input_details, + output_details=output_details, + class_mapping=class_mapping, + preprocess_config=preprocess_config + ) + + predicted_label = result["predicted_label"] + confidence = result["confidence"] + correct = actual_label == predicted_label + + results.append({ + "audio_path": audio_path, + "actual_label": actual_label, + "predicted_label": predicted_label, + "confidence": confidence, + "correct": correct + }) + + print( + f"Actual: {actual_label} | " + f"TFLite Predicted: {predicted_label} | " + f"Confidence: {confidence:.4f} | " + f"Correct: {correct}" + ) + + except Exception as e: + results.append({ + "audio_path": audio_path, + "actual_label": actual_label, + "predicted_label": None, + "confidence": None, + "correct": False, + "error": str(e) + }) + + print(f"Error processing {audio_path}: {e}") + + results_df = pd.DataFrame(results) + + total_files = len(results_df) + correct_count = int(results_df["correct"].sum()) + accuracy = correct_count / total_files if total_files > 0 else 0 + + print("\nTFLite validation summary") + print("-------------------------") + print("Total files tested:", total_files) + print("Correct predictions:", correct_count) + print("TFLite validation accuracy:", round(accuracy, 4)) + + output_path = BASE_DIR / "efficientnetv2_tflite_dataset_validation_results.csv" + results_df.to_csv(output_path, index=False) + + print("\nTFLite dataset validation results saved to:") + print(output_path) + + +def main(): + single_audio_comparison() + dataset_tflite_validation() + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/helpers/melspectrogram_to_cam.py b/src/Prototypes/engine/torch_impl/helpers/melspectrogram_to_cam.py new file mode 100644 index 000000000..af24c6dd1 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/helpers/melspectrogram_to_cam.py @@ -0,0 +1,92 @@ +import numpy as np +import cv2 + +# TODO: Find a alternative way, as installation of torch take a lot of storage +# import torch +# from torchvision import models, transforms +models=None +torch = None +transforms=None +def convert(image): + # Load a pre-trained ResNet model from torchvision and set it to evaluation mode + model = models.resnet18(pretrained=True) + model.eval() + + # Ensure Mel spectrogram is a 3-channel image + if len(image.shape) == 2: # If it's a 2D array, convert to 3 channels + image = np.stack([image] * 3, axis=-1) + + # Resize the Mel spectrogram to match the expected input size for ResNet (224x224) + resized_image = cv2.resize(image, (224, 224)) + + # Convert the resized image to a PyTorch tensor and normalize + preprocess = transforms.Compose([ + transforms.ToTensor(), # Convert image to a PyTorch tensor + transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), # Normalize + ]) + input_tensor = preprocess(resized_image).unsqueeze(0) # Add a batch dimension + + # Save the features of the last convolutional layer for generating CAM + class SaveFeatures: + def __init__(self, module): + self.hook = module.register_forward_hook(self.hook_fn) + def hook_fn(self, module, input, output): + self.features = output.data.numpy() # Save the output features as a numpy array + def remove(self): + self.hook.remove() # Remove the hook when done + + # Hook to the final convolutional layer of ResNet + final_layer = model._modules.get('layer4') + activated_features = SaveFeatures(final_layer) + + # Perform a forward pass through the model to get the predictions + with torch.no_grad(): + output = model(input_tensor) # Get model output + probabilities = torch.nn.functional.softmax(output[0], dim=0) + + # Get the predicted class index (the one with the highest probability) + pred_class = probabilities.argmax().item() + print(f'Predicted class: {pred_class}') + + # Remove the hook after getting the features + activated_features.remove() + + # Generate CAM + def get_cam(feature_conv, weight_fc, class_idx): + _, nc, h, w = feature_conv.shape + cam = weight_fc[class_idx].dot(feature_conv.reshape((nc, h * w))) + cam = cam.reshape(h, w) + cam = cam - np.min(cam) + cam_img = cam / np.max(cam) + return cam_img + + weight_softmax_params = list(model._modules.get('fc').parameters()) + weight_softmax = np.squeeze(weight_softmax_params[0].data.numpy()) + + cam = get_cam(activated_features.features, weight_softmax, pred_class) + + # Resize CAM to match the original image dimensions + input_height, input_width = image.shape[:2] + cam_resized = cv2.resize(cam, (input_width, input_height)) + + # Apply heatmap + heatmap = cv2.applyColorMap(np.uint8(255 * cam_resized), cv2.COLORMAP_JET) + + # Convert image to uint8 type + image_uint8 = np.uint8(255 * image / np.max(image)) + + # Overlay CAM on the image + alpha = 0.5 # Transparency factor + overlayed_image = cv2.addWeighted(image_uint8, alpha, heatmap, 1 - alpha, 0) + + # Draw bounding box + # Threshold CAM to find the region with significant activations + threshold = np.max(cam_resized) * 0.5 # Adjust threshold as needed + mask = cam_resized > threshold + y_indices, x_indices = np.where(mask) + x_min, x_max = np.min(x_indices), np.max(x_indices) + y_min, y_max = np.min(y_indices), np.max(y_indices) + cv2.rectangle(overlayed_image, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2) + + # Return the final processed image + return overlayed_image diff --git a/src/Prototypes/engine/torch_impl/helpers/tesing2.py.ipynb b/src/Prototypes/engine/torch_impl/helpers/tesing2.py.ipynb new file mode 100644 index 000000000..634d2f20b --- /dev/null +++ b/src/Prototypes/engine/torch_impl/helpers/tesing2.py.ipynb @@ -0,0 +1,287 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 12, + "id": "695b8c80", + "metadata": {}, + "outputs": [], + "source": [ + "import melspectrogram_to_cam\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "import librosa\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "05a9cbcf", + "metadata": {}, + "outputs": [], + "source": [ + "# Generate a sample Mel spectrogram\n", + "y, sr = librosa.load(librosa.example('trumpet'))\n", + "mel_spectrogram = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128, fmax=8000)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "4346432e", + "metadata": {}, + "outputs": [], + "source": [ + "# Convert to decibel scale\n", + "mel_spectrogram_db = librosa.power_to_db(mel_spectrogram, ref=np.max)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "09633676", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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3/3ud9i5cyeuuuqqg120A8KXvvQlvPLKK/jZz362x3PK5TL+7u/+Dl//+texcOHCaSzdocOiRYvw0ksv4ZlnnsHnP/95XHrppXj99dcPdrGmhcnU/UiYa7u7u/H3f//3uPvuu+H3+yd9/eE6306lXY60+VY4TDmoQrV3Cfl83nK5XNb9998/If/LX/6y9f73v/8glWp6mErdP/WpT1nz58+fkPf6669bAKwtW7YcsLIeLDCJPS733nuvFQgErP/5n/85sIU6BNifdrnkkkusv/7rv56Q9+STT1oArL6+vgNYuunnS1/6kjVz5kxrx44dez1vfHzcAmC5XC7nxzAMJ+8Pf/jDNJX40OGss86yPve5zx3sYhwU9lb3I2GuXb9+fdX7AMAyDMNyuVxWqVTa47WH83w7lXY5kuZb4fBFLC77gdfrxcqVK/HII49MyH/kkUdw6qmnHqRSTQ9Tqfv73vc+9PX1IZVKOXlbtmyBaZqYOXPmAS3voczPfvYzfPrTn8Z///d/i6ZYk8lkYJoTpyGXywVAWfUOByzLwpe+9CXcf//9+OMf/4h58+bt9fy6ujps2rQJL730kvNz1VVXOSvxJ5100jSV/NDBsizk8/mDXYyDwt7qfiTMtWeddVbV+3DCCSfg4osvxksvveTMF2/lcJ9vp9IuR8J8KxwBHNTPpncR9957r+XxeKx169ZZr7/+unXNNddYoVDI6urqOthFO+Dsq+7/+I//aH3yk590zk8mk9bMmTOtv/7rv7Zee+016/HHH7eOPvpo6/LLLz9YVXjHSSaT1osvvmi9+OKLFgDr+9//vvXiiy9au3btsiyruk3++7//23K73da///u/W/39/c5PLBY7WFU4IEy2XX7yk59Ybrfbuu2226zt27dbTz31lHXCCSdYJ5544sGqwjvO5z//eSsajVobNmyY0PeZTMY5563t8laOJK9i119/vfXEE09YO3futF555RXra1/7mmWapvXwww8f7KIdcPZV9yNxrq3FW71nHanz7VvZV7scCfOtcPgjHy6T4N///d+tOXPmWF6v1zr++OP3y53p4cLe6n7ppZdaZ5xxxoTz33jjDevss8+2AoGANXPmTOurX/3qhP9Re7fz2GOPWQCqfi699FLLsqrb5Iwzztjr+YcLk20Xy1LuOJcuXWoFAgGro6PDuvjii62enp7pL/wBolZ7ALB+8pOfOOfUahfOkfTh8tnPftaZa1paWqyzzjrriPhosax91/1InGtr8db/QT9S59u3sq92sazDf74VDn8MyxL7oCAIgiAIgiAIhzayx0UQBEEQBEEQhEMe+XARBEEQBEEQBOGQRz5cBEEQBEEQBEE45JEPF0EQBEEQBEEQDnnkw0UQBEEQBEEQhEMe+XARBEEQBEEQBOGQRz5cBEEQBEEQBEE45JEPF0EQBEEQBEEQDnnkw0UQBOEw5qabbsKKFSsOdjEEQRAE4W1jWJZlHexCCIIgCJPHMIy9Hr/00kvxb//2b8jn82hqapqmUgmCIAjCgUE+XARBEN6lDAwMOOn77rsP//RP/4TNmzc7eYFAANFo9GAUTRAEQRDecUQqJgiC8C6lvb3d+YlGozAMoyrvrVKxT3/60/jIRz6Cb33rW2hra0N9fT2+/vWvo1QqYe3atWhsbMTMmTPx4x//eMKzent78YlPfAINDQ1oamrChz/8YXR1dU1vhQVBEIQjGvlwEQRBOML44x//iL6+PjzxxBP4/ve/j5tuugkXXHABGhoa8Oyzz+Kqq67CVVddhe7ubgBAJpPBmWeeiXA4jCeeeAJPPfUUwuEwPvjBD6JQKBzk2giCIAhHCvLhIgiCcITR2NiIH/7wh1i0aBE++9nPYtGiRchkMvja176Go48+Gtdffz28Xi/+9Kc/AQDuvfdemKaJH/3oR1i+fDmWLFmCn/zkJ9i9ezc2bNhwcCsjCIIgHDG4D3YBBEEQhOnlmGOOgWnSulVbWxuWLVvm/O5yudDU1IShoSEAwMaNG7Ft2zZEIpEJ98nlcti+ffv0FFoQBEE44pEPF0EQhCMMj8cz4XfDMGrmVSoVAEClUsHKlStxzz33VN2rpaXlwBVUEARBEBjy4SIIgiDsleOPPx733XcfWltbUVdXd7CLIwiCIByhyB4XQRAEYa9cfPHFaG5uxoc//GE8+eST2LlzJx5//HH8/d//PXp6eg528QRBEIQjBPlwEQRBEPZKMBjEE088gdmzZ+NjH/sYlixZgs9+9rPIZrNigREEQRCmDQlAKQiCIAiCIAjCIY9YXARBEARBEARBOOSRDxdBEARBEARBEA555MNFEARBEARBEIRDHvlwEQRBEARBEAThkEc+XARBEARBEARBOOSRDxdBEARBEARBEA555MNFEARBEARBEIRDHvlwEQRBEARBEAThkEc+XARBEARBEARBOOSRDxdBEARBEARBEA555MNFEARBEARBEIRDnv8f+k1htSuuAfgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Plot the original Mel spectrogram\n", + "plt.figure(figsize=(10, 4))\n", + "librosa.display.specshow(mel_spectrogram_db, sr=sr, x_axis='time', y_axis='mel', fmax=8000)\n", + "plt.colorbar(format='%+2.0f dB')\n", + "plt.title('Mel Spectrogram (in dB)')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "44332e6b", + "metadata": {}, + "outputs": [], + "source": [ + "# Convert to tensor and add batch dimension\n", + "mel_spectrogram_tensor = tf.expand_dims(tf.convert_to_tensor(mel_spectrogram_db), -1)\n", + "mel_spectrogram_tensor = tf.repeat(mel_spectrogram_tensor, 3, axis=-1) # Repeat to get 3 channels\n", + "mel_spectrogram_tensor = tf.expand_dims(mel_spectrogram_tensor, 0) # Add batch dimension" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "99a03f53", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mel spectrogram tensor shape: (1, 128, 230, 3)\n" + ] + } + ], + "source": [ + "# Check the dimensions before passing to the convert function\n", + "print(f\"Mel spectrogram tensor shape: {mel_spectrogram_tensor.shape}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c5eca1c7", + "metadata": {}, + "outputs": [], + "source": [ + "# Call the convert function\n", + "overlayed_image = melspectrogram_to_cam.convert(mel_spectrogram_tensor)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "8db4d8af", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Display or save the result\n", + "plt.figure(figsize=(10, 4))\n", + "plt.imshow(overlayed_image)\n", + "plt.title(\"CAM Overlay on Mel Spectrogram\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "98ae1bd3", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import cv2\n", + "import torch\n", + "from PIL import Image\n", + "from torchvision import models, transforms\n", + "def main(image_path):\n", + " # Load a pre-trained ResNet model from torchvision and set it to evaluation mode\n", + " model = models.resnet18(pretrained=True)\n", + " model.eval()\n", + " # Define image preprocessing\n", + " preprocess = transforms.Compose([\n", + " transforms.Resize((224, 224)),# Resize image to 224x224 pixels\n", + " transforms.ToTensor(), # Convert image to a PyTorch tensor\n", + " transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),# Normalize with mean and std\n", + " ])\n", + " # Load and preprocess image\n", + " image_path = image_path\n", + " image = Image.open(image_path).convert('RGB') # Open and convert image to RGB\n", + " input_tensor = preprocess(image).unsqueeze(0) # Preprocess and add a batch dimension\n", + " # Save the features of the last convolutional layer for generating CAM\n", + " class SaveFeatures:\n", + " def __init__(self, module):\n", + " self.hook = module.register_forward_hook(self.hook_fn)\n", + " def hook_fn(self, module, input, output):\n", + " self.features = output.data.numpy() # Save the output features as a numpy array\n", + " def remove(self):\n", + " self.hook.remove()# Remove the hook when done\n", + "\n", + " # Hook to the final convolutional layer of ResNet\n", + " final_layer = model._modules.get('layer4')\n", + " activated_features = SaveFeatures(final_layer)\n", + " # Perform a forward pass through the model to get the predictions\n", + " with torch.no_grad():\n", + " output = model(input_tensor) # Get model output\n", + " probabilities = torch.nn.functional.softmax(output[0], dim=0)\n", + "\n", + " # Get the predicted class index (the one with the highest probability)\n", + " pred_class = probabilities.argmax().item()\n", + " print(f'Predicted class: {pred_class}')\n", + " # Remove the hook after getting the features\n", + " activated_features.remove()\n", + " # Get the predicted class index (the one with the highest probability)\n", + " pred_class = probabilities.argmax().item()\n", + " def get_cam(feature_conv, weight_fc, class_idx):\n", + " _, nc, h, w = feature_conv.shape\n", + " cam = weight_fc[class_idx].dot(feature_conv.reshape((nc, h * w)))\n", + " cam = cam.reshape(h, w)\n", + " cam = cam - np.min(cam)\n", + " cam_img = cam / np.max(cam)\n", + " return cam_img\n", + "\n", + " weight_softmax_params = list(model._modules.get('fc').parameters())\n", + " weight_softmax = np.squeeze(weight_softmax_params[0].data.numpy())\n", + "\n", + " cam = get_cam(activated_features.features, weight_softmax, pred_class)\n", + " import cv2\n", + "\n", + " mel_spectrogram_path = image_path\n", + " mel_spectrogram = cv2.imread(mel_spectrogram_path, cv2.IMREAD_GRAYSCALE)\n", + "\n", + " # Ensure Mel spectrogram is a 3-channel image\n", + " if len(mel_spectrogram.shape) == 2:\n", + " mel_spectrogram = cv2.cvtColor(mel_spectrogram, cv2.COLOR_GRAY2BGR)\n", + " # Resize CAM to match Mel spectrogram dimensions\n", + " input_height, input_width = mel_spectrogram.shape[:2]\n", + " cam_resized = cv2.resize(cam, (input_width, input_height))\n", + "\n", + " # Apply heatmap\n", + " heatmap = cv2.applyColorMap(np.uint8(255 * cam_resized), cv2.COLORMAP_JET)\n", + "\n", + " # Overlay CAM on Mel spectrogram\n", + " alpha = 0.5 # Transparency factor\n", + " overlayed_image = cv2.addWeighted(mel_spectrogram, alpha, heatmap, 1 - alpha, 0)\n", + "\n", + " # Draw bounding box\n", + " # Threshold CAM to find the region with significant activations\n", + " threshold = np.max(cam_resized) * 0.5 # Adjust threshold as needed\n", + " mask = cam_resized > threshold\n", + " y_indices, x_indices = np.where(mask)\n", + " x_min, x_max = np.min(x_indices), np.max(x_indices)\n", + " y_min, y_max = np.min(y_indices), np.max(y_indices)\n", + " cv2.rectangle(overlayed_image, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2)\n", + " plt.imshow(cv2.cvtColor(overlayed_image, cv2.COLOR_BGR2RGB))\n", + " plt.title(f'Predicted Class: {pred_class}')\n", + " plt.axis('off')\n", + " plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "7cc97b3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Predicted class: 769\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgMAAADuCAYAAACkngQiAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9a7RtV3Hf+as551r7nPvUvZLQlQSSjBBCIAlbgBACBBgcQg8wNp120umOMbHddtLJiOM4Dyd2wLFje9hOYo8enXxxDLbjpOkRdxi2MxocegAmgIxAICTAPPVCD9DrSvdxzt5rzln9oeaca+1zzpXu1QsJ7RKbe/ba6zHXa1bVv/5VJaqqrGQlK1nJSlaykmesuG/3AFaykpWsZCUrWcm3V1bGwEpWspKVrGQlz3BZGQMrWclKVrKSlTzDZWUMrGQlK1nJSlbyDJeVMbCSlaxkJStZyTNcVsbASlaykpWsZCXPcFkZAytZyUpWspKVPMNlZQysZCUrWclKVvIMl5UxsJKVrGQlK1nJM1xWxsBKnpbynve8BxFpnxACz372s3nHO97BHXfc8aSM4YILLuBHfuRH2vcPf/jDiAgf/vCHT2k/H//4x3nXu97F4cOHH9fxAfzIj/wIF1xwwUmtm3Pm93//93nDG97AGWecQdd1POtZz+LNb34zf/zHf0zOGYBbbrkFEeE973nP4z7eJ0IuuOCCpWdl+llbW9u2/r333svf+3t/jwsuuIDZbMZZZ53Fm970Ju6///6l9T75yU/yxje+kb1797Jnzx5e97rX8bGPfezJOq2VrORxlfDtHsBKVvJY5N3vfjcveMEL2NjY4M/+7M/4lV/5FT7ykY9w4403snv37id1LFdccQWf+MQneOELX3hK23384x/nF37hF/iRH/kRTjvttCdmcI8gm5ub/MAP/AB/+qd/yl/7a3+Nf/fv/h2HDh3innvu4f3vfz//0//0P/He976Xt771rd+W8T0W+S//5b8wn8+Xlt1222381b/6V/nBH/zBpeV33nknr371qwkh8PM///NcdNFF3HvvvXzoQx9isVi09a677jquueYarrzySn7/938fVeXXfu3XeP3rX8+HPvQhXvGKVzwp57aSlTxesjIGVvK0lksvvZSXvvSlALzuda8jpcQv/uIv8r73vY//5X/5X3bc5vjx4+zatetxH8u+ffu46qqrHvf9Phny0z/903zgAx/gd3/3d/nhH/7hpd/e9ra38Q//4T9kY2Pj2zS6xybf8z3fs23ZBz7wAQB+7Md+bGn53/7bf5v5fM6nPvUpDhw40Ja/7W1vW1rv53/+5znttNN4//vf356lN7zhDTz3uc/lZ37mZ1YIwUqedrIKE6zkO0qqMr711lsBg8n37NnDjTfeyF/6S3+JvXv38vrXvx6AxWLBL/3SL/GCF7yA2WzGmWeeyTve8Q7uueeepX0Ow8A/+kf/iEOHDrFr1y5e9apX8clPfnLbsU8UJvjzP/9z3vKWt3D66aeztrbGhRdeyE/91E8B8K53vYt/+A//IQDf9V3f1eDr6T7e+9738opXvILdu3ezZ88e3vjGN/KZz3xm2/Hf8573cPHFFzObzbjkkkv4vd/7vZO6ZnfffTe//du/zRvf+MZthkCViy66iMsvv/yE+/jqV7/KO97xDi666CJ27drFueeey1ve8hZuvPHGpfVyzvzSL/0SF198Mevr65x22mlcfvnl/NZv/VZb55577uF/+9/+N57znOe0+/LKV76SD37wgyd1Po8kqsq73/1unvvc5/K93/u9bfktt9zCH/3RH/HjP/7jS4bATvKxj32M1772tUtG5d69e7nmmmv4+Mc/zl133fW4jHUlK3myZIUMrOQ7Sr761a8CcOaZZ7Zli8WC7//+7+cnfuIn+Cf/5J8QYyTnzFvf+lY++tGP8o/+0T/i6quv5tZbb+Wd73wnr33ta/nUpz7F+vo6AD/+4z/O7/3e7/EzP/MzfN/3fR833XQTb3vb2zhy5MgjjucDH/gAb3nLW7jkkkv41//6X3Peeedxyy238Kd/+qeAeab3338//8f/8X/w//w//w9nn302QAs1/PIv/zI/93M/xzve8Q5+7ud+jsViwa//+q/z6le/mk9+8pNtvfe85z284x3v4K1vfSv/6l/9Kx588EHe9a53MZ/Pce7hbf4PfehDDMPAD/zAD5zaxZ7InXfeyemnn86v/uqvcuaZZ3L//ffzu7/7u7z85S/nM5/5DBdffDEAv/Zrv8a73vUufu7nfo5rrrmGYRj4i7/4iyW+xN/4G3+D66+/nn/5L/8lz3/+8zl8+DDXX3899913X1vnwx/+MK973et45zvfybve9a5TGusHP/hBbr31Vn7pl34JEWnLP/rRj6KqnHPOOfzP//P/zB//8R8TY+Sqq67iV37lV5ag/8ViwWw227bvuuzGG29s93IlK3laiK5kJU9Defe7362AXnvttToMgx45ckT/5E/+RM8880zdu3ev3n333aqq+va3v10B/Z3f+Z2l7f/Tf/pPCugf/uEfLi2/7rrrFNB/+2//raqqfvGLX1RA//7f//tL6/3BH/yBAvr2t7+9LfvQhz6kgH7oQx9qyy688EK98MILdWNj44Tn8uu//usK6M0337y0/LbbbtMQgv7dv/t3l5YfOXJEDx06pD/0Qz+kqqopJT3nnHP0iiuu0JxzW++WW27Rruv0/PPPP+GxVVV/9Vd/VQF9//vf/7DrVbn55psV0He/+90nXCfGqIvFQi+66KKla/fmN79Zv/u7v/th979nzx79qZ/6qYdd58Mf/rB67/UXfuEXTmrMU/mrf/Wvqvdev/GNbywt/5Vf+RUFdN++ffrWt75V3//+9+sf/uEf6uWXX65ra2t6ww03tHW/+7u/W5///OdrSqktG4ZBn/vc5yqg//E//sdTHtdKVvLtlFWYYCVPa7nqqqvouo69e/fy5je/mUOHDvH//r//L2edddbSev/j//g/Ln3/kz/5E0477TTe8pa3EGNsn+/+7u/m0KFDDab/0Ic+BLCNf/BDP/RDhPDwwNqXv/xlvva1r/GjP/qjO7LWH0k+8IEPEGPkh3/4h5fGuLa2xmte85o2xi996Uvceeed/PW//teXPN3zzz+fq6+++pSP+2gkxsgv//Iv88IXvpC+7wkh0Pc9X/nKV/jiF7/Y1rvyyiu54YYb+Nt/+2/zgQ98gIceemjbvq688kre85738Eu/9Etce+21DMOwbZ3XvOY1xBj55//8n5/SOO+//37e97738Zf/8l/m3HPPXfqtZks8+9nP5g//8A954xvfyNve9jbe//7345zj137t19q6f/fv/l2+/OUv83f+zt/hjjvu4Pbbb+cnf/InW3jqkdCYlazkqSarJ3YlT2v5vd/7Pa677jo+85nPcOedd/K5z32OV77ylUvr7Nq1i3379i0t++Y3v8nhw4fp+56u65Y+d999N/feey9Ag6YPHTq0tH0IgdNPP/1hx1a5B89+9rMf1bl985vfBOBlL3vZtjG+973vfcQxnmjZVjnvvPMAuPnmmx/VOMEIiD//8z/PD/zAD/DHf/zH/Pmf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ZadXLD/M9xPNXfpt/P5IYz6Z70/W3H8K8s6/9cjrrJCBlaxkJStZyUqe4bIyBlaykpWsZCUreYbLyhhYyUpWspKVrOQZLitjYCUrWclKVrKSZ7isjIGVrGQlK1nJSp7hsjIGVrIS4PDhw3zkIx/hwQcfBGBzc5OPfvSj3HXXXTuuP5/Puemmm8h5pBLnnLntttu49tprufbaa7n99tt5LD1BU0rccccdxBgf/U5WspKVrOQkZGUMrGQlwEMPPcTnP/95vvSlL6Gq3H777dx4443cc889qCrDMHDkyBGOHz+OqrJYLPjqV7/KtAP4vffey3XXXcd5553Hc57znLZ8Y2ODzc1Njhw5wjAMqCqqysbGBkeOHGGxWACgqm29zc1Njh8/zsc+9jHuu+8+5vM5GxubzOdzjhw5QkppafvpPo8ePcrm5ibDMBBjZGNjg2PHjrGxscFiseDIkSMcO3acnHPbpu53GAYWiwVHjx4lxsSqw/lKVvLMkJOuM7CSlXyny7nnnss3v/lNNjY2uOWWW7jgggsQETY3N/nv//2/A4YYXHbZZZx++unbtk8pNWV/4MAB1tbWGIaBP/mTP2Hv3r0AhBB4zWtew5133snnPvc51tbWyDlzzTXX8MADD3DdddexZ88e9u7dy5lnnsl9993HjTfeyMUXX8yNN96EKuzbt4+zzz6bz3/+86yvrzMMA6997Ws5fPgwn/zkJ9m/fz/33Xcfl1xyCfv37+cjH/kI5557Ls9+9rMZhoF77rmH+XzBuec+m+c///l88IMfpO/7dg779u3j6NGj7Nmzj6uvvhqRx7O2wUpWspKnoqyQgZWspMja2hr79+/ny1/+MqrK/v37Abj55pvJOXPFFVdwySWX8NnPfnYpPFDlzDPP5EUvehHXXXcd/+W//Beuv/56cs6klLjssst4wxvewMbGBnfddRfXX389F198MS95yUtIKfGNb3yD66+/nssvv5zXve51vOQlL+HQoUOceeaZXHXVVZx77rkMQ+R5z3seV199NV/+8pe57LLLeP3rX8/evXv52te+xo033siLX/xirrnmGg4ePNg8//X1da6++mouvPBC9u7di/ceEeGmm24ixkjOmec///nNILnwwgt51atetQpRrGQlzyBZGQMrWUkREeHCCy/k05/+NOeffz7ee8DQgPvvv5/Pf/7z3HnnnZx3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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "main('/Users/atulrane/Documents/Project Echo/Project-Echo/src/Prototypes/engine/Cam_to_mel_ar/mel_spectrogram.png')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5ae1de68", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.18" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/src/Prototypes/engine/torch_impl/light_echo_engine_efficientnetv2_tflite.py b/src/Prototypes/engine/torch_impl/light_echo_engine_efficientnetv2_tflite.py new file mode 100644 index 000000000..adb1f6ea2 --- /dev/null +++ b/src/Prototypes/engine/torch_impl/light_echo_engine_efficientnetv2_tflite.py @@ -0,0 +1,785 @@ + +################################################################################################## +# This is the Light Echo Engine application +# +# This program assumes you have already trained a model TfLite or ONNX format +################################################################################################## + + +################################################################################################## +# library imports +################################################################################################## + +# disable warnings +import warnings +warnings.filterwarnings("ignore") + +# os environment +import os +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' + +import time +import requests +import base64 +import io +import json +import base64 +import tempfile +import pickle +import math +import numpy as np +import pandas as pd +import soundfile as sf + +from platform import python_version + +import diskcache as dc +# image processing related libraries +import librosa +# generic libraries +import paho.mqtt.client as paho +# tensor flow / keras related libraries +import tensorflow as tf +from tensorflow.keras.models import load_model +from pathlib import Path + +from google.cloud import storage + +# lat long approximation +import random +from geopy.distance import geodesic + +# database libraries +import pymongo +try: + from helpers import melspectrogram_to_cam +except Exception as e: + melspectrogram_to_cam = None + print(f"helpers.melspectrogram_to_cam import skipped: {e}", flush=True) + +# for weather label +from sklearn.preprocessing import LabelEncoder + + +# print system information +print('Python Version : ', python_version()) +print('TensorFlow Version : ', tf.__version__) +print('Librosa Version : ', librosa.__version__) + + +class EchoEngine(): + + ################################################################################################## + ################################################################################################## + def __init__(self) -> None: + + # Load the engine config JSON file into a dictionary + try: + file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'light_echo_engine.json') + with open(file_path, 'r') as f: + self.config = json.load(f) + self.config['AUDIO_WINDOW'] = None + print(f"Echo Engine configuration successfully loaded", flush=True) + except: + print(f"Could not engine config : {file_path}") + + # Load the project echo credentials into a dictionary + try: + file_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'light_echo_credentials.json') + with open(file_path, 'r') as f: + self.credentials = json.load(f) + print(f"Echo Engine credentials successfully loaded", flush=True) + except: + print(f"Could not engine credentials : {file_path}") + + # Setup database client and connect + try: + # database connection string + self.connection_string=f"mongodb://{self.credentials['DB_USERNAME']}:{self.credentials['DB_PASSWORD']}@{self.config['DB_HOSTNAME']}/EchoNet" + + myclient = pymongo.MongoClient(self.connection_string) + self.echo_store = myclient["EchoNet"] + print(f"Found echo store database names: {myclient.list_database_names()}", flush=True) + except: + print(f"Failed to establish database connection", flush=True) + + # Load EfficientNetV2 TFLite model and supporting files + try: + self.eff_model_dir = Path(os.path.dirname(os.path.abspath(__file__))) / "Integrate_EfficientNetV2_Engine" / "_trained_models" + + self.eff_tflite_model_path = self.eff_model_dir / "efficientnetv2_project_echo.tflite" + self.eff_class_mapping_path = self.eff_model_dir / "class_mapping.json" + self.eff_preprocess_config_path = self.eff_model_dir / "preprocess_config.json" + + self.eff_interpreter = tf.lite.Interpreter(model_path=str(self.eff_tflite_model_path)) + self.eff_interpreter.allocate_tensors() + + self.eff_input_details = self.eff_interpreter.get_input_details() + self.eff_output_details = self.eff_interpreter.get_output_details() + + with open(self.eff_class_mapping_path, "r", encoding="utf-8") as f: + self.eff_class_mapping = json.load(f) + + with open(self.eff_preprocess_config_path, "r", encoding="utf-8") as f: + self.eff_preprocess_config = json.load(f) + + print("EfficientNetV2 TFLite model loaded successfully.", flush=True) + print(f"EfficientNetV2 input shape: {self.eff_input_details[0]['shape']}", flush=True) + print(f"EfficientNetV2 output shape: {self.eff_output_details[0]['shape']}", flush=True) + + except Exception as e: + print(f"Failed to load EfficientNetV2 TFLite model: {e}", flush=True) + + + ################################################################################################## + # This function uses the google bucket with audio files and + # leverages the folder names as the official species names + # Note: to run this you will need to first authenticate + # See https://github.com/DataBytes-Organisation/Project-Echo/tree/main/src/Prototypes/data#readme + ################################################################################################## + def gcp_load_species_list(self): + + species_names = set() + + bucket_name = self.config['BUCKET_NAME'] + os.environ["GCLOUD_PROJECT"] = self.config['GCLOUD_PROJECT'] + + storage_client = storage.Client() + bucket = storage_client.get_bucket(bucket_name) + blobs = bucket.list_blobs() # Get list of files + for blob in blobs: + folder_name = blob.name.split('/')[0] + species_names.add(folder_name) + + result = list(species_names) + result.sort() + + return result + + + ######################################################################################## + # Function to predict class and probability given a prediction + ######################################################################################## + def predict_class(self, predictions): + # STEP 1: Convert to tensor + pred_tensor = tf.convert_to_tensor(predictions) + + # STEP 2: SCALE THE LOGITS + # Check your training config for the exact value of 's'. + # CircleLoss parameters + s = 80.0 + m = 0.4 + delta_n = m + + # Apply CircleLoss transformation + scaled_predictions = s * (pred_tensor - delta_n) + + # Get prediction + predicted_index = int(tf.argmax(tf.squeeze(scaled_predictions)).numpy()) + + # STEP 3: Safe Lookup + if hasattr(self, 'class_names') and self.class_names and predicted_index < len(self.class_names): + predicted_class = self.class_names[predicted_index] + else: + predicted_class = f"Class_Index_{predicted_index}" + print(f"WARNING: Index {predicted_index} not found in class list.") + + # STEP 4: Calculate Probability + predicted_probability = 100.0 * tf.nn.softmax(scaled_predictions)[predicted_index].numpy() + predicted_probability = round(predicted_probability, 2) + + return predicted_class, predicted_probability + + + ######################################################################################## + # this method takes in string and ecodes to audio binary data + ######################################################################################## + def string_to_audio(self, audio_string) -> bytes: + base64_img_bytes = audio_string.encode('utf-8') + decoded_data = base64.decodebytes(base64_img_bytes) + return decoded_data + + ######################################################################################## + # this method takes in string and ecodes to audio binary data + ######################################################################################## + def recorded_string_to_audio(self, audio_string) -> bytes: + decoded_data = base64.b64decode(audio_string) + return decoded_data + + def load_random_subsection(self, tmp_audio_t, duration_secs): + + # Determine the audio file's duration in seconds + audio_duration_secs = tf.shape(tmp_audio_t)[0] / self.config['AUDIO_SAMPLE_RATE'] + + if audio_duration_secs>duration_secs: + + # Calculate the starting point of the 5-second subsection + max_start = tf.cast(audio_duration_secs - duration_secs, tf.float32) + start_time_secs = tf.random.uniform((), 0.0, max_start, dtype=tf.float32) + + start_index = tf.cast(start_time_secs * self.config['AUDIO_SAMPLE_RATE'], dtype=tf.int32) + + # Load the 5-second subsection + end_index = tf.cast(start_index + tf.cast(duration_secs, tf.int32) * self.config['AUDIO_SAMPLE_RATE'], tf.int32) + + subsection = tmp_audio_t[start_index : end_index] + + else: + # Pad the subsection with silence if it's shorter than 5 seconds + padding_length = duration_secs * self.config['AUDIO_SAMPLE_RATE'] - tf.shape(tmp_audio_t)[0] + padding = tf.zeros([padding_length], dtype=tmp_audio_t.dtype) + subsection = tf.concat([tmp_audio_t, padding], axis=0).numpy() + + return subsection + + def load_specific_subsection(self, tmp_audio_t, start_time_secs, end_time_secs, sample_rate): + # Ensure start and end times are within the audio duration + audio_duration_secs = tf.shape(tmp_audio_t)[0] / sample_rate + if end_time_secs > audio_duration_secs: + end_time_secs = audio_duration_secs + + # Convert start and end times to sample indices + start_index = tf.cast(start_time_secs * sample_rate, dtype=tf.int32) + end_index = tf.cast(end_time_secs * sample_rate, dtype=tf.int32) + + # Load the specified subsection + subsection = tmp_audio_t[start_index:end_index] + + # If the subsection is shorter than expected, pad it with silence + expected_length = int((end_time_secs - start_time_secs) * sample_rate) + if subsection.shape[0] < expected_length: + padding_length = expected_length - subsection.shape[0] + padding = tf.zeros([padding_length], dtype=tmp_audio_t.dtype) + subsection = tf.concat([subsection, padding], axis=0) + + return subsection + + + def combined_pipeline(self, audio_clip, mode): + """ + Complete audio processing pipeline to convert raw audio bytes into a model-ready spectrogram image. + + Args: + audio_clip (bytes): The raw audio clip in bytes + """ + + sample_rate = 0 + file = io.BytesIO(audio_clip) + + # Load audio + if str(mode) in ["Recording_Mode", "Recording_Mode_V2"]: + audio_clip, sample_rate = librosa.load(file, sr=self.config['AUDIO_SAMPLE_RATE']) + else: + audio_clip, sample_rate = librosa.load(file, sr=self.config['AUDIO_SAMPLE_RATE']) + + # Keep right channel only + if audio_clip.ndim == 2 and audio_clip.shape[0] == 2: + audio_clip = audio_clip[1, :] + + # Cast to float32 + audio_clip = audio_clip.astype(np.float32) + + # Extract random subsection + audio_clip = self.load_random_subsection(audio_clip, duration_secs=self.config['AUDIO_CLIP_DURATION']) + + # STEP 1: Compute Mel Spectrogram + mel_spec = librosa.feature.melspectrogram( + y=audio_clip, + sr=self.config['AUDIO_SAMPLE_RATE'], + n_fft=self.config['AUDIO_NFFT'], + hop_length=self.config['AUDIO_STRIDE'], + n_mels=self.config['AUDIO_MELS'], + fmin=self.config['AUDIO_FMIN'], + fmax=self.config['AUDIO_FMAX'], + win_length=self.config['AUDIO_WINDOW'] + ) + + # STEP 2: Convert to dB (CRITICAL FIX) + image = librosa.power_to_db(mel_spec, top_db=self.config['AUDIO_TOP_DB']) + image = np.clip(image, -80.0, 0.0) + + # STEP 3: Reshape and Resize + expected_clip_samples = int(self.config['AUDIO_CLIP_DURATION'] * self.config['AUDIO_SAMPLE_RATE'] / self.config['AUDIO_STRIDE']) + image = np.moveaxis(image, 1, 0) + image = image[0:expected_clip_samples, :] + + # Add channel dimension + image = tf.expand_dims(image, -1) + + # For 3-channel models, repeat + if self.config['MODEL_INPUT_IMAGE_CHANNELS'] == 3: + image = tf.repeat(image, 3, axis=2) + + # Ensure shape + image = tf.ensure_shape(image, [expected_clip_samples, self.config['AUDIO_MELS'], self.config['MODEL_INPUT_IMAGE_CHANNELS']]) + + # Resize to 224x224 + image = tf.image.resize(image, (224, 224), method=tf.image.ResizeMethod.LANCZOS5) + + # STEP 4: Normalize to [0, 1] with FIXED scale + image = (image + 80.0) / 80.0 + image = tf.clip_by_value(image, 0.0, 1.0) + + # STEP 5: Apply ImageNet Normalization + if self.config['MODEL_INPUT_IMAGE_CHANNELS'] == 1: + mean_val = 0.449 + std_val = 0.226 + image = (image - mean_val) / std_val + + elif self.config['MODEL_INPUT_IMAGE_CHANNELS'] == 3: + mean_rgb = tf.constant([0.485, 0.456, 0.406], dtype=tf.float32) + std_rgb = tf.constant([0.229, 0.224, 0.225], dtype=tf.float32) + image = (image - mean_rgb) / std_rgb + + + return image, audio_clip, sample_rate + + def efficientnetv2_preprocess_audio_bytes(self, audio_clip): + """ + Preprocess raw audio bytes for the EfficientNetV2 TFLite model. + + Expected TFLite input shape: + [1, 1, 128, 313] + """ + + file = io.BytesIO(audio_clip) + + target_sr = self.eff_preprocess_config["target_sr"] + duration_s = self.eff_preprocess_config["duration_s"] + n_mels = self.eff_preprocess_config["n_mels"] + hop_length = self.eff_preprocess_config["hop_length"] + fmin = self.eff_preprocess_config["fmin"] + fmax = self.eff_preprocess_config["fmax"] + + audio, sample_rate = librosa.load(file, sr=target_sr, mono=True) + + target_len = int(target_sr * duration_s) + + if len(audio) < target_len: + audio = np.pad(audio, (0, target_len - len(audio)), mode="constant") + else: + audio = audio[:target_len] + + mel = librosa.feature.melspectrogram( + y=audio, + sr=target_sr, + n_mels=n_mels, + hop_length=hop_length, + fmin=fmin, + fmax=fmax + ) + + mel_db = librosa.power_to_db(mel, ref=np.max).astype(np.float32) + + # Same normalisation used in the validated predictor + mel_db = (mel_db - np.mean(mel_db)) / (np.std(mel_db) + 1e-6) + + # Shape: [1, 1, 128, 313] + x = np.expand_dims(mel_db, axis=0) + x = np.expand_dims(x, axis=0) + + input_shape = tuple(self.eff_input_details[0]["shape"]) + + if tuple(x.shape) == input_shape: + return x.astype(np.float32), audio, target_sr + + # In case future TFLite export expects NHWC + x_nhwc = np.transpose(x, (0, 2, 3, 1)) + + if tuple(x_nhwc.shape) == input_shape: + return x_nhwc.astype(np.float32), audio, target_sr + + raise ValueError( + f"EfficientNetV2 input shape mismatch. " + f"Expected {input_shape}, got NCHW {x.shape} or NHWC {x_nhwc.shape}" + ) + + + def efficientnetv2_tflite_predict_from_audio_bytes(self, audio_clip): + """ + Run EfficientNetV2 TFLite inference from raw audio bytes. + """ + + input_index = self.eff_input_details[0]["index"] + output_index = self.eff_output_details[0]["index"] + + x, processed_audio, sample_rate = self.efficientnetv2_preprocess_audio_bytes(audio_clip) + + self.eff_interpreter.set_tensor(input_index, x) + self.eff_interpreter.invoke() + + output = self.eff_interpreter.get_tensor(output_index) + + logits = output[0].astype(np.float32) + + exp_values = np.exp(logits - np.max(logits)) + probs = exp_values / np.sum(exp_values) + + predicted_index = int(np.argmax(probs)) + confidence = float(probs[predicted_index]) + + index_to_label = self.eff_class_mapping["index_to_label"] + predicted_class = index_to_label[str(predicted_index)] + + predicted_probability = round(confidence * 100.0, 2) + + top_indices = np.argsort(probs)[::-1][:5] + + top_predictions = [ + { + "index": int(i), + "label": index_to_label[str(int(i))], + "confidence": float(probs[i]) + } + for i in top_indices + ] + + return predicted_class, predicted_probability, processed_audio, sample_rate, top_predictions + + # this method takes in binary audio data and encodes to string + def audio_to_string(self, audio_binary) -> str: + base64_encoded_data = base64.b64encode(audio_binary) + base64_message = base64_encoded_data.decode('utf-8') + return base64_message + + + ######################################################################################## + ######################################################################################## + def on_subscribe(self, client, userdata, mid, granted_qos): + print(f"Subscribed: message id {mid} with qos {granted_qos}") + + + ######################################################################################## + ######################################################################################## + def on_message(self, client, userdata, msg): + print("Recieved audio message, processing via engine model...") + try: + audio_event = json.loads(msg.payload) + print(audio_event['timestamp']) + + audio_clip = "" + image = None + sample_rate = 0 + + if(audio_event['audioFile'] == "Recording_Mode"): + print("Recording_Mode - EfficientNetV2 TFLite real system path") + + audio_clip = self.string_to_audio(audio_event['audioClip']) + + predicted_class, predicted_probability, processed_audio, sample_rate, top_predictions = ( + self.efficientnetv2_tflite_predict_from_audio_bytes(audio_clip) + ) + + audio_event["audioClip"] = self.audio_to_string(processed_audio) + + print(f"Predicted class : {predicted_class}") + print(f"Predicted probability : {predicted_probability}") + print(f"Top predictions : {top_predictions}") + + self.echo_api_send_detection_event( + audio_event, + sample_rate, + predicted_class, + predicted_probability + ) + + else: # simulate animals mode + # convert to string representation of audio to binary for processing + audio_clip = self.string_to_audio(audio_event['audioClip']) + + image, audio_clip, sample_rate = self.combined_pipeline(audio_clip, "Animal_Mode") + + #returned is melspectrogram with cam overlay, + #TODO: Can add this image to database + if melspectrogram_to_cam is not None: + cam = melspectrogram_to_cam.convert(image) + else: + cam = None + + # update the audio event with the re-sampled audio + audio_event["audioClip"] = self.audio_to_string(audio_clip) + + image = tf.expand_dims(image, 0) + + image_list = image.numpy().tolist() + + # Run the model via tensorflow serve + data = json.dumps({"signature_name": "serving_default", "inputs": image_list}) + url = self.config['MODEL_SERVER'] + headers = {"content-type": "application/json"} + json_response = requests.post(url, data=data, headers=headers) + model_result = json.loads(json_response.text) + predictions = model_result['outputs'][0] + + # Predict class and probability using the prediction function + predicted_class, predicted_probability = self.predict_class(predictions) + + print(f'Predicted class : {predicted_class}') + print(f'Predicted probability : {predicted_probability}') + + # populate the database with the result + self.echo_api_send_detection_event( + audio_event, + sample_rate, + predicted_class, + predicted_probability) + + image = tf.expand_dims(image, 0) + + image_list = image.numpy().tolist() + + except Exception as e: + # Catch the exception and print it to the console + print(f"An error occurred: {e}", flush=True) + + + ######################################################################################## + # this function populates the database with the prediction results + ######################################################################################## + def echo_api_send_detection_event(self, audio_event, sample_rate, predicted_class, predicted_probability): + + detection_event = { + "timestamp": audio_event["timestamp"], + "species": predicted_class, + "confidence": predicted_probability, + "sensorId": audio_event["sensorId"], + "microphoneLLA": audio_event["microphoneLLA"], + "animalEstLLA": audio_event["animalEstLLA"], + "animalTrueLLA": audio_event["animalTrueLLA"], + "animalLLAUncertainty": audio_event["animalLLAUncertainty"], + "audioClip": audio_event["audioClip"], + "sampleRate": sample_rate + } + + url = 'http://ts-api-cont:9000/engine/event' + x = requests.post(url, json = detection_event) + print(x.text) + + def weather_pipeline(self, audio_clip): + """ + Processes an audio clip to generate a resized log-mel spectrogram. To be used similar to combined_pipeline() function + + Args: + audio_clip (bytes): The audio clip in bytes format. + + Returns: + tuple: A tuple containing: + - spectrogram_resized (numpy.ndarray): The resized log-mel spectrogram with shape (260, 260, 3). + - audio (numpy.ndarray): The processed audio data. + - sample_rate (int): The sample rate used for the audio processing. + + The function performs the following steps: + 1. Converts the audio clip from bytes to a file-like object. + 2. Loads the audio data using librosa with a specified sample rate. + 3. Pads or truncates the audio data to ensure it has the required number of samples. + 4. Computes the mel spectrogram of the audio data. + 5. Converts the mel spectrogram to a log-mel spectrogram. + 6. Resizes the log-mel spectrogram to a fixed size of 260x260 pixels and repeats it across three channels. + 7. Returns the resized log-mel spectrogram, the processed audio data, and the sample rate. + """ + + file = io.BytesIO(audio_clip) + # Load the audio data with librosa + audio, sample_rate = librosa.load(file, sr=self.config['WEATHER_SAMPLE_RATE']) + required_samples = self.config['WEATHER_SAMPLE_RATE'] * self.config['WEATHER_CLIP_DURATION'] + if len(audio) < required_samples: + audio = np.pad(audio, (0, required_samples - len(audio)), 'constant') + else: + audio = audio[:required_samples] + + mel_spectrogram = librosa.feature.melspectrogram( + y=audio, sr=self.config['WEATHER_SAMPLE_RATE'], + n_fft=self.config['AUDIO_NFFT'], + hop_length=self.config['AUDIO_STRIDE'], + n_mels=self.config['AUDIO_MELS'], + fmin=self.config['AUDIO_FMIN'], + fmax=self.config['AUDIO_FMAX'] + ) + log_mel_spectrogram = librosa.power_to_db(mel_spectrogram, top_db=self.config['AUDIO_TOP_DB']) + spectrogram_resized = tf.image.resize(log_mel_spectrogram[np.newaxis, :, :, np.newaxis], [260, 260]) + spectrogram_resized = np.repeat(spectrogram_resized, 3, axis=-1) + return spectrogram_resized, audio, self.config['WEATHER_SAMPLE_RATE'] + + def predict_weather_audio(self, audio_clip): + """ + Call with audio clip, it will call weather detection model running on model container + """ + image, audio, sample_rate = self.weather_pipeline(audio_clip) + + image = tf.expand_dims(image, 0) + + image_list = image.numpy().tolist() + + data = json.dumps({"signature_name": "serving_default", "inputs": image_list}) + url = self.config['WEATHER_SERVER'] + headers = {"content-type": "application/json"} + json_response = requests.post(url, data=data, headers=headers) + model_result = json.loads(json_response.text) + predictions = model_result['outputs'][0] + print("Weather Prediciton", predictions) + #TODO: Map prediciton of appropriate class label + return predictions + + + def load_audio_file(self, file_path): + wav, sr = librosa.load(file_path, sr=16000) + return np.array([wav]) + + def extract_features(self, model, X): + features = [] + for wav in X: + scores, embeddings, spectrogram = model(wav) + features.append(embeddings.numpy().mean(axis=0)) + return np.array(features) + + def predict_on_audio(self, binary_audio): + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio_file: + with open(temp_audio_file.name, 'wb') as f: + f.write(binary_audio) + X_new = self.load_audio_file(temp_audio_file.name) + X_new_features = self.extract_features(yamnet_model, X_new) + + predictions = model.predict(X_new_features) + top_two_prob_indices = np.argsort(predictions[0])[-2:] + top_two_prob_values = predictions[0][top_two_prob_indices] + + top_two_class_names = le.inverse_transform(top_two_prob_indices) + + return [(class_names[top_two_prob_indices[1-i]], top_two_prob_values[1-i]) for i in range(2)] + + def sound_event_detection(self, filepath, sample_rate): + data, sr = librosa.load(filepath, sr=16000) + frame_len = int(sr * 1) + num_chunks = len(data) // frame_len + chunks = [data[i*frame_len:(i+1)*frame_len] for i in range(num_chunks)] + + # Adding the last chunk which can be less than 1 second + last_chunk = data[num_chunks*frame_len:] + if len(last_chunk) > 0: + chunks.append(last_chunk) + + animal_related_classes = [ + 'Dog', 'Cat', 'Bird', 'Animal', 'Birdsong', 'Canidae', 'Feline', 'Livestock', + 'Rodents, Mice', 'Wild animals', 'Pets', 'Frogs', 'Insect', 'Snake', + 'Domestic animals, pets', 'crow' + ] + + df_rows = [] + buffer = [] + start_time = None + for cnt, frame_data in enumerate(chunks): + frame_data = np.reshape(frame_data, (-1,)) # Flatten the array to 1D + frame_data = np.array([frame_data]) # Wrapping it back into a 2D array + outputs = yamnet(frame_data) + yamnet_prediction = np.mean(outputs[0], axis=0) + top2_i = np.argsort(yamnet_prediction)[::-1][:2] + threshold=0.2 + if any(yamnet_prediction[np.where(yamnet_classes == cls)[0][0]] >= threshold for cls in animal_related_classes if cls in yamnet_classes): + if start_time is None: + start_time = cnt + buffer.append(frame_data) + else: + if start_time is not None: + segment_data = np.concatenate(buffer, axis=1)[0] + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio_file: + sf.write(temp_audio_file.name, segment_data, sr) + with open(temp_audio_file.name, 'rb') as binary_file: + top2_predictions = self.predict_on_audio(binary_file.read()) + + df_row = {'start_time': start_time, 'end_time': cnt} + + for i, pred in enumerate(top2_predictions[:2]): + df_row[f'echonet_label_{i+1}'] = pred[0] if pred[0] is not None else None + df_row[f'echonet_confidence_{i+1}'] = pred[1] if pred[1] is not None else None + + df_rows.append(df_row) + buffer = [] + start_time = None + + # Handling the case where the last chunk contains an animal-related sound + if start_time is not None: + segment_data = np.concatenate(buffer, axis=1)[0] + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_audio_file: + sf.write(temp_audio_file.name, segment_data, sr) + with open(temp_audio_file.name, 'rb') as binary_file: + top2_predictions = self.predict_on_audio(binary_file.read()) + + df_row = {'start_time': start_time, 'end_time': len(chunks)} + + for i, pred in enumerate(top2_predictions[:2]): + df_row[f'echonet_label_{i+1}'] = pred[0] if pred[0] is not None else None + df_row[f'echonet_confidence_{i+1}'] = pred[1] if pred[1] is not None else None + + df_rows.append(df_row) + + df = pd.DataFrame(df_rows) + + # keep right channel only + if data.ndim == 2 and data.shape[0] == 2: + data = data[1, :] + + # cast to float32 type + data = data.astype(np.float32) + + return df, data + + def generate_random_location(self, lat, lon, min_distance, max_distance): + # Generate a random direction in radians (0 to 2*pi) + random_direction = random.uniform(0, 2 * 3.14159265359) + + # Generate a random distance between min and max distances + random_distance = random.uniform(min_distance, max_distance) / 6371000 #Earth Radius + + # Calculate the latitude and longitude offsets + lat_offset = random_distance * math.cos(random_direction) + lon_offset = random_distance * math.sin(random_direction) + + # Calculate the new latitude and longitude + new_lat = lat + lat_offset + new_lon = lon + lon_offset + + return new_lat, new_lon + + ######################################################################################## + # Execute the main engine loop (which waits for messages to arrive from MQTT) + ######################################################################################## + def execute(self): + print("Engine started.", flush=True) + # Use simple client setup for compatibility + client = paho.Client() + client.on_subscribe = self.on_subscribe + client.on_message = self.on_message + + print(f"DEBUG: Attempting to connect to Broker at: {self.config['MQTT_CLIENT_URL']}:{self.config['MQTT_CLIENT_PORT']}", flush=True) + + connected = False + while not connected: + try: + client.connect(self.config['MQTT_CLIENT_URL'], self.config['MQTT_CLIENT_PORT']) + connected=True + print("DEBUG: Connection Successful!", flush=True) + except Exception as e: + # PRINT THE ERROR SO WE CAN SEE IT + print(f"CONNECTION ERROR: {e}", flush=True) + time.sleep(2) + + print(f'Subscribing to MQTT: {self.config["MQTT_CLIENT_URL"]} {self.config["MQTT_PUBLISH_URL"]}') + client.subscribe(self.config['MQTT_PUBLISH_URL']) + + print("Retrieving species names from GCP") + self.class_names = self.gcp_load_species_list() + + # ------------------------------------------ + for cs in self.class_names: + print(f" class name {cs}") + + print("Engine waiting for audio to arrive...") + client.loop_forever() + + + + +if __name__ == "__main__": + + engine = EchoEngine() + engine.execute() diff --git a/src/Prototypes/engine/torch_impl/light_engine.Dockerfile b/src/Prototypes/engine/torch_impl/light_engine.Dockerfile index 0c8754ab4..88a66c836 100644 --- a/src/Prototypes/engine/torch_impl/light_engine.Dockerfile +++ b/src/Prototypes/engine/torch_impl/light_engine.Dockerfile @@ -19,7 +19,7 @@ RUN apt-get update && apt-get install -y \ # Install Python Packages Safely COPY requirements.txt ./ RUN python3 -m pip install --upgrade pip && \ - pip install --no-cache-dir "numpy<2.0" "tensorflow==2.15.0" -r requirements.txt + pip install --no-cache-dir "numpy==1.23.5" "tensorflow==2.10.0" -r requirements.txt # 4. Install Google Cloud SDK RUN echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list \ @@ -34,10 +34,11 @@ RUN mkdir -p /root/.config/gcloud/ # 5. Copy Application Code AND Credentials WORKDIR /app -COPY ./light_echo_engine.py ./ +COPY ./light_echo_engine_efficientnetv2_tflite.py ./ COPY ./light_echo_engine.json ./ COPY ./light_echo_credentials.json ./ COPY ./helpers ./helpers +COPY ./Integrate_EfficientNetV2_Engine ./Integrate_EfficientNetV2_Engine # 6. Run the Engine -CMD ["python", "light_echo_engine.py"] \ No newline at end of file +CMD ["python", "light_echo_engine_efficientnetv2_tflite.py"] \ No newline at end of file diff --git a/src/Prototypes/engine/torch_impl/requirements.txt b/src/Prototypes/engine/torch_impl/requirements.txt index 81f4da572..106253814 100644 --- a/src/Prototypes/engine/torch_impl/requirements.txt +++ b/src/Prototypes/engine/torch_impl/requirements.txt @@ -1,86 +1,12 @@ -absl-py==2.3.1 -antlr4-python3-runtime==4.9.3 -audiomentations==0.42.0 -audioread==3.0.1 -certifi==2025.8.3 -cffi==1.17.1 -charset-normalizer==3.4.3 -contourpy==1.3.3 -cycler==0.12.1 -decorator==5.2.1 -diskcache==5.6.3 -filelock==3.18.0 -fonttools==4.59.1 -fsspec==2025.7.0 -grpcio==1.74.0 -hydra-core==1.3.2 -idna==3.10 -jinja2==3.1.6 -joblib==1.5.1 -kiwisolver==1.4.9 -lazy-loader==0.4 -librosa==0.11.0 -llvmlite==0.44.0 -markdown==3.8.2 -markupsafe==3.0.2 -matplotlib==3.10.5 -mpmath==1.3.0 -msgpack==1.1.1 -networkx==3.5 -numba==0.61.2 -numpy==2.2.6 -numpy-minmax==0.5.0 -numpy-rms==0.6.0 -nvidia-cublas-cu12==12.8.4.1 -nvidia-cuda-cupti-cu12==12.8.90 -nvidia-cuda-nvrtc-cu12==12.8.93 -nvidia-cuda-runtime-cu12==12.8.90 -nvidia-cudnn-cu12==9.10.2.21 -nvidia-cufft-cu12==11.3.3.83 -nvidia-cufile-cu12==1.13.1.3 -nvidia-curand-cu12==10.3.9.90 -nvidia-cusolver-cu12==11.7.3.90 -nvidia-cusparse-cu12==12.5.8.93 -nvidia-cusparselt-cu12==0.7.1 -nvidia-nccl-cu12==2.27.3 -nvidia-nvjitlink-cu12==12.8.93 -nvidia-nvtx-cu12==12.8.90 -omegaconf==2.3.0 -packaging==25.0 -pandas==2.3.1 -pillow==11.3.0 -pip==25.2 -platformdirs==4.3.8 -pooch==1.8.2 -protobuf==6.31.1 -pycparser==2.22 -pynndescent==0.5.13 -pyparsing==3.2.3 -python-dateutil==2.9.0.post0 -python-stretch==0.3.1 -pytz==2025.2 -pyyaml==6.0.2 -requests==2.32.4 -scikit-learn==1.7.1 -scipy==1.16.1 -setuptools==80.9.0 -six==1.17.0 +numpy==1.23.5 +tensorflow==2.10.0 +librosa==0.9.2 soundfile==0.13.1 -soxr==0.5.0.post1 -sympy==1.14.0 -tensorboard==2.20.0 -tensorboard-data-server==0.7.2 -tensorboardx==2.6.4 -threadpoolctl==3.6.0 -torch==2.8.0 -torchaudio==2.8.0 -torchvision==0.23.0 -tqdm==4.67.1 -triton==3.4.0 -typing-extensions==4.14.1 -tzdata==2025.2 -umap-learn==0.5.9.post2 -urllib3==2.5.0 -werkzeug==3.1.3 -wheel==0.45.1 -gradio +paho-mqtt==2.1.0 +requests==2.32.5 +pymongo==4.17.0 +google-cloud-storage==3.9.0 +geopy==2.4.1 +pandas==2.3.3 +scikit-learn==1.6.1 +diskcache==5.6.3 \ No newline at end of file