{"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":"# Change working directory","metadata":{}},{"cell_type":"code","source":"%cd /kaggle/working\n%ls","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:22:38.915610Z","iopub.execute_input":"2022-01-20T10:22:38.916150Z","iopub.status.idle":"2022-01-20T10:22:39.626245Z","shell.execute_reply.started":"2022-01-20T10:22:38.916033Z","shell.execute_reply":"2022-01-20T10:22:39.625117Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Libraries\n- cv2: load image and draw rectangle (imread,  cvtColor, rectangle)\n- random: shuffle the list (shuffle)\n- pandas: create DataFrame\n- matplotlib: show images and plot graph\n- os: work with file","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport os, cv2, random\nimport pandas as pd\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.models import Sequential, Model, load_model\nfrom tensorflow.keras.layers import Convolution2D,MaxPooling2D, Dense, Flatten, InputLayer\nfrom tensorflow.keras.applications.vgg16 import VGG16\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.backend import clear_session\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:22:39.629181Z","iopub.execute_input":"2022-01-20T10:22:39.629931Z","iopub.status.idle":"2022-01-20T10:22:44.446372Z","shell.execute_reply.started":"2022-01-20T10:22:39.629881Z","shell.execute_reply":"2022-01-20T10:22:44.445529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Unzip files","metadata":{}},{"cell_type":"code","source":"import zipfile\ninput_path = '/kaggle/input/dogs-vs-cats'\nwork_path = '/kaggle/working/data'\ntrain_path = os.path.join(input_path,'train.zip')\ntest_path = os.path.join(input_path,'test1.zip')\n\nwith zipfile.ZipFile(train_path, 'r') as zip_ref:\n    zip_ref.extractall(work_path)\nwith zipfile.ZipFile(test_path, 'r') as zip_ref:\n    zip_ref.extractall(work_path)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:22:44.448213Z","iopub.execute_input":"2022-01-20T10:22:44.448501Z","iopub.status.idle":"2022-01-20T10:23:04.940594Z","shell.execute_reply.started":"2022-01-20T10:22:44.448474Z","shell.execute_reply":"2022-01-20T10:23:04.939751Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create directory path","metadata":{}},{"cell_type":"code","source":"data_dir = work_path\ntrain_dir = data_dir+\"/train\"\ntest_dir = data_dir+\"/test1\"","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:04.942387Z","iopub.execute_input":"2022-01-20T10:23:04.942756Z","iopub.status.idle":"2022-01-20T10:23:04.947504Z","shell.execute_reply.started":"2022-01-20T10:23:04.942720Z","shell.execute_reply":"2022-01-20T10:23:04.946616Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Create dataframe and data generator\n- Frame structure: { 'filename': [...], 'class': ['dog',...] }\n- Create the frame with the struct above and shuffle random (frac=1)\n- We do the data augmentation and shuffle with train data only (shuffle validation set does not achive anything)\n- Splitting 80% for train, 20% for testing\n- Each set, we divide into multiple batchs\n- Because of having only 2 classes, the mode will be binary","metadata":{}},{"cell_type":"code","source":"df = pd.DataFrame()\nfnames = os.listdir(train_dir)\n\nclass_name = []\nfor name in fnames:\n    class_name.append(name.split('.')[0])\n    \ndata = {'filename':fnames,'class':class_name}\ndf = pd.DataFrame(data)\ndf = df.sample(frac=1)\n\ntrain_datagen = ImageDataGenerator(rescale = 1/255,\n                                   rotation_range=20,\n                                   shear_range=0.2,\n                                   width_shift_range=0.2,\n                                   height_shift_range=0.2,\n                                   horizontal_flip=True,\n                                   zoom_range=0.2)\nvalid_datagen = ImageDataGenerator(rescale = 1/255)\n\nidx = int(0.8*len(df))\ntrain_df = df.iloc[:idx]\nvalid_df = df.iloc[idx:]\n\ntarget = (224,224)\n\ntrain_set = train_datagen.flow_from_dataframe(train_df,\n                                              directory=train_dir,\n                                              shuffle=True,\n                                              target_size = target,\n                                              batch_size = 64,\n                                              class_mode = 'binary')\n\nvalid_set = valid_datagen.flow_from_dataframe(valid_df,\n                                              directory=train_dir,\n                                              shuffle=False,\n                                              target_size = target,\n                                              batch_size = 32,\n                                              class_mode = 'binary')","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:04.948845Z","iopub.execute_input":"2022-01-20T10:23:04.949329Z","iopub.status.idle":"2022-01-20T10:23:05.329323Z","shell.execute_reply.started":"2022-01-20T10:23:04.949292Z","shell.execute_reply":"2022-01-20T10:23:05.328379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# VGG 1-block model\n- This is the model with VGG-style using 1 block of Conv2D and Pooling\n- Using the learning rate 0.05\n- Momentum .9 and Nesterov (NAG optimizer)\n- Loss function is binary cross entropy (binary classification cat or dog only)","metadata":{}},{"cell_type":"code","source":"clear_session()\n\nmodel = Sequential([\n    InputLayer(input_shape=target+(3,)),\n    Convolution2D(32,3,activation='relu'),\n    MaxPooling2D(2),\n    Flatten(),\n    Dense(512,activation='relu'),\n    Dense(1,activation='sigmoid')\n])\nmodel.summary()\n\nopt = SGD(learning_rate = 0.05,\n          momentum = 0.9,\n          nesterov = True)\nmodel.compile(loss='binary_crossentropy',optimizer=opt,metrics=['acc'])\nmodel.optimizer.get_config()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:05.330616Z","iopub.execute_input":"2022-01-20T10:23:05.331025Z","iopub.status.idle":"2022-01-20T10:23:07.666281Z","shell.execute_reply.started":"2022-01-20T10:23:05.330979Z","shell.execute_reply":"2022-01-20T10:23:07.665422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# VGG 2-block model\n- This is the model with VGG-style using 2 block of Conv2D and Pooling\n- Using the learning rate 0.05\n- Momentum 0.9 and Nesterov (NAG optimizer)\n- Loss function is binary cross entropy (binary classification cat or dog only)","metadata":{}},{"cell_type":"code","source":"clear_session()\n\nmodel = Sequential([\n    InputLayer(input_shape=target+(3,)),\n    Convolution2D(32,3,activation='relu'),\n    MaxPooling2D(2),\n    Convolution2D(64,3,activation='relu'),\n    MaxPooling2D(2),\n    Flatten(),\n    Dense(512,activation='relu'),\n    Dense(1,activation='sigmoid')\n])\nmodel.summary()\n\nopt = SGD(learning_rate = 0.05,\n          momentum = 0.9,\n          nesterov = True)\nmodel.compile(loss='binary_crossentropy',optimizer=opt,metrics=['acc'])\nmodel.optimizer.get_config()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:07.667538Z","iopub.execute_input":"2022-01-20T10:23:07.668056Z","iopub.status.idle":"2022-01-20T10:23:07.732357Z","shell.execute_reply.started":"2022-01-20T10:23:07.668019Z","shell.execute_reply":"2022-01-20T10:23:07.731615Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# VGG 3-block model\n- This is the model with VGG-style using 3 block of Conv2D and Pooling\n- Using the learning rate 0.05\n- Momentum 0.9 and Nesterov (NAG optimizer)\n- Loss function is binary cross entropy (binary classification cat or dog only)\n","metadata":{}},{"cell_type":"code","source":"clear_session()\n\nmodel = Sequential([\n    InputLayer(input_shape=target+(3,)),\n    Convolution2D(32,3,activation='relu'),\n    MaxPooling2D(2),\n    Convolution2D(64,3,activation='relu'),\n    MaxPooling2D(2),\n    Convolution2D(128,3,activation='relu'),\n    MaxPooling2D(2),\n    Flatten(),\n    Dense(512,activation='relu'),\n    Dense(1,activation='sigmoid')\n])\nmodel.summary()\n\nopt = SGD(learning_rate = 0.05,\n          momentum = 0.9,\n          nesterov = True)\nmodel.compile(loss='binary_crossentropy',optimizer=opt,metrics=['acc'])\nmodel.optimizer.get_config()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:07.735054Z","iopub.execute_input":"2022-01-20T10:23:07.735300Z","iopub.status.idle":"2022-01-20T10:23:07.806686Z","shell.execute_reply.started":"2022-01-20T10:23:07.735276Z","shell.execute_reply":"2022-01-20T10:23:07.805850Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# VGG-16 modified model\n- Using pre-trained model VGG-16 without top layers\n- The VGG-16 required image of shape (224,224)\n- We freeze the trained parameters of the model\n- Then we add our top layer by our self","metadata":{}},{"cell_type":"code","source":"clear_session()\n\nmodel = VGG16(include_top=False, input_shape=target+(3,))\nfor layer in model.layers:\n    layer.trainable = False\nflat1 = Flatten()(model.layers[-1].output)\nclass1 = Dense(128, activation='relu', kernel_initializer='he_uniform')(flat1)\noutput = Dense(1, activation='sigmoid')(class1)\nmodel = Model(inputs=model.inputs, outputs=output)\n\nmodel.summary()\n\nopt = SGD(learning_rate = 0.001,\n          momentum = 0.9)\nmodel.compile(loss='binary_crossentropy',optimizer=opt,metrics=['acc'])\nmodel.optimizer.get_config()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:07.809427Z","iopub.execute_input":"2022-01-20T10:23:07.809732Z","iopub.status.idle":"2022-01-20T10:23:08.531176Z","shell.execute_reply.started":"2022-01-20T10:23:07.809700Z","shell.execute_reply":"2022-01-20T10:23:08.530335Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train model\nIn this section, we train the model with the checkpoint and early stopping method:\n\n- Checkpoint:\n    - We save only the best model which have highest \"val acc\" value\n    - We save whole the model not the weight only\n    - Mode is auto which means we choose model whose the maximum value \"val acc\"\n- Early stopping:\n    - We also use the \"val acc\" value of the model to evaluate\n    - If the model is not improve after 10 epochs, we will stop trainning (it may be converged)\n- Trainning:\n    - The iteration of each epoch can be calculated by: number of data / batch size (we use all the batches to train)","metadata":{}},{"cell_type":"code","source":"checkpoint = ModelCheckpoint(\"temp_model.h5\", \n                             monitor='val_acc', \n                             verbose=1, \n                             save_best_only=True, \n                             save_weight_only=False, \n                             mode='auto')\n\nearly = EarlyStopping(monitor='val_acc', \n                      min_delta=0, \n                      patience=10, \n                      verbose=1, \n                      mode='auto')\n\nhistory = model.fit(train_set,\n                    validation_data=valid_set,\n                    steps_per_epoch=train_set.n//train_set.batch_size,\n                    validation_steps=valid_set.n//valid_set.batch_size,\n                    epochs = 50,\n                    callbacks=[checkpoint,early])","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:23:08.532939Z","iopub.execute_input":"2022-01-20T10:23:08.533543Z","iopub.status.idle":"2022-01-20T10:30:16.741222Z","shell.execute_reply.started":"2022-01-20T10:23:08.533505Z","shell.execute_reply":"2022-01-20T10:30:16.736870Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Plot the loss and accuracy of train and validation\n- This step can help you detect the overfitting problem by looking at learning curve","metadata":{}},{"cell_type":"code","source":"legend = ['train','validation']\n\nplt.plot(history.history['acc'])\nplt.plot(history.history['val_acc'])\nplt.title(\"Accuracy\")\nplt.xlabel(\"epochs\")\nplt.ylabel(\"acc\")\nplt.legend(legend,loc='upper left')\nplt.show()\n\nplt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title(\"Binary cross-entropy loss\")\nplt.xlabel(\"epochs\")\nplt.ylabel(\"loss\")\nplt.legend(legend,loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:30:16.744691Z","iopub.status.idle":"2022-01-20T10:30:16.747361Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Save the model down","metadata":{}},{"cell_type":"code","source":"model.save('my_model.h5')","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:30:16.752422Z","iopub.status.idle":"2022-01-20T10:30:16.754946Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Load the model up","metadata":{}},{"cell_type":"code","source":"model = load_model('my_model.h5')\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:30:24.232762Z","iopub.execute_input":"2022-01-20T10:30:24.233167Z","iopub.status.idle":"2022-01-20T10:30:24.564496Z","shell.execute_reply.started":"2022-01-20T10:30:24.233130Z","shell.execute_reply":"2022-01-20T10:30:24.563588Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Test the accuracy after load the model","metadata":{}},{"cell_type":"code","source":"print(\"Accuracy:\",model.evaluate(valid_set)) # (loss, accuracy)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:30:27.385324Z","iopub.execute_input":"2022-01-20T10:30:27.385640Z","iopub.status.idle":"2022-01-20T10:30:47.536895Z","shell.execute_reply.started":"2022-01-20T10:30:27.385611Z","shell.execute_reply":"2022-01-20T10:30:47.535987Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Extract all the conv layer to visualize the prediction\n- We take all the output of layer whose name contains 'conv' \n- Then we create a new model whose outputs are those layers\n- This step is to understand what is working inside the model","metadata":{}},{"cell_type":"code","source":"layer_outputs = []\nfor layer in model.layers:\n    if 'conv' not in(layer.name):\n        continue\n    layer_outputs.append(layer.output)\n\nactivation_model = Model(inputs = model.input, outputs = layer_outputs)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:30:48.955114Z","iopub.execute_input":"2022-01-20T10:30:48.955487Z","iopub.status.idle":"2022-01-20T10:30:48.964708Z","shell.execute_reply.started":"2022-01-20T10:30:48.955451Z","shell.execute_reply":"2022-01-20T10:30:48.963826Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Define functions\n- Preprocess: Use to resize the function to the target size and map the value of pixel to [0,1]\n- Predict: Create the batch and get the proabiblity of the model\n- Get label: Get the label of the prediction from the probability\n- Visualize: Visualize the conv layers output\n- Get box: Get the best predicted box which contains the dog/cat using sliding window","metadata":{}},{"cell_type":"code","source":"def preprocess(img):\n    img = cv2.resize(img,target)\n    img = img/255\n    return np.array(img)\n    \ndef predict(img):\n    img = preprocess(img)\n    img = img.reshape((1,)+img.shape)\n    probability = model.predict(img)\n    return probability\n\ndef getLabel(probability):\n    if probability<0.5:\n        probability=0\n    else:\n        probability=1\n    return list(train_set.class_indices)[probability]\n\ndef visualize(img):\n    img = preprocess(img)\n    img = img.reshape((1,)+img.shape)\n    fmaps = activation_model.predict(img)\n\n    for i in range(len(fmaps)):\n        activation = fmaps[i]\n        \n        fig = plt.figure(figsize=(20,15))\n        fig.suptitle(layer_outputs[i].name)\n        \n        for j in range(min(8*8,activation.shape[-1])):\n            plt.subplot(8,8,j+1)\n            plt.imshow(activation[0,:,:,j],cmap='gray')\n    plt.show()\n\nWIN_SIZES=[]\nfor i in range(100,260,20):\n    WIN_SIZES.append(i)\n    \ndef get_box(img,step=20,win_sizes=WIN_SIZES):\n    best_box = None\n    best_distance = 1\n    raw_prob = predict(img)\n    if (raw_prob<0.5):\n        raw_prob=0\n    else:\n        raw_prob=1\n        \n    for win in win_sizes:\n        print(\"Run with window size:\",str(win))\n        for top in range(0,img.shape[0]-win+1,step):\n            for left in range(0,img.shape[1]-win+1,step):\n                box = (left,top,left+win,top+win)\n                crop = img[box[1]:box[3],box[0]:box[2]]\n                prob = predict(crop)\n                distance = abs(raw_prob-prob)\n                if (distance<best_distance):\n                    best_box = box\n                    best_distance = distance\n    return (best_box, best_distance)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:30:54.200241Z","iopub.execute_input":"2022-01-20T10:30:54.200572Z","iopub.status.idle":"2022-01-20T10:30:54.214581Z","shell.execute_reply.started":"2022-01-20T10:30:54.200542Z","shell.execute_reply":"2022-01-20T10:30:54.213699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Testing predict with random images","metadata":{}},{"cell_type":"code","source":"test_fnames = os.listdir(test_dir)\nrandom.shuffle(test_fnames)\nresult = []\n\nnRow = 5\nnCol = 5\nnPic = nRow*nCol\n\nfig, ax = plt.subplots(nRow,nCol,figsize=(30,20))\n\nfor i, fnames in enumerate(test_fnames):\n    if (i==nPic):\n        break\n    \n    pred_path = os.path.join(test_dir,fnames)\n    img = cv2.imread(pred_path)\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    \n    col = i%nCol\n    row = i//nCol\n    title = getLabel(predict(img))\n    ax[row][col].imshow(img)\n    ax[row][col].set_title(title)\n    ax[row][col].axis('off')\nplt.show()    ","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:32:00.126374Z","iopub.execute_input":"2022-01-20T10:32:00.126722Z","iopub.status.idle":"2022-01-20T10:32:04.979337Z","shell.execute_reply.started":"2022-01-20T10:32:00.126692Z","shell.execute_reply":"2022-01-20T10:32:04.976635Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Testing predict one image","metadata":{}},{"cell_type":"code","source":"# Load the image\ntest_fnames = os.listdir(test_dir)\nidPic = random.randint(0,len(test_fnames))\npred_path = os.path.join(test_dir,test_fnames[idPic])\nimg = cv2.imread(pred_path)\nimg = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n\n# Get the prediction\ntitle = getLabel(predict(img))\n        \n# Plot the image and result\nplt.imshow(img)\nplt.title(title)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:35:48.301059Z","iopub.execute_input":"2022-01-20T10:35:48.301402Z","iopub.status.idle":"2022-01-20T10:35:48.500528Z","shell.execute_reply.started":"2022-01-20T10:35:48.301371Z","shell.execute_reply":"2022-01-20T10:35:48.499720Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Visualize CNN output's layers","metadata":{}},{"cell_type":"code","source":"# Visualize the conv layers\nvisualize(img)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:35:51.483155Z","iopub.execute_input":"2022-01-20T10:35:51.483484Z","iopub.status.idle":"2022-01-20T10:36:55.449344Z","shell.execute_reply.started":"2022-01-20T10:35:51.483453Z","shell.execute_reply":"2022-01-20T10:36:55.448540Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Running sliding window","metadata":{}},{"cell_type":"code","source":"# Finding the best fit box for detection\nbox, best_distance = get_box(img)\nprint(\"Probability:\",float(1-best_distance))\nstartPoint = (box[0],box[1])\nendPoint = (box[2],box[3])\n\n# Draw rectangle and show image\nCOLOR=(255,0,0)\nimg = cv2.rectangle(img,startPoint,endPoint,COLOR,2)\nplt.imshow(img)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:37:28.493391Z","iopub.execute_input":"2022-01-20T10:37:28.493721Z","iopub.status.idle":"2022-01-20T10:37:28.811800Z","shell.execute_reply.started":"2022-01-20T10:37:28.493690Z","shell.execute_reply":"2022-01-20T10:37:28.810988Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Output for submission","metadata":{}},{"cell_type":"code","source":"submission = pd.DataFrame()\n\ntest_fnames = os.listdir(test_dir)\nresult = []\n\npre_percent = 0\nfor i,fnames in enumerate(test_fnames):\n    pred_path = os.path.join(test_dir,fnames)\n    img = cv2.imread(pred_path)\n    img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)\n    result.append(float(np.round(np.reshape(predict(img),-1))))\n    \n    percent = i/len(test_fnames)\n    if (pre_percent+0.1<=percent):\n        pre_percent+=0.1\n        print(\"Done:\",str(round(percent*100))+'%')\n\nfor i in range(len(test_fnames)):\n    test_fnames[i] = test_fnames[i].split('.')[0]\nsubmission['id'] = test_fnames\nsubmission['label'] = result\nsubmission.to_csv('submission.csv',index=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-20T10:44:03.453454Z","iopub.execute_input":"2022-01-20T10:44:03.453787Z","iopub.status.idle":"2022-01-20T10:44:09.164976Z","shell.execute_reply.started":"2022-01-20T10:44:03.453757Z","shell.execute_reply":"2022-01-20T10:44:09.159672Z"},"trusted":true},"execution_count":null,"outputs":[]}]}