{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":18647,"databundleVersionId":1126921,"sourceType":"competition"},{"sourceId":848739,"sourceType":"datasetVersion","datasetId":251095},{"sourceId":949646,"sourceType":"datasetVersion","datasetId":515318},{"sourceId":1205442,"sourceType":"datasetVersion","datasetId":687495},{"sourceId":1371707,"sourceType":"datasetVersion","datasetId":779449}],"dockerImageVersionId":29926,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport random\nimport gc\n\nimport numpy as np\nimport pandas as pd\nimport skimage.io\nfrom tqdm import tqdm\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\n\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport efficientnet_pytorch as enet\n\nimport tensorflow as tf\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Input, Dense, Dropout, GlobalAveragePooling2D, Reshape, Permute, Conv2D\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.losses import BinaryCrossentropy\nimport efficientnet.tfkeras as efn\n\n# --------------------------------------------------------------------------------\n# Configuration\n# --------------------------------------------------------------------------------\n\nclass CFG:\n    debug = False  # Set to True for debugging (uses smaller dataset)\n    data_dir = '../input/prostate-cancer-grade-assessment'\n    model_dir_pytorch = '../input/panda-public-models'\n    model_dir_tf = '../input/pandaenetb042x256x256x3'\n    image_folder = 'test_images' if os.path.exists('../input/prostate-cancer-grade-assessment/test_images') else 'train_images'\n    desired_num_images = 10 if debug else None  # Set to None to use full dataset\n    seed = 315019\n    tile_size = 256\n    image_size = 256\n    n_tiles = 36\n    batch_size = 8\n    num_workers = 2  # Reduced for stability\n    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# --------------------------------------------------------------------------------\n# Utility Functions\n# --------------------------------------------------------------------------------\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\ndef generate_subdataset(df, num_images, seed):\n    if num_images is None:\n        return df\n    else:\n        random.seed(seed)\n        return df.sample(n=num_images, random_state=seed)\n\n# --------------------------------------------------------------------------------\n# PyTorch Model and Dataset\n# --------------------------------------------------------------------------------\n\nclass ENetV2(nn.Module):\n    def __init__(self, backbone, out_dim):\n        super().__init__()\n        self.enet = enet.EfficientNet.from_name(backbone)\n        self.myfc = nn.Linear(self.enet._fc.in_features, out_dim)\n        self.enet._fc = nn.Identity()\n\n    def extract(self, x):\n        return self.enet(x)\n\n    def forward(self, x):\n        x = self.extract(x)\n        x = self.myfc(x)\n        return x\n\ndef load_pytorch_models(model_files, model_dir, backbone):\n    models = []\n    for model_f in model_files:\n        model_path = os.path.join(model_dir, model_f)\n        model = ENetV2(backbone, out_dim=5)\n        model.load_state_dict(torch.load(model_path, map_location=CFG.device))\n        model.eval()\n        model.to(CFG.device)\n        models.append(model)\n        print(f'{model_path} loaded!')\n    return models\n\nclass PandaDataset(Dataset):\n    def __init__(self, df, image_size, n_tiles, tile_mode, transform, rand=False, sub_imgs=False):\n        self.df = df.reset_index(drop=True)\n        self.image_size = image_size\n        self.n_tiles = n_tiles\n        self.tile_mode = tile_mode\n        self.rand = rand\n        self.sub_imgs = sub_imgs\n        self.transform = transform\n\n    def __len__(self):\n        return self.df.shape[0]\n\n    def __getitem__(self, index):\n        row = self.df.iloc[index]\n        img_id = row.image_id\n\n        tiff_file = os.path.join(CFG.data_dir, CFG.image_folder, f'{img_id}.tiff')\n        image = skimage.io.MultiImage(tiff_file)[1]\n\n        tiles, OK = get_tiles(image, self.tile_mode) \n\n        if self.rand:\n            idxes = np.random.choice(list(range(self.n_tiles)), self.n_tiles, replace=False)\n        else:\n            idxes = list(range(self.n_tiles))\n        \n        idxes = np.asarray(idxes) + self.n_tiles if self.sub_imgs else idxes\n        idxes = idxes.astype(int)  # Cast idxes to integers\n\n        n_row_tiles = int(np.sqrt(self.n_tiles))\n        images = np.zeros((CFG.image_size * n_row_tiles, CFG.image_size * n_row_tiles, 3))\n\n        for h in range(n_row_tiles):\n            for w in range(n_row_tiles):\n                i = h * n_row_tiles + w\n                # Convert idxes[i] to an integer before using it to index tiles\n                this_img = tiles[int(idxes[i])]['img'] if len(tiles) > int(idxes[i]) else np.ones((CFG.image_size, CFG.image_size, 3)).astype(np.uint8) * 255\n                this_img = 255 - this_img\n                h1 = h * CFG.image_size\n                w1 = w * CFG.image_size\n                images[h1:h1+CFG.image_size, w1:w1+CFG.image_size] = this_img\n        \n        if self.transform:\n            images = self.transform(image=images)['image']\n\n        images = images.to(torch.float32) / 255 \n        images = images.permute(2, 0, 1)\n        return images\n\ndef get_tiles(img, mode=0):\n    h, w, c = img.shape\n    pad_h = (CFG.tile_size - h % CFG.tile_size) % CFG.tile_size + ((CFG.tile_size * mode) // 2)\n    pad_w = (CFG.tile_size - w % CFG.tile_size) % CFG.tile_size + ((CFG.tile_size * mode) // 2)\n    img2 = np.pad(img, [[pad_h // 2, pad_h - pad_h // 2], \n                          [pad_w // 2, pad_w - pad_w // 2], [0, 0]], \n                   constant_values=255)\n\n    # Create a grid of tiles and calculate variance\n    tiles = []\n    variances = []\n    for i in range(0, img2.shape[0] - CFG.tile_size + 1, CFG.tile_size):\n        for j in range(0, img2.shape[1] - CFG.tile_size + 1, CFG.tile_size):\n            tile = img2[i:i+CFG.tile_size, j:j+CFG.tile_size, :]\n            tiles.append({'img': tile, 'idx': len(tiles)}) # Store tile and its index\n            variances.append(np.var(tile))\n\n    # Select top N tiles with highest variance\n    top_indices = np.argsort(variances)[::-1][:CFG.n_tiles]\n    tiles = [tiles[i] for i in top_indices]\n\n    return tiles, True \n\n# --------------------------------------------------------------------------------\n# TensorFlow/Keras Model and Dataset\n# --------------------------------------------------------------------------------\n\ndef create_tf_model(backbone_name, n_tiles, img_size):\n    if backbone_name.startswith('efficientnet'):\n        backbone = getattr(efn, f'EfficientNetB{backbone_name[-1]}')(include_top=False,\n                                                                       input_shape=(img_size, img_size, 3),\n                                                                       weights=None)\n    elif backbone_name.startswith('densenet'):\n        backbone = getattr(tf.keras.applications, backbone_name)(include_top=False,\n                                                                   input_shape=(img_size, img_size, 3),\n                                                                   weights=None)\n    else:\n        raise ValueError(f\"Unsupported backbone: {backbone_name}\")\n    \n    input_tensor = Input(shape=(n_tiles, img_size, img_size, 3))\n    x = Reshape((-1, img_size, img_size, 3))(input_tensor)\n    x = backbone(x)\n    x = Reshape((-1, n_tiles, x.shape[1], x.shape[2], x.shape[3]))(x) \n    x = Permute((1, 3, 2, 4, 5))(x) \n    x = Reshape((-1, x.shape[2], n_tiles * x.shape[3], x.shape[4]))(x) \n    x = GlobalAveragePooling2D()(x)\n    x = Dropout(0.5)(x)\n    x = Dense(1024, activation='relu')(x)\n    output_tensor = Dense(5, activation='sigmoid')(x)\n\n    return Model(inputs=input_tensor, outputs=output_tensor)\n\n# --------------------------------------------------------------------------------\n# Data Loading\n# --------------------------------------------------------------------------------\ndf_train = pd.read_csv(os.path.join(CFG.data_dir, 'train.csv'))\ndf_test = pd.read_csv(os.path.join(CFG.data_dir, 'test.csv'))\n\nif CFG.debug:\n    df_train = generate_subdataset(df_train, CFG.desired_num_images, CFG.seed)\ndf = df_test if CFG.image_folder == 'test_images' else df_train\n\n# --------------------------------------------------------------------------------\n# Augmentations\n# --------------------------------------------------------------------------------\n\ntransforms_train = A.Compose([\n    A.Transpose(p=0.5),\n    A.VerticalFlip(p=0.5),\n    A.HorizontalFlip(p=0.5),\n])\n\ntransforms_val = A.Compose([\n    ToTensorV2(),\n])\n\ntta_transforms = [\n    A.Compose([A.Transpose(p=1), ToTensorV2()]),\n    A.Compose([A.VerticalFlip(p=1), ToTensorV2()]),\n    A.Compose([A.HorizontalFlip(p=1), ToTensorV2()]),\n    A.Compose([A.Transpose(p=1), A.VerticalFlip(p=1), A.HorizontalFlip(p=1), ToTensorV2()]),\n]\n\n# --------------------------------------------------------------------------------\n# PyTorch Inference\n# --------------------------------------------------------------------------------\n\ndef run_pytorch_inference(models, df, tile_mode, transforms):\n    dataset = PandaDataset(df, CFG.image_size, CFG.n_tiles, tile_mode, transforms)\n    loader = DataLoader(dataset, batch_size=CFG.batch_size, num_workers=CFG.num_workers, shuffle=False)\n    logits = []\n    with torch.no_grad():\n        for data in tqdm(loader, leave=False):\n            data = data.to(CFG.device)\n            for model in models:\n                outputs = model(data)\n                logits.append(outputs)\n    return torch.cat(logits).sigmoid().cpu()\n\n# Load and run PyTorch models \npytorch_models_b0 = load_pytorch_models(['cls_effnet_b0_Rand36r36tiles256_big_bce_lr0.3_augx2_30epo_model_fold0.pth'], \n                                      CFG.model_dir_pytorch, 'efficientnet-b0')\npytorch_preds_b0_mode0 = run_pytorch_inference(pytorch_models_b0, df, tile_mode=0, transforms=transforms_val)\npytorch_preds_b0_mode2 = run_pytorch_inference(pytorch_models_b0, df, tile_mode=2, transforms=transforms_val)\n\npytorch_models_b1 = load_pytorch_models(['enet_b1_8ep_fold0.pth'], CFG.model_dir_pytorch, 'efficientnet-b1')\npytorch_preds_b1_mode0 = run_pytorch_inference(pytorch_models_b1, df, tile_mode=0, transforms=transforms_val)\npytorch_preds_b1_mode2 = run_pytorch_inference(pytorch_models_b1, df, tile_mode=2, transforms=transforms_val)\n\n# --------------------------------------------------------------------------------\n# TensorFlow/Keras Inference\n# --------------------------------------------------------------------------------\n\ndef run_tf_inference(model, df, tile_fn, n_tiles, img_size, transforms=None):\n    predictions = []\n    for _, row in tqdm(df.iterrows(), total=len(df), leave=False):\n        img_id = row['image_id']\n        img_path = os.path.join(CFG.data_dir, CFG.image_folder, f'{img_id}.tiff')\n        img = skimage.io.MultiImage(img_path)[1]\n        patches = tile_fn(img)\n\n        if transforms:\n            augmented_patches = []\n            for transform in transforms:\n                augmented_patches.append(transform(image=patches)['image'])\n            augmented_patches = np.stack(augmented_patches)\n            augmented_patches = augmented_patches / 255.0\n            pred = model.predict(augmented_patches)\n            isup = np.mean(pred)\n        else:\n            patches = patches / 255.0\n            pred = model.predict(np.expand_dims(patches, axis=0))\n            isup = np.sum(pred) / 2\n        predictions.append(isup)\n\n        del patches, img\n        gc.collect()\n    return predictions\n\n\ndef tile(img):\n    sz = CFG.tile_size\n    N = CFG.n_tiles\n    result = []\n    shape = img.shape\n    pad0, pad1 = (sz - shape[0] % sz) % sz, (sz - shape[1] % sz) % sz\n    img = np.pad(img, [[pad0 // 2, pad0 - pad0 // 2], [pad1 // 2, pad1 - pad1 // 2], [0, 0]], constant_values=255)\n    img = img.reshape(img.shape[0] // sz, sz, img.shape[1] // sz, sz, 3)\n    img = img.transpose(0, 2, 1, 3, 4).reshape(-1, sz, sz, 3)\n    if len(img) < N:\n        img = np.pad(img, [[0, N - len(img)], [0, 0], [0, 0], [0, 0]], constant_values=255)\n    idxs = np.argsort(img.reshape(img.shape[0], -1).sum(-1))[:N]\n    img = img[idxs]\n    return img\n\ndef tile2(img):\n    sz = CFG.tile_size\n    N = CFG.n_tiles\n    result = []\n    shape = img.shape\n    pad0, pad1 = (sz - shape[0] % sz) % sz + ((sz * 2) // 2), (sz - shape[1] % sz) % sz + ((sz * 2) // 2)\n    img = np.pad(img, [[pad0 // 2, pad0 - pad0 // 2], [pad1 // 2, pad1 - pad1 // 2], [0, 0]], constant_values=255)\n    img = img.reshape(img.shape[0] // sz, sz, img.shape[1] // sz, sz, 3)\n    img = img.transpose(0, 2, 1, 3, 4).reshape(-1, sz, sz, 3)\n    if len(img) < N:\n        img = np.pad(img, [[0, N - len(img)], [0, 0], [0, 0], [0, 0]], constant_values=255)\n    idxs = np.argsort(img.reshape(img.shape[0], -1).sum(-1))[:N]\n    img = img[idxs]\n    return img\n\n\n# Model loading and inference\ntf_model_b0 = create_tf_model('efficientnet-b0', 48, 256)\ntf_model_b0.load_weights('../input/pandaenetb042x256x256x3/efficientnet-b0-48-full-epochs60.h5')\ntf_preds_b0_tile1 = run_tf_inference(tf_model_b0, df, tile, 48, 256, transforms=tta_transforms)\ntf_preds_b0_tile2 = run_tf_inference(tf_model_b0, df, tile2, 48, 256, transforms=tta_transforms)\n\n\ntf_model_b1 = create_tf_model('efficientnet-b1', 48, 256)\ntf_model_b1.load_weights('../input/pandaenetb042x256x256x3/efficientnet-b1-48-full-epochs60.h5')\ntf_preds_b1_tile1 = run_tf_inference(tf_model_b1, df, tile, 48, 256, transforms=tta_transforms)\ntf_preds_b1_tile2 = run_tf_inference(tf_model_b1, df, tile2, 48, 256, transforms=tta_transforms)\n\ntf_model_b2 = create_tf_model('efficientnet-b2', 48, 256)\ntf_model_b2.load_weights('../input/pandaenetb042x256x256x3/efficientnet-b2-48-full-epochs60.h5')\ntf_preds_b2_tile1 = run_tf_inference(tf_model_b2, df, tile, 48, 256, transforms=tta_transforms)\ntf_preds_b2_tile2 = run_tf_inference(tf_model_b2, df, tile2, 48, 256, transforms=tta_transforms)\n\ntf_model_densenet = create_tf_model('densenet121', 48, 256)\ntf_model_densenet.load_weights('../input/pandaenetb042x256x256x3/DenseNet121-48-full-epochs60.h5')\ntf_preds_densenet_tile1 = run_tf_inference(tf_model_densenet, df, tile, 48, 256, transforms=tta_transforms)\ntf_preds_densenet_tile2 = run_tf_inference(tf_model_densenet, df, tile2, 48, 256, transforms=tta_transforms)\n\ntf_model_b0_42_fold0 = create_tf_model('efficientnet-b0', 42, 256)\ntf_model_b0_42_fold0.load_weights('../input/pandaenetb042x256x256x3/efficientnet-b0-fold0-epochs40.h5')\ntf_preds_b0_42_fold0_tile1 = run_tf_inference(tf_model_b0_42_fold0, df, tile, 42, 256, transforms=tta_transforms)\ntf_preds_b0_42_fold0_tile2 = run_tf_inference(tf_model_b0_42_fold0, df, tile2, 42, 256, transforms=tta_transforms)\n\ntf_model_b0_42_fold4 = create_tf_model('efficientnet-b0', 42, 256)\ntf_model_b0_42_fold4.load_weights('../input/pandaenetb042x256x256x3/efficientnet-b0-fold4-epochs60.h5')\ntf_preds_b0_42_fold4_tile1 = run_tf_inference(tf_model_b0_42_fold4, df, tile, 42, 256, transforms=tta_transforms)\ntf_preds_b0_42_fold4_tile2 = run_tf_inference(tf_model_b0_42_fold4, df, tile2, 42, 256, transforms=tta_transforms)\n\ntf_model_b0_42_fold2 = create_tf_model('efficientnet-b0', 42, 256)\ntf_model_b0_42_fold2.load_weights('../input/pandaenetb042x256x256x3/efficientnet-b0-fold2-epochs40.h5')\ntf_preds_b0_42_fold2_tile1 = run_tf_inference(tf_model_b0_42_fold2, df, tile, 42, 256, transforms=tta_transforms)\ntf_preds_b0_42_fold2_tile2 = run_tf_inference(tf_model_b0_42_fold2, df, tile2, 42, 256, transforms=tta_transforms)\n\n# --------------------------------------------------------------------------------\n# Prediction Aggregation and Submission\n# --------------------------------------------------------------------------------\n\n# PyTorch prediction averaging\npytorch_preds_b0 = (pytorch_preds_b0_mode0 + pytorch_preds_b0_mode2) / 2\npytorch_preds_b1 = (pytorch_preds_b1_mode0 + pytorch_preds_b1_mode2) / 2\npytorch_preds = (pytorch_preds_b0.sum(1).numpy() + pytorch_preds_b1.sum(1).numpy()) / 2\n\n# TensorFlow predictions averaging and combination\ntf_preds_b0 = (np.array(tf_preds_b0_tile1) + np.array(tf_preds_b0_tile2)) / 2\ntf_preds_b1 = (np.array(tf_preds_b1_tile1) + np.array(tf_preds_b1_tile2)) / 2\ntf_preds_b2 = (np.array(tf_preds_b2_tile1) + np.array(tf_preds_b2_tile2)) / 2\ntf_preds_densenet = (np.array(tf_preds_densenet_tile1) + np.array(tf_preds_densenet_tile2)) / 2\ntf_preds_b0_42_fold0 = (np.array(tf_preds_b0_42_fold0_tile1) + np.array(tf_preds_b0_42_fold0_tile2)) / 2\ntf_preds_b0_42_fold4 = (np.array(tf_preds_b0_42_fold4_tile1) + np.array(tf_preds_b0_42_fold4_tile2)) / 2\ntf_preds_b0_42_fold2 = (np.array(tf_preds_b0_42_fold2_tile1) + np.array(tf_preds_b0_42_fold2_tile2)) / 2\n\n\n# Combine all predictions\nfinal_preds = (\n    0.25 * pytorch_preds + \n    0.1 * tf_preds_b0 + \n    0.1 * tf_preds_b1 +\n    0.1 * tf_preds_b2 +\n    0.1 * tf_preds_densenet +\n    0.05 * tf_preds_b0_42_fold0 + \n    0.05 * tf_preds_b0_42_fold4 +\n    0.05 * tf_preds_b0_42_fold2 \n)\n\n# Create the submission file\ndf['isup_grade'] = np.round(final_preds).astype(int)\ndf[['image_id', 'isup_grade']].to_csv('submission.csv', index=False)\n\nprint('Inference complete! Submission file saved.')\n\n# (Optional) Evaluation on a subset of the training data if CFG.debug is True\n# You'll need to add your evaluation logic here","metadata":{"execution":{"iopub.status.busy":"2024-05-11T22:15:18.327601Z","iopub.execute_input":"2024-05-11T22:15:18.327994Z"},"trusted":true},"execution_count":null,"outputs":[]}]}