{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":39272,"databundleVersionId":4629629,"sourceType":"competition"},{"sourceId":4696088,"sourceType":"datasetVersion","datasetId":2687741}],"dockerImageVersionId":30397,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#  Breast Cancer Prediction with ResNet50 \n\n---","metadata":{}},{"cell_type":"markdown","source":"## Dataset Description\n\nThe dataset for this challenge contains radiographic breast images of female subjects.\nThe goal of this competition is to identify **cases of breast cancer in mammograms from screening exams**. It is important to identify cases of cancer for obvious reasons, but **false positives also have downsides for patients**. As millions of women get mammograms each year, a useful machine learning tool **could help a great many people**.\n\n## Files\n---\n- **site_id** - ID code for the source hospital.\n- **patient_id** - ID code for the patient.\n- **image_id** - ID code for the image.\n- **laterality** - Whether the image is of the left or right breast.\n- **view** - The orientation of the image. The default for a screening exam is to capture two views per breast.\n- **age** - The patient's age in years.\n- **implant** - Whether or not the patient had breast implants. Site 1 only provides breast implant information at the patient level, not at the breast level.\n- **density** - A rating for how dense the breast tissue is, with A being the least dense and D being the most dense. Extremely dense tissue can make diagnosis more difficult. Only provided for train.\n- **machine_id** - An ID code for the imaging device.\n- **cancer** - Whether or not the breast was positive for malignant cancer. The target value. Only provided for train.\n- **biopsy** - Whether or not a follow-up biopsy was performed on the breast. Only provided for train.\n- **invasive** - If the breast is positive for cancer, whether or not the cancer proved to be invasive. Only provided for train.\n- **BIRADS** - 0 if the breast required follow-up, 1 if the breast was rated as negative for cancer, and 2 if the breast was rated as normal. Only provided for train.\n- **prediction_id** - The ID for the matching submission row. Multiple images will share the same prediction ID. Test only.\n- **difficult_negative_case** - True if the case was unusually difficult. Only provided for train.","metadata":{}},{"cell_type":"markdown","source":"## Files aux\n---\n\n- The files in \"**rsna-mammography-images-as-pngs**\" are images filtred as lenght 256x256 made by notebook https://www.kaggle.com/code/radek1/eda-training-a-fast-ai-model-submission","metadata":{}},{"cell_type":"markdown","source":"Evaluation\n---\n\n- Evaluated using the **probabilistic** **F1 score (pF1)**. This extension of the traditional F score accepts probabilities instead of binary classifications. \n\n![image.png](attachment:af9393ba-6dbd-452f-a597-0a2662f741f9.png)","metadata":{},"attachments":{"af9393ba-6dbd-452f-a597-0a2662f741f9.png":{"image/png":"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xv79+41f/epXRtOmTY327dsbf/vb34z09HQjLi7OCAoKMh599FGjqKjIcDgcxtatW42ePXsawcHBxujRo439+/e7rllVVWW89957RpMmTYzo6Gjjyy+/dD0DERGRy8lkGKetFRYREZFr3meffcYjjzxCvXr1SEhIYMeOHdjtdsLCwujSpQvBwcEqDHuN6ty5M2vXrr3gmjuvvPIKs2fP5tlnn6VLly7k5+fj7+9PbGwsLVq00PdGRETkClFBZRERkeuIYRhs2bKFvLw87rzzTqKiooiKirrS0xIPYBgGS5cuxWKxEBERcUFFtUVEROTSUM0dERGR68iJEyc4cuQIAM8+++wVno1cbv7+/vj5+V3Qubm5uRQVFdGsWTPi4uIu8sxERETkl1C4IyIicp1IS0vj448/Zs+ePZjNZjIyMti0adOVnpZcRps2bSIgIKDO56WmpvLhhx9SUFCAw+Fg+/btdSqmLSIiIpeWwh0REZHrxNq1a1mwYAFlZWWEhobypz/9iX/+859XelriAdasWcOiRYvw9/fHMAxefPFFjh8/fqWnJSIiIv+lgsoiIiIiIiIiIh5MK3dERERERERERDyYwh0REREREREREQ+mcEdERERERERExIMp3BERERERERER8WAKd0REREREREREPJjCHRERERERERERD6ZwR0RERERERETEgyncERERERERERHxYAp3REREREREREQ8mMIdEREREREREREPpnBHRERERERERMSDKdwREREREREREfFgCndERERERERERDyYwh0REREREREREQ+mcEdERERERERExIMp3BERERERERER8WAKd0REREREREREPJjCHRERERERERERD6ZwR0RERERERETEgyncERERERERERHxYAp3REREREREREQ8mMIdEREREREREREPpnBHRERERERERMSDKdwREREREREREfFgCndERERERERERDyYwh0REREREREREQ926cKdnfPoHfcYq3Iv2RVERERERERERK57WrkjIiIiIiIiIuLBFO6IiIiIiIiIiHiws4c7B5bxuwd+y/J97oePLH+Khx54ilWZ7sdTFj7AQ1OXcej0g45SUhY/w5S7h9A7bggPPLmQXbW8qlW670v++uQDDB/Un353P8DsOV+yv+j0HgVsfP4BHno/idKkD5l97xD3174cuez68Bmm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ZYsriDjdwIEDad++PWvXrgVg1KhRTJw40a1PcXEx3t7eLFy4kNjYWJ5//vlLeIcXX9++fWnSpAnvv/8+AJMnT+a+++6r0W/48OHk5ubSuHHjGm2xsbHExsYyf/58ANq0acP48eNr7F43YsQIpk2bxrJly4iOjmb27NmX4I5EREQuHe2WJSIi17zc3FyKiopo1qwZcXFxV3o6ch0qKSkhOzub4cOHu71alJqaSklJCRaLpUaI4+XlRb169c644iYvL4+MjAzgZDjxc2az2fX60YoVKy547q+//jpVVVUXfP4vkZuby48//ojZbGb48OG19rnhhhto2rQpUVFRZxxn48aNAERERNCqVasa7T4+Pvj4+GAYBsuXL784kxcREbmMFO6IiMg1LTU1lQ8//JCCggIcDgfbt2/X1tdy2RUUFOB0OunYsaPb8T179mCz2bBYLDV2aysrKyMwMLBGnZlTcnNzSU9PJzg4mJtvvrlGu81mIzMzE+CMY5yPt956i8LCwgs+/0I5nU6ysrIoKSmhQ4cObkXJT5w4waFDh4CTq6IcDgcdOnSodRyHw0FCQgK+vr5ERETUusLn+PHjHD9+HOCM9bFERESuZgp3RETkmrZmzRoWLVqEv78/hmHw4osvun6JE7lcDh8+zKBBg9wCCsMwSEpKwmazERgYWCOg8fX1JS4uzlWb53QOh4OUlBQqKiq47bbbar1mcXExe/bswWw28+tf//qC536lyjNWV1ezZcsWAOLj493a3nvvPd58801Xvz59+rgVEz/dqd3vGjduTKdOnWrtk5GRQUZGBmazmcmTJ1/EuxAREbk8FO6IiMg17amnnmLfvn1kZmaSkZFBcnKy285MIhcqNzeX+++/nxYtWtCvXz++++47vvjiC+Lj42nZsiXdu3dn2bJlVFdXExcXx7333ut2/rFjx8jMzMTpdHLrrbfWGD8qKoopU6bg7+9fo81ut5OUlARAz549a7Q7nU4++OADfvrpJ+666y7uueeei3TXdVOXZ/RzdrudxMREAHr06OE6fvDgQVavXk2fPn0AsFqtTJ06laCgoFrnsGHDBgCCgoJo3759jfbs7GyWLFnC0aNHGTNmzBlf/xIREbmaKdwRERERqaPKykrefvtt2rZty7p16/jPf/7D2LFjWb58OU8//TQ7duygd+/ePPnkk+zYsaPWMbKyssjKygJOFkiui1OrWnx8fLjlllvc2jIzM4mLi+PNN99k0KBBzJkz54zBx6X0S59RcXExe/fuxWQy8fzzz9O1a1diYmK47bbb2LVrF7179z6veaxYsQKTyURoaGiNcCcxMZFBgwbxxRdfMHHiRF555RUaNmx4Ue5fRETkctJuWSIiIiJ1lJmZyYEDB/jDH/6Aj48PAC1atOAPf/iDa2VYt27d+OCDD/joo49qXZmTnZ1NVlYWZrP5jK9WncnRo0fJysrCz8+PBx54AG/vkz/SGYaB0+nEbDbz+uuvM3nyZFfb5fZLn9GqVatwOBzUr1+f8vJyKisrMQyDsrIy2rdvf161cQoLC0lLSwMgKSmJyMhIV9upZxUWFsayZcsYMmQIXl5eF+v2RURELiuFOyIiIiJ1dPjwYZo0aUJQUBCrV68GTgYVp+9sVVFRQVVVFd98802N8+12O+np6RQXFxMREUFYWFidrv+vf/0LwzAYMGAAr776Kk2aNHG1mUwmrFYrvr6+F3h3F8cvfUYJCQkAjBw5kjfffJOgoCCqq6t54YUXKCoqOq/Qav369VRUVNCgQQPmz59P//793dq9vLywWq1XLAATERG5WPQ3mYiIiEgd9e3bl759+2IYBs8++yz16tUjODjYtfLD4XCQn5/vKpb8czabjeTkZAzDYPDgwZhMpjpdf82aNZhMJm6++WYiIiJ+UZBjt9tJTU0lPz+/1vbKyko2bdpU66tdjRo1okuXLrWe90ufUUJCAl5eXrRt25aAgADgZC0hu91O9+7dXauBzmbbtm1UVVXRqFEjBgwYcEVeTxMREbkcFO6IiIiIXKCioiIOHz5MTEwMQ4YMcR3Pz89n27ZtGIZB9+7da5xns9n44YcfAGp9Zetsqqqq2Lp1K1arlZYtW/7iFTrV1dUkJSWxe/fuWtudTidfffUVDRo0qNEWFRV1xnDnlAt5Rt988w0FBQU0adKENm3auAIhb29vfvOb39CoUaNzBmKn7qu6upq2bdsq2BERkWuawh0RERGRC5SRkQFASEgIN910k+t4YWEhSUlJeHl5cd9999U4Lz8/n9TUVKxWa627XZ3N119/TVVVFS1btqRt27a/7AYAPz8/7rrrrhqvLJ2ybt06pkyZQuPGjWu0nb61+5lcyDM6VWC5adOmbrvbeXl5ERERce6bApKTk8nJycEwDIYNG3Ze54iIiHgqhTsiIiIiF+jtt9/GZDLVCDmOHDnCwYMH+c1vfuMWaJySmJhIRUUF3bp1w2q11umamzdvBqBJkya0atXqwif/X2azmaCgoDOubPHx8SEsLIymTZte0PgX8ozWrVsHnAyETg936iI1NZWCggIA4uPjL2gMERERT6FwR0REROQCVFVV8e2332IYBuXl5ZSVldGgQQOSk5OZMGECPXr04JFHHsFqtWK329m7dy/5+fmUlJTw0UcfASdXzXzzzTcEBAQQGhrqtpvTz6+VlJREQUEBK1eudB3/8ccfqaysvCghz6VQl2dUXFzMmjVrOHHiBElJSZhMJqqqqtixYwd+fn7cfvvt57ye3W5n165dFBYWsnLlSvLz8/Hx8eHEiRPs3LmTqKgobXUuIiLXJJNhGMaVnoSIiIiIp8nMzGTgwIFkZGQwbNgwmjVrRtOmTfnkk0/o3LkzU6dOpXfv3phMJnJzc+nfvz+HDx/G6XRSXl6O3W7Hx8cHi8WC2Wxm3LhxzJ07t9ZrZWRk0L17d+x2O8XFxTgcDnx9ffH392fKlCm88MILl+w+O3fuzNq1ay9o5U5dntH69eu55557qK6uxmaz4XQ68fX1de38lZmZec4iyrm5uXTq1AmHw0FZWRmVlZWYTCaCgoJo3bo1b775Jr169brQRyEiInLVUrgjIiIicgE+++wzHnnkEerVq0dCQgI7duzAbrcTFhZGly5dCA4OrvMuWFejXxLuXC/PSERE5ErTa1kiIiIidWQYBlu2bCEvL48777yTqKgooqKirvS0rip6RiIiIpeP+UpPQERERMTTnDhxgiNHjgDw7LPPXuHZXFr+/v4XtN369fSMRERErjSt3BERERGpg7S0NNauXcuePXswm81kZGRQUVFxXgV/PdGmTZvqfM719oxERESuNNXcEREREamDt956iwULFriK/gYEBNC7d28WLFhwpad21dAzEhERubwU7oiIiIiIiIiIeDDV3BERERERERER8WAKd0REREREREREPJjCHRERERERERERD6ZwR0RERERERETEgyncERERERERERHxYAp3REREREREREQ8mMIdEREREREREREPpnBHRERERERERMSDKdwREREREREREfFgCndERERERERERDyYwh0REREREREREQ+mcEdERERERERExIMp3BERERERERER8WAKd0REREREREREPJjCHRERERERERERD6ZwR0RERERERETEgyncERERERERERHxYAp3REREREREREQ8mMIdEREREREREREPpnBHRERERERERMSDKdwREREREREREfFgCndERERERERERDyY95WegIiIiIicv7179zJhwgSSkpKoV68eZvPJf6srLy+nuroab29vLBaL63hlZSUVFRXMmDGD119/3W2s1atXs3DhQsrLy7HZbDidTvz8/PDz83P1MQyD8PBw7rrrLnr37k1QUBAmk+ny3bCIiIick8IdEREREQ9hGAZpaWkUFxczceJEBg0ahNVqBWDGjBns3r2bHj16MHPmTKxWKw6Hgw0bNvDuu+8SFhZWY7yQkBBGjBhBSkoKb7zxBoGBgYwdO5aYmBjX9YqKiti6dSuTJk0iPj6eOXPmEB4eflnvW0RERM5O4Y6IiIiIh6iqqiI1NZWRI0cya9YsLBYLAEVFRZSUlODt7U3Pnj25++67Xee0bt2aLVu2uAKb08XGxhIbG8v8+fMBaNOmDePHj6dbt25u/UaMGMG0adNYtmwZ0dHRzJ49+xLepYiIiNSVau6IiIiIeIiSkhKys7MZPny4K9gBSE1NpaSkBIvFUiPE8fLyol69erRs2fKM427cuBGAiIgIWrVqVaPdx8cHHx8fDMNg+fLlF+luRERE5GJRuCMiIiLiIQoKCnA6nXTs2NHt+J49e7DZbFgsFqKjo93aysrKCAwMdAuDTudwOEhISMDX15eIiAgaN25co8/x48c5fvw4APXr179IdyMiIiIXi8IdEREREQ9x+PBhBg0aRL169VzHDMMgKSkJm81GYGAgN998s9s5vr6+xMXFuWrz/NyuXbsoLCykcePGdOrUqdY+GRkZZGRkYDabmTx58sW7IREREbkoFO6IiIiIXGHp6el0796d5s2bM3fuXA4dOsS0adOIiooiKiqKd955h8rKSuLi4rj33nvdzj127BiZmZk4nU5uvfXWGmNHRUUxZcoU/P39a732hg0bAAgKCqJ9+/Y12rOzs1myZAlHjx5lzJgxDB8+/CLcsYiIiFxMKqgsIiIicgWVl5fzxBNPMG3aNJKSkpg+fTobN27k9ttv57nnnmPVqlU888wztGnThv79+9c4Pysri6ysLAAGDhxY5+uvWLECk8lEaGhojXAnMTGRhx56iPT0dCZO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cre92+2mtLTUe+j117/r9u3bXLlyBWjcUuZyue56MHZFRQW3b99m4sSJJCcnExQURFZWFufOnWvSNy8vj5EjR2rLooiISCso3BEREfmOHThwgE2bNlFTU0P37t15++23+eCDDx70sqSFrFYrU6dOJTQ01Od5VVUVp0+fxmQykZSU1CTcCQoK4qmnnmo23HG5XPzrX//C4XCQnJzc7HttNps3EPzFL37x3X3QfWpoaODYsWMAjBs3zqdt8+bNrFu3zttvzJgx3sO3m5Oenk7Pnj2Ji4tj6tSpREVFAfD+++/7BFLXrl2jrKys2fBLREREvpnO3BEREfmOLV682OfKcWlfRo8ezejRo5s8z8/Px263069fPyIjI5u0Dx8+nOHDhzc7p9Pp9Fb9NNfH7XazdetWLl26xJQpU5g2bdq3/Ipvz+l0cvLkSQCGDRvmfX758mX27t3LkiVLAAgNDWXhwoV3nWv//v0MHz4ck8lE//79GT16NBcvXmTfvn388pe/JC4uDmgMRiMjI71/i4iISMuockdERESkBf7xj38AEBsb6608aak7VTD+/v6MGDHCp62kpISUlBTWrVvHxIkTWbt2LV27dv3O1n2/bDYb586dw2AwsGLFChITExk8eDDJycmcPXuWUaNGtXiu7OxsbzWO0Whk/vz5APznP//xOV9o7969jB8/3nvFvIiIiLSMKndEREREWiA7OxuDwUCfPn2ardy5m2vXrlFaWkqHDh2YPXs2JlPjTzCPx4Pb7cbPz4/f//73pKametsetIyMDFwuF8HBwdTW1lJXV4fH46Gmpob4+HiCg4NbNM+hQ4eAxtu37hgxYgRDhw7lzJkzHDt2jDlz5hAREUFWVhabN29uk+8RERH5Pns4fj2IiIiIPMSuXLnC5cuXCQ0NpXfv3i0ONu7YtWsXHo+HJ598krS0NMxms7fNYDAQGhr60FWrZGVlAfDcc8+xbt06unbtSkNDAytXrqSysrLFIVReXh6DBg1qUo309ttv85Of/ISCggJOnTqF2WzG6XS2qiJIREREGincEREREbmH/fv343a76dGjB/Hx8a0en5mZicFgYNCgQfTu3fu+gxyHw8GWLVu8Z+H8t6KiIvz9/cnNzcVoNDZpT0hIYPny5S16V1ZWFkajkdjYWDp16gQ0ng3kdDpJSkrC39+/RfMcPnyYZ555psnzSZMmkZiYyOnTpzl8+DBhYWEMHDjQJ/gSERGRllG4IyIiInIPn332GQCRkZHExMS0amx9fT25ubmEhoYSHR39rSp0TCYTCQkJOJ3OZtttNhtBQUEMHTq02XAnOjq6Re85ceIEt27dwmw207dvX+9cJpOJefPm0blz5xZdVX7p0iWuXLnC5MmTm7T5+fkxf/588vPz2bdvHw0NDbzwwgstWp+IiIj4UrgjIiIiche1tbUcPnwYo9FITEwMvXr1atX4w4cPU19fT3R0NLGxsd9qLSaTiZSUFFJSUpptd7lcdO7cmTlz5rS4sqY5p06dAhrDrD59+nifG41Gevfu3eJ5zp49S3BwMAkJCc22jxkzht69e1NcXIzBYOCpp5667zWLiIj8kOm2LBEREZG7KCgowGazERwczIABA/Dza93Pp6NHjwJgNptbXfXzoBw8eBAAi8XiE+60xp2r1IcMGfKNfSwWC88++ywGgwGLxfKNIZCIiIjcnSp3RERERL7G5XJx4cIFrl+/Tm1tLTt37sTpdBIYGIjNZiM7Oxuj0UhiYiJBQUHNzlFfX09BQQG3bt0iPT3d+/yLL76grq7uoQx5bDYbmZmZ3L59m4KCAgwGA/X19Zw6dYoOHTowevToFs3jcDjYvXs3xcXF7N69m6ioKPbt20d0dDRxcXE+fcPCwnjiiSf4+OOPGTZsWLNbyUREROTeDB6Px/OgFyEiIiLysLh16xbjx4/HarXidrtxOBzU19djMBgIDg7G398fk8nEiRMnvrGqxWq1kpSUhNPpxGaz4XK5CAgIICwsjAULFrBy5co2WfvatWvve1vWZ599xrRp02hoaMBut+N2uwkICPDe5FVSUtKiOU+cOMGkSZOoq6ujrq4Oo9FIp06dmDVrFhs2bGjSv7y8nEWLFvH444/zyiuvKOARERG5Dwp3RERERL4nvk24IyIiIu2XztwREREREREREWnHFO6IiIiIfE/c2UbVkmvKRURE5PtD27JERERERERERNoxVe6IiIiIiIiIiLRjCndERERERERERNoxhTsiIiIiIiIiIu2Ywh0RERERERERkXZM4Y6IiIiIiIiISDumcEdEREREREREpB1TuCMiIiIiIiIi0o4p3BERERERERERaccU7oiIiIiIiIiItGMKd0RERERERERE2jGFOyIiIiIiIiIi7ZjCHRERERERERGRduz/AIMV8uwpi8wEAAAAAElFTkSuQmCC"}}},{"cell_type":"markdown","source":"> **My post in the Medium about imbalance dataset, see more: [Click here](http://https://marcos-gois.medium.com/balancing-datasets-in-binary-classification-1aa9abe89752)**","metadata":{}},{"cell_type":"markdown","source":"# **1. Exploratory Data Analysis (EDA)**","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport warnings\nimport tensorflow as tf\nimport cv2\nimport plotly.express as px\n\nwarnings.filterwarnings(\"ignore\")\n\nfrom pathlib import Path\nfrom termcolor import colored\nfrom PIL import Image\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\n\nfrom sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix\nfrom keras.metrics import AUC\nfrom keras.utils import to_categorical\nfrom tensorflow.keras.models import * \nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2025-06-12T23:58:52.024736Z","iopub.execute_input":"2025-06-12T23:58:52.025398Z","iopub.status.idle":"2025-06-12T23:59:00.746357Z","shell.execute_reply.started":"2025-06-12T23:58:52.025318Z","shell.execute_reply":"2025-06-12T23:59:00.745608Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_breast_cancer = pd.read_csv('../input/rsna-breast-cancer-detection/train.csv')\ndf_breast_cancer.head()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:05.716984Z","iopub.execute_input":"2025-06-12T23:59:05.717397Z","iopub.status.idle":"2025-06-12T23:59:05.82856Z","shell.execute_reply.started":"2025-06-12T23:59:05.717367Z","shell.execute_reply":"2025-06-12T23:59:05.827615Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for template in [\"plotly\"]:\n    fig = px.scatter(df_breast_cancer,\n                     x=\"patient_id\", y=\"age\", color=\"cancer\",\n                     log_x=True, size_max=20,\n                     template=template, title=\"Which Age have more cancer?\")\n    fig.show()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:08.29973Z","iopub.execute_input":"2025-06-12T23:59:08.300662Z","iopub.status.idle":"2025-06-12T23:59:09.111714Z","shell.execute_reply.started":"2025-06-12T23:59:08.300628Z","shell.execute_reply":"2025-06-12T23:59:09.110816Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig = px.scatter_3d(df_breast_cancer, x='patient_id', y='cancer', z='age',\n                    color='age',\n                    template=template, title=\"Seeing the volume and age with cancer in 3d\")\nfig.show()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:11.364962Z","iopub.execute_input":"2025-06-12T23:59:11.365851Z","iopub.status.idle":"2025-06-12T23:59:11.467045Z","shell.execute_reply.started":"2025-06-12T23:59:11.365815Z","shell.execute_reply":"2025-06-12T23:59:11.466201Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(colored(\"Résumé des données: \\n\", attrs=['bold']))\n# Get an overview of the data\ndisplay(df_breast_cancer.head())\ndisplay(df_breast_cancer.info())\ndisplay(df_breast_cancer.describe().T)\n\nprint(colored(\"\\n\\n\\n Histogramme des features: \\n\", attrs=['bold']))\n# Plot histograms for numerical features\ndf_breast_cancer.hist(bins=50, figsize=(20,15))\nplt.show()\n\nprint(colored(\"\\n\\n\\n Matrice de corrélation: \\n\", attrs=['bold']))\n# Plot a heatmap of the correlation matrix\ncorr_matrix = df_breast_cancer.corr()\nf, ax = plt.subplots(figsize = (15, 15))\nsns.heatmap(corr_matrix, annot=True)\nplt.show()\n\nprint(colored(\"\\n\\n\\n Vérification des valeurs manquantes: \\n\", attrs=['bold']))\n# Check for missing values\ndisplay(df_breast_cancer.isnull().sum())\n\nprint(colored(\"\\n\\n\\n Distribution du cancer en fonction de l'âge: \\n\", attrs=['bold']))\n\ndf_breast_cancer['cancer'].value_counts().plot.pie(autopct='%.1f%%', figsize=(5, 5))\nmy_colors = [\"#517664\", \"#73AA90\", \"#94DDBC\", \"#DAB06C\", \"#DF928E\", \"#C97973\", \"#B25F57\"]\n# Plot\nf, (a0, a1) = plt.subplots(2, 1, gridspec_kw={'height_ratios': [3, 1]}, figsize=(24, 15))\nsns.distplot(a=df_breast_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=df_breast_cancer[\"age\"], ax=a1, color=my_colors[2])\n\nplt.suptitle(\"Distribution des âges\", weight=\"bold\", size=25)\nsns.despine(right=True, top=True, left=True);\n\nf, (a0, a1) = plt.subplots(1, 2, figsize=(24, 12))\nsns.distplot(a=df_breast_cancer[df_breast_cancer[\"cancer\"]==0][\"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)\na0.set_title(\"Cancer innexistant\", weight=\"bold\", size=20)\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=79, y=0.037, s=\"max: 89\", size=17, color=\"black\", weight=\"bold\")\n\n\nsns.distplot(a=df_breast_cancer[df_breast_cancer[\"cancer\"]==1][\"age\"], rug=True, hist=False, \n             rug_kws={\"color\": my_colors[2]},\n             kde_kws={\"color\": my_colors[2], \"lw\": 5, \"alpha\": 0.7},\n             ax=a1)\na1.set_title(\"Cancer présent\", weight=\"bold\", size=20)\na1.axvline(x=63, ls=\":\", lw=2, color=\"black\")\na1.text(x=63.5, y=0.018, s=\"mean: 63\", size=17, color=\"black\", weight=\"bold\")\na1.axvline(x=38, ls=\":\", lw=2, color=\"black\")\na1.text(x=38.5, y=0.008, s=\"min: 38\", size=17, color=\"black\", weight=\"bold\")\na1.axvline(x=89, ls=\":\", lw=2, color=\"black\")\na1.text(x=79, y=0.037, s=\"max: 89\", size=17, color=\"black\", weight=\"bold\")\n\n\nplt.suptitle(\"Age Distribution\", weight=\"bold\", size=25)\nsns.despine(right=True, top=True, left=True);","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:13.5759Z","iopub.execute_input":"2025-06-12T23:59:13.576764Z","iopub.status.idle":"2025-06-12T23:59:19.657599Z","shell.execute_reply.started":"2025-06-12T23:59:13.57673Z","shell.execute_reply":"2025-06-12T23:59:19.65664Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#visualization outliers of age witch cancer  == 0\ndf_breast_cancer[df_breast_cancer['cancer'] == 0][['age']].boxplot()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:30.011717Z","iopub.execute_input":"2025-06-12T23:59:30.012406Z","iopub.status.idle":"2025-06-12T23:59:30.144635Z","shell.execute_reply.started":"2025-06-12T23:59:30.012375Z","shell.execute_reply":"2025-06-12T23:59:30.143625Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#visualization outliers of age with cancer == 1\ndf_breast_cancer[df_breast_cancer['cancer'] == 1][['age']].boxplot()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:32.401847Z","iopub.execute_input":"2025-06-12T23:59:32.402521Z","iopub.status.idle":"2025-06-12T23:59:32.832635Z","shell.execute_reply.started":"2025-06-12T23:59:32.402488Z","shell.execute_reply":"2025-06-12T23:59:32.831742Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **2. Prepare data**","metadata":{}},{"cell_type":"code","source":"#remove outleirs of age\ndf_breast_cancer = df_breast_cancer.loc[(df_breast_cancer['age'] > 30) |\n                                        (df_breast_cancer['age'] < 87)]","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:35.057515Z","iopub.execute_input":"2025-06-12T23:59:35.05811Z","iopub.status.idle":"2025-06-12T23:59:35.06636Z","shell.execute_reply.started":"2025-06-12T23:59:35.05808Z","shell.execute_reply":"2025-06-12T23:59:35.065695Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_breast_cancer = df_breast_cancer.reset_index(drop=True)","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:40.336084Z","iopub.execute_input":"2025-06-12T23:59:40.336484Z","iopub.status.idle":"2025-06-12T23:59:40.343511Z","shell.execute_reply.started":"2025-06-12T23:59:40.336451Z","shell.execute_reply":"2025-06-12T23:59:40.342494Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#values for cancer before balance\ndf_breast_cancer.cancer.value_counts()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:41.564098Z","iopub.execute_input":"2025-06-12T23:59:41.564456Z","iopub.status.idle":"2025-06-12T23:59:41.57194Z","shell.execute_reply.started":"2025-06-12T23:59:41.564426Z","shell.execute_reply":"2025-06-12T23:59:41.571012Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"*  #### Now we tryed **balances the number of samples of two classes**, \"cancer\" and \"not cancer\", in a dataframe named \"df_breast_cancer\". It first gets the number of samples with class \"cancer\" and the indexes of those samples. Then, it selects the same number of random samples with class \"not cancer\" and combines the indexes of both classes. Finally, it creates a new dataframe using the combined indexes and has equal number of samples from both classes.","metadata":{}},{"cell_type":"code","source":"#balance class of cancer with not cancer aleatoring\n\n#get number of data with class 1(cancer)\nnum_cancer = df_breast_cancer.cancer.value_counts()[1]\n\n#get index on dataframe of class 1(cancer)\nindexs_cancer = np.array(df_breast_cancer[df_breast_cancer.cancer == 1].index)\n\n#get index on dataframe of class 0(not cancer)\nindexs_not_cancer =np.array(df_breast_cancer[df_breast_cancer.cancer == 0].index)\n\n#get indexs on dataframe aleatory of class 0(not cancer) with the same amount of class 1(cancer)\nindexs_not_cancer_balanced = np.random.choice(indexs_not_cancer, num_cancer, replace = False)\n\n#cancatenate indexs with data class 0 and 1\nindexs_cancer_balanced = np.concatenate([indexs_cancer, indexs_not_cancer_balanced], axis = None)\n\n#new dataframe balanced with the same amount class\ndf_breast_cancer = df_breast_cancer.iloc[indexs_cancer_balanced,:]\n\ndf_breast_cancer.head()\ndf_breast_cancer['cancer'].value_counts().plot.pie(autopct='%.1f%%', figsize=(5, 5))","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:44.364189Z","iopub.execute_input":"2025-06-12T23:59:44.365108Z","iopub.status.idle":"2025-06-12T23:59:44.446796Z","shell.execute_reply.started":"2025-06-12T23:59:44.365077Z","shell.execute_reply":"2025-06-12T23:59:44.445736Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#values for cancer after balance\ndf_breast_cancer.cancer.value_counts()","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:47.026703Z","iopub.execute_input":"2025-06-12T23:59:47.027546Z","iopub.status.idle":"2025-06-12T23:59:47.034596Z","shell.execute_reply.started":"2025-06-12T23:59:47.02751Z","shell.execute_reply":"2025-06-12T23:59:47.033606Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# data\nX = df_breast_cancer.drop('cancer', axis=1)\ny = df_breast_cancer['cancer']\n\n# Split the data into training, validation, and testing sets\nX_train, X_valid_test, y_train, y_valid_test = train_test_split(X, y, test_size=0.15, random_state=42, stratify=y)\nX_valid, X_test, y_valid, y_test = train_test_split(X_valid_test, y_valid_test, test_size=0.5, random_state=42, stratify=y_valid_test)\n\nprint(f\"Training data shape: {X_train.shape}\")\nprint(f\"Number of training samples: {len(X_train)}\")\nprint(f\"Validation data shape: {X_valid.shape}\")\nprint(f\"Number of validation samples: {len(X_valid)}\")\nprint(f\"Testing data shape: {X_test.shape}\")\nprint(f\"Number of testing samples: {len(X_test)}\")\n","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:48.547015Z","iopub.execute_input":"2025-06-12T23:59:48.547686Z","iopub.status.idle":"2025-06-12T23:59:48.559633Z","shell.execute_reply.started":"2025-06-12T23:59:48.54765Z","shell.execute_reply":"2025-06-12T23:59:48.558665Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def separe_folder_train_valid_test(folders_name, files_name, name):\n    for folder, file in tqdm(zip(folders_name, files_name), total=len(folders_name), desc=\"Saving Images\"):\n        img = Image.open(os.path.join(path, folder, file))\n        save_path = os.path.join('/kaggle/working/rsna-mammography-images-as-pngs/', name, folder, file)\n        if not os.path.exists(os.path.dirname(save_path)):\n            os.makedirs(os.path.dirname(save_path))\n        img.save(save_path)","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:50.481938Z","iopub.execute_input":"2025-06-12T23:59:50.482826Z","iopub.status.idle":"2025-06-12T23:59:50.488335Z","shell.execute_reply.started":"2025-06-12T23:59:50.482781Z","shell.execute_reply":"2025-06-12T23:59:50.487462Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#path general\npath = '/kaggle/input/rsna-mammography-images-as-pngs/images_as_pngs/train_images_processed/'\n\n#get train images\nfolders_train = X_train.patient_id.astype('str') + '/'\nfiles_train = X_train.image_id.astype('str') + '.png'\n\n#get valid images\nfolders_valid = X_valid.patient_id.astype('str') + '/'\nfiles_valid = X_valid.image_id.astype('str') + '.png'\n\n#get test images\nfolders_test = X_test.patient_id.astype('str') + '/'\nfiles_test = X_test.image_id.astype('str') + '.png'\n\n#separete images\nsepare_folder_train_valid_test(folders_train, files_train, 'train')\nsepare_folder_train_valid_test(folders_valid, files_valid, 'valid')\nsepare_folder_train_valid_test(folders_test, files_test, 'test')","metadata":{"execution":{"iopub.status.busy":"2025-06-12T23:59:57.425262Z","iopub.execute_input":"2025-06-12T23:59:57.426167Z","iopub.status.idle":"2025-06-13T00:00:34.94969Z","shell.execute_reply.started":"2025-06-12T23:59:57.426133Z","shell.execute_reply":"2025-06-13T00:00:34.94879Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_array_imgs(folders, files):\n    list_imgs = []\n\n    for folder, file in tqdm(zip(folders, files), total=len(folders), desc='Loading images'):\n        list_imgs.append(np.array(Image.open(os.path.join(path, folder, file))))\n\n    array_imgs = np.array(list_imgs)\n    # Resize the images to the correct dimensions\n    array_imgs = [cv2.resize(img, (256,256)) for img in array_imgs]\n    # Convert the images to RGB\n    array_imgs = [cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) for img in array_imgs]\n    array_imgs = np.array(array_imgs)\n    array_imgs = array_imgs / 255.0\n    \n    return array_imgs","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:00:51.897416Z","iopub.execute_input":"2025-06-13T00:00:51.897768Z","iopub.status.idle":"2025-06-13T00:00:51.904435Z","shell.execute_reply.started":"2025-06-13T00:00:51.89774Z","shell.execute_reply":"2025-06-13T00:00:51.903479Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"array_imgs_train = get_array_imgs(folders_train, files_train)\narray_imgs_valid = get_array_imgs(folders_valid, files_valid)\narray_imgs_test = get_array_imgs(folders_test, files_test)","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:00:55.075721Z","iopub.execute_input":"2025-06-13T00:00:55.076809Z","iopub.status.idle":"2025-06-13T00:01:01.014358Z","shell.execute_reply.started":"2025-06-13T00:00:55.076771Z","shell.execute_reply":"2025-06-13T00:01:01.013204Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.imshow(array_imgs_train[0])","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:01:05.506942Z","iopub.execute_input":"2025-06-13T00:01:05.50788Z","iopub.status.idle":"2025-06-13T00:01:05.665421Z","shell.execute_reply.started":"2025-06-13T00:01:05.507845Z","shell.execute_reply":"2025-06-13T00:01:05.664614Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"array_imgs_train.shape","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:01:07.722665Z","iopub.execute_input":"2025-06-13T00:01:07.723564Z","iopub.status.idle":"2025-06-13T00:01:07.729316Z","shell.execute_reply.started":"2025-06-13T00:01:07.723529Z","shell.execute_reply":"2025-06-13T00:01:07.728397Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#y as categorical\ny_train = to_categorical(y_train)\ny_valid = to_categorical(y_valid)\ny_test = to_categorical(y_test)","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:01:10.946683Z","iopub.execute_input":"2025-06-13T00:01:10.947077Z","iopub.status.idle":"2025-06-13T00:01:10.953617Z","shell.execute_reply.started":"2025-06-13T00:01:10.947043Z","shell.execute_reply":"2025-06-13T00:01:10.952397Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **3. Construct model to predict**","metadata":{}},{"cell_type":"code","source":"from keras.layers import Dense, Dropout, Activation, Conv2D, MaxPool2D, Flatten\n\nresnet = tf.keras.applications.ResNet50(\n            include_top=False,\n            weights=\"imagenet\",\n            input_shape=(256,256,3)\n        )\n\n# Freeze all layers in the ResNet50 architecture\nfor layer in resnet.layers:\n    layer.trainable = False\n\n# Add a global average pooling layer\nx = GlobalAveragePooling2D()(resnet.output)\n\n# Add a dense layer with 128 units and a ReLU activation function\nx = Dense(128, activation='relu')(x)\n\nx = Dense(1024, activation='relu')(x)\nx = Dropout(0.5)(x)\nx = Dense(1024, activation='relu')(x)\nx = Dropout(0.3)(x)\nx = Dense(512, activation='relu')(x)\n\n# Additional layers for classification\nx = Dense(128, activation='relu')(x)\nx = Dense(3, activation='softmax')(x)\n\n# Add a final dense layer with 1 unit and a sigmoid activation function for binary classification\npredictions = Dense(2, activation='softmax')(x)\n\n# Create the final model by connecting the ResNet50 architecture and the added layers\nmodel = Model(inputs=resnet.input, outputs=predictions)\n\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:03:39.289471Z","iopub.execute_input":"2025-06-13T00:03:39.289818Z","iopub.status.idle":"2025-06-13T00:03:41.19674Z","shell.execute_reply.started":"2025-06-13T00:03:39.289791Z","shell.execute_reply":"2025-06-13T00:03:41.195784Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.utils import plot_model\nfrom IPython.display import Image as IPImage\n\n# Specify the file path for saving the visualization image\nmodel_visualization_path = \"resnet50_notop_ft_20_epochs_architecture.png\"\nplot_model(model, to_file=model_visualization_path, show_shapes=True, show_layer_names=True)\nIPImage(filename=model_visualization_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-13T00:04:51.842403Z","iopub.execute_input":"2025-06-13T00:04:51.843369Z","iopub.status.idle":"2025-06-13T00:04:54.398343Z","shell.execute_reply.started":"2025-06-13T00:04:51.843327Z","shell.execute_reply":"2025-06-13T00:04:54.397154Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\n\nes_vl = EarlyStopping(monitor='val_loss', mode='min', patience=5, restore_best_weights=True, verbose=1)\nes_va = EarlyStopping(monitor='val_accuracy', mode='max', patience=5, restore_best_weights=True, verbose=1)\nchp_path = \"resnet50_notop_ft_20_epochs.keras\"\nchp1 = ModelCheckpoint(chp_path, monitor='val_accuracy', mode='max', save_best_only=True, verbose=1)  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-13T00:05:28.408752Z","iopub.execute_input":"2025-06-13T00:05:28.409124Z","iopub.status.idle":"2025-06-13T00:05:28.414844Z","shell.execute_reply.started":"2025-06-13T00:05:28.409095Z","shell.execute_reply":"2025-06-13T00:05:28.413905Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Compile the model with a binary cross-entropy loss function and the Adam optimization algorithm\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy', AUC()])\n\n\n#fit model\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\n\nes_vl = EarlyStopping(monitor='val_loss', mode='min', patience=5, restore_best_weights=True, verbose=1)\nes_va = EarlyStopping(monitor='val_accuracy', mode='max', patience=5, restore_best_weights=True, verbose=1)\n\nchp_path = \"resnet50_pred_notop_ft_20_epochs.keras\"\nchp1 = ModelCheckpoint(chp_path, monitor='val_accuracy', mode='max', save_best_only=True, verbose=1)  \n\nhistory = model.fit(\n    array_imgs_train,\n    y_train,\n    batch_size=64,\n    epochs=20, \n    validation_data=(array_imgs_valid, y_valid),\n    callbacks=[chp1, es_vl, es_va]\n)","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:05:29.600664Z","iopub.execute_input":"2025-06-13T00:05:29.601018Z","iopub.status.idle":"2025-06-13T00:06:07.58173Z","shell.execute_reply.started":"2025-06-13T00:05:29.60099Z","shell.execute_reply":"2025-06-13T00:06:07.580738Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Create a Pandas DataFrame containing the training history (metrics) of the model\ntrain_history = pd.DataFrame(history.history)\n\n# Sauvegarde classique : pas d'index, séparateur virgule, encodage UTF-8\ntrain_history.to_csv(f\"{chp_path}.csv\", index=False, encoding=\"utf-8\")\n\n# Add a new column 'Epoch' with values from 1 to the number of epochs\ntrain_history['Epoch'] = range(1, len(train_history) + 1)\n\n# Reorder columns for clarity\ntrain_history = train_history[['Epoch', 'accuracy', 'val_accuracy', 'loss', 'val_loss']]\n\ntrain_history","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-13T00:06:19.424548Z","iopub.execute_input":"2025-06-13T00:06:19.425223Z","iopub.status.idle":"2025-06-13T00:06:19.444195Z","shell.execute_reply.started":"2025-06-13T00:06:19.42519Z","shell.execute_reply":"2025-06-13T00:06:19.443096Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\ndef Train_Val_Plot(chp_path, acc, val_acc, loss, val_loss):\n    \n    fig, (ax1, ax2) = plt.subplots(1,2, figsize= (15,10))\n    fig.suptitle(\" MODEL'S METRICS VISUALIZATION \")\n\n    ax1.plot(range(1, len(acc) + 1), acc)\n    ax1.plot(range(1, len(val_acc) + 1), val_acc)\n    ax1.set_title('History of Accuracy')\n    ax1.set_xlabel('Epochs')\n    ax1.set_ylabel('Accuracy')\n    ax1.legend(['training', 'validation'])\n\n\n    ax2.plot(range(1, len(loss) + 1), loss)\n    ax2.plot(range(1, len(val_loss) + 1), val_loss)\n    ax2.set_title('History of Loss')\n    ax2.set_xlabel('Epochs')\n    ax2.set_ylabel('Loss')\n    ax2.legend(['training', 'validation'])\n    \n    # Save figures\n    plt.savefig(f'{chp_path}_plot1.png')\n    \n    plt.show()\n    \n\nTrain_Val_Plot(chp_path, history.history['accuracy'],history.history['val_accuracy'],\n               history.history['loss'],history.history['val_loss'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-13T00:06:21.398227Z","iopub.execute_input":"2025-06-13T00:06:21.399165Z","iopub.status.idle":"2025-06-13T00:06:21.980091Z","shell.execute_reply.started":"2025-06-13T00:06:21.399128Z","shell.execute_reply":"2025-06-13T00:06:21.979003Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **4. Results prediction**","metadata":{}},{"cell_type":"code","source":"#predict images test\ny_pred = model.predict(array_imgs_test)\n\ny_pred = np.argmax(y_pred, axis=1)\ny_test = np.argmax(y_test, axis=1)","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:13:34.128433Z","iopub.execute_input":"2025-06-13T00:13:34.129244Z","iopub.status.idle":"2025-06-13T00:13:36.271833Z","shell.execute_reply.started":"2025-06-13T00:13:34.129207Z","shell.execute_reply":"2025-06-13T00:13:36.270961Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# exploration unique values predicted and counts too\nunique_values, counts = np.unique(y_pred, return_counts=True)\nprint(\"Unique values:\", unique_values)\nprint(\"Counts:\", counts)","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:13:38.104056Z","iopub.execute_input":"2025-06-13T00:13:38.10491Z","iopub.status.idle":"2025-06-13T00:13:38.110825Z","shell.execute_reply.started":"2025-06-13T00:13:38.104875Z","shell.execute_reply":"2025-06-13T00:13:38.109639Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Evaluate the performance of the random forest classifier on the test data\naccuracy = accuracy_score(y_test, y_pred)\nprecision = precision_score(y_test, y_pred)\nrecall = recall_score(y_test, y_pred)\nf1 = f1_score(y_test, y_pred)\n\n# Create the confusion matrix\ncm = confusion_matrix(y_test, y_pred)\n\n# Plot the confusion matrix\nsns.heatmap(cm, annot=True, fmt='d')\nplt.xlabel('Predicted Label')\nplt.ylabel('True Label')\nplt.title('Confusion Matrix')\nplt.show()\n\n# Print the evaluation metrics\nprint(\"Accuracy:\", accuracy)\nprint(\"Precision:\", precision)\nprint(\"Recall:\", recall)\nprint(\"F1 score:\", f1)","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:13:40.422303Z","iopub.execute_input":"2025-06-13T00:13:40.422857Z","iopub.status.idle":"2025-06-13T00:13:40.670829Z","shell.execute_reply.started":"2025-06-13T00:13:40.422756Z","shell.execute_reply":"2025-06-13T00:13:40.669668Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Probabilistic F1 score metric\ndef pfbeta(labels, predictions, beta):\n    y_true_count = 0\n    ctp = 0\n    cfp = 0\n\n    for idx in range(len(labels)):\n        prediction = min(max(predictions[idx], 0), 1)\n        if (labels[idx]):\n            y_true_count += 1\n            ctp += prediction\n        else:\n            cfp += prediction\n\n    beta_squared = beta * beta\n    c_precision = ctp / (ctp + cfp)\n    c_recall = ctp / y_true_count\n    if (c_precision > 0 and c_recall > 0):\n        result = (1 + beta_squared) * (c_precision * c_recall) / (beta_squared * c_precision + c_recall)\n        return result\n    else:\n        return 0","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:13:42.448375Z","iopub.execute_input":"2025-06-13T00:13:42.449337Z","iopub.status.idle":"2025-06-13T00:13:42.456258Z","shell.execute_reply.started":"2025-06-13T00:13:42.449293Z","shell.execute_reply":"2025-06-13T00:13:42.455075Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_imgs = list(X_test.patient_id.astype(str) + '_' + X_test.laterality.astype(str))\n\nprint('Probabilistic F1 score:'+ str(pfbeta(labels_imgs, y_pred, 1)))","metadata":{"execution":{"iopub.status.busy":"2025-06-13T00:13:47.744644Z","iopub.execute_input":"2025-06-13T00:13:47.745728Z","iopub.status.idle":"2025-06-13T00:13:47.753017Z","shell.execute_reply.started":"2025-06-13T00:13:47.745675Z","shell.execute_reply":"2025-06-13T00:13:47.751873Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}