MORTISCOPE: YOLOv8 Model Training and Evaluation

Author: Mortiscope

This Jupyter Notebook serves as the core development and training environment for the object detection component of the MORTISCOPE system. The primary objective is to train a YOLOv8 model to accurately detect and classify the different life stages of Chrysomya megacephala. The successful training and evaluation of this model are fundamental to the application’s ability to process images and provide accurate entomological data for PMI estimation.

Workflow Summary

This notebook follows a pipeline designed not just for training, but also for deployment and preliminary analysis of forensic imagery:

  1. Environment & Storage Initialization: Mounts Google Drive for persistent artifact storage and installs specific dependencies, including sahi for advanced inference.
  2. Dynamic Dataset Aggregation: Downloads multiple versioned datasets (varying in resolution and environmental conditions) from Roboflow and programmatically builds a unified data.yaml configuration file to prevent path errors.
  3. Custom Architecture Definition (P2): Unlike standard training, this step explicitly defines a YOLOv8-P2 architecture. This variant adds a high-resolution detection head (P2) to the model to potentially improve the ability to detect tiny objects like early-stage instars. It uses transfer learning by mapping standard YOLOv8 weights to this custom architecture.
  4. Training Pipeline: Features a smart resume system that automatically detects interruptions and resumes training from the last saved checkpoint in Google Drive, alongside a custom callback to archive weights for every epoch.
  5. Comprehensive Evaluation: Generates training loss plots, a normalized confusion matrix for biological stage differentiation, and detailed inference speed benchmarks.
  6. Advanced Inference Setup: Integrates SAHI (Sliced Aided Hyper Inference) to handle high-resolution field imagery to allow the detection of small insect instars that standard resizing would miss.
  7. Deployment & Demonstration: Exports the final model to ONNX format for edge deployment and creates an interactive inference engine. This engine detects scene types (Macro vs. Field) to automatically switch between standard and sliced inference modes, applies outlier filtering, and generates publication-ready visualizations.

Tech Stack

Deep Learning and Inference Engines

  • YOLOv8-P2 (P2 Head Variant): The standard YOLO architecture downsamples images significantly up to 32x, often causing small objects to vanish. This notebook uses a P2 variant, which includes an additional detection head at a higher resolution (4x downsampling). This preserves fine-grained texture details essential for distinguishing between instars.
  • SAHI (Sliced Aided Hyper Inference): An advanced library used to perform inference on high-resolution images by slicing them into smaller overlapping windows. This is critical for detecting small instars in wide-angle “field” shots.
  • PyTorch: The underlying tensor computation framework powering YOLOv8.
  • Transfer Learning: A technique to load weights from the standard yolov8l.pt model into the custom P2 backbone, speeding up convergence.
  • ONNX (Open Neural Network Exchange): Used to export the trained PyTorch model into a standardized format to optimize it for deployment on various hardware platforms without Python dependencies.

Data Management and Processing

  • Roboflow: Used for versioning, hosting, preprocessing, augmenting and downloading the multi-part Chrysomya megacephala dataset.
  • Google Drive: Serves as the persistent storage layer to ensure trained weights (best.pt, last.pt) and historical logs are preserved across Google Colab sessions.
  • Pandas: Used to parse and manipulate the training logs (results.csv) for custom performance plotting.
  • PyYAML: Automates the creation of the dataset configuration file, dynamically linking downloaded image paths to the model.

Visualization and Analysis

  • OpenCV (cv2): Handles image I/O, color space conversion, and drawing bounding boxes for the inference demonstration.
  • Seaborn & Matplotlib: Used to generate high-quality statistical plots, including the smoothed loss curves and the heatmap-style confusion matrix.
  • NumPy: Performs essential array operations, particularly for calculating Intersection over Union (IoU) and filtering statistical outliers in prediction areas.

Section 1: Project Initialization and Dependencies

Purpose: This section establishes the file system structure required for the project and installs the specific external libraries needed for computer vision tasks. It ensures the notebook has access to persistent storage (Google Drive) and the necessary tools for dataset management and inference.

Key Activities:

  • Drive Mounting: Connects to Google Drive to create a persistent directory. This is crucial for saving trained weights and logs so they are not lost when the Colab session disconnects.
  • Library Installation: Installs roboflow (for downloading datasets), ultralytics (for the YOLO model), and sahi (for Sliced Aided Hyper Inference, used for detecting small objects).
  • Credential Management: Securely retrieves the Roboflow API key from Colab’s secrets manager to allow authorized access to the private datasets.
import os

from google.colab import drive

# Mounts Google Drive to allow the Colab notebook to access and save files directly.
drive.mount('/content/drive')

# Defines configuration variables for the main project and the specific model folder.
root_folder = "Mortiscope Models"
model_name = "YOLOv8"

# Constructs the full, platform-independent path to the model's project directory.
project_path = os.path.join('/content/drive/MyDrive', root_folder, model_name)
# Constructs the path for a dedicated subdirectory to store model weights.
weights_path = os.path.join(project_path, 'weights')

# Creates the project and weights directories.
os.makedirs(project_path, exist_ok=True)
os.makedirs(weights_path, exist_ok=True)

# Prints the fully constructed paths to the console for user confirmation.
print(f"Project Directory: {project_path}")
print(f"Weight Storage:   {weights_path}")
Mounted at /content/drive
Project Directory: /content/drive/MyDrive/Mortiscope Models/YOLOv8
Weight Storage:   /content/drive/MyDrive/Mortiscope Models/YOLOv8/weights
# Executes the shell command to install the specified libraries using pip.
!pip install roboflow ultralytics sahi shapely

# Prints a confirmation message to the console after the installation command completes.
print("Libraries installed successfully.")
Collecting roboflow
  Downloading roboflow-1.2.11-py3-none-any.whl.metadata (9.7 kB)
Collecting ultralytics
  Downloading ultralytics-8.3.239-py3-none-any.whl.metadata (37 kB)
Collecting sahi
  Downloading sahi-0.11.36-py3-none-any.whl.metadata (19 kB)
Requirement already satisfied: shapely in /usr/local/lib/python3.12/dist-packages (2.1.2)
Requirement already satisfied: certifi in /usr/local/lib/python3.12/dist-packages (from roboflow) (2025.11.12)
Collecting idna==3.7 (from roboflow)
  Downloading idna-3.7-py3-none-any.whl.metadata (9.9 kB)
Requirement already satisfied: cycler in /usr/local/lib/python3.12/dist-packages (from roboflow) (0.12.1)
Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from roboflow) (1.4.9)
Requirement already satisfied: matplotlib in /usr/local/lib/python3.12/dist-packages (from roboflow) (3.10.0)
Requirement already satisfied: numpy>=1.18.5 in /usr/local/lib/python3.12/dist-packages (from roboflow) (2.0.2)
Collecting opencv-python-headless==4.10.0.84 (from roboflow)
  Downloading opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (20 kB)
Requirement already satisfied: Pillow>=7.1.2 in /usr/local/lib/python3.12/dist-packages (from roboflow) (11.3.0)
Collecting pi-heif<2 (from roboflow)
  Downloading pi_heif-1.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (6.5 kB)
Collecting pillow-avif-plugin<2 (from roboflow)
  Downloading pillow_avif_plugin-1.5.2-cp312-cp312-manylinux_2_28_x86_64.whl.metadata (2.1 kB)
Requirement already satisfied: python-dateutil in /usr/local/lib/python3.12/dist-packages (from roboflow) (2.9.0.post0)
Requirement already satisfied: python-dotenv in /usr/local/lib/python3.12/dist-packages (from roboflow) (1.2.1)
Requirement already satisfied: requests in /usr/local/lib/python3.12/dist-packages (from roboflow) (2.32.4)
Requirement already satisfied: six in /usr/local/lib/python3.12/dist-packages (from roboflow) (1.17.0)
Requirement already satisfied: urllib3>=1.26.6 in /usr/local/lib/python3.12/dist-packages (from roboflow) (2.5.0)
Requirement already satisfied: tqdm>=4.41.0 in /usr/local/lib/python3.12/dist-packages (from roboflow) (4.67.1)
Requirement already satisfied: PyYAML>=5.3.1 in /usr/local/lib/python3.12/dist-packages (from roboflow) (6.0.3)
Requirement already satisfied: requests-toolbelt in /usr/local/lib/python3.12/dist-packages (from roboflow) (1.0.0)
Collecting filetype (from roboflow)
  Downloading filetype-1.2.0-py2.py3-none-any.whl.metadata (6.5 kB)
Requirement already satisfied: opencv-python>=4.6.0 in /usr/local/lib/python3.12/dist-packages (from ultralytics) (4.12.0.88)
Requirement already satisfied: scipy>=1.4.1 in /usr/local/lib/python3.12/dist-packages (from ultralytics) (1.16.3)
Requirement already satisfied: torch>=1.8.0 in /usr/local/lib/python3.12/dist-packages (from ultralytics) (2.9.0+cu126)
Requirement already satisfied: torchvision>=0.9.0 in /usr/local/lib/python3.12/dist-packages (from ultralytics) (0.24.0+cu126)
Requirement already satisfied: psutil>=5.8.0 in /usr/local/lib/python3.12/dist-packages (from ultralytics) (5.9.5)
Requirement already satisfied: polars>=0.20.0 in /usr/local/lib/python3.12/dist-packages (from ultralytics) (1.31.0)
Collecting ultralytics-thop>=2.0.18 (from ultralytics)
  Downloading ultralytics_thop-2.0.18-py3-none-any.whl.metadata (14 kB)
Requirement already satisfied: click in /usr/local/lib/python3.12/dist-packages (from sahi) (8.3.1)
Collecting fire (from sahi)
  Downloading fire-0.7.1-py3-none-any.whl.metadata (5.8 kB)
Collecting opencv-python>=4.6.0 (from ultralytics)
  Downloading opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (20 kB)
Collecting pybboxes==0.1.6 (from sahi)
  Downloading pybboxes-0.1.6-py3-none-any.whl.metadata (9.9 kB)
