{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":39272,"databundleVersionId":4629629,"sourceType":"competition"},{"sourceId":5089415,"sourceType":"datasetVersion","datasetId":2694061}],"dockerImageVersionId":30747,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# EDA","metadata":{}},{"cell_type":"code","source":"import cv2\nimport pydicom\nfrom pydicom.data import get_testdata_file\nfrom gzip import GzipFile\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom PIL import Image","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:24:39.631105Z","iopub.execute_input":"2024-11-06T12:24:39.631736Z","iopub.status.idle":"2024-11-06T12:24:40.334879Z","shell.execute_reply.started":"2024-11-06T12:24:39.631705Z","shell.execute_reply":"2024-11-06T12:24:40.334101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Đọc dữ liệu","metadata":{}},{"cell_type":"code","source":"file_path = '/kaggle/input/rsna-breast-cancer-detection/train.csv'\ndf = pd.read_csv(file_path)\n\n\n# df_MLO = df[df['view'] != 'MLO']\n# cancer_1 = df_MLO[df_MLO['cancer'] == 1].head(25)\n# cancer_0 = df_MLO[df_MLO['cancer'] == 0].head(25)\n# result = pd.concat([cancer_1, cancer_0])\n# print(result)\ncancer_1 = df[df['cancer'] == 1].head(25)\ncancer_0 = df[df['cancer'] == 0].head(25)\nresult = pd.concat([cancer_1, cancer_0])\n# in ket qua\nprint(result.head(3))\nprint(\"--------------------------------------------------------------------------------\")\nprint(result.tail(3))\n# cancer_1\n# df_MLO","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:24:42.593108Z","iopub.execute_input":"2024-11-06T12:24:42.593862Z","iopub.status.idle":"2024-11-06T12:24:42.742583Z","shell.execute_reply.started":"2024-11-06T12:24:42.593827Z","shell.execute_reply":"2024-11-06T12:24:42.741639Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Show để so sánh cancer hoặc không cancer","metadata":{}},{"cell_type":"code","source":"import matplotlib.image as mpimg\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\ndef load_images_from_paths(image_paths):\n    images = []\n    for path in image_paths:\n        img = Image.open(path)\n        images.append(np.array(img))\n    return images\n\ndef plot_image_grid(images, grid_size=(5, 10), figsize=(25, 25)):\n    fig, axes = plt.subplots(grid_size[0], grid_size[1], figsize=figsize)\n    axes = axes.flatten()\n    \n    for img, ax in zip(images, axes):\n        ax.imshow(img, cmap='gray')\n        ax.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\nimage_paths = []\n\nfor index, row in result.iterrows():\n    temp ='/kaggle/input/rsna-breast-cancer-detection-poi-images/bc_1280_train_lut/'\n    img_path = temp + str(row['patient_id']) + '_' + str(row['image_id'])+ '.png'\n    image_paths.append(img_path)\nimages = load_images_from_paths(image_paths)\nplot_image_grid(images, grid_size=(2, 25))","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:24:45.805638Z","iopub.execute_input":"2024-11-06T12:24:45.805994Z","iopub.status.idle":"2024-11-06T12:24:50.457401Z","shell.execute_reply.started":"2024-11-06T12:24:45.805967Z","shell.execute_reply":"2024-11-06T12:24:50.456439Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## So sánh độ cân bằng","metadata":{}},{"cell_type":"code","source":"cancer = df[df['cancer'] == 1].head(1000)\nnon_cancer = df[df['cancer'] == 0].head(1000)\ncancer_count = len(cancer)\nnon_cancer_count = len(non_cancer)\n\nprint(f'Số lượng ảnh ung thư: {cancer_count}')\nprint(f'Số lượng ảnh không ung thư: {non_cancer_count}')\n\nlabels = ['Ảnh ung thư', 'Ảnh không ung thư']\ncounts = [cancer_count, non_cancer_count]\n\n# Điều chỉnh kích thước của biểu đồ bằng cách sử dụng figsize\nfig, ax = plt.subplots(figsize=(4, 3))  # Kích thước 4x3 inches\n\nax.bar(labels, counts, color=['red', 'blue'])\n\nax.set_ylabel('Số lượng')\nax.set_title('So sánh số lượng ảnh ung thư và không ung thư')\n\nplt.show()\n","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:24:56.810177Z","iopub.execute_input":"2024-11-06T12:24:56.810831Z","iopub.status.idle":"2024-11-06T12:24:56.969902Z","shell.execute_reply.started":"2024-11-06T12:24:56.810793Z","shell.execute_reply":"2024-11-06T12:24:56.969013Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import seaborn as sns\nfrom matplotlib.colors import ListedColormap, LinearSegmentedColormap\nclass clr:\n    S = '\\033[1m' + '\\033[91m'\n    E = '\\033[0m'\n\nmy_colors = [\"#517664\", \"#73AA90\", \"#94DDBC\", \"#DAB06C\", \n             \"#DF928E\", \"#C97973\", \"#B25F57\"]\nCMAP1 = ListedColormap(my_colors)\n\nprint(clr.S+\"Notebook Color Schemes:\"+clr.E)\nsns.palplot(sns.color_palette(my_colors))\n\n\nf, (a0, a1) = plt.subplots(2, 1, gridspec_kw={'height_ratios': [3, 1]}, figsize=(24, 15))\nsns.distplot(a=cancer[\"age\"], rug=True, hist=False, \n             rug_kws={\"color\": my_colors[5]},\n             kde_kws={\"color\": my_colors[5], \"lw\": 5, \"alpha\": 0.7},\n             ax=a0)\n\na0.axvline(x=58, ls=\":\", lw=2, color=\"black\")\na0.text(x=58.5, y=0.018, s=\"mean: 58\", size=17, color=\"black\", weight=\"bold\")\na0.axvline(x=26, ls=\":\", lw=2, color=\"black\")\na0.text(x=26.5, y=0.008, s=\"min: 26\", size=17, color=\"black\", weight=\"bold\")\na0.axvline(x=89, ls=\":\", lw=2, color=\"black\")\na0.text(x=84, y=0.037, s=\"max: 89\", size=17, color=\"black\", weight=\"bold\")\n\nsns.boxenplot(x=cancer[\"age\"], ax=a1, color=my_colors[2])\n\nplt.suptitle(\"Age Distribution\", weight=\"bold\", size=25)\nsns.despine(right=True, top=True, left=True);","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:24:58.954353Z","iopub.execute_input":"2024-11-06T12:24:58.954729Z","iopub.status.idle":"2024-11-06T12:25:00.348629Z","shell.execute_reply.started":"2024-11-06T12:24:58.954691Z","shell.execute_reply":"2024-11-06T12:25:00.347744Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## EDA thử","metadata":{}},{"cell_type":"code","source":"image_paths_cancer = image_paths[0] \nimage_paths_non_cancer = image_paths[-5]\n\nimage_paths_cancer\n","metadata":{"execution":{"iopub.status.busy":"2024-11-06T13:08:33.730245Z","iopub.execute_input":"2024-11-06T13:08:33.730663Z","iopub.status.idle":"2024-11-06T13:08:33.738027Z","shell.execute_reply.started":"2024-11-06T13:08:33.730634Z","shell.execute_reply":"2024-11-06T13:08:33.737011Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\n\nimport cv2\nimport numpy as np\nimport matplotlib.pyplot as plt\n\ndef select_largest_obj(img_bin, lab_val=255, fill_holes=False, \n                       smooth_boundary=False, kernel_size=15):\n    n_labels, img_labeled, lab_stats, _ = cv2.connectedComponentsWithStats(\n        img_bin, connectivity=8, ltype=cv2.CV_32S)\n    largest_obj_lab = np.argmax(lab_stats[1:, 4]) + 1\n    largest_mask = np.zeros(img_bin.shape, dtype=np.uint8)\n    largest_mask[img_labeled == largest_obj_lab] = lab_val\n    if fill_holes:\n        bkg_locs = np.where(img_labeled == 0)\n        bkg_seed = (bkg_locs[0][0], bkg_locs[1][0])\n        img_floodfill = largest_mask.copy()\n        h_, w_ = largest_mask.shape\n        mask_ = np.zeros((h_ + 2, w_ + 2), dtype=np.uint8)\n        cv2.floodFill(img_floodfill, mask_, seedPoint=bkg_seed, newVal=lab_val)\n        holes_mask = cv2.bitwise_not(img_floodfill)  # mask of the holes.\n        largest_mask = largest_mask + holes_mask\n    if smooth_boundary:\n        kernel_ = np.ones((kernel_size, kernel_size), dtype=np.uint8)\n        largest_mask = cv2.morphologyEx(largest_mask, cv2.MORPH_OPEN, kernel_)\n        \n    return largest_mask\n\ndef process_image(image_path, threshold=230):\n    image_array = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n    if image_array.dtype != np.uint8:\n        image_array = cv2.normalize(image_array, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)\n    _, binary_image = cv2.threshold(image_array, threshold, 255, cv2.THRESH_BINARY)\n    largest_object_mask = select_largest_obj(binary_image, lab_val=255, fill_holes=True, smooth_boundary=True, kernel_size=15)\n    result_image = cv2.bitwise_and(image_array, largest_object_mask)\n    return image_array, binary_image, largest_object_mask, result_image\n\n# Đường dẫn tới các ảnh .png\nimage_pathCancer = cancer\nimage_pathNonCancer = non_cancer\n\n# Process both images\nimage_arrayCancer, binary_imageCancer, largest_object_maskCancer, result_imageCancer = process_image(image_paths_cancer)\nimage_arrayNonCancer, binary_imageNonCancer, largest_object_maskNonCancer, result_imageNonCancer = process_image(image_paths_non_cancer)\n\n# Display the images in a single row\nfig, axes = plt.subplots(2, 4, figsize=(20, 12))\n\n# Display Original Image Cancer\naxes[0, 0].imshow(image_arrayCancer, cmap='gray')\naxes[0, 0].set_title('Original Image Cancer')\naxes[0, 0].axis('off')\n\n# Display Binarized Image Cancer\naxes[0, 1].imshow(binary_imageCancer, cmap='gray')\naxes[0, 1].set_title('Binarized Image Cancer')\naxes[0, 1].axis('off')\n\n# Display Largest Object Mask Cancer\naxes[0, 2].imshow(largest_object_maskCancer, cmap='gray')\naxes[0, 2].set_title('Largest Object Mask Cancer')\naxes[0, 2].axis('off')\n\n# Display Artifact Suppressed Image Cancer\naxes[0, 3].imshow(result_imageCancer, cmap='gray')\naxes[0, 3].set_title('Artifact Suppressed Image Cancer')\naxes[0, 3].axis('off')\n\n# Display Original Image NonCancer\naxes[1, 0].imshow(image_arrayNonCancer, cmap='gray')\naxes[1, 0].set_title('Original Image NonCancer')\naxes[1, 0].axis('off')\n\n# Display Binarized Image NonCancer\naxes[1, 1].imshow(binary_imageNonCancer, cmap='gray')\naxes[1, 1].set_title('Binarized Image NonCancer')\naxes[1, 1].axis('off')\n\n# Display Largest Object Mask NonCancer\naxes[1, 2].imshow(largest_object_maskNonCancer, cmap='gray')\naxes[1, 2].set_title('Largest Object Mask NonCancer')\naxes[1, 2].axis('off')\n\n# Display Artifact Suppressed Image NonCancer\naxes[1, 3].imshow(result_imageNonCancer, cmap='gray')\naxes[1, 3].set_title('Artifact Suppressed Image NonCancer')\naxes[1, 3].axis('off')\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:25:03.198568Z","iopub.execute_input":"2024-11-06T12:25:03.198926Z","iopub.status.idle":"2024-11-06T12:25:04.660022Z","shell.execute_reply.started":"2024-11-06T12:25:03.198897Z","shell.execute_reply":"2024-11-06T12:25:04.659165Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"* Do có su chenh lech giua cac mat cat ne nhom chung em quyet dinh sai nguon cho 195 *","metadata":{}},{"cell_type":"markdown","source":"# PYTORCH DATASET","metadata":{}},{"cell_type":"code","source":"from sklearn.preprocessing import LabelEncoder, normalize","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:25:12.148836Z","iopub.execute_input":"2024-11-06T12:25:12.149455Z","iopub.status.idle":"2024-11-06T12:25:12.340686Z","shell.execute_reply.started":"2024-11-06T12:25:12.149416Z","shell.execute_reply":"2024-11-06T12:25:12.339905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Keep only columns in test + target variable and reset the index\ntrain = df[[\"patient_id\", \"image_id\", \"laterality\", \"age\", \"implant\", \"cancer\"]].reset_index(drop=True)\n\nbase_path = \"/kaggle/input/rsna-breast-cancer-detection-poi-images/bc_1280_train_lut/\"\n\n# Add the 'path' column\ntrain['path'] = base_path + train['patient_id'].astype(str) + \"_\" + train['image_id'].astype(str) + \".png\"\n\n# Encode categorical variables\nle_laterality = LabelEncoder()\nle_view = LabelEncoder()\n\ntrain['laterality'] = le_laterality.fit_transform(df['laterality'])\ntrain['view'] = le_view.fit_transform(df['view'])\n\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:25:14.494426Z","iopub.execute_input":"2024-11-06T12:25:14.494854Z","iopub.status.idle":"2024-11-06T12:25:14.634807Z","shell.execute_reply.started":"2024-11-06T12:25:14.494826Z","shell.execute_reply":"2024-11-06T12:25:14.633749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(clr.S+\"Number of missing values in Age:\"+clr.E, train[\"age\"].isna().sum())\ntrain['age'] = train['age'].fillna(58)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:25:21.023586Z","iopub.execute_input":"2024-11-06T12:25:21.023959Z","iopub.status.idle":"2024-11-06T12:25:21.030936Z","shell.execute_reply.started":"2024-11-06T12:25:21.023929Z","shell.execute_reply":"2024-11-06T12:25:21.030021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"!pip install -q efficientnet_pytorch","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:25:28.024693Z","iopub.execute_input":"2024-11-06T12:25:28.025316Z","iopub.status.idle":"2024-11-06T12:25:43.816701Z","shell.execute_reply.started":"2024-11-06T12:25:28.025282Z","shell.execute_reply":"2024-11-06T12:25:43.815566Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# General Libraries\nimport os\nimport re\nimport gc\nimport cv2\nimport wandb\nimport random\nimport math\nfrom glob import glob\nfrom tqdm import tqdm\nfrom pprint import pprint\nfrom time import time\nimport datetime as dtime\nfrom datetime import datetime\nimport itertools\nimport warnings\nimport pandas as pd\nimport numpy as np\nfrom skimage.transform import resize\nfrom sklearn.preprocessing import LabelEncoder, normalize\n\n# For the Visuals\nimport seaborn as sns\nimport matplotlib as mpl\nfrom matplotlib import cm\nimport matplotlib.patches as patches\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nfrom matplotlib.offsetbox import AnnotationBbox, OffsetImage\nfrom matplotlib.colors import ListedColormap, LinearSegmentedColormap\nfrom matplotlib.patches import Rectangle\nfrom IPython.display import display_html\nimport torch\nimport torch.nn as nn\nfrom torchvision.models import resnext50_32x4d\nplt.rcParams.update({'font.size': 16})\n\n# Environment check\nwarnings.filterwarnings(\"ignore\")\nos.environ[\"WANDB_SILENT\"] = \"true\"\nCONFIG = {'competition': 'RSNA_Breast_Cancer', '_wandb_kernel': 'aot'}\n\n# Custom colors\nclass clr:\n    S = '\\033[1m' + '\\033[91m'\n    E = '\\033[0m'\n    \nmy_colors = [\"#517664\", \"#73AA90\", \"#94DDBC\", \"#DAB06C\", \n             \"#DF928E\", \"#C97973\", \"#B25F57\"]\nCMAP1 = ListedColormap(my_colors)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:25:52.763768Z","iopub.execute_input":"2024-11-06T12:25:52.764623Z","iopub.status.idle":"2024-11-06T12:25:58.180362Z","shell.execute_reply.started":"2024-11-06T12:25:52.764589Z","shell.execute_reply":"2024-11-06T12:25:58.179368Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import random\n# PyTorch\nimport torch\nimport torchvision\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch import FloatTensor, LongTensor\nfrom torch.utils.data import Dataset, DataLoader, Subset\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\n\n# Data Augmentation for Image Preprocessing\nfrom albumentations import (ToFloat, Normalize, VerticalFlip, HorizontalFlip, Compose, Resize,\n                            RandomBrightnessContrast, HueSaturationValue, Blur, GaussNoise,\n                            Rotate, RandomResizedCrop, ShiftScaleRotate, ToGray)\nfrom albumentations.pytorch import ToTensorV2\n\nfrom efficientnet_pytorch import EfficientNet\nfrom torchvision.models import resnet34, resnet50\n\n# SKlearn\nfrom sklearn.model_selection import StratifiedKFold, GroupKFold\nfrom sklearn.metrics import accuracy_score, roc_auc_score, confusion_matrix\n\ndef set_seed(seed = 1234):\n    '''Sets the seed of the entire notebook so results are the same every time we run.\n    This is for REPRODUCIBILITY.'''\n    np.random.seed(seed)\n    random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    # When running on the CuDNN backend, two further options must be set\n    torch.backends.cudnn.deterministic = True\n    # Set a fixed value for the hash seed\n    os.environ['PYTHONHASHSEED'] = str(seed)\n\ndef show_values_on_bars(axs, h_v=\"v\", space=0.4):\n    '''Plots the value at the end of the a seaborn barplot.\n    axs: the ax of the plot\n    h_v: weather or not the barplot is vertical/ horizontal'''\n    \n    def _show_on_single_plot(ax):\n        if h_v == \"v\":\n            for p in ax.patches:\n                _x = p.get_x() + p.get_width() / 2\n                _y = p.get_y() + p.get_height()\n                value = int(p.get_height())\n                ax.text(_x, _y, format(value, ','), ha=\"center\") \n        elif h_v == \"h\":\n            for p in ax.patches:\n                _x = p.get_x() + p.get_width() + float(space)\n                _y = p.get_y() + p.get_height()\n                value = int(p.get_width())\n                ax.text(_x, _y, format(value, ','), ha=\"left\")\n\n    if isinstance(axs, np.ndarray):\n        for idx, ax in np.ndenumerate(axs):\n            _show_on_single_plot(ax)\n    else:\n        _show_on_single_plot(axs)\n        \n\n\n# Seed\nset_seed()\nDEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint('Device available now:', DEVICE)\n\n# Read in Data\n# train = pd.read_csv(file_path)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:03.415779Z","iopub.execute_input":"2024-11-06T12:26:03.416561Z","iopub.status.idle":"2024-11-06T12:26:03.692626Z","shell.execute_reply.started":"2024-11-06T12:26:03.416529Z","shell.execute_reply":"2024-11-06T12:26:03.691775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ----- GLOBAL PARAMS -----\nvertical_flip = 0.5\nhorizontal_flip = 0.5\n\ncsv_columns = ['laterality', 'view', 'age', 'implant']\nno_columns = len(csv_columns)\noutput_size = 1\n# -------------------------","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:07.954717Z","iopub.execute_input":"2024-11-06T12:26:07.955074Z","iopub.status.idle":"2024-11-06T12:26:07.959669Z","shell.execute_reply.started":"2024-11-06T12:26:07.955047Z","shell.execute_reply":"2024-11-06T12:26:07.958786Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from PIL import Image\nimport numpy as np\nfrom torch.utils.data import Dataset\nfrom albumentations import Compose, RandomResizedCrop, ShiftScaleRotate, HorizontalFlip, VerticalFlip\nfrom albumentations.pytorch import ToTensorV2\n\nclass RSNADataset(Dataset):\n    \n    def __init__(self, dataframe, vertical_flip, horizontal_flip, is_train=True):\n        self.dataframe = dataframe\n        self.is_train = is_train\n        self.vertical_flip = vertical_flip\n        self.horizontal_flip = horizontal_flip\n        \n        # Data Augmentation (custom for each dataset type)\n        if is_train:\n            self.transform = Compose([\n                RandomResizedCrop(height=224, width=224),\n                ShiftScaleRotate(rotate_limit=90, scale_limit=[0.8, 1.2]),\n                HorizontalFlip(p=self.horizontal_flip),\n                VerticalFlip(p=self.vertical_flip),\n                ToTensorV2()\n            ])\n        else:\n            self.transform = Compose([\n                ToTensorV2()\n            ])\n            \n    def __len__(self):\n        return len(self.dataframe)\n    \n    def __getitem__(self, index):\n        '''Take each row in batch at a time.'''\n        \n        # Select path and read image\n        image_path = self.dataframe['path'][index]\n        image = Image.open(image_path).convert('RGB')\n        image = np.array(image).astype(np.float32)\n        \n        # For this image also import .csv information\n        csv_row = self.dataframe.iloc[index][csv_columns]\n        csv_data = []\n        for value in csv_row:\n            try:\n                csv_data.append(float(value))\n            except ValueError:\n                # Handle the case where conversion fails\n                csv_data.append(np.nan)  # or some other default value\n                print(f\"Warning: Unable to convert value '{value}' to float.\")\n        \n        csv_data = np.array(csv_data, dtype=np.float32)\n        \n        # Apply transforms\n        # albumentations expects a dictionary with an 'image' key\n        transformed = self.transform(image=image)\n        transf_image = transformed['image']\n        \n        # Return info\n        if self.is_train:\n            return {\"image\": transf_image, \n                    \"meta\": csv_data, \n                    \"target\": self.dataframe['cancer'][index]}\n        else:\n            return {\"image\": transf_image, \n                    \"meta\": csv_data}\n","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:13.986557Z","iopub.execute_input":"2024-11-06T12:26:13.987251Z","iopub.status.idle":"2024-11-06T12:26:13.999689Z","shell.execute_reply.started":"2024-11-06T12:26:13.987219Z","shell.execute_reply":"2024-11-06T12:26:13.998725Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def data_to_device(data):\n    image, metadata, targets = data.values()\n    return image.to(DEVICE), metadata.to(DEVICE), targets.to(DEVICE)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:18.86987Z","iopub.execute_input":"2024-11-06T12:26:18.870222Z","iopub.status.idle":"2024-11-06T12:26:18.875112Z","shell.execute_reply.started":"2024-11-06T12:26:18.870193Z","shell.execute_reply":"2024-11-06T12:26:18.87412Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Sample data\n# train = \n# Instantiate Dataset object\ndataset = RSNADataset(train.head(6), vertical_flip, horizontal_flip,\n                      is_train=True)\n# # The Dataloader\ndataloader = DataLoader(dataset, batch_size=3, shuffle=False)\n\n# Output of the Dataloader\nfor k, data in enumerate(dataloader):\n    image, meta, targets = data_to_device(data)\n    print(clr.S + f\"Batch: {k}\" + clr.E, \"\\n\" +\n          clr.S + \"Image:\" + clr.E, image.shape, \"\\n\" +\n          clr.S + \"Meta:\" + clr.E, meta, \"\\n\" +\n          clr.S + \"Targets:\" + clr.E, targets, \"\\n\" +\n          \"=\"*50)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:22.582694Z","iopub.execute_input":"2024-11-06T12:26:22.583488Z","iopub.status.idle":"2024-11-06T12:26:23.169395Z","shell.execute_reply.started":"2024-11-06T12:26:22.583454Z","shell.execute_reply":"2024-11-06T12:26:23.16845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# NET WORK\n","metadata":{}},{"cell_type":"markdown","source":"## Resnet50","metadata":{}},{"cell_type":"code","source":"class ResNet50Network(nn.Module):\n    def __init__(self, output_size, no_columns):\n        super().__init__()\n        self.no_columns, self.output_size = no_columns, output_size\n        \n        # Define Feature part (IMAGE)\n        self.features = resnet50(pretrained=True) # 1000 neurons out\n        # (metadata)\n        self.csv = nn.Sequential(nn.Linear(self.no_columns, 500),\n                                 nn.BatchNorm1d(500),\n                                 nn.ReLU(),\n                                 nn.Dropout(p=0.2))\n        \n        # Define Classification part\n        self.classification = nn.Linear(1000 + 500, output_size)\n        \n        \n    def forward(self, image, meta, prints=False):\n        if prints: print('Input Image shape:', image.shape, '\\n'+\n                         'Input metadata shape:', meta.shape)\n        \n        # Image CNN\n        image = self.features(image)\n        if prints: print('Features Image shape:', image.shape)\n        \n        # CSV FNN\n        meta = self.csv(meta)\n        if prints: print('Meta Data:', meta.shape)\n            \n        # Concatenate layers from image with layers from csv_data\n        image_meta_data = torch.cat((image, meta), dim=1)\n        if prints: print('Concatenated Data:', image_meta_data.shape)\n        \n        # CLASSIF\n        out = self.classification(image_meta_data)\n        if prints: print('Out shape:', out.shape)\n        \n        return out","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:38.762194Z","iopub.execute_input":"2024-11-06T12:26:38.762563Z","iopub.status.idle":"2024-11-06T12:26:38.771969Z","shell.execute_reply.started":"2024-11-06T12:26:38.762536Z","shell.execute_reply":"2024-11-06T12:26:38.77101Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Load Model\nmodel_example = ResNet50Network(output_size=output_size, no_columns=no_columns).to(DEVICE)\n\n# Outputs\nout = model_example(image, meta, prints=True)\n\n# Criterion example\ncriterion_example = nn.BCEWithLogitsLoss()\n# Unsqueeze(1) from shape=[3] to shape=[3, 1]\nloss = criterion_example(out, targets.unsqueeze(1).float()) \nprint(\"=\"*50)\nprint(clr.S+'Loss:'+clr.E, loss.item())","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:43.836139Z","iopub.execute_input":"2024-11-06T12:26:43.836493Z","iopub.status.idle":"2024-11-06T12:26:46.007047Z","shell.execute_reply.started":"2024-11-06T12:26:43.836466Z","shell.execute_reply":"2024-11-06T12:26:46.006024Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# TRAINING","metadata":{}},{"cell_type":"code","source":"def add_in_file(text, f):\n    \n    with open(f'logs_{VERSION}.txt', 'a+') as f:\n        print(text, file=f)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:26:49.918438Z","iopub.execute_input":"2024-11-06T12:26:49.919339Z","iopub.status.idle":"2024-11-06T12:26:49.924313Z","shell.execute_reply.started":"2024-11-06T12:26:49.919298Z","shell.execute_reply":"2024-11-06T12:26:49.923225Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def train_folds(model, train_original):\n    # Creates a .txt file that will contain the logs\n    # logs == what we also print to console\n    f = open(f\"logs_{VERSION}.txt\", \"w+\")\n    \n    # Split in folds\n    group_fold = GroupKFold(n_splits = FOLDS)\n\n    # Generate indices to split data into training and test set.\n    k_folds = group_fold.split(X = np.zeros(len(train_original)), \n                               y = train_original['cancer'], \n                               groups = train_original['patient_id'].tolist())\n    \n    # For each fold\n    for i, (train_index, valid_index) in enumerate(k_folds):\n        \n        print(clr.S+f\"---------- Fold: {i+1} ----------\"+clr.E)\n        add_in_file(f\"---------- Fold: {i+1} ----------\", f)\n        \n        # 🐝 W&B Tracking\n        RUN_CONFIG = CONFIG.copy()\n        params = dict(model=MODEL, \n                      version=VERSION,\n                      fold=i,\n                      epochs=EPOCHS, \n                      batch=BATCH_SIZE1,\n                      lr=LR,\n                      weight_decay=WD)\n        RUN_CONFIG.update(params)\n#         run = wandb.init(project='RSNA_Breast_Cancer', config=RUN_CONFIG)\n\n#         wandb.watch(model, log_freq=100) # 🐝\n\n        # --- Create Instances ---\n        # Best ROC score in this fold\n        best_roc = None\n        # Reset patience before every fold\n        patience_f = PATIENCE\n\n        # Optimizer/ Scheduler/ Criterion\n        optimizer = torch.optim.Adam(model.parameters(), lr = LR, \n                                     weight_decay=WD)\n        scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='max', \n                                      patience=LR_PATIENCE, verbose=True, factor=LR_FACTOR)\n        criterion = nn.BCEWithLogitsLoss()\n\n\n        # --- Read in Data ---\n        train_data = train_original.iloc[train_index].reset_index(drop=True)\n        valid_data = train_original.iloc[valid_index].reset_index(drop=True)\n\n        # Create Data instances\n        train = RSNADataset(train_data, vertical_flip, horizontal_flip, \n                            is_train=True)\n        valid = RSNADataset(valid_data, vertical_flip, horizontal_flip,\n                            is_train=True)\n\n        # Dataloaders\n        train_loader = DataLoader(train, batch_size=BATCH_SIZE1, \n                                  shuffle=True, num_workers=WORKERS)\n        valid_loader = DataLoader(valid, batch_size=BATCH_SIZE2, \n                                  shuffle=False, num_workers=WORKERS)\n\n\n        # === EPOCHS ===\n        for epoch in range(EPOCHS):\n            start_time = time()\n            correct = 0\n            train_losses = 0\n\n            # === TRAIN ===\n            # Sets the module in training mode.\n            model.train()\n\n            # For each batch\n            for k, data in tqdm(enumerate(train_loader)):\n                # Save them to device\n                image, meta, targets = data_to_device(data)\n\n                # Clear gradients first; very important\n                # usually done BEFORE prediction\n                optimizer.zero_grad()\n\n                # Log Probabilities & Backpropagation\n                out = model(image, meta)\n                loss = criterion(out, targets.unsqueeze(1).float())\n                loss.backward()\n                optimizer.step()\n\n                # --- Save information after this batch ---\n                # Save loss\n                train_losses += loss.item()\n#                 wandb.log({\"train_loss\": loss.item()}, step=epoch) # 🐝\n                # From log probabilities to actual probabilities\n                train_preds = torch.round(torch.sigmoid(out)) # 0 and 1\n                # Number of correct predictions\n                correct += (train_preds.cpu() == targets.cpu().unsqueeze(1)).sum().item()\n\n            # Compute Train Accuracy\n            train_acc = correct / len(train_index)\n#             wandb.log({\"train_acc\": train_acc}) # 🐝\n\n\n            # === EVAL ===\n            # Sets the model in evaluation mode.\n            model.eval()\n\n            # Create matrix to store evaluation predictions (for accuracy)\n            valid_preds = torch.zeros(size = (len(valid_index), 1), \n                                      device=DEVICE, dtype=torch.float32)\n\n\n            # Disables gradients (we need to be sure no optimization happens)\n            with torch.no_grad():\n                for k, data in tqdm(enumerate(valid_loader)):\n                    # Save them to device\n                    image, meta, targets = data_to_device(data)\n\n                    out = model(image, meta)\n                    pred = torch.sigmoid(out)\n                    valid_preds[k*image.shape[0] : k*image.shape[0] + image.shape[0]] = pred\n\n                # Calculate accuracy\n                valid_acc = accuracy_score(valid_data['cancer'].values, \n                                           torch.round(valid_preds.cpu()))\n#                 wandb.log({\"valid_acc\": valid_acc}) # 🐝\n                # Calculate ROC\n                valid_roc = roc_auc_score(valid_data['cancer'].values, \n                                          valid_preds.cpu())\n#                 wandb.log({\"valid_roc\": valid_roc}) # 🐝\n\n                # Calculate time on Train + Eval\n                duration = str(dtime.timedelta(seconds=time() - start_time))[:7]\n\n\n                # PRINT INFO\n                final_logs = '{} | Epoch: {}/{} | Loss: {:.4} | Acc_tr: {:.3} | Acc_vd: {:.3} | ROC: {:.3}'.\\\n                                format(duration, epoch+1, EPOCHS, \n                                       train_losses, train_acc, valid_acc, valid_roc)\n                add_in_file(final_logs,f)\n                print(final_logs)\n\n\n                # === SAVE MODEL ===\n\n                # Update scheduler (for learning_rate)\n                scheduler.step(valid_roc)\n                # Name the model\n                model_name = f\"Fold{i+1}_Epoch{epoch+1}_ValidAcc{valid_acc:.3f}_ROC{valid_roc:.3f}.pth\"\n\n                # Update best_roc\n                if not best_roc: # If best_roc = None\n                    best_roc = valid_roc\n                    torch.save(model.state_dict(), model_name)\n                    continue\n\n                if valid_roc > best_roc:\n                    best_roc = valid_roc\n                    # Reset patience (because we have improvement)\n                    patience_f = PATIENCE\n                    torch.save(model.state_dict(), model_name)\n                else:\n                    # Decrease patience (no improvement in ROC)\n                    patience_f = patience_f - 1\n                    if patience_f == 0:\n                        stop_logs = 'Early stopping (no improvement since 3 models) | Best ROC: {}'.\\\n                                    format(best_roc)\n                        add_in_file(stop_logs, f)\n                        print(stop_logs)\n                        break\n\n\n        # === CLEANING ===\n        # Clear memory\n        del train, valid, train_loader, valid_loader, image, targets\n        gc.collect()","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:27:28.066843Z","iopub.execute_input":"2024-11-06T12:27:28.067167Z","iopub.status.idle":"2024-11-06T12:27:28.091066Z","shell.execute_reply.started":"2024-11-06T12:27:28.067142Z","shell.execute_reply":"2024-11-06T12:27:28.090149Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Train Resnet50","metadata":{}},{"cell_type":"code","source":"FOLDS = 3\nEPOCHS = 1\nPATIENCE = 3\nWORKERS = 8\nLR = 0.0005\nWD = 0.0\nLR_PATIENCE = 1            # 1 model not improving until lr is decreasing\nLR_FACTOR = 0.4            # by how much the lr is decreasing\n\nBATCH_SIZE1 = 64           # for train\nBATCH_SIZE2 = 16           # for valid\n\nVERSION = 'v1'\nMODEL = 'resnet50'\n\nmodel1 = ResNet50Network(output_size=output_size, no_columns=no_columns).to(DEVICE)\n\n# ------------------\n\n# Run the cell below to train\n# Ran it locally on all data, see the results below\ntrain_folds(model=model1, train_original=train)\n\n# Print the logs during training\n# f = open('/kaggle/working/logs_v1.txt', \"r\")\n# contents = f.read()\n# print(contents)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:27:42.478096Z","iopub.execute_input":"2024-11-06T12:27:42.478467Z","iopub.status.idle":"2024-11-06T12:53:23.275154Z","shell.execute_reply.started":"2024-11-06T12:27:42.478438Z","shell.execute_reply":"2024-11-06T12:53:23.272848Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Print the logs during training\nf = open('/kaggle/working/logs_v1.txt', \"r\")\ncontents = f.read()\nprint(contents)","metadata":{"execution":{"iopub.status.busy":"2024-11-06T12:53:27.060118Z","iopub.execute_input":"2024-11-06T12:53:27.060495Z","iopub.status.idle":"2024-11-06T12:53:27.067995Z","shell.execute_reply.started":"2024-11-06T12:53:27.060465Z","shell.execute_reply":"2024-11-06T12:53:27.066436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import torch\nimport cv2\nimport numpy as np\nfrom torchvision import transforms\n\n# Define output size and metadata column count\noutput_size = 1  # e.g., 1 for binary classification\nno_columns = 4  # Set this to the number used during training\n\n# Initialize the model and load pre-trained weights\nmodel = ResNet50Network(output_size=output_size, no_columns=no_columns)\nmodel.load_state_dict(torch.load(\"/kaggle/working/Fold1_Epoch1_ValidAcc0.977_ROC0.581.pth\"))\nmodel.eval()\n\n# Function to read and preprocess a PNG image\ndef preprocess_png_image(png_path):\n    # Read the PNG file as a grayscale image\n    image = cv2.imread(png_path, cv2.IMREAD_GRAYSCALE)\n    if image is None:\n        print(f\"Error: {png_path} could not be loaded.\")\n        return None\n    \n    # Resize image to 224x224 for ResNet50\n    image = cv2.resize(image, (224, 224))\n    # Convert grayscale to RGB by duplicating channels\n    image = np.stack([image] * 3, axis=-1)\n\n    # Convert to tensor and normalize\n    transform = transforms.Compose([\n        transforms.ToTensor(),\n        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])\n    ])\n    image_tensor = transform(image)\n\n    # Add batch dimension\n    return image_tensor.unsqueeze(0)\n\n# Paths to the PNG images\npng_path1 = \"/kaggle/input/rsna-breast-cancer-detection-poi-images/bc_1280_train_lut/10011_1031443799.png\"\npng_path2 = \"/kaggle/input/rsna-breast-cancer-detection-poi-images/bc_1280_train_lut/10006_1459541791.png\"\n\n# Process PNG images\nimage1 = preprocess_png_image(png_path1)\nimage2 = preprocess_png_image(png_path2)\n\n# Create a placeholder metadata tensor\n# Shape: (batch_size, no_columns), filled with zeros\nmeta_placeholder = torch.zeros((1, no_columns))\n\n# Ensure images are processed successfully before predicting\nif image1 is not None and image2 is not None:\n    with torch.no_grad():\n        output1 = model(image1, meta_placeholder)\n        output2 = model(image2, meta_placeholder)\n        \n        prediction1 = torch.sigmoid(output1).item()\n        prediction2 = torch.sigmoid(output2).item()\n        \n        pred_class1 = 1 if prediction1 >= 0.5 else 0\n        pred_class2 = 1 if prediction2 >= 0.5 else 0\n\n    print(f\"Ảnh PNG 1 - Xác suất: {prediction1:.4f}, Nhãn dự đoán: {pred_class1}\")\n    print(f\"Ảnh PNG 2 - Xác suất: {prediction2:.4f}, Nhãn dự đoán: {pred_class2}\")\nelse:\n    print(\"One or both images could not be processed.\")\n","metadata":{"execution":{"iopub.status.busy":"2024-11-06T13:02:46.292617Z","iopub.execute_input":"2024-11-06T13:02:46.293519Z","iopub.status.idle":"2024-11-06T13:02:47.306073Z","shell.execute_reply.started":"2024-11-06T13:02:46.293484Z","shell.execute_reply":"2024-11-06T13:02:47.305058Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}