apocaliss92 commited on
Commit
3a7998e
·
verified ·
1 Parent(s): 5fc4edb

Corresponding source for the YOLOv9-s EdgeTPU mirror

Browse files
source/yolov9-edgetpu-dbro/LICENSE.md ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License
2
+
3
+ Copyright (c) 2025 Dan Brown
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
source/yolov9-edgetpu-dbro/README.md ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # frigate-detector-edgetpu-yolo9
2
+
3
+ ***DO NOT USE THE CODE IN THIS REPO!***
4
+
5
+ ***Official [Frigate](https://github.com/blakeblackshear/frigate) version 0.17 added support to run YOLO v9 models on Google Coral device.*** The [pull request to add YOLO v9 support to the main Frigate repository](https://github.com/blakeblackshear/frigate/pull/21124) was accepted on December 2 2025. Since then, there is no need to use this code.
6
+
7
+ This repository provides a custom detector plugin for [Frigate](https://github.com/blakeblackshear/frigate), specifically designed to enable the Google Coral Edge TPU to run a **YOLOv9** model to detect objects visible in camera feeds. The plugin handles the necessary post-processing for YOLO model outputs, making them compatible with Frigate's detection pipeline without modifying Frigate's core source code. YOLOv9 model weights can be downloaded here as well, see the release notes and installation instructions.
8
+
9
+ ![abbeyroad-detections](https://github.com/user-attachments/assets/a4dfbe6f-2ed3-43f1-9697-6ea119e463fe)
10
+
11
+ ## Why YOLOv9 and this Plugin?
12
+
13
+ Frigate is an open-source NVR system that supports various detector acceleration hardware, including the Google Coral Edge TPU. While the default SSD MobileDet model is an effective choice in many systems, running a YOLOv9 model may improve detection accuracy and reduce false positives.
14
+
15
+ YOLOv9 can run efficiently on the EdgeTPU, and requires a small amount of post-processing by the CPU. This detection method is both high-speed and low-energy, handling approximately 100 detections per second and using only a few Watts of power, while leaving the CPU available for other tasks. Another consideration is minimizing heat produced by the CPU, which can challenge my system's small fan.
16
+
17
+ There are other YOLO models with incompatible software license terms (eg Ultralytics is licensed with the AGPL). This approach is meant to be used to start with the GPL-licensed YOLOv9 model, which IS compatible with Frigate (also MIT-licensed). For more information, see [this discussion in Frigate's issues](https://github.com/blakeblackshear/frigate/discussions/15630#discussioncomment-11639733).
18
+
19
+ ## Features
20
+
21
+ * **Improved Accuracy:** Fewer false positive detections than the default SSD MobileDet object detection model.
22
+ * **YOLO Compatibility:** Processes output from YOLO models exported for Edge TPU.
23
+ * **Google Coral Edge TPU Support:** Optimized for efficient inference on Coral devices. 10ms inference time at 320x320 image size (vs 7ms for the MobileDet model)
24
+ * **Simple Integration:** Adds as a Frigate plugin via a Docker volume mount, no core Frigate code changes needed.
25
+
26
+ ## Example System Performance
27
+
28
+ * CPU is Intel 3rd Generation i7, 8GB RAM, circa 2012 (the built in GPU is not supported by OpenVINO)
29
+ * Google Coral mini-PCIe card
30
+ * 10ms detection speed when running a load test averaging 40 detections per second for images having size 320x320 pixels
31
+ * zero skipped frames
32
+ * Detector CPU usage varies between 15% and 20%
33
+
34
+ Regarding CPU usage, all YOLO models (pre YOLO v10) use the CPU during post-processing to decode and de-duplicate the raw results.
35
+
36
+ ## Model Input Size, Detection Sensitivity, and Run Time
37
+
38
+ There are currently two versions of YOLO v9 model weights available for download that can be used with this plugin:
39
+
40
+ * [YOLO v9 "small" 320x320 input size](https://github.com/dbro/frigate-detector-edgetpu-yolo9/releases/download/v1.0/yolov9-s-relu6-best_320_int8_edgetpu.tflite) **recommended**
41
+
42
+ This version takes 10ms to run detections on my old system, and should run a bit faster on newer systems. This translates to a capacity of 100 detections per second. Note that there can be multiple detections run for a single frame from the video feed, if there are multiple separate areas where motion gets detected. According to the EdgeTPU compiler, all 334 operations that are part of the model run on the Google Coral TPU.
43
+
44
+ * [YOLO v9 "small" 512x512 input size](https://github.com/dbro/frigate-detector-edgetpu-yolo9/releases/download/v1.5/yolov9-s-relu6-tpumax_512_int8_edgetpu.tflite)
45
+
46
+ This model runs in about 21ms. It can detect smaller objects relative to the full frame size when compared with the 320x320 size version of the model. It might be preferable in situations where it is important to detect small or faraway objects within a large region of motion. All but 2 of the 334 operations in the model run on the Google Coral TPU, and the 2 that do not run at the end of the model's sequence which means the slowdown is minimal. The main reason for the slower operation is the larger tensor sizes.
47
+
48
+ Other versions of YOLO may run with this plugin, but they are not supported or offered for download here.
49
+
50
+ ## Performance Benchmarks
51
+
52
+ | Device | Model | Size | mAP 50% | Detection Time |
53
+ |---|---|---|---|---|
54
+ | Coral | [YOLO v9 s](https://github.com/dbro/frigate-detector-edgetpu-yolo9/releases/download/v1.0/yolov9-s-relu6-best_320_int8_edgetpu.tflite) (**recommended**) | 320x320 | 40.6% | 10ms |
55
+ | Coral | [YOLO v9 s](https://github.com/dbro/frigate-detector-edgetpu-yolo9/releases/download/v1.5/yolov9-s-relu6-tpumax_512_int8_edgetpu.tflite) | 512x512 | 44.3% | 21ms |
56
+ | Coral | [SSD/MobileNet](https://github.com/google-coral/test_data/raw/release-frogfish/ssdlite_mobiledet_coco_qat_postprocess_edgetpu.tflite) (Frigate's default for Coral) | 320x320 | 25.6% | 8ms |
57
+ | OpenVINO (CPU) | YOLO v9 s ReLU6 ONNX | 320x320 | 48.5% | 133ms |
58
+ | OpenVINO (CPU) | YOLO v9 s SiLU ONNX | 320x320 | 52.1% | 155ms |
59
+
60
+ The Coral performance benchmarks above were measured using the models available for download here, running on Coral TPU hardware. The images used for validation were [COCO validation images](https://www.google.com/url?sa=E&q=http%3A%2F%2Fimages.cocodataset.org%2Fzips%2Fval2017.zip) and [labels](https://huggingface.co/datasets/merve/coco/blob/main/annotations/instances_train2017.json). The threshold for minimum score was 0.4, and the NMS threshold was 0.4. mAP50 is the industry-standard accuracy score that considers a detection "correct" only if the predicted bounding box overlaps the real object by at least 50%. For comparison, the accuracy metrics for two ONNX OpenVINO models is shown, which indicate a 3.6 percentage point reduction in accuracy when using the ReLU6 activation function instead of SiLU, and a further 8 percentage point reduction when using the INT8 quantized version of the model. (The detection times for CPU inference with OpenVINO are likely to be much slower than iGPU or GPU performance.)
61
+
62
+ ## Prerequisites
63
+
64
+ Before you begin, ensure you have:
65
+
66
+ * **A running Frigate installation:** This plugin has been tested with Frigate **v0.15 and v0.16**.
67
+ * **Google Coral Edge TPU:** Properly configured and accessible by your Frigate Docker container. Should work with any variation of Google Coral hardware (USB, M2, mPCIe)
68
+
69
+ ## Installation
70
+
71
+ Follow these steps to integrate the replacement `edgetpu_tfl.py` plugin into your Frigate setup.
72
+
73
+ ### 1. Download the Model Weights, Plugin File and Class Label File
74
+
75
+ Create a directory on your host system to store the plugin file. For example, you might create `/opt/frigate-plugins/`.
76
+
77
+ ```bash
78
+ sudo mkdir -p /opt/frigate-plugins
79
+ cd /opt/frigate-plugins
80
+ # download weights
81
+ sudo wget https://github.com/dbro/frigate-detector-edgetpu-yolo9/releases/download/v1.0/yolov9-s-relu6-best_320_int8_edgetpu.tflite
82
+ # download plugin
83
+ sudo wget https://raw.githubusercontent.com/dbro/frigate-detector-edgetpu-yolo9/main/edgetpu_tfl.py
84
+ # download labels
85
+ sudo wget https://raw.githubusercontent.com/dbro/frigate-detector-edgetpu-yolo9/main/labels-coco17.txt
86
+ ```
87
+
88
+ ### 2. Update docker-compose.yml
89
+
90
+ You need to add a volume mount to your Frigate service in your docker-compose.yml file. This mounts the plugin file into Frigate's detector plugins directory.
91
+
92
+ Locate your Frigate service definition and add the following two lines in the volumes: section. Adjust /opt/frigate-plugins/edgetpu_tfl.py if you stored the file elsewhere.
93
+
94
+ The second line to add will make your YOLO model accessible.
95
+
96
+ ```yaml
97
+ # ... other services ...
98
+ frigate:
99
+ # ... other frigate configurations ...
100
+ volumes:
101
+ # ... existing volumes ...
102
+ - /opt/frigate-plugins/edgetpu_tfl.py:/opt/frigate/frigate/detectors/plugins/edgetpu_tfl.py:ro
103
+ - /opt/frigate-plugins/labels-coco17.txt:/opt/frigate/models/labels-coco17.txt:ro
104
+ - /opt/frigate-plugins/yolov9-s-relu6-best_320_int8_edgetpu.tflite:/opt/frigate/models/yolov9-s-relu6-best_320_int8_edgetpu.tflite:ro
105
+ # ... rest of frigate service ...
106
+ ```
107
+
108
+ After modifying docker-compose.yml, restart your Frigate container:
109
+
110
+ ```bash
111
+ docker-compose down
112
+ docker-compose up -d
113
+ ```
114
+
115
+ ### 3. Configure Frigate's config.yml
116
+
117
+ Now, you need to tell Frigate to use this new detector plugin and your YOLO model.
118
+
119
+ In your config.yml, in the model: section, add the model_type parameter as shown below and update the model path to point to your YOLO model file that you mounted in the previous step.
120
+
121
+ ```yaml
122
+ model:
123
+ model_type: yolo-generic
124
+ labelmap_path: /opt/frigate/models/labels-coco17.txt
125
+ path: /opt/frigate/models/yolov9-s-relu6-best_320_int8_edgetpu.tflite
126
+ # Optionally specify the model dimensions (these are the same as Frigate's default 320x320)
127
+ width: 320
128
+ height: 320
129
+ detectors:
130
+ coral:
131
+ type: edgetpu
132
+ # ... other detector settings ...
133
+ ```
134
+
135
+ ### 4. Restart Frigate and Check Performance
136
+
137
+ Save the Frigate configuration and rexstart Frigate.
138
+
139
+ * Are there any error messages in Frigate logs during startup?
140
+ * What is the inference speed reported in Frigate's System Metrics page?
141
+ * What is the level of Detector CPU Usage?
142
+ * Are there any skipped frames?
143
+ * How many detection errors do you observe? Look for both false positives and false negatives.
144
+
145
+ ## How it Works With Frigate
146
+
147
+ This modified version of edgetpu_tfl.py replaces the standard version of the plugin and acts as an intermediary. When Frigate requests a detection:
148
+
149
+ 1. Frigate's standard Edge TPU handler passes the image frame to the plugin.
150
+ 2. The plugin loads your specified YOLO model.
151
+ 3. It performs inference on the Edge TPU.
152
+ 4. It then post-processes the raw output from the YOLO model to transform it into the format expected by Frigate (e.g., converting bounding box coordinates, handling confidence scores).
153
+ 5. The processed detections are returned to Frigate.
154
+
155
+ Note that Frigate does not automatically send the entire image from the camera to the detector. Instead, it looks for areas in the image that changed from previous frames, and sends only these regions with motion to the detector. There can be more than one region sent per frame from the camera. For example, on a still day, a region might be tightly cropped around a cat walking across a lawn. On a windy autumn day with many leaves or rain creating motion in the overall image, the region of motion could be the entire image or there could be multiple regions sent to the detector. Frigate next resizes the region to match the detection model's input size, then sends it to the detector.
156
+
157
+ To understand how Frigate uses motion to trigger object detection processing, see [this explanation in the Frigate FAQ](https://docs.frigate.video/configuration/masks/#further-clarification) and a live view of motion regions is in Frigate's "Debug" view in the Settings page..
158
+
159
+ ## Lessons Learned Getting YOLO v9 to Run on Google Coral
160
+
161
+ It is possible to convert the pre-trained weights for YOLO from PyTorch format to ONNX and then to TFLite and then compile for EdgeTPU, which runs on Google Coral. However, simple conversions of the models available for download perform poorly on the Coral, because it uses operations that are not appropriate for the 8-bit limitation of Google Coral. This leads to a total loss of information and zero detections. The following changes to the YOLO model structure enable the information to be preserved accurately through the sequence of operations run on Google Coral. The goal is to let the Coral perform all the convolution operations, and then send the data for post-processing on the CPU.
162
+
163
+ * **ReLU6 activation** should be used instead of SiLU activation. It is smaller and faster than SiLU, and quantizes better.
164
+ * **Send Logit scores** to the CPU for post-processing to transform them into probabilities. The Coral cannot do the sigmoid() transformation.
165
+ * **Decode boxes and run Non-Maximum Supression (NMS) on the CPU** because the Coral cannot run these operations efficiently.
166
+ * **Separate tensors for each quantization scale** to preserve precision.
167
+ * **Data size limits are tricky.** For example, using a non-standard order of dimensions (BNC) in the boxes tensor enables it to run entirely on the TPU, but with the standard order (BCN) some operations must either run on the CPU or be done repeatedly on smaller chunks. Coral has a limited amount of on-chip RAM for caching the intermediate results of the operations, and if it needs more it will use the (slower) system RAM.
168
+ * **Post-processing takes time** Careful post-processing reduced the overall detection time by about 2ms.
169
+
170
+ Applying those lessons lets the Google Coral run all of the convolutiuon operations on its TPU for a YOLO v9 "s" model with size 320x320 pixels. The resulting model uses 385kB of off-chip RAM, which slows down the process.
171
+
172
+ ## Contributing
173
+
174
+ This project is open-source. If you find issues or have improvements, feel free to open an issue or submit a pull request.
175
+
176
+ ## References
177
+
178
+ * [https://github.com/blakeblackshear/frigate](https://github.com/blakeblackshear/frigate)
179
+ * [https://github.com/WongKinYiu/yolov9/tree/main](https://github.com/WongKinYiu/yolov9/tree/main)
180
+ * [https://engineering.cloudflight.io/quantized-yolo-for-edge-solutions](https://engineering.cloudflight.io/quantized-yolo-for-edge-solutions)
181
+
182
+ ## License
183
+ This project is licensed under the MIT License. See the LICENSE file for details.
source/yolov9-edgetpu-dbro/SOURCE.md ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # YOLOv9-s for the Coral Edge TPU — corresponding source
2
+
3
+ The two `.tflite` files under `objectDetection/yolov9/tflite/` are mirrored
4
+ UNCHANGED from Dan Brown's releases of
5
+ https://github.com/dbro/frigate-detector-edgetpu-yolo9 (repository commit
6
+ d8ddfdaee7af76bf3847274604a5ecff0d8c1cc4, mirrored 2026-10-22):
7
+
8
+ | File | Release | SHA-256 |
9
+ | --- | --- | --- |
10
+ | yolov9-s-relu6-best_320_int8_edgetpu.tflite | v1.0 | 49a38e77439b475d0d259a6d9fe7ea32fcaa58de3748499dd120aceebad9c591 |
11
+ | yolov9-s-relu6-tpumax_512_int8_edgetpu.tflite | v1.5 | f634f60259101556825fd69662f96282e74364b507f83e6382cc34d5d0e6170e |
12
+
13
+ Corresponding source, in this directory:
14
+
15
+ - `yolov9-s-relu6-best.pt` — the fine-tuned PyTorch checkpoint (release v1.0,
16
+ SHA-256 0005b3e03044acb007dbc1d6be2d1040b64ca98e5d2c30aaf5678f0935c6ebf9).
17
+ - `notebooks/Fine_tune_YOLOv9_s_for_EdgeTPU_use_ReLU6_and_17_COCO_classes.ipynb`
18
+ — the fine-tuning recipe (ReLU6 activations, 17 COCO classes).
19
+ - `notebooks/Convert_YOLO_v9_ReLU6_PyTorch_format_to_EdgeTPU_for_Google_Coral.ipynb`
20
+ — the PyTorch → int8 TFLite → edgetpu_compiler conversion.
21
+ - `labels-coco17.txt`, `README.md`, `LICENSE.md` — as published.
22
+
23
+ Licences:
24
+
25
+ - Weights: derived from WongKinYiu/yolov9 `yolov9-s-converted.pt`,
26
+ GPL-3.0 (https://github.com/WongKinYiu/yolov9). The training code is that
27
+ repository.
28
+ - Notebooks and plugin code: MIT, © 2025 Dan Brown (`LICENSE.md`).
29
+ - Training data: COCO 2017, filtered to 17 classes.
30
+
31
+ CamStack does not modify these files; it decodes their output with its own
32
+ `yolo-split-dfl` postprocessor.
source/yolov9-edgetpu-dbro/labels-coco17.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ person
2
+ bicycle
3
+ car
4
+ motorcycle
5
+ airplane
6
+ bus
7
+ train
8
+ truck
9
+ boat
10
+ bird
11
+ cat
12
+ dog
13
+ horse
14
+ sheep
15
+ cow
16
+ elephant
17
+ bear
source/yolov9-edgetpu-dbro/notebooks/Convert_YOLO_v9_ReLU6_PyTorch_format_to_EdgeTPU_for_Google_Coral.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
source/yolov9-edgetpu-dbro/notebooks/Fine_tune_YOLOv9_s_for_EdgeTPU_use_ReLU6_and_17_COCO_classes.ipynb ADDED
@@ -0,0 +1,1843 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {
6
+ "id": "header"
7
+ },
8
+ "source": [
9
+ "# YOLOv9-s ReLU6 Training for EdgeTPU\n",
10
+ "\n",
11
+ "This notebook fine-tunes YOLOv9-s with ReLU6 activations for better INT8 quantization."
12
+ ]
13
+ },
14
+ {
15
+ "cell_type": "markdown",
16
+ "metadata": {
17
+ "id": "gpu_check"
18
+ },
19
+ "source": [
20
+ "## Step 1: Check GPU and Setup"
21
+ ]
22
+ },
23
+ {
24
+ "cell_type": "code",
25
+ "execution_count": null,
26
+ "metadata": {
27
+ "colab": {
28
+ "base_uri": "https://localhost:8080/"
29
+ },
30
+ "id": "setup",
31
+ "outputId": "f6d8f7fc-20d8-425e-bb9d-d4e69f343e3c"
32
+ },
33
+ "outputs": [
34
+ {
35
+ "output_type": "stream",
36
+ "name": "stdout",
37
+ "text": [
38
+ "======================================================================\n",
39
+ "SETUP: Checking GPU\n",
40
+ "======================================================================\n",
41
+ "✓ GPU detected: Tesla T4\n",
42
+ " Memory: 15.83 GB\n"
43
+ ]
44
+ }
45
+ ],
46
+ "source": [
47
+ "import torch\n",
48
+ "import os\n",
49
+ "from pathlib import Path\n",
50
+ "\n",
51
+ "print(\"=\"*70)\n",
52
+ "print(\"SETUP: Checking GPU\")\n",
53
+ "print(\"=\"*70)\n",
54
+ "\n",
55
+ "if torch.cuda.is_available():\n",
56
+ " print(f\"✓ GPU detected: {torch.cuda.get_device_name(0)}\")\n",
57
+ " print(f\" Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB\")\n",
58
+ "else:\n",
59
+ " print(\"⚠️ NO GPU DETECTED!\")\n",
60
+ " print(\" Go to: Runtime > Change runtime type > Hardware accelerator > GPU\")\n",
61
+ " print(\" Then restart this notebook\")"
62
+ ]
63
+ },
64
+ {
65
+ "cell_type": "markdown",
66
+ "metadata": {
67
+ "id": "clone_repo"
68
+ },
69
+ "source": [
70
+ "## Step 2: Clone YOLOv9 Repository"
71
+ ]
72
+ },
73
+ {
74
+ "cell_type": "code",
75
+ "execution_count": null,
76
+ "metadata": {
77
+ "colab": {
78
+ "base_uri": "https://localhost:8080/"
79
+ },
80
+ "id": "clone",
81
+ "outputId": "fe1a758b-51e8-4707-8623-ca6e16fd3b01"
82
+ },
83
+ "outputs": [
84
+ {
85
+ "output_type": "stream",
86
+ "name": "stdout",
87
+ "text": [
88
+ "======================================================================\n",
89
+ "Cloning YOLOv9 Repository\n",
90
+ "======================================================================\n",
91
+ "Cloning into 'yolov9'...\n",
92
+ "remote: Enumerating objects: 781, done.\u001b[K\n",
93
+ "remote: Total 781 (delta 0), reused 0 (delta 0), pack-reused 781 (from 1)\u001b[K\n",
94
+ "Receiving objects: 100% (781/781), 3.27 MiB | 4.74 MiB/s, done.\n",
95
+ "Resolving deltas: 100% (330/330), done.\n",
96
+ "✓ Repository cloned\n",
97
+ "✓ Working directory: /content/yolov9\n",
98
+ "\n",
99
+ "Installing requirements...\n",
100
+ "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m44.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
101
+ "\u001b[?25h✓ Requirements installed\n"
102
+ ]
103
+ }
104
+ ],
105
+ "source": [
106
+ "print(\"=\"*70)\n",
107
+ "print(\"Cloning YOLOv9 Repository\")\n",
108
+ "print(\"=\"*70)\n",
109
+ "\n",
110
+ "if not Path('yolov9').exists():\n",
111
+ " !git clone https://github.com/WongKinYiu/yolov9.git\n",
112
+ " print(\"✓ Repository cloned\")\n",
113
+ "else:\n",
114
+ " print(\"✓ Repository already exists\")\n",
115
+ "\n",
116
+ "# Navigate to repo\n",
117
+ "os.chdir('/content/yolov9')\n",
118
+ "print(f\"✓ Working directory: {os.getcwd()}\")\n",
119
+ "\n",
120
+ "# Install requirements\n",
121
+ "print(\"\\nInstalling requirements...\")\n",
122
+ "!pip install -q -r requirements.txt\n",
123
+ "print(\"✓ Requirements installed\")"
124
+ ]
125
+ },
126
+ {
127
+ "cell_type": "markdown",
128
+ "metadata": {
129
+ "id": "config_relu"
130
+ },
131
+ "source": [
132
+ "## Step 5: Create ReLU6 Configuration"
133
+ ]
134
+ },
135
+ {
136
+ "cell_type": "code",
137
+ "execution_count": null,
138
+ "metadata": {
139
+ "id": "WHzMYpBm9Ide"
140
+ },
141
+ "outputs": [],
142
+ "source": [
143
+ "# needed for model definition yaml\n",
144
+ "# keep only the classes that are relevant for object detection in surveillance\n",
145
+ "CLASSES_TO_KEEP = [\n",
146
+ " 0, # person\n",
147
+ " 1, # bicycle\n",
148
+ " 2, # car\n",
149
+ " 3, # motorcycle\n",
150
+ " 4, # airplane\n",
151
+ " 5, # bus\n",
152
+ " 6, # train\n",
153
+ " 7, # truck\n",
154
+ " 8, # boat\n",
155
+ " 14, # bird\n",
156
+ " 15, # cat\n",
157
+ " 16, # dog\n",
158
+ " 17, # horse\n",
159
+ " 18, # sheep\n",
160
+ " 19, # cow\n",
161
+ " 20, # elephant\n",
162
+ " 21, # bear\n",
163
+ "]\n"
164
+ ]
165
+ },
166
+ {
167
+ "cell_type": "code",
168
+ "execution_count": null,
169
+ "metadata": {
170
+ "colab": {
171
+ "base_uri": "https://localhost:8080/"
172
+ },
173
+ "id": "create_relu_config",
174
+ "outputId": "08af4f61-a8a2-4b19-dd88-88fe36a8cd6c"
175
+ },
176
+ "outputs": [
177
+ {
178
+ "output_type": "stream",
179
+ "name": "stdout",
180
+ "text": [
181
+ "======================================================================\n",
182
+ "Creating ReLU6 Model Configuration with DualDDetectEdgeTPU\n",
183
+ "======================================================================\n",
184
+ "✓ Changed number of classes to 17\n",
185
+ "✓ Set activation: nn.ReLU6()\n",
186
+ "✓ Changed detection head: DualDDetect → DualDDetectEdgeTPU\n",
187
+ "✓ Created: models/detect/yolov9-s-relu6.yaml\n",
188
+ "\n",
189
+ "Verifying configuration:\n",
190
+ " Activation: activation: nn.ReLU6()\n",
191
+ " Detection head: [[28, 25, 22, 15, 18, 21], 1, DualDDetectEdgeTPU, [nc]], # Detect(P3, P4, P5)\n",
192
+ "\n",
193
+ "✅ Configuration ready for EdgeTPU quantization:\n",
194
+ " - ReLU6 activation (bounded [0,6] for all layers)\n",
195
+ " - DualDDetectTwoOutputs (separate box/class tensors)\n",
196
+ " - Both prevent quantization value crushing\n"
197
+ ]
198
+ }
199
+ ],
200
+ "source": [
201
+ "print(\"=\"*70)\n",
202
+ "print(\"Creating ReLU6 Model Configuration with DualDDetectEdgeTPU\")\n",
203
+ "print(\"=\"*70)\n",
204
+ "\n",
205
+ "from pathlib import Path\n",
206
+ "\n",
207
+ "# Read the original config\n",
208
+ "config_path = Path('models/detect/yolov9-s.yaml')\n",
209
+ "with open(config_path, 'r') as f:\n",
210
+ " lines = f.readlines()\n",
211
+ "\n",
212
+ "# Create ReLU6 + DualDDetectTwoOutputs version\n",
213
+ "relu6_config_path = Path('models/detect/yolov9-s-relu6.yaml')\n",
214
+ "new_lines = []\n",
215
+ "\n",
216
+ "for line in lines:\n",
217
+ " if line.strip() == '#activation: nn.ReLU()':\n",
218
+ " # Uncomment and change to ReLU6\n",
219
+ " new_lines.append('activation: nn.ReLU6()\\n')\n",
220
+ " print(\"✓ Set activation: nn.ReLU6()\")\n",
221
+ " elif line.strip().startswith('#activation:'):\n",
222
+ " # Keep other activation lines commented\n",
223
+ " new_lines.append(line)\n",
224
+ " elif 'DualDDetect' in line and 'DualDDetectEdgeTPU' not in line:\n",
225
+ " # Replace DualDDetect with DualDDetectEdgeTPU\n",
226
+ " new_line = line.replace('DualDDetect', 'DualDDetectEdgeTPU')\n",
227
+ " new_lines.append(new_line)\n",
228
+ " print(f\"✓ Changed detection head: DualDDetect → DualDDetectEdgeTPU\")\n",
229
+ " elif line.strip() == 'nc: 80 # number of classes':\n",
230
+ " new_lines.append(f'nc: {len(CLASSES_TO_KEEP)} # number of classes\\n')\n",
231
+ " print(f\"✓ Changed number of classes to {len(CLASSES_TO_KEEP)}\")\n",
232
+ " else:\n",
233
+ " new_lines.append(line)\n",
234
+ "\n",
235
+ "# Save config\n",
236
+ "with open(relu6_config_path, 'w') as f:\n",
237
+ " f.writelines(new_lines)\n",
238
+ "print(f\"✓ Created: {relu6_config_path}\")\n",
239
+ "\n",
240
+ "# Verify the changes\n",
241
+ "print(\"\\nVerifying configuration:\")\n",
242
+ "with open(relu6_config_path, 'r') as f:\n",
243
+ " content = f.read()\n",
244
+ " for line in content.split('\\n'):\n",
245
+ " if 'activation:' in line and not line.strip().startswith('#'):\n",
246
+ " print(f\" Activation: {line.strip()}\")\n",
247
+ " if 'DualDDetect' in line:\n",
248
+ " print(f\" Detection head: {line.strip()}\")\n",
249
+ "\n",
250
+ "print(\"\\n✅ Configuration ready for EdgeTPU quantization:\")\n",
251
+ "print(\" - ReLU6 activation (bounded [0,6] for all layers)\")\n",
252
+ "print(\" - DualDDetectTwoOutputs (separate box/class tensors)\")\n",
253
+ "print(\" - Both prevent quantization value crushing\")"
254
+ ]
255
+ },
256
+ {
257
+ "cell_type": "code",
258
+ "execution_count": null,
259
+ "metadata": {
260
+ "id": "HWTEDxIvdRJJ",
261
+ "colab": {
262
+ "base_uri": "https://localhost:8080/"
263
+ },
264
+ "outputId": "eb81b7dc-9969-47a6-d393-ad43cc44eba2"
265
+ },
266
+ "outputs": [
267
+ {
268
+ "output_type": "stream",
269
+ "name": "stdout",
270
+ "text": [
271
+ "Updated 1 path from the index\n"
272
+ ]
273
+ }
274
+ ],
275
+ "source": [
276
+ "# reset / clean up for use in re-runs\n",
277
+ "\n",
278
+ "if False:\n",
279
+ " os.chdir('/content/yolov9')\n",
280
+ " !rm -rf runs/train/yolov9-s-relu6\n",
281
+ "\n",
282
+ "if False:\n",
283
+ " !git checkout models/yolo.py"
284
+ ]
285
+ },
286
+ {
287
+ "cell_type": "code",
288
+ "execution_count": null,
289
+ "metadata": {
290
+ "colab": {
291
+ "base_uri": "https://localhost:8080/"
292
+ },
293
+ "id": "gxFHEtKf6q9i",
294
+ "outputId": "f6412b0e-565e-4a61-cf76-c335c8e686bc"
295
+ },
296
+ "outputs": [
297
+ {
298
+ "output_type": "stream",
299
+ "name": "stdout",
300
+ "text": [
301
+ "======================================================================\n",
302
+ "DualDDetectEdgeTPU - Two Tensors Output\n",
303
+ "======================================================================\n",
304
+ "======================================================================\n",
305
+ "Adding DualDDetectEdgeTPU to models/yolo.py\n",
306
+ "======================================================================\n",
307
+ "✅ Found insertion point at line 259 (before TripleDetect)\n",
308
+ "✅ Successfully added DualDDetectEdgeTPU class\n",
309
+ "\n",
310
+ "======================================================================\n",
311
+ "Updating parse_model to recognize DualDDetectEdgeTPU\n",
312
+ "======================================================================\n",
313
+ "✅ Updated: elif m in {Detect, DualDetect, TripleDetect, DDete...\n",
314
+ "✅ Updated: if isinstance(m, (Detect, DualDetect, TripleDetect...\n",
315
+ "✅ Updated: if isinstance(m, (DualDetect, TripleDetect, DualDD...\n",
316
+ "\n",
317
+ "✅ All updates completed successfully!\n"
318
+ ]
319
+ }
320
+ ],
321
+ "source": [
322
+ "print(\"=\"*70)\n",
323
+ "print(\"DualDDetectEdgeTPU - Two Tensors Output\")\n",
324
+ "print(\"=\"*70)\n",
325
+ "# This version keeps the tensors separate to allow different ranges of values\n",
326
+ "# that the EdgeTPU can quantize individually.\n",
327
+ "\n",
328
+ "import os\n",
329
+ "os.chdir('/content/yolov9')\n",
330
+ "\n",
331
+ "dualddetect_edgetpu_code = '''\n",
332
+ "class DualDDetectEdgeTPU(nn.Module):\n",
333
+ " \"\"\"\n",
334
+ " EdgeTPU-optimized DualDDetect that outputs two tensores with:\n",
335
+ " - DFL distribution for calculating box coordinates\n",
336
+ " - Logit value class scores clamped to [-4, 4], or about 2% to 98% probability\n",
337
+ "\n",
338
+ " Both ranges are compatible for INT8 quantization.\n",
339
+ " \"\"\"\n",
340
+ "\n",
341
+ " dynamic = False\n",
342
+ " export = False\n",
343
+ " shape = None\n",
344
+ " anchors = torch.empty(0)\n",
345
+ " strides = torch.empty(0)\n",
346
+ "\n",
347
+ " def __init__(self, nc=80, ch=(), inplace=True):\n",
348
+ " super().__init__()\n",
349
+ " self.nc = nc\n",
350
+ " self.nl = len(ch) // 2\n",
351
+ " self.reg_max = 16\n",
352
+ " self.no = nc + self.reg_max * 4\n",
353
+ " self.inplace = inplace\n",
354
+ " self.stride = torch.zeros(self.nl)\n",
355
+ "\n",
356
+ " c2 = make_divisible(max((ch[0] // 4, self.reg_max * 4, 16)), 4)\n",
357
+ " c3 = max((ch[0], min((self.nc * 2, 128))))\n",
358
+ " c4 = make_divisible(max((ch[self.nl] // 4, self.reg_max * 4, 16)), 4)\n",
359
+ " c5 = max((ch[self.nl], min((self.nc * 2, 128))))\n",
360
+ "\n",
361
+ " # Branch 1 (auxiliary)\n",
362
+ " self.cv2 = nn.ModuleList(\n",
363
+ " nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3, g=4),\n",
364
+ " nn.Conv2d(c2, 4 * self.reg_max, 1, groups=4))\n",
365
+ " for x in ch[:self.nl]\n",
366
+ " )\n",
367
+ " self.cv3 = nn.ModuleList(\n",
368
+ " nn.Sequential(Conv(x, c3, 3), Conv(c3, c3, 3), nn.Conv2d(c3, self.nc, 1))\n",
369
+ " for x in ch[:self.nl]\n",
370
+ " )\n",
371
+ "\n",
372
+ " # Branch 2 (main)\n",
373
+ " self.cv4 = nn.ModuleList(\n",
374
+ " nn.Sequential(Conv(x, c4, 3), Conv(c4, c4, 3, g=4),\n",
375
+ " nn.Conv2d(c4, 4 * self.reg_max, 1, groups=4))\n",
376
+ " for x in ch[self.nl:]\n",
377
+ " )\n",
378
+ " self.cv5 = nn.ModuleList(\n",
379
+ " nn.Sequential(Conv(x, c5, 3), Conv(c5, c5, 3), nn.Conv2d(c5, self.nc, 1))\n",
380
+ " for x in ch[self.nl:]\n",
381
+ " )\n",
382
+ "\n",
383
+ " self.dfl = DFL(self.reg_max)\n",
384
+ " self.dfl2 = DFL(self.reg_max)\n",
385
+ "\n",
386
+ " def forward(self, x):\n",
387
+ " shape = x[0].shape # BCHW\n",
388
+ " d1 = []\n",
389
+ " d2 = []\n",
390
+ "\n",
391
+ " for i in range(self.nl):\n",
392
+ " d1.append(torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1))\n",
393
+ " d2.append(torch.cat((self.cv4[i](x[self.nl+i]), self.cv5[i](x[self.nl+i])), 1))\n",
394
+ "\n",
395
+ " if self.training:\n",
396
+ " return [d1, d2]\n",
397
+ "\n",
398
+ " if self.export:\n",
399
+ " # Inference path: keep boxes and classes separate on edgetpu\n",
400
+ " # Output in non-standard B(H*W)C format instead of BC(H*W)\n",
401
+ " # to avoid limits of EdgeTPU concatenation sizes\n",
402
+ " # AND to facilitate post-processing on CPU\n",
403
+ "\n",
404
+ " boxes_raw = torch.cat([\n",
405
+ " self.cv4[i](x[self.nl+i]).permute(0, 2, 3, 1).flatten(1, 2)\n",
406
+ " for i in range(self.nl)\n",
407
+ " ], dim=1)\n",
408
+ "\n",
409
+ " classes_raw = torch.cat([\n",
410
+ " torch.clamp(self.cv5[i](x[self.nl+i]), -4.0, 4.0).permute(0, 2, 3, 1).flatten(1, 2)\n",
411
+ " for i in range(self.nl)\n",
412
+ " ], dim=1)\n",
413
+ "\n",
414
+ " return boxes_raw, classes_raw\n",
415
+ " else:\n",
416
+ " # Validation path\n",
417
+ " # Set up anchors if needed\n",
418
+ " if self.dynamic or self.shape != shape:\n",
419
+ " self.anchors, self.strides = (d1.transpose(0, 1) for d1 in make_anchors(d1, self.stride, 0.5))\n",
420
+ " self.shape = shape\n",
421
+ "\n",
422
+ " # Decode boxes\n",
423
+ " box, cls = torch.cat([di.view(shape[0], self.no, -1) for di in d1], 2).split((self.reg_max * 4, self.nc), 1)\n",
424
+ " dbox = dist2bbox(self.dfl(box), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides\n",
425
+ "\n",
426
+ " box2, cls2 = torch.cat([di.view(shape[0], self.no, -1) for di in d2], 2).split((self.reg_max * 4, self.nc), 1)\n",
427
+ " dbox2 = dist2bbox(self.dfl2(box2), self.anchors.unsqueeze(0), xywh=True, dim=1) * self.strides\n",
428
+ "\n",
429
+ " # Apply sigmoid to classes\n",
430
+ " cls_sigmoid = cls.sigmoid()\n",
431
+ " cls2_sigmoid = cls2.sigmoid()\n",
432
+ " # Validation needs PIXEL coordinates\n",
433
+ " y_aux = torch.cat((dbox, cls_sigmoid), 1)\n",
434
+ " y_main = torch.cat((dbox2, cls2_sigmoid), 1) # NO normalization!\n",
435
+ " return ([y_aux, y_main], [d1, d2])\n",
436
+ "\n",
437
+ " def bias_init(self):\n",
438
+ " \"\"\"Initialize biases\"\"\"\n",
439
+ " m = self\n",
440
+ " for a, b, s in zip(m.cv2, m.cv3, m.stride):\n",
441
+ " a[-1].bias.data[:] = 1.0\n",
442
+ " b[-1].bias.data[:m.nc] = math.log(5 / m.nc / (640 / s) ** 2)\n",
443
+ " for a, b, s in zip(m.cv4, m.cv5, m.stride):\n",
444
+ " a[-1].bias.data[:] = 1.0\n",
445
+ " b[-1].bias.data[:m.nc] = math.log(5 / m.nc / (640 / s) ** 2)\n",
446
+ "\n",
447
+ "'''\n",
448
+ "\n",
449
+ "print(\"=\"*70)\n",
450
+ "print(\"Adding DualDDetectEdgeTPU to models/yolo.py\")\n",
451
+ "print(\"=\"*70)\n",
452
+ "\n",
453
+ "# Read the file\n",
454
+ "with open('models/yolo.py', 'r') as f:\n",
455
+ " lines = f.readlines()\n",
456
+ "\n",
457
+ "# Check if code already present\n",
458
+ "code_already_present = False\n",
459
+ "for i, line in enumerate(lines):\n",
460
+ " if 'class DualDDetectEdgeTPU(nn.Module):' in line:\n",
461
+ " code_already_present = True\n",
462
+ " print(f\"✅ Code already present at line {i+1}\")\n",
463
+ " break\n",
464
+ "\n",
465
+ "# Find where to insert (after DualDDetect class)\n",
466
+ "insert_idx = -1\n",
467
+ "for i, line in enumerate(lines):\n",
468
+ " if 'class TripleDetect(nn.Module):' in line:\n",
469
+ " insert_idx = i\n",
470
+ " print(f\"✅ Found insertion point at line {i+1} (before TripleDetect)\")\n",
471
+ " break\n",
472
+ "\n",
473
+ "if insert_idx == -1:\n",
474
+ " print(\"❌ Could not find TripleDetect class\")\n",
475
+ " print(\"Looking for alternative insertion point...\")\n",
476
+ " for i, line in enumerate(lines):\n",
477
+ " if 'class DualDDetect(nn.Module):' in line:\n",
478
+ " # Find the end of DualDDetect\n",
479
+ " for j in range(i+1, len(lines)):\n",
480
+ " if lines[j].strip().startswith('class ') and j > i + 10:\n",
481
+ " insert_idx = j\n",
482
+ " print(f\"✅ Found insertion point at line {j+1}\")\n",
483
+ " break\n",
484
+ " break\n",
485
+ "\n",
486
+ "if insert_idx == -1:\n",
487
+ " print(\"❌ Error: Could not find insertion point!\")\n",
488
+ "elif code_already_present:\n",
489
+ " print(\"✅ Code already present\")\n",
490
+ "else:\n",
491
+ " # Insert the new class\n",
492
+ " lines.insert(insert_idx, '\\n\\n' + dualddetect_edgetpu_code + '\\n\\n')\n",
493
+ "\n",
494
+ " # Write back\n",
495
+ " with open('models/yolo.py', 'w') as f:\n",
496
+ " f.writelines(lines)\n",
497
+ "\n",
498
+ " print(\"✅ Successfully added DualDDetectEdgeTPU class\")\n",
499
+ "\n",
500
+ " # Now update parse_model to recognize the new class\n",
501
+ " print(\"\\n\" + \"=\"*70)\n",
502
+ " print(\"Updating parse_model to recognize DualDDetectEdgeTPU\")\n",
503
+ " print(\"=\"*70)\n",
504
+ "\n",
505
+ " # Read again\n",
506
+ " with open('models/yolo.py', 'r') as f:\n",
507
+ " content = f.read()\n",
508
+ "\n",
509
+ " # Update all the isinstance checks\n",
510
+ " updates = [\n",
511
+ " ('elif m in {Detect, DualDetect, TripleDetect, DDetect, DualDDetect, TripleDDetect, Segment, DSegment, DualDSegment, Panoptic}:',\n",
512
+ " 'elif m in {Detect, DualDetect, TripleDetect, DDetect, DualDDetect, DualDDetectEdgeTPU, TripleDDetect, Segment, DSegment, DualDSegment, Panoptic}:'),\n",
513
+ "\n",
514
+ " ('if isinstance(m, (Detect, DualDetect, TripleDetect, DDetect, DualDDetect, TripleDDetect, Segment, DSegment, DualDSegment, Panoptic)):',\n",
515
+ " 'if isinstance(m, (Detect, DualDetect, TripleDetect, DDetect, DualDDetect, DualDDetectEdgeTPU, TripleDDetect, Segment, DSegment, DualDSegment, Panoptic)):'),\n",
516
+ "\n",
517
+ " ('if isinstance(m, (DualDetect, TripleDetect, DualDDetect, TripleDDetect, DualDSegment)):',\n",
518
+ " 'if isinstance(m, (DualDetect, TripleDetect, DualDDetect, DualDDetectEdgeTPU, TripleDDetect, DualDSegment)):'),\n",
519
+ " ]\n",
520
+ "\n",
521
+ " for old, new in updates:\n",
522
+ " if old in content:\n",
523
+ " content = content.replace(old, new)\n",
524
+ " print(f\"✅ Updated: {old[:50]}...\")\n",
525
+ "\n",
526
+ " # Write back\n",
527
+ " with open('models/yolo.py', 'w') as f:\n",
528
+ " f.write(content)\n",
529
+ "\n",
530
+ " print(\"\\n✅ All updates completed successfully!\")\n"
531
+ ]
532
+ },
533
+ {
534
+ "cell_type": "markdown",
535
+ "metadata": {
536
+ "id": "download_weights"
537
+ },
538
+ "source": [
539
+ "## Step 4: Download Pretrained Weights"
540
+ ]
541
+ },
542
+ {
543
+ "cell_type": "code",
544
+ "execution_count": null,
545
+ "metadata": {
546
+ "colab": {
547
+ "base_uri": "https://localhost:8080/"
548
+ },
549
+ "id": "weights_download",
550
+ "outputId": "7eabb677-901f-4b6c-88a0-335c837baa0c"
551
+ },
552
+ "outputs": [
553
+ {
554
+ "output_type": "stream",
555
+ "name": "stdout",
556
+ "text": [
557
+ "======================================================================\n",
558
+ "Downloading YOLOv9-t Pretrained Weights (based on SiLU activation and 80 classes)\n",
559
+ "======================================================================\n",
560
+ "Downloading pretrained weights...\n",
561
+ "yolov9-s-converted. 100%[===================>] 14.35M 76.0MB/s in 0.2s \n",
562
+ "✓ Weights downloaded\n",
563
+ " File size: 15.04 MB\n"
564
+ ]
565
+ }
566
+ ],
567
+ "source": [
568
+ "print(\"=\"*70)\n",
569
+ "print(\"Downloading YOLOv9-s Pretrained Weights (based on SiLU activation and 80 classes)\")\n",
570
+ "print(\"=\"*70)\n",
571
+ "\n",
572
+ "weights_file = Path('yolov9-s-converted.pt')\n",
573
+ "\n",
574
+ "if not weights_file.exists():\n",
575
+ " print(\"Downloading pretrained weights...\")\n",
576
+ " !wget -q --show-progress https://github.com/WongKinYiu/yolov9/releases/download/v0.1/yolov9-s-converted.pt\n",
577
+ " print(\"✓ Weights downloaded\")\n",
578
+ "else:\n",
579
+ " print(\"✓ Weights already exist\")\n",
580
+ "\n",
581
+ "print(f\" File size: {weights_file.stat().st_size / 1e6:.2f} MB\")"
582
+ ]
583
+ },
584
+ {
585
+ "cell_type": "markdown",
586
+ "metadata": {
587
+ "id": "download_data"
588
+ },
589
+ "source": [
590
+ "## Step 3: Download COCO Dataset\n",
591
+ "\n",
592
+ "**This takes 10-15 minutes and downloads ~20GB**\n",
593
+ "\n",
594
+ "Only needs to be done once - Colab will cache it."
595
+ ]
596
+ },
597
+ {
598
+ "cell_type": "code",
599
+ "execution_count": null,
600
+ "metadata": {
601
+ "colab": {
602
+ "base_uri": "https://localhost:8080/"
603
+ },
604
+ "id": "coco_download",
605
+ "outputId": "a251dfd3-a4b2-4854-da26-5dd934517014"
606
+ },
607
+ "outputs": [
608
+ {
609
+ "output_type": "stream",
610
+ "name": "stdout",
611
+ "text": [
612
+ "======================================================================\n",
613
+ "Downloading COCO Dataset (YOLO Format)\n",
614
+ "======================================================================\n",
615
+ "\n",
616
+ "Downloading COCO dataset in YOLO format...\n",
617
+ "This includes both images and labels in the correct structure\n",
618
+ "coco2017labels.zip 100%[===================>] 46.39M 53.7MB/s in 0.9s \n",
619
+ "train2017.zip 100%[===================>] 18.01G 43.9MB/s in 7m 21s \n",
620
+ "val2017.zip 100%[===================>] 777.80M 45.3MB/s in 18s \n",
621
+ "\n",
622
+ "✓ COCO dataset downloaded with correct structure!\n",
623
+ "\n",
624
+ "Structure:\n",
625
+ " coco/\n",
626
+ " images/\n",
627
+ " train2017/\n",
628
+ " val2017/\n",
629
+ " labels/\n",
630
+ " train2017/\n",
631
+ " val2017/\n",
632
+ "\n",
633
+ "✓ Train: 118,287 images, 117,266 labels\n"
634
+ ]
635
+ }
636
+ ],
637
+ "source": [
638
+ "# ============================================================================\n",
639
+ "# Download Pre-Converted COCO Dataset with YOLO Format Labels\n",
640
+ "# ============================================================================\n",
641
+ "print(\"=\"*70)\n",
642
+ "print(\"Downloading COCO Dataset (YOLO Format)\")\n",
643
+ "print(\"=\"*70)\n",
644
+ "\n",
645
+ "# Remove the old incorrect structure\n",
646
+ "import shutil\n",
647
+ "\n",
648
+ "if False:\n",
649
+ " if Path('coco').exists():\n",
650
+ " shutil.rmtree('coco')\n",
651
+ " print(\"✓ Removed old COCO directory\")\n",
652
+ "\n",
653
+ "if not Path('coco').exists():\n",
654
+ "\n",
655
+ " # Download COCO in YOLO format (images + labels already structured correctly)\n",
656
+ " print(\"\\nDownloading COCO dataset in YOLO format...\")\n",
657
+ " print(\"This includes both images and labels in the correct structure\")\n",
658
+ "\n",
659
+ " TEST_SET = False # can reduce data size with this\n",
660
+ "\n",
661
+ " !mkdir -p coco/images\n",
662
+ " if not Path('coco2017labels.zip').exists():\n",
663
+ " !wget -q --show-progress https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels.zip\n",
664
+ " !unzip -q coco2017labels.zip # -d coco\n",
665
+ " if TEST_SET:\n",
666
+ " if not Path('coco128.zip').exists():\n",
667
+ " !wget -q --show-progress https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip\n",
668
+ " !rm -rf coco/labels/train2017\n",
669
+ " !rm -rf coco/images/train2017\n",
670
+ " !unzip -q coco128.zip\n",
671
+ " !mv coco128/images/* coco/images/\n",
672
+ " !mv coco128/labels/* coco/labels/\n",
673
+ " !rm -rf coco128\n",
674
+ " !rm coco/train2017.txt\n",
675
+ " !ls coco/images/train2017/*.jpg | sed 's|^coco|.|' > coco/train2017.txt\n",
676
+ " else:\n",
677
+ " if not Path('train2017.zip').exists():\n",
678
+ " !wget -q --show-progress http://images.cocodataset.org/zips/train2017.zip\n",
679
+ " !unzip -q train2017.zip -d coco/images\n",
680
+ " if not Path('val2017.zip').exists():\n",
681
+ " !wget -q --show-progress http://images.cocodataset.org/zips/val2017.zip\n",
682
+ " !unzip -q -n val2017.zip -d coco/images\n",
683
+ "\n",
684
+ " # Cleanup\n",
685
+ " #!rm coco2017labels.zip train2017.zip val2017.zip\n",
686
+ "\n",
687
+ " print(\"\\n✓ COCO dataset downloaded with correct structure!\")\n",
688
+ " print(\"\\nStructure:\")\n",
689
+ " print(\" coco/\")\n",
690
+ " print(\" images/\")\n",
691
+ " print(\" train2017/\")\n",
692
+ " print(\" val2017/\")\n",
693
+ " print(\" labels/\")\n",
694
+ " print(\" train2017/\")\n",
695
+ " print(\" val2017/\")\n",
696
+ "\n",
697
+ "# Verify\n",
698
+ "train_imgs = len(list(Path('coco/images/train2017').glob('*.jpg')))\n",
699
+ "train_lbls = len(list(Path('coco/labels/train2017').glob('*.txt')))\n",
700
+ "print(f\"\\n✓ Train: {train_imgs:,} images, {train_lbls:,} labels\")"
701
+ ]
702
+ },
703
+ {
704
+ "cell_type": "code",
705
+ "source": [
706
+ "# cleanup commands in case of re-try\n",
707
+ "\n",
708
+ "#!rm -rf coco\n",
709
+ "#!unzip -q coco2017labels.zip # -d coco\n",
710
+ "#!unzip -q val2017.zip -d coco/images\n",
711
+ "#!unzip -q -n val2017.zip -d coco/images"
712
+ ],
713
+ "metadata": {
714
+ "id": "i0THhtR6fv1l"
715
+ },
716
+ "execution_count": null,
717
+ "outputs": []
718
+ },
719
+ {
720
+ "cell_type": "code",
721
+ "execution_count": null,
722
+ "metadata": {
723
+ "colab": {
724
+ "base_uri": "https://localhost:8080/"
725
+ },
726
+ "id": "XM6zmAjCDtJT",
727
+ "outputId": "1f6d1dbc-3ac4-4b2e-b30b-14ec36587cea"
728
+ },
729
+ "outputs": [
730
+ {
731
+ "output_type": "stream",
732
+ "name": "stdout",
733
+ "text": [
734
+ "======================================================================\n",
735
+ "Filtering COCO Dataset to Your Classes\n",
736
+ "======================================================================\n",
737
+ "\n",
738
+ "Keeping 17 classes:\n",
739
+ "\n",
740
+ "Filtering labels...\n"
741
+ ]
742
+ },
743
+ {
744
+ "output_type": "stream",
745
+ "name": "stderr",
746
+ "text": [
747
+ " train2017: 100%|██████████| 117266/117266 [00:51<00:00, 2295.91it/s]\n",
748
+ " val2017: 100%|██████████| 4952/4952 [00:01<00:00, 2624.20it/s]\n"
749
+ ]
750
+ },
751
+ {
752
+ "output_type": "stream",
753
+ "name": "stdout",
754
+ "text": [
755
+ "\n",
756
+ "✓ Filtering complete:\n",
757
+ "\n",
758
+ "Training set:\n",
759
+ " Images: 118,287 → 86,145 (72.8%)\n",
760
+ " Objects: 849,947 → 404,191 (47.6%)\n",
761
+ "\n",
762
+ "Validation set:\n",
763
+ " Images: 5,000 → 3,645 (72.9%)\n",
764
+ " Objects: 36,335 → 16,998 (46.8%)\n",
765
+ "\n",
766
+ "✓ Labels replaced (original backed up to labels_full)\n",
767
+ "✓ Cache cleared\n"
768
+ ]
769
+ }
770
+ ],
771
+ "source": [
772
+ "# ============================================================================\n",
773
+ "# Filter COCO Dataset to 17 Specific Classes\n",
774
+ "# ============================================================================\n",
775
+ "from pathlib import Path\n",
776
+ "from tqdm import tqdm\n",
777
+ "import shutil\n",
778
+ "\n",
779
+ "print(\"=\"*70)\n",
780
+ "print(\"Filtering COCO Dataset to Your Classes\")\n",
781
+ "print(\"=\"*70)\n",
782
+ "print(f\"\\nKeeping {len(CLASSES_TO_KEEP)} classes:\")\n",
783
+ "\n",
784
+ "# Map old COCO IDs to new sequential IDs\n",
785
+ "old_to_new = {old_id: new_id for new_id, old_id in enumerate(CLASSES_TO_KEEP)}\n",
786
+ "\n",
787
+ "def filter_labels(split='train2017'):\n",
788
+ " \"\"\"Filter labels to only include specified classes\"\"\"\n",
789
+ " label_dir = Path(f'coco/labels/{split}')\n",
790
+ " filtered_dir = Path(f'coco/labels_filtered/{split}')\n",
791
+ " filtered_dir.mkdir(parents=True, exist_ok=True)\n",
792
+ "\n",
793
+ " kept_images = 0\n",
794
+ " total_objects_before = 0\n",
795
+ " total_objects_after = 0\n",
796
+ "\n",
797
+ " for label_file in tqdm(list(label_dir.glob('*.txt')), desc=f\" {split}\"):\n",
798
+ " with open(label_file, 'r') as f:\n",
799
+ " lines = f.readlines()\n",
800
+ "\n",
801
+ " total_objects_before += len(lines)\n",
802
+ "\n",
803
+ " # Filter and remap class IDs\n",
804
+ " filtered_lines = []\n",
805
+ " for line in lines:\n",
806
+ " parts = line.strip().split()\n",
807
+ " if len(parts) >= 5:\n",
808
+ " class_id = int(parts[0])\n",
809
+ " if class_id in old_to_new:\n",
810
+ " # Remap to new sequential ID\n",
811
+ " new_class_id = old_to_new[class_id]\n",
812
+ " filtered_lines.append(f\"{new_class_id} {' '.join(parts[1:])}\\n\")\n",
813
+ "\n",
814
+ " # Only keep label files for images that have at least one relevant object\n",
815
+ " if filtered_lines:\n",
816
+ " output_file = filtered_dir / label_file.name\n",
817
+ " with open(output_file, 'w') as f:\n",
818
+ " f.writelines(filtered_lines)\n",
819
+ " kept_images += 1\n",
820
+ " total_objects_after += len(filtered_lines)\n",
821
+ "\n",
822
+ " return kept_images, total_objects_before, total_objects_after\n",
823
+ "\n",
824
+ "# Filter both train and val\n",
825
+ "print(\"\\nFiltering labels...\")\n",
826
+ "train_kept, train_before, train_after = filter_labels('train2017')\n",
827
+ "val_kept, val_before, val_after = filter_labels('val2017')\n",
828
+ "\n",
829
+ "print(f\"\\n✓ Filtering complete:\")\n",
830
+ "print(f\"\\nTraining set:\")\n",
831
+ "print(f\" Images: 118,287 → {train_kept:,} ({train_kept/118287*100:.1f}%)\")\n",
832
+ "print(f\" Objects: {train_before:,} → {train_after:,} ({train_after/train_before*100:.1f}%)\")\n",
833
+ "print(f\"\\nValidation set:\")\n",
834
+ "print(f\" Images: 5,000 → {val_kept:,} ({val_kept/5000*100:.1f}%)\")\n",
835
+ "print(f\" Objects: {val_before:,} → {val_after:,} ({val_after/val_before*100:.1f}%)\")\n",
836
+ "\n",
837
+ "# Backup and replace\n",
838
+ "if Path('coco/labels_full').exists():\n",
839
+ " shutil.rmtree('coco/labels_full')\n",
840
+ "shutil.move('coco/labels', 'coco/labels_full')\n",
841
+ "shutil.move('coco/labels_filtered', 'coco/labels')\n",
842
+ "print(f\"\\n✓ Labels replaced (original backed up to labels_full)\")\n",
843
+ "\n",
844
+ "# Delete old cache\n",
845
+ "for cache in Path('coco/labels').rglob('*.cache'):\n",
846
+ " cache.unlink()\n",
847
+ "print(f\"✓ Cache cleared\")"
848
+ ]
849
+ },
850
+ {
851
+ "cell_type": "code",
852
+ "execution_count": null,
853
+ "metadata": {
854
+ "colab": {
855
+ "base_uri": "https://localhost:8080/"
856
+ },
857
+ "id": "vN6KCyT3FYHL",
858
+ "outputId": "75e46a4e-70df-42d0-bfde-c1647d1a892a"
859
+ },
860
+ "outputs": [
861
+ {
862
+ "output_type": "stream",
863
+ "name": "stdout",
864
+ "text": [
865
+ "======================================================================\n",
866
+ "Subsampling to 10% of Filtered Dataset\n",
867
+ "======================================================================\n",
868
+ "\n",
869
+ "✓ Subsampling complete:\n",
870
+ " Training: 86,145 → 8,614 images\n",
871
+ " Validation: 3,645 → 364 images\n"
872
+ ]
873
+ }
874
+ ],
875
+ "source": [
876
+ "# ============================================================================\n",
877
+ "# Subsample Filtered Dataset\n",
878
+ "# ============================================================================\n",
879
+ "import random\n",
880
+ "from pathlib import Path\n",
881
+ "import shutil\n",
882
+ "\n",
883
+ "SUBSAMPLE_PERCENT = 10 # Use 20% of data\n",
884
+ "\n",
885
+ "print(\"=\"*70)\n",
886
+ "print(f\"Subsampling to {SUBSAMPLE_PERCENT}% of Filtered Dataset\")\n",
887
+ "print(\"=\"*70)\n",
888
+ "\n",
889
+ "SUBSAMPLE_SUFFIX = \"_sub\"\n",
890
+ "\n",
891
+ "def subsample_dataset(split='train2017', percent=15):\n",
892
+ " label_dir = Path(f'coco/labels/{split}')\n",
893
+ " img_dir = Path(f'coco/images/{split}')\n",
894
+ "\n",
895
+ " # Get all labels\n",
896
+ " all_labels = list(label_dir.glob('*.txt'))\n",
897
+ "\n",
898
+ " # Random subsample\n",
899
+ " random.seed(42) # Reproducible\n",
900
+ " n_keep = int(len(all_labels) * percent / 100)\n",
901
+ " selected = random.sample(all_labels, n_keep)\n",
902
+ "\n",
903
+ " # Create subsample directories\n",
904
+ " sub_label_dir = Path(f'coco/labels/{split}{SUBSAMPLE_SUFFIX}')\n",
905
+ " sub_img_dir = Path(f'coco/images/{split}{SUBSAMPLE_SUFFIX}')\n",
906
+ " sub_label_dir.mkdir(parents=True, exist_ok=True)\n",
907
+ " sub_img_dir.mkdir(parents=True, exist_ok=True)\n",
908
+ "\n",
909
+ " # Copy selected files\n",
910
+ " for label_file in selected:\n",
911
+ " # Copy label\n",
912
+ " shutil.copy(label_file, sub_label_dir / label_file.name)\n",
913
+ "\n",
914
+ " # Copy corresponding image\n",
915
+ " img_file = img_dir / f\"{label_file.stem}.jpg\"\n",
916
+ " if img_file.exists():\n",
917
+ " shutil.copy(img_file, sub_img_dir / img_file.name)\n",
918
+ "\n",
919
+ " return n_keep, len(all_labels)\n",
920
+ "\n",
921
+ "\n",
922
+ "if SUBSAMPLE_PERCENT < 99.999:\n",
923
+ " # Subsample both splits\n",
924
+ " train_kept, train_total = subsample_dataset('train2017', SUBSAMPLE_PERCENT)\n",
925
+ " val_kept, val_total = subsample_dataset('val2017', SUBSAMPLE_PERCENT)\n",
926
+ "\n",
927
+ " print(f\"\\n✓ Subsampling complete:\")\n",
928
+ " print(f\" Training: {train_total:,} → {train_kept:,} images\")\n",
929
+ " print(f\" Validation: {val_total:,} → {val_kept:,} images\")\n",
930
+ "else:\n",
931
+ " SUBSAMPLE_SUFFIX = \"\"\n",
932
+ " print(\"✓ No subsampling required\")\n"
933
+ ]
934
+ },
935
+ {
936
+ "cell_type": "markdown",
937
+ "metadata": {
938
+ "id": "setup_data_config"
939
+ },
940
+ "source": [
941
+ "## Step 6: Setup COCO Data Configuration"
942
+ ]
943
+ },
944
+ {
945
+ "cell_type": "code",
946
+ "execution_count": null,
947
+ "metadata": {
948
+ "colab": {
949
+ "base_uri": "https://localhost:8080/"
950
+ },
951
+ "id": "data_config",
952
+ "outputId": "2357a671-9401-4a02-a184-ab3400dcba11"
953
+ },
954
+ "outputs": [
955
+ {
956
+ "output_type": "stream",
957
+ "name": "stdout",
958
+ "text": [
959
+ "======================================================================\n",
960
+ "Configuring Dataset Paths\n",
961
+ "======================================================================\n",
962
+ "✓ COCO configuration created\n"
963
+ ]
964
+ }
965
+ ],
966
+ "source": [
967
+ "print(\"=\"*70)\n",
968
+ "print(\"Configuring Dataset Paths\")\n",
969
+ "print(\"=\"*70)\n",
970
+ "\n",
971
+ "# COCO data config (if not already correct)\n",
972
+ "coco_yaml_content = f\"\"\"\n",
973
+ "# COCO 2017 dataset\n",
974
+ "\n",
975
+ "path: {os.getcwd()}/coco\n",
976
+ "train: images/train2017{SUBSAMPLE_SUFFIX}\n",
977
+ "val: images/val2017{SUBSAMPLE_SUFFIX}\n",
978
+ "\n",
979
+ "nc: {len(CLASSES_TO_KEEP)}\n",
980
+ "\n",
981
+ "# Classes\n",
982
+ "names:\n",
983
+ " 0: person\n",
984
+ " 1: bicycle\n",
985
+ " 2: car\n",
986
+ " 3: motorcycle\n",
987
+ " 4: airplane\n",
988
+ " 5: bus\n",
989
+ " 6: train\n",
990
+ " 7: truck\n",
991
+ " 8: boat\n",
992
+ " 9: bird\n",
993
+ " 10: cat\n",
994
+ " 11: dog\n",
995
+ " 12: horse\n",
996
+ " 13: sheep\n",
997
+ " 14: cow\n",
998
+ " 15: elephant\n",
999
+ " 16: bear\n",
1000
+ "\"\"\"\n",
1001
+ "\n",
1002
+ "with open('data/coco.yaml', 'w') as f:\n",
1003
+ " f.write(coco_yaml_content)\n",
1004
+ "\n",
1005
+ "print(\"✓ COCO configuration created\")"
1006
+ ]
1007
+ },
1008
+ {
1009
+ "cell_type": "markdown",
1010
+ "metadata": {
1011
+ "id": "training_config"
1012
+ },
1013
+ "source": [
1014
+ "## Step 7: Configure Training Parameters"
1015
+ ]
1016
+ },
1017
+ {
1018
+ "cell_type": "code",
1019
+ "execution_count": null,
1020
+ "metadata": {
1021
+ "id": "_TgLuSYyQ25D"
1022
+ },
1023
+ "outputs": [],
1024
+ "source": [
1025
+ "#!rm -rf /content/yolov9/runs/train/yolov9-s-edgetpu"
1026
+ ]
1027
+ },
1028
+ {
1029
+ "cell_type": "code",
1030
+ "execution_count": null,
1031
+ "metadata": {
1032
+ "colab": {
1033
+ "base_uri": "https://localhost:8080/"
1034
+ },
1035
+ "id": "config_training",
1036
+ "outputId": "082c7115-69da-4947-f2e8-0ea429054d38"
1037
+ },
1038
+ "outputs": [
1039
+ {
1040
+ "output_type": "stream",
1041
+ "name": "stdout",
1042
+ "text": [
1043
+ "======================================================================\n",
1044
+ "Training Configuration\n",
1045
+ "======================================================================\n",
1046
+ "\n",
1047
+ "📋 Training Configuration:\n",
1048
+ " Model: YOLOv9-s with ReLU6 activation\n",
1049
+ " Starting weights: yolov9-s-converted.pt\n",
1050
+ " Batch size: 24\n",
1051
+ " Image size: 640x640\n",
1052
+ " Epochs: 10\n",
1053
+ " Checkpoint frequency: every 3 epochs\n",
1054
+ "\n",
1055
+ "💡 Colab Free Tier Tips:\n",
1056
+ " • Training will take ~3-5 hours\n",
1057
+ " • Session may disconnect - rerun next cell to resume\n",
1058
+ " • Don't close the browser tab\n",
1059
+ " • Checkpoints saved automatically\n"
1060
+ ]
1061
+ }
1062
+ ],
1063
+ "source": [
1064
+ "print(\"=\"*70)\n",
1065
+ "print(\"Training Configuration\")\n",
1066
+ "print(\"=\"*70)\n",
1067
+ "\n",
1068
+ "# Training parameters\n",
1069
+ "BATCH_SIZE = 24 # Adjust if out of memory (try 8 or 12 or 16)\n",
1070
+ "EPOCHS = 20 # Fine-tuning epochs\n",
1071
+ "IMAGE_SIZE = 640 # Match pre-training size\n",
1072
+ "CHECKPOINT_FREQ = 3 # Save every N epochs\n",
1073
+ "\n",
1074
+ "print(f\"\\n📋 Training Configuration:\")\n",
1075
+ "print(f\" Model: YOLOv9-s with ReLU6 activation\")\n",
1076
+ "print(f\" Starting weights: {weights_file}\")\n",
1077
+ "print(f\" Batch size: {BATCH_SIZE}\")\n",
1078
+ "print(f\" Image size: {IMAGE_SIZE}x{IMAGE_SIZE}\")\n",
1079
+ "print(f\" Epochs: {EPOCHS}\")\n",
1080
+ "print(f\" Checkpoint frequency: every {CHECKPOINT_FREQ} epochs\")\n",
1081
+ "\n",
1082
+ "print(f\"\\n💡 Colab Free Tier Tips:\")\n",
1083
+ "print(f\" • Training will take ~3-5 hours\")\n",
1084
+ "print(f\" • Session may disconnect - rerun next cell to resume\")\n",
1085
+ "print(f\" • Don't close the browser tab\")\n",
1086
+ "print(f\" • Checkpoints saved automatically\")"
1087
+ ]
1088
+ },
1089
+ {
1090
+ "cell_type": "markdown",
1091
+ "metadata": {
1092
+ "id": "start_training"
1093
+ },
1094
+ "source": [
1095
+ "## Step 8: Start Training\n",
1096
+ "\n",
1097
+ "Leave browser tab open while this runs"
1098
+ ]
1099
+ },
1100
+ {
1101
+ "cell_type": "code",
1102
+ "execution_count": null,
1103
+ "metadata": {
1104
+ "colab": {
1105
+ "base_uri": "https://localhost:8080/"
1106
+ },
1107
+ "id": "NCFldBD8yGNL",
1108
+ "outputId": "92008548-f5e2-4615-a96e-b9e838587b33"
1109
+ },
1110
+ "outputs": [
1111
+ {
1112
+ "output_type": "stream",
1113
+ "name": "stdout",
1114
+ "text": [
1115
+ "Patching train_dual.py for PyTorch 2.8 compatibility...\n",
1116
+ "✓ train_dual.py patched successfully\n"
1117
+ ]
1118
+ }
1119
+ ],
1120
+ "source": [
1121
+ "# ============================================================================\n",
1122
+ "# FIX: Patch train_dual.py to allow loading pretrained weights\n",
1123
+ "# ============================================================================\n",
1124
+ "print(\"Patching train_dual.py for PyTorch 2.8 compatibility...\")\n",
1125
+ "\n",
1126
+ "# Read the file\n",
1127
+ "with open('train_dual.py', 'r') as f:\n",
1128
+ " content = f.read()\n",
1129
+ "\n",
1130
+ "# Find and replace the torch.load line\n",
1131
+ "old_line = \"ckpt = torch.load(weights, map_location='cpu') # load checkpoint to CPU to avoid CUDA memory leak\"\n",
1132
+ "new_line = \"ckpt = torch.load(weights, map_location='cpu', weights_only=False) # load checkpoint to CPU to avoid CUDA memory leak\"\n",
1133
+ "\n",
1134
+ "if old_line in content:\n",
1135
+ " content = content.replace(old_line, new_line)\n",
1136
+ "\n",
1137
+ " # Write back\n",
1138
+ " with open('train_dual.py', 'w') as f:\n",
1139
+ " f.write(content)\n",
1140
+ "\n",
1141
+ " print(\"✓ train_dual.py patched successfully\")\n",
1142
+ "else:\n",
1143
+ " print(\"⚠️ Line not found - manual edit needed\")"
1144
+ ]
1145
+ },
1146
+ {
1147
+ "cell_type": "code",
1148
+ "execution_count": null,
1149
+ "metadata": {
1150
+ "colab": {
1151
+ "base_uri": "https://localhost:8080/"
1152
+ },
1153
+ "id": "train",
1154
+ "outputId": "8196025a-f194-4b88-94ce-8bfdf72e13d1",
1155
+ "collapsed": true
1156
+ },
1157
+ "outputs": [
1158
+ {
1159
+ "output_type": "stream",
1160
+ "name": "stdout",
1161
+ "text": [
1162
+ "======================================================================\n",
1163
+ "STARTING TRAINING\n",
1164
+ "======================================================================\n",
1165
+ "You can minimize browser but don't close the tab\n",
1166
+ "\n",
1167
+ "======================================================================\n",
1168
+ "\n",
1169
+ "2025-11-10 12:09:02.588121: E external/local_xla/xla/stream_executor/cuda/cuda_fft.cc:467] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\n",
1170
+ "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n",
1171
+ "E0000 00:00:1762776542.847725 5421 cuda_dnn.cc:8579] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\n",
1172
+ "E0000 00:00:1762776542.920591 5421 cuda_blas.cc:1407] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n",
1173
+ "W0000 00:00:1762776543.458684 5421 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
1174
+ "W0000 00:00:1762776543.458722 5421 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
1175
+ "W0000 00:00:1762776543.458725 5421 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
1176
+ "W0000 00:00:1762776543.458731 5421 computation_placer.cc:177] computation placer already registered. Please check linkage and avoid linking the same target more than once.\n",
1177
+ "2025-11-10 12:09:03.508261: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n",
1178
+ "To enable the following instructions: AVX2 AVX512F FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n",
1179
+ "\u001b[34m\u001b[1mwandb\u001b[0m: (1) Create a W&B account\n",
1180
+ "\u001b[34m\u001b[1mwandb\u001b[0m: (2) Use an existing W&B account\n",
1181
+ "\u001b[34m\u001b[1mwandb\u001b[0m: (3) Don't visualize my results\n",
1182
+ "\u001b[34m\u001b[1mwandb\u001b[0m: Enter your choice: (30 second timeout) 3\n",
1183
+ "\u001b[34m\u001b[1mwandb\u001b[0m: You chose \"Don't visualize my results\"\n",
1184
+ "\u001b[34m\u001b[1mtrain_dual: \u001b[0mweights=yolov9-s-converted.pt, cfg=models/detect/yolov9-s-relu6.yaml, data=data/coco.yaml, hyp=data/hyps/hyp.scratch-high.yaml, epochs=10, batch_size=24, imgsz=640, rect=False, resume=False, nosave=False, noval=False, noautoanchor=False, noplots=False, evolve=None, bucket=, cache=ram, image_weights=False, device=0, multi_scale=False, single_cls=False, optimizer=SGD, sync_bn=False, workers=4, project=runs/train, name=yolov9-s-relu6, exist_ok=True, quad=False, cos_lr=False, flat_cos_lr=False, fixed_lr=False, label_smoothing=0.0, patience=100, freeze=[0], save_period=3, seed=0, local_rank=-1, min_items=0, close_mosaic=0, entity=None, upload_dataset=False, bbox_interval=-1, artifact_alias=latest\n",
1185
+ "YOLO 🚀 v0.1-104-g5b1ea9a Python-3.12.12 torch-2.8.0+cu126 CUDA:0 (Tesla T4, 15095MiB)\n",
1186
+ "\n",
1187
+ "\u001b[34m\u001b[1mhyperparameters: \u001b[0mlr0=0.01, lrf=0.01, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=7.5, cls=0.5, cls_pw=1.0, obj=0.7, obj_pw=1.0, dfl=1.5, iou_t=0.2, anchor_t=5.0, fl_gamma=0.0, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.9, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.15, copy_paste=0.3\n",
1188
+ "\u001b[34m\u001b[1mClearML: \u001b[0mrun 'pip install clearml' to automatically track, visualize and remotely train YOLO 🚀 in ClearML\n",
1189
+ "\u001b[34m\u001b[1mComet: \u001b[0mrun 'pip install comet_ml' to automatically track and visualize YOLO 🚀 runs in Comet\n",
1190
+ "\u001b[34m\u001b[1mTensorBoard: \u001b[0mStart with 'tensorboard --logdir runs/train', view at http://localhost:6006/\n",
1191
+ "Downloading https://ultralytics.com/assets/Arial.ttf to /root/.config/Ultralytics/Arial.ttf...\n",
1192
+ "100% 755k/755k [00:00<00:00, 42.4MB/s]\n",
1193
+ "\n",
1194
+ " from n params module arguments \n",
1195
+ "\u001b[34m\u001b[1mactivation:\u001b[0m nn.ReLU6()\n",
1196
+ " 0 -1 1 928 models.common.Conv [3, 32, 3, 2] \n",
1197
+ " 1 -1 1 18560 models.common.Conv [32, 64, 3, 2] \n",
1198
+ " 2 -1 1 31104 models.common.ELAN1 [64, 64, 64, 32] \n",
1199
+ " 3 -1 1 73984 models.common.AConv [64, 128] \n",
1200
+ " 4 -1 1 258432 models.common.RepNCSPELAN4 [128, 128, 128, 64, 3] \n",
1201
+ " 5 -1 1 221568 models.common.AConv [128, 192] \n",
1202
+ " 6 -1 1 579648 models.common.RepNCSPELAN4 [192, 192, 192, 96, 3] \n",
1203
+ " 7 -1 1 442880 models.common.AConv [192, 256] \n",
1204
+ " 8 -1 1 1028864 models.common.RepNCSPELAN4 [256, 256, 256, 128, 3] \n",
1205
+ " 9 -1 1 164608 models.common.SPPELAN [256, 256, 128] \n",
1206
+ " 10 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
1207
+ " 11 [-1, 6] 1 0 models.common.Concat [1] \n",
1208
+ " 12 -1 1 628800 models.common.RepNCSPELAN4 [448, 192, 192, 96, 3] \n",
1209
+ " 13 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
1210
+ " 14 [-1, 4] 1 0 models.common.Concat [1] \n",
1211
+ " 15 -1 1 283008 models.common.RepNCSPELAN4 [320, 128, 128, 64, 3] \n",
1212
+ " 16 -1 1 110784 models.common.AConv [128, 96] \n",
1213
+ " 17 [-1, 12] 1 0 models.common.Concat [1] \n",
1214
+ " 18 -1 1 598080 models.common.RepNCSPELAN4 [288, 192, 192, 96, 3] \n",
1215
+ " 19 -1 1 221440 models.common.AConv [192, 128] \n",
1216
+ " 20 [-1, 9] 1 0 models.common.Concat [1] \n",
1217
+ " 21 -1 1 1061632 models.common.RepNCSPELAN4 [384, 256, 256, 128, 3] \n",
1218
+ " 22 8 1 164608 models.common.SPPELAN [256, 256, 128] \n",
1219
+ " 23 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
1220
+ " 24 [-1, 6] 1 0 models.common.Concat [1] \n",
1221
+ " 25 -1 1 628800 models.common.RepNCSPELAN4 [448, 192, 192, 96, 3] \n",
1222
+ " 26 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
1223
+ " 27 [-1, 4] 1 0 models.common.Concat [1] \n",
1224
+ " 28 -1 1 283008 models.common.RepNCSPELAN4 [320, 128, 128, 64, 3] \n",
1225
+ " 29[28, 25, 22, 15, 18, 21] 1 2955014 models.yolo.DualDDetectEdgeTPU [17, [128, 192, 256, 128, 192, 256]]\n",
1226
+ "yolov9-s-relu6 summary: 1219 layers, 9755750 parameters, 9755718 gradients, 39.7 GFLOPs\n",
1227
+ "\n",
1228
+ "Transferred 1254/1772 items from yolov9-s-converted.pt\n",
1229
+ "\u001b[34m\u001b[1mAMP: \u001b[0mchecks passed ✅\n",
1230
+ "\u001b[34m\u001b[1moptimizer:\u001b[0m SGD(lr=0.01) with parameter groups 291 weight(decay=0.0), 305 weight(decay=0.0005625000000000001), 303 bias\n",
1231
+ "\u001b[34m\u001b[1malbumentations: \u001b[0m1 validation error for InitSchema\n",
1232
+ "size\n",
1233
+ " Field required [type=missing, input_value={'scale': (0.8, 1.0), 'ra...: None, 'strict': False}, input_type=dict]\n",
1234
+ " For further information visit https://errors.pydantic.dev/2.11/v/missing\n",
1235
+ "\u001b[34m\u001b[1mtrain: \u001b[0mScanning /content/yolov9/coco/labels/train2017_sub... 8614 images, 0 backgrounds, 0 corrupt: 100% 8614/8614 [00:01<00:00, 8128.97it/s]\n",
1236
+ "\u001b[34m\u001b[1mtrain: \u001b[0mNew cache created: /content/yolov9/coco/labels/train2017_sub.cache\n",
1237
+ "\u001b[34m\u001b[1mtrain: \u001b[0mCaching images (7.0GB ram): 100% 8614/8614 [00:06<00:00, 1362.93it/s]\n",
1238
+ "\u001b[34m\u001b[1mval: \u001b[0mScanning /content/yolov9/coco/labels/val2017_sub... 364 images, 0 backgrounds, 0 corrupt: 100% 364/364 [00:00<00:00, 2436.87it/s]\n",
1239
+ "\u001b[34m\u001b[1mval: \u001b[0mNew cache created: /content/yolov9/coco/labels/val2017_sub.cache\n",
1240
+ "\u001b[34m\u001b[1mval: \u001b[0mCaching images (0.3GB ram): 100% 364/364 [00:00<00:00, 1244.03it/s]\n",
1241
+ "Plotting labels to runs/train/yolov9-s-relu6/labels.jpg... \n",
1242
+ "/content/yolov9/train_dual.py:255: FutureWarning: `torch.cuda.amp.GradScaler(args...)` is deprecated. Please use `torch.amp.GradScaler('cuda', args...)` instead.\n",
1243
+ " scaler = torch.cuda.amp.GradScaler(enabled=amp)\n",
1244
+ "Image sizes 640 train, 640 val\n",
1245
+ "Using 4 dataloader workers\n",
1246
+ "Logging results to \u001b[1mruns/train/yolov9-s-relu6\u001b[0m\n",
1247
+ "Starting training for 10 epochs...\n",
1248
+ "\n",
1249
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1250
+ " 0% 0/359 [00:00<?, ?it/s]/content/yolov9/train_dual.py:313: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n",
1251
+ " with torch.cuda.amp.autocast(amp):\n",
1252
+ " 0/9 9.94G 4.464 6.559 5.351 203 640: 0% 0/359 [00:04<?, ?it/s]Exception in thread Thread-31 (plot_images):\n",
1253
+ "Traceback (most recent call last):\n",
1254
+ " File \"/usr/lib/python3.12/threading.py\", line 1075, in _bootstrap_inner\n",
1255
+ " self.run()\n",
1256
+ " File \"/usr/lib/python3.12/threading.py\", line 1012, in run\n",
1257
+ " self._target(*self._args, **self._kwargs)\n",
1258
+ " File \"/content/yolov9/utils/plots.py\", line 300, in plot_images\n",
1259
+ " annotator.box_label(box, label, color=color)\n",
1260
+ " File \"/content/yolov9/utils/plots.py\", line 86, in box_label\n",
1261
+ " w, h = self.font.getsize(label) # text width, height\n",
1262
+ " ^^^^^^^^^^^^^^^^^\n",
1263
+ "AttributeError: 'FreeTypeFont' object has no attribute 'getsize'\n",
1264
+ "WARNING ⚠️ TensorBoard graph visualization failure Only tensors, lists, tuples of tensors, or dictionary of tensors can be output from traced functions\n",
1265
+ " 0/9 9.94G 4.464 6.559 5.351 203 640: 0% 1/359 [00:06<41:26, 6.94s/it]/content/yolov9/train_dual.py:313: FutureWarning: `torch.cuda.amp.autocast(args...)` is deprecated. Please use `torch.amp.autocast('cuda', args...)` instead.\n",
1266
+ " with torch.cuda.amp.autocast(amp):\n",
1267
+ " 0/9 10.2G 4.558 6.469 5.347 266 640: 1% 2/359 [00:07<20:01, 3.37s/it]Exception in thread Thread-32 (plot_images):\n",
1268
+ "Traceback (most recent call last):\n",
1269
+ " File \"/usr/lib/python3.12/threading.py\", line 1075, in _bootstrap_inner\n",
1270
+ " self.run()\n",
1271
+ " File \"/usr/lib/python3.12/threading.py\", line 1012, in run\n",
1272
+ " self._target(*self._args, **self._kwargs)\n",
1273
+ " File \"/content/yolov9/utils/plots.py\", line 300, in plot_images\n",
1274
+ " annotator.box_label(box, label, color=color)\n",
1275
+ " File \"/content/yolov9/utils/plots.py\", line 86, in box_label\n",
1276
+ " w, h = self.font.getsize(label) # text width, height\n",
1277
+ " ^^^^^^^^^^^^^^^^^\n",
1278
+ "AttributeError: 'FreeTypeFont' object has no attribute 'getsize'\n",
1279
+ " 0/9 10.7G 4.619 6.399 5.346 234 640: 1% 3/359 [00:08<13:04, 2.20s/it]Exception in thread Thread-33 (plot_images):\n",
1280
+ "Traceback (most recent call last):\n",
1281
+ " File \"/usr/lib/python3.12/threading.py\", line 1075, in _bootstrap_inner\n",
1282
+ " self.run()\n",
1283
+ " File \"/usr/lib/python3.12/threading.py\", line 1012, in run\n",
1284
+ " self._target(*self._args, **self._kwargs)\n",
1285
+ " File \"/content/yolov9/utils/plots.py\", line 300, in plot_images\n",
1286
+ " annotator.box_label(box, label, color=color)\n",
1287
+ " File \"/content/yolov9/utils/plots.py\", line 86, in box_label\n",
1288
+ " w, h = self.font.getsize(label) # text width, height\n",
1289
+ " ^^^^^^^^^^^^^^^^^\n",
1290
+ "AttributeError: 'FreeTypeFont' object has no attribute 'getsize'\n",
1291
+ " 0/9 13.5G 4.168 4.944 4.797 203 640: 100% 359/359 [05:03<00:00, 1.18it/s]\n",
1292
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:05<00:00, 1.46it/s]\n",
1293
+ " all 364 1621 0.921 0.0423 0.0627 0.0293\n",
1294
+ "\n",
1295
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1296
+ " 1/9 14.7G 2.763 3.121 3.369 222 640: 100% 359/359 [04:50<00:00, 1.24it/s]\n",
1297
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.70it/s]\n",
1298
+ " all 364 1621 0.407 0.267 0.258 0.142\n",
1299
+ "\n",
1300
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1301
+ " 2/9 14.7G 2.236 2.479 2.585 185 640: 100% 359/359 [04:48<00:00, 1.24it/s]\n",
1302
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.71it/s]\n",
1303
+ " all 364 1621 0.523 0.493 0.485 0.312\n",
1304
+ "\n",
1305
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1306
+ " 3/9 14.7G 2.017 2.167 2.254 203 640: 100% 359/359 [04:45<00:00, 1.26it/s]\n",
1307
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.69it/s]\n",
1308
+ " all 364 1621 0.623 0.532 0.576 0.381\n",
1309
+ "\n",
1310
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1311
+ " 4/9 14.7G 1.905 2.005 2.106 187 640: 100% 359/359 [04:45<00:00, 1.26it/s]\n",
1312
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.72it/s]\n",
1313
+ " all 364 1621 0.682 0.509 0.574 0.39\n",
1314
+ "\n",
1315
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1316
+ " 5/9 14.7G 1.826 1.889 2.024 211 640: 100% 359/359 [04:45<00:00, 1.26it/s]\n",
1317
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.71it/s]\n",
1318
+ " all 364 1621 0.607 0.532 0.566 0.378\n",
1319
+ "\n",
1320
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1321
+ " 6/9 14.7G 1.77 1.815 1.968 188 640: 100% 359/359 [04:44<00:00, 1.26it/s]\n",
1322
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.71it/s]\n",
1323
+ " all 364 1621 0.729 0.577 0.665 0.467\n",
1324
+ "\n",
1325
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1326
+ " 7/9 14.7G 1.748 1.752 1.935 245 640: 100% 359/359 [04:46<00:00, 1.25it/s]\n",
1327
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.70it/s]\n",
1328
+ " all 364 1621 0.668 0.615 0.678 0.486\n",
1329
+ "\n",
1330
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1331
+ " 8/9 14.7G 1.702 1.675 1.898 274 640: 100% 359/359 [04:45<00:00, 1.26it/s]\n",
1332
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.71it/s]\n",
1333
+ " all 364 1621 0.724 0.569 0.669 0.479\n",
1334
+ "\n",
1335
+ " Epoch GPU_mem box_loss cls_loss dfl_loss Instances Size\n",
1336
+ " 9/9 14.7G 1.663 1.628 1.864 289 640: 100% 359/359 [04:44<00:00, 1.26it/s]\n",
1337
+ " Class Images Instances P R mAP50 mAP50-95: 100% 8/8 [00:04<00:00, 1.71it/s]\n",
1338
+ " all 364 1621 0.72 0.606 0.69 0.499\n",
1339
+ "\n",
1340
+ "10 epochs completed in 0.819 hours.\n",
1341
+ "Traceback (most recent call last):\n",
1342
+ " File \"/content/yolov9/train_dual.py\", line 644, in <module>\n",
1343
+ " main(opt)\n",
1344
+ " File \"/content/yolov9/train_dual.py\", line 538, in main\n",
1345
+ " train(opt.hyp, opt, device, callbacks)\n",
1346
+ " File \"/content/yolov9/train_dual.py\", line 413, in train\n",
1347
+ " strip_optimizer(f) # strip optimizers\n",
1348
+ " ^^^^^^^^^^^^^^^^^^\n",
1349
+ " File \"/content/yolov9/utils/general.py\", line 999, in strip_optimizer\n",
1350
+ " x = torch.load(f, map_location=torch.device('cpu'))\n",
1351
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
1352
+ " File \"/usr/local/lib/python3.12/dist-packages/torch/serialization.py\", line 1529, in load\n",
1353
+ " raise pickle.UnpicklingError(_get_wo_message(str(e))) from None\n",
1354
+ "_pickle.UnpicklingError: Weights only load failed. This file can still be loaded, to do so you have two options, \u001b[1mdo those steps only if you trust the source of the checkpoint\u001b[0m. \n",
1355
+ "\t(1) In PyTorch 2.6, we changed the default value of the `weights_only` argument in `torch.load` from `False` to `True`. Re-running `torch.load` with `weights_only` set to `False` will likely succeed, but it can result in arbitrary code execution. Do it only if you got the file from a trusted source.\n",
1356
+ "\t(2) Alternatively, to load with `weights_only=True` please check the recommended steps in the following error message.\n",
1357
+ "\tWeightsUnpickler error: Unsupported global: GLOBAL numpy._core.multiarray._reconstruct was not an allowed global by default. Please use `torch.serialization.add_safe_globals([numpy._core.multiarray._reconstruct])` or the `torch.serialization.safe_globals([numpy._core.multiarray._reconstruct])` context manager to allowlist this global if you trust this class/function.\n",
1358
+ "\n",
1359
+ "Check the documentation of torch.load to learn more about types accepted by default with weights_only https://pytorch.org/docs/stable/generated/torch.load.html.\n",
1360
+ "\n",
1361
+ "======================================================================\n",
1362
+ "✓ TRAINING COMPLETE!\n",
1363
+ "======================================================================\n"
1364
+ ]
1365
+ }
1366
+ ],
1367
+ "source": [
1368
+ "print(\"=\"*70)\n",
1369
+ "print(\"STARTING TRAINING\")\n",
1370
+ "print(\"=\"*70)\n",
1371
+ "print(\"You can minimize browser but don't close the tab\\n\")\n",
1372
+ "print(\"=\"*70 + \"\\n\")\n",
1373
+ "\n",
1374
+ "\n",
1375
+ "import shutil\n",
1376
+ "from pathlib import Path\n",
1377
+ "import threading\n",
1378
+ "import time\n",
1379
+ "\n",
1380
+ "# Run training\n",
1381
+ "!python train_dual.py \\\n",
1382
+ " --workers 4 \\\n",
1383
+ " --device 0 \\\n",
1384
+ " --batch-size {BATCH_SIZE} \\\n",
1385
+ " --data data/coco.yaml \\\n",
1386
+ " --img {IMAGE_SIZE} \\\n",
1387
+ " --cfg models/detect/yolov9-s-relu6.yaml \\\n",
1388
+ " --weights {weights_file} \\\n",
1389
+ " --name yolov9-s-relu6 \\\n",
1390
+ " --hyp data/hyps/hyp.scratch-high.yaml \\\n",
1391
+ " --epochs {EPOCHS} \\\n",
1392
+ " --save-period {CHECKPOINT_FREQ} \\\n",
1393
+ " --cache \\\n",
1394
+ " --exist-ok\n",
1395
+ "\n",
1396
+ "print(\"\\n\" + \"=\"*70)\n",
1397
+ "print(\"✓ TRAINING COMPLETE!\")\n",
1398
+ "print(\"=\"*70)"
1399
+ ]
1400
+ },
1401
+ {
1402
+ "cell_type": "markdown",
1403
+ "metadata": {
1404
+ "id": "download"
1405
+ },
1406
+ "source": [
1407
+ "## Step 9: Download Trained Model"
1408
+ ]
1409
+ },
1410
+ {
1411
+ "cell_type": "code",
1412
+ "execution_count": null,
1413
+ "metadata": {
1414
+ "id": "download_model",
1415
+ "colab": {
1416
+ "base_uri": "https://localhost:8080/",
1417
+ "height": 403
1418
+ },
1419
+ "outputId": "0830fe0a-99a8-4917-b020-f3db19f1834e"
1420
+ },
1421
+ "outputs": [
1422
+ {
1423
+ "output_type": "stream",
1424
+ "name": "stdout",
1425
+ "text": [
1426
+ "======================================================================\n",
1427
+ "Download Trained Model\n",
1428
+ "======================================================================\n",
1429
+ "✓ Model ready: /content/yolov9-s-relu6-best.pt\n",
1430
+ " File size: 79.88 MB\n",
1431
+ "\n",
1432
+ "Downloading to your computer...\n"
1433
+ ]
1434
+ },
1435
+ {
1436
+ "output_type": "display_data",
1437
+ "data": {
1438
+ "text/plain": [
1439
+ "<IPython.core.display.Javascript object>"
1440
+ ],
1441
+ "application/javascript": [
1442
+ "\n",
1443
+ " async function download(id, filename, size) {\n",
1444
+ " if (!google.colab.kernel.accessAllowed) {\n",
1445
+ " return;\n",
1446
+ " }\n",
1447
+ " const div = document.createElement('div');\n",
1448
+ " const label = document.createElement('label');\n",
1449
+ " label.textContent = `Downloading \"${filename}\": `;\n",
1450
+ " div.appendChild(label);\n",
1451
+ " const progress = document.createElement('progress');\n",
1452
+ " progress.max = size;\n",
1453
+ " div.appendChild(progress);\n",
1454
+ " document.body.appendChild(div);\n",
1455
+ "\n",
1456
+ " const buffers = [];\n",
1457
+ " let downloaded = 0;\n",
1458
+ "\n",
1459
+ " const channel = await google.colab.kernel.comms.open(id);\n",
1460
+ " // Send a message to notify the kernel that we're ready.\n",
1461
+ " channel.send({})\n",
1462
+ "\n",
1463
+ " for await (const message of channel.messages) {\n",
1464
+ " // Send a message to notify the kernel that we're ready.\n",
1465
+ " channel.send({})\n",
1466
+ " if (message.buffers) {\n",
1467
+ " for (const buffer of message.buffers) {\n",
1468
+ " buffers.push(buffer);\n",
1469
+ " downloaded += buffer.byteLength;\n",
1470
+ " progress.value = downloaded;\n",
1471
+ " }\n",
1472
+ " }\n",
1473
+ " }\n",
1474
+ " const blob = new Blob(buffers, {type: 'application/binary'});\n",
1475
+ " const a = document.createElement('a');\n",
1476
+ " a.href = window.URL.createObjectURL(blob);\n",
1477
+ " a.download = filename;\n",
1478
+ " div.appendChild(a);\n",
1479
+ " a.click();\n",
1480
+ " div.remove();\n",
1481
+ " }\n",
1482
+ " "
1483
+ ]
1484
+ },
1485
+ "metadata": {}
1486
+ },
1487
+ {
1488
+ "output_type": "display_data",
1489
+ "data": {
1490
+ "text/plain": [
1491
+ "<IPython.core.display.Javascript object>"
1492
+ ],
1493
+ "application/javascript": [
1494
+ "download(\"download_78f70657-8b62-4119-bff7-460ff6495d73\", \"yolov9-s-relu6-best.pt\", 79878777)"
1495
+ ]
1496
+ },
1497
+ "metadata": {}
1498
+ },
1499
+ {
1500
+ "output_type": "stream",
1501
+ "name": "stdout",
1502
+ "text": [
1503
+ "\n",
1504
+ "✓ Download complete!\n",
1505
+ "✓ Model ready: /content/yolov9-s-relu6-last.pt\n",
1506
+ " File size: 79.88 MB\n",
1507
+ "\n",
1508
+ "Downloading to your computer...\n"
1509
+ ]
1510
+ },
1511
+ {
1512
+ "output_type": "display_data",
1513
+ "data": {
1514
+ "text/plain": [
1515
+ "<IPython.core.display.Javascript object>"
1516
+ ],
1517
+ "application/javascript": [
1518
+ "\n",
1519
+ " async function download(id, filename, size) {\n",
1520
+ " if (!google.colab.kernel.accessAllowed) {\n",
1521
+ " return;\n",
1522
+ " }\n",
1523
+ " const div = document.createElement('div');\n",
1524
+ " const label = document.createElement('label');\n",
1525
+ " label.textContent = `Downloading \"${filename}\": `;\n",
1526
+ " div.appendChild(label);\n",
1527
+ " const progress = document.createElement('progress');\n",
1528
+ " progress.max = size;\n",
1529
+ " div.appendChild(progress);\n",
1530
+ " document.body.appendChild(div);\n",
1531
+ "\n",
1532
+ " const buffers = [];\n",
1533
+ " let downloaded = 0;\n",
1534
+ "\n",
1535
+ " const channel = await google.colab.kernel.comms.open(id);\n",
1536
+ " // Send a message to notify the kernel that we're ready.\n",
1537
+ " channel.send({})\n",
1538
+ "\n",
1539
+ " for await (const message of channel.messages) {\n",
1540
+ " // Send a message to notify the kernel that we're ready.\n",
1541
+ " channel.send({})\n",
1542
+ " if (message.buffers) {\n",
1543
+ " for (const buffer of message.buffers) {\n",
1544
+ " buffers.push(buffer);\n",
1545
+ " downloaded += buffer.byteLength;\n",
1546
+ " progress.value = downloaded;\n",
1547
+ " }\n",
1548
+ " }\n",
1549
+ " }\n",
1550
+ " const blob = new Blob(buffers, {type: 'application/binary'});\n",
1551
+ " const a = document.createElement('a');\n",
1552
+ " a.href = window.URL.createObjectURL(blob);\n",
1553
+ " a.download = filename;\n",
1554
+ " div.appendChild(a);\n",
1555
+ " a.click();\n",
1556
+ " div.remove();\n",
1557
+ " }\n",
1558
+ " "
1559
+ ]
1560
+ },
1561
+ "metadata": {}
1562
+ },
1563
+ {
1564
+ "output_type": "display_data",
1565
+ "data": {
1566
+ "text/plain": [
1567
+ "<IPython.core.display.Javascript object>"
1568
+ ],
1569
+ "application/javascript": [
1570
+ "download(\"download_d27f3ab9-2747-4643-83f6-90341961ff7e\", \"yolov9-s-relu6-last.pt\", 79878777)"
1571
+ ]
1572
+ },
1573
+ "metadata": {}
1574
+ },
1575
+ {
1576
+ "output_type": "stream",
1577
+ "name": "stdout",
1578
+ "text": [
1579
+ "\n",
1580
+ "✓ Download complete!\n",
1581
+ "✓ Epoch summary ready: /content/epoch-results.csv\n",
1582
+ " File size: 0.00 MB\n",
1583
+ "\n",
1584
+ "Downloading to your computer...\n"
1585
+ ]
1586
+ },
1587
+ {
1588
+ "output_type": "display_data",
1589
+ "data": {
1590
+ "text/plain": [
1591
+ "<IPython.core.display.Javascript object>"
1592
+ ],
1593
+ "application/javascript": [
1594
+ "\n",
1595
+ " async function download(id, filename, size) {\n",
1596
+ " if (!google.colab.kernel.accessAllowed) {\n",
1597
+ " return;\n",
1598
+ " }\n",
1599
+ " const div = document.createElement('div');\n",
1600
+ " const label = document.createElement('label');\n",
1601
+ " label.textContent = `Downloading \"${filename}\": `;\n",
1602
+ " div.appendChild(label);\n",
1603
+ " const progress = document.createElement('progress');\n",
1604
+ " progress.max = size;\n",
1605
+ " div.appendChild(progress);\n",
1606
+ " document.body.appendChild(div);\n",
1607
+ "\n",
1608
+ " const buffers = [];\n",
1609
+ " let downloaded = 0;\n",
1610
+ "\n",
1611
+ " const channel = await google.colab.kernel.comms.open(id);\n",
1612
+ " // Send a message to notify the kernel that we're ready.\n",
1613
+ " channel.send({})\n",
1614
+ "\n",
1615
+ " for await (const message of channel.messages) {\n",
1616
+ " // Send a message to notify the kernel that we're ready.\n",
1617
+ " channel.send({})\n",
1618
+ " if (message.buffers) {\n",
1619
+ " for (const buffer of message.buffers) {\n",
1620
+ " buffers.push(buffer);\n",
1621
+ " downloaded += buffer.byteLength;\n",
1622
+ " progress.value = downloaded;\n",
1623
+ " }\n",
1624
+ " }\n",
1625
+ " }\n",
1626
+ " const blob = new Blob(buffers, {type: 'application/binary'});\n",
1627
+ " const a = document.createElement('a');\n",
1628
+ " a.href = window.URL.createObjectURL(blob);\n",
1629
+ " a.download = filename;\n",
1630
+ " div.appendChild(a);\n",
1631
+ " a.click();\n",
1632
+ " div.remove();\n",
1633
+ " }\n",
1634
+ " "
1635
+ ]
1636
+ },
1637
+ "metadata": {}
1638
+ },
1639
+ {
1640
+ "output_type": "display_data",
1641
+ "data": {
1642
+ "text/plain": [
1643
+ "<IPython.core.display.Javascript object>"
1644
+ ],
1645
+ "application/javascript": [
1646
+ "download(\"download_2d70d0be-02c0-412c-8449-42ab400ba56f\", \"epoch-results.csv\", 1470)"
1647
+ ]
1648
+ },
1649
+ "metadata": {}
1650
+ },
1651
+ {
1652
+ "output_type": "stream",
1653
+ "name": "stdout",
1654
+ "text": [
1655
+ "\n",
1656
+ "✓ Download complete!\n"
1657
+ ]
1658
+ }
1659
+ ],
1660
+ "source": [
1661
+ "from google.colab import files\n",
1662
+ "import shutil\n",
1663
+ "\n",
1664
+ "print(\"=\"*70)\n",
1665
+ "print(\"Download Trained Model\")\n",
1666
+ "print(\"=\"*70)\n",
1667
+ "\n",
1668
+ "best_weights = Path('runs/train/yolov9-s-relu6/weights/best.pt')\n",
1669
+ "last_weights = Path('runs/train/yolov9-s-relu6/weights/last.pt')\n",
1670
+ "epoch_summary = Path('runs/train/yolov9-s-relu6/results.csv')\n",
1671
+ "\n",
1672
+ "if best_weights.exists():\n",
1673
+ " output_path = '/content/yolov9-s-relu6-best.pt'\n",
1674
+ " shutil.copy(best_weights, output_path)\n",
1675
+ " print(f\"✓ Model ready: {output_path}\")\n",
1676
+ " print(f\" File size: {Path(output_path).stat().st_size / 1e6:.2f} MB\")\n",
1677
+ " print(\"\\nDownloading to your computer...\")\n",
1678
+ " files.download(output_path)\n",
1679
+ " print(\"\\n✓ Download complete!\")\n",
1680
+ "if last_weights.exists():\n",
1681
+ " output_path = '/content/yolov9-s-relu6-last.pt'\n",
1682
+ " shutil.copy(last_weights, output_path)\n",
1683
+ " print(f\"✓ Model ready: {output_path}\")\n",
1684
+ " print(f\" File size: {Path(output_path).stat().st_size / 1e6:.2f} MB\")\n",
1685
+ " print(\"\\nDownloading to your computer...\")\n",
1686
+ " files.download(output_path)\n",
1687
+ " print(\"\\n✓ Download complete!\")\n",
1688
+ "if epoch_summary.exists():\n",
1689
+ " output_path = '/content/epoch-results.csv'\n",
1690
+ " shutil.copy(epoch_summary, output_path)\n",
1691
+ " print(f\"✓ Epoch summary ready: {output_path}\")\n",
1692
+ " print(f\" File size: {Path(output_path).stat().st_size / 1e6:.2f} MB\")\n",
1693
+ " print(\"\\nDownloading to your computer...\")\n",
1694
+ " files.download(output_path)\n",
1695
+ " print(\"\\n✓ Download complete!\")\n"
1696
+ ]
1697
+ },
1698
+ {
1699
+ "cell_type": "markdown",
1700
+ "metadata": {
1701
+ "id": "validate"
1702
+ },
1703
+ "source": [
1704
+ "## Step 10: (Optional) Validate Trained Model"
1705
+ ]
1706
+ },
1707
+ {
1708
+ "cell_type": "code",
1709
+ "execution_count": null,
1710
+ "metadata": {
1711
+ "id": "validation"
1712
+ },
1713
+ "outputs": [],
1714
+ "source": [
1715
+ "print(\"=\"*70)\n",
1716
+ "print(\"Validating Trained Model\")\n",
1717
+ "print(\"=\"*70)\n",
1718
+ "\n",
1719
+ "best_weights = Path('runs/train/yolov9-s-relu6/weights/best.pt')\n",
1720
+ "\n",
1721
+ "if best_weights.exists():\n",
1722
+ " print(f\"✓ Best weights found: {best_weights}\\n\")\n",
1723
+ "\n",
1724
+ " # Run validation\n",
1725
+ " print(\"Running validation on COCO val set...\\n\")\n",
1726
+ " !python val_dual.py \\\n",
1727
+ " --data data/coco.yaml \\\n",
1728
+ " --img {IMAGE_SIZE} \\\n",
1729
+ " --batch 32 \\\n",
1730
+ " --conf 0.001 \\\n",
1731
+ " --iou 0.7 \\\n",
1732
+ " --device 0 \\\n",
1733
+ " --weights {best_weights} \\\n",
1734
+ " --task val\n",
1735
+ "\n",
1736
+ " print(\"\\n✓ Validation complete!\")\n",
1737
+ "else:\n",
1738
+ " print(\"⚠️ Best weights not found. Training may have failed.\")\n",
1739
+ " print(\" Check training output above for errors.\")"
1740
+ ]
1741
+ },
1742
+ {
1743
+ "cell_type": "markdown",
1744
+ "metadata": {
1745
+ "id": "plot_results"
1746
+ },
1747
+ "source": [
1748
+ "## Step 11: (Optional) Plot Training Results"
1749
+ ]
1750
+ },
1751
+ {
1752
+ "cell_type": "code",
1753
+ "execution_count": null,
1754
+ "metadata": {
1755
+ "id": "plot"
1756
+ },
1757
+ "outputs": [],
1758
+ "source": [
1759
+ "import matplotlib.pyplot as plt\n",
1760
+ "import pandas as pd\n",
1761
+ "\n",
1762
+ "print(\"=\"*70)\n",
1763
+ "print(\"Training Results\")\n",
1764
+ "print(\"=\"*70)\n",
1765
+ "\n",
1766
+ "results_file = Path('runs/train/yolov9-s-relu6/results.txt')\n",
1767
+ "\n",
1768
+ "if results_file.exists():\n",
1769
+ " # Read results\n",
1770
+ " results = pd.read_csv(results_file, sep=r'\\s+', header=0)\n",
1771
+ "\n",
1772
+ " # Plot\n",
1773
+ " fig, axes = plt.subplots(2, 2, figsize=(14, 10))\n",
1774
+ " fig.suptitle('YOLOv9-s ReLU Training Results', fontsize=16, fontweight='bold')\n",
1775
+ "\n",
1776
+ " # Box loss\n",
1777
+ " axes[0, 0].plot(results['epoch'], results['train/box_loss'], label='Train', linewidth=2)\n",
1778
+ " axes[0, 0].plot(results['epoch'], results['val/box_loss'], label='Val', linewidth=2)\n",
1779
+ " axes[0, 0].set_xlabel('Epoch')\n",
1780
+ " axes[0, 0].set_ylabel('Loss')\n",
1781
+ " axes[0, 0].set_title('Box Loss')\n",
1782
+ " axes[0, 0].legend()\n",
1783
+ " axes[0, 0].grid(True, alpha=0.3)\n",
1784
+ "\n",
1785
+ " # Object loss\n",
1786
+ " axes[0, 1].plot(results['epoch'], results['train/obj_loss'], label='Train', linewidth=2)\n",
1787
+ " axes[0, 1].plot(results['epoch'], results['val/obj_loss'], label='Val', linewidth=2)\n",
1788
+ " axes[0, 1].set_xlabel('Epoch')\n",
1789
+ " axes[0, 1].set_ylabel('Loss')\n",
1790
+ " axes[0, 1].set_title('Object Loss')\n",
1791
+ " axes[0, 1].legend()\n",
1792
+ " axes[0, 1].grid(True, alpha=0.3)\n",
1793
+ "\n",
1794
+ " # Class loss\n",
1795
+ " axes[1, 0].plot(results['epoch'], results['train/cls_loss'], label='Train', linewidth=2)\n",
1796
+ " axes[1, 0].plot(results['epoch'], results['val/cls_loss'], label='Val', linewidth=2)\n",
1797
+ " axes[1, 0].set_xlabel('Epoch')\n",
1798
+ " axes[1, 0].set_ylabel('Loss')\n",
1799
+ " axes[1, 0].set_title('Class Loss')\n",
1800
+ " axes[1, 0].legend()\n",
1801
+ " axes[1, 0].grid(True, alpha=0.3)\n",
1802
+ "\n",
1803
+ " # mAP metrics\n",
1804
+ " axes[1, 1].plot(results['epoch'], results['metrics/mAP50(B)'], label='mAP@0.5', linewidth=2)\n",
1805
+ " axes[1, 1].plot(results['epoch'], results['metrics/mAP50-95(B)'], label='mAP@0.5:0.95', linewidth=2)\n",
1806
+ " axes[1, 1].set_xlabel('Epoch')\n",
1807
+ " axes[1, 1].set_ylabel('mAP')\n",
1808
+ " axes[1, 1].set_title('Mean Average Precision')\n",
1809
+ " axes[1, 1].legend()\n",
1810
+ " axes[1, 1].grid(True, alpha=0.3)\n",
1811
+ "\n",
1812
+ " plt.tight_layout()\n",
1813
+ " plt.savefig('training_results.png', dpi=150, bbox_inches='tight')\n",
1814
+ " plt.show()\n",
1815
+ "\n",
1816
+ " # Show final metrics\n",
1817
+ " last_row = results.iloc[-1]\n",
1818
+ " print(f\"\\n📊 Final Metrics (Epoch {int(last_row['epoch'])}):\")\n",
1819
+ " print(f\" mAP@0.5: {last_row['metrics/mAP50(B)']:.4f}\")\n",
1820
+ " print(f\" mAP@0.5:0.95: {last_row['metrics/mAP50-95(B)']:.4f}\")\n",
1821
+ "else:\n",
1822
+ " print(\"⚠️ Results file not found\")"
1823
+ ]
1824
+ }
1825
+ ],
1826
+ "metadata": {
1827
+ "colab": {
1828
+ "provenance": [],
1829
+ "gpuType": "T4",
1830
+ "machine_shape": "hm"
1831
+ },
1832
+ "kernelspec": {
1833
+ "display_name": "Python 3",
1834
+ "name": "python3"
1835
+ },
1836
+ "language_info": {
1837
+ "name": "python"
1838
+ },
1839
+ "accelerator": "GPU"
1840
+ },
1841
+ "nbformat": 4,
1842
+ "nbformat_minor": 0
1843
+ }
source/yolov9-edgetpu-dbro/yolov9-s-relu6-best.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0005b3e03044acb007dbc1d6be2d1040b64ca98e5d2c30aaf5678f0935c6ebf9
3
+ size 79878777