Collecting terminaltables (from sahi)
  Downloading terminaltables-3.1.10-py2.py3-none-any.whl.metadata (3.5 kB)
Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->roboflow) (1.3.3)
Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib->roboflow) (4.61.0)
Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib->roboflow) (25.0)
Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib->roboflow) (3.2.5)
Requirement already satisfied: charset_normalizer<4,>=2 in /usr/local/lib/python3.12/dist-packages (from requests->roboflow) (3.4.4)
Requirement already satisfied: filelock in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (3.20.0)
Requirement already satisfied: typing-extensions>=4.10.0 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (4.15.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (75.2.0)
Requirement already satisfied: sympy>=1.13.3 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (1.14.0)
Requirement already satisfied: networkx>=2.5.1 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (3.6.1)
Requirement already satisfied: jinja2 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (3.1.6)
Requirement already satisfied: fsspec>=0.8.5 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (2025.3.0)
Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.6.77 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.6.77)
Requirement already satisfied: nvidia-cuda-runtime-cu12==12.6.77 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.6.77)
Requirement already satisfied: nvidia-cuda-cupti-cu12==12.6.80 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.6.80)
Requirement already satisfied: nvidia-cudnn-cu12==9.10.2.21 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (9.10.2.21)
Requirement already satisfied: nvidia-cublas-cu12==12.6.4.1 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.6.4.1)
Requirement already satisfied: nvidia-cufft-cu12==11.3.0.4 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (11.3.0.4)
Requirement already satisfied: nvidia-curand-cu12==10.3.7.77 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (10.3.7.77)
Requirement already satisfied: nvidia-cusolver-cu12==11.7.1.2 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (11.7.1.2)
Requirement already satisfied: nvidia-cusparse-cu12==12.5.4.2 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.5.4.2)
Requirement already satisfied: nvidia-cusparselt-cu12==0.7.1 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (0.7.1)
Requirement already satisfied: nvidia-nccl-cu12==2.27.5 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (2.27.5)
Requirement already satisfied: nvidia-nvshmem-cu12==3.3.20 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (3.3.20)
Requirement already satisfied: nvidia-nvtx-cu12==12.6.77 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.6.77)
Requirement already satisfied: nvidia-nvjitlink-cu12==12.6.85 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (12.6.85)
Requirement already satisfied: nvidia-cufile-cu12==1.11.1.6 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (1.11.1.6)
Requirement already satisfied: triton==3.5.0 in /usr/local/lib/python3.12/dist-packages (from torch>=1.8.0->ultralytics) (3.5.0)
Requirement already satisfied: termcolor in /usr/local/lib/python3.12/dist-packages (from fire->sahi) (3.2.0)
Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from sympy>=1.13.3->torch>=1.8.0->ultralytics) (1.3.0)
Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.12/dist-packages (from jinja2->torch>=1.8.0->ultralytics) (3.0.3)
Downloading roboflow-1.2.11-py3-none-any.whl (89 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 89.9/89.9 kB 6.4 MB/s eta 0:00:00
Downloading idna-3.7-py3-none-any.whl (66 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 66.8/66.8 kB 7.0 MB/s eta 0:00:00
Downloading opencv_python_headless-4.10.0.84-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (49.9 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 49.9/49.9 MB 54.7 MB/s eta 0:00:00
Downloading ultralytics-8.3.239-py3-none-any.whl (1.1 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.1/1.1 MB 55.7 MB/s eta 0:00:00
Downloading sahi-0.11.36-py3-none-any.whl (111 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 111.7/111.7 kB 14.0 MB/s eta 0:00:00
Downloading pybboxes-0.1.6-py3-none-any.whl (24 kB)
Downloading opencv_python-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (63.0 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 63.0/63.0 MB 42.4 MB/s eta 0:00:00
Downloading pi_heif-1.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (1.4 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1.4/1.4 MB 89.7 MB/s eta 0:00:00
Downloading pillow_avif_plugin-1.5.2-cp312-cp312-manylinux_2_28_x86_64.whl (4.2 MB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2/4.2 MB 128.8 MB/s eta 0:00:00
Downloading ultralytics_thop-2.0.18-py3-none-any.whl (28 kB)
Downloading filetype-1.2.0-py2.py3-none-any.whl (19 kB)
Downloading fire-0.7.1-py3-none-any.whl (115 kB)
   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 115.9/115.9 kB 14.3 MB/s eta 0:00:00
Downloading terminaltables-3.1.10-py2.py3-none-any.whl (15 kB)
Installing collected packages: pillow-avif-plugin, filetype, terminaltables, pybboxes, pi-heif, opencv-python-headless, opencv-python, idna, fire, ultralytics-thop, sahi, roboflow, ultralytics
  Attempting uninstall: opencv-python-headless
    Found existing installation: opencv-python-headless 4.12.0.88
    Uninstalling opencv-python-headless-4.12.0.88:
      Successfully uninstalled opencv-python-headless-4.12.0.88
  Attempting uninstall: opencv-python
    Found existing installation: opencv-python 4.12.0.88
    Uninstalling opencv-python-4.12.0.88:
      Successfully uninstalled opencv-python-4.12.0.88
  Attempting uninstall: idna
    Found existing installation: idna 3.11
    Uninstalling idna-3.11:
      Successfully uninstalled idna-3.11
Successfully installed filetype-1.2.0 fire-0.7.1 idna-3.7 opencv-python-4.11.0.86 opencv-python-headless-4.10.0.84 pi-heif-1.1.1 pillow-avif-plugin-1.5.2 pybboxes-0.1.6 roboflow-1.2.11 sahi-0.11.36 terminaltables-3.1.10 ultralytics-8.3.239 ultralytics-thop-2.0.18

Libraries installed successfully.
from google.colab import userdata
from roboflow import Roboflow

try:
    # Attempts to retrieve the 'ROBOFLOW_API_KEY' from Google Colab's secret manager.
    rf_api_key = userdata.get('ROBOFLOW_API_KEY')
    print("API Key retrieved successfully.")
except Exception as e:
    # Handles potential errors, such as the secret not being defined in the Colab environment.
    print("Error: Could not retrieve key.")
    # Re-raises the exception to halt execution if the key is essential.
    raise e

# Initializes the Roboflow client with the successfully retrieved API key.
rf = Roboflow(api_key=rf_api_key)
API Key retrieved successfully.

Section 2: Dataset Acquisition and Configuration

Purpose: To aggregate data from multiple sources and format it for the YOLOv8 training pipeline. Since the dataset is versioned across different projects (representing different resolutions and environmental conditions), this section consolidates them into a single training configuration.

Key Activities:

  • Dataset Download: Uses the Roboflow API to download specific versions of the Chrysomya megacephala datasets.
  • Dynamic YAML Generation: Instead of manually creating a configuration file, the script iterates through the downloaded folders to verify valid paths. It then programmatically generates a data.yaml file to ensure that the model only attempts to train on data that actually exists locally, preventing “path not found” errors.
# Accesses the specific Roboflow workspace that contains all the project datasets.
workspace = rf.workspace("mortiscope-fvkhd")

# Downloads version 1 of several distinct projects from the workspace.
decomposition_1 = workspace.project("decomposition-high-resolution-300").version(1).download("yolov8")
decomposition_2 = workspace.project("decomposition-high-resolution-250").version(1).download("yolov8")
decomposition_3 = workspace.project("decomposition-high-resolution-200").version(1).download("yolov8")
decomposition_4 = workspace.project("decomposition-standard-resolution-300").version(1).download("yolov8")
decomposition_5 = workspace.project("decomposition-standard-resolution-250").version(1).download("yolov8")
complementary = workspace.project("complementary").version(1).download("yolov8")

# Prints a confirmation message to the console after all download operations have successfully completed.
print("\nAll datasets downloaded successfully.")
loading Roboflow workspace...
loading Roboflow project...
loading Roboflow project...
loading Roboflow project...
loading Roboflow project...
loading Roboflow project...
loading Roboflow project...

All datasets downloaded successfully.
import yaml

# A list containing all the dataset objects that were previously downloaded.
all_datasets = [
    decomposition_1,
    decomposition_2,
    decomposition_3,
    decomposition_4,
    decomposition_5,
    complementary
]

# Initializes lists to store the file paths to the training and validation image folders.
train_paths = []
val_paths = []

print("-"*70)
print("Building Dataset Configuration")

# Iterates through each dataset to locate and collect the paths to its image directories.
for ds in all_datasets:
    # Constructs the expected paths for the 'train' and 'valid' image subdirectories.
    t_path = os.path.join(ds.location, 'train', 'images')
    v_path = os.path.join(ds.location, 'valid', 'images')

    # Verifies that the training directory actually exists before adding it to the list.
    if os.path.exists(t_path):
        train_paths.append(t_path)
        print(f"Added train: {ds.location.split('/')[-1]}")
    else:
        print(f"Skipped train (Empty): {ds.location.split('/')[-1]}")

    # Verifies that the validation directory actually exists before adding it.
    if os.path.exists(v_path):
        val_paths.append(v_path)
        print(f"Added valid: {ds.location.split('/')[-1]}")
    else:
        print(f"Skipped valid (Not found): {ds.location.split('/')[-1]}")

# Defines the master configuration dictionary in the format required by Ultralytics YOLO.
data_config = {
    'names': {
        0: 'adult',
        1: 'instar_1',
        2: 'instar_2',
        3: 'instar_3',
        4: 'pupa'
    },
    'nc': 5, # Number of classes.
    'train': train_paths,
    'val': val_paths,
}

# Defines the output path for the YAML file in the current working directory.
yaml_path = os.path.join(os.getcwd(), 'data.yaml')

# Writes the configuration dictionary to the 'data.yaml' file.
with open(yaml_path, 'w') as outfile:
    yaml.dump(data_config, outfile, default_flow_style=False)

# Prints a confirmation summary, showing the location of the file and the total number of included data folders.
print("\n" + "-"*70)
print(f"Balanced configuration created at: {yaml_path}")
print(f"Total Train Folders: {len(train_paths)}")
print(f"Total Valid Folders: {len(val_paths)}")
print("-"*70)
----------------------------------------------------------------------
Building Dataset Configuration
Added train: Decomposition-(High-Resolution---300%)-1
Added valid: Decomposition-(High-Resolution---300%)-1
Added train: Decomposition-(High-Resolution---250%)-1
Added valid: Decomposition-(High-Resolution---250%)-1
Added train: Decomposition-(High-Resolution---200%)-1
Skipped valid (Not found): Decomposition-(High-Resolution---200%)-1
Added train: Decomposition-(Standard-Resolution---300%)-1
Added valid: Decomposition-(Standard-Resolution---300%)-1
Added train: Decomposition-(Standard-Resolution---250%)-1
Added valid: Decomposition-(Standard-Resolution---250%)-1
Added train: Complementary-1
Added valid: Complementary-1
----------------------------------------------------------------------
Balanced configuration created at: /content/data.yaml
Total Train Folders: 6
Total Valid Folders: 5
----------------------------------------------------------------------

Section 3: Custom P2 Architecture Definition

Purpose: Standard YOLO models struggle with tiny objects. Here, the YOLOv8-P2 architecture was manually defined which adds a high-resolution detection layer, and prepare it for training.

Key Activities:

  • Architecture Definition: A raw string containing the YAML configuration for yolov8-p2 is defined and written to disk. This config adds a detection head at stride 4 (P2), keeping high-resolution features.
  • Weight Transfer: The standard yolov8l.pt weights are downloaded. The model.load() function then transfers the matching backbone weights into our custom P2 architecture, allowing us to benefit from pre-training even on a custom structure.
  • Callback Registration: Attaches the custom on_train_epoch_end function to save history to Drive.
import shutil

# Defines and creates a dedicated directory for storing a snapshot of the model weights at the end of each training epoch.
history_path = os.path.join(weights_path, 'epoch_history')
os.makedirs(history_path, exist_ok=True)


def on_train_epoch_end(trainer):
    """
    A callback function executed at the end of each training epoch.

    This function performs the following actions:
    1. Saves a copy of the latest model weights (`last.pt`) to a historical
       archive, named with the corresponding epoch number.
    2. Updates the primary `last.pt` file in the persistent Google Drive
       weights folder, allowing for training resumption.
    3. Updates the `best.pt` file in the persistent folder whenever the trainer
       identifies a new best-performing model.

    Args:
        trainer: The Ultralytics trainer object, which provides access to the
                 current training state, including epoch number and file paths.
    """
    # Gets the current epoch number.
    current_epoch = trainer.epoch + 1

    # Defines the source paths for the weights generated by the trainer in the temporary, session-specific output directory.
    local_last = os.path.join(trainer.save_dir, 'weights', 'last.pt')
    local_best = os.path.join(trainer.save_dir, 'weights', 'best.pt')

    # Checks for the existence of the latest epoch's weights before proceeding.
    if os.path.exists(local_last):
        # Creates a unique filename for the historical weight file.
        history_filename = f"{current_epoch:03d}_epoch.pt"
        history_dest = os.path.join(history_path, history_filename)

        # Copies the latest weights to the historical archive directory.
        shutil.copy(local_last, history_dest)
        print(f"   History Saved: {history_filename}")

        # Overwrites the main 'last.pt' file in the persistent Google Drive folder.
        resume_dest = os.path.join(weights_path, 'last.pt')
        shutil.copy(local_last, resume_dest)

    # Checks if the trainer has produced a new best-performing model weight file.
    if os.path.exists(local_best):
        # Overwrites the main 'best.pt' file in the persistent Google Drive folder.
        best_dest = os.path.join(weights_path, 'best.pt')
        shutil.copy(local_best, best_dest)

# Prints a confirmation message detailing the callback's configuration and the locations where files will be saved.
print("Callback Defined: ")
print(f"   1. History saved to: {history_path}")
print(f"   2. Resume file active at: {weights_path}/last.pt")
Callback Defined: 
   1. History saved to: /content/drive/MyDrive/Mortiscope Models/YOLOv8/weights/epoch_history
   2. Resume file active at: /content/drive/MyDrive/Mortiscope Models/YOLOv8/weights/last.pt
import os

import torch
from ultralytics import YOLO

p2_yaml_content = """
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license

# Ultralytics YOLOv8 object detection model with P2/4 - P5/32 outputs
# Model docs: https://docs.ultralytics.com/models/yolov8
# Task docs: https://docs.ultralytics.com/tasks/detect

# Parameters
nc: 80 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-p2.yaml' will call yolov8-p2.yaml with scale 'n'
  # [depth, width, max_channels]
  n: [0.33, 0.25, 1024]
  s: [0.33, 0.50, 1024]
  m: [0.67, 0.75, 768]
  l: [1.00, 1.00, 512]
  x: [1.00, 1.25, 512]

# YOLOv8.0 backbone
backbone:
  # [from, repeats, module, args]
  - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2
  - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4
  - [-1, 3, C2f, [128, True]]
  - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
  - [-1, 6, C2f, [256, True]]
  - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
  - [-1, 6, C2f, [512, True]]
  - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
  - [-1, 3, C2f, [1024, True]]
  - [-1, 1, SPPF, [1024, 5]] # 9

# YOLOv8.0-p2 head
head:
  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 6], 1, Concat, [1]] # cat backbone P4
  - [-1, 3, C2f, [512]] # 12

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 4], 1, Concat, [1]] # cat backbone P3
  - [-1, 3, C2f, [256]] # 15 (P3/8-small)

  - [-1, 1, nn.Upsample, [None, 2, "nearest"]]
  - [[-1, 2], 1, Concat, [1]] # cat backbone P2
  - [-1, 3, C2f, [128]] # 18 (P2/4-xsmall)

  - [-1, 1, Conv, [128, 3, 2]]
  - [[-1, 15], 1, Concat, [1]] # cat head P3
  - [-1, 3, C2f, [256]] # 21 (P3/8-small)

  - [-1, 1, Conv, [256, 3, 2]]
  - [[-1, 12], 1, Concat, [1]] # cat head P4
  - [-1, 3, C2f, [512]] # 24 (P4/16-medium)

  - [-1, 1, Conv, [512, 3, 2]]
  - [[-1, 9], 1, Concat, [1]] # cat head P5
  - [-1, 3, C2f, [1024]] # 27 (P5/32-large)

  - [[18, 21, 24, 27], 1, Detect, [nc]] # Detect(P2, P3, P4, P5)
"""

# Save the configuration file
custom_yaml_path = os.path.join(os.getcwd(), 'yolov8l-p2.yaml')
with open(custom_yaml_path, 'w') as f:
    f.write(p2_yaml_content)

print(f"Custom Architecture Configuration saved to: {custom_yaml_path}")


standard_weights_url = "https://github.com/ultralytics/assets/releases/download/v8.2.0/yolov8l.pt"
standard_weights_file = "yolov8l.pt"

if not os.path.exists(standard_weights_file):
    print(f"Downloading standard weights: {standard_weights_file}...")
    torch.hub.download_url_to_file(standard_weights_url, standard_weights_file)
else:
    print(f"Standard weights found locally: {standard_weights_file}")


print("Building YOLOv8-P2 architecture...")
model = YOLO(custom_yaml_path)

print("Transferring pretrained backbone weights...")
model.load(standard_weights_file)

model.add_callback("on_train_epoch_end", on_train_epoch_end)

print("\nModel Architecture (P2 Head Enabled):")
model.info(detailed=True)
Custom Architecture Configuration saved to: /content/yolov8l-p2.yaml
Downloading standard weights: yolov8l.pt...100%|██████████| 83.7M/83.7M [00:00<00:00, 145MB/s]

Building YOLOv8-P2 architecture...
Transferring pretrained backbone weights...
Transferred 419/725 items from pretrained weights

Model Architecture (P2 Head Enabled):
layer                                    name                type  gradient  parameters               shape        mu     sigma
    0                     model.0.conv.weight              Conv2d      True        1728       [64, 3, 3, 3] -0.000978    0.0926        float32
    1                       model.0.bn.weight         BatchNorm2d      True          64                [64]      3.26     0.969        float32
    1                         model.0.bn.bias         BatchNorm2d      True          64                [64]    0.0492      1.88        float32
    2                             model.0.act                SiLU     False           0                  []         -         -              -
    3                     model.1.conv.weight              Conv2d      True       73728     [128, 64, 3, 3]  -0.00049    0.0166        float32
    4                       model.1.bn.weight         BatchNorm2d      True         128               [128]      2.69     0.555        float32
    4                         model.1.bn.bias         BatchNorm2d      True         128               [128]     0.446       1.2        float32
    5                 model.2.cv1.conv.weight              Conv2d      True       16384    [128, 128, 1, 1]  -0.00246    0.0251        float32
    6                   model.2.cv1.bn.weight         BatchNorm2d      True         128               [128]      1.51     0.708        float32
    6                     model.2.cv1.bn.bias         BatchNorm2d      True         128               [128]      0.17     0.943        float32
    7                 model.2.cv2.conv.weight              Conv2d      True       40960    [128, 320, 1, 1]  -0.00105    0.0183        float32
    8                   model.2.cv2.bn.weight         BatchNorm2d      True         128               [128]     0.842     0.196        float32
    8                     model.2.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.585     0.613        float32
    9             model.2.m.0.cv1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000676    0.0154        float32
   10               model.2.m.0.cv1.bn.weight         BatchNorm2d      True          64                [64]      1.45      0.51        float32
   10                 model.2.m.0.cv1.bn.bias         BatchNorm2d      True          64                [64]     0.398       0.7        float32
   11             model.2.m.0.cv2.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000613     0.014        float32
   12               model.2.m.0.cv2.bn.weight         BatchNorm2d      True          64                [64]     0.998     0.332        float32
   12                 model.2.m.0.cv2.bn.bias         BatchNorm2d      True          64                [64]     0.151      0.92        float32
   13             model.2.m.1.cv1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000402    0.0134        float32
   14               model.2.m.1.cv1.bn.weight         BatchNorm2d      True          64                [64]      1.27     0.272        float32
   14                 model.2.m.1.cv1.bn.bias         BatchNorm2d      True          64                [64]     -0.36     0.626        float32
   15             model.2.m.1.cv2.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000758    0.0131        float32
   16               model.2.m.1.cv2.bn.weight         BatchNorm2d      True          64                [64]       1.2     0.439        float32
   16                 model.2.m.1.cv2.bn.bias         BatchNorm2d      True          64                [64]     0.299     0.925        float32
   17             model.2.m.2.cv1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000408    0.0125        float32
   18               model.2.m.2.cv1.bn.weight         BatchNorm2d      True          64                [64]     0.801     0.179        float32
   18                 model.2.m.2.cv1.bn.bias         BatchNorm2d      True          64                [64]    -0.728     0.565        float32
   19             model.2.m.2.cv2.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000742    0.0128        float32
   20               model.2.m.2.cv2.bn.weight         BatchNorm2d      True          64                [64]      1.96     0.314        float32
   20                 model.2.m.2.cv2.bn.bias         BatchNorm2d      True          64                [64]    0.0871     0.705        float32
   21                     model.3.conv.weight              Conv2d      True      294912    [256, 128, 3, 3] -0.000197   0.00828        float32
   22                       model.3.bn.weight         BatchNorm2d      True         256               [256]     0.759     0.152        float32
   22                         model.3.bn.bias         BatchNorm2d      True         256               [256]    -0.319     0.562        float32
   23                 model.4.cv1.conv.weight              Conv2d      True       65536    [256, 256, 1, 1]  -0.00057    0.0142        float32
   24                   model.4.cv1.bn.weight         BatchNorm2d      True         256               [256]     0.983     0.539        float32
   24                     model.4.cv1.bn.bias         BatchNorm2d      True         256               [256]      0.22     0.508        float32
   25                 model.4.cv2.conv.weight              Conv2d      True      262144   [256, 1024, 1, 1] -0.000499   0.00829        float32
   26                   model.4.cv2.bn.weight         BatchNorm2d      True         256               [256]      0.95     0.203        float32
   26                     model.4.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.671     0.584        float32
   27             model.4.m.0.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000447    0.0068        float32
   28               model.4.m.0.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.917     0.152        float32
   28                 model.4.m.0.cv1.bn.bias         BatchNorm2d      True         128               [128]    -0.855      0.43        float32
   29             model.4.m.0.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000294   0.00714        float32
   30               model.4.m.0.cv2.bn.weight         BatchNorm2d      True         128               [128]      0.57     0.141        float32
   30                 model.4.m.0.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.257     0.343        float32
   31             model.4.m.1.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000464    0.0073        float32
   32               model.4.m.1.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.878     0.145        float32
   32                 model.4.m.1.cv1.bn.bias         BatchNorm2d      True         128               [128]     -1.16      0.36        float32
   33             model.4.m.1.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000213   0.00663        float32
   34               model.4.m.1.cv2.bn.weight         BatchNorm2d      True         128               [128]     0.539     0.185        float32
   34                 model.4.m.1.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.302     0.363        float32
   35             model.4.m.2.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000498   0.00718        float32
   36               model.4.m.2.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.837     0.123        float32
   36                 model.4.m.2.cv1.bn.bias         BatchNorm2d      True         128               [128]     -1.28     0.365        float32
   37             model.4.m.2.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000209    0.0067        float32
   38               model.4.m.2.cv2.bn.weight         BatchNorm2d      True         128               [128]     0.655     0.138        float32
   38                 model.4.m.2.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.356     0.316        float32
   39             model.4.m.3.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000524   0.00703        float32
   40               model.4.m.3.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.799    0.0991        float32
   40                 model.4.m.3.cv1.bn.bias         BatchNorm2d      True         128               [128]     -1.35     0.345        float32
   41             model.4.m.3.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000237    0.0065        float32
   42               model.4.m.3.cv2.bn.weight         BatchNorm2d      True         128               [128]     0.772     0.128        float32
   42                 model.4.m.3.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.286     0.315        float32
   43             model.4.m.4.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000548   0.00703        float32
   44               model.4.m.4.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.798    0.0881        float32
   44                 model.4.m.4.cv1.bn.bias         BatchNorm2d      True         128               [128]     -1.37     0.367        float32
   45             model.4.m.4.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000316   0.00653        float32
   46               model.4.m.4.cv2.bn.weight         BatchNorm2d      True         128               [128]      1.03     0.143        float32
   46                 model.4.m.4.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.211     0.294        float32
   47             model.4.m.5.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]  -0.00044   0.00703        float32
   48               model.4.m.5.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.782    0.0771        float32
   48                 model.4.m.5.cv1.bn.bias         BatchNorm2d      True         128               [128]     -1.42     0.351        float32
   49             model.4.m.5.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]  -0.00022   0.00639        float32
   50               model.4.m.5.cv2.bn.weight         BatchNorm2d      True         128               [128]      1.61     0.205        float32
   50                 model.4.m.5.cv2.bn.bias         BatchNorm2d      True         128               [128]   0.00459     0.285        float32
   51                     model.5.conv.weight              Conv2d      True 1.17965e+06    [512, 256, 3, 3] -7.97e-05    0.0047        float32
   52                       model.5.bn.weight         BatchNorm2d      True         512               [512]     0.979     0.122        float32
   52                         model.5.bn.bias         BatchNorm2d      True         512               [512]    -0.544     0.301        float32
   53                 model.6.cv1.conv.weight              Conv2d      True      262144    [512, 512, 1, 1] -0.000611   0.00825        float32
   54                   model.6.cv1.bn.weight         BatchNorm2d      True         512               [512]      1.24      0.46        float32
   54                     model.6.cv1.bn.bias         BatchNorm2d      True         512               [512]     -0.27     0.478        float32
   55                 model.6.cv2.conv.weight              Conv2d      True 1.04858e+06   [512, 2048, 1, 1] -0.000242   0.00514        float32
   56                   model.6.cv2.bn.weight         BatchNorm2d      True         512               [512]      1.06     0.122        float32
   56                     model.6.cv2.bn.bias         BatchNorm2d      True         512               [512]     -1.02     0.411        float32
   57             model.6.m.0.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000283     0.004        float32
   58               model.6.m.0.cv1.bn.weight         BatchNorm2d      True         256               [256]       1.1     0.118        float32
   58                 model.6.m.0.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.916     0.206        float32
   59             model.6.m.0.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  -0.00022   0.00416        float32
   60               model.6.m.0.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.714     0.146        float32
   60                 model.6.m.0.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.489     0.268        float32
   61             model.6.m.1.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000281   0.00419        float32
   62               model.6.m.1.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.02    0.0844        float32
   62                 model.6.m.1.cv1.bn.bias         BatchNorm2d      True         256               [256]     -1.01     0.196        float32
   63             model.6.m.1.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000161   0.00418        float32
   64               model.6.m.1.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.774     0.117        float32
   64                 model.6.m.1.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.721     0.221        float32
   65             model.6.m.2.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000292    0.0042        float32
   66               model.6.m.2.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.01    0.0831        float32
   66                 model.6.m.2.cv1.bn.bias         BatchNorm2d      True         256               [256]     -1.08     0.163        float32
   67             model.6.m.2.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000204    0.0041        float32
   68               model.6.m.2.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.848     0.122        float32
   68                 model.6.m.2.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.728     0.207        float32
   69             model.6.m.3.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000279   0.00415        float32
   70               model.6.m.3.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.02    0.0889        float32
   70                 model.6.m.3.cv1.bn.bias         BatchNorm2d      True         256               [256]     -1.09     0.175        float32
   71             model.6.m.3.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000228   0.00403        float32
   72               model.6.m.3.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.949     0.108        float32
   72                 model.6.m.3.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.644     0.205        float32
   73             model.6.m.4.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000274   0.00432        float32
   74               model.6.m.4.cv1.bn.weight         BatchNorm2d      True         256               [256]     0.951     0.071        float32
   74                 model.6.m.4.cv1.bn.bias         BatchNorm2d      True         256               [256]     -1.21     0.152        float32
   75             model.6.m.4.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000211   0.00404        float32
   76               model.6.m.4.cv2.bn.weight         BatchNorm2d      True         256               [256]      1.11    0.0895        float32
   76                 model.6.m.4.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.595     0.196        float32
   77             model.6.m.5.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000293   0.00416        float32
   78               model.6.m.5.cv1.bn.weight         BatchNorm2d      True         256               [256]      0.96    0.0608        float32
   78                 model.6.m.5.cv1.bn.bias         BatchNorm2d      True         256               [256]     -1.15     0.147        float32
   79             model.6.m.5.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000137   0.00377        float32
   80               model.6.m.5.cv2.bn.weight         BatchNorm2d      True         256               [256]      1.44    0.0838        float32
   80                 model.6.m.5.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.295     0.105        float32
   81                     model.7.conv.weight              Conv2d      True  2.3593e+06    [512, 512, 3, 3] -0.000144   0.00279        float32
   82                       model.7.bn.weight         BatchNorm2d      True         512               [512]       1.1     0.108        float32
   82                         model.7.bn.bias         BatchNorm2d      True         512               [512]    -0.661     0.164        float32
   83                 model.8.cv1.conv.weight              Conv2d      True      262144    [512, 512, 1, 1] -0.000678   0.00572        float32
   84                   model.8.cv1.bn.weight         BatchNorm2d      True         512               [512]      1.13     0.222        float32
   84                     model.8.cv1.bn.bias         BatchNorm2d      True         512               [512]    -0.449      0.47        float32
   85                 model.8.cv2.conv.weight              Conv2d      True      655360   [512, 1280, 1, 1] -0.000326   0.00382        float32
   86                   model.8.cv2.bn.weight         BatchNorm2d      True         512               [512]      1.15     0.134        float32
   86                     model.8.cv2.bn.bias         BatchNorm2d      True         512               [512]    -0.233      0.16        float32
   87             model.8.m.0.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000136   0.00325        float32
   88               model.8.m.0.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.19     0.124        float32
   88                 model.8.m.0.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.395     0.259        float32
   89             model.8.m.0.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000324   0.00323        float32
   90               model.8.m.0.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.988     0.175        float32
   90                 model.8.m.0.cv2.bn.bias         BatchNorm2d      True         256               [256]     -0.41     0.216        float32
   91             model.8.m.1.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000146   0.00289        float32
   92               model.8.m.1.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.16     0.132        float32
   92                 model.8.m.1.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.433      0.19        float32
   93             model.8.m.1.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000167   0.00285        float32
   94               model.8.m.1.cv2.bn.weight         BatchNorm2d      True         256               [256]      1.07     0.194        float32
   94                 model.8.m.1.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.293     0.177        float32
   95             model.8.m.2.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000154   0.00281        float32
   96               model.8.m.2.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.14     0.135        float32
   96                 model.8.m.2.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.398     0.144        float32
   97             model.8.m.2.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000119   0.00266        float32
   98               model.8.m.2.cv2.bn.weight         BatchNorm2d      True         256               [256]      1.34     0.151        float32
   98                 model.8.m.2.cv2.bn.bias         BatchNorm2d      True         256               [256]   -0.0886    0.0886        float32
   99                 model.9.cv1.conv.weight              Conv2d      True      131072    [256, 512, 1, 1]  -0.00124   0.00735        float32
  100                   model.9.cv1.bn.weight         BatchNorm2d      True         256               [256]     0.848      0.19        float32
  100                     model.9.cv1.bn.bias         BatchNorm2d      True         256               [256]     0.316     0.413        float32
  101                 model.9.cv2.conv.weight              Conv2d      True      524288   [512, 1024, 1, 1] -3.45e-05   0.00519        float32
  102                   model.9.cv2.bn.weight         BatchNorm2d      True         512               [512]     0.893      0.15        float32
  102                     model.9.cv2.bn.bias         BatchNorm2d      True         512               [512]    -0.883     0.257        float32
  103                               model.9.m           MaxPool2d     False           0                  []         -         -              -
  104                                model.10            Upsample     False           0                  []         -         -              -
  105                                model.11              Concat     False           0                  []         -         -              -
  106                model.12.cv1.conv.weight              Conv2d      True      524288   [512, 1024, 1, 1] -0.000563   0.00756        float32
  107                  model.12.cv1.bn.weight         BatchNorm2d      True         512               [512]      1.14     0.173        float32
  107                    model.12.cv1.bn.bias         BatchNorm2d      True         512               [512]    -0.836      0.51        float32
  108                model.12.cv2.conv.weight              Conv2d      True      655360   [512, 1280, 1, 1]  -0.00052   0.00638        float32
  109                  model.12.cv2.bn.weight         BatchNorm2d      True         512               [512]     0.877     0.136        float32
  109                    model.12.cv2.bn.bias         BatchNorm2d      True         512               [512]    -0.816     0.316        float32
  110            model.12.m.0.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000391   0.00499        float32
  111              model.12.m.0.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.09     0.113        float32
  111                model.12.m.0.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.897     0.352        float32
  112            model.12.m.0.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000404   0.00479        float32
  113              model.12.m.0.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.996     0.109        float32
  113                model.12.m.0.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.766     0.343        float32
  114            model.12.m.1.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  -0.00035   0.00408        float32
  115              model.12.m.1.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.12     0.119        float32
  115                model.12.m.1.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.597     0.278        float32
  116            model.12.m.1.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000287   0.00404        float32
  117              model.12.m.1.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.919     0.089        float32
  117                model.12.m.1.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.897     0.277        float32
  118            model.12.m.2.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000241   0.00337        float32
  119              model.12.m.2.cv1.bn.weight         BatchNorm2d      True         256               [256]      1.11    0.0919        float32
  119                model.12.m.2.cv1.bn.bias         BatchNorm2d      True         256               [256]     -0.54     0.141        float32
  120            model.12.m.2.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -0.000255   0.00333        float32
  121              model.12.m.2.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.926     0.097        float32
  121                model.12.m.2.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.579     0.159        float32
  122                                model.13            Upsample     False           0                  []         -         -              -
  123                                model.14              Concat     False           0                  []         -         -              -
  124                model.15.cv1.conv.weight              Conv2d      True      196608    [256, 768, 1, 1] -0.000492    0.0093        float32
  125                  model.15.cv1.bn.weight         BatchNorm2d      True         256               [256]      0.72     0.181        float32
  125                    model.15.cv1.bn.bias         BatchNorm2d      True         256               [256]    -0.357     0.658        float32
  126                model.15.cv2.conv.weight              Conv2d      True      163840    [256, 640, 1, 1]  -0.00046   0.00755        float32
  127                  model.15.cv2.bn.weight         BatchNorm2d      True         256               [256]     0.669     0.287        float32
  127                    model.15.cv2.bn.bias         BatchNorm2d      True         256               [256]    -0.582     0.621        float32
  128            model.15.m.0.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000465   0.00872        float32
  129              model.15.m.0.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.812     0.119        float32
  129                model.15.m.0.cv1.bn.bias         BatchNorm2d      True         128               [128]    -0.675     0.489        float32
  130            model.15.m.0.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000586   0.00813        float32
  131              model.15.m.0.cv2.bn.weight         BatchNorm2d      True         128               [128]      0.82     0.143        float32
  131                model.15.m.0.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.702     0.505        float32
  132            model.15.m.1.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000615    0.0072        float32
  133              model.15.m.1.cv1.bn.weight         BatchNorm2d      True         128               [128]     0.932     0.138        float32
  133                model.15.m.1.cv1.bn.bias         BatchNorm2d      True         128               [128]    -0.616     0.415        float32
  134            model.15.m.1.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000587   0.00678        float32
  135              model.15.m.1.cv2.bn.weight         BatchNorm2d      True         128               [128]     0.861     0.157        float32
  135                model.15.m.1.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.615     0.489        float32
  136            model.15.m.2.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000515   0.00596        float32
  137              model.15.m.2.cv1.bn.weight         BatchNorm2d      True         128               [128]      0.87     0.171        float32
  137                model.15.m.2.cv1.bn.bias         BatchNorm2d      True         128               [128]    -0.854     0.334        float32
  138            model.15.m.2.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -0.000293   0.00573        float32
  139              model.15.m.2.cv2.bn.weight         BatchNorm2d      True         128               [128]      1.08     0.291        float32
  139                model.15.m.2.cv2.bn.bias         BatchNorm2d      True         128               [128]    -0.443     0.396        float32
  140                                model.16            Upsample     False           0                  []         -         -              -
  141                                model.17              Concat     False           0                  []         -         -              -
  142                model.18.cv1.conv.weight              Conv2d      True       49152    [128, 384, 1, 1] -1.03e-05    0.0295        float32
  143                  model.18.cv1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  143                    model.18.cv1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  144                model.18.cv2.conv.weight              Conv2d      True       40960    [128, 320, 1, 1] -2.74e-05    0.0322        float32
  145                  model.18.cv2.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  145                    model.18.cv2.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  146            model.18.m.0.cv1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3]  -2.6e-05     0.024        float32
  147              model.18.m.0.cv1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  147                model.18.m.0.cv1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  148            model.18.m.0.cv2.conv.weight              Conv2d      True       36864      [64, 64, 3, 3]  0.000121    0.0241        float32
  149              model.18.m.0.cv2.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  149                model.18.m.0.cv2.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  150            model.18.m.1.cv1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3]  0.000135    0.0241        float32
  151              model.18.m.1.cv1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  151                model.18.m.1.cv1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  152            model.18.m.1.cv2.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000123    0.0241        float32
  153              model.18.m.1.cv2.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  153                model.18.m.1.cv2.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  154            model.18.m.2.cv1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -0.000146     0.024        float32
  155              model.18.m.2.cv1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  155                model.18.m.2.cv1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  156            model.18.m.2.cv2.conv.weight              Conv2d      True       36864      [64, 64, 3, 3]   0.00018    0.0241        float32
  157              model.18.m.2.cv2.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  157                model.18.m.2.cv2.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  158                    model.19.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -4.66e-06     0.017        float32
  159                      model.19.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  159                        model.19.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  160                                model.20              Concat     False           0                  []         -         -              -
  161                model.21.cv1.conv.weight              Conv2d      True       98304    [256, 384, 1, 1] -5.54e-05    0.0295        float32
  162                  model.21.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  162                    model.21.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  163                model.21.cv2.conv.weight              Conv2d      True      163840    [256, 640, 1, 1]     4e-05    0.0229        float32
  164                  model.21.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  164                    model.21.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  165            model.21.m.0.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]   6.6e-05     0.017        float32
  166              model.21.m.0.cv1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  166                model.21.m.0.cv1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  167            model.21.m.0.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -8.38e-05     0.017        float32
  168              model.21.m.0.cv2.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  168                model.21.m.0.cv2.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  169            model.21.m.1.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -2.49e-05     0.017        float32
  170              model.21.m.1.cv1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  170                model.21.m.1.cv1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  171            model.21.m.1.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -2.91e-05     0.017        float32
  172              model.21.m.1.cv2.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  172                model.21.m.1.cv2.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  173            model.21.m.2.cv1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]  -7.1e-06     0.017        float32
  174              model.21.m.2.cv1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  174                model.21.m.2.cv1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  175            model.21.m.2.cv2.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]  3.03e-06     0.017        float32
  176              model.21.m.2.cv2.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  176                model.21.m.2.cv2.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  177                    model.22.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  2.08e-05     0.012        float32
  178                      model.22.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  178                        model.22.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  179                                model.23              Concat     False           0                  []         -         -              -
  180                model.24.cv1.conv.weight              Conv2d      True      393216    [512, 768, 1, 1] -4.04e-05    0.0208        float32
  181                  model.24.cv1.bn.weight         BatchNorm2d      True         512               [512]         1         0        float32
  181                    model.24.cv1.bn.bias         BatchNorm2d      True         512               [512]         0         0        float32
  182                model.24.cv2.conv.weight              Conv2d      True      655360   [512, 1280, 1, 1]  5.11e-07    0.0161        float32
  183                  model.24.cv2.bn.weight         BatchNorm2d      True         512               [512]         1         0        float32
  183                    model.24.cv2.bn.bias         BatchNorm2d      True         512               [512]         0         0        float32
  184            model.24.m.0.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  5.24e-06     0.012        float32
  185              model.24.m.0.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  185                model.24.m.0.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  186            model.24.m.0.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  9.88e-06     0.012        float32
  187              model.24.m.0.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  187                model.24.m.0.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  188            model.24.m.1.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  4.73e-06     0.012        float32
  189              model.24.m.1.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  189                model.24.m.1.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  190            model.24.m.1.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  1.22e-05     0.012        float32
  191              model.24.m.1.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  191                model.24.m.1.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  192            model.24.m.2.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -3.99e-07     0.012        float32
  193              model.24.m.2.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  193                model.24.m.2.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  194            model.24.m.2.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  1.81e-05     0.012        float32
  195              model.24.m.2.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  195                model.24.m.2.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  196                    model.25.conv.weight              Conv2d      True  2.3593e+06    [512, 512, 3, 3]  4.54e-06   0.00851        float32
  197                      model.25.bn.weight         BatchNorm2d      True         512               [512]         1         0        float32
  197                        model.25.bn.bias         BatchNorm2d      True         512               [512]         0         0        float32
  198                                model.26              Concat     False           0                  []         -         -              -
  199                model.27.cv1.conv.weight              Conv2d      True      524288   [512, 1024, 1, 1]   2.3e-05     0.018        float32
  200                  model.27.cv1.bn.weight         BatchNorm2d      True         512               [512]         1         0        float32
  200                    model.27.cv1.bn.bias         BatchNorm2d      True         512               [512]         0         0        float32
  201                model.27.cv2.conv.weight              Conv2d      True      655360   [512, 1280, 1, 1] -2.17e-05    0.0161        float32
  202                  model.27.cv2.bn.weight         BatchNorm2d      True         512               [512]         1         0        float32
  202                    model.27.cv2.bn.bias         BatchNorm2d      True         512               [512]         0         0        float32
  203            model.27.m.0.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -6.21e-06     0.012        float32
  204              model.27.m.0.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  204                model.27.m.0.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  205            model.27.m.0.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  1.41e-05     0.012        float32
  206              model.27.m.0.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  206                model.27.m.0.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  207            model.27.m.1.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  3.59e-06     0.012        float32
  208              model.27.m.1.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  208                model.27.m.1.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  209            model.27.m.1.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3] -1.45e-06     0.012        float32
  210              model.27.m.1.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  210                model.27.m.1.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  211            model.27.m.2.cv1.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  -5.2e-06     0.012        float32
  212              model.27.m.2.cv1.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  212                model.27.m.2.cv1.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  213            model.27.m.2.cv2.conv.weight              Conv2d      True      589824    [256, 256, 3, 3]  9.15e-06     0.012        float32
  214              model.27.m.2.cv2.bn.weight         BatchNorm2d      True         256               [256]         1         0        float32
  214                model.27.m.2.cv2.bn.bias         BatchNorm2d      True         256               [256]         0         0        float32
  215            model.28.cv2.0.0.conv.weight              Conv2d      True       73728     [64, 128, 3, 3]  3.48e-05     0.017        float32
  216              model.28.cv2.0.0.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  216                model.28.cv2.0.0.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  217            model.28.cv2.0.1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3]  3.72e-05    0.0241        float32
  218              model.28.cv2.0.1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  218                model.28.cv2.0.1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  219                 model.28.cv2.0.2.weight              Conv2d      True        4096      [64, 64, 1, 1]   0.00113    0.0719        float32
  219                   model.28.cv2.0.2.bias              Conv2d      True          64                [64]         1         0        float32
  220            model.28.cv2.1.0.conv.weight              Conv2d      True      147456     [64, 256, 3, 3]  2.22e-05     0.012        float32
  221              model.28.cv2.1.0.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  221                model.28.cv2.1.0.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  222            model.28.cv2.1.1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -5.27e-05     0.024        float32
  223              model.28.cv2.1.1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  223                model.28.cv2.1.1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  224                 model.28.cv2.1.2.weight              Conv2d      True        4096      [64, 64, 1, 1]  -0.00144     0.072        float32
  224                   model.28.cv2.1.2.bias              Conv2d      True          64                [64]         1         0        float32
  225            model.28.cv2.2.0.conv.weight              Conv2d      True      294912     [64, 512, 3, 3] -1.59e-05   0.00851        float32
  226              model.28.cv2.2.0.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  226                model.28.cv2.2.0.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  227            model.28.cv2.2.1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3]  0.000101    0.0241        float32
  228              model.28.cv2.2.1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  228                model.28.cv2.2.1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  229                 model.28.cv2.2.2.weight              Conv2d      True        4096      [64, 64, 1, 1] -0.000107    0.0723        float32
  229                   model.28.cv2.2.2.bias              Conv2d      True          64                [64]         1         0        float32
  230            model.28.cv2.3.0.conv.weight              Conv2d      True      294912     [64, 512, 3, 3] -1.23e-05    0.0085        float32
  231              model.28.cv2.3.0.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  231                model.28.cv2.3.0.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  232            model.28.cv2.3.1.conv.weight              Conv2d      True       36864      [64, 64, 3, 3] -3.15e-05     0.024        float32
  233              model.28.cv2.3.1.bn.weight         BatchNorm2d      True          64                [64]         1         0        float32
  233                model.28.cv2.3.1.bn.bias         BatchNorm2d      True          64                [64]         0         0        float32
  234                 model.28.cv2.3.2.weight              Conv2d      True        4096      [64, 64, 1, 1]   0.00121    0.0725        float32
  234                   model.28.cv2.3.2.bias              Conv2d      True          64                [64]         1         0        float32
  235            model.28.cv3.0.0.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -8.21e-05     0.017        float32
  236              model.28.cv3.0.0.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  236                model.28.cv3.0.0.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  237            model.28.cv3.0.1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]  2.72e-05     0.017        float32
  238              model.28.cv3.0.1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  238                model.28.cv3.0.1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  239                 model.28.cv3.0.2.weight              Conv2d      True       10240     [80, 128, 1, 1] -9.79e-05    0.0508        float32
  239                   model.28.cv3.0.2.bias              Conv2d      True          80                [80]     -12.9   9.6e-07        float32
  240            model.28.cv3.1.0.conv.weight              Conv2d      True      294912    [128, 256, 3, 3]  -2.5e-06     0.012        float32
  241              model.28.cv3.1.0.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  241                model.28.cv3.1.0.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  242            model.28.cv3.1.1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -7.73e-05     0.017        float32
  243              model.28.cv3.1.1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  243                model.28.cv3.1.1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  244                 model.28.cv3.1.2.weight              Conv2d      True       10240     [80, 128, 1, 1] -0.000987    0.0512        float32
  244                   model.28.cv3.1.2.bias              Conv2d      True          80                [80]     -11.5  1.92e-06        float32
  245            model.28.cv3.2.0.conv.weight              Conv2d      True      589824    [128, 512, 3, 3]  1.86e-05   0.00851        float32
  246              model.28.cv3.2.0.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  246                model.28.cv3.2.0.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  247            model.28.cv3.2.1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3]  5.05e-05     0.017        float32
  248              model.28.cv3.2.1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  248                model.28.cv3.2.1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  249                 model.28.cv3.2.2.weight              Conv2d      True       10240     [80, 128, 1, 1]  0.000127     0.051        float32
  249                   model.28.cv3.2.2.bias              Conv2d      True          80                [80]     -10.2         0        float32
  250            model.28.cv3.3.0.conv.weight              Conv2d      True      589824    [128, 512, 3, 3]  2.04e-06   0.00851        float32
  251              model.28.cv3.3.0.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  251                model.28.cv3.3.0.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  252            model.28.cv3.3.1.conv.weight              Conv2d      True      147456    [128, 128, 3, 3] -3.37e-05     0.017        float32
  253              model.28.cv3.3.1.bn.weight         BatchNorm2d      True         128               [128]         1         0        float32
  253                model.28.cv3.3.1.bn.bias         BatchNorm2d      True         128               [128]         0         0        float32
  254                 model.28.cv3.3.2.weight              Conv2d      True       10240     [80, 128, 1, 1] -0.000234    0.0507        float32
  254                   model.28.cv3.3.2.bias              Conv2d      True          80                [80]     -8.76         0        float32
  255                model.28.dfl.conv.weight              Conv2d     False          16       [1, 16, 1, 1]       7.5      4.76        float32

