{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":" # Importing Libraries and Testing and Loading the Dataset","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport os\nimport gc\n\nfrom sklearn.model_selection import train_test_split\n\n\nimport tensorflow as tf\nfrom tqdm import tqdm\n\nimport numpy as np #\nimport pandas as pd \n\nfrom keras import Sequential\nfrom keras.callbacks import EarlyStopping\n\nfrom keras.optimizers import Adam, SGD\nfrom keras.callbacks import ReduceLROnPlateau\nfrom keras.layers import Flatten,Dense,BatchNormalization,Activation,Dropout\nfrom keras.layers import Lambda, Input, GlobalAveragePooling2D,BatchNormalization\nfrom keras.utils import to_categorical\n# from keras import regularizers\nfrom tensorflow.keras.models import Model\n\n\nfrom keras.preprocessing.image import load_img\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-07-20T19:49:57.176657Z","iopub.execute_input":"2021-07-20T19:49:57.177091Z","iopub.status.idle":"2021-07-20T19:50:02.305764Z","shell.execute_reply.started":"2021-07-20T19:49:57.177031Z","shell.execute_reply":"2021-07-20T19:50:02.304730Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#reading labels csv file\n\nlabels = pd.read_csv('/kaggle/input/dog-breed-identification/labels.csv')\nlabels.head()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:50:02.313699Z","iopub.execute_input":"2021-07-20T19:50:02.314139Z","iopub.status.idle":"2021-07-20T19:50:02.363666Z","shell.execute_reply.started":"2021-07-20T19:50:02.314101Z","shell.execute_reply":"2021-07-20T19:50:02.362636Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Describing the labels \nlabels.describe()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:12:17.135283Z","iopub.execute_input":"2021-07-20T20:12:17.135769Z","iopub.status.idle":"2021-07-20T20:12:17.270371Z","shell.execute_reply.started":"2021-07-20T20:12:17.135714Z","shell.execute_reply":"2021-07-20T20:12:17.269477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Showing the CSV as a bar graph\ndef barg(ax):\n    for p in ax.patches:\n        val = p.get_width()  # Height of the Bar\n        x = p.get_x() + p.get_width() # x position\n        y = p.get_y() + p.get_height() / 2 # y postion\n        ax.annotate(round(val,2),(x,y))\n        \nplt.figure(figsize = (15,30))\nax0 =sns.countplot(y=labels['breed'],order=labels['breed'].value_counts().index)\nbarg(ax0)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:12:19.719166Z","iopub.execute_input":"2021-07-20T20:12:19.719521Z","iopub.status.idle":"2021-07-20T20:12:22.471465Z","shell.execute_reply.started":"2021-07-20T20:12:19.719491Z","shell.execute_reply":"2021-07-20T20:12:22.470499Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# no of unique breeds \nlabels['breed'].nunique()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:12:26.520366Z","iopub.execute_input":"2021-07-20T20:12:26.520722Z","iopub.status.idle":"2021-07-20T20:12:26.529589Z","shell.execute_reply.started":"2021-07-20T20:12:26.520689Z","shell.execute_reply":"2021-07-20T20:12:26.528524Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# testing one picture \nfrom IPython.display import display, Image \nImage('../input/dog-breed-identification/train/001513dfcb2ffafc82cccf4d8bbaba97.jpg')","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:12:31.202176Z","iopub.execute_input":"2021-07-20T20:12:31.202580Z","iopub.status.idle":"2021-07-20T20:12:31.218844Z","shell.execute_reply.started":"2021-07-20T20:12:31.202532Z","shell.execute_reply":"2021-07-20T20:12:31.217872Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Alphabetically sorting labels and putting it into a list \nclasses = sorted(list(set(labels['breed'])))\nn_classes = len(classes)  # no of unique breeds\nprint(\"Total unique breed: {}\".format(n_classes))\n\n# Now to map each label string to an integer label for easy use \n# To do this we create a dict\nclass_to_num = dict(zip(classes, range(n_classes)))\n\n# testing the dict\nclass_to_num","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:50:10.660048Z","iopub.execute_input":"2021-07-20T19:50:10.660429Z","iopub.status.idle":"2021-07-20T19:50:10.676908Z","shell.execute_reply.started":"2021-07-20T19:50:10.660393Z","shell.execute_reply":"2021-07-20T19:50:10.675647Z"},"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Since the output of our predictor for each input is a vector of probabilities for each class we must, \n# convert out label dataset to be the same format, \n# for this we will use one hot encoding.\n\ninput_shape = (331,331,3)  # numbers are specific to each keras model\n\n# maps each image and breed of the image to a numpy array\ndef images_to_array(directory, label_dataframe, target_size = input_shape):\n    \n    image_labels = label_dataframe['breed']\n    # passes the input shape and all 10222 images from the dataset from the label.csv file\n    images = np.zeros([len(label_dataframe), target_size[0], target_size[1], target_size[2]],dtype=np.uint8) \n    y = np.zeros([len(label_dataframe),1],dtype = np.uint8)\n    # as we have huge data and limited ram memory. uint8 takes less memory\n    \n    for ix, image_name in enumerate(tqdm(label_dataframe['id'].values)):\n        img_dir = os.path.join(directory, image_name + '.jpg')  # creates directory of each image\n        img = load_img(img_dir, target_size = target_size)  # reading the image\n\n        images[ix]=img  # saving the image with respect to its id\n        del img  # frees memory\n        \n        dog_breed = image_labels[ix]   # getting breed info for the img\n        y[ix] = class_to_num[dog_breed]  # converting breed info to a numerical value \n    \n    y = to_categorical(y)  # converting target variable to a one hot encoded matrix     \n    \n    return images,y","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:50:15.709233Z","iopub.execute_input":"2021-07-20T19:50:15.709668Z","iopub.status.idle":"2021-07-20T19:50:15.718594Z","shell.execute_reply.started":"2021-07-20T19:50:15.709634Z","shell.execute_reply":"2021-07-20T19:50:15.717328Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Calling the previous cell\nimport time \nt = time.time()\n\nX,y = images_to_array('/kaggle/input/dog-breed-identification/train', labels[:])\n\nprint('runtime in seconds: {}'.format(time.time() - t))","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:13:13.837548Z","iopub.execute_input":"2021-07-20T20:13:13.837988Z","iopub.status.idle":"2021-07-20T20:14:28.231131Z","shell.execute_reply.started":"2021-07-20T20:13:13.837951Z","shell.execute_reply":"2021-07-20T20:14:28.230056Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# To test the previous function, I will now print some images with their breed info from the train folder\n\nn = 25\n\nplt.figure(figsize=(20,20))\n\nfor i in range(n):\n    x = plt.subplot(5, 5, i+1)\n    plt.title(classes[np.where(y[i] ==1)[0][0]])\n    plt.imshow(X[i].astype('int32')) # converting to int as imshow() needs integer data to read the image","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:14:29.353692Z","iopub.execute_input":"2021-07-20T20:14:29.354111Z","iopub.status.idle":"2021-07-20T20:14:33.258080Z","shell.execute_reply.started":"2021-07-20T20:14:29.354078Z","shell.execute_reply":"2021-07-20T20:14:33.257270Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating Callbacks","metadata":{}},{"cell_type":"code","source":"# Learning Rate Annealer automatically reduces learning rate when a metric has stopped improving. \n# by monitoring the validation accuracy\nlrr= ReduceLROnPlateau(monitor='val_acc', factor=.01, patience=3, min_lr=1e-5,verbose = 1) \n\n# Prepare call backs\n# Monitors validation loss and stops the training if nothing significant is happening \nEarlyStop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:51:33.783200Z","iopub.execute_input":"2021-07-20T19:51:33.783550Z","iopub.status.idle":"2021-07-20T19:51:33.790654Z","shell.execute_reply.started":"2021-07-20T19:51:33.783512Z","shell.execute_reply":"2021-07-20T19:51:33.789642Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Creating hyperparameters\nbatch_size = 128\nepochs = 50\nlearn_rate = .001\n# creating optimizers \nsgd = SGD(lr=learn_rate,momentum=.9,nesterov=False)\nadam = Adam(lr=learn_rate, beta_1=0.9, beta_2=0.999, epsilon=None,  amsgrad=False)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:51:36.726089Z","iopub.execute_input":"2021-07-20T19:51:36.726444Z","iopub.status.idle":"2021-07-20T19:51:36.737599Z","shell.execute_reply.started":"2021-07-20T19:51:36.726411Z","shell.execute_reply":"2021-07-20T19:51:36.736664Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Building \n### * The basic idea here is to extract features from the data set from pretrained models and create a simple deep net by using all those features combined.\n### * So we will use **GlobalAveragePooling2D** to extract a pooled output from our selected models\n### * Models I will use:\n###   1. **Inception**\n###   2. **Xception**\n###   3. **NASNETLarge**\n###   4. **InceptionResnetV2**\n### * Using these models I will extract features and combine them to create a combines feature map\n### * I will then feed this feature map to a neural net with an inputlayer and a softmax layer\n### * As a softmax layer is used, the loss function generated will be Categorical Cross Entropy\n### * This will lead to an output","metadata":{}},{"cell_type":"code","source":"# Function to extract features from the dataset by a given pretrained model\nimg_size = (331,331,3)\n\n# the name of each model is passed along with its preprocessor unit and an input size and some data\ndef get_features(model_name, model_preprocessor, input_size, data):\n    \n    # The data enters the input layer with the dimention of input size \n    input_layer = Input(input_size)\n    # On the input layer I apply the preprocessor to create the required preprocessed input \n    preprocessor = Lambda(model_preprocessor)(input_layer)\n    # The model now extracts features from the preprocessed input \n    base_model = model_name(weights='imagenet', include_top=False,\n                            input_shape=input_size)(preprocessor)\n    # I apply globalaveragepooling to the output to average all values\n    avg = GlobalAveragePooling2D()(base_model)\n    feature_extractor = Model(inputs = input_layer, outputs = avg)\n    \n    # Extract feature.\n    feature_maps = feature_extractor.predict(data, verbose=1)\n    print('Feature maps shape: ', feature_maps.shape)\n    return feature_maps","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:51:49.999154Z","iopub.execute_input":"2021-07-20T19:51:49.999497Z","iopub.status.idle":"2021-07-20T19:51:50.006036Z","shell.execute_reply.started":"2021-07-20T19:51:49.999464Z","shell.execute_reply":"2021-07-20T19:51:50.004909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### InceptionV3","metadata":{}},{"cell_type":"code","source":"from keras.applications.inception_v3 import InceptionV3, preprocess_input\ninception_preprocessor = preprocess_input\ninception_features = get_features(InceptionV3,\n                                  inception_preprocessor,\n                                  img_size, X)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:51:52.019236Z","iopub.execute_input":"2021-07-20T19:51:52.019593Z","iopub.status.idle":"2021-07-20T19:52:34.028904Z","shell.execute_reply.started":"2021-07-20T19:51:52.019559Z","shell.execute_reply":"2021-07-20T19:52:34.027916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Xception","metadata":{}},{"cell_type":"code","source":"from keras.applications.xception import Xception, preprocess_input\nxception_preprocessor = preprocess_input\nxception_features = get_features(Xception,\n                                 xception_preprocessor,\n                                 img_size, X)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:52:34.032146Z","iopub.execute_input":"2021-07-20T19:52:34.032424Z","iopub.status.idle":"2021-07-20T19:53:27.772400Z","shell.execute_reply.started":"2021-07-20T19:52:34.032394Z","shell.execute_reply":"2021-07-20T19:53:27.770489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### InceptionResNetV2","metadata":{}},{"cell_type":"code","source":"from keras.applications.inception_resnet_v2 import InceptionResNetV2, preprocess_input\ninc_resnet_preprocessor = preprocess_input\ninc_resnet_features = get_features(InceptionResNetV2,\n                                   inc_resnet_preprocessor,\n                                   img_size, X)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:53:27.774388Z","iopub.execute_input":"2021-07-20T19:53:27.775915Z","iopub.status.idle":"2021-07-20T19:54:47.892652Z","shell.execute_reply.started":"2021-07-20T19:53:27.775876Z","shell.execute_reply":"2021-07-20T19:54:47.891460Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### NASNetLarge","metadata":{}},{"cell_type":"code","source":"from keras.applications.nasnet import NASNetLarge, preprocess_input\nnasnet_preprocessor = preprocess_input\nnasnet_features = get_features(NASNetLarge,\n                               nasnet_preprocessor,\n                               img_size, X)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:54:47.894496Z","iopub.execute_input":"2021-07-20T19:54:47.894895Z","iopub.status.idle":"2021-07-20T19:57:59.270786Z","shell.execute_reply.started":"2021-07-20T19:54:47.894852Z","shell.execute_reply":"2021-07-20T19:57:59.269859Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del X  # To free up some ram as its not needed seeing as the feature map has been made \ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:58:14.709177Z","iopub.execute_input":"2021-07-20T19:58:14.709515Z","iopub.status.idle":"2021-07-20T19:58:15.444239Z","shell.execute_reply.started":"2021-07-20T19:58:14.709485Z","shell.execute_reply":"2021-07-20T19:58:15.443194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Combining all the feature maps","metadata":{}},{"cell_type":"code","source":"final_features = np.concatenate([inception_features,\n                                 xception_features,\n                                 nasnet_features,\n                                 inc_resnet_features,], axis=-1)  # axis=-1 to concatinate horizontally\n\nprint('Final feature maps shape', final_features.shape)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:58:42.816971Z","iopub.execute_input":"2021-07-20T19:58:42.817326Z","iopub.status.idle":"2021-07-20T19:58:42.964090Z","shell.execute_reply.started":"2021-07-20T19:58:42.817294Z","shell.execute_reply":"2021-07-20T19:58:42.963125Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating the Neural net","metadata":{}},{"cell_type":"markdown","source":"### As we are useing a Softmax layer, the loss function used is **Categorical Cross Entropy**. Below is the formala\n![image.png](attachment:b886ceac-8ed9-4b00-8ba3-a3e9e5ab290b.png)\n\n### where, $ y_{i} $ is the observed data and, $\\hat{y}_{i}$ is the predicted value ","metadata":{},"attachments":{"b886ceac-8ed9-4b00-8ba3-a3e9e5ab290b.png":{"image/png":"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1+MTPqPHgCGYMW+RYN/0jAzBAHklyuoNGy06d7FQ8uYtQwkRkT1hKCEisnFJSckoV83V6KL8yo1oo202bNkOlVqDLr8OVKiVRYehhIjI/jGUEBHZuI3bdhldkHsE1hNs07xjBFRqDY6fOqtAC4uWWCh5/fad0s0iIiIrYighIrJx3fsPMbognzJnvtHrT5+/RJmKjvAMDIFer1eolUVHLJS8evNW6WYREZEVMZQQEdm4Nl17GV2QX7xyzej1ngOHQqXWYP2mrQq1sGiJhZKXr98o3SwiIrIihhIiIhvXb9goowvyDzGfDK9t37MfKrUGoe06l/inJE+evcDNO/cQffsurtyIxqlzF7Dqr7/hWjNYEEqatu+Cjdt24eKVa7h5556hxMbFK30aRERkAYYSIiIbt+fgYaML8ktXryM9IwMLlq3C91Vc8JOjJ168eq10Mwutfst2hZ7ha/GqtUqfBhERWYChhIjIxul0OqMuXN9VcsJ3lZygUmvws1N1nL14WekmWgVDCRFR6cVQQkRUAmRlZWP2wqXwDm6I76u4QOMTgAHDR3MWKiIisgsMJUREREREpCiGEiIiIiIiUhRDCRERERERKYqhhIiIiIiIFMVQQkREREREimIoISIiIiIiRTGUEBERERGRohhKiIiIiIhIUQwlRERERESkKIYSIiIiIiJSFEMJEREREREpiqGEiIiIiIgUxVBCRERERESKYighIiIiIiJFMZQQEREREZGiGEqIiIiIiEhR/x/O4/TJYnWmkgAAAABJRU5ErkJggg=="}}},{"cell_type":"code","source":"# Prepare Deep net\nmodel = Sequential()\nmodel.add(Dropout(0.7,input_shape=(final_features.shape[1],)))\nmodel.add(Dense(n_classes,activation= 'softmax'))\n\nmodel.compile(optimizer=adam,\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])\n\n# Training the model. \nhistory = model.fit(final_features, y,\n            batch_size=batch_size,\n            epochs=epochs,\n            validation_split=0.2,\n            callbacks=[lrr,EarlyStop])","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:58:45.891942Z","iopub.execute_input":"2021-07-20T19:58:45.892311Z","iopub.status.idle":"2021-07-20T19:58:52.507307Z","shell.execute_reply.started":"2021-07-20T19:58:45.892280Z","shell.execute_reply":"2021-07-20T19:58:52.506480Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Deleting to free up ram memory\n\ndel inception_features\ndel xception_features\ndel nasnet_features\ndel inc_resnet_features\ndel final_features\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:59:00.625824Z","iopub.execute_input":"2021-07-20T19:59:00.626177Z","iopub.status.idle":"2021-07-20T19:59:00.820261Z","shell.execute_reply.started":"2021-07-20T19:59:00.626143Z","shell.execute_reply":"2021-07-20T19:59:00.819466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Function to read images from test directory\n# This is the same as the previous images_to_array function but now applied to test data\n\ndef images_to_array_test(test_path, img_size = (331,331,3)):\n    test_filenames = [test_path + fname for fname in os.listdir(test_path)]\n\n    data_size = len(test_filenames)\n    images = np.zeros([data_size, img_size[0], img_size[1], 3], dtype=np.uint8)\n    \n    \n    for ix,img_dir in enumerate(tqdm(test_filenames)):\n        img = load_img(img_dir, target_size = img_size)\n        images[ix]=img\n        del img\n    print('Ouptut Data Size: ', images.shape)\n    return images\n\ntest_data = images_to_array_test('/kaggle/input/dog-breed-identification/test/', img_size)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T19:59:09.993606Z","iopub.execute_input":"2021-07-20T19:59:09.993989Z","iopub.status.idle":"2021-07-20T20:00:25.279923Z","shell.execute_reply.started":"2021-07-20T19:59:09.993956Z","shell.execute_reply":"2021-07-20T20:00:25.278844Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Extract test data features.\ndef extact_features(data):\n    # Getting Inception features\n    inception_features = get_features(InceptionV3, inception_preprocessor, img_size, data)\n    # Getting Xception features\n    xception_features = get_features(Xception, xception_preprocessor, img_size, data)\n    # Getting NASNetLarge features\n    nasnet_features = get_features(NASNetLarge, nasnet_preprocessor, img_size, data)\n    # Getting InceptionResNet features\n    inc_resnet_features = get_features(InceptionResNetV2, inc_resnet_preprocessor, img_size, data)\n\n    # Concatenating all features\n    final_features = np.concatenate([inception_features,\n                                     xception_features,\n                                     nasnet_features,\n                                     inc_resnet_features],axis=-1)\n    \n    print('Final feature maps shape', final_features.shape)\n    \n    #deleting to free up ram memory\n    del inception_features\n    del xception_features\n    del nasnet_features\n    del inc_resnet_features\n    gc.collect()\n    \n    \n    return final_features\n\ntest_features = extact_features(test_data)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:00:42.441769Z","iopub.execute_input":"2021-07-20T20:00:42.442166Z","iopub.status.idle":"2021-07-20T20:06:45.474083Z","shell.execute_reply.started":"2021-07-20T20:00:42.442130Z","shell.execute_reply":"2021-07-20T20:06:45.473082Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Free up some space.\ndel test_data\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:06:58.796604Z","iopub.execute_input":"2021-07-20T20:06:58.796986Z","iopub.status.idle":"2021-07-20T20:06:59.084395Z","shell.execute_reply.started":"2021-07-20T20:06:58.796953Z","shell.execute_reply":"2021-07-20T20:06:59.083520Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Predict test labels given test data features.\npred = model.predict(test_features)","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:07:03.579598Z","iopub.execute_input":"2021-07-20T20:07:03.580004Z","iopub.status.idle":"2021-07-20T20:07:04.342381Z","shell.execute_reply.started":"2021-07-20T20:07:03.579972Z","shell.execute_reply":"2021-07-20T20:07:04.341457Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# First prediction\nprint(pred[0])\n# The max probability value predicted by the model\nprint(f\"Max value (probability of prediction): {np.max(pred[0])}\") \n\n# Because we used softmax activation in our model, this will be close to 1\nprint(f\"Sum: {np.sum(pred[0])}\") \n\n# The index of where the max value in predictions[0] occurs\nprint(f\"Max index: {np.argmax(pred[0])}\") \nprint(f\"Predicted label: {classes[np.argmax(pred[0])]}\")","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:07:06.773195Z","iopub.execute_input":"2021-07-20T20:07:06.773697Z","iopub.status.idle":"2021-07-20T20:07:06.798222Z","shell.execute_reply.started":"2021-07-20T20:07:06.773652Z","shell.execute_reply":"2021-07-20T20:07:06.797104Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Creating a file for all the predictions","metadata":{}},{"cell_type":"code","source":"# Create pandas DataFrame with empty columns\npreds_df = pd.DataFrame(columns=[\"id\"] + list(classes))\npreds_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:07:22.625857Z","iopub.execute_input":"2021-07-20T20:07:22.626274Z","iopub.status.idle":"2021-07-20T20:07:22.679457Z","shell.execute_reply.started":"2021-07-20T20:07:22.626241Z","shell.execute_reply":"2021-07-20T20:07:22.678430Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Append test image ID's to predictions DataFrame\ntest_path = \"/kaggle/input/dog-breed-identification/test/\"\npreds_df[\"id\"] = [os.path.splitext(path)[0] for path in os.listdir(test_path)]\npreds_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:07:31.099379Z","iopub.execute_input":"2021-07-20T20:07:31.099726Z","iopub.status.idle":"2021-07-20T20:07:31.167502Z","shell.execute_reply.started":"2021-07-20T20:07:31.099694Z","shell.execute_reply":"2021-07-20T20:07:31.166719Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds_df.loc[:,list(classes)]= pred\n\npreds_df.to_csv('result.csv',index=None)\npreds_df.head()","metadata":{"execution":{"iopub.status.busy":"2021-07-20T20:07:35.965912Z","iopub.execute_input":"2021-07-20T20:07:35.966264Z","iopub.status.idle":"2021-07-20T20:07:38.493374Z","shell.execute_reply.started":"2021-07-20T20:07:35.966234Z","shell.execute_reply":"2021-07-20T20:07:38.492573Z"},"trusted":true},"execution_count":null,"outputs":[]}]}