{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.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":9900,"sourceType":"datasetVersion","datasetId":6209},{"sourceId":110819,"sourceType":"datasetVersion","datasetId":57539},{"sourceId":110930,"sourceType":"datasetVersion","datasetId":57578},{"sourceId":7325366,"sourceType":"datasetVersion","datasetId":4251698},{"sourceId":7342815,"sourceType":"datasetVersion","datasetId":4263507}],"dockerImageVersionId":11543,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n%matplotlib inline \nimport cv2\nimport os","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-01-05T19:39:01.363119Z","iopub.execute_input":"2024-01-05T19:39:01.363427Z","iopub.status.idle":"2024-01-05T19:39:01.553983Z","shell.execute_reply.started":"2024-01-05T19:39:01.363375Z","shell.execute_reply":"2024-01-05T19:39:01.553373Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nroot_dir=\"../dataFile/\"\nos.mkdir(root_dir)","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:39:01.555158Z","iopub.execute_input":"2024-01-05T19:39:01.555373Z","iopub.status.idle":"2024-01-05T19:39:01.558937Z","shell.execute_reply.started":"2024-01-05T19:39:01.555334Z","shell.execute_reply":"2024-01-05T19:39:01.558351Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np\nimport shutil\nimport random\n# /kaggle/input/cancer-image-div/Cancerous images/Atypical nevus\n# /kaggle/input/cancer-image-div/Cancerous images/Melanoma\n# /kaggle/input/cancer-dataset/dataset_cancer/Cancerous images\n# /kaggle/input/cancer-dataset/dataset_cancer/Non cancerous images\n# classes_dir = ['melanoma','nevus']\nclasses_dir = ['Cancerous images','Non cancerous images']\n\ntest_ratio = 0.20\n\nfor cls in classes_dir:\n    os.makedirs(root_dir +'train/' + cls)\n    os.makedirs(root_dir +'test/' + cls)","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:39:01.56014Z","iopub.execute_input":"2024-01-05T19:39:01.560427Z","iopub.status.idle":"2024-01-05T19:39:01.570979Z","shell.execute_reply.started":"2024-01-05T19:39:01.56037Z","shell.execute_reply":"2024-01-05T19:39:01.570358Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"os.listdir(root_dir+\"/\")","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:39:01.57211Z","iopub.execute_input":"2024-01-05T19:39:01.572321Z","iopub.status.idle":"2024-01-05T19:39:01.58371Z","shell.execute_reply.started":"2024-01-05T19:39:01.572281Z","shell.execute_reply":"2024-01-05T19:39:01.583039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def call2(filen):\n#     src = '/kaggle/input/cancer-detection4/main_train/'+filen\n    src = '/kaggle/input/cancer-dataset/dataset_cancer/'+filen\n    allFileNames = os.listdir(src)\n    np.random.shuffle(allFileNames)\n    train_FileNames, test_FileNames = np.split(np.array(allFileNames),\n                                                              [int(len(allFileNames)* (1 - test_ratio))])\n\n    train_FileNames = [src+'/'+ name for name in train_FileNames.tolist()]\n    test_FileNames = [src+'/' + name for name in test_FileNames.tolist()]\n\n    print(\"*****************************\")\n    print('Total images: ', len(allFileNames))\n    print('Training: ', len(train_FileNames))\n    print('Testing: ', len(test_FileNames))\n    print(\"*****************************\")\n  \n    for name in train_FileNames:\n        shutil.copy(name, root_dir +'train/'+filen+'/' )\n\n    for name in test_FileNames:\n        shutil.copy(name, root_dir +'test/'+filen+'/')\n    print(\"Copying Done!\")\n\n# call2('melanoma')\n# call2('nevus')\ncall2(\"Cancerous images\")\ncall2(\"Non cancerous images\")","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:39:01.585006Z","iopub.execute_input":"2024-01-05T19:39:01.585275Z","iopub.status.idle":"2024-01-05T19:39:15.305513Z","shell.execute_reply.started":"2024-01-05T19:39:01.58522Z","shell.execute_reply":"2024-01-05T19:39:15.30462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fixed for our Cats & Dogs classes\nNUM_CLASSES = 2\n\n# Fixed for Cats & Dogs color images\nCHANNELS = 3\n\nIMAGE_RESIZE = 224\nRESNET50_POOLING_AVERAGE = 'avg'\nDENSE_LAYER_ACTIVATION = 'softmax'\nOBJECTIVE_FUNCTION = 'categorical_crossentropy'\n\n# Common accuracy metric for all outputs, but can use different metrics for different output\nLOSS_METRICS = ['accuracy']\n\n# EARLY_STOP_PATIENCE must be < NUM_EPOCHS\nNUM_EPOCHS = 10\nEARLY_STOP_PATIENCE = 5\n\n# These steps value should be proper FACTOR of no.-of-images in train & valid folders respectively\n# Training images processed in each step would be no.-of-train-images / STEPS_PER_EPOCH_TRAINING\nSTEPS_PER_EPOCH_TRAINING = 10\nSTEPS_PER_EPOCH_VALIDATION = 10\n\n# These steps value should be proper FACTOR of no.-of-images in train & valid folders respectively\n# NOTE that these BATCH* are for Keras ImageDataGenerator batching to fill epoch step input\nBATCH_SIZE_TRAINING = 100\nBATCH_SIZE_VALIDATION = 100\n\n# Using 1 to easily manage mapping between test_generator & prediction for submission preparation\nBATCH_SIZE_TESTING = 1","metadata":{"_uuid":"d157dc1311928d1ada13ed2ef4a34c4ea5c0b538","execution":{"iopub.status.busy":"2024-01-05T19:39:15.30669Z","iopub.execute_input":"2024-01-05T19:39:15.306935Z","iopub.status.idle":"2024-01-05T19:39:15.313289Z","shell.execute_reply.started":"2024-01-05T19:39:15.306885Z","shell.execute_reply":"2024-01-05T19:39:15.31233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.python.keras.applications import ResNet50\nfrom tensorflow.python.keras.models import Sequential\nfrom tensorflow.python.keras.layers import Dense\n\n### \n### Below systax is available with TensorFlow 1.11 onwards but this upgrade is not available for Kaggle kernel yet\n###\n#import tensorflow as tf\n#print(tf.__version__)\n#import tensorflow as tf\n#from tf.keras.applications import ResNet50\n#from tf.keras.models import Sequential","metadata":{"_uuid":"ee0da10690e2848537102cb0c74f9fe19a50431b","execution":{"iopub.status.busy":"2024-01-05T19:39:15.314735Z","iopub.execute_input":"2024-01-05T19:39:15.314945Z","iopub.status.idle":"2024-01-05T19:39:15.327846Z","shell.execute_reply.started":"2024-01-05T19:39:15.314907Z","shell.execute_reply":"2024-01-05T19:39:15.326869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resnet_weights_path = '../input/resnet50/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5'","metadata":{"_uuid":"2fc47c3f887e6713b5a2be38c02b38a8ec80bf97","execution":{"iopub.status.busy":"2024-01-05T19:39:15.329486Z","iopub.execute_input":"2024-01-05T19:39:15.329789Z","iopub.status.idle":"2024-01-05T19:39:15.337281Z","shell.execute_reply.started":"2024-01-05T19:39:15.329729Z","shell.execute_reply":"2024-01-05T19:39:15.336478Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Still not talking about our train/test data or any pre-processing.\n\nmodel = Sequential()\n\n# 1st layer as the lumpsum weights from resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5\n# NOTE that this layer will be set below as NOT TRAINABLE, i.e., use it as is\nmodel.add(ResNet50(include_top = False, pooling = RESNET50_POOLING_AVERAGE, weights = resnet_weights_path))\n\n# 2nd layer as Dense for 2-class classification, i.e., dog or cat using SoftMax activation\nmodel.add(Dense(NUM_CLASSES, activation = DENSE_LAYER_ACTIVATION))\n\n# Say not to train first layer (ResNet) model as it is already trained\nmodel.layers[0].trainable = False","metadata":{"_uuid":"0bb4a8eccd70874ef21f0809f478f993fa127fb2","execution":{"iopub.status.busy":"2024-01-05T19:39:15.338534Z","iopub.execute_input":"2024-01-05T19:39:15.338782Z","iopub.status.idle":"2024-01-05T19:39:27.553892Z","shell.execute_reply.started":"2024-01-05T19:39:15.338735Z","shell.execute_reply":"2024-01-05T19:39:27.553205Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.summary()","metadata":{"_uuid":"b617a28f0f89b272a0aa2af6cf72f2dd642ee052","execution":{"iopub.status.busy":"2024-01-05T19:39:27.555171Z","iopub.execute_input":"2024-01-05T19:39:27.55565Z","iopub.status.idle":"2024-01-05T19:39:27.56919Z","shell.execute_reply.started":"2024-01-05T19:39:27.555417Z","shell.execute_reply":"2024-01-05T19:39:27.568022Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tensorflow.python.keras import optimizers\n\nsgd = optimizers.SGD(lr = 0.01, decay = 1e-6, momentum = 0.9, nesterov = True)\nmodel.compile(optimizer = sgd, loss = OBJECTIVE_FUNCTION, metrics = LOSS_METRICS)","metadata":{"_uuid":"670238611770f43a056332cc06efff44e20ad124","execution":{"iopub.status.busy":"2024-01-05T19:39:27.570563Z","iopub.execute_input":"2024-01-05T19:39:27.570812Z","iopub.status.idle":"2024-01-05T19:39:27.697507Z","shell.execute_reply.started":"2024-01-05T19:39:27.570763Z","shell.execute_reply":"2024-01-05T19:39:27.696837Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.applications.resnet50 import preprocess_input\nfrom keras.preprocessing.image import ImageDataGenerator\n\nimage_size = IMAGE_RESIZE\n\n# preprocessing_function is applied on each image but only after re-sizing & augmentation (resize => augment => pre-process)\n# Each of the keras.application.resnet* preprocess_input MOSTLY mean BATCH NORMALIZATION (applied on each batch) stabilize the inputs to nonlinear activation functions\n# Batch Normalization helps in faster convergence\ndata_generator = ImageDataGenerator(preprocessing_function=preprocess_input)\n\n# flow_From_directory generates batches of augmented data (where augmentation can be color conversion, etc)\n# Both train & valid folders must have NUM_CLASSES sub-folders\ntrain_generator = data_generator.flow_from_directory(\n        '../dataFile/train',\n        target_size=(image_size, image_size),\n        batch_size=BATCH_SIZE_TRAINING,\n        class_mode='categorical')\n\nvalidation_generator = data_generator.flow_from_directory(\n        '../dataFile/test',\n        target_size=(image_size, image_size),\n        batch_size=BATCH_SIZE_VALIDATION,\n        class_mode='categorical') ","metadata":{"_uuid":"ac9ebd909fee81c8c8d9b9fccb6590944d8106eb","execution":{"iopub.status.busy":"2024-01-05T19:39:27.698748Z","iopub.execute_input":"2024-01-05T19:39:27.699039Z","iopub.status.idle":"2024-01-05T19:39:28.138172Z","shell.execute_reply.started":"2024-01-05T19:39:27.698984Z","shell.execute_reply":"2024-01-05T19:39:28.13746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Max number of steps that these generator will have opportunity to process their source content\n# len(train_generator) should be 'no. of available train images / BATCH_SIZE_TRAINING'\n# len(valid_generator) should be 'no. of available train images / BATCH_SIZE_VALIDATION'\n(BATCH_SIZE_TRAINING, len(train_generator), BATCH_SIZE_VALIDATION, len(validation_generator))","metadata":{"_uuid":"5014f91b6aeae661f0bc5db5d546089a07ca5b9d","execution":{"iopub.status.busy":"2024-01-05T19:39:28.139423Z","iopub.execute_input":"2024-01-05T19:39:28.139647Z","iopub.status.idle":"2024-01-05T19:39:28.14554Z","shell.execute_reply.started":"2024-01-05T19:39:28.139608Z","shell.execute_reply":"2024-01-05T19:39:28.144614Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Early stopping & checkpointing the best model in ../working dir & restoring that as our model for prediction\nfrom tensorflow.python.keras.callbacks import EarlyStopping, ModelCheckpoint\n\ncb_early_stopper = EarlyStopping(monitor = 'val_loss', patience = EARLY_STOP_PATIENCE)\ncb_checkpointer = ModelCheckpoint(filepath = '../working/best.hdf5', monitor = 'val_loss', save_best_only = True, mode = 'auto')\n","metadata":{"_uuid":"2dfad78129d725f42110cde0270c32d7373d6d1d","execution":{"iopub.status.busy":"2024-01-05T19:39:28.146757Z","iopub.execute_input":"2024-01-05T19:39:28.147038Z","iopub.status.idle":"2024-01-05T19:39:28.15738Z","shell.execute_reply.started":"2024-01-05T19:39:28.14698Z","shell.execute_reply":"2024-01-05T19:39:28.15676Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Grid Search is an ideal candidate for distributed machine learning\n# Pseudo code for hyperparameters Grid Search\n\n'''\nfrom sklearn.grid_search import ParameterGrid\nparam_grid = {'epochs': [5, 10, 15], 'steps_per_epoch' : [10, 20, 50]}\n\ngrid = ParameterGrid(param_grid)\n\n# Accumulate history of all permutations (may be for viewing trend) and keep watching for lowest val_loss as final model\nfor params in grid:\n    print(params)\n'''","metadata":{"_uuid":"d108d3598210930bab431f43c6865488b01d467b","execution":{"iopub.status.busy":"2024-01-05T19:39:28.158818Z","iopub.execute_input":"2024-01-05T19:39:28.159069Z","iopub.status.idle":"2024-01-05T19:39:28.166888Z","shell.execute_reply.started":"2024-01-05T19:39:28.159028Z","shell.execute_reply":"2024-01-05T19:39:28.166146Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fit_history = model.fit_generator(\n        train_generator,\n        steps_per_epoch=STEPS_PER_EPOCH_TRAINING,\n        epochs = NUM_EPOCHS,\n        validation_data=validation_generator,\n        validation_steps=STEPS_PER_EPOCH_VALIDATION,\n        callbacks=[cb_checkpointer, cb_early_stopper]\n)\nmodel.load_weights(\"../working/best.hdf5\")","metadata":{"_uuid":"cf85fe3c0653aa56503b7da058ea8acf445eec6e","execution":{"iopub.status.busy":"2024-01-05T19:39:28.168206Z","iopub.execute_input":"2024-01-05T19:39:28.168444Z","iopub.status.idle":"2024-01-05T19:40:41.625488Z","shell.execute_reply.started":"2024-01-05T19:39:28.16839Z","shell.execute_reply":"2024-01-05T19:40:41.624814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"assert(False)","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:40:41.627189Z","iopub.execute_input":"2024-01-05T19:40:41.627623Z","iopub.status.idle":"2024-01-05T19:40:41.928954Z","shell.execute_reply.started":"2024-01-05T19:40:41.62745Z","shell.execute_reply":"2024-01-05T19:40:41.866822Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# User interface using gradio","metadata":{}},{"cell_type":"code","source":"!pip install typing_extensions -q\n!pip install gradio -q","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.models import load_model\nfrom tensorflow.keras.applications.resnet50 import preprocess_input, decode_predictions\nimport numpy as np\nimport os\nfrom tensorflow.python.keras import optimizers","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"IMAGE_RESIZE = 224\nRESNET50_POOLING_AVERAGE = 'avg'\nDENSE_LAYER_ACTIVATION = 'softmax'\nOBJECTIVE_FUNCTION = 'categorical_crossentropy'\nLOSS_METRICS = ['accuracy']\ndef model_load(model_name):\n  model = load_model(os.path.join(\"/content/drive/MyDrive/model\",model_name+\".hdf5\"))\n  sgd = tf.keras.optimizers.legacy.SGD(lr = 0.01, decay = 1e-6, momentum = 0.9, nesterov = True)\n  model.compile(optimizer = sgd, loss = OBJECTIVE_FUNCTION, metrics = LOSS_METRICS)\n  return model","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict_image(img_path):\n  img = image.load_img(img_path, target_size=(224, 224))\n  img_array = image.img_to_array(img)\n  img_array = np.expand_dims(img_array, axis=0)\n  img_array = preprocess_input(img_array)\n  model=model_load(\"noncancer\")\n  predictions = model.predict(img_array)\n  can_name={0:\"Cancer\",1:\"No cancer\"}\n  ans=can_name[np.argmax(predictions)]\n  can_type={0:\"Melanoma\",1:\"Nevus\"}\n  if ans==\"Cancer\":\n    model=model_load(\"cancer_type\")\n    predictions = model.predict(img_array)\n    ans=can_type[np.argmax(predictions)]\n  else:\n    ans=\"No cancer\"\n  return ans","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gradio as gr\n\niface = gr.Interface(\n    fn=predict_image,\n    inputs=gr.Image(type=\"filepath\"),\n    outputs=\"text\",\n)\n\n# Launch the interface\niface.launch()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Test the model","metadata":{}},{"cell_type":"code","source":"from keras.preprocessing.image import load_img\nfrom keras.preprocessing.image import img_to_array\nfrom keras.applications.resnet50 import ResNet50\nfrom keras.applications import resnet50\noriginal = load_img(\"/kaggle/input/cancer-detection4/main_train/nevus/ISIC_0158187.jpg\", target_size = (image_size, image_size)) \nnumpy_image = img_to_array(original)\nimage_batch = np.expand_dims(numpy_image, axis = 0)\nprocessed_image = resnet50.preprocess_input(image_batch.copy()) \npredictions = model.predict(processed_image)\ncan={0:\"Melanoma\",1:\"Nevus\"}\nans=can[np.argmax(predictions)]\n","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:40:41.867506Z","iopub.status.idle":"2024-01-05T19:40:41.868071Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(fit_history.history.keys())","metadata":{"_uuid":"383d7a0aa87e1508a46fcebfcd5a80d7aafb840f","execution":{"iopub.status.busy":"2024-01-05T19:40:41.868714Z","iopub.status.idle":"2024-01-05T19:40:41.869348Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":" plt.figure(1, figsize = (15,8)) \n    \nplt.subplot(221)  \nplt.plot(fit_history.history['acc'])  \nplt.plot(fit_history.history['val_acc'])  \nplt.title('model accuracy')  \nplt.ylabel('accuracy')  \nplt.xlabel('epoch')  \nplt.legend(['train', 'valid']) \n    \nplt.subplot(222)  \nplt.plot(fit_history.history['loss'])  \nplt.plot(fit_history.history['val_loss'])  \nplt.title('model loss')  \nplt.ylabel('loss')  \nplt.xlabel('epoch')  \nplt.legend(['train', 'valid']) \n\nplt.show()","metadata":{"_uuid":"00e51568f7e6022c6738c0e1a41080bc7152c257","execution":{"iopub.status.busy":"2024-01-05T19:40:41.870016Z","iopub.status.idle":"2024-01-05T19:40:41.870707Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"assert(False)","metadata":{"execution":{"iopub.status.busy":"2024-01-05T19:40:41.871391Z","iopub.status.idle":"2024-01-05T19:40:41.872014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# NOTE that flow_from_directory treats each sub-folder as a class which works fine for training data\n# Actually class_mode=None is a kind of workaround for test data which too must be kept in a subfolder\n\n# batch_size can be 1 or any factor of test dataset size to ensure that test dataset is samples just once, i.e., no data is left out\ntest_generator = data_generator.flow_from_directory(\n    directory = '../dataFile/test',\n    target_size = (image_size, image_size),\n    batch_size = BATCH_SIZE_TESTING,\n    class_mode = None,\n    shuffle = False,\n    seed = 123\n)\n\n# Try batch size of 1+ in test_generator & check batch_index & filenames in resulting batches\n'''\nfor i in test_generator:\n    #print(test_generator.batch_index, test_generator.batch_size)\n    idx = (test_generator.batch_index - 1) * test_generator.batch_size\n    print(test_generator.filenames[idx : idx + test_generator.batch_size])\n'''","metadata":{"_uuid":"3941e7c5d983e2ad6ae6d6da0a1415ea27e1db7e","execution":{"iopub.status.busy":"2024-01-05T19:40:41.872697Z","iopub.status.idle":"2024-01-05T19:40:41.873178Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Reset before each call to predict\ntest_generator.reset()\n\npred = model.predict_generator(test_generator, steps = len(test_generator), verbose = 1)\n\npredicted_class_indices = np.argmax(pred, axis = 1)","metadata":{"_uuid":"9911ff38ec4b82f5282f078aae63b2a510a9a6cc","execution":{"iopub.status.busy":"2024-01-05T19:40:41.873861Z","iopub.status.idle":"2024-01-05T19:40:41.87448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TEST_DIR = '../dataFile/test/'\nf, ax = plt.subplots(5, 5, figsize = (15, 15))\n\nfor i in range(0,25):\n    imgBGR = cv2.imread(TEST_DIR + test_generator.filenames[i])\n    imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB)\n    \n    # a if condition else b\n    predicted_class = \"Dog\" if predicted_class_indices[i] else \"Cat\"\n\n    ax[i//5, i%5].imshow(imgRGB)\n    ax[i//5, i%5].axis('off')\n    ax[i//5, i%5].set_title(\"Predicted:{}\".format(predicted_class))    \n\nplt.show()","metadata":{"_uuid":"e54bc22bd1a2af7d704f9164b624b79b8cbd4f56","execution":{"iopub.status.busy":"2024-01-05T19:40:41.875168Z","iopub.status.idle":"2024-01-05T19:40:41.875739Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"results_df = pd.DataFrame(\n    {\n        'id': pd.Series(test_generator.filenames), \n        'label': pd.Series(predicted_class_indices)\n    })\nresults_df['id'] = results_df.id.str.extract('(\\d+)')\nresults_df['id'] = pd.to_numeric(results_df['id'], errors = 'coerce')\nresults_df.sort_values(by='id', inplace = True)\n\nresults_df.to_csv('submission.csv', index=False)\nresults_df.head()","metadata":{"_uuid":"39bac850385ba7129c97c1c0d389b36e7f2f0dfe","execution":{"iopub.status.busy":"2024-01-05T19:40:41.876675Z","iopub.status.idle":"2024-01-05T19:40:41.877293Z"},"trusted":true},"execution_count":null,"outputs":[]}]}