{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":20270,"databundleVersionId":1222630,"sourceType":"competition"},{"sourceId":13312689,"sourceType":"datasetVersion","datasetId":8439135},{"sourceId":266886595,"sourceType":"kernelVersion"}],"dockerImageVersionId":31154,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import sys\nsys.path.append(\"../input/tez-lib/tez-main\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:22.076829Z","iopub.execute_input":"2025-10-10T08:33:22.077708Z","iopub.status.idle":"2025-10-10T08:33:22.086886Z","shell.execute_reply.started":"2025-10-10T08:33:22.077665Z","shell.execute_reply":"2025-10-10T08:33:22.086035Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import random\nimport numpy as np\nimport torch\nimport os\n\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    \nseed_everything(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:23.032014Z","iopub.execute_input":"2025-10-10T08:33:23.033024Z","iopub.status.idle":"2025-10-10T08:33:24.646106Z","shell.execute_reply.started":"2025-10-10T08:33:23.032994Z","shell.execute_reply":"2025-10-10T08:33:24.645257Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn\n\nfrom tez import Tez, TezConfig\nimport tez\nimport albumentations\nimport pandas as pd\nimport cv2\nimport numpy as np\nimport timm\nimport torch.nn as nn\nfrom sklearn import metrics\nimport torch\nfrom tez.callbacks import EarlyStopping\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:24.647147Z","iopub.execute_input":"2025-10-10T08:33:24.647503Z","iopub.status.idle":"2025-10-10T08:33:28.742736Z","shell.execute_reply.started":"2025-10-10T08:33:24.647474Z","shell.execute_reply":"2025-10-10T08:33:28.742038Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/aditya-23bcs10007-melanoma-classification-kfolds/train_5folds.csv\")\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:30.037186Z","iopub.execute_input":"2025-10-10T08:33:30.037990Z","iopub.status.idle":"2025-10-10T08:33:30.106240Z","shell.execute_reply.started":"2025-10-10T08:33:30.037963Z","shell.execute_reply":"2025-10-10T08:33:30.105475Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class args:\n    batch_size = 8\n    image_size = 384\n    epochs = 10\n    fold = 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:30.431533Z","iopub.execute_input":"2025-10-10T08:33:30.432250Z","iopub.status.idle":"2025-10-10T08:33:30.435763Z","shell.execute_reply.started":"2025-10-10T08:33:30.432196Z","shell.execute_reply":"2025-10-10T08:33:30.435155Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MelanomaDataset:\n    def __init__(self, image_paths, dense_features, targets, augmentations):\n        self.image_paths = image_paths\n        self.dense_features = dense_features\n        self.targets = targets\n        self.augmentations = augmentations\n        \n    def __len__(self):\n        return len(self.image_paths)\n    \n    def __getitem__(self, item):\n        image = cv2.imread(self.image_paths[item])\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        \n        if self.augmentations is not None:\n            augmented = self.augmentations(image=image)\n            image = augmented[\"image\"]\n        \n        image = image.astype(np.float32) / 255.0   \n        image = np.transpose(image, (2, 0, 1)).astype(np.float32)\n        \n        features = self.dense_features[item, :].astype(np.float32)\n        targets = self.targets[item]\n        \n        return {\n            \"image\": torch.tensor(image, dtype=torch.float),\n            \"features\": torch.tensor(features, dtype=torch.float),\n            \"targets\": torch.tensor(targets, dtype=torch.float),\n        }","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:32.831750Z","iopub.execute_input":"2025-10-10T08:33:32.832238Z","iopub.status.idle":"2025-10-10T08:33:32.838180Z","shell.execute_reply.started":"2025-10-10T08:33:32.832185Z","shell.execute_reply":"2025-10-10T08:33:32.837505Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MelanomaModel(nn.Module):\n    def __init__(self):\n        super().__init__()        \n        self.model = timm.create_model(\"resnet50\", pretrained=True, num_classes=0)#resnet50#resnet101#eca_nfnet_l1#resnest101e\n\n        \n        self.dropout = nn.Dropout(0.5)# increase dropout\n\n        self.out = nn.Linear(2066, 1)\n\n        \n        self.step_scheduler_after = \"epoch\"\n\n\n    def monitor_metrics(self, outputs, targets, loss):\n        return {\"bce_loss\": loss}\n\n    def optimizer_scheduler(self):\n        opt = torch.optim.AdamW(self.parameters(), lr=2.5e-05, weight_decay=0.01)\n        sch = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(\n            opt, T_0=10, T_mult=1, eta_min=1e-6, last_epoch=-1\n        )\n        return opt,sch\n\n    def forward(self, image, features, targets=None):\n\n        x = self.model(image)\n        x = self.dropout(x)\n        x = torch.cat([x, features], dim=1)\n        x = self.dropout(x)\n        x = self.out(x)\n\n        if targets is not None:\n            targets = targets.view(-1, 1).float()\n            loss = nn.BCEWithLogitsLoss()(x, targets)\n            metrics = self.monitor_metrics(x, targets, loss)\n            return x, loss, metrics\n        return x, 0, {}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:35.067152Z","iopub.execute_input":"2025-10-10T08:33:35.067428Z","iopub.status.idle":"2025-10-10T08:33:35.074088Z","shell.execute_reply.started":"2025-10-10T08:33:35.067402Z","shell.execute_reply":"2025-10-10T08:33:35.073261Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_aug = albumentations.Compose(\n    [\n\n        albumentations.LongestMaxSize(args.image_size, p=1),\n        albumentations.PadIfNeeded(args.image_size,args.image_size, p=1,border_mode=0),\n        \n       albumentations.HorizontalFlip(p=0.5),\n       albumentations.VerticalFlip(p=0.1),\n       albumentations.Rotate(limit=180, p=0.5),\n       albumentations.ShiftScaleRotate(\n                shift_limit=0.1, scale_limit=0.1, rotate_limit=45, p=0.5\n            ),\n        \n        albumentations.HueSaturationValue(\n            hue_shift_limit=0.2, sat_shift_limit=0.2, val_shift_limit=0.2, p=0.5\n        ),\n        albumentations.RandomBrightnessContrast(\n            brightness_limit=(-0.1, 0.1), contrast_limit=(-0.1, 0.1), p=0.5\n        ),\n        albumentations.Normalize(\n            mean=[0.485, 0.456, 0.406],\n            std=[0.229, 0.224, 0.225],\n            max_pixel_value=255.0,\n            p=1.0,\n        ),\n    ],\n    p=1.0,\n)\n\nvalid_aug = albumentations.Compose(\n    [\n\n        albumentations.LongestMaxSize(args.image_size, p=1),\n        albumentations.PadIfNeeded(args.image_size,args.image_size, p=1,border_mode=0),\n        albumentations.Normalize(\n            mean=[0.485, 0.456, 0.406],\n            std=[0.229, 0.224, 0.225],\n            max_pixel_value=255.0,\n            p=1.0,\n        ),\n    ],\n    p=1.0,\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:41.272883Z","iopub.execute_input":"2025-10-10T08:33:41.273479Z","iopub.status.idle":"2025-10-10T08:33:41.286701Z","shell.execute_reply.started":"2025-10-10T08:33:41.273453Z","shell.execute_reply":"2025-10-10T08:33:41.286039Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[\"diagnosis\"] = df[\"diagnosis\"].fillna(\"unknown\")\ndf[\"anatom_site_general_challenge\"] = df[\"anatom_site_general_challenge\"].fillna(\"unknown\")\n\n# One-hot encode desired columns, dtype uint8 -> ensures 0/1 integers and no float NaNs\nto_encode = [\"diagnosis\", \"anatom_site_general_challenge\"]\ndf_encoded = pd.get_dummies(df, columns=to_encode, dtype=np.uint8)\n\n# Keep 'sex' and 'age_approx' as numeric features (fill NaN)\nif \"sex\" in df_encoded.columns:\n    df_encoded[\"sex\"] = df_encoded[\"sex\"].replace({\"male\":1, \"female\":0}).fillna(0).astype(np.float32)\nelse:\n    # If sex was a boolean column or similar\n    if \"sex\" in df.columns:\n        df_encoded[\"sex\"] = df[\"sex\"].fillna(0).astype(np.float32)\n    else:\n        df_encoded[\"sex\"] = 0.0  # fallback\n\nif \"age_approx\" in df_encoded.columns:\n    df_encoded[\"age_approx\"] = df_encoded[\"age_approx\"].fillna(df_encoded[\"age_approx\"].median()).astype(np.float32)\nelse:\n    df_encoded[\"age_approx\"] = 0.0\n\n# define dense feature list (you can adapt to use only existing columns)\ndense_features = [\n    \"sex\", \"age_approx\"\n]\n# add diagnosis and anatom_site dummies present in df_encoded\nfor c in df_encoded.columns:\n    if c.startswith(\"diagnosis_\") or c.startswith(\"anatom_site_general_challenge_\"):\n        dense_features.append(c)\n\n# final feature matrix\nX = df_encoded[dense_features].fillna(0).values.astype(np.float32)\ny = df_encoded[\"target\"].values.astype(np.float32)\nimage_names = df_encoded[\"image_name\"].values  # adjust if the column is named differently\n\nprint(\"Num features:\", X.shape[1])\nprint(\"Feature names sample:\", dense_features[:10])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:41.632034Z","iopub.execute_input":"2025-10-10T08:33:41.632772Z","iopub.status.idle":"2025-10-10T08:33:41.676517Z","shell.execute_reply.started":"2025-10-10T08:33:41.632747Z","shell.execute_reply":"2025-10-10T08:33:41.675829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df=df_encoded","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:42.041548Z","iopub.execute_input":"2025-10-10T08:33:42.042242Z","iopub.status.idle":"2025-10-10T08:33:42.045494Z","shell.execute_reply.started":"2025-10-10T08:33:42.042193Z","shell.execute_reply":"2025-10-10T08:33:42.044741Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for i in range(5):\n    print(f\"training fold: {i} start\")\n    args.fold = i\n\n    df_train = df[df.kfold != args.fold].reset_index(drop=True)\n    df_valid = df[df.kfold == args.fold].reset_index(drop=True)\n\n    dense_features = [\n        'sex', 'age_approx',\n        'diagnosis_atypical melanocytic proliferation',\n        'diagnosis_cafe-au-lait macule',\n        'diagnosis_lentigo NOS',\n        'diagnosis_lichenoid keratosis',\n        'diagnosis_melanoma',\n        'diagnosis_nevus',\n        'diagnosis_seborrheic keratosis',\n        'diagnosis_solar lentigo',\n        'diagnosis_unknown',\n        'anatom_site_general_challenge_head/neck',\n        'anatom_site_general_challenge_lower extremity',\n        'anatom_site_general_challenge_oral/genital',\n        'anatom_site_general_challenge_palms/soles',\n        'anatom_site_general_challenge_torso',\n        'anatom_site_general_challenge_unknown',\n        'anatom_site_general_challenge_upper extremity'\n    ]\n\n    train_img_paths = [\n        f\"/kaggle/input/siim-isic-melanoma-classification/jpeg/train/{x}.jpg\"\n        for x in df_train[\"image_name\"].values\n    ]\n    valid_img_paths = [\n        f\"/kaggle/input/siim-isic-melanoma-classification/jpeg/train/{x}.jpg\"\n        for x in df_valid[\"image_name\"].values\n    ]\n    \n    train_feats = df_train[dense_features].fillna(0).clip(-1e6, 1e6).values.astype(np.float32)\n    valid_feats = df_valid[dense_features].fillna(0).clip(-1e6, 1e6).values.astype(np.float32)\n\n    train_targets = df_train[\"target\"].values.astype(np.float32)\n    valid_targets = df_valid[\"target\"].values.astype(np.float32)\n    \n    train_dataset = MelanomaDataset(\n        image_paths=train_img_paths,\n        dense_features=train_feats,\n        targets=train_targets,\n        augmentations=train_aug,\n    )\n\n    valid_dataset = MelanomaDataset(\n        image_paths=valid_img_paths,\n        dense_features=valid_feats,\n        targets=valid_targets,\n        augmentations=valid_aug,\n    )\n\n    model = MelanomaModel()  # must use BCEWithLogitsLoss inside\n    model = Tez(model)\n\n    config = TezConfig(\n        training_batch_size=args.batch_size,\n        validation_batch_size=2 * args.batch_size,\n        epochs=args.epochs,\n        step_scheduler_after=\"epoch\",\n        step_scheduler_metric=\"valid_bce_loss\",  # updated metric\n        fp16=True,\n        val_strategy=\"batch\",\n        val_steps=max(1, len(valid_dataset) // (2 * args.batch_size)),\n    )\n\n    es = EarlyStopping(\n        monitor=\"valid_bce_loss\",  # updated to match metric name\n        model_path=f\"model_f{i}.bin\",\n        patience=3,\n        mode=\"min\",\n        save_weights_only=True,\n    )\n\n    model.fit(\n        train_dataset,\n        valid_dataset=valid_dataset,\n        callbacks=[es],\n        config=config,\n    )\n\n    print(f\"training fold: {i} complete\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-10T08:33:49.907425Z","iopub.execute_input":"2025-10-10T08:33:49.908178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}