{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":30626,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Overview\n\nWelcome to my first Kaggle notebook! I aim to keep it simple, clear, and reader-friendly, hoping it proves helpful for your learning journey.\n\n","metadata":{}},{"cell_type":"markdown","source":"# Description\n\nIn this task, we encounter a classification challenge involving images of cats and dogs provided in the dataset. This problem falls under the domain of Deep Learning and is specifically known as image classification.\n\nSince our data is in image format, we will employ Convolutional Neural Networks (CNNs) for feature extraction and learning. The objective is to train the model to recognize patterns and features in the images, enabling it to make accurate predictions (high accuracy).","metadata":{}},{"cell_type":"markdown","source":"# Imports\n\nThe following imports are libraries and tools that we are going to need to create our classification model.","metadata":{}},{"cell_type":"code","source":"import os\nimport cv2\nimport numpy as np\nimport pandas as pd \nimport seaborn as sns\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom matplotlib.image import imread\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Flatten, Dropout, Activation, Conv2D, MaxPool2D, BatchNormalization\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.losses import BinaryCrossentropy\nfrom keras.metrics import BinaryAccuracy\nfrom keras.optimizers import RMSprop\nfrom sklearn.model_selection import train_test_split","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:33:05.821964Z","iopub.execute_input":"2024-01-16T00:33:05.822715Z","iopub.status.idle":"2024-01-16T00:33:05.829484Z","shell.execute_reply.started":"2024-01-16T00:33:05.822676Z","shell.execute_reply":"2024-01-16T00:33:05.828446Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Exploring the data\n\nLet's take a look at the data","metadata":{}},{"cell_type":"code","source":"for dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:33:05.834689Z","iopub.execute_input":"2024-01-16T00:33:05.834982Z","iopub.status.idle":"2024-01-16T00:33:05.845058Z","shell.execute_reply.started":"2024-01-16T00:33:05.834956Z","shell.execute_reply":"2024-01-16T00:33:05.844144Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Unzipping the content of train.zip","metadata":{}},{"cell_type":"code","source":"!unzip -q '/kaggle/input/dogs-vs-cats/train.zip'","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:33:06.143768Z","iopub.execute_input":"2024-01-16T00:33:06.144127Z","iopub.status.idle":"2024-01-16T00:37:34.908818Z","shell.execute_reply.started":"2024-01-16T00:33:06.144097Z","shell.execute_reply":"2024-01-16T00:37:34.907776Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Our data has been extracted to kaggle/working/train, to view directory items uncomment the following line\nINPUT_PATH = \"/kaggle/working/train/\"\n# print(os.listdir(INPUT_PATH))\nlen(os.listdir(INPUT_PATH))\nimage_files = os.listdir(INPUT_PATH)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:34.911001Z","iopub.execute_input":"2024-01-16T00:37:34.911325Z","iopub.status.idle":"2024-01-16T00:37:34.946639Z","shell.execute_reply.started":"2024-01-16T00:37:34.911298Z","shell.execute_reply":"2024-01-16T00:37:34.945913Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Currently, we possess a dataset comprising 25,000 images. However, there is a lack of information regarding the distribution or balance of these images—specifically, whether they predominantly depict cats or dogs.\n\nWe are going to see that in the next part ! Now let's just visualize some of the pictures.","metadata":{}},{"cell_type":"code","source":"preview_files = image_files[:8]\n\nplt.figure(figsize=(12,8))\n\nfor i, image_file in enumerate(preview_files, 1):\n    plt.subplot(2, 4, i)\n    \n    img = imread(os.path.join(INPUT_PATH, image_file))\n    plt.imshow(img)\n    \n    label = image_file.split('.')[0]\n    plt.title(label)\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:34.947678Z","iopub.execute_input":"2024-01-16T00:37:34.947947Z","iopub.status.idle":"2024-01-16T00:37:36.873843Z","shell.execute_reply.started":"2024-01-16T00:37:34.947923Z","shell.execute_reply":"2024-01-16T00:37:36.872941Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Data Target Balance Analysis\n\nIn this section, we will delve into the examination of data target balance.\n\nHint: For the preprocessing step, we will leverage Keras's built-in preprocessing tool, the \"ImageDataGenerator.\" To generate data batches for feeding into our model, the \"ImageDataGenerator\" object offers a method named \"flow_from_directory().\" This method is particularly useful when our data is organized as images within a specific directory.\n\nHowever, a common scenario involves the need for a separate directory for validation images when using \"flow_from_directory().\" Creating this directory and populating it with the corresponding data might consume additional time. Fortunately, there exists an alternative method, namely \"flow_from_dataframe(),\" which proves to be highly efficient in this context.","metadata":{}},{"cell_type":"code","source":"# We'll start by creating our dataframe, it should contain 2 colomns: filename, class or label.\n\nlabels = [file.split('.')[0] for file in image_files]\n\nraw_data = pd.DataFrame({'filename': image_files, 'label': labels})\n\nraw_data.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:36.875207Z","iopub.execute_input":"2024-01-16T00:37:36.875537Z","iopub.status.idle":"2024-01-16T00:37:36.899634Z","shell.execute_reply.started":"2024-01-16T00:37:36.875510Z","shell.execute_reply":"2024-01-16T00:37:36.898642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"raw_data.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:36.902269Z","iopub.execute_input":"2024-01-16T00:37:36.902853Z","iopub.status.idle":"2024-01-16T00:37:36.941517Z","shell.execute_reply.started":"2024-01-16T00:37:36.902826Z","shell.execute_reply":"2024-01-16T00:37:36.940593Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Excellent! The dataset aligns with the initial number of pictures.","metadata":{}},{"cell_type":"code","source":"raw_data.tail(7)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:36.942798Z","iopub.execute_input":"2024-01-16T00:37:36.943135Z","iopub.status.idle":"2024-01-16T00:37:36.952263Z","shell.execute_reply.started":"2024-01-16T00:37:36.943102Z","shell.execute_reply":"2024-01-16T00:37:36.951272Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"value_counts = raw_data['label'].value_counts()\n\n# Create a bar plot using Seaborn\nplt.figure(figsize=(6, 4))\nsns.barplot(x=value_counts.index, y=value_counts.values)\nplt.xlabel('Values')\nplt.ylabel('Count')\nplt.title('Data targets balance')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:36.953458Z","iopub.execute_input":"2024-01-16T00:37:36.953767Z","iopub.status.idle":"2024-01-16T00:37:37.185086Z","shell.execute_reply.started":"2024-01-16T00:37:36.953730Z","shell.execute_reply":"2024-01-16T00:37:37.184206Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Fantastic! The labels in our dataset are well-balanced, indicating that we are effectively mitigating the risk of overfitting in our model. Keep up the good work!\n\n\nNext, we will delve into the metadata of our images to gain insights. Our focus will be on analyzing the dimensions, including height, width, and channels.","metadata":{}},{"cell_type":"code","source":"# We will take a sample of size of 10% of our total data: 2500 images.\n\nsample_size = 10000\nsample_images = raw_data['filename'].sample(sample_size).tolist()\n\nheights = []\nwidths = []\nchannels = []\n\nfor image_file in sample_images:\n    img = cv2.imread(os.path.join(INPUT_PATH, image_file))\n    \n    height, width, channel = img.shape\n    heights.append(height)\n    widths.append(width)\n    channels.append(channel)\n\n\nsample_dimensions_df = pd.DataFrame({'Filename': sample_images, 'Height': heights, 'Width': widths, 'Channels': channels})\nsample_dimensions_df['Aspect_ratio'] = sample_dimensions_df['Width']/sample_dimensions_df['Height']\nsample_dimensions_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:37.186157Z","iopub.execute_input":"2024-01-16T00:37:37.186452Z","iopub.status.idle":"2024-01-16T00:37:54.589416Z","shell.execute_reply.started":"2024-01-16T00:37:37.186426Z","shell.execute_reply":"2024-01-16T00:37:54.588414Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_dimensions_df['Channels'].unique()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:54.591053Z","iopub.execute_input":"2024-01-16T00:37:54.591441Z","iopub.status.idle":"2024-01-16T00:37:54.598298Z","shell.execute_reply.started":"2024-01-16T00:37:54.591404Z","shell.execute_reply":"2024-01-16T00:37:54.597314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(18, 6))\nplt.subplot(1, 3, 1)\nplt.hist(sample_dimensions_df['Height'], bins=30, color='skyblue', edgecolor='black')\nplt.title('Distribution of Sample Heights')\nplt.xlabel('Height')\nplt.ylabel('Frequency')\n\nplt.subplot(1, 3, 2)\nplt.hist(sample_dimensions_df['Width'], bins=30, color='lightcoral', edgecolor='black')\nplt.title('Distribution of Sample Widths')\nplt.xlabel('Width')\nplt.ylabel('Frequency')\n\nplt.subplot(1, 3, 3)\nplt.hist(sample_dimensions_df['Aspect_ratio'], bins=30, color='orange', edgecolor='black')\nplt.title('Distribution of Sample Aspect_ratio')\nplt.xlabel('Aspect_ratio')\nplt.ylabel('Frequency')\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:54.599717Z","iopub.execute_input":"2024-01-16T00:37:54.600068Z","iopub.status.idle":"2024-01-16T00:37:55.486552Z","shell.execute_reply.started":"2024-01-16T00:37:54.600035Z","shell.execute_reply":"2024-01-16T00:37:55.485609Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Insights:\n\nIn the gathered sample, all images consistently have 3 color channels.\n\nImage heights and widths exhibit variability within the range of 30 to 1000. However, the model we are aiming to build requires a uniform input shape. Addressing this challenge involves selecting a common shape for all the data. The question then arises: What should be the chosen shape?","metadata":{}},{"cell_type":"code","source":"sample_dimensions_df.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:55.487878Z","iopub.execute_input":"2024-01-16T00:37:55.488231Z","iopub.status.idle":"2024-01-16T00:37:55.512066Z","shell.execute_reply.started":"2024-01-16T00:37:55.488197Z","shell.execute_reply":"2024-01-16T00:37:55.511135Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"np.quantile(heights, 0.95) , np.quantile(widths, 0.95), np.quantile(sample_dimensions_df['Aspect_ratio'], 0.95)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:55.513433Z","iopub.execute_input":"2024-01-16T00:37:55.514066Z","iopub.status.idle":"2024-01-16T00:37:55.524367Z","shell.execute_reply.started":"2024-01-16T00:37:55.514030Z","shell.execute_reply":"2024-01-16T00:37:55.523211Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Looks like the majority of images heights and widths are both located around 500, and the width is 1.5 times bigger than the height.\n\nThe debate here is \"what is the proper shape to chose?\", knowing that the priority is to preserve the maximum amount of details, excessive downsizing images causes loss of important details, upscaling images can be even worse as the interpolation method (param of flow_from_dataframe()) for upscaling determines the outcome of the resize operation in terms of details preservation(sharpness/blur/blocky artifacts)/speed of processing/ computational expense. It is also worth mentioning that aspect ratio preservation is a crucial point, to learn more about it take a look [here](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#filters-comparison-table).\n\nLooking at our sample description, the aspect ratio of most data is superior to 1 (width >= height), and only fewer than 25% of the data has an aspect ratio that is inferior than 1, that means most of our data have tendency to be landscape oriented (minority are portrait oriented or width < height)\n\n\nCombinations to try: \n\nResize shapes: (384, 256,3) (256,256,3), (128,128,3),..\n\nInterpolations: Bilinear, Bicubic, Lanczos,..","metadata":{}},{"cell_type":"markdown","source":"Before we move to preprocessing step, it's a good practice to see how images would look if we resize them, let's find images that have minimum and maximum heights (width is worth trying too).","metadata":{}},{"cell_type":"code","source":"min_height_row = sample_dimensions_df.loc[sample_dimensions_df['Height'].idxmin()]\nmax_height_row = sample_dimensions_df.loc[sample_dimensions_df['Height'].idxmax()]\n\nmin_height_row, max_height_row","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:55.525774Z","iopub.execute_input":"2024-01-16T00:37:55.526075Z","iopub.status.idle":"2024-01-16T00:37:55.535793Z","shell.execute_reply.started":"2024-01-16T00:37:55.526047Z","shell.execute_reply":"2024-01-16T00:37:55.535026Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"target_size = (384, 256)\n\nmin_height_img = Image.open(os.path.join(INPUT_PATH , min_height_row['Filename']))\nmax_height_img = Image.open(os.path.join(INPUT_PATH, max_height_row['Filename']))\n\n\nplt.figure(figsize=(12, 8))\n\nplt.subplot(2, 2, 1)\nplt.imshow(min_height_img)\nplt.title(f\"Image with Minimum Height\\nOriginal\")\n\nplt.subplot(2, 2, 2)\nplt.imshow(max_height_img)\nplt.title(f\"Image with Maximum Height\\nOriginal\")\n\n# Resize imagesm you can try resample=Image.LANCZOS OR Image.BILINEAR\nmin_height_resized = min_height_img.resize(target_size, resample=Image.BICUBIC)\nmax_height_resized = max_height_img.resize(target_size, resample=Image.BICUBIC)\n\nplt.subplot(2, 2, 3)\nplt.imshow(min_height_resized)\nplt.title(f\"Resized Image with Minimum Height\\n({target_size[0]}, {target_size[1]})\")\n\nplt.subplot(2, 2, 4)\nplt.imshow(max_height_resized)\nplt.title(f\"Resized Image with Maximum Height\\n({target_size[0]}, {target_size[1]})\")\n\nplt.tight_layout()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:55.541201Z","iopub.execute_input":"2024-01-16T00:37:55.541554Z","iopub.status.idle":"2024-01-16T00:37:56.649574Z","shell.execute_reply.started":"2024-01-16T00:37:55.541524Z","shell.execute_reply":"2024-01-16T00:37:56.648117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As we can see, small images don't give a bit information, even if so, it would be very hard to interpret by humans eye, so we can imaging how hard it would be for the machine, we will deal with this kind of images by removing outliers!\n\nA note about interpolation, lanczos method is a bit sharper and give better result, while bicubic is good as well but a bit blury. In terms of performance, no difference have been noticed so far.\n\nwe are still working with our sample dimensions data, now we will remove outliers, to keep only images with height > 100 and a width > 150 (width = 1.5 * height), for big size images we will keep them as they conserve details after resize!","metadata":{}},{"cell_type":"code","source":"filtered_dimensions_df = sample_dimensions_df[(sample_dimensions_df['Height'] > 100) & (sample_dimensions_df['Width'] > 150)]\nfiltered_dimensions_df.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:56.650855Z","iopub.execute_input":"2024-01-16T00:37:56.651150Z","iopub.status.idle":"2024-01-16T00:37:56.678179Z","shell.execute_reply.started":"2024-01-16T00:37:56.651123Z","shell.execute_reply":"2024-01-16T00:37:56.677313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Looking at the description of the results, we have lost just a few images (we still have 9760 out of 10000).\n\nThat's quite a filtering using width and height of images! Now how about Aspect_ratio ! let's take a look ^^","metadata":{}},{"cell_type":"code","source":"sorted_dimensions_df = filtered_dimensions_df.sort_values(by='Aspect_ratio', ascending=False)\nsorted_dimensions_df.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:56.679393Z","iopub.execute_input":"2024-01-16T00:37:56.679767Z","iopub.status.idle":"2024-01-16T00:37:56.692485Z","shell.execute_reply.started":"2024-01-16T00:37:56.679732Z","shell.execute_reply":"2024-01-16T00:37:56.691371Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.imshow(imread(os.path.join(INPUT_PATH, \"cat.5351.jpg\")))","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:56.693520Z","iopub.execute_input":"2024-01-16T00:37:56.693784Z","iopub.status.idle":"2024-01-16T00:37:56.966671Z","shell.execute_reply.started":"2024-01-16T00:37:56.693761Z","shell.execute_reply":"2024-01-16T00:37:56.965734Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"What a surprise! Our data may still contain images that are not relevant! eventhough it may not be a good idea to resize images that have aspect_ratio < 0.7 and aspect_ratio > 3, because that kind of resize will only make image too much enlarged or condensed, thus loss of details or distortion!\n\nHere is a fair example:","metadata":{}},{"cell_type":"code","source":"tmp_image = Image.open(os.path.join(INPUT_PATH, \"cat.11060.jpg\"))\nplt.figure(figsize=(8,6))\nplt.subplot(1, 2, 1)\nplt.imshow(tmp_image)\nplt.title(\"Original image\")\nplt.subplot(1, 2, 2)\nplt.title(\"Resized image\")\nplt.imshow(tmp_image.resize(target_size, resample=Image.BICUBIC))","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:56.967885Z","iopub.execute_input":"2024-01-16T00:37:56.968169Z","iopub.status.idle":"2024-01-16T00:37:57.479173Z","shell.execute_reply.started":"2024-01-16T00:37:56.968143Z","shell.execute_reply":"2024-01-16T00:37:57.478290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"To solve this problem we will focus on images that have 0.5 < aspect_ratio < 2.2 like it is show in the aspect_ratio distribution plot above !","metadata":{}},{"cell_type":"code","source":"selected_aspect_ratio_df = filtered_dimensions_df[(filtered_dimensions_df['Aspect_ratio'] > 0.5) & (filtered_dimensions_df['Aspect_ratio'] < 2.2)]\nselected_aspect_ratio_df.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:57.480391Z","iopub.execute_input":"2024-01-16T00:37:57.480720Z","iopub.status.idle":"2024-01-16T00:37:57.505430Z","shell.execute_reply.started":"2024-01-16T00:37:57.480686Z","shell.execute_reply":"2024-01-16T00:37:57.504602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"After applying the filter based on aspect ratio, we see that we haven't lost a lot of data ! So far so good!\n\nNow we are ready to go to preprocessing part.","metadata":{}},{"cell_type":"markdown","source":"# Data Preprocessing","metadata":{}},{"cell_type":"markdown","source":"In this part, we will apply all info and insights we have seen before to our original data, because we were dealing only with a sample.\n\n\nFirst thing is to gather images specs (width, height)","metadata":{}},{"cell_type":"code","source":"data_images = raw_data['filename'].tolist()\n\nheights = []\nwidths = []\n\nfor image_file in data_images:\n    img = cv2.imread(os.path.join(INPUT_PATH, image_file))\n    \n    height, width, _ = img.shape\n    heights.append(height)\n    widths.append(width)\n\n\ndata_df = pd.DataFrame({'filename': data_images, 'Height': heights, 'Width': widths})\ndata_df['Aspect_ratio'] = data_df['Width']/data_df['Height']\ndata_df = data_df.merge(raw_data, how=\"right\", on='filename')\ndata_df.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:37:57.506749Z","iopub.execute_input":"2024-01-16T00:37:57.507037Z","iopub.status.idle":"2024-01-16T00:38:40.747607Z","shell.execute_reply.started":"2024-01-16T00:37:57.507010Z","shell.execute_reply":"2024-01-16T00:38:40.746616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Filtering and cleaning","metadata":{}},{"cell_type":"code","source":"filtered_data_df = data_df[(data_df['Height'] > 100) & \n                           (data_df['Width'] > 150) & \n                           (data_df['Aspect_ratio'] > 0.5) & \n                           (data_df['Aspect_ratio'] < 2.2)]\nfiltered_data_df.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:40.748807Z","iopub.execute_input":"2024-01-16T00:38:40.749090Z","iopub.status.idle":"2024-01-16T00:38:40.774088Z","shell.execute_reply.started":"2024-01-16T00:38:40.749067Z","shell.execute_reply":"2024-01-16T00:38:40.773290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"A quick check of dataset target balance to see if the filtering has impacted our data negatively","metadata":{}},{"cell_type":"code","source":"filtered_data_df['label'].value_counts().sort_index().plot(kind='bar', color=['skyblue', 'orange'])\n\nplt.title('Filtered data targets balance')\nplt.xlabel('Label')\nplt.ylabel('Count')","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:40.775053Z","iopub.execute_input":"2024-01-16T00:38:40.775316Z","iopub.status.idle":"2024-01-16T00:38:41.036584Z","shell.execute_reply.started":"2024-01-16T00:38:40.775293Z","shell.execute_reply":"2024-01-16T00:38:41.035770Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Sounds perfect! we are good to go.","metadata":{}},{"cell_type":"markdown","source":"## Shuffling","metadata":{}},{"cell_type":"markdown","source":"We have to shuffle our data before splitting it to train and validation to avoid any possible previous order.","metadata":{}},{"cell_type":"code","source":"shuffled_data = filtered_data_df.sample(frac = 1).reset_index(drop=True)\nshuffled_data.describe()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.037784Z","iopub.execute_input":"2024-01-16T00:38:41.038379Z","iopub.status.idle":"2024-01-16T00:38:41.074813Z","shell.execute_reply.started":"2024-01-16T00:38:41.038348Z","shell.execute_reply":"2024-01-16T00:38:41.073714Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"From now on, we won't be needing the columns \"Height\", \"Width\" and \"Aspect_ratio\", as they were useful for filtering purposes only!","metadata":{}},{"cell_type":"code","source":"# making a checkpoint\ndata = shuffled_data.copy()\n\n# droping columns\ndata = data.drop(['Height', 'Width', 'Aspect_ratio'], axis=1)\ndata.head()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.076285Z","iopub.execute_input":"2024-01-16T00:38:41.076687Z","iopub.status.idle":"2024-01-16T00:38:41.093407Z","shell.execute_reply.started":"2024-01-16T00:38:41.076651Z","shell.execute_reply":"2024-01-16T00:38:41.092463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Splitting","metadata":{}},{"cell_type":"code","source":"data_length = len(data.index)\ndata_length","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.094686Z","iopub.execute_input":"2024-01-16T00:38:41.095325Z","iopub.status.idle":"2024-01-16T00:38:41.101640Z","shell.execute_reply.started":"2024-01-16T00:38:41.095286Z","shell.execute_reply":"2024-01-16T00:38:41.100666Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We will split data to 2 parts: 80% for train and 20% for validation","metadata":{}},{"cell_type":"code","source":"train_len = int(data_length/10*8)\n\ntrain_data = data.iloc[:train_len,:]\nvalid_data = data.iloc[train_len:,:]\n\ntrain_data.shape , valid_data.shape","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.103036Z","iopub.execute_input":"2024-01-16T00:38:41.103443Z","iopub.status.idle":"2024-01-16T00:38:41.114201Z","shell.execute_reply.started":"2024-01-16T00:38:41.103406Z","shell.execute_reply":"2024-01-16T00:38:41.113389Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_data.shape[0]/data_length , valid_data.shape[0]/data_length","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.115268Z","iopub.execute_input":"2024-01-16T00:38:41.115623Z","iopub.status.idle":"2024-01-16T00:38:41.126019Z","shell.execute_reply.started":"2024-01-16T00:38:41.115597Z","shell.execute_reply":"2024-01-16T00:38:41.125215Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Approximately the split we wanted !","metadata":{}},{"cell_type":"markdown","source":"# Data Augmentation\n\nData augmentation is a technique commonly used in machine learning, especially in the field of computer vision, to artificially increase the diversity of a training dataset by applying various transformations to the existing data, in our case it means applying transofrmations to data images like zoom, rotation and flips, resize,...\n\nFortunately we don't have to do it manually, there are methods that can that for us.","metadata":{}},{"cell_type":"code","source":"datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=10,\n    zoom_range=0.1,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    brightness_range=(0.8,1.2),\n    shear_range=0.2,\n    horizontal_flip=True,\n    vertical_flip=False,\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.127237Z","iopub.execute_input":"2024-01-16T00:38:41.127576Z","iopub.status.idle":"2024-01-16T00:38:41.135172Z","shell.execute_reply.started":"2024-01-16T00:38:41.127548Z","shell.execute_reply":"2024-01-16T00:38:41.134375Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# interpolation : \"nearest\", \"bilinear\", and \"bicubic\". If PIL version 1.1.3 or newer is installed, \"lanczos\"\ninput_shape = (384, 256)\n\ntrain_it = datagen.flow_from_dataframe(\n    train_data,\n    directory=INPUT_PATH,\n    x_col='filename',\n    y_col='label',\n    target_size=input_shape,\n    color_mode='rgb',\n    class_mode='binary',\n    batch_size=32,\n    shuffle=True,\n    seed=42,\n    interpolation='bicubic',\n    validate_filenames=False\n)\n\nvalid_it = datagen.flow_from_dataframe(\n    valid_data,\n    directory=INPUT_PATH,\n    x_col='filename',\n    y_col='label',\n    target_size=input_shape,\n    color_mode='rgb',\n    class_mode='binary',\n    batch_size=32,\n    shuffle=True,\n    seed=42,\n    interpolation='bicubic',\n    validate_filenames=False\n)","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.136160Z","iopub.execute_input":"2024-01-16T00:38:41.136448Z","iopub.status.idle":"2024-01-16T00:38:41.247141Z","shell.execute_reply.started":"2024-01-16T00:38:41.136418Z","shell.execute_reply":"2024-01-16T00:38:41.246415Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Modeling and Training\n\nIn this part we will create our model and train it using data generators we created (train_it, valid_it)","metadata":{}},{"cell_type":"code","source":"model = Sequential()\nmodel.add(Conv2D(64, (3, 3), strides=1, padding=\"same\", activation=\"relu\", input_shape=(384, 256, 1)))\nmodel.add(BatchNormalization())\nmodel.add(MaxPool2D((2, 2), strides=2, padding=\"same\"))\nmodel.add(Conv2D(48, (3, 3), strides=1, padding=\"same\", activation=\"relu\"))\nmodel.add(Dropout(0.2))\nmodel.add(BatchNormalization())\nmodel.add(MaxPool2D((2, 2), strides=2, padding=\"same\"))\nmodel.add(Conv2D(24, (3, 3), strides=1, padding=\"same\", activation=\"relu\"))\nmodel.add(BatchNormalization())\nmodel.add(MaxPool2D((2, 2), strides=2, padding=\"same\"))\nmodel.add(Flatten())\nmodel.add(Dense(units=128, activation=\"relu\"))\nmodel.add(Dropout(0.3))\nmodel.add(Dense(units=1, activation=\"sigmoid\"))\n\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.248235Z","iopub.execute_input":"2024-01-16T00:38:41.248543Z","iopub.status.idle":"2024-01-16T00:38:41.420289Z","shell.execute_reply.started":"2024-01-16T00:38:41.248517Z","shell.execute_reply":"2024-01-16T00:38:41.417602Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.compile(optimizer=RMSprop(learning_rate = .00001),\n              loss=BinaryCrossentropy(from_logits=False),\n              metrics=[BinaryAccuracy()])","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.421321Z","iopub.execute_input":"2024-01-16T00:38:41.421598Z","iopub.status.idle":"2024-01-16T00:38:41.439324Z","shell.execute_reply.started":"2024-01-16T00:38:41.421574Z","shell.execute_reply":"2024-01-16T00:38:41.438193Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# model.fit(train_it, validation_data=valid_it, epochs=20,\n#           steps_per_epoch=train_it.samples/train_it.batch_size,\n#           validation_steps=valid_it.samples/valid_it.batch_size,\n#          )","metadata":{"execution":{"iopub.status.busy":"2024-01-16T00:38:41.440785Z","iopub.execute_input":"2024-01-16T00:38:41.441367Z","iopub.status.idle":"2024-01-16T00:38:41.445533Z","shell.execute_reply.started":"2024-01-16T00:38:41.441335Z","shell.execute_reply":"2024-01-16T00:38:41.444557Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}