YOLOv8l-p2 summary: 256 layers, 42,888,080 parameters, 42,888,064 gradients, 206.3 GFLOPs (256, 42888080, 42888064, 206.2875136)

Section 4: Training Configuration and Execution

Purpose: This is the computational core of the notebook. It defines the hyperparameters that control how the model learns and executes the training loop.

Key Activities:

  • Hyperparameter Definition: Sets critical parameters such as epochs (100), batch_size (16), and img_size (640).
  • Augmentation Strategy: Configures geometric and color-space augmentations (Mosaic, HSV shifts, Scale) to artificially increase dataset diversity and prevent overfitting.
  • Smart Resume Logic: The script checks if a training run was interrupted. If valid weights exist in Google Drive, it automatically resumes training from the last checkpoint; otherwise, it starts a new session.
  • Training Loop: Calls model.train() to begin the backpropagation process, optimizing the weights to detect the five life stages.
# A unique identifier for this specific training run, used for naming output folders.
experiment_name = 'run_v1'
# The total number of times the model will iterate over the entire training dataset.
epochs = 100
# The number of images processed in a single forward/backward pass of the model.
batch_size = 16
# The resolution (in pixels) to which all input images will be resized before training.
img_size = 640
# The number of consecutive epochs with no improvement in validation metrics before training is stopped early.
patience = 15

