annaferrari02 commited on
Commit
63293c3
·
verified ·
1 Parent(s): a94ec9e

Upload 2 files

Browse files
Files changed (2) hide show
  1. best (3).pt +3 -0
  2. yolo8-train-corrected.ipynb +87 -0
best (3).pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:65fbce93b438271942770a5456fb82c107136095b086e282d4f7749696bd4651
3
+ size 52009362
yolo8-train-corrected.ipynb ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
4
+ "language_info": {"name": "python", "version": "3.12.12"},
5
+ "accelerator": "GPU",
6
+ "kaggle": {"accelerator": "gpu", "isInternetEnabled": true, "language": "python", "isGpuEnabled": true}
7
+ },
8
+ "nbformat_minor": 4,
9
+ "nbformat": 4,
10
+ "cells": [
11
+ {
12
+ "cell_type": "code",
13
+ "source": "# ============================================================================\n# CONFIGURATION\n# ============================================================================\n\nDATASET_NAME = 'your-username/surgical-tool-ism'\nDATASET_PATH = f'/kaggle/input/{DATASET_NAME.split(\"/\")[-1]}'\nDATA_YAML_PATH = f'{DATASET_PATH}/data.yaml'\n\nprint(f\"Dataset path: {DATASET_PATH}\")\nprint(f\"Data YAML path: {DATA_YAML_PATH}\")",
14
+ "metadata": {"trusted": true},
15
+ "outputs": [],
16
+ "execution_count": null
17
+ },
18
+ {
19
+ "cell_type": "code",
20
+ "source": "# Check GPU\n!nvidia-smi",
21
+ "metadata": {"trusted": true},
22
+ "outputs": [],
23
+ "execution_count": null
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "source": "# Install ultralytics\n!pip install -q ultralytics",
28
+ "metadata": {"trusted": true},
29
+ "outputs": [],
30
+ "execution_count": null
31
+ },
32
+ {
33
+ "cell_type": "code",
34
+ "source": "import os\n\n# Check dataset structure\nprint(\"=== ROOT DATASET ===\")\nprint(os.listdir(DATASET_PATH))\n\nprint(\"\\n=== STRUCTURE ===\")\nfor root, dirs, files in os.walk(DATASET_PATH):\n level = root.replace(DATASET_PATH, '').count(os.sep)\n if level < 3:\n indent = ' ' * 2 * level\n print(f'{indent}{os.path.basename(root)}/')\n subindent = ' ' * 2 * (level + 1)\n for file in files[:3]:\n print(f'{subindent}{file}')\n if len(files) > 3:\n print(f'{subindent}... ({len(files)} files total)')",
35
+ "metadata": {"trusted": true},
36
+ "outputs": [],
37
+ "execution_count": null
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "source": "# ===== CREATE TRAIN/VAL SPLIT =====\n\nimport os\nimport shutil\nfrom collections import defaultdict\n\nprint(\"Creating train/val split...\")\n\nSOURCE_IMAGES = f'{DATASET_PATH}/images'\nSOURCE_LABELS = f'{DATASET_PATH}/labels'\n\nDEST_BASE = '/kaggle/working/dataset_split'\nDEST_IMAGES_TRAIN = f'{DEST_BASE}/images/train'\nDEST_IMAGES_VAL = f'{DEST_BASE}/images/val'\nDEST_LABELS_TRAIN = f'{DEST_BASE}/labels/train'\nDEST_LABELS_VAL = f'{DEST_BASE}/labels/val'\n\nfor path in [DEST_IMAGES_TRAIN, DEST_IMAGES_VAL, DEST_LABELS_TRAIN, DEST_LABELS_VAL]:\n os.makedirs(path, exist_ok=True)\n\nall_images = [f for f in os.listdir(SOURCE_IMAGES) if f.endswith('.jpg')]\nprint(f\"Found {len(all_images)} total images\")\n\n# Stratified split by background condition\nconditions = ['b00', 'b01', 'b02', 'b03']\ntrain_images = []\nval_images = []\n\nprint(\"\\nSplitting by condition (80/20):\")\nprint(f\"{'Condition':<12} | {'Total':<8} | {'Train':<8} | {'Val':<8}\")\nprint(\"-\" * 50)\n\nfor cond in conditions:\n cond_images = [f for f in all_images if f.startswith(cond)]\n n_total = len(cond_images)\n n_train = int(n_total * 0.8)\n \n train_cond = cond_images[:n_train]\n val_cond = cond_images[n_train:]\n \n train_images.extend(train_cond)\n val_images.extend(val_cond)\n \n print(f\"{cond:<12} | {n_total:<8} | {len(train_cond):<8} | {len(val_cond):<8}\")\n\nprint(f\"\\nFinal split: {len(train_images)} train, {len(val_images)} val\")\n\n# Copy files\nprint(\"\\nCopying files...\")\nfor split_name, split_images in [('train', train_images), ('val', val_images)]:\n dest_img_dir = DEST_IMAGES_TRAIN if split_name == 'train' else DEST_IMAGES_VAL\n dest_lbl_dir = DEST_LABELS_TRAIN if split_name == 'train' else DEST_LABELS_VAL\n \n for img_name in split_images:\n # Copy image\n shutil.copy(\n os.path.join(SOURCE_IMAGES, img_name),\n os.path.join(dest_img_dir, img_name)\n )\n \n # Copy label\n lbl_name = img_name.replace('.jpg', '.txt')\n src_lbl = os.path.join(SOURCE_LABELS, lbl_name)\n if os.path.exists(src_lbl):\n shutil.copy(src_lbl, os.path.join(dest_lbl_dir, lbl_name))\n\nprint(\"Split complete!\")",
42
+ "metadata": {"trusted": true},
43
+ "outputs": [],
44
+ "execution_count": null
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "source": "# ===== CRITICAL FIX: CREATE CORRECT data.yaml =====\n\nimport yaml\n\n# CORRECTED data.yaml structure - names MUST be a list, not a dictionary!\ndata_config = {\n 'path': DEST_BASE,\n 'train': 'images/train',\n 'val': 'images/val',\n 'nc': 3,\n 'names': ['Large Needle Driver', 'Prograsp Forceps', 'Monopolar Curved Scissors'] # ✅ LIST format!\n}\n\ndata_yaml_output = os.path.join(DEST_BASE, 'data.yaml')\n\nwith open(data_yaml_output, 'w') as f:\n yaml.dump(data_config, f, default_flow_style=False, sort_keys=False)\n\nprint(\"✅ Created corrected data.yaml\")\nprint(\"\\nContents:\")\nwith open(data_yaml_output) as f:\n print(f.read())\n\n# Verify the format\nwith open(data_yaml_output) as f:\n loaded = yaml.safe_load(f)\n assert isinstance(loaded['names'], list), \"❌ ERROR: names should be a list!\"\n assert len(loaded['names']) == 3, \"❌ ERROR: should have exactly 3 classes!\"\n print(\"\\n✅ Verification passed: names is a list with 3 classes\")",
49
+ "metadata": {"trusted": true},
50
+ "outputs": [],
51
+ "execution_count": null
52
+ },
53
+ {
54
+ "cell_type": "code",
55
+ "source": "# ===== CHECK CLASS DISTRIBUTION =====\n\nimport os\nfrom collections import Counter\n\ndef check_class_distribution(labels_dir, split_name):\n class_counts = Counter()\n total_boxes = 0\n \n for label_file in os.listdir(labels_dir):\n if not label_file.endswith('.txt'):\n continue\n with open(os.path.join(labels_dir, label_file)) as f:\n for line in f:\n parts = line.strip().split()\n if len(parts) >= 5:\n class_id = int(parts[0])\n class_counts[class_id] += 1\n total_boxes += 1\n \n print(f\"\\n{'='*60}\")\n print(f\"{split_name.upper()} CLASS DISTRIBUTION\")\n print(f\"{'='*60}\")\n \n class_names = ['Large Needle Driver', 'Prograsp Forceps', 'Monopolar Curved Scissors']\n \n for class_id in sorted(class_counts.keys()):\n count = class_counts[class_id]\n percentage = (count / total_boxes) * 100\n name = class_names[class_id] if class_id < 3 else f\"Unknown-{class_id}\"\n print(f\"{name:<30}: {count:5d} ({percentage:5.1f}%)\")\n \n if len(class_counts) > 1:\n max_count = max(class_counts.values())\n min_count = min(class_counts.values())\n ratio = max_count / min_count\n print(f\"\\nImbalance ratio: {ratio:.2f}:1\")\n if ratio > 3:\n print(\"⚠️ SEVERE IMBALANCE!\")\n elif ratio > 1.5:\n print(\"⚠️ Moderate imbalance\")\n else:\n print(\"✅ Well balanced\")\n\ncheck_class_distribution(DEST_LABELS_TRAIN, 'training')\ncheck_class_distribution(DEST_LABELS_VAL, 'validation')",
56
+ "metadata": {"trusted": true},
57
+ "outputs": [],
58
+ "execution_count": null
59
+ },
60
+ {
61
+ "cell_type": "code",
62
+ "source": "# ===== TRAIN MODEL =====\n\nfrom ultralytics import YOLO\n\nOUTPUT_DIR = '/kaggle/working'\n\nprint(\"Initializing YOLOv8m model...\")\nmodel = YOLO('yolov8m.pt')\n\nprint(\"Starting training...\")\nprint(f\"Using data.yaml: {data_yaml_output}\")\n\nresults = model.train(\n data=data_yaml_output,\n epochs=60,\n imgsz=640,\n batch=10,\n patience=15,\n save=True,\n project=OUTPUT_DIR,\n name='yolov8m_kaggle_run',\n exist_ok=True,\n pretrained=True,\n optimizer='AdamW',\n lr0=0.001,\n lrf=0.01,\n momentum=0.937,\n weight_decay=0.00046875,\n warmup_epochs=3.0,\n warmup_momentum=0.8,\n warmup_bias_lr=0.1,\n box=7.5,\n cls=0.5,\n dfl=1.5,\n hsv_h=0.015,\n hsv_s=0.3,\n hsv_v=0.2,\n degrees=0.0,\n translate=0.05,\n scale=0.3,\n shear=0.0,\n perspective=0.0,\n flipud=0.0,\n fliplr=0.5,\n mosaic=0.5,\n mixup=0.0,\n copy_paste=0.0,\n close_mosaic=20,\n amp=True,\n fraction=1.0,\n profile=False,\n freeze=None,\n workers=2,\n device=0,\n plots=False,\n val=True,\n rect=False,\n cos_lr=False,\n overlap_mask=True,\n mask_ratio=4,\n dropout=0.0,\n cache=False\n)\n\nprint(\"\\n\" + \"=\"*70)\nprint(\"TRAINING COMPLETE!\")\nprint(\"=\"*70)",
63
+ "metadata": {"trusted": true},
64
+ "outputs": [],
65
+ "execution_count": null
66
+ },
67
+ {
68
+ "cell_type": "code",
69
+ "source": "# ===== VALIDATE BEST MODEL =====\n\nbest_model_path = f'{OUTPUT_DIR}/yolov8m_kaggle_run/weights/best.pt'\n\nprint(f\"Loading best model from: {best_model_path}\")\nmodel = YOLO(best_model_path)\n\nmetrics = model.val()\n\nprint(\"\\n\" + \"=\"*70)\nprint(\"VALIDATION METRICS\")\nprint(\"=\"*70)\nprint(f\"mAP@0.5: {metrics.box.map50:.4f}\")\nprint(f\"mAP@0.5:0.95: {metrics.box.map:.4f}\")\nprint(f\"Precision: {metrics.box.mp:.4f}\")\nprint(f\"Recall: {metrics.box.mr:.4f}\")\n\n# Per-class metrics\nif hasattr(metrics.box, 'maps'):\n print(\"\\nPer-class mAP@0.5:\")\n class_names = ['Large Needle Driver', 'Prograsp Forceps', 'Monopolar Curved Scissors']\n for i, (name, map_val) in enumerate(zip(class_names, metrics.box.maps)):\n print(f\" {name}: {map_val:.4f}\")",
70
+ "metadata": {"trusted": true},
71
+ "outputs": [],
72
+ "execution_count": null
73
+ },
74
+ {
75
+ "cell_type": "code",
76
+ "source": "# ===== LIST OUTPUT FILES =====\n\nprint(\"Output files available for download:\")\n!ls -lh {OUTPUT_DIR}/yolov8m_kaggle_run/weights/",
77
+ "metadata": {"trusted": true},
78
+ "outputs": [],
79
+ "execution_count": null
80
+ },
81
+ {
82
+ "cell_type": "markdown",
83
+ "source": "## Key Changes Made:\n\n### 1. **Fixed data.yaml structure** (CRITICAL)\n```yaml\n# ❌ WRONG (your old version):\nnames:\n 0: Large Needle Driver\n 1: Prograsp Forceps\n 2: Monopolar Curved Scissors\n\n# ✅ CORRECT (new version):\nnames: ['Large Needle Driver', 'Prograsp Forceps', 'Monopolar Curved Scissors']\n```\n\n### 2. **Added class distribution check**\nVerifies your data is balanced before training starts\n\n### 3. **Added verification step**\nConfirms the YAML file is in correct format\n\n### 4. **Added per-class validation metrics**\nSo you can see if all 3 classes are being detected\n\n## Expected Results:\n\nWith the corrected data.yaml:\n- All 3 classes should train properly\n- Per-class mAP should be similar across classes\n- Confusion matrix should show diagonal dominance for all classes\n\n## If Still Only Detecting One Class:\n\n1. Check the class distribution output - if one class has >70% of boxes, you have data imbalance\n2. Look at per-class mAP - if two classes show 0.000, there's still a data/label issue\n3. Verify label files have class IDs 0, 1, 2 (not 1, 2, 3)",
84
+ "metadata": {}
85
+ }
86
+ ]
87
+ }