{"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":"# Breed Classification ","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nfrom keras.models import Sequential\nfrom keras.layers import Dense, Dropout, Input\nfrom keras.layers import MaxPooling2D, ZeroPadding2D, Conv2D, Flatten\nfrom keras.optimizers import Adam, SGD\nfrom keras.preprocessing.image import img_to_array, load_img, ImageDataGenerator\nfrom keras.utils import to_categorical","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"execution":{"iopub.status.busy":"2021-07-07T10:17:14.653275Z","iopub.execute_input":"2021-07-07T10:17:14.653682Z","iopub.status.idle":"2021-07-07T10:17:16.339628Z","shell.execute_reply.started":"2021-07-07T10:17:14.653614Z","shell.execute_reply":"2021-07-07T10:17:16.338757Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Target\n- A model that can classify 120 classes of the different breeds of dogs.\n\n# Approach \n- A CNN with 120 classes ","metadata":{}},{"cell_type":"markdown","source":"# EDA\n","metadata":{}},{"cell_type":"code","source":"# read the CSV that contains the class labels\ndf1=pd.read_csv('../input/dog-breed-identification/labels.csv')\ndf1.head()","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:16.341535Z","iopub.execute_input":"2021-07-07T10:17:16.341837Z","iopub.status.idle":"2021-07-07T10:17:16.372266Z","shell.execute_reply.started":"2021-07-07T10:17:16.341809Z","shell.execute_reply":"2021-07-07T10:17:16.371290Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#location of files \nimg_file='../input/dog-breed-identification/train/'\n\n# adding column in the df for the image location\ndf = df1.assign(img_path=lambda x: img_file + x['id']+'.jpg')\ndf.head()","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:16.373863Z","iopub.execute_input":"2021-07-07T10:17:16.374215Z","iopub.status.idle":"2021-07-07T10:17:16.394750Z","shell.execute_reply.started":"2021-07-07T10:17:16.374180Z","shell.execute_reply":"2021-07-07T10:17:16.394010Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(f\"Total {df.shape[0]} images available for training\")","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:16.395774Z","iopub.execute_input":"2021-07-07T10:17:16.396092Z","iopub.status.idle":"2021-07-07T10:17:16.402646Z","shell.execute_reply.started":"2021-07-07T10:17:16.396063Z","shell.execute_reply":"2021-07-07T10:17:16.400671Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"\nThere looks like a case of class imbalance. We can add synthetic oversampling (for the minority class) and undersampling (for the majority class) in the of data augmentaiton. \n","metadata":{"execution":{"iopub.status.busy":"2021-07-07T08:38:03.427807Z","iopub.execute_input":"2021-07-07T08:38:03.428216Z","iopub.status.idle":"2021-07-07T08:38:03.433639Z","shell.execute_reply.started":"2021-07-07T08:38:03.428181Z","shell.execute_reply":"2021-07-07T08:38:03.432439Z"}}},{"cell_type":"code","source":"# class distribution \ndf.breed.value_counts()","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:16.404220Z","iopub.execute_input":"2021-07-07T10:17:16.404715Z","iopub.status.idle":"2021-07-07T10:17:16.418156Z","shell.execute_reply.started":"2021-07-07T10:17:16.404678Z","shell.execute_reply":"2021-07-07T10:17:16.417014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Loading dataset\n\nAssuming that we have cleaned the data and the data is ready for modeling, there are multiple ways of loading the dataset. The size of the available RAM dictates our choices here. \n\n\nThe data overfits the RAM in our case, so to keep things a bit simple, we’ll use a third of the data available.\n","metadata":{"execution":{"iopub.status.busy":"2021-07-07T08:15:19.561673Z","iopub.execute_input":"2021-07-07T08:15:19.562006Z","iopub.status.idle":"2021-07-07T08:15:19.568337Z","shell.execute_reply.started":"2021-07-07T08:15:19.561976Z","shell.execute_reply":"2021-07-07T08:15:19.566929Z"}}},{"cell_type":"code","source":"from tqdm import tqdm_notebook as tqdm\n\n''' loading the data directly to the RAM\n    \n    - create a numpy array.\n    - fill it with the images.\n \n    \n'''\n\nparts = 2\nimage_shape = (324, 324, 3)\n\n\nimages = df.img_path.values.tolist()\ntotal_images = len(images)\n\nimg_pixels = np.zeros(shape=(total_images//parts,image_shape[0],image_shape[1],image_shape[2]), dtype=np.uint8)\n\nfor i, img in tqdm(enumerate(images[:len(images)//parts])):\n    pixels = load_img(img, target_size=image_shape)\n    img_pixels[i,:,:,:] = pixels\n\nprint(img_pixels.shape)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:16.419464Z","iopub.execute_input":"2021-07-07T10:17:16.419826Z","iopub.status.idle":"2021-07-07T10:17:34.470006Z","shell.execute_reply.started":"2021-07-07T10:17:16.419779Z","shell.execute_reply":"2021-07-07T10:17:34.468916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# label encoding\n\nlabels = df.breed[:len(df.breed)//parts]\nimg_label = pd.get_dummies(labels)\nimg_label","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:34.471211Z","iopub.execute_input":"2021-07-07T10:17:34.471485Z","iopub.status.idle":"2021-07-07T10:17:34.496438Z","shell.execute_reply.started":"2021-07-07T10:17:34.471458Z","shell.execute_reply":"2021-07-07T10:17:34.495520Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# lets get the dimensions \nX=img_pixels\ny=img_label.values\nprint(X.shape)\nprint(y.shape)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:34.499019Z","iopub.execute_input":"2021-07-07T10:17:34.499379Z","iopub.status.idle":"2021-07-07T10:17:34.504709Z","shell.execute_reply.started":"2021-07-07T10:17:34.499344Z","shell.execute_reply":"2021-07-07T10:17:34.503555Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# train_test_split\nfrom sklearn.model_selection import train_test_split\nX_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.2)\nprint(X_train.shape)\nprint(y_train.shape)\nprint(X_test.shape)\nprint(y_test.shape)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:34.506542Z","iopub.execute_input":"2021-07-07T10:17:34.506932Z","iopub.status.idle":"2021-07-07T10:17:35.174358Z","shell.execute_reply.started":"2021-07-07T10:17:34.506897Z","shell.execute_reply":"2021-07-07T10:17:35.173475Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Data Augmentation\ntrain_datagen = ImageDataGenerator(\n#     rotation_range=30,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    rescale=1./255,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    fill_mode='nearest')\n\ntest_datagen=ImageDataGenerator(rescale=1./255)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:35.175690Z","iopub.execute_input":"2021-07-07T10:17:35.176065Z","iopub.status.idle":"2021-07-07T10:17:35.183049Z","shell.execute_reply.started":"2021-07-07T10:17:35.176027Z","shell.execute_reply":"2021-07-07T10:17:35.181347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"batch_size = 64\ntraining_set=train_datagen.flow(X_train,y=y_train,batch_size=batch_size)\ntesting_set=test_datagen.flow(X_test,y=y_test,batch_size=batch_size)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:35.184324Z","iopub.execute_input":"2021-07-07T10:17:35.184674Z","iopub.status.idle":"2021-07-07T10:17:36.918883Z","shell.execute_reply.started":"2021-07-07T10:17:35.184635Z","shell.execute_reply":"2021-07-07T10:17:36.917743Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# image preview\ndef show_image(generator):\n    pass\n\nshow_image(training_set)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:36.920285Z","iopub.execute_input":"2021-07-07T10:17:36.920643Z","iopub.status.idle":"2021-07-07T10:17:36.927052Z","shell.execute_reply.started":"2021-07-07T10:17:36.920605Z","shell.execute_reply":"2021-07-07T10:17:36.925886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model definition [Keras]","metadata":{}},{"cell_type":"code","source":"# CNN model definition \nfrom keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D\nfrom keras.layers import Dropout, Activation, Dense\nfrom keras.models import Sequential\nfrom keras.layers.normalization import BatchNormalization\n\n\n\nmodel = Sequential()\n\nmodel.add(Conv2D(16, (3, 3), padding='same', use_bias=False, input_shape=image_shape))\nmodel.add(BatchNormalization(axis=3, scale=False))\nmodel.add(Activation(\"relu\"))\nmodel.add(MaxPooling2D(pool_size=(4, 4), strides=(4, 4), padding='same'))\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(32, (3, 3), padding='same', use_bias=False))\nmodel.add(BatchNormalization(axis=3, scale=False))\nmodel.add(Activation(\"relu\"))\nmodel.add(MaxPooling2D(pool_size=(4, 4), strides=(4, 4), padding='same'))\nmodel.add(Dropout(0.4))\n\nmodel.add(Conv2D(64, (3, 3), padding='same', use_bias=False))\nmodel.add(BatchNormalization(axis=3, scale=False))\nmodel.add(Activation(\"relu\"))\nmodel.add(MaxPooling2D(pool_size=(4, 4), strides=(4, 4), padding='same'))\nmodel.add(Dropout(0.6))\n\nmodel.add(Conv2D(128, (3, 3), padding='same', use_bias=False))\nmodel.add(BatchNormalization(axis=3, scale=False))\nmodel.add(Activation(\"relu\"))\nmodel.add(Flatten())\nmodel.add(Dropout(0.5))\n\nmodel.add(Dense(1024, activation='relu'))\nmodel.add(Dense(120, activation='softmax'))\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:18:45.579988Z","iopub.execute_input":"2021-07-07T10:18:45.580360Z","iopub.status.idle":"2021-07-07T10:18:45.710753Z","shell.execute_reply.started":"2021-07-07T10:18:45.580329Z","shell.execute_reply":"2021-07-07T10:18:45.709845Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# compile the model with loss\nfrom keras.losses import categorical_crossentropy\nmodel.compile(loss=categorical_crossentropy,optimizer='adam',metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:18:57.735532Z","iopub.execute_input":"2021-07-07T10:18:57.735879Z","iopub.status.idle":"2021-07-07T10:18:57.748803Z","shell.execute_reply.started":"2021-07-07T10:18:57.735846Z","shell.execute_reply":"2021-07-07T10:18:57.747924Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# scheduling learning rate\nimport tensorflow as tf\n\ndef scheduler(epoch, lr):\n    if epoch < 5:\n        return lr\n    else:\n        return lr * tf.math.exp(-0.1)\n\nfrom keras import callbacks \ncallback = callbacks.LearningRateScheduler(scheduler)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:19:01.011216Z","iopub.execute_input":"2021-07-07T10:19:01.011560Z","iopub.status.idle":"2021-07-07T10:19:01.016893Z","shell.execute_reply.started":"2021-07-07T10:19:01.011532Z","shell.execute_reply":"2021-07-07T10:19:01.016039Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Lets train it out \nhistory=model.fit_generator(training_set,\n                            steps_per_epoch = 64,\n                            validation_data = testing_set,\n                            validation_steps = 32,\n                            callbacks=[callback],\n                            epochs = 50,\n                            verbose = 1)","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:19:04.733413Z","iopub.execute_input":"2021-07-07T10:19:04.733756Z","iopub.status.idle":"2021-07-07T10:23:26.290837Z","shell.execute_reply.started":"2021-07-07T10:19:04.733725Z","shell.execute_reply":"2021-07-07T10:23:26.287682Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(211)\nplt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('model accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'val'], loc='upper left')\n\nplt.figure(212)\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('model loss')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'val'], loc='upper left')\n\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-07-07T10:17:43.391368Z","iopub.status.idle":"2021-07-07T10:17:43.392188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}