# The initial step size for the optimizer's weight updates.
learning_rate = 0.005
# The optimization algorithm to be used. 'auto' allows Ultralytics to select a suitable default.
optimizer_type = 'auto'

# Probability of applying the mosaic augmentation, which combines four images into one.
mosaic_probability = 1.0
# The degree of random hue shift applied in HSV color-space augmentation.
hsv_hue_fraction = 0.015
# The degree of random saturation shift applied in HSV color-space augmentation.
hsv_saturation_fraction = 0.7
# The degree of random brightness shift applied in HSV color-space augmentation.
hsv_brightness_fraction = 0.0
# The range (in degrees) of random rotation applied to images.
rotation_degrees = 0.0
# The probability of vertically flipping an image.
flip_vertical_probability = 0.0
# The probability of horizontally flipping an image.
flip_horizontal_probability = 0.0
# The gain for applying random scaling (zoom in/out) to images.
scale_gain = 0.5

# A boolean flag to force the training to start from scratch, ignoring any existing checkpoints.
force_restart = False
# The default path to the 'last.pt' checkpoint file for automatic training resumption.
auto_resume_path = os.path.join(weights_path, 'last.pt')
# An optional path to a specific weight file to resume from, overriding the auto-resume logic if set.
specific_weight_path = ""

# Prints a formatted summary of the key training and augmentation configurations for user verification.
print("-" * 40)
print("Training Configuration")
print(f"{'Experiment Name':<25} : {experiment_name}")
print(f"{'Epochs':<25} : {epochs}")
print(f"{'Batch Size':<25} : {batch_size}")
print(f"{'Image Size':<25} : {img_size}")
print(f"{'Learning Rate':<25} : {learning_rate}")
print("-" * 40)
print("Augmentation Strategy")
print(f"{'Mosaic Probability':<25} : {mosaic_probability}")
print(f"{'Hue Fraction':<25} : {hsv_hue_fraction}")
print(f"{'Saturation Fraction':<25} : {hsv_saturation_fraction}")
print(f"{'Brightness Fraction':<25} : {hsv_brightness_fraction}")
print(f"{'Rotation Degrees':<25} : {rotation_degrees}")
print(f"{'Vertical Flip Prob':<25} : {flip_vertical_probability}")
print(f"{'Horizontal Flip Prob':<25} : {flip_horizontal_probability}")
print("-" * 40)
print(f"{'Auto Resume Path':<25} : {auto_resume_path}")
----------------------------------------
Training Configuration
Experiment Name           : run_v1
Epochs                    : 100
Batch Size                : 16
Image Size                : 640
Learning Rate             : 0.005
----------------------------------------
Augmentation Strategy
Mosaic Probability        : 1.0
Hue Fraction              : 0.015
Saturation Fraction       : 0.7
Brightness Fraction       : 0.0
Rotation Degrees          : 0.0
Vertical Flip Prob        : 0.0
Horizontal Flip Prob      : 0.0
----------------------------------------
Auto Resume Path          : /content/drive/MyDrive/Mortiscope Models/YOLOv8/weights/last.pt
import time

