{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport os\nfrom sklearn.model_selection import train_test_split\nimport ast\n\n# ==========================================\n# 1. Data Preparation & YOLO Formatting\n# ==========================================\ndf = pd.read_csv('/kaggle/input/global-wheat-detection/train.csv')\ndf['bbox'] = df['bbox'].apply(ast.literal_eval)\n\n# Extract raw coordinates\ndf['xmin'] = df['bbox'].apply(lambda x: x[0])\ndf['ymin'] = df['bbox'].apply(lambda x: x[1])\ndf['w'] = df['bbox'].apply(lambda x: x[2])\ndf['h'] = df['bbox'].apply(lambda x: x[3])\n\nIMG_SIZE = 1024.0\ndf['x_center'] = (df['xmin'] + df['w'] / 2) / IMG_SIZE\ndf['y_center'] = (df['ymin'] + df['h'] / 2) / IMG_SIZE\ndf['w_norm'] = df['w'] / IMG_SIZE\ndf['h_norm'] = df['h'] / IMG_SIZE\ndf['class_id'] = 0","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================\n# 2. Directory Setup with Symlinks\n# ==========================================\nbase_dir = '/kaggle/working/wheat_yolo'\nos.makedirs(f'{base_dir}/images/train', exist_ok=True)\nos.makedirs(f'{base_dir}/images/val', exist_ok=True)\nos.makedirs(f'{base_dir}/labels/train', exist_ok=True)\nos.makedirs(f'{base_dir}/labels/val', exist_ok=True)\n\n# CRITICAL FIX: Read actual files to include empty background images\n# Not just the ones listed in the CSV!\nTRAIN_DIR = '/kaggle/input/global-wheat-detection/train'\nall_image_files = [f.split('.')[0] for f in os.listdir(TRAIN_DIR) if f.endswith('.jpg')]\n\n# Split 80/20\ntrain_imgs, val_imgs = train_test_split(all_image_files, test_size=0.2, random_state=42)\n\ndef process_dataset(image_ids, split_name):\n    for img_id in image_ids:\n        src_img = f'/kaggle/input/global-wheat-detection/train/{img_id}.jpg'\n        dst_img = f'{base_dir}/images/{split_name}/{img_id}.jpg'\n        \n        # USE SYMLINKS to save disk space on Kaggle\n        if not os.path.exists(dst_img):\n            os.symlink(src_img, dst_img)\n            \n        # Write labels if boxes exist (empty images just get an empty .txt file)\n        label_file = f'{base_dir}/labels/{split_name}/{img_id}.txt'\n        img_boxes = df[df['image_id'] == img_id]\n        \n        with open(label_file, 'w') as f:\n            for _, row in img_boxes.iterrows():\n                f.write(f\"{row['class_id']} {row['x_center']} {row['y_center']} {row['w_norm']} {row['h_norm']}\\n\")\n\nprint(\"Symlinking training data...\")\nprocess_dataset(train_imgs, 'train')\nprint(\"Symlinking validation data...\")\nprocess_dataset(val_imgs, 'val')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================\n# 3. Create Dataset YAML\n# ==========================================\nyaml_content = f\"\"\"\npath: {base_dir}\ntrain: images/train\nval: images/val\n\nnames:\n  0: wheat_head\n\"\"\"\nwith open('/kaggle/working/wheat.yaml', 'w') as f:\n    f.write(yaml_content)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. Install Ultralytics (requires Internet toggle ON)\n!pip install ultralytics\n\nimport torch\nfrom ultralytics import YOLO\n\n# Verify Kaggle sees both T4 GPUs (should output 2)\nprint(f\"GPUs available: {torch.cuda.device_count()}\")\n\n# Load a heavier pretrained model\nmodel = YOLO(\"yolov8x.pt\")\n\n# Train using Multi-GPU on Kaggle\nresults = model.train(\n    data=\"/kaggle/working/wheat.yaml\",\n    epochs=60,\n    imgsz=1024,\n    batch=16,           # Doubled batch size since we have 32GB total VRAM across two GPUs\n    device=[0, 1],      # Passing a list automatically triggers DataParallel across both T4s\n    workers=4,          # Optimal dataloader workers for Kaggle's CPU\n    \n    # KAGGLE MEMORY MANAGEMENT\n    save=True,          # Keep saving enabled for best/last weights\n    save_period=-1,     # CRITICAL: Do not save intermediate epochs to avoid hitting the 20GB disk limit\n    cache=False,        # If Kaggle's RAM crashes, ensure cache=False\n    \n    # AGGRESSIVE AUGMENTATIONS FOR SINGLE-FOLD\n    mosaic=1.0,         \n    mixup=0.1,          \n    degrees=90.0,       # Field crops have no \"up\" or \"down\"\n    hsv_h=0.015,        \n    hsv_s=0.7,          \n    hsv_v=0.4,          \n    \n    project=\"/kaggle/working/wheat_advanced\",\n    name=\"run_1\"\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nfrom ultralytics import YOLO\n\n# Load the best weights from the multi-GPU run\nbest_model = YOLO(\"/kaggle/working/wheat_advanced/run_1/weights/best.pt\")\n\nTEST_DIR = '/kaggle/input/global-wheat-detection/test'\nsubmission = []\n\ntest_images = [img for img in os.listdir(TEST_DIR) if img.endswith('.jpg')]\n\nfor img_name in test_images:\n    img_id = img_name.split('.')[0]\n    img_path = os.path.join(TEST_DIR, img_name)\n    \n    # Run inference on a single GPU (device=0) to avoid overhead\n    results = best_model.predict(\n        source=img_path,\n        imgsz=1280,         \n        conf=0.35,          \n        iou=0.60,           \n        augment=True,       # Enable TTA\n        device=0,           # Force inference onto the primary GPU\n        verbose=False       \n    )[0]\n    \n    prediction_string = []\n    \n    for box in results.boxes:\n        x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()\n        conf = box.conf[0].cpu().item()\n        \n        w = x2 - x1\n        h = y2 - y1\n        \n        prediction_string.append(f\"{conf:.4f} {x1:.1f} {y1:.1f} {w:.1f} {h:.1f}\")\n        \n    submission.append({\n        'image_id': img_id,\n        'PredictionString': \" \".join(prediction_string)\n    })\n\nsub_df = pd.DataFrame(submission)\nsub_df.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}