edge-sign / src /quant /run_experiments.py
gyann's picture
Deploy Edge-Sign (Direction A redesign) β€” detection+tracking+recognition+Q&A
76ec265 verified
Raw
History Blame Contribute Delete
6.27 kB
"""
E1~E5 μ–‘μžν™” μ‹€ν—˜ μžλ™ μ‹€ν–‰ 슀크립트.
각 μ‹€ν—˜λ³„λ‘œ:
1. μ–‘μžν™” 적용 β†’ ONNX 내보내기
2. ultralytics val둜 mAP μΈ‘μ •
3. κ²°κ³Ό 좜λ ₯ (EXPERIMENTS.md에 μˆ˜λ™μœΌλ‘œ κΈ°μž…)
μ‚¬μš©λ²•:
python src/quant/run_experiments.py # E1~E5 전체
python src/quant/run_experiments.py --exp E1 # νŠΉμ • μ‹€ν—˜λ§Œ
"""
import argparse
import sys
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(ROOT))
DEFAULT_WEIGHTS = ROOT / "runs" / "detect" / "edge_sign_v2_e0_full3" / "weights" / "best.pt"
WEIGHTS = DEFAULT_WEIGHTS # ν˜Έν™˜μ„±μ„ μœ„ν•΄ λͺ¨λ“ˆ μ „μ—­ μœ μ§€ β€” main()μ—μ„œ override
MODEL_SPACE = ROOT / "model_space"
YOLO_YAML = ROOT / "data" / "yolo_signs" / "dataset.yaml"
# ────────────────────────────────────────────
# ONNX val (ultralytics)
# ────────────────────────────────────────────
def val_onnx(onnx_path: Path, batch: int = 16, device: str = "cpu") -> dict:
"""ONNX λͺ¨λΈ 평가 β€” ONNX Runtime은 CPU μ‹€ν–‰ (GPU DLL μ˜μ‘΄μ„± μ—†μŒ)."""
from ultralytics import YOLO
model = YOLO(str(onnx_path))
results = model.val(
data=str(YOLO_YAML),
imgsz=640,
batch=batch,
device="cpu", # ONNX Runtime GPU DLL μ—†μœΌλ―€λ‘œ 항상 CPU
verbose=False,
plots=False,
)
return {
"map50": round(results.box.map50, 4),
"map": round(results.box.map, 4),
"precision": round(results.box.mp, 4),
"recall": round(results.box.mr, 4),
}
# ────────────────────────────────────────────
# κ°œλ³„ μ‹€ν—˜
# ────────────────────────────────────────────
def run_e1(device="0"):
"""E1: W8A8 YOLOv8s (κ²€μΆœκΈ°λ§Œ)."""
from src.quant.quantize_yolo import run_w8a8
print("\n" + "=" * 50)
print("E1: W8A8 PTQ κ²€μΆœκΈ°")
print("=" * 50)
onnx_path = run_w8a8(WEIGHTS)
metrics = val_onnx(onnx_path, device=device)
return "E1", onnx_path, metrics
def run_e4(device="0"):
"""E4: W4A16 전체 (κ²€μΆœκΈ° κΈ°μ€€)."""
from src.quant.quantize_yolo import run_w4a16
print("\n" + "=" * 50)
print("E4: W4A16 PTQ κ²€μΆœκΈ°")
print("=" * 50)
onnx_path = run_w4a16(WEIGHTS)
metrics = val_onnx(onnx_path, device=device)
return "E4", onnx_path, metrics
def run_e5(device="0", calib_batches=10):
"""E5: SmoothQuant 전체 (κ²€μΆœκΈ° κΈ°μ€€)."""
from src.quant.quantize_yolo import run_smoothquant
print("\n" + "=" * 50)
print("E5: SmoothQuant + W8A8 κ²€μΆœκΈ°")
print("=" * 50)
onnx_path = run_smoothquant(WEIGHTS, calib_batches=calib_batches)
metrics = val_onnx(onnx_path, device=device)
return "E5", onnx_path, metrics
# ────────────────────────────────────────────
# κ²°κ³Ό 좜λ ₯
# ────────────────────────────────────────────
def print_results(results: list):
print("\n" + "=" * 60)
print("κ²€μΆœ μ‹€ν—˜ κ²°κ³Ό μš”μ•½")
print("=" * 60)
print(f"{'ID':<6} {'mAP@0.5':<10} {'mAP@0.5:0.95':<14} {'P':<8} {'R':<8} {'크기(MB)':<10}")
print("-" * 60)
# E0 κΈ°μ€€μ„ 
e0_onnx = MODEL_SPACE / "yolov8s_signs_fp32.onnx"
e0_size = e0_onnx.stat().st_size / 1024 / 1024 if e0_onnx.exists() else 42.67
print(
f"{'E0':<6} {'0.6275':<10} {'0.4371':<14} {'0.722':<8} {'0.543':<8} {e0_size:<10.2f} ← FP32 κΈ°μ€€μ„ "
)
for exp_id, onnx_path, m in results:
size_mb = onnx_path.stat().st_size / 1024 / 1024
print(
f"{exp_id:<6} {m['map50']:<10} {m['map']:<14} {m['precision']:<8} {m['recall']:<8} {size_mb:<10.2f}"
)
print("\n[민감도 뢄석]")
if results:
e0_map = 0.6275
for exp_id, _, m in results:
delta = m["map50"] - e0_map
pct = delta / e0_map * 100
print(f" {exp_id}: mAP50 {e0_map:.4f} β†’ {m['map50']:.4f} ({delta:+.4f}, {pct:+.1f}%)")
# ────────────────────────────────────────────
# CLI
# ────────────────────────────────────────────
EXPERIMENT_MAP = {
"E1": run_e1,
"E4": run_e4,
"E5": run_e5,
}
def main():
parser = argparse.ArgumentParser(description="μ–‘μžν™” μ‹€ν—˜ E1/E4/E5 μ‹€ν–‰")
parser.add_argument(
"--exp", choices=list(EXPERIMENT_MAP.keys()) + ["all"], default="all", help="μ‹€ν–‰ν•  μ‹€ν—˜ ID"
)
parser.add_argument("--device", type=str, default="0", help="GPU λ””λ°”μ΄μŠ€")
parser.add_argument(
"--calib_batches", type=int, default=10, help="SmoothQuant μΊ˜λ¦¬λΈŒλ ˆμ΄μ…˜ 배치 수"
)
parser.add_argument(
"--weights",
type=str,
default=None,
help="κ²€μΆœκΈ° κ°€μ€‘μΉ˜ (.pt) β€” λ―Έμ§€μ • μ‹œ κΈ°λ³Έ v1 경둜 μ‚¬μš©",
)
args = parser.parse_args()
# κ°€μ€‘μΉ˜ μ „μ—­ override
if args.weights:
global WEIGHTS
WEIGHTS = Path(args.weights)
print(f"[INFO] κ°€μ€‘μΉ˜ override: {WEIGHTS}")
targets = list(EXPERIMENT_MAP.keys()) if args.exp == "all" else [args.exp]
results = []
for exp_id in targets:
fn = EXPERIMENT_MAP[exp_id]
if exp_id == "E5":
result = fn(device=args.device, calib_batches=args.calib_batches)
else:
result = fn(device=args.device)
results.append(result)
print_results(results)
print("\nμœ„ κ²°κ³Όλ₯Ό docs/EXPERIMENTS.md κ²€μΆœ κ²°κ³Ό ν‘œμ— κΈ°μž…ν•˜μ„Έμš”.")
if __name__ == "__main__":
main()