{"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":"markdown","source":"This notebook is a baseline, important things like `StratifiedGroupKFold` and other techniques necessary for a \"good\" model were not used.\n\nThe only purpose is to check if a basic CNN model can learn anything, then we can work further on finetuning.","metadata":{}},{"cell_type":"code","source":"# try:\n#     !pip install -q torchinfo\n# except:\n#     pass","metadata":{"_kg_hide-output":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:43.956887Z","iopub.execute_input":"2023-01-04T13:19:43.95726Z","iopub.status.idle":"2023-01-04T13:19:43.961846Z","shell.execute_reply.started":"2023-01-04T13:19:43.957228Z","shell.execute_reply":"2023-01-04T13:19:43.960676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# To read compressed dicom files:\n\n'''Online'''\n#! pip install python-gdcm\n#! pip install pylibjpeg pylibjpeg-libjpeg pydicom\n\n'''Offline (need to add dataset --> for-pydicom)'''\n!pip install -qU ../input/for-pydicom/python_gdcm-3.0.14-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl ../input/for-pydicom/pylibjpeg-1.4.0-py3-none-any.whl --find-links frozen_packages --no-index","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:43.973505Z","iopub.execute_input":"2023-01-04T13:19:43.973909Z","iopub.status.idle":"2023-01-04T13:19:53.596428Z","shell.execute_reply.started":"2023-01-04T13:19:43.973864Z","shell.execute_reply":"2023-01-04T13:19:53.595257Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport cv2\nimport inspect\nimport logging\nimport os\nimport random\nfrom sklearn import model_selection\nimport sys\nimport uuid\nimport zipfile\nimport copy\nfrom pathlib import Path, PurePath\nfrom typing import Any, Dict, List, Optional, Tuple, Union\nimport gdcm\nimport pydicom\nimport sys\nsys.path.append('/kaggle/input/timm-0-6-9/pytorch-image-models-master')\nimport glob\nimport timm\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport requests\nimport torch\nimport torchvision.transforms.functional as F\nfrom tqdm import tqdm\nimport timm\nfrom joblib import Parallel, delayed\nfrom torchmetrics import AUROC, Accuracy, MetricCollection, Precision, Recall","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:53.599725Z","iopub.execute_input":"2023-01-04T13:19:53.600342Z","iopub.status.idle":"2023-01-04T13:19:53.610074Z","shell.execute_reply.started":"2023-01-04T13:19:53.600299Z","shell.execute_reply":"2023-01-04T13:19:53.608868Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SAVE_FOLDER = \"/kaggle/tmp/output/\"\nos.makedirs(SAVE_FOLDER, exist_ok=True)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.61187Z","iopub.execute_input":"2023-01-04T13:19:53.612405Z","iopub.status.idle":"2023-01-04T13:19:53.620353Z","shell.execute_reply.started":"2023-01-04T13:19:53.612319Z","shell.execute_reply":"2023-01-04T13:19:53.619341Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_images = glob.glob(\"/kaggle/input/rsna-breast-cancer-detection/test_images/*/*.dcm\")","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.623338Z","iopub.execute_input":"2023-01-04T13:19:53.623741Z","iopub.status.idle":"2023-01-04T13:19:53.631969Z","shell.execute_reply.started":"2023-01-04T13:19:53.623662Z","shell.execute_reply":"2023-01-04T13:19:53.630993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_dicom(\n    path: str, image_size: int, save_folder: str = \"\", extension: str = \"png\"\n) -> None:\n    patient = path.split(\"/\")[-2]\n    image = path.split(\"/\")[-1][:-4]\n\n    dicom = pydicom.dcmread(path)\n    img = dicom.pixel_array\n\n    img = (img - img.min()) / (img.max() - img.min())\n\n    if dicom.PhotometricInterpretation == \"MONOCHROME1\":\n        img = 1 - img\n\n    img = cv2.resize(img, (image_size, image_size))\n\n    cv2.imwrite(\n        save_folder + f\"{patient}_{image}.{extension}\", (img * 255).astype(np.uint8)\n    )","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.633663Z","iopub.execute_input":"2023-01-04T13:19:53.634713Z","iopub.status.idle":"2023-01-04T13:19:53.642582Z","shell.execute_reply.started":"2023-01-04T13:19:53.634677Z","shell.execute_reply":"2023-01-04T13:19:53.641545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Utils","metadata":{}},{"cell_type":"code","source":"def return_filepath(\n    image_id: str,\n    folder: Path,\n    extension: str = \"jpg\",\n) -> str:\n    \"\"\"Add a new column image_path to the train and test csv.\n\n    Note:\n        We can call the images easily in __getitem__ in Dataset.\n        If the image_id has extension already, then there is no need to add the extension.\n\n    Args:\n        image_id (str): The unique image id: 1000015157.jpg\n        folder (Path): The data folder.\n        extension (str): The extension of the image. Defaults to \"jpg\".\n\n    Returns:\n        image_path (str): The path to the image.\n    \"\"\"\n    image_path = Path.joinpath(folder, f\"{image_id}.{extension}\").as_posix()\n    return image_path\n\n\ndef generate_uuid4() -> str:\n    \"\"\"Generate a random UUID4.\n\n    Returns:\n        (str): Random UUID4\n    \"\"\"\n    return str(uuid.uuid4())\n\ndef return_list_of_files(\n    directory: Union[str, Path],\n    extensions: Optional[List[str]] = None,\n    return_string: bool = True,\n) -> Union[List[str], List[Path]]:\n    \"\"\"Returns a list of files in a directory based on extensions.\n    If extensions is None, all files are returned.\n\n    Note:\n        all_image_extensions = [\".jpg\", \".jpeg\", \".png\", \".ppm\", \".bmp\", \".pgm\", \".tif\"]\n\n    Args:\n        directory (Union[str, Path]): The directory to search.\n        extensions (Optional[List[str]]): The extension of the files to search for.\n            Defaults to None.\n        return_string (bool): Whether to return a list of strings or Paths.\n            Defaults to True.\n\n    Returns:\n        List[str, Path]: List of files in the directory.\n    \"\"\"\n    if isinstance(directory, str):\n        directory = Path(directory)\n\n    if extensions is None and return_string:\n        return [f.as_posix() for f in directory.resolve().glob(\"**/*\") if f.is_file()]\n\n    if extensions is None and not return_string:\n        return [f for f in directory.resolve().glob(\"**/*\") if f.is_file()]\n\n    if return_string:\n        list_of_files = sorted(\n            [\n                path.as_posix()\n                for path in filter(\n                    lambda path: path.suffix in extensions, directory.glob(\"**/*\")\n                )\n            ]\n        )\n    else:\n        list_of_files = sorted(\n            filter(lambda path: path.suffix in extensions, directory.glob(\"**/*\"))\n        )\n    return list_of_files\n\ndef seed_all(seed: int = 1992) -> None:\n    \"\"\"Seed all random number generators.\"\"\"\n    print(f\"Using Seed Number {seed}\")\n\n    # set PYTHONHASHSEED env var at fixed value\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n    torch.cuda.manual_seed(seed)  # pytorch (both CPU and CUDA)\n    np.random.seed(seed)  # for numpy pseudo-random generator\n    # set fixed value for python built-in pseudo-random generator\n    random.seed(seed)\n    torch.backends.cudnn.deterministic = True\n    torch.backends.cudnn.benchmark = False\n    torch.backends.cudnn.enabled = False","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.6442Z","iopub.execute_input":"2023-01-04T13:19:53.644828Z","iopub.status.idle":"2023-01-04T13:19:53.659193Z","shell.execute_reply.started":"2023-01-04T13:19:53.644789Z","shell.execute_reply":"2023-01-04T13:19:53.658256Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"seed_all(1992)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.660548Z","iopub.execute_input":"2023-01-04T13:19:53.661365Z","iopub.status.idle":"2023-01-04T13:19:53.675519Z","shell.execute_reply.started":"2023-01-04T13:19:53.661331Z","shell.execute_reply":"2023-01-04T13:19:53.674489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Config","metadata":{}},{"cell_type":"code","source":"class config:\n    BASE_DIR = \"/kaggle\"\n    DATA_DIR = Path(BASE_DIR, \"input/rsna-breast-cancer-detection\")\n    LOGS_DIR = \"\"\n    MODEL_ARTIFACTS = \"\"","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:53.677652Z","iopub.execute_input":"2023-01-04T13:19:53.67812Z","iopub.status.idle":"2023-01-04T13:19:53.684034Z","shell.execute_reply.started":"2023-01-04T13:19:53.678094Z","shell.execute_reply":"2023-01-04T13:19:53.683054Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"Base class for params class.\n\"\"\"\nfrom dataclasses import dataclass, asdict, field\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, Any\nimport albumentations as A\nimport torchvision.transforms as T\n\n@dataclass\nclass DataConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    root_dir: Path\n    train_dir: Path\n    valid_dir: Optional[Path] = field(default=None)\n    test_dir: Optional[Path] = field(default=None)\n\n    url: Optional[str] = field(default=None)\n\n\n@dataclass\nclass ResamplingConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    resample_strategy: str\n    resample_params: Dict[str, Any]\n\n\n@dataclass\nclass DataModuleConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    debug: bool\n    num_debug_samples: int\n\n    train_loader: Dict[str, Any]\n    valid_loader: Dict[str, Any]\n    test_loader: Dict[str, Any]\n\n\n@dataclass\nclass TransformConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    image_size: int\n\n    mean: Optional[List[float]] = field(default=None)\n    std: Optional[List[float]] = field(default=None)\n\n    mixup: Optional[bool] = field(default=False)\n    mixup_params: Optional[Dict[str, Any]] = field(default=None)\n\n    cutmix: Optional[bool] = field(default=False)\n    cutmix_params: Optional[Dict[str, Any]] = field(default=None)\n\n    train_transforms: Union[T.Compose, A.Compose] = field(init=False, default=None)\n    valid_transforms: Union[T.Compose, A.Compose] = field(init=False, default=None)\n    test_transforms: Union[T.Compose, A.Compose] = field(init=False, default=None)\n\n\n@dataclass\nclass ModelConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    adapter: str\n    model_name: str\n    num_classes: int\n    pretrained: bool\n    model_params: Optional[Dict[str, Any]] = field(default=None)\n\n\n@dataclass\nclass OptimizerConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    optimizer: str\n    optimizer_params: Dict[str, Any]\n\n\n@dataclass\nclass SchedulerConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    scheduler: str\n    scheduler_params: Dict[str, Any]\n\n\n@dataclass\nclass CriterionConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    train_criterion: str\n    valid_criterion: str\n    train_criterion_params: Dict[str, Any]\n    valid_criterion_params: Dict[str, Any]\n\n\n@dataclass\nclass CallbackConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    callbacks: List[str]\n    # callbacks_params: Dict[str, Any]\n\n\n@dataclass\nclass TrainConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    epochs: int\n    classification_type: str\n    monitored_metric: Dict[str, Any]\n    use_amp: bool = field(default=False)\n    patience: float = field(default=float(\"inf\"))\n\n\n@dataclass\nclass StoresConfig(ABC):\n    \"\"\"Abstract Base Class.\"\"\"\n\n    project_name: str\n    unique_id: str = field(init=False, default_factory=generate_uuid4)\n    logs_dir: Path = field(init=False)\n    model_artifacts_dir: Path = field(init=False)\n\n    def __post_init__(self) -> None:\n        \"\"\"Initialize the store config.\"\"\"\n        self.logs_dir = Path(config.LOGS_DIR) / self.project_name / self.unique_id\n        self.model_artifacts_dir = (\n            Path(config.MODEL_ARTIFACTS) / self.project_name / self.unique_id\n        )\n\n        self.logs_dir.mkdir(parents=True, exist_ok=True)\n        self.model_artifacts_dir.mkdir(parents=True, exist_ok=True)\n\n@dataclass(frozen=False, init=True)\nclass AbstractPipelineConfig(ABC):\n    \"\"\"The pipeline configuration class.\"\"\"\n\n    data: DataConfig\n    resample: ResamplingConfig\n    datamodule: DataModuleConfig\n    transforms: TransformConfig\n    model: ModelConfig\n    optimizer_params: OptimizerConfig\n    scheduler_params: SchedulerConfig\n    criterion_params: CriterionConfig\n    callback_params: CallbackConfig\n    global_train_params: TrainConfig\n    stores: StoresConfig\n\n    device: str = field(init=False, repr=False, default=\"cpu\")\n    os: str = sys.platform\n    all_params: Dict[str, Any] = field(default_factory=dict)\n\n    def to_dict(self) -> Dict[str, Any]:\n        \"\"\"Recursively convert dataclass obj as dict.\"\"\"\n        return asdict(self)\n\n    def set_device(self) -> str:\n        \"\"\"Get the device.\"\"\"\n        if self.os == \"darwin\":\n            self.device = \"mps\" if torch.backends.mps.is_available() else \"cpu\"\n        else:\n            self.device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n\n    def get_all_params(self) -> Dict[str, Any]:\n        \"\"\"Get all params.\"\"\"\n        return self.to_dict()\n\n    def __post_init__(self) -> None:\n        \"\"\"Initialize the pipeline config.\n        Currently only initializes the device.\"\"\"\n        self.set_device()  # assign device\n        self.all_params = self.get_all_params()\n\n    def __str__(self) -> str:\n        \"\"\"Get the string representation of the pipeline config.\"\"\"\n        return pprint.pformat(self.all_params, depth=4)\n\n    @property\n    @abstractmethod\n    def seed(self) -> int:\n        \"\"\"The seed for the experiment.\"\"\"\n\n\n\n@dataclass(frozen=False, init=True)\nclass PipelineConfig(AbstractPipelineConfig):\n    \"\"\"The pipeline configuration class.\"\"\"","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:53.685648Z","iopub.execute_input":"2023-01-04T13:19:53.686615Z","iopub.status.idle":"2023-01-04T13:19:53.720772Z","shell.execute_reply.started":"2023-01-04T13:19:53.686581Z","shell.execute_reply":"2023-01-04T13:19:53.719651Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"RSNA Brast Cancer 2022 configurations.\"\"\"\nfrom dataclasses import dataclass, field\nfrom pathlib import Path\nfrom typing import Any, Dict, List, Optional, Union\nimport torchvision.transforms as T\nimport torch\n\n@dataclass(frozen=False, init=True)\nclass Data:\n    \"\"\"Class for data related params.\"\"\"\n\n    root_dir: Optional[Path] = config.DATA_DIR  # data/\n    url: Optional[str] = \"\"\n    blob_file: Optional[str] = \"\"\n    train_csv: Union[str, Path] = field(init=False)\n    test_csv: Union[str, Path] = field(init=False)\n    train_dir: Union[str, Path] = field(init=False)\n    test_dir: Union[str, Path] = field(init=False)\n    data_dir: Union[str, Path] = field(init=False)\n    image_col_name: str = \"image_id_final\"\n    image_path_col_name: str = \"image_path\"\n    group_by: str = \"patient_id\"\n    stratify_by: Optional[str] = \"cancer\"\n    target_col_name: str = \"cancer\"\n    image_extension: str = \"png\"\n    class_name_to_id: Optional[Dict[str, int]] = field(\n        default_factory=lambda: {\"benign\": 0, \"malignant\": 1}\n    )\n    class_id_to_name: Optional[Dict[int, str]] = field(init=False)\n\n    def __post_init__(self) -> None:\n        \"\"\"Post init method for dataclass.\"\"\"\n        self.data_dir = Path(config.DATA_DIR)  # / \"rsna_breast_cancer\"\n        self.train_dir = Path(\"/kaggle/input/rsna-breast-cancer-256-pngs\")\n        self.train_csv = \"/kaggle/input/rsna-breast-cancer-detection/train.csv\"\n        self.test_dir = Path(\"/kaggle/input/rsna-breast-cancer-detection/test_images\")\n        self.test_csv = Path(\"/kaggle/input/rsna-breast-cancer-detection/test.csv\")\n        self.class_id_to_name = {v: k for k, v in self.class_name_to_id.items()}\n\n    @property\n    def download(self) -> bool:\n        \"\"\"Return True if data is not downloaded.\"\"\"\n        return not Path(self.data_dir).exists()\n\n\n@dataclass(frozen=False, init=True)\nclass Resampling:\n    \"\"\"Class for cross validation.\"\"\"\n\n    # scikit-learn resampling strategy\n    resample_strategy: str = \"StratifiedGroupKFold\"  # same name as in scikit-learn\n    resample_params: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"n_splits\": 4,\n            \"random_state\": 42,\n            \"shuffle\": True,\n        }\n    )\n\n\n@dataclass(frozen=False, init=True)\nclass DataModuleParams:\n    \"\"\"Class to keep track of the data loader parameters.\"\"\"\n\n    debug: bool = False\n    num_debug_samples: int = 128\n\n    test_loader: Optional[Dict[str, Any]] = field(\n        default_factory=lambda: {\n            \"batch_size\": 32,\n            \"num_workers\": 0,\n            \"pin_memory\": True,\n            \"drop_last\": False,\n            \"shuffle\": False,\n            \"collate_fn\": None,\n        }\n    )\n\n    debug_loader: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"batch_size\": 4,\n            \"num_workers\": 0,\n            \"pin_memory\": True,\n            \"drop_last\": False,\n            \"shuffle\": False,\n            \"collate_fn\": None,\n        }\n    )\n\n    train_loader: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"batch_size\": 32,\n            \"num_workers\": 2,\n            \"pin_memory\": True,\n            \"drop_last\": False,\n            \"shuffle\": True,\n            \"collate_fn\": None,\n        }\n    )\n    valid_loader: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"batch_size\": 32,\n            \"num_workers\": 2,\n            \"pin_memory\": True,\n            \"drop_last\": False,\n            \"shuffle\": False,\n            \"collate_fn\": None,\n        }\n    )\n\n\n@dataclass(frozen=False, init=True)\nclass AugmentationParams:\n    \"\"\"Class to keep track of the augmentation parameters.\"\"\"\n\n    image_size: int = 256\n    mean: List[float] = field(default_factory=lambda: [0.5, 0.5, 0.5])\n    std: List[float] = field(default_factory=lambda: [0.5, 0.5, 0.5])\n\n    mixup: bool = False\n    mixup_params: Optional[Dict[str, Any]] = None\n\n    train_transforms: Optional[T.Compose] = field(init=False, default=None)\n    valid_transforms: Optional[T.Compose] = field(init=False, default=None)\n    test_transforms: Optional[T.Compose] = field(init=False, default=None)\n    debug_transforms: Optional[T.Compose] = field(init=False, default=None)\n\n    def __post_init__(self) -> None:\n        \"\"\"Post init method for dataclass.\"\"\"\n        self.train_transforms = T.Compose(\n            [\n                T.ToPILImage(),\n                T.RandomResizedCrop(self.image_size),\n                T.RandomVerticalFlip(p=0.5),\n                T.RandomHorizontalFlip(p=0.5),\n                T.RandomRotation(degrees=45),\n                T.ToTensor(),\n                T.Normalize(self.mean, self.std),\n            ]\n        )\n        self.valid_transforms = T.Compose(\n            [\n                T.ToPILImage(),\n                T.Resize(self.image_size),\n                T.ToTensor(),\n                T.Normalize(self.mean, self.std),\n            ]\n        )\n        self.test_transforms = T.Compose(\n            [\n                T.ToPILImage(),\n                T.Resize(self.image_size),\n                T.ToTensor(),\n                T.Normalize(self.mean, self.std),\n            ]\n        )\n\n\n@dataclass(frozen=False, init=True)\nclass ModelParams:\n    \"\"\"Class to keep track of the model parameters.\"\"\"\n\n    adapter: str = \"timm\"\n    model_name: str = \"resnet50\"\n    pretrained: bool = False\n    num_classes: int = 2\n    # dropout: float = 0.3  # 0.5\n\n\n@dataclass(frozen=False, init=True)\nclass Stores:\n    \"\"\"A class to keep track of model artifacts.\"\"\"\n\n    project_name: str = \"RSNA-Breast-Cancer-Detection-2022\"\n    unique_id: str = field(default_factory=generate_uuid4)\n    logs_dir: Path = field(init=False)\n    model_artifacts_dir: Path = field(init=False)\n\n    def __post_init__(self) -> None:\n        \"\"\"Create the logs directory.\"\"\"\n        self.logs_dir = (\n            Path(\"/kaggle/working\") / \"logging\" / self.project_name / self.unique_id\n        )\n        self.logs_dir.mkdir(parents=True, exist_ok=True)\n\n        self.model_artifacts_dir = (\n            Path(\"/kaggle/working\") / \"artifacts\" / self.project_name / self.unique_id\n        )\n        self.model_artifacts_dir.mkdir(parents=True, exist_ok=True)\n\n\n@dataclass(frozen=False, init=True)\nclass CriterionParams:\n    \"\"\"A class to track loss function parameters.\"\"\"\n\n    train_criterion: str = \"CrossEntropyLoss\"\n    valid_criterion: str = \"CrossEntropyLoss\"\n    train_criterion_params: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"weight\": None,\n            \"size_average\": None,\n            \"ignore_index\": -100,\n            \"reduce\": None,\n            \"reduction\": \"mean\",\n            \"label_smoothing\": 0.0,\n        }\n    )\n    valid_criterion_params: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"weight\": None,\n            \"size_average\": None,\n            \"ignore_index\": -100,\n            \"reduce\": None,\n            \"reduction\": \"mean\",\n            \"label_smoothing\": 0.0,\n        }\n    )\n\n\n@dataclass(frozen=False, init=True)\nclass OptimizerParams:\n    \"\"\"A class to track optimizer parameters.\"\"\"\n\n    # batch size increase 2, lr increases a factor of 2 as well.\n    optimizer: str = \"AdamW\"\n    optimizer_params: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"lr\": 3e-4,  # 32 -> 1e-4\n            \"betas\": (0.9, 0.999),\n            \"amsgrad\": False,\n            \"weight_decay\": 1e-6,\n            \"eps\": 1e-08,\n            # \"warmup_prop\": 0.1,\n        }\n    )\n\n\n@dataclass(frozen=False, init=True)\nclass SchedulerParams:\n    \"\"\"A class to track Scheduler Params.\"\"\"\n\n    scheduler: str = \"CosineAnnealingWarmRestarts\"  # Debug\n    scheduler_params: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"T_0\": 10,\n            \"T_mult\": 1,\n            \"eta_min\": 1e-6,\n            \"last_epoch\": -1,\n            \"verbose\": False,\n        }\n    )\n\n    def __post_init__(self) -> None:\n        \"\"\"Initialize the scheduler params based on some choices given.\"\"\"\n\n\n@dataclass(frozen=False, init=True)\nclass GlobalTrainParams:\n    \"\"\"Train params, a lot of overlapping.\n    FIXME: overlapping with other params.\n    \"\"\"\n\n    debug: bool = False\n    debug_multiplier: int = 128\n    epochs: int = 10  # 10 when not debug\n    use_amp: bool = True\n    patience: int = 100\n    classification_type: str = \"multiclass\"\n    monitored_metric: Dict[str, Any] = field(\n        default_factory=lambda: {\n            \"monitor\": \"val_AUROC\",\n            \"mode\": \"max\",\n        }\n    )\n\n\n@dataclass(frozen=False, init=True)\nclass CallbackParams:\n    \"\"\"Callback params.\"\"\"\n\n#     callbacks: List[Callback] = field(\n#         default_factory=lambda: [\n#             History(),\n#             MetricMeter(),\n#             ModelCheckpoint(mode=\"max\", monitor=\"val_AUROC\"),\n#             EarlyStopping(mode=\"max\", monitor=\"val_AUROC\", patience=100),\n#         ]\n#     )\n\n\n@dataclass(frozen=False, init=True)\nclass PipelineConfig(AbstractPipelineConfig):\n    \"\"\"The pipeline configuration class.\"\"\"\n\n    data: DataConfig = Data()\n    resample: ResamplingConfig = Resampling()\n    datamodule: DataModuleConfig = DataModuleParams()\n    transforms: TransformConfig = AugmentationParams()\n    model: ModelConfig = ModelParams()\n    stores: StoresConfig = Stores()\n    global_train_params: TrainConfig = GlobalTrainParams()\n    optimizer_params: OptimizerConfig = OptimizerParams()\n    scheduler_params: SchedulerConfig = SchedulerParams()\n    criterion_params: CriterionConfig = CriterionParams()\n    callback_params: CallbackConfig = CallbackParams()\n\n    device: str = field(init=False)\n    seed: int = 1992\n    all_params: Dict[str, Any] = field(default_factory=dict)\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:53.726155Z","iopub.execute_input":"2023-01-04T13:19:53.727654Z","iopub.status.idle":"2023-01-04T13:19:53.771782Z","shell.execute_reply.started":"2023-01-04T13:19:53.727618Z","shell.execute_reply":"2023-01-04T13:19:53.770878Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pipeline_config = PipelineConfig()","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.773223Z","iopub.execute_input":"2023-01-04T13:19:53.773912Z","iopub.status.idle":"2023-01-04T13:19:53.782485Z","shell.execute_reply.started":"2023-01-04T13:19:53.773877Z","shell.execute_reply":"2023-01-04T13:19:53.781618Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"_ = Parallel(n_jobs=2)(\n    delayed(load_dicom)(\n        image,\n        image_size=pipeline_config.transforms.image_size,\n        save_folder=SAVE_FOLDER,\n        extension=pipeline_config.data.image_extension,\n    )\n    for image in tqdm(test_images)\n)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:53.784093Z","iopub.execute_input":"2023-01-04T13:19:53.784452Z","iopub.status.idle":"2023-01-04T13:19:57.552232Z","shell.execute_reply.started":"2023-01-04T13:19:53.784418Z","shell.execute_reply":"2023-01-04T13:19:57.551062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Transforms","metadata":{}},{"cell_type":"code","source":"\"\"\"Transforms for data augmentation.\"\"\"\n\nfrom abc import ABC, abstractmethod\nfrom typing import Any\n\nimport torchvision.transforms as T\n\n\nclass Transforms(ABC):\n    \"\"\"Create a Transforms class that can take in albumentations\n    and torchvision transforms.\n    \"\"\"\n\n    def __init__(self, pipeline_config: PipelineConfig) -> None:\n        self.pipeline_config = pipeline_config\n\n    @property\n    @abstractmethod\n    def train_transforms(self):\n        \"\"\"Get the training transforms.\"\"\"\n\n    @property\n    @abstractmethod\n    def valid_transforms(self):\n        \"\"\"Get the validation transforms.\"\"\"\n\n    @property\n    def test_transforms(self):\n        \"\"\"Get the test transforms.\"\"\"\n\n    @property\n    def gradcam_transforms(self):\n        \"\"\"Get the gradcam transforms.\"\"\"\n\n    @property\n    def debug_transforms(self):\n        \"\"\"Get the debug transforms.\"\"\"\n\n    @property\n    def test_time_augmentations(self):\n        \"\"\"Get the test time augmentations.\"\"\"\n\n    def __call__(self, *args: Any, **kwargs: Any) -> Any:\n        \"\"\"Call the transforms.\"\"\"\n\n\nclass ImageClassificationTransforms(Transforms):\n    \"\"\"General Image Classification Transforms.\"\"\"\n\n    def __init__(self, pipeline_config: PipelineConfig) -> None:\n        super().__init__(pipeline_config)\n        self.pipeline_config = pipeline_config\n\n    @property\n    def train_transforms(self) -> T.Compose:\n        return self.pipeline_config.transforms.train_transforms\n\n    @property\n    def valid_transforms(self) -> T.Compose:\n        return self.pipeline_config.transforms.valid_transforms\n\n    @property\n    def test_transforms(self):\n        return self.pipeline_config.transforms.test_transforms\n\n    @property\n    def debug_transforms(self) -> T.Compose:\n        return self.pipeline_config.transforms.debug_transforms","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:19:57.55452Z","iopub.execute_input":"2023-01-04T13:19:57.554899Z","iopub.status.idle":"2023-01-04T13:19:57.566364Z","shell.execute_reply.started":"2023-01-04T13:19:57.554858Z","shell.execute_reply":"2023-01-04T13:19:57.565444Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Dataset","metadata":{}},{"cell_type":"code","source":"from __future__ import annotations\n\nfrom abc import ABC, abstractmethod\nfrom pathlib import Path\nfrom typing import Optional, Union\n\nimport albumentations as A\nimport cv2\nimport pandas as pd\nimport torch\nimport torchvision\nimport torchvision.transforms as T\nfrom sklearn.model_selection import train_test_split\nfrom torch.utils.data import DataLoader, Dataset, Subset\n\nTransformTypes = Optional[Union[A.Compose, T.Compose]]\n\n# pylint: disable=invalid-name\nclass ImageClassificationDataset(Dataset):\n    \"\"\"A sample template for Image Classification Dataset.\"\"\"\n\n    def __init__(\n        self,\n        pipeline_config: PipelineConfig,\n        df: Optional[pd.DataFrame] = None,\n        path: Optional[Union[str, Path]] = None,\n        transforms: TransformTypes = None,\n        stage: str = \"train\",\n        **kwargs,\n    ) -> None:\n        \"\"\"Constructor for the dataset class.\n\n        Args:\n            df (pd.DataFrame): Dataframe for either train, valid or test.\n                This holds the image infos such as image path, image id, target etc.\n            path (str): Path to the directory where the images are stored.\n            transforms (TransformTypes): torchvision/albumentations transforms\n                to apply to the images.\n            stage (str): Defaults to \"train\". One of ['train', 'valid', 'test', 'gradcam']\n        \"\"\"\n        super().__init__(**kwargs)\n        self.image_path = df[pipeline_config.data.image_path_col_name].values\n        self.image_ids = df[pipeline_config.data.image_col_name].values\n        self.targets = (\n            df[pipeline_config.data.target_col_name].values if stage != \"test\" else None\n        )\n        self.df = df\n        self.transforms = transforms\n        self.stage = stage\n        self.path = path\n        self.pipeline_config = pipeline_config\n\n    def __len__(self) -> int:\n        \"\"\"Return the length of the dataset.\"\"\"\n        return len(self.df)\n\n    def check_correct_dtype(self, X: torch.Tensor, y: torch.Tensor) -> None:\n        \"\"\"Check if the datatypes of X and y are correct. This is important as\n        the loss function expects a certain datatype. See my repo/src/dataset.py.\"\"\"\n        raise NotImplementedError\n\n    def check_correct_shape(self) -> None:\n        \"\"\"Check the shapes of X and y, for eg channels first.\"\"\"\n        raise NotImplementedError\n\n    def apply_image_transforms(\n        self, image: torch.Tensor, dtype: torch.dtype = torch.float32\n    ) -> torch.Tensor:\n        \"\"\"Apply transforms to the image.\"\"\"\n        if self.transforms and isinstance(self.transforms, A.Compose):\n            image = self.transforms(image=image)[\"image\"]\n        elif self.transforms and isinstance(self.transforms, T.Compose):\n            image = self.transforms(image)\n        else:\n            image = torch.from_numpy(image).permute(2, 0, 1)  # float32\n        return torch.tensor(image, dtype=dtype)\n\n    # pylint: disable=no-self-use # not yet!\n    def apply_target_transforms(\n        self, target: torch.Tensor, dtype: torch.dtype = torch.long\n    ) -> torch.Tensor:\n        \"\"\"Apply transforms to the target.\n        This is useful for tasks such as segmentation object detection\n        where targets are in the form of bounding boxes, segmentation masks etc.\n        \"\"\"\n        return torch.tensor(target, dtype=dtype)\n\n    \n    def __getitem__(\n        self, index: int\n    ) -> Union[torch.FloatTensor, Union[torch.FloatTensor, torch.LongTensor]]:\n        \"\"\"Implements the getitem method.\n\n        Note:\n            The following target dtype is expected:\n            - BCEWithLogitsLoss expects a target.float()\n            - CrossEntropyLoss expects a target.long()\n\n        Args:\n            index (int): index of the dataset.\n\n        Returns:\n            image (torch.FloatTensor): The image tensor.\n            target (Union[torch.FloatTensor, torch.LongTensor]]): The target tensor.\n        \"\"\"\n        image_path = self.image_path[index]\n        # image = self.load_dicom(image_path, image_size = self.pipeline_config.augmentation.image_size)\n        image = cv2.imread(image_path)\n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n        image = self.apply_image_transforms(image)\n\n        # Get target for all modes except for test dataset.\n        # If test, replace target with dummy ones as placeholder.\n        target = self.targets[index] if self.stage != \"test\" else torch.ones(1)\n        target = self.apply_target_transforms(target)\n\n        if self.stage in [\"train\", \"valid\", \"debug\"]:\n            return image, target\n        elif self.stage == \"test\":\n            return image\n        elif self.stage == \"gradcam\":\n            # get image id as well to show on matplotlib image!\n            return image, target, self.image_ids[index]\n        else:\n            raise ValueError(f\"Invalid stage {self.stage}.\")\n\n\n\nclass CustomizedDataModule(ABC):\n    \"\"\"Base class for custom data module.\"\"\"\n\n    def __init__(self, pipeline_config: Optional[PipelineConfig] = None) -> None:\n        self.pipeline_config = pipeline_config\n\n    def prepare_data(self) -> None:\n        \"\"\"See docstring in PyTorch Lightning.\"\"\"\n        # download data here\n\n    @abstractmethod\n    def setup(self, stage: str) -> None:\n        \"\"\"See docstring in PyTorch Lightning.\n\n        Example:\n            if stage == \"fit\":\n                # assign train and valid datasets for use in dataloaders\n                pass\n\n            if stage == \"test\":\n                # assign test dataset for use in dataloaders\n                pass\n\n            if stage == \"debug\":\n                # assign debug dataset for use in dataloaders\n                pass\n        \"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def train_dataloader(self) -> DataLoader:\n        \"\"\"Train dataloader\"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def valid_dataloader(self) -> DataLoader:\n        \"\"\"Validation dataloader\"\"\"\n        raise NotImplementedError\n\n    def test_dataloader(self) -> DataLoader:\n        \"\"\"Test dataloader\"\"\"\n\n    def debug_train_dataloader(self) -> DataLoader:\n        \"\"\"Debug dataloader\"\"\"\n\n    def debug_valid_dataloader(self) -> DataLoader:\n        \"\"\"Debug dataloader\"\"\"\n\nclass RSNABreastDataModule(CustomizedDataModule):\n    \"\"\"Data module for RSBA Breast image classification dataset.\"\"\"\n\n    def __init__(self, pipeline_config: Optional[PipelineConfig] = None) -> None:\n        super().__init__(pipeline_config)\n        self.pipeline_config = pipeline_config\n        self.transforms = ImageClassificationTransforms(pipeline_config)\n\n    def cross_validation_split(\n        self, df: pd.DataFrame, fold: Optional[int] = None\n    ) -> Tuple[pd.DataFrame, pd.DataFrame]:\n        \"\"\"Split the dataframe into train and validation dataframes.\"\"\"\n        resample_strategy = self.pipeline_config.resample.resample_strategy\n        resample_params = self.pipeline_config.resample.resample_params\n        group_by = self.pipeline_config.data.group_by\n        stratify_by = self.pipeline_config.data.stratify_by\n        stratify = df[stratify_by].values if stratify_by else None\n        groups = df[group_by].values if group_by else None\n\n        if resample_strategy == \"train_test_split\":\n    \n            train_df, valid_df = getattr(model_selection, resample_strategy)(\n                df,\n                # FIXME: stratify is hard coded for now\n                stratify=df[self.pipeline_config.data.target_col_name],\n                **resample_params,\n            )\n        else:\n            \n            try:\n                cv = getattr(model_selection, resample_strategy)(**resample_params)\n            except AttributeError as attr_err:\n                raise ValueError(\n                    f\"{resample_strategy} is not a valid resample strategy.\"\n                ) from attr_err\n            except TypeError as type_err:\n                raise ValueError(\n                    f\"Invalid resample params for {resample_strategy}.\"\n                ) from type_err\n\n            for _fold, (_train_idx, valid_idx) in enumerate(\n                cv.split(df, stratify, groups)\n            ):\n                df.loc[valid_idx, \"fold\"] = _fold + 1\n            df[\"fold\"] = df[\"fold\"].astype(int)\n\n            print(\n                df.groupby([\"fold\", self.pipeline_config.data.target_col_name]).size()\n            )\n            train_df = df[df.fold != fold].reset_index(drop=True)\n            valid_df = df[df.fold == fold].reset_index(drop=True)\n            print(\"fold\", fold, \"train\", train_df.shape, \"valid\", valid_df.shape)\n            \n        return train_df, valid_df\n\n    def prepare_data(self, fold: Optional[int] = None) -> None:\n        url = self.pipeline_config.data.url\n        blob_file = self.pipeline_config.data.blob_file\n        root_dir = self.pipeline_config.data.root_dir\n        train_dir = self.pipeline_config.data.train_dir\n        test_dir = self.pipeline_config.data.test_dir\n\n        if self.pipeline_config.data.download:\n            download_to(url, blob_file, root_dir)\n            extract_file(root_dir, blob_file)\n\n        train_images = return_list_of_files(\n            train_dir, extensions=[\".jpg\", \".png\", \".jpeg\"], return_string=False\n        )\n        test_images = return_list_of_files(\n            test_dir, extensions=[\".jpg\", \".png\", \".jpeg\"], return_string=False\n        )\n        print(f\"Total number of images: {len(train_images)}\")\n        print(f\"Total number of test images: {len(test_images)}\")\n\n        df = pd.read_csv(self.pipeline_config.data.train_csv)\n        df[\"image_id_final\"] = (\n            df[\"patient_id\"].astype(str) + \"_\" + df[\"image_id\"].astype(str)\n        )\n        df[\"image_path\"] = df[self.pipeline_config.data.image_col_name].apply(\n            lambda x: return_filepath(\n                image_id=x,\n                folder=train_dir,\n                extension=self.pipeline_config.data.image_extension,\n            )\n        )\n        df.to_csv(\"/kaggle/working/df.csv\", index=False)\n        print(df.head())\n\n        self.train_df, self.valid_df = self.cross_validation_split(df, fold)\n        self.oof_df = self.valid_df.copy()\n        if self.pipeline_config.datamodule.debug:\n            num_debug_samples = self.pipeline_config.datamodule.num_debug_samples\n            print(f\"Debug mode is on, using {num_debug_samples} images for training.\")\n            self.train_df = self.train_df.sample(num_debug_samples)\n            self.valid_df = self.valid_df.sample(num_debug_samples)\n            self.oof_df = self.valid_df.copy()\n\n        # hardcode\n        test_df = pd.read_csv(self.pipeline_config.data.test_csv)\n        test_df[\"image_id_final\"] = (\n            test_df[\"patient_id\"].astype(str) + \"/\" + test_df[\"image_id\"].astype(str)\n        )\n#         test_df[\"image_path\"] = test_df[self.pipeline_config.data.image_col_name].apply(\n#             lambda x: return_filepath(\n#                 image_id=x,\n#                 folder=test_dir,\n#                 extension=\".dcm\",\n#             )\n#         )\n        test_df[\"image_path\"] = SAVE_FOLDER + test_df[\"patient_id\"].astype(str) + \"_\" + test_df[\"image_id\"].astype(str) + \".png\"\n        self.test_df = test_df\n        # hardcode\n\n    def setup(self, stage: str) -> None:\n        \"\"\"Assign train/val datasets for use in dataloaders.\"\"\"\n        if stage == \"fit\":\n            train_transforms = self.transforms.train_transforms\n            valid_transforms = self.transforms.valid_transforms\n\n            self.train_dataset = ImageClassificationDataset(\n                self.pipeline_config,\n                df=self.train_df,\n                stage=\"train\",\n                transforms=train_transforms,\n            )\n            self.valid_dataset = ImageClassificationDataset(\n                self.pipeline_config,\n                df=self.valid_df,\n                stage=\"valid\",\n                transforms=valid_transforms,\n            )\n        elif stage == \"test\":\n            test_transforms = self.transforms.test_transforms\n            self.test_dataset = ImageClassificationDataset(\n                self.pipeline_config,\n                df=self.test_df,\n                stage=\"test\",\n                transforms=test_transforms,\n            )\n\n    def train_dataloader(self) -> DataLoader:\n        \"\"\"Train dataloader.\"\"\"\n        return DataLoader(\n            self.train_dataset,\n            **self.pipeline_config.datamodule.train_loader,\n        )\n\n    def valid_dataloader(self) -> DataLoader:\n        return DataLoader(\n            self.valid_dataset, **self.pipeline_config.datamodule.valid_loader\n        )\n\n    def test_dataloader(self) -> DataLoader:\n        return DataLoader(\n            self.test_dataset, **self.pipeline_config.datamodule.test_loader\n        )\n\n","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:57.568459Z","iopub.execute_input":"2023-01-04T13:19:57.569156Z","iopub.status.idle":"2023-01-04T13:19:57.612911Z","shell.execute_reply.started":"2023-01-04T13:19:57.569119Z","shell.execute_reply":"2023-01-04T13:19:57.611831Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"dm = RSNABreastDataModule(pipeline_config)\ndm.prepare_data()\ndm.setup(stage=\"test\")","metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"execution":{"iopub.status.busy":"2023-01-04T13:19:57.615503Z","iopub.execute_input":"2023-01-04T13:19:57.616504Z","iopub.status.idle":"2023-01-04T13:20:02.467359Z","shell.execute_reply.started":"2023-01-04T13:19:57.616465Z","shell.execute_reply":"2023-01-04T13:20:02.466349Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_loader = dm.test_dataloader()","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:02.468841Z","iopub.execute_input":"2023-01-04T13:20:02.470677Z","iopub.status.idle":"2023-01-04T13:20:02.476324Z","shell.execute_reply.started":"2023-01-04T13:20:02.470633Z","shell.execute_reply":"2023-01-04T13:20:02.475423Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Model","metadata":{}},{"cell_type":"code","source":"import functools\nfrom abc import ABC, abstractmethod\nfrom typing import Any, Optional, Tuple\n\nimport torch\nimport torch.nn.functional as F\n# import torchinfo\n# from torchinfo.model_statistics import ModelStatistics\nimport torchvision\nfrom torch import nn\nimport functools\n\ndef rsetattr(obj, attr, val):\n    pre, _, post = attr.rpartition(\".\")\n    return setattr(rgetattr(obj, pre) if pre else obj, post, val)\n\n\ndef rgetattr(obj, attr, *args):\n    def _getattr(obj, attr):\n        return getattr(obj, attr, *args)\n\n    return functools.reduce(_getattr, [obj] + attr.split(\".\"))\n\nclass Model(ABC, nn.Module):\n    \"\"\"Model Base Class.\"\"\"\n\n    def __init__(self, pipeline_config: PipelineConfig) -> None:\n        super().__init__()\n        self.backbone: Optional[nn.Module]\n        self.head: Optional[nn.Module]\n        self.model: nn.Module\n        self.pipeline_config = pipeline_config\n\n    @abstractmethod\n    def create_model(self) -> nn.Module:\n        \"\"\"Create the model.\n        Note that users can implement anything they want, as long\n        as the shape matches.\n        \"\"\"\n        raise NotImplementedError(\"Please implement your own model.\")\n\n    @abstractmethod\n    def forward(self, inputs: torch.Tensor) -> torch.Tensor:\n        \"\"\"Forward pass of the model.\"\"\"\n\n    def load_backbone(self) -> nn.Module:\n        \"\"\"Load the backbone of the model.\"\"\"\n\n    def modify_head(self) -> nn.Module:\n        \"\"\"Modify the head of the model.\"\"\"\n\n    def _init_weights(self, module: nn.Module) -> None:\n        \"\"\"Initialize the weights of the model.\n        Note need to be private since it is only used internally.\n        \"\"\"\n\n    def extract_embeddings(self, inputs: torch.Tensor) -> torch.Tensor:\n        \"\"\"Extract the embeddings from the model.\n        NOTE:\n            Users can use feature embeddings to do metric learning or clustering,\n            as well as other tasks.\n        Sample Implementation:\n            ```python\n            def extract_embeddings(self, inputs: torch.Tensor) -> torch.Tensor:\n                return self.backbone(inputs)\n            ```\n        \"\"\"\n\n    def model_summary(\n        self, input_size: Optional[Tuple[int, int, int, int]] = None, **kwargs: Any\n    ) -> ModelStatistics:\n        \"\"\"Wrapper for torchinfo package to get the model summary.\"\"\"\n        if input_size is None:\n            input_size = (\n                1,\n                3,\n                self.pipeline_config.augmentation.image_size,\n                self.pipeline_config.augmentation.image_size,\n            )\n        return torchinfo.summary(self.model, input_size=input_size, **kwargs)\n\n    def get_last_layer(self) -> Tuple[list, int, nn.Module]:\n        \"\"\"Get the last layer information of a PyTorch Model.\n        NOTE:\n            This is only correct if the last layer is a linear layer and is the head.\n            The easy way for timm is actually to use the `reset_classifier` method to\n            remove the head and then add a new head.\n        \"\"\"\n        # propagate through the model to get the last layer name\n        for name, _param in self.backbone.named_modules():\n            last_layer_name = name\n\n        last_layer_attributes = last_layer_name.split(\".\")  # + ['in_features']\n        # reduce applies to a list recursively and reduce it to a single value\n        linear_layer = functools.reduce(getattr, last_layer_attributes, self.backbone)\n        in_features = linear_layer.in_features\n        last_layer_name = \".\".join(last_layer_attributes)\n        return last_layer_name, linear_layer, in_features\n\nclass ImageClassificationModel(Model):\n    \"\"\"A generic image classification model. This is generic in the sense that\n    it can be used for any image classification by just modifying the head.\n    \"\"\"\n\n    def __init__(self, pipeline_config: PipelineConfig) -> None:\n        super().__init__(pipeline_config)\n\n        self.adapter = self.pipeline_config.model.adapter\n        self.model_name = self.pipeline_config.model.model_name\n        self.pretrained = self.pipeline_config.model.pretrained\n        self.model = self.create_model()\n\n        # self.model.apply(self._init_weights) # activate if init weights\n        print(f\"Successfully created model: {self.pipeline_config.model.model_name}\")\n\n    def _concat_backbone_and_head(self, last_layer_name) -> nn.Module:\n        \"\"\"Concatenate the backbone and head of the model.\"\"\"\n        model = copy.deepcopy(self.backbone)\n        head = copy.deepcopy(self.head)\n        rsetattr(model, last_layer_name, head)\n        return model\n\n    def create_model(self) -> nn.Module:\n        \"\"\"Create the model sequentially.\"\"\"\n        self.backbone = self.load_backbone()\n        last_layer_name, _, in_features = self.get_last_layer()\n        rsetattr(self.backbone, last_layer_name, nn.Identity())\n        self.head = self.modify_head(in_features)\n        model = self._concat_backbone_and_head(last_layer_name)\n        return model\n\n    def load_backbone(self) -> nn.Module:\n        \"\"\"Load the backbone of the model.\n        NOTE:\n            1. Backbones are usually loaded from timm or torchvision.\n            2. This is not mandatory since users can just create it in create_model.\n        \"\"\"\n        if self.adapter == \"torchvision\":\n            backbone = getattr(torchvision.models, self.model_name)(\n                pretrained=self.pretrained\n            )\n        elif self.adapter == \"timm\":\n            backbone = timm.create_model(\n                self.model_name,\n                pretrained=self.pretrained,\n                # in_chans=3\n            )\n        else:\n            raise ValueError(f\"Adapter {self.adapter} not supported.\")\n        return backbone\n\n    def modify_head(self, in_features: int = None) -> nn.Module:\n        \"\"\"Modify the head of the model.\n        NOTE/TODO:\n            This part is very tricky, to modify the head,\n            the penultimate layer of the backbone is taken, but different\n            models will have different names for the penultimate layer.\n        \"\"\"\n        # fully connected\n        out_features = self.pipeline_config.model.num_classes\n        head = nn.Linear(in_features=in_features, out_features=out_features)\n        return head\n\n    def forward_features(self, inputs: torch.Tensor) -> torch.Tensor:\n        \"\"\"Forward pass of the model up to the penultimate layer to get\n        feature embeddings.\n        \"\"\"\n        features = self.backbone(inputs)\n        return features\n\n    def forward_head(self, inputs: torch.Tensor) -> torch.Tensor:\n        \"\"\"Forward pass of the model up to the head to get predictions.\"\"\"\n        # nn.AdaptiveAvgPool2d(1)(inputs) is used by both timm and torchvision\n        outputs = self.head(inputs)\n        return outputs\n\n    def forward(self, inputs: torch.Tensor) -> torch.Tensor:\n        \"\"\"Forward pass of the model.\"\"\"\n        features = self.forward_features(inputs)\n        outputs = self.forward_head(features)\n        return outputs\n        # return self.model(inputs)\n\n    def _init_weights(self, module: nn.Module) -> None:\n        \"\"\"Initialize the weights of the model.\"\"\"\n        if isinstance(module, nn.Conv2d):\n            nn.init.kaiming_normal_(module.weight, mode=\"fan_out\", nonlinearity=\"relu\")\n            if module.bias is not None:\n                nn.init.constant_(module.bias, 0)\n        elif isinstance(module, nn.BatchNorm2d):\n            nn.init.constant_(module.weight, 1)\n            nn.init.constant_(module.bias, 0)\n        elif isinstance(module, nn.Linear):\n            nn.init.normal_(module.weight, 0, 0.01)\n            nn.init.constant_(module.bias, 0)\n\n    def forward_pass(self, inputs: torch.Tensor) -> torch.Tensor:\n        \"\"\"Forward pass of the model.\"\"\"\n        y = self.model(inputs)\n        print(f\"X: {inputs.shape}, Y: {y.shape}\")\n        print(\"Forward Pass Successful\")","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:02.478056Z","iopub.execute_input":"2023-01-04T13:20:02.47847Z","iopub.status.idle":"2023-01-04T13:20:02.510387Z","shell.execute_reply.started":"2023-01-04T13:20:02.478424Z","shell.execute_reply":"2023-01-04T13:20:02.509393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = ImageClassificationModel(pipeline_config).to(pipeline_config.device)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:02.511794Z","iopub.execute_input":"2023-01-04T13:20:02.512508Z","iopub.status.idle":"2023-01-04T13:20:03.071607Z","shell.execute_reply.started":"2023-01-04T13:20:02.512461Z","shell.execute_reply":"2023-01-04T13:20:03.070455Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Inference","metadata":{}},{"cell_type":"code","source":"@torch.inference_mode(mode=True)\ndef inference_one_fold(\n    model: Model,\n    state_dict: collections.OrderedDict,\n    test_loader: DataLoader,\n    pipeline_config: PipelineConfig,\n) -> np.ndarray:\n    \"\"\"Inference the model on one fold.\n\n    Args:\n        model (Model): The model to be used for inference.\n            Note that pretrained should be set to False.\n        state_dict (collections.OrderedDict): The state dict of the model.\n        test_loader (DataLoader): The dataloader for the test set.\n\n    Returns:\n        test_probs (np.ndarray): The predictions of the model.\n    \"\"\"\n    device = pipeline_config.device\n    model.to(device)\n    model.eval()\n\n    model.load_state_dict(state_dict)\n\n    current_fold_probs = []\n\n    for batch in tqdm(test_loader, position=0, leave=True):\n        images = batch.to(device, non_blocking=True)\n        test_logits = model(images)\n        test_probs = get_sigmoid_softmax(pipeline_config)(test_logits).cpu().numpy()\n        current_fold_probs.append(test_probs)\n    current_fold_probs = np.concatenate(current_fold_probs, axis=0)\n    return current_fold_probs\n\n\n@torch.inference_mode(mode=True)\ndef inference_all_folds(\n    model: Model,\n    state_dicts: List[collections.OrderedDict],\n    test_loader: DataLoader,\n    pipeline_config: PipelineConfig,\n) -> np.ndarray:\n    \"\"\"Inference the model on all K folds.\n\n    Args:\n        model (Model): The model to be used for inference.\n            Note that pretrained should be set to False.\n        state_dicts (List[collections.OrderedDict]): The state dicts of the models.\n            Generally, K Fold means K state dicts.\n        test_loader (DataLoader): The dataloader for the test set.\n\n    Returns:\n        mean_preds (np.ndarray): The mean of the predictions of all folds.\n    \"\"\"\n    all_folds_probs = []\n    for _fold_num, state in enumerate(state_dicts):\n        current_fold_probs = inference_one_fold(\n            model, state, test_loader, pipeline_config\n        )\n        all_folds_probs.append(current_fold_probs)\n    mean_preds = np.mean(all_folds_probs, axis=0)\n    return mean_preds","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:03.073541Z","iopub.execute_input":"2023-01-04T13:20:03.073924Z","iopub.status.idle":"2023-01-04T13:20:03.086534Z","shell.execute_reply.started":"2023-01-04T13:20:03.073886Z","shell.execute_reply":"2023-01-04T13:20:03.08553Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_sigmoid_softmax(\n    pipeline_config: PipelineConfig,\n) -> Union[torch.nn.Sigmoid, torch.nn.Softmax]:\n    \"\"\"Get the sigmoid or softmax function depending on loss function.\"\"\"\n    if pipeline_config.criterion_params.train_criterion == \"BCEWithLogitsLoss\":\n        return getattr(torch.nn, \"Sigmoid\")()\n\n    if pipeline_config.criterion_params.train_criterion == \"CrossEntropyLoss\":\n        return getattr(torch.nn, \"Softmax\")(dim=1)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:03.087795Z","iopub.execute_input":"2023-01-04T13:20:03.088288Z","iopub.status.idle":"2023-01-04T13:20:03.097917Z","shell.execute_reply.started":"2023-01-04T13:20:03.088244Z","shell.execute_reply":"2023-01-04T13:20:03.096934Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"weights = [\"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_1_epoch9.pt\",\n           \"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_2_epoch9.pt\",\n           \"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_3_epoch7.pt\",\n           \"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_4_epoch9.pt\"]\n\nstate_dicts = [torch.load(path)[\"model_state_dict\"] for path in weights]\n\npredictions = inference_all_folds(\n    model=model,\n    state_dicts=state_dicts,\n    test_loader=test_loader,\n    pipeline_config=pipeline_config,\n)\npredictions","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:03.099482Z","iopub.execute_input":"2023-01-04T13:20:03.099795Z","iopub.status.idle":"2023-01-04T13:20:04.964478Z","shell.execute_reply.started":"2023-01-04T13:20:03.099769Z","shell.execute_reply":"2023-01-04T13:20:04.962331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def create_oof_df(pipeline_config: PipelineConfig) -> None:\n    \"\"\"Create OOF dataframe for Generic Image Dataset with a Resampling Strategy.\"\"\"\n    num_classes = pipeline_config.model.num_classes\n    weights = [\"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_1_epoch9.pt\",\n               \"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_2_epoch9.pt\",\n               \"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_3_epoch7.pt\",\n               \"/kaggle/input/resnet50-256-timm/resnet50_best_val_AUROC_fold_4_epoch9.pt\"]\n    dm = RSNABreastDataModule(pipeline_config)\n    df_oof = pd.DataFrame()\n    for fold in range(pipeline_config.resample.resample_params[\"n_splits\"]):\n        fold = fold + 1  # since fold starts from 1\n        dm.prepare_data(fold=fold)\n        _df_oof = dm.oof_df\n        weight = weights[fold - 1]\n        states = torch.load(weight)\n        oof_probs = states[\"oof_probs\"]\n        oof_trues = states[\"oof_trues\"]\n        oof_preds = states[\"oof_preds\"]\n\n        _df_oof[[f\"class_{str(c)}_oof\" for c in range(num_classes)]] = (\n            oof_probs.detach().cpu().numpy()\n        )\n        _df_oof[\"oof_trues\"] = oof_trues.detach().cpu().numpy()\n        _df_oof[\"oof_preds\"] = oof_preds.detach().cpu().numpy()\n\n        df_oof = pd.concat([df_oof, _df_oof], axis=0)\n\n    oof_probs = torch.from_numpy(\n        df_oof[[f\"class_{str(c)}_oof\" for c in range(num_classes)]].values\n    )\n    oof_trues = torch.from_numpy(df_oof[\"oof_trues\"].values)\n\n    accuracy = Accuracy(num_classes=num_classes)(oof_probs, oof_trues)\n    print(\"OOF Accuracy\", accuracy)  # 0.3281 confirms that it is the best epoch\n    return df_oof","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:04.965862Z","iopub.execute_input":"2023-01-04T13:20:04.966664Z","iopub.status.idle":"2023-01-04T13:20:04.977282Z","shell.execute_reply.started":"2023-01-04T13:20:04.966626Z","shell.execute_reply":"2023-01-04T13:20:04.976217Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_oof = create_oof_df(pipeline_config)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:04.978958Z","iopub.execute_input":"2023-01-04T13:20:04.979463Z","iopub.status.idle":"2023-01-04T13:20:25.183788Z","shell.execute_reply.started":"2023-01-04T13:20:04.979421Z","shell.execute_reply":"2023-01-04T13:20:25.182764Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_oof","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:25.185199Z","iopub.execute_input":"2023-01-04T13:20:25.186639Z","iopub.status.idle":"2023-01-04T13:20:25.235259Z","shell.execute_reply.started":"2023-01-04T13:20:25.186599Z","shell.execute_reply":"2023-01-04T13:20:25.234294Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# weight = \"/kaggle/input/fork-of-notebookc58e81f8fb/artifacts/RSNA-Breast-Cancer-Detection-2022/4e34034f-5c9c-4a17-afcc-a7ef448bb6ad/resnet50_best_val_AUROC_fold_1_epoch9.pt\"\n\n# state_dict = torch.load(weight)[\"model_state_dict\"]","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:25.236866Z","iopub.execute_input":"2023-01-04T13:20:25.237237Z","iopub.status.idle":"2023-01-04T13:20:25.242541Z","shell.execute_reply.started":"2023-01-04T13:20:25.2372Z","shell.execute_reply":"2023-01-04T13:20:25.241542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# predictions = inference_one_fold(\n#     model=model,\n#     state_dict=state_dict,\n#     test_loader=test_loader,\n#     pipeline_config=pipeline_config,\n# )","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:25.243993Z","iopub.execute_input":"2023-01-04T13:20:25.244735Z","iopub.status.idle":"2023-01-04T13:20:25.252276Z","shell.execute_reply.started":"2023-01-04T13:20:25.244694Z","shell.execute_reply":"2023-01-04T13:20:25.251073Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predictions","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:25.253821Z","iopub.execute_input":"2023-01-04T13:20:25.254219Z","iopub.status.idle":"2023-01-04T13:20:25.269039Z","shell.execute_reply.started":"2023-01-04T13:20:25.254184Z","shell.execute_reply":"2023-01-04T13:20:25.267922Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pfbeta_torch(labels, preds, beta=1):  # beta=1 is F1 score\n    preds = preds[:, 1]\n    preds = preds.clip(0, 1)\n    y_true_count = labels.sum()\n    ctp = preds[labels == 1].sum()\n    # cfp = (1 - preds[labels==1]).sum() + (preds[labels==0]).sum()\n    cfp = preds[labels == 0].sum()\n    beta_squared = beta * beta\n    c_precision = ctp / (ctp + cfp + 1e-4)\n    c_recall = ctp / (y_true_count + 1e-4)\n    if c_precision > 0 and c_recall > 0:\n        result = (\n            (1 + beta_squared)\n            * (c_precision * c_recall)\n            / (beta_squared * c_precision + c_recall + 1e-4)\n        )\n        return result\n    else:\n        return 0.0","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:36:25.079034Z","iopub.execute_input":"2023-01-04T13:36:25.079408Z","iopub.status.idle":"2023-01-04T13:36:25.086623Z","shell.execute_reply.started":"2023-01-04T13:36:25.079356Z","shell.execute_reply":"2023-01-04T13:36:25.08563Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def optimize_thresholds(labels, predictions):\n    labels = labels.detach().cpu().numpy()\n    predictions = predictions.detach().cpu().numpy()\n    \n    f1_thresholded = []\n    thresholds = np.linspace(0.001, 0.999, 999)\n    for threshold in thresholds:\n        predictions_thresholded = (predictions > threshold).astype(int)\n        f1 = pfbeta_torch(labels, predictions_thresholded)\n        f1_thresholded.append(float(f1))\n    max_f1 = np.max(f1_thresholded)\n    best_threshold = thresholds[np.argmax(f1_thresholded)]\n    return max_f1, best_threshold","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:38:32.370607Z","iopub.execute_input":"2023-01-04T13:38:32.370964Z","iopub.status.idle":"2023-01-04T13:38:32.379534Z","shell.execute_reply.started":"2023-01-04T13:38:32.370933Z","shell.execute_reply":"2023-01-04T13:38:32.377393Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"oof_probs = torch.from_numpy(\n    df_oof[[f\"class_{str(c)}_oof\" for c in range(2)]].values\n)\noof_trues = torch.from_numpy(df_oof[\"oof_trues\"].values)\noof_f1 = pfbeta_torch(oof_trues.flatten(), oof_probs)\nprint(oof_f1)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:36:30.013728Z","iopub.execute_input":"2023-01-04T13:36:30.014097Z","iopub.status.idle":"2023-01-04T13:36:30.026288Z","shell.execute_reply.started":"2023-01-04T13:36:30.014063Z","shell.execute_reply":"2023-01-04T13:36:30.025035Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optimize_thresholds(oof_trues.flatten(), oof_probs)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:38:34.487211Z","iopub.execute_input":"2023-01-04T13:38:34.487595Z","iopub.status.idle":"2023-01-04T13:38:34.898315Z","shell.execute_reply.started":"2023-01-04T13:38:34.487562Z","shell.execute_reply":"2023-01-04T13:38:34.89741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# array([[0.9824349 , 0.01756509],\n#        [0.98461956, 0.01538041],\n#        [0.99039197, 0.00960808],\n#        [0.98820776, 0.01179225]], dtype=float32)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:27.225364Z","iopub.execute_input":"2023-01-04T13:20:27.226167Z","iopub.status.idle":"2023-01-04T13:20:27.230766Z","shell.execute_reply.started":"2023-01-04T13:20:27.226127Z","shell.execute_reply":"2023-01-04T13:20:27.229738Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# # calculate pf1 for multiple thresholds\n# thrs = np.arange(0,1,0.05)\n# scores = []\n# for thr in tqdm(thrs):\n#     scores+=[pfbeta(df_oof.true.astype('float32'), df_oof.pred.astype('float32')>thr)]\n    \n# # get best thr for max pF1\n# best_score_idx = np.argmax(scores)\n# best_score = np.max(scores)\n# best_thr = thrs[best_score_idx]\n# print(f'\\n## MAX pF1 = {best_score: 0.3f} @ {best_thr:0.3f}\\n')\n\n# # plot thr vs pF1 graph\n# fig, ax = plt.subplots(figsize=(8,4))\n# ax.fill_between(thrs, scores,\n#                 color='red', alpha=0.3, )\n# ax.plot(thrs, scores, '-ok');\n# ax.axvline(x=best_thr, color='blue', ls='--')\n# ax.plot(best_thr, best_score, color='blue', marker='o', markersize=12)\n# ax.set_xlabel('Threshold');\n# ax.set_ylabel('pF1');\n# ax.set_title('Threshold Vs pF1');","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_oof[\"prediction_id\"] = df_oof[\"patient_id\"].astype(str) + \"_\" + df_oof[\"laterality\"].astype(str)\ndf_oof.head(5)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:43:45.349893Z","iopub.execute_input":"2023-01-04T13:43:45.350263Z","iopub.status.idle":"2023-01-04T13:43:45.411713Z","shell.execute_reply.started":"2023-01-04T13:43:45.350232Z","shell.execute_reply":"2023-01-04T13:43:45.41062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_oof = df_oof.groupby('prediction_id').max() #.mean() #\ndf_oof = df_oof.sort_index()","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:44:50.808367Z","iopub.execute_input":"2023-01-04T13:44:50.809063Z","iopub.status.idle":"2023-01-04T13:45:01.426258Z","shell.execute_reply.started":"2023-01-04T13:44:50.809029Z","shell.execute_reply":"2023-01-04T13:45:01.425193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"oof_probs = torch.from_numpy(\n    df_oof[[f\"class_{str(c)}_oof\" for c in range(2)]].values\n)\noof_trues = torch.from_numpy(df_oof[\"oof_trues\"].values)\noof_f1 = pfbeta_torch(oof_trues.flatten(), oof_probs)\nprint(oof_f1)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:45:33.783675Z","iopub.execute_input":"2023-01-04T13:45:33.784589Z","iopub.status.idle":"2023-01-04T13:45:33.796609Z","shell.execute_reply.started":"2023-01-04T13:45:33.784541Z","shell.execute_reply":"2023-01-04T13:45:33.795523Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"optimize_thresholds(oof_trues.flatten(), oof_probs)","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:45:40.417489Z","iopub.execute_input":"2023-01-04T13:45:40.417908Z","iopub.status.idle":"2023-01-04T13:45:40.761812Z","shell.execute_reply.started":"2023-01-04T13:45:40.417872Z","shell.execute_reply":"2023-01-04T13:45:40.760759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_test = dm.test_df\ndf_test","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:20:27.266262Z","iopub.execute_input":"2023-01-04T13:20:27.266945Z","iopub.status.idle":"2023-01-04T13:20:27.285353Z","shell.execute_reply.started":"2023-01-04T13:20:27.266905Z","shell.execute_reply":"2023-01-04T13:20:27.284432Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Submission","metadata":{}},{"cell_type":"markdown","source":"### Max Threshold","metadata":{}},{"cell_type":"code","source":"# submission = pd.DataFrame()\n# submission[\"prediction_id\"] = df_test['prediction_id']\n# submission[\"cancer\"] = predictions[:, 1]\n# submission = submission.groupby('prediction_id').max() #.mean() #\n# submission = submission.sort_index()\n# submission.loc[:, 'cancer'] = (submission.cancer.values>0.061).astype(np.float32)\n# submission.to_csv('submission.csv', index=True) # true or false depend on df\n# submission.head()","metadata":{"execution":{"iopub.status.busy":"2023-01-04T13:51:44.289235Z","iopub.execute_input":"2023-01-04T13:51:44.289613Z","iopub.status.idle":"2023-01-04T13:51:44.310322Z","shell.execute_reply.started":"2023-01-04T13:51:44.289579Z","shell.execute_reply":"2023-01-04T13:51:44.30929Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Max No Threshold","metadata":{}},{"cell_type":"code","source":"submission = pd.DataFrame()\nsubmission[\"prediction_id\"] = df_test['prediction_id']\nsubmission[\"cancer\"] = predictions[:, 1]\nsubmission = submission.groupby('prediction_id').max() #.mean() #\nsubmission = submission.sort_index()\nsubmission.to_csv('submission.csv', index=True) # true or false depend on df\nsubmission.head()","metadata":{},"execution_count":null,"outputs":[]}]}