# Checkpoint Resumption Logic

# Initializes the default training mode to start a new session.
resume_mode = False

# Sets the default weights to the pre-trained model file.
try:
    weights_to_load = standard_weights_file
except NameError:
    # Fallback if Cell 7 variables are lost.
    weights_to_load = "yolov8l.pt"

# Checks for a specific, manually provided weight file to resume from.
if specific_weight_path and os.path.exists(specific_weight_path):
    print(f"Manual override detected.\nResuming from specific weight: {specific_weight_path}")
    resume_mode = True
    weights_to_load = specific_weight_path
# If no manual override is given, checks for the default auto-resume checkpoint file.
elif os.path.exists(auto_resume_path):
    # Handles the case where a checkpoint exists but a fresh start is explicitly required.
    if force_restart:
        print(f"Previous run found at {auto_resume_path}, but 'force_restart' is True.")
        print("Starting new training...")
        resume_mode = False
    # If a checkpoint exists and a fresh start is not forced, sets up auto-resumption.
    else:
        print(f"Previous run detected.\nAuto-resuming from: {auto_resume_path}")
        resume_mode = True
        weights_to_load = auto_resume_path
# If no checkpoints are found, configures the script to start a new training session.
else:
    print("No previous run found. Starting new training...")


# Training Execution
print("\nInitializing training loop...")
# Records the timestamp at the beginning of the training process to measure total duration.
start_time = time.time()

# Executes the appropriate training command based on whether a session is being resumed.
if resume_mode:
    # When resuming, the `resume` argument is used.
    results = model.train(
        resume=weights_to_load,
        project=project_path,
        name=experiment_name
    )
else:
    # For a new run, all hyperparameters and augmentation settings are passed explicitly.
    results = model.train(
        # Core Parameters
        data=yaml_path,
        epochs=epochs,
        imgsz=img_size,
        batch=batch_size,
        device=0,
        patience=patience,
        # Project and Checkpointing
        save=True,
        save_period=1,
        project=project_path,
        name=experiment_name,
        exist_ok=True,
        plots=True,
        # Optimizer Settings
        lr0=learning_rate,
        optimizer=optimizer_type,
        # Augmentation Parameters
        mosaic=mosaic_probability,
        hsv_h=hsv_hue_fraction,
        hsv_s=hsv_saturation_fraction,
        hsv_v=hsv_brightness_fraction,
        degrees=rotation_degrees,
        flipud=flip_vertical_probability,
        fliplr=flip_horizontal_probability,
        scale=scale_gain
    )

# Records the timestamp at the end of the training process.
end_time = time.time()

