{"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":129617,"databundleVersionId":15872846,"isSourceIdPinned":false}],"dockerImageVersionId":31286,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\nimport torchvision.transforms as T\n\nfrom PIL import Image\nimport pandas as pd\nimport numpy as np\n\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.svm import SVC\nfrom tqdm import tqdm\n\nimport os\nimport re\n\nfrom transformers import CLIPProcessor, CLIPModel\n\ndevice = torch.cuda.current_device() if torch.cuda.is_available() else \"cpu\"\ndinov2 = torch.hub.load('facebookresearch/dinov2', 'dinov2_vits14').to(device)\ndinov2.eval()\n\nclip_model = CLIPModel.from_pretrained(\"openai/clip-vit-base-patch32\").to(device)\nclip_processor = CLIPProcessor.from_pretrained(\"openai/clip-vit-base-patch32\")\n\nTRAIN_DIR = '/kaggle/input/competitions/acm-ai-mini-ml-challenge-2026/train'\nTEST_DIR = '/kaggle/input/competitions/acm-ai-mini-ml-challenge-2026/test'\nTRAIN_CSV = '/kaggle/input/competitions/acm-ai-mini-ml-challenge-2026/train.csv'\nTEST_CSV = '/kaggle/input/competitions/acm-ai-mini-ml-challenge-2026/test.csv'\n\ntrain_df = pd.read_csv(TRAIN_CSV)\ntest_df = pd.read_csv(TEST_CSV)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-28T22:06:41.387577Z","iopub.execute_input":"2026-02-28T22:06:41.388400Z","iopub.status.idle":"2026-02-28T22:07:02.431177Z","shell.execute_reply.started":"2026-02-28T22:06:41.388365Z","shell.execute_reply":"2026-02-28T22:07:02.430177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transform_base = T.Compose([\n    T.Resize(256, interpolation=T.InterpolationMode.BICUBIC),\n    T.CenterCrop(224),\n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\ntransform_flip = T.Compose([\n    T.Resize(256, interpolation=T.InterpolationMode.BICUBIC),\n    T.CenterCrop(224),\n    T.RandomHorizontalFlip(p=1.0),\n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\ntransform_jitter = T.Compose([\n    T.Resize(256, interpolation=T.InterpolationMode.BICUBIC),\n    T.CenterCrop(224),\n    T.ColorJitter(brightness=0.2, contrast=0.2), \n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\n\ndef get_fused_tta_features(df, image_dir):\n    features_list = []\n\n    for img_name in tqdm(df['path']):\n        path = os.path.join(image_dir, img_name)\n        img = Image.open(path).convert('RGB')\n\n        dino_versions = []\n        clip_versions = []\n\n        for t in [transform_base, transform_flip, transform_jitter]:\n            img_t = t(img).unsqueeze(0).to(device)\n\n            with torch.no_grad():\n                d_feat = dinov2(img_t).cpu().numpy().flatten()\n                dino_versions.append(d_feat)\n\n                c_output = clip_model.get_image_features(pixel_values=img_t)\n                \n                if hasattr(c_output, \"pooler_output\"):\n                    c_tensor = c_output.pooler_output\n                elif isinstance(c_output, torch.Tensor):\n                    c_tensor = c_output\n                else:\n                    c_tensor = c_output[0] if isinstance(c_output, (list, tuple)) else c_output\n                \n                c_tensor = c_tensor / c_tensor.norm(p=2, dim=-1, keepdim=True)\n                clip_versions.append(c_tensor.cpu().numpy().flatten())\n\n        avg_dino = np.mean(dino_versions, axis=0)\n        avg_clip = np.mean(clip_versions, axis=0)\n\n        fused = np.concatenate([avg_dino, avg_clip])\n        features_list.append(fused)\n\n    return np.array(features_list)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-28T22:10:31.389226Z","iopub.execute_input":"2026-02-28T22:10:31.389974Z","iopub.status.idle":"2026-02-28T22:10:31.405803Z","shell.execute_reply.started":"2026-02-28T22:10:31.389936Z","shell.execute_reply":"2026-02-28T22:10:31.404735Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_features(df, image_dir):\n    all_features = []\n    for img_name in tqdm(df['path']):\n        path = os.path.join(image_dir, img_name)\n        try:\n            img = Image.open(path).convert('RGB')\n            img_t = transform_base(img).unsqueeze(0).to(device)\n\n            with torch.no_grad():\n                features = dinov2(img_t)\n\n            all_features.append(features.cpu().numpy().flatten())\n        except Exception as e:\n            print(f\"Error loading{img_name}: {e}\")\n            all_features.append(np.zeros(384))\n\n    return np.array(all_features)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Extracting Train Features...\")\nX_train = get_fused_tta_features(train_df, TRAIN_DIR)\ny_train = train_df['target'].values","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-28T22:10:36.725614Z","iopub.execute_input":"2026-02-28T22:10:36.726480Z","iopub.status.idle":"2026-02-28T22:29:02.810177Z","shell.execute_reply.started":"2026-02-28T22:10:36.726446Z","shell.execute_reply":"2026-02-28T22:29:02.808964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Extracting Test Features...\")\nX_test = get_fused_tta_features(test_df, TEST_DIR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-28T22:29:13.311303Z","iopub.execute_input":"2026-02-28T22:29:13.311779Z","iopub.status.idle":"2026-02-28T22:33:29.329863Z","shell.execute_reply.started":"2026-02-28T22:29:13.311746Z","shell.execute_reply":"2026-02-28T22:33:29.328997Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from xgboost import XGBClassifier\n\ntarget_map = {'good': 1, 'bad': 0}\ny_train_numeric = np.array([target_map[label] for label in y_train_extended])\n\nxgb_clf = XGBClassifier(\n    n_estimators=1000,\n    learning_rate=0.02,\n    max_depth=4,\n    subsample=0.8,\n    colsample_bytree=0.8,\n    n_jobs=-1,\n    tree_method='hist',\n    random_state=42\n)\nxgb_clf.fit(X_train_extended, y_train_numeric)\n\nsvm_probs = final_clf.predict_proba(X_test)\nxgb_probs = xgb_clf.predict_proba(X_test)\n\n\nblends = [\n    (0.8, 0.2), \n    (0.7, 0.3),\n    (0.5, 0.5), \n    (0.4, 0.6),\n    (0.3, 0.7) \n]\n\nidx_to_label = {i: label for i, label in enumerate(final_clf.classes_)}\n\nfor s_weight, x_weight in blends:\n    print(f\"Blending: SVM({s_weight}) + XGB({x_weight})...\")\n    \n    blended_probs = (s_weight * svm_probs) + (x_weight * xgb_probs)\n    final_preds_idx = np.argmax(blended_probs, axis=1)\n    \n    final_str_preds = [idx_to_label[idx] for idx in final_preds_idx]\n    \n    file_name = f'blend_svm_{int(s_weight*100)}_xgb_{int(x_weight*100)}.csv'\n    sub_df = pd.DataFrame({'ID': clean_ids, 'TARGET': final_str_preds})\n    sub_df.to_csv(file_name, index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-28T22:57:25.349033Z","iopub.execute_input":"2026-02-28T22:57:25.350148Z","iopub.status.idle":"2026-02-28T22:57:47.843220Z","shell.execute_reply.started":"2026-02-28T22:57:25.350111Z","shell.execute_reply":"2026-02-28T22:57:47.842178Z"}},"outputs":[],"execution_count":null}]}