{"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":20270,"databundleVersionId":1222630,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Фиксирование сидов","metadata":{}},{"cell_type":"code","source":"seed=42","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:22.064849Z","iopub.execute_input":"2025-09-07T20:54:22.066364Z","iopub.status.idle":"2025-09-07T20:54:22.078594Z","shell.execute_reply.started":"2025-09-07T20:54:22.066334Z","shell.execute_reply":"2025-09-07T20:54:22.077874Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport torch\nimport tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:22.080637Z","iopub.execute_input":"2025-09-07T20:54:22.084837Z","iopub.status.idle":"2025-09-07T20:54:26.320512Z","shell.execute_reply.started":"2025-09-07T20:54:22.084810Z","shell.execute_reply":"2025-09-07T20:54:26.319955Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.random.seed(seed)\ntorch.manual_seed(seed)\ntorch.cuda.manual_seed(seed)\ntorch.cuda.manual_seed_all(seed)\nos.environ['PYTHONHASHSEED']=str(seed)\n\n#torch.backends.cudnn.deterministic = True\n#torch.backends.cudnn.benchmark = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:26.321391Z","iopub.execute_input":"2025-09-07T20:54:26.321749Z","iopub.status.idle":"2025-09-07T20:54:26.330604Z","shell.execute_reply.started":"2025-09-07T20:54:26.321721Z","shell.execute_reply":"2025-09-07T20:54:26.329964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"device=torch.device('cuda' if torch.cuda.is_available() else 'cpu')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:26.332110Z","iopub.execute_input":"2025-09-07T20:54:26.332298Z","iopub.status.idle":"2025-09-07T20:54:26.409543Z","shell.execute_reply.started":"2025-09-07T20:54:26.332283Z","shell.execute_reply":"2025-09-07T20:54:26.408753Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Импорты","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nfrom PIL import Image\nimport timm\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom torchvision import transforms as T\nfrom sklearn.metrics import roc_auc_score\nfrom torch import nn","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:26.410398Z","iopub.execute_input":"2025-09-07T20:54:26.410635Z","iopub.status.idle":"2025-09-07T20:54:34.225630Z","shell.execute_reply.started":"2025-09-07T20:54:26.410610Z","shell.execute_reply":"2025-09-07T20:54:34.224666Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Загрузка данных","metadata":{}},{"cell_type":"code","source":"TEST_MODE=True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.226588Z","iopub.execute_input":"2025-09-07T20:54:34.226903Z","iopub.status.idle":"2025-09-07T20:54:34.231432Z","shell.execute_reply.started":"2025-09-07T20:54:34.226875Z","shell.execute_reply":"2025-09-07T20:54:34.230584Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainn_csv=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/train.csv')\ntest_csv=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/test.csv')\nsample=pd.read_csv('/kaggle/input/siim-isic-melanoma-classification/sample_submission.csv')\n\ntrain_img_root='/kaggle/input/siim-isic-melanoma-classification/jpeg/train'\ntest_img_root='/kaggle/input/siim-isic-melanoma-classification/jpeg/test'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.232242Z","iopub.execute_input":"2025-09-07T20:54:34.232480Z","iopub.status.idle":"2025-09-07T20:54:34.509389Z","shell.execute_reply.started":"2025-09-07T20:54:34.232454Z","shell.execute_reply":"2025-09-07T20:54:34.508813Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if TEST_MODE:\n    trainn_csv = trainn_csv.sample(n=11000, random_state=seed).reset_index(drop=True)\nelse:\n    trainn_csv=trainn_csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.510111Z","iopub.execute_input":"2025-09-07T20:54:34.510306Z","iopub.status.idle":"2025-09-07T20:54:34.521781Z","shell.execute_reply.started":"2025-09-07T20:54:34.510289Z","shell.execute_reply":"2025-09-07T20:54:34.521052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_csv, eval_csv=train_test_split(trainn_csv,  test_size=0.2,\n    \n    stratify=trainn_csv[\"target\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.524006Z","iopub.execute_input":"2025-09-07T20:54:34.524230Z","iopub.status.idle":"2025-09-07T20:54:34.543451Z","shell.execute_reply.started":"2025-09-07T20:54:34.524213Z","shell.execute_reply":"2025-09-07T20:54:34.542745Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Датасет","metadata":{}},{"cell_type":"markdown","source":"## Всякие аугментации","metadata":{}},{"cell_type":"code","source":"#train_tfms=T.Compose(\n#    T.ResizedCrop\n#)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.544333Z","iopub.execute_input":"2025-09-07T20:54:34.544592Z","iopub.status.idle":"2025-09-07T20:54:34.558996Z","shell.execute_reply.started":"2025-09-07T20:54:34.544568Z","shell.execute_reply":"2025-09-07T20:54:34.558313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = 224\ntrain_tfms = T.Compose([\n    T.RandomResizedCrop(IMG_SIZE, scale=(0.8, 1.0)),\n    T.RandomHorizontalFlip(),\n    T.ColorJitter(brightness=0.1, contrast=0.1, saturation=0.1, hue=0.05),\n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])\neval_tfms = T.Compose([\n    T.Resize(IMG_SIZE + 32),\n    T.CenterCrop(IMG_SIZE),\n    T.ToTensor(),\n    T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.559669Z","iopub.execute_input":"2025-09-07T20:54:34.560060Z","iopub.status.idle":"2025-09-07T20:54:34.574843Z","shell.execute_reply.started":"2025-09-07T20:54:34.560041Z","shell.execute_reply":"2025-09-07T20:54:34.574186Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MelanomaDataset(Dataset):\n    def __init__(self, df, img_root, transform, with_labels):\n        self.df=df\n        self.with_labels = with_labels\n        self.img_root=img_root\n        self.transform=transform\n        \n    def __len__(self):\n        return len(self.df)\n        \n    def __getitem__(self, idx):\n        row=self.df.iloc[idx]\n        image_name=row['image_name']\n        image_path=os.path.join(self.img_root, f'{image_name}.jpg')\n        image=Image.open(image_path).convert('RGB')\n        if self.transform is not None:\n            image=self.transform(image)\n        if self.with_labels and 'target' in row.index:\n            label = torch.tensor(int(row['target']), dtype=torch.long)\n            return {'image': image, 'label': label}\n        else:\n            return {'image': image, 'image_name': row['image_name']}\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.575437Z","iopub.execute_input":"2025-09-07T20:54:34.575659Z","iopub.status.idle":"2025-09-07T20:54:34.592016Z","shell.execute_reply.started":"2025-09-07T20:54:34.575634Z","shell.execute_reply":"2025-09-07T20:54:34.591466Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Создание датасетов ","metadata":{}},{"cell_type":"code","source":"train_dataset = MelanomaDataset(train_csv, img_root=train_img_root, transform=train_tfms, with_labels=True)\neval_dataset = MelanomaDataset(eval_csv, img_root=train_img_root, transform=eval_tfms, with_labels=True)\ntest_dataset  = MelanomaDataset(test_csv,  img_root=test_img_root,  transform=eval_tfms, with_labels=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.592784Z","iopub.execute_input":"2025-09-07T20:54:34.593260Z","iopub.status.idle":"2025-09-07T20:54:34.611415Z","shell.execute_reply.started":"2025-09-07T20:54:34.593236Z","shell.execute_reply":"2025-09-07T20:54:34.610716Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Даталоадеры","metadata":{}},{"cell_type":"code","source":"train_dataloader=DataLoader(train_dataset, batch_size=32, shuffle=True, num_workers=8,)\neval_dataloader=DataLoader(eval_dataset, batch_size=32, shuffle=False, num_workers=8,)\ntest_dataloader=DataLoader(test_dataset, batch_size=32, shuffle=False, num_workers=8,)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.612216Z","iopub.execute_input":"2025-09-07T20:54:34.612423Z","iopub.status.idle":"2025-09-07T20:54:34.628799Z","shell.execute_reply.started":"2025-09-07T20:54:34.612400Z","shell.execute_reply":"2025-09-07T20:54:34.628033Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Модель","metadata":{}},{"cell_type":"code","source":"model=timm.create_model('resnet50.a1_in1k', pretrained=True, num_classes=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:34.629585Z","iopub.execute_input":"2025-09-07T20:54:34.629870Z","iopub.status.idle":"2025-09-07T20:54:35.715715Z","shell.execute_reply.started":"2025-09-07T20:54:34.629846Z","shell.execute_reply":"2025-09-07T20:54:35.715003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.to(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.716468Z","iopub.execute_input":"2025-09-07T20:54:35.716661Z","iopub.status.idle":"2025-09-07T20:54:35.920350Z","shell.execute_reply.started":"2025-09-07T20:54:35.716646Z","shell.execute_reply":"2025-09-07T20:54:35.919642Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Loss","metadata":{}},{"cell_type":"code","source":"criterion=nn.CrossEntropyLoss()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.920964Z","iopub.execute_input":"2025-09-07T20:54:35.921141Z","iopub.status.idle":"2025-09-07T20:54:35.924842Z","shell.execute_reply.started":"2025-09-07T20:54:35.921126Z","shell.execute_reply":"2025-09-07T20:54:35.924079Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Optimizer","metadata":{}},{"cell_type":"code","source":"BATCH_SIZE = 32  # matches train_loader above\nbase_lr = 0.1 * (BATCH_SIZE / 256.0) ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.925551Z","iopub.execute_input":"2025-09-07T20:54:35.925805Z","iopub.status.idle":"2025-09-07T20:54:35.941440Z","shell.execute_reply.started":"2025-09-07T20:54:35.925782Z","shell.execute_reply":"2025-09-07T20:54:35.940840Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"optim=torch.optim.SGD(model.parameters(), lr=base_lr, momentum=0.9, weight_decay=1e-4)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.942134Z","iopub.execute_input":"2025-09-07T20:54:35.942342Z","iopub.status.idle":"2025-09-07T20:54:35.956887Z","shell.execute_reply.started":"2025-09-07T20:54:35.942326Z","shell.execute_reply":"2025-09-07T20:54:35.956215Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Scheduler","metadata":{}},{"cell_type":"code","source":"scheduler=torch.optim.lr_scheduler.CosineAnnealingLR(optim, T_max=5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.957619Z","iopub.execute_input":"2025-09-07T20:54:35.957880Z","iopub.status.idle":"2025-09-07T20:54:35.973196Z","shell.execute_reply.started":"2025-09-07T20:54:35.957859Z","shell.execute_reply":"2025-09-07T20:54:35.972488Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# ","metadata":{}},{"cell_type":"markdown","source":"# Training loop","metadata":{}},{"cell_type":"markdown","source":"","metadata":{}},{"cell_type":"code","source":"EPOCHS=5","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.973999Z","iopub.execute_input":"2025-09-07T20:54:35.974242Z","iopub.status.idle":"2025-09-07T20:54:35.987255Z","shell.execute_reply.started":"2025-09-07T20:54:35.974227Z","shell.execute_reply":"2025-09-07T20:54:35.986554Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_auc = -1.0\nfor epoch in range(1, EPOCHS+1):\n    print(f\"\\nEpoch {epoch}/{EPOCHS} • lr={optim.param_groups[0]['lr']:.6f}\")\n    model.train()\n    running_loss, running_correct, n= 0.0,0,0\n\n    optim.zero_grad()\n    pbar=tqdm.tqdm(train_dataloader, desc='train', leave=False)\n\n    for step, batch in enumerate(pbar):\n        X=batch['image'].to(device)\n        y=batch['label'].to(device)\n        optim.zero_grad()\n        logits=model(X)\n        \n        loss=criterion(logits, y)\n        loss.backward()\n        optim.step()\n        \n\n        running_loss+=loss.item()*X.size(0)\n\n        preds=logits.argmax(dim=1)\n\n        running_correct+=(preds==y).sum().item()\n\n        n+=X.size(0)\n        pbar.set_postfix(loss=running_loss / max(n, 1), acc=running_correct / max(n, 1))\n\n    scheduler.step()\n    print(f\"train: loss={running_loss / n:.4f}, acc={running_correct / n:.4f}\")\n    #@torch.no_grad()\n    model.eval()\n    loss_sum, correct, n=0.0,0,0\n    all_probs, all_targets = [], []\n    with torch.no_grad():\n        pbar=tqdm.tqdm(eval_dataloader, desc='validation', leave=False)\n    \n        for batch in pbar:\n            X=batch['image'].to(device)\n            y=batch['label'].to(device)\n\n            logits=model(X)\n            loss=criterion(logits, y)\n\n            loss_sum+=loss.item()* X.size(0)\n\n            preds=logits.argmax(dim=1)\n\n            correct+=(preds==y).sum().item()\n\n            n+=X.size(0)\n            probs=torch.softmax(logits, dim=1)[:, 1].detach().cpu().numpy()\n\n            all_probs.append(probs)\n            all_targets.append(y.detach().cpu().numpy())\n        all_probs = np.concatenate(all_probs) if len(all_probs) else np.array([])\n        all_targets = np.concatenate(all_targets) if len(all_targets) else np.array([])\n        auc = roc_auc_score(all_targets, all_probs)\n        val_loss = loss_sum / n\n        val_acc  = correct / n\n        print(f\"valid: loss={val_loss:.4f}, acc={val_acc:.4f}, AUC={auc:.4f}\")\n\n        if auc > best_auc:\n            best_auc = auc\n            torch.save({\"model\": model.state_dict()}, \"best_resnet50.pt\")\n            print(f\"✓ Saved new best (AUC {best_auc:.4f})\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:54:35.988077Z","iopub.execute_input":"2025-09-07T20:54:35.988272Z","iopub.status.idle":"2025-09-07T20:55:33.911659Z","shell.execute_reply.started":"2025-09-07T20:54:35.988250Z","shell.execute_reply":"2025-09-07T20:55:33.910773Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Submission","metadata":{}},{"cell_type":"code","source":"ckpt_path=\"best_resnet50.pt\"\nstate=torch.load(ckpt_path, map_location=device)\nmodel.load_state_dict(state[\"model\"])\n\nmodel.eval()\n\ntest_names, test_probs=[], []\n\nwith torch.no_grad():\n    pbar = tqdm.tqdm(test_dataloader, desc=\"test\", leave=False)\n    for batch in pbar:\n        x = batch[\"image\"].to(device, non_blocking=True)\n        logits = model(x)                       # [B, 2]\n        probs  = torch.softmax(logits, 1)[:, 1] # positive-class probability\n        test_probs.append(probs.cpu().numpy())\n        test_names.extend(batch[\"image_name\"])\ntest_probs = np.concatenate(test_probs, axis=0)\npred_df = pd.DataFrame({\"image_name\": test_names, \"target\": test_probs})\n\n# 3) Align to sample order & save\nsubmission = sample[[\"image_name\"]].merge(pred_df, on=\"image_name\", how=\"left\")\n#submission[\"target\"] = submission[\"target\"].fillna(0.0)  # just in case\nsubmission.to_csv(\"submission.csv\", index=False)\nprint(submission.head(), \"\\nSaved -> submission.csv\")\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-07T20:55:33.912781Z","iopub.execute_input":"2025-09-07T20:55:33.913018Z","iopub.status.idle":"2025-09-07T21:07:06.018904Z","shell.execute_reply.started":"2025-09-07T20:55:33.912994Z","shell.execute_reply":"2025-09-07T21:07:06.018003Z"}},"outputs":[],"execution_count":null}]}