# Calculates the total training duration and formats it into hours, minutes, and seconds.
duration_seconds = end_time - start_time
hours = int(duration_seconds // 3600)
minutes = int((duration_seconds % 3600) // 60)
seconds = int(duration_seconds % 60)

# Prints a final summary of the completed training session and its duration.
print("\n" + "-"*30)
print("Training complete.")
print(f"Total Time: {hours}h {minutes}m {seconds}s")
print("-"*30)
No previous run found. Starting new training...

Initializing training loop...

Ultralytics 8.3.239 🚀 Python-3.12.12 torch-2.9.0+cu126 CUDA:0 (NVIDIA L4, 22693MiB)
engine/trainer: agnostic_nms=False, amp=True, augment=False, auto_augment=randaugment, batch=16, bgr=0.0, box=7.5, cache=False, cfg=None, classes=None, close_mosaic=10, cls=0.5, compile=False, conf=None, copy_paste=0.0, copy_paste_mode=flip, cos_lr=False, cutmix=0.0, data=/content/data.yaml, degrees=0.0, deterministic=True, device=0, dfl=1.5, dnn=False, dropout=0.0, dynamic=False, embed=None, epochs=100, erasing=0.4, exist_ok=True, fliplr=0.0, flipud=0.0, format=torchscript, fraction=1.0, freeze=None, half=False, hsv_h=0.015, hsv_s=0.7, hsv_v=0.0, imgsz=640, int8=False, iou=0.7, keras=False, kobj=1.0, line_width=None, lr0=0.005, lrf=0.01, mask_ratio=4, max_det=300, mixup=0.0, mode=train, model=/content/yolov8l-p2.yaml, momentum=0.937, mosaic=1.0, multi_scale=False, name=run_v1, nbs=64, nms=False, opset=None, optimize=False, optimizer=auto, overlap_mask=True, patience=15, perspective=0.0, plots=True, pose=12.0, pretrained=yolov8l.pt, profile=False, project=/content/drive/MyDrive/Mortiscope Models/YOLOv8, rect=False, resume=False, retina_masks=False, save=True, save_conf=False, save_crop=False, save_dir=/content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1, save_frames=False, save_json=False, save_period=1, save_txt=False, scale=0.5, seed=0, shear=0.0, show=False, show_boxes=True, show_conf=True, show_labels=True, simplify=True, single_cls=False, source=None, split=val, stream_buffer=False, task=detect, time=None, tracker=botsort.yaml, translate=0.1, val=True, verbose=True, vid_stride=1, visualize=False, warmup_bias_lr=0.1, warmup_epochs=3.0, warmup_momentum=0.8, weight_decay=0.0005, workers=8, workspace=None

Downloading https://ultralytics.com/assets/Arial.ttf to '/root/.config/Ultralytics/Arial.ttf': 100% ━━━━━━━━━━━━ 755.1KB 22.2MB/s 0.0s
Overriding model.yaml nc=80 with nc=5

                   from  n    params  module                                       arguments                     
  0                  -1  1      1856  ultralytics.nn.modules.conv.Conv             [3, 64, 3, 2]                 
  1                  -1  1     73984  ultralytics.nn.modules.conv.Conv             [64, 128, 3, 2]               
  2                  -1  3    279808  ultralytics.nn.modules.block.C2f             [128, 128, 3, True]           
  3                  -1  1    295424  ultralytics.nn.modules.conv.Conv             [128, 256, 3, 2]              
  4                  -1  6   2101248  ultralytics.nn.modules.block.C2f             [256, 256, 6, True]           
  5                  -1  1   1180672  ultralytics.nn.modules.conv.Conv             [256, 512, 3, 2]              
  6                  -1  6   8396800  ultralytics.nn.modules.block.C2f             [512, 512, 6, True]           
  7                  -1  1   2360320  ultralytics.nn.modules.conv.Conv             [512, 512, 3, 2]              
  8                  -1  3   4461568  ultralytics.nn.modules.block.C2f             [512, 512, 3, True]           
  9                  -1  1    656896  ultralytics.nn.modules.block.SPPF            [512, 512, 5]                 
 10                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 11             [-1, 6]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 12                  -1  3   4723712  ultralytics.nn.modules.block.C2f             [1024, 512, 3]                
 13                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 14             [-1, 4]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 15                  -1  3   1247744  ultralytics.nn.modules.block.C2f             [768, 256, 3]                 
 16                  -1  1         0  torch.nn.modules.upsampling.Upsample         [None, 2, 'nearest']          
 17             [-1, 2]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 18                  -1  3    312576  ultralytics.nn.modules.block.C2f             [384, 128, 3]                 
 19                  -1  1    147712  ultralytics.nn.modules.conv.Conv             [128, 128, 3, 2]              
 20            [-1, 15]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 21                  -1  3   1149440  ultralytics.nn.modules.block.C2f             [384, 256, 3]                 
 22                  -1  1    590336  ultralytics.nn.modules.conv.Conv             [256, 256, 3, 2]              
 23            [-1, 12]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 24                  -1  3   4592640  ultralytics.nn.modules.block.C2f             [768, 512, 3]                 
 25                  -1  1   2360320  ultralytics.nn.modules.conv.Conv             [512, 512, 3, 2]              
 26             [-1, 9]  1         0  ultralytics.nn.modules.conv.Concat           [1]                           
 27                  -1  3   4723712  ultralytics.nn.modules.block.C2f             [1024, 512, 3]                
 28    [18, 21, 24, 27]  1   3192612  ultralytics.nn.modules.head.Detect           [5, [128, 256, 512, 512]]     

YOLOv8l-p2 summary: 256 layers, 42,849,380 parameters, 42,849,364 gradients, 205.6 GFLOPs
Transferred 717/725 items from pretrained weights
Freezing layer 'model.28.dfl.conv.weight'

AMP: running Automatic Mixed Precision (AMP) checks...
Downloading https://github.com/ultralytics/assets/releases/download/v8.3.0/yolo11n.pt to 'yolo11n.pt': 100% ━━━━━━━━━━━━ 5.4MB 104.1MB/s 0.1s
AMP: checks passed ✅
train: Fast image access ✅ (ping: 0.0±0.0 ms, read: 1575.0±556.0 MB/s, size: 59.0 KB)
train: Scanning /content/Complementary-1/train/labels... 14102 images, 347 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 14102/14102 1.5Kit/s 9.1s
train: New cache created: /content/Complementary-1/train/labels.cache
albumentations: Blur(p=0.01, blur_limit=(3, 7)), MedianBlur(p=0.01, blur_limit=(3, 7)), ToGray(p=0.01, method='weighted_average', num_output_channels=3), CLAHE(p=0.01, clip_limit=(1.0, 4.0), tile_grid_size=(8, 8))
val: Fast image access ✅ (ping: 0.0±0.0 ms, read: 422.4±159.4 MB/s, size: 44.3 KB)
val: Scanning /content/Complementary-1/valid/labels... 896 images, 36 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 896/896 1.1Kit/s 0.8s
val: New cache created: /content/Complementary-1/valid/labels.cache
Plotting labels to /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/labels.jpg... 
optimizer: 'optimizer=auto' found, ignoring 'lr0=0.005' and 'momentum=0.937' and determining best 'optimizer', 'lr0' and 'momentum' automatically... 
optimizer: SGD(lr=0.01, momentum=0.9) with parameter groups 118 weight(decay=0.0), 127 weight(decay=0.0005), 126 bias(decay=0.0)

Image sizes 640 train, 640 val
Using 8 dataloader workers
Logging results to /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1
Starting training for 100 epochs...

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      1/100        17G      2.468      3.767      3.274         26        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:38
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.5it/s 18.2s
                   all        896       2844      0.527       0.53      0.525      0.256

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      2/100      16.5G      1.806      2.368      2.293         38        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:27
   History Saved: 002_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.8s
                   all        896       2844      0.522      0.534      0.511      0.279

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      3/100      20.7G       1.77      2.152      2.168         14        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:25
   History Saved: 003_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.4s
                   all        896       2844      0.446      0.519      0.463      0.252

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      4/100      20.9G      1.737      1.981      2.105         27        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 004_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.1s
                   all        896       2844      0.625        0.5      0.567      0.344

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      5/100      19.8G      1.676      1.775      2.041         27        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 005_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.618      0.513      0.564      0.347

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      6/100      19.2G      1.622      1.646      1.995         17        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 006_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.1s
                   all        896       2844      0.707      0.529      0.628      0.397

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      7/100      18.2G      1.592      1.569      1.951         32        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 007_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844       0.73      0.549      0.647      0.413

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      8/100      21.8G       1.56      1.501      1.919         39        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:22
   History Saved: 008_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.1s
                   all        896       2844      0.787      0.528      0.639      0.376

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
      9/100      19.5G      1.542      1.444      1.896         27        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 009_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.698      0.587      0.653       0.43

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     10/100      20.9G      1.512      1.383      1.888         45        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:22
   History Saved: 010_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.728      0.583      0.663      0.427

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     11/100      18.6G      1.497      1.366      1.854         44        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 011_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.815      0.556      0.685      0.446

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     12/100      17.6G      1.483      1.317       1.85         16        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 012_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.681      0.594      0.657      0.442

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     13/100      19.5G      1.473      1.289      1.844          8        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 013_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.695      0.634      0.687      0.457

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     14/100      19.3G      1.451       1.24      1.814         42        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:22
   History Saved: 014_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 17.0s
                   all        896       2844      0.659      0.637      0.684      0.451

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     15/100      21.8G      1.434      1.199      1.795         44        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 015_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.723      0.625      0.701      0.467

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     16/100      17.5G      1.427       1.18      1.785         12        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 016_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.6it/s 17.0s
                   all        896       2844      0.707      0.637      0.691      0.462

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     17/100      18.5G      1.412      1.157      1.777         21        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 017_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.711      0.632      0.693      0.464

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     18/100      21.7G      1.394       1.12       1.77         36        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 018_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.794      0.531      0.664      0.447

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     19/100      20.2G      1.392      1.106      1.764          8        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 019_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.729      0.633      0.707      0.477

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     20/100      18.6G      1.374       1.07       1.74         13        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 020_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.707      0.644      0.701       0.47

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     21/100      20.4G       1.36      1.052      1.736         17        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 021_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 17.0s
                   all        896       2844       0.71       0.63      0.698      0.474

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     22/100      19.4G       1.35       1.03      1.723         27        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 022_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.699      0.659        0.7      0.472

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     23/100      19.1G      1.334      1.012      1.709         14        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:22
   History Saved: 023_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.742      0.631      0.704      0.478

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     24/100      16.8G      1.325     0.9808      1.712         19        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:22
   History Saved: 024_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.699      0.624      0.687       0.47

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     25/100      19.4G      1.318     0.9655      1.693         10        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 025_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.734      0.639      0.708      0.481

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     26/100        19G      1.307     0.9505      1.694         17        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 026_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.751      0.598      0.691      0.471

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     27/100      18.8G      1.299     0.9319      1.676         28        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 027_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.714       0.65      0.707      0.486

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     28/100      21.2G      1.283     0.9106      1.665         13        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:22
   History Saved: 028_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.694      0.637        0.7       0.48

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     29/100      17.5G      1.273      0.903      1.661         14        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 029_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.731       0.63      0.706      0.481

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     30/100      18.6G      1.265     0.8816      1.652         11        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 030_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.718      0.634      0.701       0.48

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     31/100      17.3G      1.258     0.8589       1.65         14        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 031_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.719      0.635      0.704      0.484

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     32/100      20.5G      1.243     0.8518      1.641         29        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 032_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.725      0.635      0.699      0.481

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     33/100      17.8G      1.248     0.8508      1.635         16        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 033_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.718      0.619      0.688      0.472

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     34/100      17.5G      1.222      0.823      1.609          9        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 034_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.707       0.64      0.695      0.479

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     35/100      16.5G      1.218      0.812      1.609         16        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 035_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.717      0.626      0.693       0.48

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     36/100      19.8G      1.208      0.798      1.601         34        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 036_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.717      0.632      0.695      0.479

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     37/100      17.9G      1.201     0.7841      1.589         13        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:26
   History Saved: 037_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.741      0.613      0.693      0.482

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     38/100      18.9G      1.186     0.7622      1.577         18        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:24
   History Saved: 038_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.8s
                   all        896       2844      0.728      0.621      0.687      0.477

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     39/100      17.9G      1.173     0.7482       1.57          8        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 039_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844       0.72      0.615      0.677       0.47

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     40/100      18.2G      1.168     0.7455      1.566         29        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 040_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.9s
                   all        896       2844      0.707      0.652      0.698      0.484

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     41/100      18.6G      1.158     0.7293      1.562         20        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 041_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.8s
                   all        896       2844      0.735      0.631      0.689      0.479

      Epoch    GPU_mem   box_loss   cls_loss   dfl_loss  Instances       Size
     42/100      19.7G      1.155     0.7211      1.557         38        640: 100% ━━━━━━━━━━━━ 882/882 1.4it/s 10:23
   History Saved: 042_epoch.pt
                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.8s
                   all        896       2844      0.737      0.639      0.697      0.484

EarlyStopping: Training stopped early as no improvement observed in last 15 epochs. Best results observed at epoch 27, best model saved as best.pt.

To update EarlyStopping(patience=15) pass a new patience value, i.e. `patience=300` or use `patience=0` to disable EarlyStopping.

42 epochs completed in 7.526 hours.
Optimizer stripped from /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/last.pt, 86.3MB
Optimizer stripped from /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt, 86.3MB

Validating /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt...
Ultralytics 8.3.239 🚀 Python-3.12.12 torch-2.9.0+cu126 CUDA:0 (NVIDIA L4, 22693MiB)
YOLOv8l-p2 summary (fused): 138 layers, 42,824,484 parameters, 0 gradients, 204.8 GFLOPs

                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 28/28 1.7it/s 16.1s
                   all        896       2844      0.714       0.65      0.707      0.486
                 adult        176        593       0.76       0.56       0.63      0.334
              instar_1        222       1164       0.73      0.402      0.607      0.325
              instar_2        168        521      0.389      0.555      0.501      0.329
              instar_3        240        334      0.698      0.802      0.848      0.664
                  pupa        205        232      0.994      0.931      0.951      0.778

Speed: 0.2ms preprocess, 14.2ms inference, 0.0ms loss, 1.4ms postprocess per image
Results saved to /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1

------------------------------
Training complete.
Total Time: 7h 32m 19s
------------------------------

Section 5: Visual Performance Evaluation

Purpose: To assess the model’s accuracy and reliability using visual metrics. Raw numbers are often insufficient; visualization helps identify specific classes or scenarios where the model struggles.

Key Activities:

  • Training Metrics Plotting: Parses the results.csv log file to generate line charts for Box Loss, Classification Loss, Precision, Recall, and mAP (Mean Average Precision). This helps verify that the model is converging and not overfitting.
  • Confusion Matrix Generation: Runs a validation pass on the best-performing model to generate a normalized Confusion Matrix. This heatmap visualizes how often the model confuses one life stage with another, providing insight into biological similarities affecting the AI.
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns

# Constructs the full path to the results log file.
results_csv_path = os.path.join(project_path, experiment_name, 'results.csv')

# The number of data points to average over when applying a rolling mean.
smoothing_window_size = 5

# Sets a professional and consistent visual theme for all generated plots.
sns.set_theme(style="whitegrid", context="notebook", font_scale=1.1)
# Increases the resolution of the output figures for better clarity.
plt.rcParams['figure.dpi'] = 120

# Defines a perceptually uniform colormap to derive a consistent color palette.
color_map = plt.get_cmap('plasma')

# Assigns specific colors from the colormap to different metrics for consistency.
color_train = color_map(0.0)       # Color for training metrics.
color_val   = color_map(0.6)       # Color for validation metrics.
color_prec  = color_map(0.25)      # Color for the precision curve.
color_rec   = color_map(0.75)      # Color for the recall curve.
color_map_metric = color_map(0.5)  # Color for the mAP metric.
color_lr    = color_map(0.05)      # Color for the learning rate schedule.


def plot_training_results(csv_file_path):
    """
    Reads a results.csv file and generates a 2x3 grid of training performance plots.

    Args:
        csv_file_path (str): The full path to the results.csv file.
    """
    # Verifies the existence of the results file before attempting to read it.
    if not os.path.exists(csv_file_path):
        print(f"Error: Could not find results at {csv_file_path}")
        return

    # Reads the CSV data into a pandas DataFrame.
    df = pd.read_csv(csv_file_path)
    # Cleans up column names by removing leading/trailing whitespace.
    df.columns = df.columns.str.strip()

    # Creates a Matplotlib figure and an array of 2x3 subplots (axes).
    figure, axis_array = plt.subplots(2, 3, figsize=(18, 10))

    # Defines the mapping of DataFrame columns to their respective plot titles for the three primary loss functions.
    loss_map = [
        ('train/box_loss', 'val/box_loss', 'Box Loss'),
        ('train/cls_loss', 'val/cls_loss', 'Classification Loss'),
        ('train/dfl_loss', 'val/dfl_loss', 'Distribution Focal Loss')
    ]

    # Iterates through the loss map to generate the top row of plots.
    for i, (train_col, val_col, title) in enumerate(loss_map):
        axis = axis_array[0, i]

        # Plots the raw, noisy data with low opacity to serve as a background reference.
        sns.lineplot(data=df, x=df.index, y=train_col, ax=axis, color=color_train, alpha=0.15)
        sns.lineplot(data=df, x=df.index, y=val_col, ax=axis, color=color_val, alpha=0.15)

        # Overlays the smoothed data using a rolling mean for clearer trend visualization.
        sns.lineplot(x=df.index, y=df[train_col].rolling(smoothing_window_size).mean(),
                     ax=axis, color=color_train, linewidth=2.5, label='Train')
        sns.lineplot(x=df.index, y=df[val_col].rolling(smoothing_window_size).mean(),
                     ax=axis, color=color_val, linewidth=2.5, label='Validation')

        # Configures the title, labels, and legend for each loss plot.
        axis.set_title(title, color='#333333')
        axis.set_xlabel('Epochs')
        axis.set_ylabel('Loss Value')
        axis.legend()

    # Precision and Recall Plot
    axis_precision_recall = axis_array[1, 0]
    sns.lineplot(x=df.index, y=df['metrics/precision(B)'].rolling(smoothing_window_size).mean(),
                 ax=axis_precision_recall, color=color_prec, label='Precision')
    sns.lineplot(x=df.index, y=df['metrics/recall(B)'].rolling(smoothing_window_size).mean(),
                 ax=axis_precision_recall, color=color_rec, label='Recall')

    axis_precision_recall.set_title('Precision & Recall')
    axis_precision_recall.set_xlabel('Epochs')
    axis_precision_recall.set_ylabel('Score')
    axis_precision_recall.set_ylim(0, 1)

    # Mean Average Precision (mAP) Plot
    axis_map = axis_array[1, 1]
    sns.lineplot(x=df.index, y=df['metrics/mAP50(B)'].rolling(smoothing_window_size).mean(),
                 ax=axis_map, color=color_map_metric, linewidth=2.5, label='mAP @ 0.50')

    axis_map.set_title('Mean Average Precision (IoU=0.50)')
    axis_map.set_xlabel('Epochs')
    axis_map.set_ylabel('Score')
    axis_map.set_ylim(0, 1)
    # Fills the area under the mAP curve to visually emphasize the performance metric.
    axis_map.fill_between(df.index, df['metrics/mAP50(B)'].rolling(smoothing_window_size).mean(),
                          color=color_map_metric, alpha=0.1)

    # Learning Rate Schedule Plot
    axis_learning_rate = axis_array[1, 2]
    sns.lineplot(x=df.index, y=df['lr/pg0'], ax=axis_learning_rate, color=color_lr, linestyle='--')

    axis_learning_rate.set_title('Learning Rate Schedule')
    axis_learning_rate.set_xlabel('Epochs')
    axis_learning_rate.set_ylabel('Learning Rate')

    # Adjusts the spacing between subplots to prevent labels from overlapping.
    plt.tight_layout(
        pad=3.0,
        w_pad=4.0,
        h_pad=5.0
    )
    # Renders and displays the final, complete figure.
    plt.show()

# Executes the plotting function with the path to the results file.
plot_training_results(results_csv_path)

import numpy as np

# Defines the path to the best-performing model weights saved during training.
best_weight_path = os.path.join(project_path, experiment_name, 'weights', 'best.pt')
# Defines the output path for the final, publication-quality confusion matrix image.
output_image_path = os.path.join(project_path, experiment_name, 'confusion_matrix.png')

# Ensures that the script only runs if the best weight file exists.
if os.path.exists(best_weight_path):
    print(f"Loading weights from: {best_weight_path}")
    # Instantiates a new YOLO model object using the best saved weights.
    validation_model = YOLO(best_weight_path)

    # Runs a validation pass on the model.
    validation_metrics = validation_model.val(
        data=yaml_path,
        split='val',
        plots=True,
        device=0,
        batch=16,
        conf=0.001
    )

    # Extracts the raw confusion matrix (a NumPy array) from the results.
    raw_matrix = validation_metrics.confusion_matrix.matrix
    num_classes = 5
    # Slices the matrix to ensure it only contains the defined classes.
    matrix_data = raw_matrix[:num_classes, :num_classes]

    # Retrieves the class names from the validation results and formats them.
    raw_names = list(validation_metrics.names.values())[:num_classes]
    class_names = [name.replace('_', ' ').title() for name in raw_names]

    # Normalizes the confusion matrix by rows to calculate recall scores.
    row_sums = matrix_data.sum(axis=1, keepdims=True)
    # Replaces zero sums with a small number to avoid division-by-zero errors for classes that may not have appeared in the validation set.
    row_sums[row_sums == 0] = 1e-9
    matrix_normalized = matrix_data / row_sums

    # Initializes a high-resolution figure for the plot.
    plt.figure(figsize=(16, 12), dpi=300)
    sns.set_theme(style="white", font_scale=1.1)

    # Creates the heatmap using Seaborn, configuring annotations, colormap, and labels.
    axis = sns.heatmap(
        matrix_normalized,
        annot=True,                 # Displays the numerical value in each cell.
        annot_kws={"size": 14},     # Sets the font size for annotations.
        fmt='.2f',                  # Formats annotations to two decimal places.
        cmap='Blues',               # Sets the color scheme.
        xticklabels=class_names,
        yticklabels=class_names,
        vmin=0.0, vmax=1.0,         # Fixes the color bar range from 0 to 1.
        square=True,                # Enforces square cells for better proportionality.
        linewidths=2.5,
        linecolor='white',
        cbar_kws={
            'shrink': 0.6,          # Adjusts the size of the color bar.
            'pad': 0.04
        }
    )

    # Configures the color bar label to clarify that the values represent recall.
    cbar = axis.collections[0].colorbar
    cbar.set_label('Recall (Sensitivity)', labelpad=30, fontsize=14)

    # Sets the main title and axis labels with appropriate padding.
    plt.title('Confusion Matrix', fontsize=20, pad=30)
    plt.xlabel('Predicted Class', fontsize=16, labelpad=25)
    plt.ylabel('Actual Class', fontsize=16, labelpad=25)

    # Adjusts tick label appearance for clarity.
    plt.xticks(rotation=0, fontsize=13)
    plt.yticks(rotation=0, fontsize=13)

    # Adjusts subplot parameters to give a tight layout.
    plt.tight_layout(pad=5.0)

    # Saves the final figure to the specified output path.
    plt.savefig(output_image_path, dpi=300, bbox_inches='tight')
    print(f"Confusion Matrix saved to: {output_image_path}")

    # Displays the plot in the notebook output.
    plt.show()

else:
    # Prints an error message if the required 'best.pt' file is not found.
    print(f"Error: Best weights not found at {best_weight_path}")
Loading weights from: /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt

Ultralytics 8.3.239 🚀 Python-3.12.12 torch-2.9.0+cu126 CUDA:0 (NVIDIA L4, 22693MiB)
YOLOv8l-p2 summary (fused): 138 layers, 42,824,484 parameters, 0 gradients, 204.8 GFLOPs
val: Fast image access ✅ (ping: 0.0±0.0 ms, read: 1519.3±480.7 MB/s, size: 34.8 KB)
val: Scanning /content/Complementary-1/valid/labels.cache... 896 images, 36 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 896/896 1.4Mit/s 0.0s

                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 56/56 2.3it/s 24.3s
                   all        896       2844      0.714       0.65      0.707      0.486
                 adult        176        593      0.755      0.557      0.626      0.332
              instar_1        222       1164       0.73      0.401      0.608      0.326
              instar_2        168        521      0.392       0.56      0.504      0.328
              instar_3        240        334      0.698      0.803      0.849      0.663
                  pupa        205        232      0.994      0.931      0.951       0.78

Speed: 0.8ms preprocess, 22.7ms inference, 0.0ms loss, 1.1ms postprocess per image
Results saved to /content/runs/detect/val
Confusion Matrix saved to: /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/confusion_matrix.png

Section 6: Benchmarking and SAHI Integration

Purpose: To evaluate the model’s operational efficiency (speed) and prepare it for advanced inference scenarios involving high-resolution imagery.

Key Activities:

  • Speed Benchmarking: Runs a validation pass specifically to measure pre-processing, inference, and post-processing times. This calculates the estimated FPS (Frames Per Second) to determine if the model is suitable for real-time applications.
  • SAHI Wrapper Initialization: Initializes the Sliced Aided Hyper Inference wrapper. Standard YOLO resizing can make small insects vanish in 4K images. SAHI solves this by slicing the image into smaller overlapping windows, performing inference on each, and stitching the results back together.
# Defines the path to the best-performing model weights from the training run.
best_weight_path = os.path.join(project_path, experiment_name, 'weights', 'best.pt')

# Ensures the script proceeds only if the required model weight file is found.
if os.path.exists(best_weight_path):
    print(f"Loading best model for benchmarking: {best_weight_path}")
    # Loads the best model weights into a YOLO object for evaluation.
    benchmark_model = YOLO(best_weight_path)

    # Runs a validation pass on the specified dataset split.
    metrics = benchmark_model.val(data=yaml_path, split='val', plots=False, device=0)

    # Extracts the speed dictionary, which contains timing information for different stages of the inference pipeline.
    speed_metrics = metrics.speed

    # Displays a formatted summary of the average inference speed metrics.
    print("\n" + "-"*45)
    print("Inference Speed Benchmark (Average per Image)")
    print("-"*45)
    print(f"{'Pre-process':<25} : {speed_metrics['preprocess']:.2f} ms")
    print(f"{'Inference (Model)':<25} : {speed_metrics['inference']:.2f} ms")
    print(f"{'Post-process (NMS)':<25} : {speed_metrics['postprocess']:.2f} ms")
    print("-" * 45)

    # Calculates the total latency by summing the timings of all pipeline stages.
    total_latency = sum(speed_metrics.values())
    print(f"{'Total Latency':<25} : {total_latency:.2f} ms")

    # Estimates the throughput in Frames Per Second (FPS) based on the total latency.
    fps = 1000 / total_latency
    print(f"{'Estimated FPS':<25} : {fps:.2f} fps")
    print("-"*45)

else:
    # Prints an error message if the model weights file could not be located.
    print("Error: Best weights file not found.")
    print("Please make sure that the training completed successfully.")
Loading best model for benchmarking: /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt

Ultralytics 8.3.239 🚀 Python-3.12.12 torch-2.9.0+cu126 CUDA:0 (NVIDIA L4, 22693MiB)
YOLOv8l-p2 summary (fused): 138 layers, 42,824,484 parameters, 0 gradients, 204.8 GFLOPs
val: Fast image access ✅ (ping: 0.0±0.0 ms, read: 1577.8±498.4 MB/s, size: 42.6 KB)
val: Scanning /content/Complementary-1/valid/labels.cache... 896 images, 36 backgrounds, 0 corrupt: 100% ━━━━━━━━━━━━ 896/896 1.8Mit/s 0.0s

                 Class     Images  Instances      Box(P          R      mAP50  mAP50-95): 100% ━━━━━━━━━━━━ 56/56 2.4it/s 23.1s
                   all        896       2844      0.714       0.65      0.707      0.486
                 adult        176        593      0.755      0.557      0.626      0.332
              instar_1        222       1164       0.73      0.401      0.608      0.326
              instar_2        168        521      0.392       0.56      0.504      0.328
              instar_3        240        334      0.698      0.803      0.849      0.663
                  pupa        205        232      0.994      0.931      0.951       0.78

Speed: 0.6ms preprocess, 22.9ms inference, 0.0ms loss, 0.7ms postprocess per image

---------------------------------------------
Inference Speed Benchmark (Average per Image)
---------------------------------------------
Pre-process               : 0.65 ms
Inference (Model)         : 22.88 ms
Post-process (NMS)        : 0.71 ms
---------------------------------------------
Total Latency             : 24.24 ms
Estimated FPS             : 41.25 fps
---------------------------------------------
from sahi import AutoDetectionModel
from sahi.predict import get_sliced_prediction

# Defines the path to the best-performing model weights from the training run, which will be loaded into the SAHI wrapper.
best_weight_path = os.path.join(project_path, experiment_name, 'weights', 'best.pt')

# The height of each individual slice in pixels.
slice_height = 640
# The width of each individual slice in pixels.
slice_width = 640
# The percentage of overlap between adjacent slices vertically.
overlap_height_ratio = 0.2
# The percentage of overlap between adjacent slices horizontally.
overlap_width_ratio = 0.2

print(f"Initializing SAHI wrapper for: {best_weight_path}")

# Verifies that the model weight file exists before attempting to load it.
if os.path.exists(best_weight_path):
    # Initializes the SAHI AutoDetectionModel.
    detection_model = AutoDetectionModel.from_pretrained(
        model_type='yolov8',          # Specifies the model architecture.
        model_path=best_weight_path,  # Provides the path to the custom-trained weights.
        confidence_threshold=0.25,    # Sets the minimum confidence for a detection to be considered valid.
        device="cuda:0"               # Assigns the model to a specific GPU device for inference.
    )

    # Prints a confirmation message summarizing the SAHI configuration.
    print("\n" + "-"*45)
    print("SAHI Model Ready")
    print("-" * 45)
    print(f"{'Slice Dimensions':<20} : {slice_height}x{slice_width}")
    print(f"{'Overlap Ratio':<20} : {overlap_height_ratio * 100}%")
    print(f"{'Confidence Thresh':<20} : 0.25")
    print("-" * 45)

else:
    # Handles the case where the required weight file is not found.
    print(f"Error: Weights not found at {best_weight_path}")
    print("Cannot initialize SAHI.")
Initializing SAHI wrapper for: /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt

---------------------------------------------
SAHI Model Ready
---------------------------------------------
Slice Dimensions     : 640x640
Overlap Ratio        : 20.0%
Confidence Thresh    : 0.25
---------------------------------------------

Section 7: Deployment Export and Interactive Demonstration

Purpose: To finalize the model for production deployment and provide a tangible demonstration of its capabilities on user-provided data.

Key Activities:

  • ONNX Export: Converts the PyTorch model (.pt) to the ONNX (Open Neural Network Exchange) format. This format is hardware-agnostic and optimized for deployment on edge devices or web servers.
  • Interactive Inference Pipeline: A comprehensive script that:
    1. Accepts a user-uploaded image.
    2. Detects the scene type (Macro vs. Field) to choose between Standard or Sliced inference.
    3. Filters outliers based on box area to reduce false positives.
    4. Draws bounding boxes and creates a summary legend of the detected entomological evidence.
# Defines the source path for the best PyTorch model weights and the desired target path and filename for the exported ONNX model.
source_weights = os.path.join(project_path, experiment_name, 'weights', 'best.pt')
target_filename = "yolov8_mortiscope.onnx"
target_path = os.path.join(project_path, experiment_name, 'weights', target_filename)

print(f"Loading weights from: {source_weights}")

# Verifies the existence of the source weight file before initiating the export process.
if os.path.exists(source_weights):
    # Loads the trained PyTorch model from the specified '.pt' file.
    model = YOLO(source_weights)

    # Executes the model export process with specific configurations.
    exported_path = model.export(
        format='onnx',      # Specifies the target export format as ONNX.
        dynamic=False,      # Exports the model with fixed input/output dimensions for performance.
        simplify=True,      # Applies the ONNX-Simplifier to optimize the model graph.
        opset=12            # Sets the ONNX operator set version for broad compatibility.
    )

    # After export, the file is renamed and moved to the final target location.
    if isinstance(exported_path, str):
        # The `export` method saves the file with a default name.
        shutil.move(exported_path, target_path)
        print("\n" + "-"*100)
        print(f"File Saved: {target_path}")
        print("-"*100)
    else:
        # Handles cases where the export process does not return a valid file path.
        print("Export returned unexpected format.")

else:
    # Provides an error message if the source PyTorch model weights are not found.
    print(f"Error: Could not find weights at {source_weights}")
Loading weights from: /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt

Ultralytics 8.3.239 🚀 Python-3.12.12 torch-2.9.0+cu126 CPU (Intel Xeon CPU @ 2.20GHz)
YOLOv8l-p2 summary (fused): 138 layers, 42,824,484 parameters, 0 gradients, 204.8 GFLOPs

PyTorch: starting from '/content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.pt' with input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 9, 34000) (82.3 MB)
ONNX: starting export with onnx 1.20.0 opset 12...
ONNX: slimming with onnxslim 0.1.80...
ONNX: export success ✅ 4.5s, saved as '/content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.onnx' (164.1 MB)
Export complete (6.1s)

Results saved to /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights
Predict:         yolo predict task=detect model=/content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.onnx imgsz=640  
Validate:        yolo val task=detect model=/content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/best.onnx imgsz=640 data=/content/data.yaml  
Visualize:       https://netron.app

----------------------------------------------------------------------------------------------------
File Saved: /content/drive/MyDrive/Mortiscope Models/YOLOv8/run_v1/weights/yolov8_mortiscope.onnx
----------------------------------------------------------------------------------------------------
from collections import Counter, defaultdict

import cv2
import matplotlib.patches as mpatches
from google.colab import files
from sahi.prediction import ObjectPrediction

# Global Configuration

# The minimum confidence score required for a detection to be considered valid.
confidence_threshold = 0.25
# The target width in pixels for the final output visualization.
target_width = 3840
# The target height in pixels for the final output visualization.
target_height = 2160
# The resolution (Dots Per Inch) for the generated Matplotlib figure.
dpi = 100

# A standardized color palette for different insect life stages.
color_map = {
    "instar_1": "#eab308",
    "instar_2": "#84cc16",
    "instar_3": "#22c55e",
    "pupa":     "#f97316",
    "adult":    "#f43f5e"
}

# Defines the canonical order for presenting life stages in summaries and legends.
lifecycle_order = ["instar_1", "instar_2", "instar_3", "pupa", "adult"]


def hex_to_bgr(hex_color):
    """
    Converts a hexadecimal color string to a BGR tuple for use with OpenCV.

    Args:
        hex_color (str): The color in hexadecimal format (e.g., '#eab308').

    Returns:
        tuple: The color in BGR format (e.g., (8, 179, 234)).
    """
    hex_color = hex_color.lstrip('#')
    rgb = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
    # OpenCV uses BGR order, so the RGB tuple is reversed.
    return (rgb[2], rgb[1], rgb[0])


def format_class_name(name):
    """
    Formats an internal class name into a human-readable, title-cased string.

    Args:
        name (str): The internal class name (e.g., 'instar_1').

    Returns:
        str: The formatted name (e.g., 'Instar 1').
    """
    return name.replace("_", " ").title()


def calculate_iou(box1, box2):
    """
    Calculates the Intersection over Union (IoU) of two bounding boxes.

    Args:
        box1 (sahi.prediction.BBox): The first bounding box.
        box2 (sahi.prediction.BBox): The second bounding box.

    Returns:
        float: The IoU score, a value between 0.0 and 1.0.
    """
    # Extracts coordinates for easier calculation.
    b1 = [box1.minx, box1.miny, box1.maxx, box1.maxy]
    b2 = [box2.minx, box2.miny, box2.maxx, box2.maxy]

    # Determines the coordinates of the intersection rectangle.
    x1 = max(b1[0], b2[0])
    y1 = max(b1[1], b2[1])
    x2 = min(b1[2], b2[2])
    y2 = min(b1[3], b2[3])

    # Computes the area of intersection.
    intersection = max(0, x2 - x1) * max(0, y2 - y1)
    # Computes the area of both bounding boxes.
    area1 = (b1[2] - b1[0]) * (b1[3] - b1[1])
    area2 = (b2[2] - b2[0]) * (b2[3] - b2[1])
    # Computes the area of the union.
    union = area1 + area2 - intersection

    if union == 0:
        return 0
    return intersection / union


def apply_class_agnostic_nms(predictions, iou_threshold=0.6):
    """
    Applies a custom class-agnostic Non-Maximum Suppression (NMS) to a list
    of object predictions to filter out highly overlapping boxes.

    Args:
        predictions (list[ObjectPrediction]): A list of SAHI ObjectPrediction objects.
        iou_threshold (float): The IoU threshold above which boxes are suppressed.

    Returns:
        list[ObjectPrediction]: A filtered list of object predictions.
    """
    # Sorts predictions by confidence score in descending order.
    sorted_preds = sorted(predictions, key=lambda x: x.score.value, reverse=True)
    kept_preds = []

    for current in sorted_preds:
        should_keep = True
        for kept in kept_preds:
            iou = calculate_iou(current.bbox, kept.bbox)
            if iou > iou_threshold:
                # Suppresses the current box if it has a high IoU with an already kept box.
                should_keep = False
                break
        if should_keep:
            kept_preds.append(current)

    return kept_preds


def detect_scene_type(sahi_model, image_path):
    """
    Analyzes an image to determine if it is a 'macro' (close-up) or 'field'
    (wide-angle) scene.

    This heuristic is based on the average relative area of objects detected
    in an initial, low-resolution pass. Large average areas suggest a macro shot.

    Args:
        sahi_model (sahi.AutoDetectionModel): The initialized SAHI model.
        image_path (str): The path to the image file.

    Returns:
        str: The detected scene type, either 'macro' or 'field'.
    """
    native_model = sahi_model.model
    results = native_model.predict(image_path, imgsz=640, conf=0.25, verbose=False)
    boxes = results[0].boxes

    if len(boxes) == 0:
        return "field"

    # Calculates the normalized area (width * height) of each detected box.
    areas = boxes.xywhn[:, 2] * boxes.xywhn[:, 3]
    avg_area = torch.mean(areas).item()

    # Classifies the scene based on a predefined area threshold.
    if avg_area > 0.015:
        return "macro"
    else:
        return "field"


def run_image_analysis():
    """
    Orchestrates the main image analysis workflow.

    This function handles the user file upload, selects an inference strategy
    based on the scene type, processes the detections, applies filtering, and
    generates a final visual report with annotations and a summary legend.
    """
    print("Click button to upload image:")
    uploaded_files = files.upload()

    if not uploaded_files:
        print("No file uploaded.")
        return

    for filename in uploaded_files.keys():
        print(f"\nProcessing {filename}")

        # Determines the appropriate inference strategy for the uploaded image.
        scene_type = detect_scene_type(detection_model, filename)

        image_cv = cv2.imread(filename)
        img_h, img_w, _ = image_cv.shape
        img_area = img_w * img_h

        object_prediction_list = []

        # Scene-Adaptive Inference
        if scene_type == "macro":
            # For close-up images, use the standard, non-sliced prediction method.
            native_model = detection_model.model

            results = native_model.predict(
                filename,
                conf=0.45,
                imgsz=640,
                augment=False,
                agnostic_nms=True, # Uses YOLO's built-in NMS.
                verbose=False
            )

            # Manually converts the native YOLO results into the SAHI ObjectPrediction format.
            for r in results:
                for box in r.boxes:
                    x1, y1, x2, y2 = box.xyxy[0].tolist()
                    score = box.conf[0].item()
                    cls_id = int(box.cls[0].item())
                    cls_name = native_model.names[cls_id]

                    obj = ObjectPrediction(
                        bbox=[x1, y1, x2, y2],
                        category_id=cls_id,
                        category_name=cls_name,
                        score=score
                    )
                    object_prediction_list.append(obj)

            use_outlier_filter = False

        else:
            # For wide-angle images, use SAHI's sliced prediction method.
            if img_w < 2500 or img_h < 2500:
                current_slice_size = 160
                current_overlap = 0.35
            else:
                current_slice_size = 320
                current_overlap = 0.25

            result = get_sliced_prediction(
                filename,
                detection_model,
                slice_height=current_slice_size,
                slice_width=current_slice_size,
                overlap_height_ratio=current_overlap,
                overlap_width_ratio=current_overlap,
                postprocess_type="NMS",
                postprocess_match_metric="IOS",
                postprocess_match_threshold=0.5,
                postprocess_class_agnostic=True,
                verbose=1
            )
            object_prediction_list = result.object_prediction_list
            use_outlier_filter = True

        # Applies a final class-agnostic NMS pass to refine the results.
        object_prediction_list = apply_class_agnostic_nms(object_prediction_list, iou_threshold=0.6)

        class_counts = Counter()
        class_confidences = defaultdict(list)

        # Calculates the median area of all detections to use for outlier filtering.
        all_areas = []
        for pred in object_prediction_list:
            if pred.score.value >= confidence_threshold:
                bbox = pred.bbox
                area = (bbox.maxx - bbox.minx) * (bbox.maxy - bbox.miny)
                all_areas.append(area)

        median_area = np.median(all_areas) if all_areas else 0

        # Iterates through predictions to filter outliers and draw annotations.
        for prediction in object_prediction_list:
            if prediction.score.value < confidence_threshold:
                continue

            bbox = prediction.bbox
            box_area = (bbox.maxx - bbox.minx) * (bbox.maxy - bbox.miny)

            # Applies an outlier filter to remove unusually large detections, which are often false positives in field images.
            if use_outlier_filter and median_area > 0:
                coverage_ratio = box_area / img_area
                # Skip if box covers >5% of image.
                if coverage_ratio > 0.05:
                    continue
                # Skip if box is >15x median area.
                if box_area > (median_area * 15):
                    continue

            # Aggregates statistics for the final summary.
            class_name = prediction.category.name
            score = prediction.score.value
            class_counts[class_name] += 1
            class_confidences[class_name].append(score)

            # Draws the bounding box rectangle onto the image.
            color_hex = color_map.get(class_name, "#ffffff")
            color_bgr = hex_to_bgr(color_hex)
            x_min, y_min = int(bbox.minx), int(bbox.miny)
            x_max, y_max = int(bbox.maxx), int(bbox.maxy)
            cv2.rectangle(image_cv, (x_min, y_min), (x_max, y_max), color_bgr, 2)

        # Visualization and Reporting

        # Converts the OpenCV (BGR) image to RGB for Matplotlib display.
        img_rgb = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB)
        fig_w_in, fig_h_in = target_width / dpi, target_height / dpi

        # Creates a two-panel figure: one for the image, one for the legend.
        fig, (ax_image, ax_legend) = plt.subplots(
            1, 2,
            figsize=(fig_w_in, fig_h_in),
            dpi=dpi,
            gridspec_kw={'width_ratios': [3, 1], 'wspace': 0.05}
        )

        ax_image.imshow(img_rgb)
        ax_image.axis('off')
        ax_legend.axis('off')

        # Constructs the legend handles from the aggregated detection data.
        legend_handles = []
        for class_key in lifecycle_order:
            if class_key in class_counts:
                count = class_counts[class_key]
                scores = class_confidences[class_key]
                avg_score = sum(scores) / len(scores) if scores else 0

                label_text = f"{format_class_name(class_key)}: {count}{avg_score * 100:.2f}%"
                patch = mpatches.Patch(color=color_map.get(class_key, "#000"), label=label_text)
                legend_handles.append(patch)

        # Renders the legend on the right-hand panel.
        if legend_handles:
            ax_legend.legend(
                handles=legend_handles,
                loc='center',
                title="Detection Summary",
                fontsize=24,
                title_fontsize=30,
                frameon=False,
                labelspacing=0.8
            )

        plt.tight_layout()
        plt.show()

        # Prints a final textual summary to the console.
        print("\n" + "-" * 70)
        print(f"Image report: {filename}")
        print("-" * 70)
        for class_key in lifecycle_order:
             if class_key in class_counts:
                scores = class_confidences[class_key]
                avg = sum(scores)/len(scores)
                print(f"{format_class_name(class_key):<15} | Count: {class_counts[class_key]:<5} | Avg Conf: {avg*100:.2f}%")
        print("-" * 70)

# Executes the main analysis function.
run_image_analysis()


----------------------------------------------------------------------
Image report: 00059.jpg
----------------------------------------------------------------------
Adult           | Count: 8     | Avg Conf: 70.95%
----------------------------------------------------------------------