{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31011,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"The train folder contains 25,000 images of dogs and cats. Each image in this folder has the label as part of the filename. The test folder contains 12,500 images, named according to a numeric id.\nFor each image in the test set, you should predict a probability that the image is a dog (1 = dog, 0 = cat).  \nWe will use image size of (224 x 224) and batch size of 64.","metadata":{}},{"cell_type":"markdown","source":"<a id = 'sec1'></a>\n# Load Required Packages","metadata":{}},{"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 5GB 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":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:14:42.377557Z","iopub.execute_input":"2025-04-21T19:14:42.377935Z","iopub.status.idle":"2025-04-21T19:14:42.706756Z","shell.execute_reply.started":"2025-04-21T19:14:42.377913Z","shell.execute_reply":"2025-04-21T19:14:42.705833Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import cv2\nfrom tqdm import tqdm          #Used for the purpose of showing iterations getting loaded in bar kind of form\nfrom random import shuffle\nfrom zipfile import ZipFile\nimport glob\nimport matplotlib.pyplot as plt\n%matplotlib inline\n\nfrom sklearn.model_selection import train_test_split\n\nimport tensorflow as tf\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.applications.resnet50 import preprocess_input\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, Dropout\nfrom tensorflow.keras.callbacks import ModelCheckpoint\nfrom tensorflow.keras.optimizers import Adam, RMSprop\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img, img_to_array, load_img","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:14:42.708415Z","iopub.execute_input":"2025-04-21T19:14:42.708810Z","iopub.status.idle":"2025-04-21T19:14:59.346725Z","shell.execute_reply.started":"2025-04-21T19:14:42.708790Z","shell.execute_reply":"2025-04-21T19:14:59.345992Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"weight_loc = '../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5'  \nWe'll not be using this path directly because it will cause some dimension error instead we will download weights of imagenet.\nHere is the explanation:\nhttps://stackoverflow.com/questions/60119041/failed-to-load-keras-resnet50-model-offline-using-weight-file","metadata":{}},{"cell_type":"markdown","source":"<a id = 'sec2'></a>\n# Read the Data","metadata":{}},{"cell_type":"raw","source":"path = '../input/dogs-vs-cats-redux-kernels-edition/'","metadata":{}},{"cell_type":"markdown","source":"We will unzip train and test directory using zipfile library.","metadata":{}},{"cell_type":"code","source":"zip_file = glob.glob('../input/dogs-vs-cats-redux-kernels-edition/*.zip')  #return any files with .zip extension\nprint(zip_file)\n\n#extract file into a temp folder\ndef extract_zip(file):\n    with ZipFile(file,'r') as zip_ref:\n        zip_ref.extractall('.')\n        \n#extract both train and test1 zip\nfor files in zip_file:\n    extract_zip(files)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:14:59.347633Z","iopub.execute_input":"2025-04-21T19:14:59.348126Z","iopub.status.idle":"2025-04-21T19:15:16.979701Z","shell.execute_reply.started":"2025-04-21T19:14:59.348103Z","shell.execute_reply":"2025-04-21T19:15:16.979020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_path(directory):\n    path = []\n    for files in os.listdir(directory):\n        path.append(files)\n    return path\n\ntrain_path = get_path('../working/train')\ntest_path = get_path('../working/test')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:15:16.980497Z","iopub.execute_input":"2025-04-21T19:15:16.980868Z","iopub.status.idle":"2025-04-21T19:15:17.008827Z","shell.execute_reply.started":"2025-04-21T19:15:16.980842Z","shell.execute_reply":"2025-04-21T19:15:17.007912Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'sec3'></a>\n# Visualizing Data","metadata":{}},{"cell_type":"markdown","source":"First we will one hot encode the categories(cat and dog) for the purpose of data exploration.","metadata":{}},{"cell_type":"code","source":"#this labelling function is just for the visualization, we'll have separate one for preprocessing.\ndef label_img(img):\n    category = img.split('.')[-3]\n    if category == 'cat': return [1,0]\n    elif category == 'dog': return [0,1]\n\n#Process the data, here we're converting images into numpy array. This function takes image data, image directory, a boolean as an argument.\ndef process_data(img_data, data_dir, isTrain=True):\n    data_df = []\n    for img in tqdm(img_data):\n        path = os.path.join(data_dir,img)         #Assigning path to images by concatenating directory and images\n        if(isTrain):\n            label = label_img(img)            #Calling label_img to assign labels to image present in training directory\n        else:\n            label = img.split('.')[0]\n        img = cv2.imread(path,cv2.IMREAD_COLOR)\n        img = cv2.resize(img, (224,224))\n        data_df.append([np.array(img),np.array(label)])          #append image and labels as numpy array in data_df list\n    shuffle(data_df)\n    return data_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:15:17.010910Z","iopub.execute_input":"2025-04-21T19:15:17.011173Z","iopub.status.idle":"2025-04-21T19:15:17.017590Z","shell.execute_reply.started":"2025-04-21T19:15:17.011152Z","shell.execute_reply":"2025-04-21T19:15:17.016762Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#We will plot the images of dogs and cats and display the assigned label above image\ndef show_images(data, isTest=False):\n    f, ax = plt.subplots(nrows = 5, ncols = 5, figsize = (15,15))\n    for i,data in enumerate(data[:25]):              #enumerate helps in keeping track of count of iterations\n        img_num = data[1]\n        img_data = data[0]\n        label = np.argmax(img_num)                    #to get maximum indices of an array\n        if label  == 1: \n            str_label='Dog'\n        elif label == 0: \n            str_label='Cat'\n        if(isTest):\n            str_label=\"None\"\n        ax[i//5, i%5].imshow(img_data)\n        ax[i//5, i%5].axis('off')                 #removing axis for better look\n        ax[i//5, i%5].set_title(\"Label: {}\".format(str_label))\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:15:17.018384Z","iopub.execute_input":"2025-04-21T19:15:17.018720Z","iopub.status.idle":"2025-04-21T19:15:17.034634Z","shell.execute_reply.started":"2025-04-21T19:15:17.018698Z","shell.execute_reply":"2025-04-21T19:15:17.033765Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'subsec2'></a>\n<span style=\"color:red;font-size:20px\"> Visualizing Training Set </span>","metadata":{}},{"cell_type":"code","source":"train = process_data(train_path, './train/')\nshow_images(train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:15:17.035455Z","iopub.execute_input":"2025-04-21T19:15:17.035796Z","iopub.status.idle":"2025-04-21T19:15:51.472066Z","shell.execute_reply.started":"2025-04-21T19:15:17.035764Z","shell.execute_reply":"2025-04-21T19:15:51.470972Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'subsec3'></a>\n<span style=\"color:red;font-size:20px\"> Visualizing Test Set </span>","metadata":{}},{"cell_type":"code","source":"test = process_data(test_path, './test/', False)\nshow_images(test,True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:15:51.473252Z","iopub.execute_input":"2025-04-21T19:15:51.473700Z","iopub.status.idle":"2025-04-21T19:16:09.448913Z","shell.execute_reply.started":"2025-04-21T19:15:51.473660Z","shell.execute_reply":"2025-04-21T19:16:09.447675Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'sec4'></a>\n# Preprocessing Data","metadata":{}},{"cell_type":"markdown","source":"<a id = 'subsec11'></a>\n<span style=\"color:green;font-size:20px\"> Convert data into dataframe </span>\n\nIn our data formatting, we will generate the respective labels for dogs (1) and cats (0) for our training data. File path will also be collected as a column for our dataframe so that it can be used to load and train our images.  \n\nfile.split('.')[0] means -\n* the particular filename is a String separated by dots.\n* line.split(\".\")[0] returns the 1st item of the array. (the actual file looks like \"cat.134.jpg\")","metadata":{}},{"cell_type":"code","source":"#We will use below function to label data\ndef get_label(directory):\n    label = []\n    for file in os.listdir(directory):\n        if (file.split('.')[0] == 'dog'):\n            label.append(str(1))\n        elif (file.split('.')[0] == 'cat'):\n            label.append(str(0))\n    return label\n\nlabel = get_label('../working/train')\n\n#In case of train_test_split, allowed inputs are lists, numpy arrays, scipy-sparse matrices or pandas dataframes. Convert the images and labels into dataframe.\ndf = pd.DataFrame({'filename': train_path, 'label': label})\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:09.450081Z","iopub.execute_input":"2025-04-21T19:16:09.450567Z","iopub.status.idle":"2025-04-21T19:16:09.526305Z","shell.execute_reply.started":"2025-04-21T19:16:09.450529Z","shell.execute_reply":"2025-04-21T19:16:09.525579Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#split data into train and valid set\ntrain_df, valid_df = train_test_split(df, test_size = 0.2, stratify = df['label'], random_state = 123)\nprint(train_df.shape)\nprint(valid_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:09.527283Z","iopub.execute_input":"2025-04-21T19:16:09.528033Z","iopub.status.idle":"2025-04-21T19:16:09.565459Z","shell.execute_reply.started":"2025-04-21T19:16:09.528011Z","shell.execute_reply":"2025-04-21T19:16:09.564534Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'subsec12'></a>\n<span style=\"color:green;font-size:20px\"> Generating mini batches for training </span>  \n\nWe'll use ImageDataGenerator for preprocessing. It helps in data augmentation and helps to get better accuracy by making data flow in batches. Mini-Batch Gradient Descent uses a mini-batches of training example for each gradient calculation; which makes convergence faster, and at the same time, utilise the speed up from vectorization.","metadata":{}},{"cell_type":"markdown","source":"Here we'll assign preprocess_input to preprocessing_function argument of ImageDataGenerator because it has better advantage on accuracy.","metadata":{}},{"cell_type":"code","source":"#We'll perform individually on train and validation set.\ntrain_datagen = ImageDataGenerator(rotation_range = 10, zoom_range = 0.1, horizontal_flip = True, fill_mode = 'nearest', \n                                   width_shift_range = 0.1, height_shift_range = 0.1, preprocessing_function = preprocess_input)\n\n#flow_from_dataframe() method will accept dataframe with filenames as x_column and labels as y_column to generate mini-batches\ntrain_gen = train_datagen.flow_from_dataframe(train_df, directory = '../working/train', x_col = 'filename', y_col = 'label', target_size = (224,224),\n                                              batch_size = 64, class_mode='binary')\n\n#we do not augment validation data.\nvalid_datagen = ImageDataGenerator(preprocessing_function = preprocess_input)\n\nvalid_gen = valid_datagen.flow_from_dataframe(valid_df, directory = '../working/train', x_col = 'filename', y_col = 'label', target_size = (224,224),\n                                              batch_size = 64, class_mode='binary')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:09.566407Z","iopub.execute_input":"2025-04-21T19:16:09.566839Z","iopub.status.idle":"2025-04-21T19:16:09.807905Z","shell.execute_reply.started":"2025-04-21T19:16:09.566812Z","shell.execute_reply":"2025-04-21T19:16:09.807134Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'sec5'></a>\n# Model","metadata":{}},{"cell_type":"markdown","source":"<a id = 'subsec21'></a>\n<span style=\"color:green;font-size:20px\"> Prepare the model </span>  \n\nWe initialize the ResNet-50 model, adding an additional last layer of type Dense, with sigmoid activation function.\nWe also set the first layer of the model to be not trainable, becaise ResNet-50 model was already trained.","metadata":{}},{"cell_type":"code","source":"model = Sequential()\nmodel.add(ResNet50(include_top = False, pooling = 'max', weights = 'imagenet'))\nmodel.add(Dense(1, activation = 'sigmoid'))\n\nmodel.layers[0].trainable = False ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:09.808868Z","iopub.execute_input":"2025-04-21T19:16:09.809173Z","iopub.status.idle":"2025-04-21T19:16:14.469329Z","shell.execute_reply.started":"2025-04-21T19:16:09.809143Z","shell.execute_reply":"2025-04-21T19:16:14.468646Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style = 'color:green;font-size:20px'> Model summary </span>  \n\nWe plot the model description. We can see that the ResNet-50 model represent the 1st layer of our model, of type Model.","metadata":{}},{"cell_type":"code","source":"model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:14.470215Z","iopub.execute_input":"2025-04-21T19:16:14.470527Z","iopub.status.idle":"2025-04-21T19:16:14.490450Z","shell.execute_reply.started":"2025-04-21T19:16:14.470506Z","shell.execute_reply":"2025-04-21T19:16:14.489835Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style=\"color:green;font-size:20px\"> Compile the model </span>  \n\nWe compile the model, using a adam optimized, the loss function as binary crossentropy and the metric accuracy.","metadata":{}},{"cell_type":"code","source":"model.compile(optimizer = 'adam', metrics = ['accuracy'], loss = 'binary_crossentropy')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:14.493517Z","iopub.execute_input":"2025-04-21T19:16:14.493893Z","iopub.status.idle":"2025-04-21T19:16:14.508716Z","shell.execute_reply.started":"2025-04-21T19:16:14.493875Z","shell.execute_reply":"2025-04-21T19:16:14.507974Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style = 'color:green;font-size:20px'> Callback </span>\n\nBest weight will be stored in 'dogcat.weights.best.hdf5'","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.callbacks import ModelCheckpoint\n\ncheckpointer = ModelCheckpoint(\n    filepath='dogcat.weights.best.weights.h5',\n    save_best_only=True,\n    save_weights_only=True\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:14.509486Z","iopub.execute_input":"2025-04-21T19:16:14.509723Z","iopub.status.idle":"2025-04-21T19:16:14.514263Z","shell.execute_reply.started":"2025-04-21T19:16:14.509705Z","shell.execute_reply":"2025-04-21T19:16:14.513430Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'subsec22'></a>\n<span style = 'color:green;font-size:20px'> Train the model </span>","metadata":{}},{"cell_type":"code","source":"model.fit(train_gen, epochs=10, validation_data=valid_gen, callbacks=[checkpointer])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-21T19:16:14.515276Z","iopub.execute_input":"2025-04-21T19:16:14.515544Z","execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Plot the loss and accuracy curves.","metadata":{}},{"cell_type":"code","source":"loss = pd.DataFrame(model.history.history)\nloss[['loss', 'val_loss']].plot()\nloss[['accuracy', 'val_accuracy']].plot()","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'sec6'></a>\n# Results","metadata":{}},{"cell_type":"code","source":"#load the best weights saved using checkpointer\nmodel.load_weights('dogcat.weights.best.weights.h5')","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df = pd.DataFrame({'filename': test_path})    #test_path from function get_path","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'subsec31'></a>\n<span style = 'color:green;font-size:20px'> Visualizing Predictions </span>","metadata":{}},{"cell_type":"markdown","source":"We will use test dataframe generated above and process the image. We will predict class using 'model.predict' and append the labels in a list.","metadata":{}},{"cell_type":"code","source":"test_sample = test_df.sample(n = 25, random_state = 123)    #Randomly sample 25 images from test directory for visualizing predictions\n\nlabel_pred = []                         #list for predicted labels\nfor path in test_sample['filename'].to_numpy():\n    full_path = '../working/test/'+path\n    x = load_img(full_path, target_size=(224,224))\n    img_array = img_to_array(x)\n    img_array = np.expand_dims(img_array, axis=0)\n    \n    out =  model.predict(img_array)    \n    out = 'Dog' if float(out) >0.5 else 'Cat'\n    label_pred.append(out)","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We will plot 25 random test images with their class using list of predicted labels.","metadata":{}},{"cell_type":"code","source":"test_array = test_sample['filename'].to_numpy()            # convert dataframe to numpy array \n\nplt.figure(figsize=(15, 15))\nfor i in range(0, 25):\n    plt.subplot(5, 5, i+1)\n    img = test_array[i]\n    path = '../working/test/' + img\n    image = load_img(path, target_size=(256,256))\n    \n    plt.title('Predicted: {}'.format(label_pred[i]), color='green',fontsize= 10, bbox=dict(facecolor='black', alpha=0.7))\n    plt.imshow(image)\n    plt.axis('off')\n\nplt.tight_layout()","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Generate mini batches of test data\ntest_datagen = ImageDataGenerator(preprocessing_function = preprocess_input)\n\ntest_gen = test_datagen.flow_from_dataframe(test_df, directory = '../working/test', batch_size = 64, x_col = 'filename', y_col = None, class_mode = None, shuffle = False,\n                                            img_size = (224, 224))","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Performing predictions\nprediction = model.predict(test_gen)\nprediction = prediction.clip(min=0.005, max=0.995)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission_df = pd.read_csv('../input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv')\n\nfor i, fname in enumerate(test_path):\n    index = int(fname[fname.rfind('/')+1:fname.rfind('.')])\n    submission_df.at[index-1, 'label'] = prediction[i]\nsubmission_df.to_csv('Cats&DogsSubmission.csv', index=False)","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<a id = 'subsec32'></a>\n<span style = 'color:green;font-size:20px'> Prepare Submission File </span>","metadata":{}},{"cell_type":"code","source":"submission_df.head()","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-21T19:27:03.514Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<span style = 'color:green;font-size:20px'> Conclusion </span>  \n\nUsing a pretrained model for tensorflow, ResNet-50, with a Dense model with sigmoid activation added on top and training with a reduced set of we were able to obtain quite good model in terms of validation accuracy.\nThe model was used to predict the classes of the images from the independent test set and results were submitted to test the accuracy of the prediction with fresh data.","metadata":{}}]}