{"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"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"%%capture\n! pip install timm\n! pip install einops\n! pip install --upgrade wandb\n! pip install --upgrade torchmetrics\n! pip install --upgrade pytorch-lightning","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport sys\nimport cv2\nimport glob\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport matplotlib.patches as patches\n\nimport wandb\nfrom einops import rearrange\n\nimport timm\nimport torch\nimport transformers\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.autograd import Variable\nfrom torch.utils.data import DataLoader, Dataset\n\nimport torchmetrics\nimport pytorch_lightning as pl\nfrom pytorch_lightning.callbacks import ModelCheckpoint, LearningRateMonitor\n\nfrom sklearn.model_selection import StratifiedGroupKFold\n\nfrom albumentations import (\n    HorizontalFlip, VerticalFlip, IAAPerspective, ShiftScaleRotate, CLAHE, RandomRotate90, Affine,\n    Transpose, ShiftScaleRotate, Blur, OpticalDistortion, GridDistortion, HueSaturationValue, PixelDropout,\n    IAAAdditiveGaussianNoise, GaussNoise, MotionBlur, MedianBlur, IAAPiecewiseAffine, RandomResizedCrop,\n    IAASharpen, IAAEmboss, RandomBrightnessContrast, Flip, OneOf, Compose, Normalize, Cutout, CoarseDropout, ShiftScaleRotate, CenterCrop, Resize\n)\n\nimport warnings\nwarnings.simplefilter('ignore')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# If you want to use Wandb Logger\nfrom kaggle_secrets import UserSecretsClient\nuser_secrets = UserSecretsClient()\nwb_key = user_secrets.get_secret(\"WANDB_API_KEY\")\n\nwandb.login(key=wb_key)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Preprocessed dataset https://www.kaggle.com/competitions/rsna-breast-cancer-detection/discussion/369762\n# Already attached to this notebook\nDATA_PATH = '/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs_cv2_512/train_images_processed_cv2_512/'\nIMG_SIZE = (512, 256) # To rescale after ROI cropping\nBATCH_SIZE = 16\nNUM_WORKERS = 8\nACCELERATOR = 'cpu' # gpu/tpu\nLR = 1e-4","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Main Competition Metric\ndef probabilistic_f1(labels, preds, beta=1):\n    \"\"\"\n    Function taken from Awsaf's notebook:\n    https://www.kaggle.com/code/awsaf49/rsna-bcd-efficientnet-tf-tpu-1vm-train\n    \"\"\"\n    eps = 1e-5\n    preds = preds.clip(0, 1)\n    y_true_count = labels.sum()\n    ctp = preds[labels==1].sum()\n    cfp = preds[labels==0].sum()\n    beta_squared = beta * beta\n    c_precision = ctp / (ctp + cfp + eps)\n    c_recall = ctp / (y_true_count + eps)\n    if (c_precision > 0 and c_recall > 0):\n        result = (1 + beta_squared) * (c_precision * c_recall) / (beta_squared * c_precision + c_recall + eps)\n        return result\n    else:\n        return 0.0","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Some basic processing of df\ndf = pd.read_csv(\"/kaggle/input/rsna-breast-cancer-detection/train.csv\")\ndf['img_name'] = df['patient_id'].astype(str) + \"/\" + df['image_id'].astype(str) + \".png\"\ndf.age = df.age.fillna(df.age.median())\ndf = df.sample(frac=1).reset_index(drop=True)\ndf.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNADataset(Dataset):\n    def __init__(self, df, img_folder, augments=None, is_test=False):\n        self.df = df\n        self.is_test = is_test\n        self.augments = augments\n        self.img_folder = img_folder\n        \n    def __getitem__(self, idx):\n        img_path = os.path.join(self.img_folder, self.df['img_name'][idx])\n        img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)\n        \n        # ROI Cropping by @snnclsr\n        (x, y, w, h) = crop_coords(img)\n        rect = patches.Rectangle((x, y), w, h, linewidth=1, edgecolor='r', facecolor='none')\n        img = img[y:y+h, x:x+w]\n        img = Resize(IMG_SIZE, p=1.0)(image=img)['image']\n        \n        clahe1 = CLAHE(clip_limit=(1, 2), always_apply=True, p=1.0)(image=img)['image']\n        clahe2 = CLAHE(clip_limit=(2, 4), always_apply=True, p=1.0)(image=img)['image']\n        \n        img = np.stack((img, clahe1, clahe2))\n        \n        if self.augments:\n            img = rearrange(img, 'c h w -> h w c')\n            img = self.augments(image=img)['image']\n            img = rearrange(img, 'h w c -> c h w')\n        \n        img = torch.tensor(img, dtype=torch.float)\n        \n        if not self.is_test:\n            target = torch.tensor(self.df['cancer'][idx]).float()\n            return (img, target)\n        \n        return (img)\n    \n    def __len__(self):\n        return len(self.df)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class RSNAModel(pl.LightningModule):\n    def __init__(self):\n        super(RSNAModel, self).__init__()\n        \n        self.save_hyperparameters()\n\n        # Your model here\n        \n#         self.model = timm.create_model('', pretrained=False)\n#         self.model.reset_classifier(0)\n#         self.n_features = 1280\n#         self.fc = nn.Sequential(\n#             nn.Linear(self.n_features, 1)\n#         )\n        \n        self.train_loss = nn.BCEWithLogitsLoss()\n        self.valid_loss = nn.BCEWithLogitsLoss()\n\n        self.pf = torchmetrics.classification.BinaryFBetaScore(0.8)\n        self.acc = torchmetrics.classification.BinaryAccuracy()\n        self.auc = torchmetrics.classification.BinaryAUROC()\n        \n    def forward(self, x):        \n        features = self.model(x)\n        return self.fc(features)\n    \n    def training_step(self, batch, batch_idx):\n        imgs, target = batch\n        \n        out = self(imgs).view(-1)\n        preds = out.sigmoid()\n        \n        train_loss = self.train_loss(out, target)\n        pf = torch.tensor([probabilistic_f1(target, preds)])\n        acc = self.acc(preds, target)\n        auc = self.auc(preds, target)\n        \n        self.log('train_loss', train_loss)\n        self.log('train_pf', pf)\n        self.log('train_acc', acc)\n        self.log('train_auc', auc)\n        \n        return {'loss': train_loss, 'pf': pf, 'acc': acc, 'auc': auc}\n    \n    def validation_step(self, batch, batch_idx):\n        imgs, target = batch\n        \n        out = self(imgs)\n        preds = out.sigmoid()\n        \n        val_loss = self.valid_loss(out.view(-1), target)\n        pf = torch.tensor([probabilistic_f1(target, preds)])\n        acc = self.acc(preds, target)\n        auc = self.auc(preds, target)\n        \n        self.log('val_loss', val_loss)\n        self.log('val_pf', pf)\n        self.log('val_acc', acc)\n        self.log('val_auc', auc)\n        \n        return {'val_loss': val_loss, 'val_pf': pf, 'val_acc': acc, 'val_auc': auc}\n    \n    def validation_end(self, outputs):\n        avg_loss = torch.stack([x['val_loss'] for x in outputs]).mean()\n        avg_acc = torch.stack([x['val_acc'] for x in outputs]).mean()\n        avg_pf = torch.stack([x['val_pf'] for x in outputs]).mean()\n        avg_auc = torch.stack([x['val_auc'] for x in outputs]).mean()\n        \n        self.log('epoch_val_loss', avg_loss)\n        self.log('epoch_val_pf', avg_pf)\n        self.log('epoch_val_acc', avg_acc)\n        self.log('epoch_val_auc', avg_auc)\n\n        return {'avg_val_loss': avg_loss}\n    \n    def training_epoch_end(self, outputs):\n        avg_loss = torch.stack([x['loss'] for x in outputs]).mean()\n        avg_acc = torch.stack([x['acc'] for x in outputs]).mean()\n        avg_pf = torch.stack([x['pf'] for x in outputs]).mean()\n        avg_auc = torch.stack([x['auc'] for x in outputs]).mean()\n        \n        self.log('epoch_train_loss', avg_loss)\n        self.log('epoch_train_pf', avg_pf)\n        self.log('epoch_train_acc', avg_acc)\n        self.log('epoch_train_auc', avg_auc)\n    \n    def predict_step(self, batch, batch_idx):\n        images, _ = batch\n        preds = self(images).view(-1).sigmoid()\n        \n        return preds\n    \n    def configure_optimizers(self):\n        opt = torch.optim.Adam(self.parameters(), lr=LR)\n        sch = torch.optim.lr_scheduler.ExponentialLR(\n            opt, \n            gamma=0.9\n        )\n        \n        return [opt], [sch]","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"wandb_logger = pl.loggers.WandbLogger(name=\"test\", project=\"rsna\", save_code=True)\n\nckpt_callback = ModelCheckpoint(\n    monitor=\"epoch_val_loss\",\n    dirpath=\"checkpoints\",\n    filename=\"ckpt-{epoch:02d}-{val_loss:.2f}\" + \"_\" + 'test',\n    save_top_k=3,\n    mode=\"min\",\n    save_last=True,\n    every_n_train_steps=100,\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Some augmentations...\naug = Compose([\n        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        PixelDropout(p=0.2, dropout_prob=0.2),\n        Affine(p=0.2),\n    ], p=1.)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dataset = RSNADataset(\n    df = df,\n    img_folder = DATA_PATH,\n    augments = aug\n)\n\nmodel = RSNAModel()\n\ntrainer = pl.Trainer(\n    max_epochs=20,\n    accelerator=ACCELERATOR,\n    logger=[wandb_logger], # or any other logger\n    callbacks=[\n        ckpt_callback, \n        LearningRateMonitor(logging_interval='step')],\n    log_every_n_steps=50,\n)\n\ntrain_loader = DataLoader(\n    train_dataset,\n    batch_size=BATCH_SIZE,\n    shuffle=True,\n    num_workers=NUM_WORKERS,\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Training...\n# trainer.fit(model, train_loader)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}