{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.7.6","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"},{"sourceId":9905,"sourceType":"datasetVersion","datasetId":6300},{"sourceId":11107803,"sourceType":"datasetVersion","datasetId":6924616,"isSourceIdPinned":true},{"sourceId":11110879,"sourceType":"datasetVersion","datasetId":6927123},{"sourceId":11111333,"sourceType":"datasetVersion","datasetId":6927448},{"sourceId":11155762,"sourceType":"datasetVersion","datasetId":6960346},{"sourceId":11315628,"sourceType":"datasetVersion","datasetId":7077910},{"sourceId":11333786,"sourceType":"datasetVersion","datasetId":7089725},{"sourceId":11360487,"sourceType":"datasetVersion","datasetId":7110283},{"sourceId":11437607,"sourceType":"datasetVersion","datasetId":7164393},{"sourceId":11475470,"sourceType":"datasetVersion","datasetId":7192017},{"sourceId":11530888,"sourceType":"datasetVersion","datasetId":7077903},{"sourceId":11530912,"sourceType":"datasetVersion","datasetId":7232390},{"sourceId":13089058,"sourceType":"datasetVersion","datasetId":8290574},{"sourceId":13049741,"sourceType":"datasetVersion","datasetId":8263659}],"dockerImageVersionId":30042,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Dogs vs Cats classification using transfer learning and CNN","metadata":{}},{"cell_type":"markdown","source":"# Import Required Libraries\n","metadata":{}},{"cell_type":"markdown","source":"This kernel shows how to use transfer learning to classify dogs and cats images. If you find this kernel useful please upvote it!\n\nTransfer learning means that instead of your model learning everything from scratch, it uses another model that was trained on a similar problem, so that you can \"transfer\" the learned \"knowledge\" of the pretrained model to your model, and then learn some new features.\n\nThe ImageNet Data set is huge data set consisting of more that 14 million images from more than 22,000 different categories, here we are using a smaller version of it which has 1000 different categories.\n\nIn this kernel we use an xception model which is pretrained on the ImageNet dataset and then build some layers on top of it to be able to classify dogs and cats images.\n\nTransfer learning makes sense here because the ImageNet data set has a much larger number of images (14 million) than the dog-cat data set (25,000). This increases the speed of training of our model and the accuracy of our predictions.","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd \nimport random\nimport os\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport keras.applications.xception as xception\nimport zipfile\nimport sys\nimport time\nimport tensorflow.keras as keras\n\nfrom PIL import Image\nfrom keras.layers import Input, Conv2D, Dense, Flatten, MaxPooling2D, Input\nfrom keras.models import Model, Sequential\nfrom keras.preprocessing import image\nfrom keras.utils import to_categorical\n\nprint('setup successful!')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:07:46.760261Z","iopub.execute_input":"2025-09-17T14:07:46.760561Z","iopub.status.idle":"2025-09-17T14:07:55.586435Z","shell.execute_reply.started":"2025-09-17T14:07:46.760528Z","shell.execute_reply":"2025-09-17T14:07:55.585148Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Define Constants","metadata":{}},{"cell_type":"code","source":"IMAGE_WIDTH = 224    \nIMAGE_HEIGHT = 224\nIMAGE_CHANNELS = 3\n\n# The image net have 1000 categories, and so 1000 outputs, so we get 1000 different features\nIMAGE_FEATURES_SIZE = 1000\n\n# For the first run PERFORM_UNZIPPING must be True, but after the first run, \n#it can be set to False skip the unzipping step to save time\nPERFORM_UNZIPPING = True \n\nprint('defining constants successful!')","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:19.663311Z","iopub.execute_input":"2025-09-17T14:08:19.663699Z","iopub.status.idle":"2025-09-17T14:08:19.670418Z","shell.execute_reply.started":"2025-09-17T14:08:19.663664Z","shell.execute_reply":"2025-09-17T14:08:19.669023Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Unzip the Train and Test folders\n\nThe train and test folders are zipped, the code below unzips both folders and the unzipped folders are saved in the output folder. For the First run PERFORM_UNZIPPING should be true to perform the unzipping, later if the folders are already unzipped you can change it to False","metadata":{}},{"cell_type":"code","source":"if PERFORM_UNZIPPING == False:\n    #Unzip the input folders\n    with zipfile.ZipFile(\"../input/lion-data/trainlion/train\",\"r\") as z:\n        z.extractall(\".\")        \n    \n    with zipfile.ZipFile(\"../input/lion-data/testlion/test\",\"r\") as z:   \n        z.extractall(\".\")   \n    \n    print('Unzipping done!')\n    \n    \nelse:\n    print('Unzipping not needed')\n    \n    \nimgs_path = \"/kaggle/input/ringtail-balanced-training/reduced/\"\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:19.672560Z","iopub.execute_input":"2025-09-17T14:08:19.672869Z","iopub.status.idle":"2025-09-17T14:08:19.691584Z","shell.execute_reply.started":"2025-09-17T14:08:19.672840Z","shell.execute_reply":"2025-09-17T14:08:19.690016Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Prepare load_image function and Data Frame\nThe pretrained model that we are using here is the xception model. The advantage of the xception model is that it has a small size, relatively small number of parameters and high accuracy. You can have a look with on a comparison of different pretrained models available in Keras in https://keras.io/api/applications/\n\n\nThe function 'load_image' takes the path of an image as an input and return the preprocessed image. \n","metadata":{}},{"cell_type":"code","source":"def _load_image(img_path):\n    img = image.load_img(img_path, target_size = (IMAGE_WIDTH, IMAGE_HEIGHT)) # load the image from the directory\n    img = image.img_to_array(img) \n    # add an additional dimension, (i.e. change the shape of each image from (224, 224, 3) to (1, 224, 224, 3)\n    # This shape is suitable for training\n    img = np.expand_dims(img, axis = 0) \n    # Apply preprocessing for the image, so that the training is faster\n    img = xception.preprocess_input(img)  \n    return img\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:19.693848Z","iopub.execute_input":"2025-09-17T14:08:19.694252Z","iopub.status.idle":"2025-09-17T14:08:19.709102Z","shell.execute_reply.started":"2025-09-17T14:08:19.694215Z","shell.execute_reply":"2025-09-17T14:08:19.708107Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Create a  dataframe that contains a list of the file names and the corresponding category. Category 1 means lion, 0 means no lion","metadata":{}},{"cell_type":"code","source":"filenames = os.listdir(imgs_path)\n\ncategories = []\n\nfor filename in filenames:\n    category = filename.split('.')[0]\n    if category == 'lion':\n        categories.append(1)\n    else:\n        categories.append(0)\n\ndf = pd.DataFrame({\n    'filename': filenames,\n    'category': categories\n})\n\nprint(df.head())\n\nprint('number of elements = ' , len(df))  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:19.711913Z","iopub.execute_input":"2025-09-17T14:08:19.712315Z","iopub.status.idle":"2025-09-17T14:08:19.758143Z","shell.execute_reply.started":"2025-09-17T14:08:19.712279Z","shell.execute_reply":"2025-09-17T14:08:19.756801Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# see sample image, you can run the same cell again to get a different image\nsample = random.choice(filenames)\nrandomimage = image.load_img(imgs_path + sample)\nplt.imshow(randomimage)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:19.759856Z","iopub.execute_input":"2025-09-17T14:08:19.760316Z","iopub.status.idle":"2025-09-17T14:08:20.064037Z","shell.execute_reply.started":"2025-09-17T14:08:19.760267Z","shell.execute_reply":"2025-09-17T14:08:20.063009Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Create the Pretrained Model\nThere are two approaches for transfer learning:\n1. One model where the first part of it is the pretrained model and the second part is your new model. \n1. Pass all the images through the pretrained model, to get the extracted features and then use those extrated features to    train your new model. \n\nHere we will use the second approach (the feature extraction approach), because a matrix which contains all our images with the shape (224, 224, 3) will not fit in our RAM. So we first pass all the images through the xception model and get the extracted features, as mentioned above the model has 1000 categories, so the output will have a size of 1000 which is much smaller than (224 X 224 X 3 = 150,528) . So if we have 100 images the training matrix will have a shape of (100, 1000)","metadata":{}},{"cell_type":"code","source":"# create the xception model used for the feature extraction\nmodel_xception = xception.Xception(include_top = True, input_shape = (IMAGE_WIDTH, IMAGE_HEIGHT,IMAGE_CHANNELS),\n                       weights = '../input/xception/xception_weights_tf_dim_ordering_tf_kernels.h5')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:20.066202Z","iopub.execute_input":"2025-09-17T14:08:20.066544Z","iopub.status.idle":"2025-09-17T14:08:24.067853Z","shell.execute_reply.started":"2025-09-17T14:08:20.066513Z","shell.execute_reply":"2025-09-17T14:08:24.065971Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We will use the EarlyStopping call back to stop our training  if the accuracy is not improving for a certain number of epochs. \nIf the accuracy is not improving for a certain number of epochs then it makes sense to reduce the learning rate, we implement that using the ReduceLROnPlateau call back.","metadata":{}},{"cell_type":"code","source":"# Define call backs\nfrom keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\nearly_stop = EarlyStopping(patience = 7, verbose = 1)\n\nlearning_rate_reduction = ReduceLROnPlateau(patience = 3, verbose = 1, factor=0.5)\n\nprint('call backs defined!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:24.070254Z","iopub.execute_input":"2025-09-17T14:08:24.070759Z","iopub.status.idle":"2025-09-17T14:08:24.081370Z","shell.execute_reply.started":"2025-09-17T14:08:24.070709Z","shell.execute_reply":"2025-09-17T14:08:24.079582Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Feature Extraction\nThe extract_features function will take the pretrained model used for feature extraction and a batch of images, then it will pass those images through the model to obtain the extracted features. It is better to pass a maximum of a round 1000 images at a time in order not to fill our RAM. The data_set parameter is dataframe containing filenames and the corresponding categories for each file.","metadata":{}},{"cell_type":"markdown","source":"# Settings for Data Augmentation ","metadata":{}},{"cell_type":"markdown","source":"Import ImageDataGenerator","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:24.084439Z","iopub.execute_input":"2025-09-17T14:08:24.084880Z","iopub.status.idle":"2025-09-17T14:08:24.100132Z","shell.execute_reply.started":"2025-09-17T14:08:24.084844Z","shell.execute_reply":"2025-09-17T14:08:24.098271Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Create an ImageDataGenerator instance:","metadata":{}},{"cell_type":"code","source":"datagen = ImageDataGenerator(\n#        featurewise_center=True,\n#        featurewise_std_normalization=True,\n#        rotation_range=10,\n#       width_shift_range=0.2,\n#       height_shift_range=0.2,\n#       shear_range=0.2,\n#       zoom_range=0.2,\n       horizontal_flip=True,\n#       fill_mode='nearest',\n       brightness_range=[0.8, 1.2]  # Adjust brightness between 80% and 120\n    \n    \n   )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:24.102093Z","iopub.execute_input":"2025-09-17T14:08:24.102675Z","iopub.status.idle":"2025-09-17T14:08:24.117740Z","shell.execute_reply.started":"2025-09-17T14:08:24.102627Z","shell.execute_reply":"2025-09-17T14:08:24.116203Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Modify extract_features function for data augmentation","metadata":{}},{"cell_type":"code","source":"PERFORM_AUGMENTATION = True  # Set to False to disable augmentation","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:24.119394Z","iopub.execute_input":"2025-09-17T14:08:24.119934Z","iopub.status.idle":"2025-09-17T14:08:24.135291Z","shell.execute_reply.started":"2025-09-17T14:08:24.119870Z","shell.execute_reply":"2025-09-17T14:08:24.134061Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def extract_features(model, data_set, augment=PERFORM_AUGMENTATION):\n       features = []\n       for data_set_entry in data_set:\n           file_name = data_set_entry[0]\n           category = data_set_entry[1]\n           path = imgs_path + file_name\n           img = image.load_img(path, target_size=(IMAGE_WIDTH, IMAGE_HEIGHT))\n           x = image.img_to_array(img)\n           x = np.expand_dims(x, axis=0)\n   \n           if augment:\n               # Apply augmentation\n               aug_iter = datagen.flow(x, batch_size=1)\n               aug_img = next(aug_iter)[0]\n               \n               aug_img = xception.preprocess_input(aug_img)\n               # extract features\n               pred = model.predict(np.expand_dims(aug_img, axis=0))\n               features.append(pred)\n           else:\n               # Use original image\n               x = xception.preprocess_input(x)\n               pred = model.predict(x)\n               features.append(pred)\n           \n       return np.array(features).reshape(len(data_set), IMAGE_FEATURES_SIZE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:24.137417Z","iopub.execute_input":"2025-09-17T14:08:24.137927Z","iopub.status.idle":"2025-09-17T14:08:24.153467Z","shell.execute_reply.started":"2025-09-17T14:08:24.137877Z","shell.execute_reply":"2025-09-17T14:08:24.152173Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We split the training set into three separate sets:\n1. **The training set**: used to train our model.\n2. **The validation set**: used to double check that our model is not overfitting the training set, i.e. it can also generalise    to other data other than the train data\n3. **The test set**: Used to estimate the accuracy of the model on new data other than the ones the model used for training\n\nFor a competetion or for some other cases, you can split the data only to training and validation sets inorder to acheive te highest possible accuracy, without the need to properly estimate how accurate the model really is.\n\nWe split the data set as follows: 2% test set, 82% train set, 16% cross_validation set\n \n","metadata":{}},{"cell_type":"code","source":"train_set, validate_set, test_set = np.split(df.sample(frac=1, random_state =43), [int(.82*len(df)), int(.96*len(df))])\n\n# Visualize the data distribution\nfig, axis = plt.subplots(1,3)\nfig.tight_layout()\nfig.set_size_inches(80,20)\n\nax0 = sns.countplot(data=train_set   , x = 'category', ax = axis[0])\nax1 = sns.countplot(data=validate_set, x = 'category', ax = axis[1])\nax2 = sns.countplot(data=test_set    , x = 'category', ax = axis[2])\n\nax0.set_title('train data', fontsize = 60)\nax1.set_title('cross validation data', fontsize = 60)\nax2.set_title('test data', fontsize = 60)\n\nax0.tick_params(labelsize=55)\nax1.tick_params(labelsize=55)\nax2.tick_params(labelsize=55)\n\nprint('shape of train_set:   ', np.shape(train_set))\nprint('shape of validate_set:', np.shape(validate_set))\nprint('shape of test_set:    ', np.shape(test_set))\n\n\n# convert the dataframes to numpy matrices\ntrain_set    = train_set.to_numpy()\nvalidate_set = validate_set.to_numpy()\ntest_set     = test_set.to_numpy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:24.156913Z","iopub.execute_input":"2025-09-17T14:08:24.157350Z","iopub.status.idle":"2025-09-17T14:08:25.062349Z","shell.execute_reply.started":"2025-09-17T14:08:24.157310Z","shell.execute_reply":"2025-09-17T14:08:25.061393Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now we perform the feature extraction. We take chunks of the train_set (1000 at a time) and pass them through the pretrained model, and save the extracted features in train_x. We then do the same with the validation_set.","metadata":{}},{"cell_type":"code","source":"def chunks(lst, n):\n    \"\"\"Yield successive n-sized chunks from lst.\"\"\"\n    for i in range(0, len(lst), n):\n        yield lst[i:i + n]\n        \n# prepare parameters for model.fit\ntrain_x = np.zeros((len(train_set), 1000)) \ntrain_y = np.zeros((len(train_set)))\n\nvalidate_x = np.zeros((len(validate_set), 1000)) \nvalidate_y = np.zeros((len(validate_set) ))\n\nchunk_size = 1000\n\n# extract features from train_set nd save it into train_x\nfor i, train_set_chunk in enumerate (chunks(train_set, chunk_size)):\n    train_x[ (i*chunk_size) : (i*chunk_size + chunk_size)] = extract_features(model_xception, train_set_chunk)    \n\nprint('shape of train_x: ',    np.shape(train_x))\n\n\n# extract features from validate_set nd save it into validate_x\nfor i, validate_set_chunk in enumerate (chunks(validate_set, chunk_size)):\n    validate_x[ (i*chunk_size) : (i*chunk_size + chunk_size)] = extract_features(model_xception, validate_set_chunk)    \n\nprint('shape of validate_x: ', np.shape(validate_x))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:08:25.064578Z","iopub.execute_input":"2025-09-17T14:08:25.064928Z","iopub.status.idle":"2025-09-17T14:10:23.169488Z","shell.execute_reply.started":"2025-09-17T14:08:25.064893Z","shell.execute_reply":"2025-09-17T14:10:23.167916Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We take the second column from the train_set (the category column) and save it in train_y. We then convert it to a one-hot vector using the \"to_categorically\" method, we need the one-hot format for the model fitting later on.","metadata":{}},{"cell_type":"code","source":"# prepare train_y\ntrain_y = train_set[:,1]\ntrain_y = to_categorical(train_y)        \n\n# prepare validate_y\nvalidate_y = validate_set[:,1]\nvalidate_y = to_categorical(validate_y)   ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:23.171007Z","iopub.execute_input":"2025-09-17T14:10:23.171390Z","iopub.status.idle":"2025-09-17T14:10:23.178022Z","shell.execute_reply.started":"2025-09-17T14:10:23.171356Z","shell.execute_reply":"2025-09-17T14:10:23.176668Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Build the new model and train it\nNote that it is a small model, that is because it doesn't need to learn everything from scratch because we are using the extracted features. Also learning will take just around 30 seconds using the GPU.","metadata":{}},{"cell_type":"code","source":"APPLY_REGULARIZATION = True # Set to False to disable regularization\n\nfrom tensorflow import keras\nfrom tensorflow.keras import layers\n\ntransfer_model = keras.Sequential()\n\ntransfer_model.add(keras.Input(shape=(IMAGE_FEATURES_SIZE)))\ntransfer_model.add(layers.Flatten())\n\nif APPLY_REGULARIZATION:\n    transfer_model.add(layers.Dense(100, activation='relu', kernel_regularizer=keras.regularizers.l2(0.01)))  # L2 regularization\n    transfer_model.add(layers.Dense(16, activation='relu', kernel_regularizer=keras.regularizers.l1(0.001)))  # L1 regularization\nelse:\n    transfer_model.add(layers.Dense(100, activation='relu'))  # No regularization\n    transfer_model.add(layers.Dense(16, activation='relu'))  # No regularization\n\ntransfer_model.add(layers.Dense(2, activation='softmax'))\n\ntransfer_model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:23.179715Z","iopub.execute_input":"2025-09-17T14:10:23.180179Z","iopub.status.idle":"2025-09-17T14:10:23.239182Z","shell.execute_reply.started":"2025-09-17T14:10:23.180142Z","shell.execute_reply":"2025-09-17T14:10:23.238202Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# compile and fit the model\ntransfer_model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])\n\nEPOCHS = 20\n\nhistory = transfer_model.fit(x = train_x , y= train_y, batch_size = 16, epochs = EPOCHS, \n                             callbacks = [early_stop, learning_rate_reduction],\n                             validation_data = (validate_x, validate_y))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:23.240470Z","iopub.execute_input":"2025-09-17T14:10:23.240792Z","iopub.status.idle":"2025-09-17T14:10:26.360984Z","shell.execute_reply.started":"2025-09-17T14:10:23.240760Z","shell.execute_reply":"2025-09-17T14:10:26.359752Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"transfer_model.save(\"model.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:26.363624Z","iopub.execute_input":"2025-09-17T14:10:26.364087Z","iopub.status.idle":"2025-09-17T14:10:26.394217Z","shell.execute_reply.started":"2025-09-17T14:10:26.364039Z","shell.execute_reply":"2025-09-17T14:10:26.392928Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Visualize the training process","metadata":{}},{"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 10))\nax1.plot(history.history['loss'], color='b', label=\"Training loss\")\nax1.plot(history.history['val_loss'], color='r', label=\"validation loss\")\nax1.set_xticks(np.arange(1, EPOCHS, 1))\nax1.set_yticks(np.arange(0, 0.14, 0.02))\nax1.legend()\n\nax2.plot(history.history['accuracy'], color='b', label=\"Training accuracy\")\nax2.plot(history.history['val_accuracy'], color='r',label=\"Validation accuracy\")\nax2.set_xticks(np.arange(1, EPOCHS, 1))\nax2.legend()\n\nlegend = plt.legend(loc='best')\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:26.396060Z","iopub.execute_input":"2025-09-17T14:10:26.396503Z","iopub.status.idle":"2025-09-17T14:10:26.810247Z","shell.execute_reply.started":"2025-09-17T14:10:26.396426Z","shell.execute_reply":"2025-09-17T14:10:26.808845Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predict\nPredict the class for the test set and evaluate the accuracy on the test set.","metadata":{}},{"cell_type":"code","source":"test_x = np.zeros((len(test_set), 1000)) \ntest_y = np.zeros((len(test_set) ))\n\n# extract features from test_set and save it into test_x\nfor i, test_set_chunk in enumerate (chunks(test_set, chunk_size)):\n    test_x[ (i*chunk_size) : (i*chunk_size + chunk_size)] = extract_features(model_xception, test_set_chunk)    \n\n# prepare test_y\ntest_y = test_set[:,1]\ntest_y = to_categorical(test_y) \n\nassert(np.shape(test_y)  == (np.shape(test_set)[0], 2))\n\n# predict and estimate the accuracy\n_, accuracy = transfer_model.evaluate(test_x, test_y)   \nprint('accuracy on test set = ',  round((accuracy * 100),2 ), '% ') ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:26.811942Z","iopub.execute_input":"2025-09-17T14:10:26.812503Z","iopub.status.idle":"2025-09-17T14:10:32.046798Z","shell.execute_reply.started":"2025-09-17T14:10:26.812448Z","shell.execute_reply":"2025-09-17T14:10:32.045701Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Predict Class of Random Image\nThe cell below will randomly select an image from the test set, predict if it is a dog or cat. You can run the cell several times to get different images.","metadata":{}},{"cell_type":"code","source":"def check_string_in_filename(filepath, search_string):\n    \"\"\"\n    Checks if a specified string is present in a filename.\n\n    Args:\n        filepath: The path to the file.\n        search_string: The string to search for within the filename.\n\n    Returns:\n        True if the string is found in the filename, False otherwise.\n    \"\"\"\n    filename = os.path.basename(filepath)\n    return search_string in filename","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:32.048393Z","iopub.execute_input":"2025-09-17T14:10:32.048719Z","iopub.status.idle":"2025-09-17T14:10:32.054565Z","shell.execute_reply.started":"2025-09-17T14:10:32.048687Z","shell.execute_reply":"2025-09-17T14:10:32.053348Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample = random.choice(test_set)[0]\nrandomimage = image.load_img(imgs_path + sample)\nplt.imshow(randomimage)\n\nloaded_image = _load_image(imgs_path + sample)\nextracted_feat = model_xception.predict(loaded_image) \npred = transfer_model.predict(extracted_feat)\n\npred = pred[0]  # convert to array\nif pred[0] >= pred[1]:\n    prediction_class = 'no lion'\n    prediction_percentage = pred[0]\n    string_to_find = \"other\"       \n    if check_string_in_filename(sample, string_to_find):\n       print('CORRECT')\n    else:\n       print('FALSE NEGATIVE')\nelse:\n    prediction_class = 'lion'\n    prediction_percentage = pred[1]\n    string_to_find = \"lion\"\n    if check_string_in_filename(sample, string_to_find):\n       print('CORRECT')\n    else:\n       print('FALSE POSITIVE')\n\nprint (sample)\nprint('I am ',int(prediction_percentage*100), '% sure that I am a ', prediction_class, '!', sep = '')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:10:32.055968Z","iopub.execute_input":"2025-09-17T14:10:32.056307Z","iopub.status.idle":"2025-09-17T14:10:32.521205Z","shell.execute_reply.started":"2025-09-17T14:10:32.056276Z","shell.execute_reply":"2025-09-17T14:10:32.519324Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Run on Real Test Images**\nSpecify your own directory of lion and other animal images in the realtestimgs_path to test the model on images with backgrounds not in the training, validation and test sets above. Note the test set above is simply split out from the training and validation sets so backgrounds and animals will be similar. This test allows to really test the generalization of the model","metadata":{}},{"cell_type":"code","source":"realtestimgs_path = \"/kaggle/input/ringtail-training01/test_ringtail01/\"\ndef _load_image(realtestimgs_path):\n    img_real = image.load_img(realtestimgs_path, target_size = (IMAGE_WIDTH, IMAGE_HEIGHT)) # load the image from the directory\n    img_real = image.img_to_array(img_real) \n    # add an additional dimension, (i.e. change the shape of each image from (224, 224, 3) to (1, 224, 224, 3)\n    # This shape is suitable for training\n    img_real = np.expand_dims(img_real, axis = 0) \n    # Apply preprocessing for the image, so that the training is faster\n    img_real = xception.preprocess_input(img_real)\n        \n    return img_real","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:16:36.950979Z","iopub.execute_input":"2025-09-17T14:16:36.951426Z","iopub.status.idle":"2025-09-17T14:16:36.960805Z","shell.execute_reply.started":"2025-09-17T14:16:36.951392Z","shell.execute_reply":"2025-09-17T14:16:36.958343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filenames = os.listdir(realtestimgs_path)\n\ncategories = []\n\nfor filename in filenames:\n    category = filename.split('.')[0]\n    if category == 'lion':\n        categories.append(1)\n    else:\n        categories.append(0)\n\ndf = pd.DataFrame({\n    'filename': filenames,\n    'category': categories\n})\n\nprint(df.head())\n\nprint('number of elements = ' , len(df))  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:16:36.963722Z","iopub.execute_input":"2025-09-17T14:16:36.964281Z","iopub.status.idle":"2025-09-17T14:16:37.012904Z","shell.execute_reply.started":"2025-09-17T14:16:36.964227Z","shell.execute_reply":"2025-09-17T14:16:37.011711Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"from tensorflow import keras\nfrom tensorflow.keras import layers\n\ntransfer_model = keras.Sequential()\n\ntransfer_model.add(keras.Input(shape = (IMAGE_FEATURES_SIZE)))\ntransfer_model.add(layers.Flatten())\ntransfer_model.add(layers.Dense(100, activation = 'relu', kernel_regularizer=keras.regularizers.l2(0.01)))  # L2 regularization\ntransfer_model.add(layers.Dense(16,  activation = 'relu', kernel_regularizer=keras.regularizers.l1(0.001))) # L1 regularization\ntransfer_model.add(layers.Dense(2,   activation = 'softmax'))That's it, if you find this kernel useful please upvote it :)\n","metadata":{}},{"cell_type":"code","source":"import shutil\nreal_test_set = df.to_numpy()\ndef copy_file_to_directory(source_path, destination_directory):\n    \"\"\"\n    Copies a file to a specified directory.\n\n    Args:\n        source_path (str): The path to the file to be copied.\n        destination_directory (str): The path to the directory where the file should be copied.\n    \"\"\"\n    try:\n        # Ensure the destination directory exists; create it if not\n        os.makedirs(destination_directory, exist_ok=True)\n\n        # Copy the file to the destination directory\n        shutil.copy2(source_path, destination_directory)\n        \n\n    except FileNotFoundError:\n        print(f\"Error: File '{source_path}' not found.\")\n    except Exception as e:\n        print(f\"An error occurred: {e}\")\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:16:37.015739Z","iopub.execute_input":"2025-09-17T14:16:37.016140Z","iopub.status.idle":"2025-09-17T14:16:37.024786Z","shell.execute_reply.started":"2025-09-17T14:16:37.016078Z","shell.execute_reply":"2025-09-17T14:16:37.022964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"failedimages_path = \"/kaggle/working/failedcases/\"\nsortfilenames = sorted(filenames)\nerror_count = 0\nimage_count = 0    \nfor filename in sortfilenames:\n    sample = random.choice(real_test_set)[0]\n    randomimage = image.load_img(realtestimgs_path + filename)\n    plt.imshow(randomimage)\n    plt.show(randomimage)\n\n    loaded_image = _load_image(realtestimgs_path + filename)\n    extracted_feat = model_xception.predict(loaded_image) \n    pred = transfer_model.predict(extracted_feat)\n    image_count = image_count + 1\n    pred = pred[0]  # convert to array\n    if pred[0] >= pred[1]:\n       prediction_class = 'no lion'\n       prediction_percentage = pred[0]\n       string_to_find = \"other\"       \n       if check_string_in_filename(filename, string_to_find):\n          print('CORRECT')\n       else:\n          print('FALSE NEGATIVE')\n          copy_file_to_directory(realtestimgs_path + filename, failedimages_path) \n          error_count = error_count + 1\n    else:\n       prediction_class = 'lion'\n       prediction_percentage = pred[1]\n       string_to_find = \"lion\"\n       if check_string_in_filename(filename, string_to_find):\n          print('CORRECT')\n       else:\n          print('FALSE POSITIVE')\n          copy_file_to_directory(realtestimgs_path + filename, failedimages_path)            \n          error_count = error_count + 1\n\n    print (filename)\n    print('I am ',int(prediction_percentage*100), '% sure that I am a ', prediction_class, '!', sep = '')\n    print ('-------------------------------------------------------------------')    \nprint('Total Images =', image_count,'Incorrect Images =', error_count, 'Accuracy =', (1-(error_count/image_count))*100)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-17T14:16:37.026753Z","iopub.execute_input":"2025-09-17T14:16:37.027294Z","iopub.status.idle":"2025-09-17T14:16:45.163564Z","shell.execute_reply.started":"2025-09-17T14:16:37.027240Z","shell.execute_reply":"2025-09-17T14:16:45.162321Z"}},"outputs":[],"execution_count":null}]}