{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator, load_img\nfrom keras.utils import to_categorical\nfrom sklearn.model_selection import train_test_split\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\nimport pandas as pd\nimport numpy as np\nfrom tqdm import tqdm\n\nimport tensorflow as tf\nnp.random.seed(0)\n\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 1. Config parameters","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"INPUT_SOURCE = '/kaggle/input/dogs-vs-cats'\nFAST_RUN = False\nIMAGE_WIDTH=224\nIMAGE_HEIGHT=224\nIMAGE_SIZE=(IMAGE_WIDTH, IMAGE_HEIGHT)\nIMAGE_CHANNELS=3\nFILE_PATH = '/kaggle/input/dogs-vs-cats-redux-kernels-edition/'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 2. Preprocessing data","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"### 2.1. Import data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -rf \"./train\"\n!unzip -q \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Preparing the data\nfilenames = os.listdir(\"./train\")\ncategories = []\nfor filename in filenames:\n    category = filename.split('.')[0]\n    if category == 'dog':\n        categories.append(1)\n    else:\n        categories.append(0)\n\ndf = pd.DataFrame({\n    'filename': filenames,\n    'category': categories\n})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Distribution of count classes\nsns.set(style=\"white\")\nsns.countplot(df[\"category\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = np.random.choice(df['filename'])\nimage = load_img(\"./train/\" + sample)\n# Each image is of different shapes and has 3 channel for RGB\nplt.imshow(image)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2.2. Split train/validation data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"df['category'] = df[\"category\"].replace({0: 'cat', 1: 'dog'}) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Splitting the data\ntrain_df, val_df = train_test_split(df, test_size=.2, stratify=df[\"category\"], random_state=42)\ntrain_df = train_df.reset_index()\nval_df = val_df.reset_index()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 2.3. Image augmentation","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"batch_size=32\ntotal_train = train_df.shape[0]\ntotal_validate = val_df.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rotation_range=15,\n    rescale=1./255,\n    shear_range=0.1,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    width_shift_range=0.1,\n    height_shift_range=0.1\n)\n\ntrain_generator = train_datagen.flow_from_dataframe(\n    train_df, \n    \"./train/\", \n    x_col='filename',\n    y_col='category',\n    target_size=IMAGE_SIZE,\n    class_mode='categorical',\n    batch_size=batch_size\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"validation_datagen = ImageDataGenerator(rescale=1./255)\nvalidation_generator = validation_datagen.flow_from_dataframe(\n    val_df, \n    \"./train/\", \n    x_col='filename',\n    y_col='category',\n    target_size=IMAGE_SIZE,\n    class_mode='categorical',\n    batch_size=batch_size\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 3. Build training model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class ConvBlock(tf.keras.Model):\n    def __init__(self, filters, kernel, strides, padding):\n        '''\n        Khởi tạo Convolution Block với các tham số đầu vào\n        \n        Parameters\n        ----------\n        filters: int\n            số lượng filter\n        kernel: int\n            kích thước kernel\n        strides: int\n            stride của convolution layer\n        padding: str\n            Loại padding của convolution layer\n        \n        '''\n        \n        super(ConvBlock, self).__init__()\n        # Tạo layer Conv2D\n        self.cnn = tf.keras.layers.Conv2D(filters, kernel,  strides=strides,\n                                          activation='relu',\n                                          kernel_initializer='he_normal',\n                                          kernel_regularizer=tf.keras.regularizers.l2(1e-3),\n                                          padding=padding)\n\n        # Tạo layer MaxPool2D\n        self.pool = tf.keras.layers.MaxPool2D((2, 2))\n        \n        \n    def call(self, inputs):\n        '''\n        Hàm này sẽ được gọi trong quá trình forwarding của mạng\n        \n        Parameters\n        ----------\n        inputs: tensor đầu vào\n        \n        Returns\n        -------\n        tensor\n            giá trị đầu ra của mạng\n        '''\n        \n        x = inputs\n        \n        x = self.cnn(x)\n        x = self.pool(x)\n\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class CNN(tf.keras.Model):\n    def __init__(self, num_classes):\n        \n        super(CNN, self).__init__()\n        \n        self.block1 = ConvBlock(32, (3,3), (1,1), 'same')\n        self.block2 = ConvBlock(64, (3,3), (1,1), 'same')\n        self.block3 = ConvBlock(64, (3,3), (1,1), 'same')\n        self.block4 = ConvBlock(64, (3,3), (1,1), 'same')\n        self.block5 = ConvBlock(128, (3,3), (1,1), 'same')\n        self.block6 = ConvBlock(128, (3,3), (1,1), 'same')\n        \n        self.flatten = tf.keras.layers.Flatten()\n        \n        self.dropout = tf.keras.layers.Dropout(.25)\n        \n        self.dense2 = tf.keras.layers.Dense(512, activation='relu', kernel_initializer='he_normal')\n        \n        self.dense3 = tf.keras.layers.Dense(512, activation='relu', kernel_initializer='he_normal')\n        \n        self.dense1 = tf.keras.layers.Dense(num_classes)\n        \n\n    def call(self, inputs):\n        \n        x = inputs\n        \n        x = self.block1(x)\n        x = self.block2(x)\n        x = self.dropout(x)\n        x = self.block3(x)\n        x = self.block4(x)\n        x = self.dropout(x)\n        x = self.block5(x)\n        x = self.block6(x)\n        x = self.dropout(x)\n        \n        x = self.flatten(x)\n        x = self.dense2(x)\n        x = self.dropout(x)\n        x = self.dense3(x)\n        x = self.dense1(x)\n        \n        with tf.device('/CPU:0'):\n            output = tf.nn.softmax(x)\n\n        return output","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from tensorflow.python.client import device_lib\n\ndef get_available_gpus():\n    local_device_protos = device_lib.list_local_devices()\n    return [x.name for x in local_device_protos if x.device_type == 'GPU']\n\nget_available_gpus()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = '/GPU:0' if len(get_available_gpus()) > 0 else '/CPU:0'\nprint(device)\nbatch_size = 32\nepochs = 100\nnum_classes = 2\nhistory = None\n\nwith tf.device(device):\n    # Khởi tạo model\n    model = CNN(num_classes)\n    \n    # Tạo callback để lưu model có accuracy trên tập validation tốt nhất\n    mcp = tf.keras.callbacks.ModelCheckpoint(\"model_CNN_v1.h5\", monitor=\"val_accuracy\", verbose=2,\n                      save_best_only=True, save_weights_only=True)\n    es = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=10)\n    \n    # Compile model\n    learning_rate = 1e-3\n    decay_rate = learning_rate / epochs\n    optimizer = tf.keras.optimizers.Adam(learning_rate, decay_rate)\n    model.compile(optimizer=optimizer, loss=tf.keras.backend.categorical_crossentropy,\n                   metrics=['accuracy'])\n    \n    steps_per_epoch = total_train // batch_size\n    validation_steps = total_validate // batch_size\n\n    history = model.fit_generator(train_generator, \n                      steps_per_epoch = steps_per_epoch,\n                      epochs=epochs,\n                      validation_data=validation_generator, \n                      validation_steps=validation_steps,\n                      verbose=1,\n                      callbacks=[es, mcp])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 4. Visualize model performance","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 12))\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, 1, 0.1))\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))\n\nlegend = plt.legend(loc='best', shadow=True)\nplt.tight_layout()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## 5. Inference","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"### 5.1. Prepare test data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!rm -rf \"./test\"\n!unzip -q \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_filenames = os.listdir(\"./test\")\ntest_df = pd.DataFrame({\n    'filename': test_filenames\n})\nnb_samples = test_df.shape[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_gen = ImageDataGenerator(rescale=1./255)\ntest_generator = test_gen.flow_from_dataframe(\n    test_df, \n    \"./test/\", \n    x_col='filename',\n    y_col=None,\n    class_mode=None,\n    target_size=IMAGE_SIZE,\n    batch_size=batch_size,\n    shuffle=False\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 5.2. Inferance","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# Load best model\nmodel = CNN(num_classes)\n\n# Thiết lập kích thước input cho model\ndummy_x = tf.zeros((1, 224, 224, 3))\nmodel._set_inputs(dummy_x)\n\n# Load model đã lưu trước đó trong quá trình huấn luyện\nmodel.load_weights('model_CNN_v1.h5')\nprint(\"Model đã được load\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Save model\nmodel.save(\"DogVsCatModelv2\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"predict = model.predict_generator(test_generator, steps=np.ceil(nb_samples/batch_size))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df['category'] = np.argmax(predict, axis=-1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"label_map = dict((v,k) for k,v in train_generator.class_indices.items())\ntest_df['category'] = test_df['category'].replace(label_map)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_df['category'] = test_df['category'].replace({ 'dog': 1, 'cat': 0 })","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Distribution of count classes\nsns.set(style=\"white\")\nsns.countplot(test_df[\"category\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = test_df.copy()\nsubmission_df['id'] = submission_df['filename'].str.split('.').str[0]\nsubmission_df['label'] = submission_df['category']\nsubmission_df.drop(['filename', 'category'], axis=1, inplace=True)\nsubmission_df.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Dọn dẹp các file hình\nimport shutil\nshutil.rmtree(\"./train\")\nshutil.rmtree(\"./test\")","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}