{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nimport pandas as pd\n\n# Tự tìm train_meta.csv\nmeta_path = list(Path(\"/kaggle/input\").rglob(\"train_meta.csv\"))[0]\n\n# Thư mục train cùng cấp với train_meta.csv\ntrain_dir = meta_path.parent / \"train\"\n\n# Đọc csv\ndf_meta = pd.read_csv(meta_path)\n\n# Đếm số image_id trong csv\nnum_csv_rows = len(df_meta)\nnum_csv_images = df_meta[\"image_id\"].nunique()\n\n# Đếm số ảnh trong folder train\nimage_files = list(train_dir.glob(\"*.png\"))\nnum_train_images = len(image_files)\n\nprint(\"Đường dẫn train_meta.csv:\", meta_path)\nprint(\"Đường dẫn thư mục train:\", train_dir)\nprint(\"-\" * 50)\n\nprint(\"Số dòng trong train_meta.csv:\", num_csv_rows)\nprint(\"Số image_id duy nhất trong train_meta.csv:\", num_csv_images)\nprint(\"Số ảnh .png trong thư mục train:\", num_train_images)\n\nprint(\"-\" * 50)\n\nif num_csv_images == num_train_images:\n    print(\"KHỚP: Số lượng ảnh trong folder train bằng số image_id trong CSV.\")\nelse:\n    print(\"KHÔNG KHỚP!\")\n    print(\"Chênh lệch:\", abs(num_csv_images - num_train_images))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-02T03:18:48.890285Z","iopub.execute_input":"2026-07-02T03:18:48.890623Z","iopub.status.idle":"2026-07-02T03:19:25.675758Z","shell.execute_reply.started":"2026-07-02T03:18:48.890591Z","shell.execute_reply":"2026-07-02T03:19:25.674881Z"}},"outputs":[{"name":"stdout","text":"Đường dẫn train_meta.csv: /kaggle/input/datasets/xhlulu/vinbigdata/train_meta.csv\nĐường dẫn thư mục train: /kaggle/input/datasets/xhlulu/vinbigdata/train\n--------------------------------------------------\nSố dòng trong train_meta.csv: 15000\nSố image_id duy nhất trong train_meta.csv: 15000\nSố ảnh .png trong thư mục train: 15000\n--------------------------------------------------\nKHỚP: Số lượng ảnh trong folder train bằng số image_id trong CSV.\n","output_type":"stream"}],"execution_count":8},{"cell_type":"code","source":"from pathlib import Path\nimport pandas as pd\n\n# Tự tìm train_path.csv\ntrain_path = list(Path(\"/kaggle/input\").rglob(\"train.csv\"))[0]\n\n# Thư mục train cùng cấp với train_meta.csv\ntrain_dir = train_path.parent / \"train\"\n\n# Đọc csv\ndf_train = pd.read_csv(train_path)\n\n# Đếm số image_id trong csv\nnum_csv_rows = len(df_meta)\nnum_csv_images = df_ytrain[\"image_id\"].nunique()\n\n# Đếm số ảnh trong folder train\nimage_files = list(train_dir.glob(\"*.png\"))\nnum_train_images = len(image_files)\n\nprint(\"Đường dẫn train.csv:\", train_path)\nprint(\"Đường dẫn thư mục train:\", train_dir)\nprint(\"-\" * 50)\n\nprint(\"Số dòng trong train_meta.csv:\", num_csv_rows)\nprint(\"Số image_id duy nhất trong train_meta.csv:\", num_csv_images)\nprint(\"Số ảnh .png trong thư mục train:\", num_train_images)\n\nprint(\"-\" * 50)\n\nif num_csv_images == num_train_images:\n    print(\"KHỚP: Số lượng ảnh trong folder train bằng số image_id trong CSV.\")\nelse:\n    print(\"KHÔNG KHỚP!\")\n    print(\"Chênh lệch:\", abs(num_csv_images - num_train_images))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-02T03:22:23.500423Z","iopub.execute_input":"2026-07-02T03:22:23.500759Z","iopub.status.idle":"2026-07-02T03:22:37.608603Z","shell.execute_reply.started":"2026-07-02T03:22:23.500727Z","shell.execute_reply":"2026-07-02T03:22:37.607541Z"}},"outputs":[{"traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)","\u001b[0;32m/tmp/ipykernel_58/3283951295.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     13\u001b[0m \u001b[0;31m# Đếm số image_id trong csv\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     14\u001b[0m \u001b[0mnum_csv_rows\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdf_meta\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0mnum_csv_images\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdf_ytrain\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m\"image_id\"\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnunique\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     16\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     17\u001b[0m \u001b[0;31m# Đếm số ảnh trong folder train\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;31mNameError\u001b[0m: name 'df_ytrain' is not defined"],"ename":"NameError","evalue":"name 'df_ytrain' is not defined","output_type":"error"}],"execution_count":9},{"cell_type":"code","source":"from pathlib import Path\nimport pandas as pd\nimport os\nimport shutil\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\n\n# =========================\n# 1. Khai báo đường dẫn\n# =========================\n\nPNG_BASE = Path(\"/kaggle/input/datasets/xhlulu/vinbigdata\")\nIMG_DIR = PNG_BASE / \"train\"\nMETA_PATH = PNG_BASE / \"train_meta.csv\"\n\n# Tìm file train.csv từ dataset gốc VinBigData\ntrain_csv_files = list(Path(\"/kaggle/input\").glob(\"**/train.csv\"))\n\nprint(\"Các file train.csv tìm thấy:\")\nfor p in train_csv_files:\n    print(p)\n\nANN_PATH = train_csv_files[0]\n\nprint(\"\\nDùng ANN_PATH:\", ANN_PATH)\nprint(\"Dùng META_PATH:\", META_PATH)\nprint(\"Dùng IMG_DIR:\", IMG_DIR)\n\n# =========================\n# 2. Đọc dữ liệu\n# =========================\n\ndf = pd.read_csv(ANN_PATH)\nmeta = pd.read_csv(META_PATH)\n\ndf_meta = df.merge(meta, on=\"image_id\", how=\"left\")\n\n# Bỏ class 14 = No finding, chỉ giữ bbox bệnh\nbbox_df = df_meta[\n    (df_meta[\"class_id\"] != 14) &\n    df_meta[[\"x_min\", \"y_min\", \"x_max\", \"y_max\", \"dim0\", \"dim1\"]].notna().all(axis=1)\n].copy()\n\nbbox_df[\"class_id\"] = bbox_df[\"class_id\"].astype(int)\n\n# =========================\n# 3. Chuyển bbox sang YOLO format\n# =========================\n\nbbox_df[\"x_center\"] = ((bbox_df[\"x_min\"] + bbox_df[\"x_max\"]) / 2) / bbox_df[\"dim1\"]\nbbox_df[\"y_center\"] = ((bbox_df[\"y_min\"] + bbox_df[\"y_max\"]) / 2) / bbox_df[\"dim0\"]\n\nbbox_df[\"box_width\"] = (bbox_df[\"x_max\"] - bbox_df[\"x_min\"]) / bbox_df[\"dim1\"]\nbbox_df[\"box_height\"] = (bbox_df[\"y_max\"] - bbox_df[\"y_min\"]) / bbox_df[\"dim0\"]\n\nfor col in [\"x_center\", \"y_center\", \"box_width\", \"box_height\"]:\n    bbox_df[col] = bbox_df[col].clip(0, 1)\n\ninvalid_boxes = bbox_df[\n    (bbox_df[\"box_width\"] <= 0) |\n    (bbox_df[\"box_height\"] <= 0)\n]\n\nprint(\"\\nSố dòng bbox:\", len(bbox_df))\nprint(\"Số ảnh có bbox:\", bbox_df[\"image_id\"].nunique())\nprint(\"Số bbox lỗi:\", len(invalid_boxes))\n\n# =========================\n# 4. Tạo thư mục YOLO\n# =========================\n\nYOLO_DIR = Path(\"/kaggle/working/vinbigdata_yolo\")\n\nif YOLO_DIR.exists():\n    shutil.rmtree(YOLO_DIR)\n\nfor split in [\"train\", \"val\"]:\n    (YOLO_DIR / \"images\" / split).mkdir(parents=True, exist_ok=True)\n    (YOLO_DIR / \"labels\" / split).mkdir(parents=True, exist_ok=True)\n\n# =========================\n# 5. Chia train / val\n# =========================\n\nimage_ids = bbox_df[\"image_id\"].unique()\n\ntrain_ids, val_ids = train_test_split(\n    image_ids,\n    test_size=0.2,\n    random_state=42\n)\n\nprint(\"\\nSố ảnh train:\", len(train_ids))\nprint(\"Số ảnh val:\", len(val_ids))\n\n# =========================\n# 6. Tạo ảnh và label YOLO\n# =========================\n\ndef link_or_copy_image(src_path, dst_path):\n    if dst_path.exists():\n        return\n    \n    try:\n        os.symlink(src_path, dst_path)\n    except:\n        shutil.copy2(src_path, dst_path)\n\n\ndef create_yolo_files(image_id, split):\n    src_img = IMG_DIR / f\"{image_id}.png\"\n    \n    if not src_img.exists():\n        return False\n    \n    dst_img = YOLO_DIR / \"images\" / split / f\"{image_id}.png\"\n    label_path = YOLO_DIR / \"labels\" / split / f\"{image_id}.txt\"\n    \n    rows = bbox_df[bbox_df[\"image_id\"] == image_id]\n    \n    lines = []\n    \n    for _, row in rows.iterrows():\n        class_id = int(row[\"class_id\"])\n        x_center = row[\"x_center\"]\n        y_center = row[\"y_center\"]\n        box_width = row[\"box_width\"]\n        box_height = row[\"box_height\"]\n        \n        lines.append(\n            f\"{class_id} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}\"\n        )\n    \n    with open(label_path, \"w\") as f:\n        f.write(\"\\n\".join(lines))\n    \n    link_or_copy_image(src_img, dst_img)\n    \n    return True\n\n\ntrain_success = 0\nfor image_id in tqdm(train_ids, desc=\"Creating train YOLO files\"):\n    if create_yolo_files(image_id, \"train\"):\n        train_success += 1\n\nval_success = 0\nfor image_id in tqdm(val_ids, desc=\"Creating val YOLO files\"):\n    if create_yolo_files(image_id, \"val\"):\n        val_success += 1\n\n# =========================\n# 7. Tạo dataset.yaml\n# =========================\n\nnames = [\n    \"Aortic enlargement\",\n    \"Atelectasis\",\n    \"Calcification\",\n    \"Cardiomegaly\",\n    \"Consolidation\",\n    \"ILD\",\n    \"Infiltration\",\n    \"Lung Opacity\",\n    \"Nodule/Mass\",\n    \"Other lesion\",\n    \"Pleural effusion\",\n    \"Pleural thickening\",\n    \"Pneumothorax\",\n    \"Pulmonary fibrosis\"\n]\n\nyaml_text = f\"\"\"\npath: {YOLO_DIR}\ntrain: images/train\nval: images/val\n\nnc: 14\nnames:\n\"\"\"\n\nfor i, name in enumerate(names):\n    yaml_text += f\"  {i}: {name}\\n\"\n\nyaml_path = YOLO_DIR / \"dataset.yaml\"\n\nwith open(yaml_path, \"w\") as f:\n    f.write(yaml_text)\n\n# =========================\n# 8. Kiểm tra kết quả\n# =========================\n\nprint(\"\\nTạo YOLO dataset xong.\")\nprint(\"YOLO_DIR:\", YOLO_DIR)\nprint(\"yaml_path:\", yaml_path)\n\nprint(\"\\nSố ảnh train:\", len(list((YOLO_DIR / \"images\" / \"train\").glob(\"*.png\"))))\nprint(\"Số label train:\", len(list((YOLO_DIR / \"labels\" / \"train\").glob(\"*.txt\"))))\n\nprint(\"Số ảnh val:\", len(list((YOLO_DIR / \"images\" / \"val\").glob(\"*.png\"))))\nprint(\"Số label val:\", len(list((YOLO_DIR / \"labels\" / \"val\").glob(\"*.txt\"))))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-02T02:04:22.414759Z","iopub.execute_input":"2026-07-02T02:04:22.415177Z","iopub.status.idle":"2026-07-02T02:05:39.002102Z","shell.execute_reply.started":"2026-07-02T02:04:22.415139Z","shell.execute_reply":"2026-07-02T02:05:39.001179Z"}},"outputs":[{"name":"stdout","text":"Các file train.csv tìm thấy:\n/kaggle/input/competitions/vinbigdata-chest-xray-abnormalities-detection/train.csv\n\nDùng ANN_PATH: /kaggle/input/competitions/vinbigdata-chest-xray-abnormalities-detection/train.csv\nDùng META_PATH: /kaggle/input/datasets/xhlulu/vinbigdata/train_meta.csv\nDùng IMG_DIR: /kaggle/input/datasets/xhlulu/vinbigdata/train\n\nSố dòng bbox: 36096\nSố ảnh có bbox: 4394\nSố bbox lỗi: 0\n\nSố ảnh train: 3515\nSố ảnh val: 879\n","output_type":"stream"},{"name":"stderr","text":"Creating train YOLO files: 100%|██████████| 3515/3515 [00:20<00:00, 172.45it/s]\nCreating val YOLO files: 100%|██████████| 879/879 [00:05<00:00, 171.39it/s]\n","output_type":"stream"},{"name":"stdout","text":"\nTạo YOLO dataset xong.\nYOLO_DIR: /kaggle/working/vinbigdata_yolo\nyaml_path: /kaggle/working/vinbigdata_yolo/dataset.yaml\n\nSố ảnh train: 3515\nSố label train: 3515\nSố ảnh val: 879\nSố label val: 879\n","output_type":"stream"}],"execution_count":2},{"cell_type":"code","source":"!pip install ultralytics -q","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-02T02:07:09.246734Z","iopub.execute_input":"2026-07-02T02:07:09.247095Z","iopub.status.idle":"2026-07-02T02:07:16.938046Z","shell.execute_reply.started":"2026-07-02T02:07:09.247063Z","shell.execute_reply":"2026-07-02T02:07:16.936756Z"}},"outputs":[{"name":"stdout","text":"\u001b[2K     \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.8/41.8 kB\u001b[0m \u001b[31m1.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m20.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0ma \u001b[36m0:00:01\u001b[0m\n\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m53.2/53.2 kB\u001b[0m \u001b[31m2.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n\u001b[?25h","output_type":"stream"}],"execution_count":3},{"cell_type":"code","source":"import os\nfrom ultralytics import YOLO\n\n# Tắt wandb để tránh hỏi login\nos.environ[\"WANDB_DISABLED\"] = \"true\"\n\nYOLO_DIR = \"/kaggle/working/vinbigdata_yolo\"\nyaml_path = \"/kaggle/working/vinbigdata_yolo/dataset.yaml\"\n\n# YOLOv8n = bản nhẹ nhất, train thử cho chắc pipeline\nmodel = YOLO(\"yolov8n.pt\")\n\nresults = model.train(\n    data=yaml_path,\n    epochs=5,\n    imgsz=512,\n    batch=8,\n    device=0,          # dùng GPU T4 số 0 thôi cho ổn định\n    workers=2,\n    project=\"/kaggle/working/runs\",\n    name=\"vinbigdata_yolov8n_test\",\n    exist_ok=True\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from pathlib import Path\nfrom IPython.display import Image, display\n\nRUN_DIR = Path(\"/kaggle/working/runs/vinbigdata_yolov8n_test\")\n\nprint(\"Các file trong thư mục kết quả:\")\nfor p in RUN_DIR.glob(\"*\"):\n    print(p)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results_img = RUN_DIR / \"results.png\"\n\nprint(\"results.png tồn tại không?\", results_img.exists())\n\nif results_img.exists():\n    display(Image(filename=str(results_img)))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_images = list(RUN_DIR.glob(\"val_batch*_pred.jpg\"))\n\nprint(\"Số ảnh dự đoán validation:\", len(pred_images))\n\nfor p in pred_images[:3]:\n    print(p)\n    display(Image(filename=str(p)))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from ultralytics import YOLO\nfrom pathlib import Path\nfrom IPython.display import Image, display\nimport random\n\n# Đường dẫn model tốt nhất sau train\nbest_model_path = \"/kaggle/working/runs/vinbigdata_yolov8n_test/weights/best.pt\"\n\n# Load model\nmodel = YOLO(best_model_path)\n\n# Lấy ngẫu nhiên 1 ảnh trong tập validation\nval_img_dir = Path(\"/kaggle/working/vinbigdata_yolo/images/val\")\nsample_img = random.choice(list(val_img_dir.glob(\"*.png\")))\n\nprint(\"Ảnh test:\", sample_img)\n\n# Predict\nresults = model.predict(\n    source=str(sample_img),\n    imgsz=512,\n    conf=0.25,\n    save=True,\n    project=\"/kaggle/working/predict_results\",\n    name=\"single_image_test\",\n    exist_ok=True\n)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_dir = Path(\"/kaggle/working/predict_results/single_image_test\")\n\npred_images = list(pred_dir.glob(\"*.png\")) + list(pred_dir.glob(\"*.jpg\"))\n\nprint(\"Số ảnh predict:\", len(pred_images))\n\nfor p in pred_images:\n    print(p)\n    display(Image(filename=str(p)))","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}