{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":30746,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-08-09T01:45:10.080343Z","iopub.execute_input":"2024-08-09T01:45:10.080669Z","iopub.status.idle":"2024-08-09T01:45:11.174441Z","shell.execute_reply.started":"2024-08-09T01:45:10.080640Z","shell.execute_reply":"2024-08-09T01:45:11.173515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nfrom os import makedirs, listdir\nfrom shutil import copyfile\nfrom random import seed, random\n\nimport numpy as np #package for scientific computing with Python\nimport pandas as pd # Dataframe package for python\n\n# Import image and plotting tools\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom matplotlib.image import imread\nfrom PIL import Image\n\n# Tensorflow \nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array\nfrom tensorflow.keras.layers import Dense,MaxPooling2D,Dropout,Flatten,BatchNormalization,Conv2D\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau,EarlyStopping","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:11.176042Z","iopub.execute_input":"2024-08-09T01:45:11.176408Z","iopub.status.idle":"2024-08-09T01:45:25.470297Z","shell.execute_reply.started":"2024-08-09T01:45:11.176383Z","shell.execute_reply":"2024-08-09T01:45:25.469518Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Read Dataset","metadata":{}},{"cell_type":"code","source":"train_path = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip\"\ntest_path = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip\"\n\nfiles = \"/kaggle/working/\"\n\nimport zipfile\n\nwith zipfile.ZipFile(train_path, 'r') as zipp:\n    zipp.extractall(files)\n    \nwith zipfile.ZipFile(test_path, 'r') as zipp:\n    zipp.extractall(files)","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:25.471372Z","iopub.execute_input":"2024-08-09T01:45:25.471889Z","iopub.status.idle":"2024-08-09T01:45:40.414961Z","shell.execute_reply.started":"2024-08-09T01:45:25.471864Z","shell.execute_reply":"2024-08-09T01:45:40.413967Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Let's build our datasets\n\nFirst let's build the train dataset. This is the one that we will show to the model. Since we are classifying dogs and cats, we need to tag every image with the right label <dog, cat>. We are going to create a pandas dataframe to store this data.\n","metadata":{}},{"cell_type":"code","source":"image_dir = \"../working/train/\"\n\nfilenames = os.listdir(image_dir)\n\nlabels = [x.split(\".\")[0] for x in filenames]\n\ntrain = pd.DataFrame({\"filename\": filenames, \"label\": labels})\n\ntrain.head()","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:40.417398Z","iopub.execute_input":"2024-08-09T01:45:40.417703Z","iopub.status.idle":"2024-08-09T01:45:40.467079Z","shell.execute_reply.started":"2024-08-09T01:45:40.417678Z","shell.execute_reply":"2024-08-09T01:45:40.466214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now, let's see how our images are","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20,20)) # create a figure space for ploting images\nplt.subplots_adjust(hspace=0.4)\n\nfor index, row in train.iterrows():\n    if index > 9:\n        break\n    plt.subplot(1,10,index+1)\n    filename = 'train/' + row[\"filename\"]\n    image = imread(filename)\n    \n    plt.imshow(image)\n    plt.title(row[\"label\"],fontsize=12)\n    plt.axis('off')\n    \n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:40.468008Z","iopub.execute_input":"2024-08-09T01:45:40.468295Z","iopub.status.idle":"2024-08-09T01:45:41.379049Z","shell.execute_reply.started":"2024-08-09T01:45:40.468272Z","shell.execute_reply":"2024-08-09T01:45:41.378179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Optional step\n\nSometimes you will want to validate your model while training. To do that, we usually divide our training dataset into 2 datasets: train and validation.\n\n'test_size=0.2': 20% of the data will be moved to validation dataset\n\n'stratify=labels': if you don't set this, your validation dataset can end up with more images of cats or dogs. By setting this to an array\nof the labels from our dataset, we are ensuring that the division os datasets will keep the same cats x dogs ratio\n\n'random_state=42': if you set this one, every time you run this function, with the same parameter, you will get the same result.","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nlabels = train['label']\n\ntrain, val = train_test_split(train, test_size=0.2, stratify=labels, random_state = 42)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:41.380283Z","iopub.execute_input":"2024-08-09T01:45:41.380626Z","iopub.status.idle":"2024-08-09T01:45:41.588656Z","shell.execute_reply.started":"2024-08-09T01:45:41.380596Z","shell.execute_reply":"2024-08-09T01:45:41.587787Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Now we need to augment the images\nMore often than not, you will need to perform some sort of pre-processing on your dataset. You might want to remove as much noise as possible from your images, rescale the images to fit your memory, change some image features (colors, orientation, rotation) to create more entropy for your model to learn, etc.\n\nThis is also an optional step, and usually takes some time and some prior knowledge to get the right transforms to apply to your dataset. ","metadata":{}},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255, \n    horizontal_flip = True,\n#     vertical_flip = True,\n    rotation_range = 15,\n    zoom_range = 0.2, \n    shear_range = 0.1, \n    fill_mode = 'nearest', \n    width_shift_range = 0.1,\n    height_shift_range = 0.1\n)\n\n# https://faroit.com/keras-docs/1.2.2/preprocessing/image/ for more details on tranformations\n\n\n# IF YOU CREATED THE VALIDATION DATASET, UNCOMMENT THESE NEXT LINES\n\nval_datagen = ImageDataGenerator(\n    rescale=1./255\n)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:41.589780Z","iopub.execute_input":"2024-08-09T01:45:41.590217Z","iopub.status.idle":"2024-08-09T01:45:42.341378Z","shell.execute_reply.started":"2024-08-09T01:45:41.590179Z","shell.execute_reply":"2024-08-09T01:45:42.340472Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's see how our images look like after we apply transformations to them","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(20, 5))\nplt.subplots_adjust(hspace=0.4)\n\nfor i in range(10):\n    filename = train.iloc[i]['filename']\n    \n    img_path = os.path.join(image_dir, filename)\n    img = load_img(img_path, target_size=(128, 128))\n    img_array = img_to_array(img) / 255.0\n\n    plt.subplot(2, 10, i + 1)\n    plt.imshow(img_array)\n    plt.axis('off')\n    plt.title(\"before\")\n    \n    img_array = img_to_array(img)  \n    img_array = img_array.reshape((1,) + img_array.shape) \n    aug_iter = train_datagen.flow(img_array, batch_size=1) \n    aug_img = next(aug_iter)[0] \n    \n    plt.subplot(2, 10, i + 11)\n    plt.imshow(aug_img)\n    plt.axis('off')\n    plt.title(\"after\")\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:45:42.342584Z","iopub.execute_input":"2024-08-09T01:45:42.342866Z","iopub.status.idle":"2024-08-09T01:45:43.920670Z","shell.execute_reply.started":"2024-08-09T01:45:42.342842Z","shell.execute_reply":"2024-08-09T01:45:43.919736Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we need to create a \"data flow\" for the model training. ","metadata":{}},{"cell_type":"code","source":"image_size = 128 \nimage_channel = 3 \nbatch_size = 10 \n\ntrain_generator = train_datagen.flow_from_dataframe(train, # the actual data\n                                                    directory = 'train/', # where the data is\n                                                    x_col= 'filename', # the data points (in this case, images)\n                                                    y_col= 'label', # The ground-truth, so that the model can learn\n                                                    batch_size = batch_size,# how many images to use at the same time\n                                                    target_size = (image_size,image_size), # the image size that we are going to use\n                                                    shuffle=True, # shuffle the data before feeding to the model\n                                                   )\n\n\n# IF YOU CREATED THE VALIDATION DATASET, UNCOMMENT THESE NEXT LINES\n\nval_generator = val_datagen.flow_from_dataframe(val, \n                                                directory = 'train/',\n                                                x_col= 'filename',\n                                                y_col= 'label',\n                                                batch_size = batch_size,\n                                                target_size = (image_size,image_size),\n                                                shuffle=False \n                                               )\n\n","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:47:27.343011Z","iopub.execute_input":"2024-08-09T01:47:27.343946Z","iopub.status.idle":"2024-08-09T01:47:27.642536Z","shell.execute_reply.started":"2024-08-09T01:47:27.343909Z","shell.execute_reply":"2024-08-09T01:47:27.641809Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Now, let's create a model from scratch\n\nWe will use a standard convolution neural network as a base model. Feel free to change it or to get some pre-trained model from the internet.","metadata":{}},{"cell_type":"code","source":"model = Sequential([\n    Conv2D(32,(3,3),activation='relu',input_shape = (image_size,image_size,image_channel)),\n    MaxPooling2D(pool_size=(2,2)),\n    Conv2D(64,(3,3),activation='relu'),\n    MaxPooling2D(pool_size=(2,2)),\n    Conv2D(128,(3,3),activation='relu'),\n    MaxPooling2D(pool_size=(2,2)),\n    Conv2D(256,(3,3),activation='relu'),\n    MaxPooling2D(pool_size=(2,2)),\n    Flatten(),\n    Dense(512,activation='relu'),\n    Dropout(0.2),\n    Dense(2,activation='softmax'),\n])\n\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:47:27.835720Z","iopub.execute_input":"2024-08-09T01:47:27.836048Z","iopub.status.idle":"2024-08-09T01:47:27.916955Z","shell.execute_reply.started":"2024-08-09T01:47:27.836010Z","shell.execute_reply":"2024-08-09T01:47:27.916170Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Before we do the actual training, we need to define a few paramenters:\n\n'**optimizer**': The algorithm used to do the gradient descent. don't tell this to anyone, but basically, ML is just a fancy word for gradient descent. \n\n'**learning_rate**': This factors how fast your model \"learns\". Usually you need to tune this parameter when your model feels like to be stuck in it's \"learning\". Also, just a reminder that fast does not mean good.\n\n'**loss**': How you are going to calculate the training error. Usually we use some sort of entropy to do this.\n\n'**metrics**': what we will be using to assess the models performance\n\n'**step**': every time you present the model with some data point\n\n'**epoch**': when you present all data to the model, it will calculate its errors and adjust the weights.\n\n","metadata":{}},{"cell_type":"code","source":"learning_rate_reduction = ReduceLROnPlateau(monitor = 'val_accuracy', # what metric you want to track [train, val]_[metric]\n                                            patience=2, # number of epochs to wait without any changes to the metrics\n                                            factor=0.5, # factor by which the learning rate will be reduced\n                                            min_lr = 0.00001, # the min possible value for learning rate\n                                            verbose = 1) \n\n# this function will stop training, if no improvement happens for 'patience' epochs\nearly_stoping = EarlyStopping(monitor='val_loss',patience= 3,restore_best_weights=True, mode='min', verbose=0) \n\nmodel.compile(optimizer='adam',loss='binary_crossentropy',metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:47:28.796952Z","iopub.execute_input":"2024-08-09T01:47:28.797309Z","iopub.status.idle":"2024-08-09T01:47:28.808543Z","shell.execute_reply.started":"2024-08-09T01:47:28.797280Z","shell.execute_reply":"2024-08-09T01:47:28.807710Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now let's train the model!!!!","metadata":{}},{"cell_type":"code","source":"cat_dog = model.fit(train_generator,\n                    validation_data  = val_generator, # UNCOMMENT IF USING VALIDATION DATASET\n                    callbacks=[early_stoping,learning_rate_reduction],\n                    epochs = 10,\n                    # steps_per_epoch = len(train_generator),\n                    # validation_steps = len(val_generaotor),\n                   )\n","metadata":{"execution":{"iopub.status.busy":"2024-08-09T01:47:30.038622Z","iopub.execute_input":"2024-08-09T01:47:30.038980Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Let's see how entropy and accuracy changed over epochs","metadata":{}},{"cell_type":"code","source":"error = pd.DataFrame(cat_dog.history)\n\nplt.figure(figsize=(18,5),dpi=200)\nsns.set_style('darkgrid')\n\nplt.subplot(121)\nplt.title('Cross Entropy Loss',fontsize=15)\nplt.xlabel('Epochs',fontsize=12)\nplt.ylabel('Loss',fontsize=12)\nplt.plot(error['loss'], label='loss')\n# plt.plot(error['val_loss'], label='val_loss')  # Uncomment if you have validation dataset\nplt.legend()\n\nplt.subplot(122)\nplt.title('Classification Accuracy',fontsize=15)\nplt.xlabel('Epochs',fontsize=12)\nplt.ylabel('Accuracy',fontsize=12)\nplt.plot(error['accuracy'], label='accuracy')\n# plt.plot(error['val_accuracy'], label='val_accuracy') # Uncomment if you have validation dataset\nplt.legend()\n\nplt.show()\n\n\n\nloss,acc = model.evaluate(train_generator,batch_size = batch_size, verbose = 0)\nprint('The accuracy of the model for training data is:',acc*100)\nprint('The Loss of the model for training data is:',loss)\n\n# Uncomment if you have validation dataset\n# loss,acc = model.evaluate(val_generator,batch_size = batch_size, verbose = 0)\n# print('The accuracy of the model for validation data is:',acc*100)\n# print('The Loss of the model for validation data is:',loss)","metadata":{"execution":{"iopub.status.busy":"2024-08-09T00:25:25.403139Z","iopub.execute_input":"2024-08-09T00:25:25.403749Z","iopub.status.idle":"2024-08-09T00:25:26.868032Z","shell.execute_reply.started":"2024-08-09T00:25:25.403703Z","shell.execute_reply":"2024-08-09T00:25:26.866669Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Now, let's test our model with the test dataset","metadata":{}},{"cell_type":"code","source":"test_dir = \"../working/test/\"\ntest_data = pd.DataFrame({\"filename\": os.listdir(test_dir)})\ntest_data['label'] = 'unknown'\n\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\ntest_idg =  test_datagen.flow_from_dataframe(test_data, \n                                     \"test/\", \n                                     x_col= \"filename\",\n                                     y_col = 'label',\n                                     batch_size = batch_size,\n                                     target_size=(image_size, image_size), \n                                     shuffle = False)\n","metadata":{"execution":{"iopub.status.busy":"2024-08-09T00:25:54.954411Z","iopub.execute_input":"2024-08-09T00:25:54.954850Z","iopub.status.idle":"2024-08-09T00:25:55.159883Z","shell.execute_reply.started":"2024-08-09T00:25:54.954813Z","shell.execute_reply":"2024-08-09T00:25:55.158364Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_predict = model.predict(test_idg,verbose = 0)\n\ntest_predict_argmax = np.argmax(test_predict, axis=1)\n\ny_test_pred = test_predict_argmax\n\ntest_data['label'] = y_test_pred","metadata":{"execution":{"iopub.status.busy":"2024-08-09T00:25:55.314023Z","iopub.execute_input":"2024-08-09T00:25:55.314534Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"label_mapping = {0: 'cat', 1: 'dog'}\ntest_data['label'] = test_data['label'].map(label_mapping)\ntest_data.head()\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, axes = plt.subplots(1, 10, figsize=(20, 4))\nfor idx in range(10):\n    image_path = os.path.join(test_dir, test_data.iloc[idx]['filename'])\n    image = Image.open(image_path)\n    axes[idx].imshow(image)\n    axes[idx].set_title(\"Label: \" + test_data.iloc[idx]['label'])\n    axes[idx].axis('off')\nplt.show()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Submit to Kaggle Challenge","metadata":{}},{"cell_type":"code","source":"sub = pd.read_csv('/kaggle/input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv',index_col='id')\n\nsub['label'] = y_test_pred\n\nsub.to_csv('submission.csv',index=True)\n","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}