yogesh-venkat's picture
Auto-deploy from GitHub Actions
08d66df verified
raw
history blame
7.99 kB
# ============================================================
# SMARTVISION AI - YOLOv8 TRAIN + EVAL SCRIPT
# - Uses separate train / val / test splits
# - QUICK_TEST flag lets you sanity-check the whole pipeline
# with just 1 epoch before doing full training
# ============================================================
import os
import glob
import time
import json
import torch
from ultralytics import YOLO
# ------------------------------------------------------------
# 0. CONFIG: QUICK TEST OR FULL TRAINING?
# ------------------------------------------------------------
# First run with QUICK_TEST = True (1 epoch, debug run).
# If everything runs end-to-end without errors, set it to False.
QUICK_TEST = True # <<< CHANGE TO False FOR FULL TRAINING
FULL_EPOCHS = 50
DEBUG_EPOCHS = 1
EPOCHS = DEBUG_EPOCHS if QUICK_TEST else FULL_EPOCHS
RUN_NAME = "smartvision_yolov8s_debug" if QUICK_TEST else "smartvision_yolov8s"
print("βš™οΈ QUICK_TEST :", QUICK_TEST)
print("βš™οΈ EPOCHS :", EPOCHS)
print("βš™οΈ RUN_NAME :", RUN_NAME)
# ------------------------------------------------------------
# 1. PATHS & CONFIG
# ------------------------------------------------------------
BASE_DIR = "smartvision_dataset"
DET_DIR = os.path.join(BASE_DIR, "detection")
DATA_YAML = os.path.join(DET_DIR, "data.yaml")
# Expected folder structure:
# smartvision_dataset/detection/
# data.yaml
# images/train, images/val, images/test
# labels/train, labels/val, labels/test
RUN_PROJECT = "yolo_runs"
MODEL_WEIGHTS = "yolov8s.pt" # base checkpoint to fine-tune
VAL_IMAGES_DIR = os.path.join(DET_DIR, "images", "val")
# Auto-select device
device = "0" if torch.cuda.is_available() else "cpu"
print("πŸš€ Using device:", device)
print("πŸ“‚ DATA_YAML :", DATA_YAML)
# Basic path checks (fail fast if something is wrong)
if not os.path.exists(DATA_YAML):
raise FileNotFoundError(f"data.yaml not found at: {DATA_YAML}")
for split in ["train", "val", "test"]:
img_dir = os.path.join(DET_DIR, "images", split)
lab_dir = os.path.join(DET_DIR, "labels", split)
if not os.path.isdir(img_dir):
raise FileNotFoundError(f"Images directory missing: {img_dir}")
if not os.path.isdir(lab_dir):
raise FileNotFoundError(f"Labels directory missing: {lab_dir}")
if len(glob.glob(os.path.join(img_dir, "*.jpg"))) == 0:
print(f"⚠️ Warning: No .jpg images found in {img_dir}")
# ------------------------------------------------------------
# 2. LOAD BASE MODEL
# ------------------------------------------------------------
print(f"\nπŸ“₯ Loading YOLOv8 base model from: {MODEL_WEIGHTS}")
model = YOLO(MODEL_WEIGHTS)
# ------------------------------------------------------------
# 3. TRAIN
# ------------------------------------------------------------
print("\n===== STARTING TRAINING =====")
print("(This is a QUICK TEST run)" if QUICK_TEST else "(Full training run)")
results = model.train(
data=DATA_YAML,
epochs=EPOCHS,
imgsz=640,
batch=8, # adjust if more GPU RAM
lr0=0.01,
optimizer="SGD",
device=device,
project=RUN_PROJECT,
name=RUN_NAME,
pretrained=True,
plots=True,
verbose=True,
)
print("\nβœ… YOLO training complete.")
RUN_DIR = os.path.join(RUN_PROJECT, RUN_NAME)
BEST_WEIGHTS = os.path.join(RUN_DIR, "weights", "best.pt")
print("πŸ“ Run directory:", RUN_DIR)
print("πŸ“¦ Best weights :", BEST_WEIGHTS)
if not os.path.exists(BEST_WEIGHTS):
raise FileNotFoundError(f"best.pt not found at: {BEST_WEIGHTS}")
# ------------------------------------------------------------
# 4. LOAD TRAINED MODEL (best.pt)
# ------------------------------------------------------------
print("\nπŸ“₯ Loading trained model from best.pt")
model = YOLO(BEST_WEIGHTS)
print("βœ… Loaded trained YOLOv8 model.")
print("πŸ“œ Class mapping (model.names):")
print(model.names)
# ------------------------------------------------------------
# 5. VALIDATION & TEST METRICS
# ------------------------------------------------------------
print("\n===== RUNNING VALIDATION (val split) =====")
metrics_val = model.val(
data=DATA_YAML,
split="val", # images/val + labels/val
imgsz=640,
save_json=False
)
print("\n===== YOLOv8 Validation Metrics =====")
print(f"[VAL] mAP@0.5 : {metrics_val.box.map50:.4f}")
print(f"[VAL] mAP@0.5:0.95 : {metrics_val.box.map:.4f}")
print("\nPer-class mAP@0.5 on VAL (first 10 classes):")
for i, m in enumerate(metrics_val.box.maps[:10]):
print(f" Class {i}: {m:.4f}")
print("\n===== RUNNING TEST EVALUATION (test split) =====")
metrics_test = model.val(
data=DATA_YAML,
split="test", # images/test + labels/test
imgsz=640,
save_json=False
)
print("\n===== YOLOv8 Test Metrics =====")
print(f"[TEST] mAP@0.5 : {metrics_test.box.map50:.4f}")
print(f"[TEST] mAP@0.5:0.95 : {metrics_test.box.map:.4f}")
# ------------------------------------------------------------
# 6. INFERENCE SPEED (FPS) ON VAL IMAGES
# ------------------------------------------------------------
print("\n===== MEASURING INFERENCE SPEED (FPS) ON VAL IMAGES =====")
val_images = glob.glob(os.path.join(VAL_IMAGES_DIR, "*.jpg"))
val_images = sorted(val_images)
num_test_images = min(10 if QUICK_TEST else 50, len(val_images))
test_images = val_images[:num_test_images]
print(f"Found {len(val_images)} images in {VAL_IMAGES_DIR}")
print(f"Using {len(test_images)} images for speed test.")
time_per_image = 0.0
fps = 0.0
if len(test_images) == 0:
print("⚠️ No images found for FPS test. Skipping speed measurement.")
else:
start = time.perf_counter()
_ = model.predict(
source=test_images,
imgsz=640,
conf=0.5,
verbose=False
)
end = time.perf_counter()
total_time = end - start
time_per_image = total_time / len(test_images)
fps = 1.0 / time_per_image
print(f"Total time : {total_time:.2f} sec for {len(test_images)} images")
print(f"Avg time / image : {time_per_image*1000:.2f} ms")
print(f"Approx FPS : {fps:.2f} images/sec")
# ------------------------------------------------------------
# 7. SAVE SAMPLE PREDICTION IMAGES (FROM VAL)
# ------------------------------------------------------------
print("\n===== SAVING SAMPLE PREDICTION IMAGES (VAL) =====")
sample_out_project = "yolo_vis"
sample_out_name = "samples_debug" if QUICK_TEST else "samples"
if len(test_images) == 0:
print("⚠️ No val images available for sample visualization. Skipping sample predictions.")
else:
_ = model.predict(
source=test_images[:4 if QUICK_TEST else 8],
imgsz=640,
conf=0.5,
save=True,
project=sample_out_project,
name=sample_out_name,
verbose=False,
)
print(f"βœ… Saved sample predictions (with boxes & labels) to: {sample_out_project}/{sample_out_name}/")
# ------------------------------------------------------------
# 8. SAVE METRICS TO JSON
# ------------------------------------------------------------
print("\n===== SAVING METRICS TO JSON =====")
os.makedirs("yolo_metrics", exist_ok=True)
metrics_json_path = os.path.join("yolo_metrics", "yolov8s_metrics_debug.json" if QUICK_TEST else "yolov8s_metrics.json")
yolo_metrics = {
"model_name": "yolov8s_smartvision",
"quick_test": QUICK_TEST,
"epochs": EPOCHS,
"run_dir": RUN_DIR,
"best_weights": BEST_WEIGHTS,
"val_map_50": float(metrics_val.box.map50),
"val_map_50_95": float(metrics_val.box.map),
"test_map_50": float(metrics_test.box.map50),
"test_map_50_95": float(metrics_test.box.map),
"num_val_images_for_speed_test": int(len(test_images)),
"avg_inference_time_sec": float(time_per_image),
"fps": float(fps),
}
with open(metrics_json_path, "w") as f:
json.dump(yolo_metrics, f, indent=2)
print(f"βœ… Saved YOLO metrics JSON to: {metrics_json_path}")
print("\n🎯 YOLOv8 training + evaluation script finished.")