{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":14126896,"sourceType":"datasetVersion","datasetId":9000922}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport sys\nimport shutil\nimport importlib.util\n\n# Kaggle working directory\nHOME = '/kaggle/working'\nos.chdir(HOME)\n\nprint(\"🚀 BẮT ĐẦU KHỞI TẠO MÔI TRƯỜNG TRÊN KAGGLE...\")\n\n# --- BƯỚC 1: CHUẨN BỊ THƯ MỤC CHECKPOINT ---\n# Trên Kaggle ta lưu tạm vào thư mục working.\n# Nếu muốn bền vững, hãy add model qua Kaggle Dataset.\nMODEL_DIR = os.path.join(HOME, 'checkpoints')\nos.makedirs(MODEL_DIR, exist_ok=True)\ncheckpoint_file = os.path.join(MODEL_DIR, 'oriented_rcnn_r50_fpn_1x_dota_le90-6d2b2ce0.pth')\n\n# --- BƯỚC 2: CÀI ĐẶT THƯ VIỆN (QUAN TRỌNG) ---\nprint(\"\\n⬇️ [2/5] Cài đặt thư viện nền (Binary)...\")\n\n# Gỡ các bản cũ có sẵn của Kaggle để tránh xung đột\n!pip uninstall -y mmcv mmcv-full mmdet mmrotate > /dev/null 2>&1\n\n# Cài đặt đúng phiên bản bạn đã fix thành công\n!pip install torch==2.3.0 torchvision==0.18.0 --index-url https://download.pytorch.org/whl/cu121 > /dev/null\n!pip install mmcv==2.2.0 -f https://download.openmmlab.com/mmcv/dist/cu121/torch2.3/index.html > /dev/null\n!pip install mmengine \"mmdet>=3.0.0\" > /dev/null\n\n# --- BƯỚC 3: TẢI MÃ NGUỒN TỪ GITHUB ---\nprint(\"\\n⬇️ [3/5] Tải mã nguồn MMRotate mới nhất...\")\nMMROTATE_DIR = os.path.join(HOME, 'mmrotate')\n\nif os.path.exists(MMROTATE_DIR):\n    shutil.rmtree(MMROTATE_DIR)\n\n!git clone -b dev-1.x https://github.com/open-mmlab/mmrotate.git {MMROTATE_DIR}\n\n# --- BƯỚC 4: VÁ LỖI TỰ ĐỘNG (GIỮ NGUYÊN LOGIC CỦA BẠN) ---\nprint(\"\\n🔧 [4/5] Vá lỗi hệ thống...\")\n\n# 4.1. Tạo version.py\nversion_file = os.path.join(MMROTATE_DIR, 'mmrotate/version.py')\nwith open(version_file, 'w') as f:\n    f.write(\"__version__ = '1.0.0rc1'\\nshort_version = '1.0.0'\\nversion_info = (1, 0, 0, 'rc1')\")\n\n# 4.2. Sửa MMDet (Tìm động đường dẫn thư viện vì Kaggle khác Colab)\ndet_spec = importlib.util.find_spec(\"mmdet\")\n# Lưu ý: Trên Kaggle, gói cài pip thường nằm trong /opt/conda/... hoặc /usr/local/...\n# Code này sẽ tự tìm đúng vị trí.\nif det_spec and det_spec.origin:\n    det_init = det_spec.origin\n    print(f\"   -> Đã tìm thấy MMDet tại: {det_init}\")\n    with open(det_init, 'w') as f:\n        f.write(\"# Copyright (c) OpenMMLab. All rights reserved.\\nimport mmcv\\nimport mmengine\\nfrom .version import __version__, version_info\\n__all__ = ['__version__', 'version_info']\")\nelse:\n    print(\"   ⚠️ CẢNH BÁO: Không tìm thấy mmdet để vá lỗi!\")\n\n# 4.3. Sửa MMRotate __init__.py\nrot_init = os.path.join(MMROTATE_DIR, 'mmrotate/__init__.py')\nsafe_rot = \"\"\"\nimport mmcv\nimport mmdet\nfrom .version import __version__, version_info\nfrom mmengine.utils import digit_version\nmmcv_version = digit_version(mmcv.__version__)\n__all__ = ['__version__', 'version_info', 'digit_version', 'mmcv_version']\n\"\"\"\nwith open(rot_init, 'w') as f:\n    f.write(safe_rot)\n\n# Cài đặt code vừa tải (Editable mode)\n%cd {MMROTATE_DIR}\n!pip install -v -e . > /dev/null\n\n# --- BƯỚC 5: TẢI MODEL ---\nprint(f\"\\n💾 [5/5] Kiểm tra Model: {checkpoint_file}\")\n# Quay về thư mục làm việc chính\nos.chdir(HOME)\n\nif os.path.exists(checkpoint_file) and os.path.getsize(checkpoint_file) > 100000000:\n    print(\"   ✅ Đã có Model! Sẵn sàng sử dụng.\")\nelse:\n    print(\"   ⬇️ Đang tải Model từ HuggingFace (thay thế Drive)...\")\n    url = \"https://huggingface.co/dl4eo/Oriented_R-CNN_pretrained_on_DOTA_1.0/resolve/main/oriented_rcnn_r50_fpn_1x_dota_le90-6d2b2ce0.pth?download=true\"\n    # Dùng curl trên Kaggle rất nhanh\n    !curl -L \"{url}\" -o \"{checkpoint_file}\"\n    print(\"   ✅ Đã tải xong!\")\n\n# --- HOÀN TẤT ---\nprint(\"\\n\" + \"=\"*50)\nprint(\"🎉 MÔI TRƯỜNG KAGGLE ĐÃ SẴN SÀNG!\")\nprint(f\"✅ Code: {MMROTATE_DIR}\")\nprint(f\"✅ Model: {checkpoint_file}\")\nprint(\"=\"*50)\n\n# Kiểm tra nhanh import để chắc chắn không lỗi\ntry:\n    import mmrotate\n    print(\"Test Import mmrotate: THÀNH CÔNG ✅\")\nexcept ImportError as e:\n    print(f\"Test Import mmrotate: THẤT BẠI ❌ - Lỗi: {e}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T06:25:19.064350Z","iopub.execute_input":"2025-12-13T06:25:19.065040Z","iopub.status.idle":"2025-12-13T06:28:29.057473Z","shell.execute_reply.started":"2025-12-13T06:25:19.065014Z","shell.execute_reply":"2025-12-13T06:28:29.056687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import mmcv\nimport mmdet\nprint(mmcv.__version__)\nprint(mmdet.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T06:28:53.544511Z","iopub.execute_input":"2025-12-13T06:28:53.544816Z","iopub.status.idle":"2025-12-13T06:28:55.682910Z","shell.execute_reply.started":"2025-12-13T06:28:53.544785Z","shell.execute_reply":"2025-12-13T06:28:55.682284Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Định vị \"Tọa độ\" Dữ liệu (Data Mapping)**","metadata":{}},{"cell_type":"code","source":"import os\n\n# ================= CẤU HÌNH ĐƯỜNG DẪN =================\n# Đường dẫn gốc tới dataset trong Input\n# Lưu ý: Dựa trên ảnh của bạn, dataset tên là 'processed-data'\ndataset_root = '/kaggle/input/processed-data/processed_mmrotate_data'\n\n# Hàm đếm file trong thư mục con\ndef count_files(split_name):\n    # Đường dẫn tới folder ảnh và nhãn\n    img_dir = os.path.join(dataset_root, split_name, 'images')\n    lbl_dir = os.path.join(dataset_root, split_name, 'labelTxt')\n    \n    n_imgs = 0\n    n_lbls = 0\n    \n    # Đếm ảnh\n    if os.path.exists(img_dir):\n        n_imgs = len([f for f in os.listdir(img_dir) \n                      if f.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.tif'))])\n    else:\n        print(f\"⚠️ Cảnh báo: Không tìm thấy thư mục {img_dir}\")\n\n    # Đếm nhãn\n    if os.path.exists(lbl_dir):\n        n_lbls = len([f for f in os.listdir(lbl_dir) \n                      if f.lower().endswith(('.txt', '.xml'))])\n    else:\n        print(f\"⚠️ Cảnh báo: Không tìm thấy thư mục {lbl_dir}\")\n        \n    return n_imgs, n_lbls\n\n# ================= THỰC HIỆN ĐẾM =================\nprint(f\"📂 Đang quét dữ liệu tại: {dataset_root}\\n\")\n\nif not os.path.exists(dataset_root):\n    print(f\"❌ LỖI: Không tìm thấy đường dẫn {dataset_root}\")\n    print(\"Các dataset đang có trong Input:\", os.listdir('/kaggle/input'))\nelse:\n    # Lấy số liệu từng tập\n    n_train_img, n_train_lbl = count_files('train')\n    n_val_img, n_val_lbl     = count_files('val')\n    n_test_img, n_test_lbl   = count_files('test')\n\n    total_imgs = n_train_img + n_val_img + n_test_img\n    total_lbls = n_train_lbl + n_val_lbl + n_test_lbl\n\n    # ================= IN BÁO CÁO (YÊU CẦU I.1) =================\n    print(\"=\"*50)\n    print(\"📊 SỐ LIỆU BÁO CÁO (CHÍNH XÁC)\")\n    print(\"=\"*50)\n\n    if total_imgs == 0:\n        print(\"❌ Vẫn chưa đếm được ảnh nào. Hãy kiểm tra kỹ tên thư mục!\")\n    else:\n        print(f\"1. TỔNG QUAN DATASET:\")\n        print(f\"   - Tổng số ảnh: {total_imgs}\")\n        print(f\"   - Tổng số nhãn: {total_lbls}\")\n        print(\"-\" * 50)\n\n        print(f\"2. CHI TIẾT PHÂN CHIA (SPLIT):\")\n        \n        # Train\n        pct_train = (n_train_img / total_imgs) * 100\n        print(f\"   ✅ TRAIN SET (Đã Augment):\")\n        print(f\"      - Số lượng: {n_train_img} ảnh\")\n        print(f\"      - Tỉ lệ thực tế: {pct_train:.2f}%\")\n        \n        # Val\n        pct_val = (n_val_img / total_imgs) * 100\n        print(f\"   ✅ VAL SET:\")\n        print(f\"      - Số lượng: {n_val_img} ảnh\")\n        print(f\"      - Tỉ lệ thực tế: {pct_val:.2f}%\")\n\n        # Test\n        pct_test = (n_test_img / total_imgs) * 100\n        print(f\"   ✅ TEST SET:\")\n        print(f\"      - Số lượng: {n_test_img} ảnh\")\n        print(f\"      - Tỉ lệ thực tế: {pct_test:.2f}%\")\n        \n        print(\"=\"*50)\n        \n        # Logic check nhanh\n        if n_train_img > n_val_img * 4:\n            print(\"💡 Nhận xét: Tập Train lớn gấp >4 lần tập Val/Test => Đã có Augmentation.\")\n        else:\n            print(\"💡 Nhận xét: Tỉ lệ các tập khá đồng đều.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T05:37:50.457989Z","iopub.execute_input":"2025-12-13T05:37:50.458844Z","iopub.status.idle":"2025-12-13T05:37:50.783295Z","shell.execute_reply.started":"2025-12-13T05:37:50.458807Z","shell.execute_reply":"2025-12-13T05:37:50.782555Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Hiển thị mẫu","metadata":{}},{"cell_type":"code","source":"import os\nimport random\nimport cv2\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# ================= CẤU HÌNH ĐƯỜNG DẪN =================\ndataset_root = '/kaggle/input/processed-data/processed_mmrotate_data'\nimg_folder = os.path.join(dataset_root, 'train', 'images')\nlbl_folder = os.path.join(dataset_root, 'train', 'labelTxt')\n\n# ================= HÀM VẼ THỦ CÔNG (CHỐNG LỖI) =================\ndef draw_rotated_box_robust(img, label_path):\n    with open(label_path, 'r') as f:\n        lines = f.readlines()\n    \n    img_copy = img.copy()\n    has_box = False\n    \n    for line in lines:\n        parts = line.strip().split()\n        \n        # Bỏ qua dòng header hoặc dòng lỗi\n        if len(parts) < 8: \n            continue\n            \n        try:\n            # 1. Lấy 8 toạ độ (x1, y1, x2, y2, x3, y3, x4, y4)\n            # Dùng float trước để hứng số thập phân\n            coords = list(map(float, parts[:8]))\n            label_name = parts[8]\n\n            # 2. Gom thành 4 cặp điểm (x, y)\n            # p1: (x1, y1), p2: (x2, y2), ...\n            points = []\n            for i in range(0, 8, 2):\n                x = int(round(coords[i]))     # Làm tròn và ép kiểu int ngay lập tức\n                y = int(round(coords[i+1]))\n                points.append((x, y))\n\n            # 3. Vẽ từng đoạn thẳng nối nhau (Thay thế cho polylines để tránh lỗi)\n            # Nối p0->p1, p1->p2, p2->p3, p3->p0\n            if len(points) == 4:\n                cv2.line(img_copy, points[0], points[1], (0, 255, 0), 2)\n                cv2.line(img_copy, points[1], points[2], (0, 255, 0), 2)\n                cv2.line(img_copy, points[2], points[3], (0, 255, 0), 2)\n                cv2.line(img_copy, points[3], points[0], (0, 255, 0), 2)\n                \n                # Viết tên class\n                cv2.putText(img_copy, label_name, points[0], \n                            cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)\n                has_box = True\n\n        except Exception as e:\n            # In lỗi chi tiết để debug nếu cần, nhưng không dừng chương trình\n            print(f\"⚠️ Lỗi dòng dữ liệu: {line.strip()} \\n   -> {e}\")\n            pass\n            \n    return img_copy, has_box\n\n# ================= CHẠY KIỂM TRA =================\nif not os.path.exists(img_folder):\n    print(f\"❌ Không tìm thấy đường dẫn: {img_folder}\")\nelse:\n    all_imgs = [f for f in os.listdir(img_folder) if f.endswith(('.png', '.jpg', '.jpeg'))]\n\n    if len(all_imgs) > 0:\n        # Chọn ngẫu nhiên 3 ảnh\n        sample_imgs = random.sample(all_imgs, 3)\n\n        plt.figure(figsize=(20, 10))\n        \n        count = 0\n        for i, img_name in enumerate(sample_imgs):\n            img_path = os.path.join(img_folder, img_name)\n            basename = os.path.splitext(img_name)[0]\n            lbl_path = os.path.join(lbl_folder, basename + '.txt')\n            \n            if os.path.exists(lbl_path):\n                img = cv2.imread(img_path)\n                if img is None: continue\n                img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n                \n                # Gọi hàm vẽ MỚI\n                vis_img, status = draw_rotated_box_robust(img, lbl_path)\n                \n                plt.subplot(1, 3, count+1)\n                plt.imshow(vis_img)\n                plt.title(f\"{img_name}\\nBox: {status}\")\n                plt.axis('off')\n                count += 1\n            else:\n                print(f\"Skipping {img_name} (No label)\")\n\n        plt.tight_layout()\n        plt.show()\n        print(\"\\n✅ ĐÃ HIỂN THỊ (Sử dụng phương pháp vẽ line thủ công)\")\n    else:\n        print(\"❌ Folder ảnh rỗng.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T05:37:59.230527Z","iopub.execute_input":"2025-12-13T05:37:59.230809Z","iopub.status.idle":"2025-12-13T05:38:00.701900Z","shell.execute_reply.started":"2025-12-13T05:37:59.230787Z","shell.execute_reply":"2025-12-13T05:38:00.701086Z"},"collapsed":true,"jupyter":{"outputs_hidden":true}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Thiết lập \"Bộ não\" R3Det (Configuring R3Det)","metadata":{}},{"cell_type":"code","source":"# Cài đặt phiên bản Numpy 1.26.x (bản ổn định nhất của dòng 1.x)\n!pip install \"numpy<2.0.0\" --force-reinstall\nimport numpy\nprint(numpy.__version__)\n# Kết quả mong đợi: 1.26.4 hoặc 1.24.x (Không được là 2.x.x)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T06:29:08.362402Z","iopub.execute_input":"2025-12-13T06:29:08.362801Z","iopub.status.idle":"2025-12-13T06:29:14.417913Z","shell.execute_reply.started":"2025-12-13T06:29:08.362779Z","shell.execute_reply":"2025-12-13T06:29:14.416985Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from mmengine.config import Config\nimport glob\nimport os\n\n# 1. Tìm file config gốc\nsearch_path = '/kaggle/working/mmrotate/configs/r3det/r3det*r50*fpn*dota*.py'\nfound_files = glob.glob(search_path)\nbase_config_path = found_files[0]\nprint(f\"✅ Base Config: {base_config_path}\")\n\ncfg = Config.fromfile(base_config_path)\n\n# ================= 3. CẤU HÌNH V18 (ULTIMATE CONFIG) =================\n\n# --- A. Dữ liệu & Pipeline (GIỮ NGUYÊN 1024x1024) ---\ndata_root = '/kaggle/input/processed-data/processed_mmrotate_data/'\ncfg.data_root = data_root\ncfg.metainfo = dict(classes=('ship',), palette=[(220, 20, 60)])\n\n# Pipeline Train: 1024x1024\ntrain_pipeline = [\n    dict(type='mmdet.LoadImageFromFile'),\n    dict(type='mmdet.LoadAnnotations', with_bbox=True, box_type='qbox'), # Quan trọng cho R3Det\n    dict(type='ConvertBoxType', box_type_mapping=dict(gt_bboxes='rbox')),\n    dict(type='mmdet.Resize', scale=(1024, 1024), keep_ratio=True), # <--- GIỮ NGUYÊN\n    dict(type='mmdet.RandomFlip', prob=0.5, direction=['horizontal', 'vertical']),\n    dict(type='mmdet.PackDetInputs')\n]\n\ntest_pipeline = [\n    dict(type='mmdet.LoadImageFromFile'),\n    dict(type='mmdet.Resize', scale=(1024, 1024), keep_ratio=True),\n    dict(type='mmdet.LoadAnnotations', with_bbox=True, box_type='qbox'),\n    dict(type='ConvertBoxType', box_type_mapping=dict(gt_bboxes='rbox')),\n    dict(type='mmdet.PackDetInputs', meta_keys=('img_id', 'img_path', 'ori_shape', 'img_shape', 'scale_factor'))\n]\n\n# Áp dụng Pipeline\ncfg.train_dataloader.dataset.data_root = data_root\ncfg.train_dataloader.dataset.metainfo = cfg.metainfo\ncfg.train_dataloader.dataset.ann_file = 'train/labelTxt/'\ncfg.train_dataloader.dataset.data_prefix = dict(img_path='train/images/')\ncfg.train_dataloader.dataset.pipeline = train_pipeline\n\n# --- CẤU HÌNH CPU & BATCH SIZE ---\n# Batch vật lý = 2 (Để GPU không chết)\ncfg.train_dataloader.batch_size = 2 \n\n# Ép CPU chạy tối đa công suất (Max 4 workers trên Kaggle)\ncfg.train_dataloader.num_workers = 4 \ncfg.train_dataloader.persistent_workers = True # CPU không được ngủ giữa các Epoch\n\ncfg.val_dataloader.dataset.data_root = data_root\ncfg.val_dataloader.dataset.metainfo = cfg.metainfo\ncfg.val_dataloader.dataset.ann_file = 'val/labelTxt/'\ncfg.val_dataloader.dataset.data_prefix = dict(img_path='val/images/')\ncfg.val_dataloader.dataset.pipeline = test_pipeline\ncfg.test_dataloader = cfg.val_dataloader\n\n# --- B. Model ---\ncfg.model.bbox_head_init['num_classes'] = 1\ncfg.model.bbox_head_refine[0]['num_classes'] = 1\ncfg.load_from = 'https://download.pytorch.org/models/resnet50-19c8e357.pth'\n\n# --- C. Optimizer (GIẢ LẬP BATCH 8) ---\n# Batch Vật lý (2) * Accumulate (4) = Batch Hiệu dụng (8)\n# Đây là cách duy nhất để chạy Batch 8 với ảnh 1024 trên T4\ncfg.optim_wrapper = dict(\n    type='OptimWrapper',\n    optimizer=dict(type='SGD', lr=0.005, momentum=0.9, weight_decay=0.0001), # Tăng LR lên phù hợp với Batch 8\n    clip_grad=dict(max_norm=35, norm_type=2),\n    accumulative_counts=4  # <--- BÍ KÍP: Tích lũy 4 lần mới cập nhật\n)\n\n# --- D. Cleanup Hooks ---\ncfg.train_cfg.max_epochs = 12\ncfg.train_cfg.val_interval = 1\ncfg.default_hooks.checkpoint.interval = 4\ncfg.work_dir = './work_dirs/r3det_ship_final_v18'\n\ncfg.default_hooks.pop('visualization', None)\ncfg.pop('visualizer', None)\n\n# Save\nfinal_config_path = 'my_r3det_final_v18.py'\ncfg.dump(final_config_path)\n\nprint(f\"\\n📝 Config V18 (Max CPU + Batch 8 Sim): {final_config_path}\")\nprint(\"✅ Ảnh: 1024x1024.\")\nprint(\"✅ CPU Workers: 4 (Max công suất).\")\nprint(\"✅ Batch Size: 8 (Thông qua tích lũy gradient).\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T06:29:17.544133Z","iopub.execute_input":"2025-12-13T06:29:17.545036Z","iopub.status.idle":"2025-12-13T06:29:17.853998Z","shell.execute_reply.started":"2025-12-13T06:29:17.545000Z","shell.execute_reply":"2025-12-13T06:29:17.853194Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install protobuf==3.20.3 numpy==1.24.4 --force-reinstall --quiet\n\nprint(\"\\n🚀 BẮT ĐẦU TRAINING (V18 - CẤU HÌNH TỐI ĐA)...\")\nprint(\"==================================================\")\n!python /kaggle/working/mmrotate/tools/train.py my_r3det_final_v18.py","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T06:29:25.802898Z","iopub.execute_input":"2025-12-13T06:29:25.803260Z","iopub.status.idle":"2025-12-13T16:24:29.514980Z","shell.execute_reply.started":"2025-12-13T06:29:25.803220Z","shell.execute_reply":"2025-12-13T16:24:29.514269Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nprint(\"🔍 Đang quét toàn bộ thư mục output...\")\ntarget_dir = './work_dirs'\nfound_json = None\n\nfor root, dirs, files in os.walk(target_dir):\n    for file in files:\n        if file.endswith('.json'):\n            full_path = os.path.join(root, file)\n            print(f\"✅ BINGO! Tìm thấy file: {full_path}\")\n            # Ưu tiên lấy file có chứa dữ liệu scalars (số liệu vẽ biểu đồ)\n            if 'scalars' in file or 'json' in file: \n                found_json = full_path\n\nif found_json:\n    print(f\"\\n👉 File log chuẩn nhất là: {found_json}\")\n    print(\"Tôi sẽ dùng file này để vẽ biểu đồ ngay dưới đây.\")\nelse:\n    print(\"\\n❌ Vẫn không tìm thấy. Có thể bạn đã Restart Session làm mất file trong /kaggle/working?\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T16:58:58.779680Z","iopub.execute_input":"2025-12-13T16:58:58.780037Z","iopub.status.idle":"2025-12-13T16:58:58.787743Z","shell.execute_reply.started":"2025-12-13T16:58:58.780013Z","shell.execute_reply":"2025-12-13T16:58:58.786995Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import json\nimport matplotlib.pyplot as plt\nimport os\n\n# Đường dẫn chính xác tới file log V18 (đã tìm thấy ở trên)\nlog_path = './work_dirs/r3det_ship_final_v18/20251213_062958/vis_data/scalars.json'\n\nprint(f\"📊 Đang xử lý dữ liệu từ: {log_path}\")\n\ntrain_steps = []\ntrain_losses = []\nval_epochs = []\nval_mAPs = []\n\nif os.path.exists(log_path):\n    with open(log_path, 'r') as f:\n        for line in f:\n            try:\n                log = json.loads(line)\n                \n                # 1. Lấy dữ liệu Training Loss\n                if 'loss' in log:\n                    train_steps.append(log['step'])\n                    train_losses.append(log['loss'])\n                \n                # 2. Lấy dữ liệu Validation mAP\n                # Trong mmrotate, key thường là 'dota/mAP'\n                if 'dota/mAP' in log:\n                    # Chúng ta dùng step để biết nó thuộc epoch nào, nhưng để vẽ đẹp ta sẽ dùng index\n                    val_mAPs.append(log['dota/mAP'] * 100) # Đổi sang đơn vị %\n                    \n            except Exception as e:\n                pass\n\n    # Tạo trục Epochs cho biểu đồ mAP (1 đến 12)\n    val_epochs = list(range(1, len(val_mAPs) + 1))\n\n    # --- VẼ BIỂU ĐỒ ---\n    if len(train_losses) > 0 and len(val_mAPs) > 0:\n        plt.figure(figsize=(18, 7))\n\n        # 1. Biểu đồ Training Loss\n        plt.subplot(1, 2, 1)\n        plt.plot(train_steps, train_losses, color='#d62728', linewidth=1.5, label='Loss')\n        plt.title('Training Loss (Hàm mất mát)', fontsize=14, fontweight='bold')\n        plt.xlabel('Steps (Số bước huấn luyện)', fontsize=12)\n        plt.ylabel('Loss Value', fontsize=12)\n        plt.grid(True, linestyle='--', alpha=0.5)\n        plt.legend()\n\n        # 2. Biểu đồ Validation mAP\n        plt.subplot(1, 2, 2)\n        plt.plot(val_epochs, val_mAPs, marker='o', color='#1f77b4', linewidth=2, label='mAP@50')\n        \n        # Đánh dấu điểm cao nhất\n        max_acc = max(val_mAPs)\n        max_ep = val_epochs[val_mAPs.index(max_acc)]\n        \n        plt.scatter(max_ep, max_acc, color='red', s=150, zorder=5)\n        plt.annotate(f'Đỉnh: {max_acc:.1f}%', (max_ep, max_acc), \n                     xytext=(max_ep, max_acc-5), ha='center',\n                     arrowprops=dict(facecolor='black', shrink=0.05), fontsize=12, fontweight='bold')\n        \n        plt.title('Độ chính xác (mAP) qua các Epochs', fontsize=14, fontweight='bold')\n        plt.xlabel('Epochs', fontsize=12)\n        plt.ylabel('mAP (%)', fontsize=12)\n        plt.xticks(val_epochs) # Hiện đủ số 1-12\n        plt.grid(True, linestyle='--', alpha=0.5)\n        plt.legend()\n\n        plt.tight_layout()\n        plt.savefig('ket_qua_bao_cao_final.png', dpi=150) # Lưu ảnh\n        plt.show()\n        \n        print(\"-\" * 40)\n        print(f\"✅ Đã vẽ xong! Ảnh biểu đồ được lưu tại: ket_qua_bao_cao_final.png\")\n        print(f\"🏆 Kết quả tốt nhất: Epoch {max_ep} đạt độ chính xác {max_acc:.2f}%\")\n        print(\"-\" * 40)\n    else:\n        print(\"⚠️ File log có tồn tại nhưng dữ liệu bên trong không đầy đủ để vẽ.\")\nelse:\n    print(f\"❌ Không tìm thấy file tại đường dẫn: {log_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-13T17:00:22.676320Z","iopub.execute_input":"2025-12-13T17:00:22.677016Z","iopub.status.idle":"2025-12-13T17:00:23.886860Z","shell.execute_reply.started":"2025-12-13T17:00:22.676985Z","shell.execute_reply":"2025-12-13T17:00:23.886116Z"}},"outputs":[],"execution_count":null}]}