{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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"},"colab":{"machine_shape":"hm","gpuType":"T4"},"accelerator":"GPU","kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":20270,"databundleVersionId":1222630,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":9797223,"sourceType":"datasetVersion","datasetId":982170},{"sourceId":266650508,"sourceType":"kernelVersion"}],"dockerImageVersionId":31090,"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/\")\n\nimport 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)\n\nimport torch.nn as nn\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\n\nclass args:\n    batch_size = 8\n    image_size = 384\n    epochs = 10\n    fold = 0\n\n\nclass CustomDataset:\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 = np.transpose(image, (2, 0, 1)).astype(np.float32)\n        \n        features = self.dense_features[item, :]\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        }\n\n\nclass CustomModel(nn.Module):\n    def __init__(self):\n        super().__init__()        \n        self.model = timm.create_model(\"resnet50\", pretrained=True, in_chans=3)\n        \n        # Get the number of features from the model\n        n_features = self.model.get_classifier().in_features\n        \n        # Remove the default classifier\n        self.model.reset_classifier(0)\n        \n        self.dropout = nn.Dropout(0.5)\n        # Output should be 1 for binary classification with sigmoid\n        self.out = nn.Linear(n_features, 1)\n        \n        self.step_scheduler_after = \"epoch\"\n\n    def monitor_metrics(self, outputs, targets):\n        \"\"\"Calculate ROC AUC score with NaN handling\"\"\"\n        outputs_np = outputs.sigmoid().cpu().detach().numpy().flatten()\n        targets_np = targets.cpu().detach().numpy().flatten()\n        \n        try:\n            # Check if we have both classes in targets\n            if len(np.unique(targets_np)) < 2:\n                auc = 0.5\n            else:\n                auc = metrics.roc_auc_score(targets_np, outputs_np)\n                # Handle NaN cases\n                if np.isnan(auc) or np.isinf(auc):\n                    auc = 0.5\n        except:\n            auc = 0.5\n        \n        # Return as tensor for Tez compatibility\n        return {\"auc\": torch.tensor(auc, device=outputs.device)}\n\n    def optimizer_scheduler(self):\n        opt = torch.optim.AdamW(self.parameters(), lr=1e-4, 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        # Get features from backbone\n        x = self.model(image)\n        x = self.dropout(x)\n        # Output logits (no activation here)\n        x = self.out(x)\n\n        if targets is not None:\n            # Use BCEWithLogitsLoss for binary classification (more stable)\n            loss = nn.BCEWithLogitsLoss()(x.view(-1), targets.view(-1))\n            metrics = self.monitor_metrics(x, targets)\n            return x, loss, metrics\n        return x, 0, {}\n\n\ntrain_aug = albumentations.Compose(\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        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)\n\ndf = pd.read_csv(\"/kaggle/input/deepak-10092-pawpularity/train_5folds.csv\")\ndf.head()\n\ni = 0\nprint(f'training fold: {i} start')\nargs.fold = 0\ndf_train = df[df.kfold != args.fold].reset_index(drop=True)\ndf_valid = df[df.kfold == args.fold].reset_index(drop=True)\n\ndense_features = []\n\n# REPLACE 'Id' WITH YOUR ACTUAL COLUMN NAME\n# Common options: 'Id', 'image_id', 'image', etc.\ntrain_img_paths = [f\"/kaggle/input/siim-isic-melanoma-classification/jpeg/train/{x}.jpg\" for x in df_train[\"image_name\"].values]\nvalid_img_paths = [f\"/kaggle/input/siim-isic-melanoma-classification/jpeg/train/{x}.jpg\" for x in df_valid[\"image_name\"].values]\n\ntrain_dataset = CustomDataset(\n    image_paths=train_img_paths,\n    dense_features=df_train[dense_features].values,\n    targets=df_train.target.values,\n    augmentations=train_aug,\n)\n\nvalid_dataset = CustomDataset(\n    image_paths=valid_img_paths,\n    dense_features=df_valid[dense_features].values,\n    targets=df_valid.target.values,\n    augmentations=valid_aug,\n)\n\nmodel = CustomModel()\nmodel = Tez(model)\n\nconfig = 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_auc\",\n    fp16=True,\n    val_strategy=\"batch\",\n    val_steps=900,\n)\n\nes = EarlyStopping(\n    monitor=\"valid_auc\",\n    model_path=f\"model_f{args.fold}.bin\",\n    patience=4,\n    mode=\"max\",\n    save_weights_only=True,\n)\n\nmodel.fit(\n    train_dataset,\n    valid_dataset=valid_dataset,\n    callbacks=[es],\n    config=config,\n)\n\nprint(f'training fold: {i} complete')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}