{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":4117,"databundleVersionId":46665},{"sourceType":"datasetVersion","sourceId":15851351,"datasetId":10161859,"databundleVersionId":16802638},{"sourceType":"datasetVersion","sourceId":15833406,"datasetId":10149311,"databundleVersionId":16783169}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torch.nn as nn\nimport xgboost as xgb\nfrom torchvision import models\n\n# 1. ĐỊNH NGHĨA ĐƯỜNG DẪN (Copy từ thông tin bạn cung cấp)\nPATH_RESNET = '/kaggle/input/datasets/azoncx/b-model/malware_resnet_full.pth'\nPATH_XGB = '/kaggle/input/datasets/azoncx/b-model/malware_xgb_full.json'\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# 2. NẠP MODEL RESNET\nprint(\">>> Đang nạp ResNet18...\")\n# Khởi tạo khung mạng trống (phải khớp kiến trúc lúc train)\nnet = models.resnet18()\nnet.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)\nnet.fc = nn.Linear(512, 9)\n\n# Load trọng số từ Dataset\ntry:\n    net.load_state_dict(torch.load(PATH_RESNET, map_location=device))\n    net.to(device)\n    net.eval()\n    print(\"✅ Nạp ResNet thành công.\")\nexcept Exception as e:\n    print(f\"❌ Lỗi nạp ResNet: {e}\")\n\n# 3. NẠP MODEL XGBOOST\nprint(\">>> Đang nạp XGBoost...\")\ntry:\n    clf_xgb = xgb.XGBClassifier()\n    clf_xgb.load_model(PATH_XGB)\n    print(\"✅ Nạp XGBoost thành công.\")\nexcept Exception as e:\n    print(f\"❌ Lỗi nạp XGBoost: {e}\")\n\n# --- BÂY GIỜ BẠN CÓ THỂ DÙNG net VÀ clf_xgb ĐỂ DỰ ĐOÁN ---","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-22T12:13:32.165727Z","iopub.execute_input":"2026-04-22T12:13:32.166135Z","iopub.status.idle":"2026-04-22T12:13:42.732351Z","shell.execute_reply.started":"2026-04-22T12:13:32.166099Z","shell.execute_reply":"2026-04-22T12:13:42.731438Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"opcodes = ['jmp', 'mov', 'retf', 'push', 'pop', 'add', 'sub', 'mul', 'div', 'and', 'or', 'xor', 'cmp', 'test', 'call']\nkeywords = ['db', 'dw', 'dd', 'offset', 'api', '.dll', 'std::']\n\ndef extract_for_prediction(fid):\n    bytes_path = os.path.join(TEMP_DIR, f\"{fid}.bytes\")\n    asm_path = os.path.join(TEMP_DIR, f\"{fid}.asm\")\n    \n    # --- 1. XỬ LÝ ẢNH (Khớp logic width=256) ---\n    img_tensor = torch.zeros((1, 1, 128, 128))\n    if os.path.exists(bytes_path):\n        try:\n            with open(bytes_path, 'r') as f:\n                hex_list = [int(x, 16) for x in f.read().split() if len(x) == 2 and x != '??']\n            if hex_list:\n                data = np.array(hex_list, dtype=np.uint8)\n                width = 256 # Cố định theo hàm train\n                height = len(data) // width\n                if height > 0:\n                    img = Image.fromarray(data[:width*height].reshape((height, width)))\n                    # Dùng NEAREST để giữ nguyên đặc trưng pixel\n                    img = img.resize((128, 128), Image.Resampling.NEAREST).convert('L')\n                    t_ = transforms.Compose([\n                        transforms.ToTensor(),\n                        transforms.Normalize((0.5,), (0.5,))\n                    ])\n                    img_tensor = t_(img).unsqueeze(0)\n        except: pass\n\n    # --- 2. XỬ LÝ ASM (Khớp logic đọc theo dòng) ---\n    # opcodes và keywords phải được định nghĩa trước (giống lúc train)\n    total_len = len(opcodes) + len(keywords)\n    asm_features_list = [0 for _ in range(total_len)]\n    \n    if os.path.exists(asm_path):\n        try:\n            with open(asm_path, 'r', encoding='latin-1') as f:\n                for line in f:\n                    line = line.lower()\n                    # Đếm Opcode\n                    for i, op in enumerate(opcodes):\n                        if op in line:\n                            asm_features_list[i] += line.count(op)\n                    # Đếm Keywords\n                    for i, key in enumerate(keywords):\n                        if key in line:\n                            asm_features_list[len(opcodes) + i] += line.count(key)\n        except: pass\n        \n    return img_tensor, asm_features_list","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-22T12:17:00.912709Z","iopub.execute_input":"2026-04-22T12:17:00.913168Z","iopub.status.idle":"2026-04-22T12:17:00.927323Z","shell.execute_reply.started":"2026-04-22T12:17:00.913132Z","shell.execute_reply":"2026-04-22T12:17:00.926267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, pandas as pd, numpy as np, subprocess, re, torch, gc\nimport torch.nn.functional as F\nfrom PIL import Image\nfrom collections import Counter\nfrom torchvision import transforms\n\n# --- CẤU HÌNH ---\nP_TEST = '/kaggle/input/competitions/malware-classification/test.7z'\nTEMP_DIR = '/kaggle/working/temp_full_test'\nSUBMISSION_FILE = 'submission_final.csv'\nBATCH_SIZE = 100\nos.makedirs(TEMP_DIR, exist_ok=True)\n\n# 1. Lấy toàn bộ danh sách ID từ tập Test\nprint(\">>> Đang quét toàn bộ danh sách file trong tập Test...\")\nraw_test = subprocess.check_output(f\"7z l {P_TEST} | grep .bytes | awk '{{print $NF}}'\", shell=True).decode().splitlines()\nall_test_ids = [os.path.basename(f).replace('.bytes', '') for f in raw_test if len(os.path.basename(f)) > 15]\nprint(f\"Tổng cộng có {len(all_test_ids)} file cần dự đoán.\")\n\n# Đảm bảo model ở chế độ dự đoán\nnet.eval()\nif os.path.exists(SUBMISSION_FILE): os.remove(SUBMISSION_FILE)\n\nprint(f\">>> Bắt đầu dự đoán {len(all_test_ids)} file...\")\n\nfor i in range(0, len(all_test_ids), BATCH_SIZE):\n    batch_ids = all_test_ids[i : i + BATCH_SIZE]\n    \n    # Giải nén Batch\n    with open('batch_test_list.txt', 'w') as f:\n        for fid in batch_ids: f.write(f\"test/{fid}.bytes\\ntest/{fid}.asm\\n\")\n    subprocess.run(f\"7z e {P_TEST} -o{TEMP_DIR} -i@batch_test_list.txt -y > /dev/null\", shell=True)\n    \n    batch_results = []\n    for fid in batch_ids:\n        img_input, asm_input = extract_for_prediction(fid)\n        \n        # 1. ResNet Predict\n        with torch.no_grad():\n            prob_resnet = F.softmax(net(img_input.to(device)), dim=1).cpu().numpy().flatten()\n        \n        # 2. XGBoost Predict\n        asm_data = np.array(asm_input).reshape(1, -1)\n        prob_xgb_raw = clf_xgb.predict_proba(asm_data).flatten()\n        \n        # Đồng bộ 9 lớp (Phòng trường hợp XGBoost thiếu class)\n        prob_xgb_9 = np.zeros(9)\n        prob_xgb_9[:min(len(prob_xgb_raw), 9)] = prob_xgb_raw[:min(len(prob_xgb_raw), 9)]\n\n        # 3. Ensemble 40/60\n        final_prob = 0.4 * prob_resnet + 0.6 * prob_xgb_9\n        \n        res_row = {'Id': fid}\n        for j in range(9): res_row[f'Prediction{j+1}'] = float(final_prob[j])\n        batch_results.append(res_row)\n\n    # Ghi batch vào CSV (Append mode)\n    pd.DataFrame(batch_results).to_csv(SUBMISSION_FILE, mode='a', header=not os.path.exists(SUBMISSION_FILE), index=False)\n    \n    # Dọn dẹp để tránh đầy bộ nhớ\n    subprocess.run(f\"rm -rf {TEMP_DIR}/*\", shell=True)\n    gc.collect()\n    torch.cuda.empty_cache()\n    \n    if (i + BATCH_SIZE) % 500 == 0:\n        print(f\"Tiến độ: {min(i + BATCH_SIZE, len(all_test_ids))}/{len(all_test_ids)}\")\n\nprint(f\"🏁 Xong! File nộp bài đã sẵn sàng: {SUBMISSION_FILE}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-22T12:17:04.138515Z","iopub.execute_input":"2026-04-22T12:17:04.138840Z"}},"outputs":[],"execution_count":null}]}