From 2378d3dccd6bd5884417d9e89df7cfa4d96cc850 Mon Sep 17 00:00:00 2001 From: hillel zehavi Date: Fri, 10 Apr 2026 17:22:25 +0200 Subject: [PATCH] feat: ajout du fine-tuning YOLO --- ml/yolov8_DINO/README.md | 48 +++++++++++++++++-- ml/yolov8_DINO/configs/finetune.yaml | 13 ++++++ ml/yolov8_DINO/finetune.py | 69 ++++++++++++++++++++++++++++ pyproject.toml | 1 + uv.lock | 2 + 5 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 ml/yolov8_DINO/configs/finetune.yaml create mode 100644 ml/yolov8_DINO/finetune.py diff --git a/ml/yolov8_DINO/README.md b/ml/yolov8_DINO/README.md index 1cc4149..48a56f0 100644 --- a/ml/yolov8_DINO/README.md +++ b/ml/yolov8_DINO/README.md @@ -35,7 +35,49 @@ dataset_biolit/ Poids entraînés → `runs/biolit_v2_bootstrap/weights/` -## Partie 2 — Fine-tuning (à venir) +## Partie 2 — Fine-tuning -Pris en charge par un autre membre de l'équipe. -Entraînement sur des images cropées et annotées manuellement pour améliorer les performances du modèle bootstrap. +Cette étape consiste à améliorer le modèle YOLOv8 obtenu lors du bootstrap en utilisant un dataset plus petit (~1,400 images) mais de meilleure qualité, annoté manuellement. +Le dataset est augmenté artificiellement ×3 sur le train set uniquement (pas sur le validation set) via flips, rotations (±15° et 90°), ajustements de luminosité (±10 %) et léger flou. + +### Données + +````text +14_Biolit/ +├── data/ +│ └── manual_annotations/ +│ ├── train/... +│ ├── valid/... +│ └── data.yaml + +### Modèle de départ + +Le fine-tuning part du modèle entraîné lors de la partie 1 : + +```text +runs/biolit_v2_bootstrap/weights/best.pt +```` + +### Lancer le fine-tuning + +```bash +python finetune.py +``` + +### Configuration + +Le fichier `configs/finetune.yaml` contient les paramètres principaux : + +- chemin vers le modèle bootstrap (`best.pt`) +- chemin vers le dataset manuel (`data.yaml`) +- hyperparamètres d'entraînement (`epochs`, `batch`, `learning rate`) + +### Résultat + +Les nouveaux poids sont sauvegardés dans : + +```text +runs/biolit_v2_finetuned/weights/ +``` + +Ce modèle est ensuite utilisé pour générer les crops finaux ou pour l’inférence. diff --git a/ml/yolov8_DINO/configs/finetune.yaml b/ml/yolov8_DINO/configs/finetune.yaml new file mode 100644 index 0000000..e4357b2 --- /dev/null +++ b/ml/yolov8_DINO/configs/finetune.yaml @@ -0,0 +1,13 @@ +model: runs/biolit_v2_bootstrap/weights/best.pt +data_yaml: ../../data/manual_annotations/data.yaml + +epochs: 100 +imgsz: 640 +batch: 16 + +optimizer: AdamW +lr0: 0.001 + +# Sorties +project: runs/ +name: biolit_v2_finetuned diff --git a/ml/yolov8_DINO/finetune.py b/ml/yolov8_DINO/finetune.py new file mode 100644 index 0000000..8355d01 --- /dev/null +++ b/ml/yolov8_DINO/finetune.py @@ -0,0 +1,69 @@ +import argparse +import sys +from pathlib import Path + +import numpy as np +import torch +import yaml + +torch.use_deterministic_algorithms(False) + +if not hasattr(np, "trapz"): + np.trapz = np.trapezoid + +from ultralytics import YOLO # noqa: E402 + +sys.path.insert(0, str(Path(__file__).parent)) +from utils.logger import get_logger # noqa: E402 + +log = get_logger("finetune") + + +def _device(): + if torch.cuda.is_available(): + return "cuda" + if torch.backends.mps.is_available(): + return "mps" + return "cpu" + + +def _validate_path(path_str: str, label: str) -> Path: + path = Path(path_str) + if not path.exists(): + raise FileNotFoundError(f"{label} not found: {path}") + return path + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--config", default="configs/finetune.yaml") + args = parser.parse_args() + + with open(args.config, encoding="utf-8") as f: + cfg = yaml.safe_load(f) + + device = _device() + log.info("Fine-tuning YOLOv8 | device=%s", device) + + weights_path = _validate_path(cfg["model"], "Model weights") + data_yaml_path = _validate_path(cfg["data_yaml"], "data.yaml") + + model = YOLO(str(weights_path)) + model.train( + data=str(data_yaml_path), + epochs=cfg["epochs"], + imgsz=cfg["imgsz"], + batch=cfg["batch"], + optimizer=cfg["optimizer"], + lr0=cfg["lr0"], + device=device, + project=cfg["project"], + name=cfg["name"], + exist_ok=True, + ) + + log.info("Fine-tuning terminé.") + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 0b97a4b..afd8c75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ dependencies = [ "autodistill>=0.1.29", "autodistill-grounding-dino>=0.1.4", "autodistill-yolov8>=0.1.4", + "ultralytics", "supervision>=0.27.0.post2", "scikit-learn>=1.8.0", "boto3>=1.42.80", diff --git a/uv.lock b/uv.lock index c67a311..e8f66fc 100644 --- a/uv.lock +++ b/uv.lock @@ -64,6 +64,7 @@ dependencies = [ { name = "torch" }, { name = "tqdm" }, { name = "transformers" }, + { name = "ultralytics" }, ] [package.metadata] @@ -108,6 +109,7 @@ requires-dist = [ { name = "torch", specifier = ">=2.11.0" }, { name = "tqdm", specifier = ">=4.67.3" }, { name = "transformers", specifier = "==4.41.2" }, + { name = "ultralytics" }, ] [[package]]