{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":4117,"databundleVersionId":46665,"isSourceIdPinned":false},{"sourceType":"kernelVersion","sourceId":313753828,"isSourceIdPinned":false}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =========================================================================\n# KỊCH BẢN HUẤN LUYỆN LEVEL 2: ĐỘNG CƠ TỐI ƯU TOÁN HỌC SLSQP\n# Cải tiến: Tương thích với CatBoost, Mở rộng báo cáo Analytics\n# =========================================================================\n\nimport os\nimport numpy as np\nimport pandas as pd\nfrom scipy.optimize import minimize\nfrom sklearn.metrics import log_loss, accuracy_score\nimport warnings\n\nwarnings.filterwarnings(\"ignore\")\n\n# ==========================================\n# 1. CẤU HÌNH VÀ TẢI TÀI SẢN TỪ LEVEL 1\n# ==========================================\nINPUT_DIR = '/kaggle/input/notebooks/padduwcs/final-level-1-base-models/level1_preds'\nOUTPUT_DIR = '/kaggle/working/level2_submission/'\nANALYTICS_DIR = '/kaggle/working/analytics/level2/'\n\nos.makedirs(OUTPUT_DIR, exist_ok=True)\nos.makedirs(ANALYTICS_DIR, exist_ok=True)\n\nprint(\"[*] Đang nạp Tài sản Dự đoán Đa hạt giống từ Level 1...\")\n\noof_lgb = np.load(os.path.join(INPUT_DIR, 'oof_lgb.npy'))\noof_xgb = np.load(os.path.join(INPUT_DIR, 'oof_xgb.npy'))\noof_et  = np.load(os.path.join(INPUT_DIR, 'oof_et.npy'))\noof_cb  = np.load(os.path.join(INPUT_DIR, 'oof_cb.npy')) # Nhận CatBoost\n\ntest_lgb = np.load(os.path.join(INPUT_DIR, 'test_lgb.npy'))\ntest_xgb = np.load(os.path.join(INPUT_DIR, 'test_xgb.npy'))\ntest_et  = np.load(os.path.join(INPUT_DIR, 'test_et.npy'))\ntest_cb  = np.load(os.path.join(INPUT_DIR, 'test_cb.npy'))\n\ny_train = np.load(os.path.join(INPUT_DIR, 'train_labels.npy'))\ntrain_ids = np.load(os.path.join(INPUT_DIR, 'train_ids.npy'), allow_pickle=True)\ntest_ids = np.load(os.path.join(INPUT_DIR, 'test_ids.npy'), allow_pickle=True)\n\noofs = [oof_lgb, oof_xgb, oof_et, oof_cb]\ntests = [test_lgb, test_xgb, test_et, test_cb]\nmodel_names = ['LightGBM', 'XGBoost', 'ExtraTrees', 'CatBoost']\n\nN_MODELS = len(model_names)\nN_CLASSES = 9\nEPSILON = 1e-15 \n\n# ==========================================\n# 2. PHASE A: PHÂN TÍCH MA TRẬN ĐỒNG THUẬN (PRE-ENSEMBLE)\n# ==========================================\nprint(\"\\n\" + \"=\"*60)\nprint(\"PHASE A: KIỂM TRA ĐỘ TƯƠNG QUAN MÔ HÌNH\")\nprint(\"=\"*60)\n\npreds_classes = [np.argmax(oof, axis=1) for oof in oofs]\ncorr_matrix = np.zeros((N_MODELS, N_MODELS))\nfor i in range(N_MODELS):\n    for j in range(N_MODELS):\n        corr_matrix[i, j] = np.mean(preds_classes[i] == preds_classes[j])\n\ndf_corr = pd.DataFrame(corr_matrix, index=model_names, columns=model_names)\ndf_corr.to_csv(os.path.join(ANALYTICS_DIR, 'model_correlation.csv'))\n\nprint(\"[*] Ma trận Đồng thuận (Correlation) sau Seed Averaging:\")\nprint(df_corr.round(4))\n\n# ==========================================\n# 3. PHASE B: ĐỘNG CƠ TOÁN HỌC SLSQP\n# ==========================================\nprint(\"\\n\" + \"=\"*60)\nprint(\"PHASE B: KHỞI ĐỘNG LÒ PHẢN ỨNG SLSQP\")\nprint(\"=\"*60)\n\ndef apply_temperature_and_weights(predictions_list, weights_matrix, temperatures):\n    N_SAMPLES = predictions_list[0].shape[0]\n    calibrated_preds = np.zeros((N_MODELS, N_SAMPLES, N_CLASSES))\n    \n    for m in range(N_MODELS):\n        p = np.clip(predictions_list[m], EPSILON, 1 - EPSILON)\n        p_temp = p ** (1.0 / temperatures[m])\n        calibrated_preds[m] = p_temp / np.sum(p_temp, axis=1, keepdims=True)\n    \n    calib_swapped = np.transpose(calibrated_preds, (1, 2, 0))\n    y_ens = np.sum(calib_swapped * weights_matrix, axis=2)\n    y_final = y_ens / np.sum(y_ens, axis=1, keepdims=True)\n    \n    return np.clip(y_final, EPSILON, 1 - EPSILON)\n\ndef objective_function(x, predictions_list, y_true):\n    W = x[:N_CLASSES * N_MODELS].reshape((N_CLASSES, N_MODELS))\n    T = x[N_CLASSES * N_MODELS:]\n    y_final = apply_temperature_and_weights(predictions_list, W, T)\n    return log_loss(y_true, y_final)\n\ninitial_weights = np.ones((N_CLASSES, N_MODELS)) / N_MODELS\ninitial_temps = np.ones(N_MODELS)\nx0 = np.concatenate((initial_weights.flatten(), initial_temps))\n\nbounds = [(0, 1)] * (N_CLASSES * N_MODELS) + [(0.1, 5.0)] * N_MODELS\n\ndef weight_sum_constraint(x):\n    W = x[:N_CLASSES * N_MODELS].reshape((N_CLASSES, N_MODELS))\n    return np.sum(W, axis=1) - 1.0\n\nconstraints = ({'type': 'eq', 'fun': weight_sum_constraint})\n\nprint(\"[*] Đang tối ưu hóa 40 tham số ranh giới...\")\nresult = minimize(\n    objective_function, x0, args=(oofs, y_train), \n    method='SLSQP', bounds=bounds, constraints=constraints,\n    options={'maxiter': 1500, 'disp': False, 'ftol': 1e-7}\n)\n\nif result.success:\n    print(f\"[+] Tối ưu hóa thành công! Tốn {result.nit} vòng lặp.\")\nelse:\n    print(\"[-] Cảnh báo: Tối ưu hóa chưa hội tụ hoàn toàn.\")\n\nopt_weights = result.x[:N_CLASSES * N_MODELS].reshape((N_CLASSES, N_MODELS))\nopt_temps = result.x[N_CLASSES * N_MODELS:]\n\nfinal_oof_preds = apply_temperature_and_weights(oofs, opt_weights, opt_temps)\nfinal_logloss = log_loss(y_train, final_oof_preds)\nfinal_acc = accuracy_score(y_train, np.argmax(final_oof_preds, axis=1))\n\nprint(f\"\\n🚀 [KẾT QUẢ VÀNG] OOF LOGLOSS SAU DUNG HỢP (LEVEL 2): {final_logloss:.6f}\")\nprint(f\"🎯 [ACCURACY ENSEMBLE]: {final_acc:.6f}\")\n\n# ==========================================\n# 4. PHASE C: X-RAY ANALYTICS (TRÍCH XUẤT TÀI SẢN)\n# ==========================================\nprint(\"\\n\" + \"=\"*60)\nprint(\"PHASE C: PHÂN TÍCH BÁO CÁO TOÁN HỌC CHI TIẾT\")\nprint(\"=\"*60)\n\n# Lưu xác suất OOF tổng thể để đối chiếu lỗi cục bộ sau này\nnp.save(os.path.join(ANALYTICS_DIR, 'final_calibrated_oof.npy'), final_oof_preds)\n\ndf_temps = pd.DataFrame({'Model': model_names, 'Optimized_Temperature': opt_temps})\ndf_temps.to_csv(os.path.join(ANALYTICS_DIR, 'optimized_temperatures.csv'), index=False)\n\nclass_names = [f'Class_{i}' for i in range(1, 10)]\ndf_weights = pd.DataFrame(opt_weights, index=class_names, columns=model_names)\ndf_weights.to_csv(os.path.join(ANALYTICS_DIR, 'optimized_weights_matrix.csv'))\n\n# Lưu báo cáo phân tích lỗi từng Class\nprint(\"\\n[*] Đang tổng hợp Báo cáo Phân tán Lỗi (Error Distribution Report)...\")\nerror_report = []\npred_classes = np.argmax(final_oof_preds, axis=1)\nfor c in range(N_CLASSES):\n    idx = (y_train == c)\n    class_loss = log_loss(y_train[idx], final_oof_preds[idx], labels=np.arange(9))\n    class_acc = accuracy_score(y_train[idx], pred_classes[idx])\n    error_report.append({'Class': c+1, 'Support': np.sum(idx), 'LogLoss': class_loss, 'Accuracy': class_acc})\n\ndf_errors = pd.DataFrame(error_report)\ndf_errors.to_csv(os.path.join(ANALYTICS_DIR, 'class_error_distribution.csv'), index=False)\nprint(\"    -> Đã xuất Báo cáo lỗi chi tiết: class_error_distribution.csv\")\n\n# -----------------------------------------------------------------\n# SỔ SINH TỬ MỚI (Khắt khe hơn, chỉ bẫy những file thực sự khó đoán)\n# -----------------------------------------------------------------\nprint(\"\\n[*] Đang quét tìm các tệp tin 'Bẫy Phạt' (Hard Samples)...\")\nindividual_loglosses = np.array([-np.log(final_oof_preds[i, y_train[i]]) for i in range(len(y_train))])\nhard_mask = individual_loglosses > 2.0\nhard_indices = np.where(hard_mask)[0]\n\nif len(hard_indices) > 0:\n    hard_df = pd.DataFrame({\n        'Id': train_ids[hard_indices],\n        'True_Class': y_train[hard_indices] + 1,\n        'LogLoss_Penalty': individual_loglosses[hard_indices],\n        'Pred_Top1_Class': np.argmax(final_oof_preds[hard_indices], axis=1) + 1,\n        'Pred_Top1_Prob': np.max(final_oof_preds[hard_indices], axis=1)\n    })\n    hard_df.sort_values(by='LogLoss_Penalty', ascending=False, inplace=True)\n    hard_csv_path = os.path.join(ANALYTICS_DIR, 'hard_samples_radar.csv')\n    hard_df.to_csv(hard_csv_path, index=False)\n    print(f\"    -> 🚨 PHÁT HIỆN: {len(hard_indices)} file bị phạt LogLoss > 2.0!\")\nelse:\n    print(\"    -> Tuyệt vời! Không có mẫu nào vượt ngưỡng phạt nguy hiểm.\")\n\n# ==========================================\n# 5. PHASE D: SUY LUẬN TẬP TEST & LƯU SUBMISSION\n# ==========================================\nprint(\"\\n\" + \"=\"*60)\nprint(\"PHASE D: SUY LUẬN TẬP TEST & LƯU SUBMISSION\")\nprint(\"=\"*60)\n\nfinal_test_preds = apply_temperature_and_weights(tests, opt_weights, opt_temps)\n\nsubmission = pd.DataFrame(\n    final_test_preds, \n    columns=['Prediction1', 'Prediction2', 'Prediction3', 'Prediction4', 'Prediction5', 'Prediction6', 'Prediction7', 'Prediction8', 'Prediction9']\n)\nsubmission.insert(0, 'Id', test_ids)\n\nsubmission_path = os.path.join(OUTPUT_DIR, 'submission_FINAL.csv')\nsubmission.to_csv(submission_path, index=False)\n\nprint(f\"✅ HOÀN TẤT TOÀN BỘ KIẾN TRÚC ENSEMBLE LEVEL 2!\")\nprint(f\"🏆 File nộp Kaggle cuối cùng đã sẵn sàng tại: {submission_path}\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}