{"nbformat_minor": 2, "cells": [{"metadata": {"_uuid": "c9bec14483a408752f2beb6d6511f58c7647a46a", "_cell_guid": "6309e032-2ff7-48e4-bbe2-eb0f4fc8c9fc"}, "outputs": [], "cell_type": "markdown", "source": "# Cat vs Dogs", "execution_count": null}, {"metadata": {"_uuid": "531a7296c9b546fed851072648474ebb5ca86b7e", "_cell_guid": "d7229e75-5dcb-4c9e-8978-20ae2b883b71"}, "outputs": [], "cell_type": "markdown", "source": "Solving binary classification problem on dataset from [Cat-vs-Dogs](https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition) Kaggle competition using Keras+TF.", "execution_count": null}, {"metadata": {"_uuid": "7ed557ba17e9cd04c08658dab94f26843c5bb8af", "_cell_guid": "cea5d204-a8f4-486d-81e7-fa9dc8cc6f04"}, "outputs": [], "cell_type": "markdown", "source": "### Import section", "execution_count": null}, {"metadata": {"_uuid": "304773d0b2a17cb00634368bf3db764de31b8c22", "trusted": false, "_cell_guid": "50c6096a-f613-4348-94c0-ce0cb4065c23"}, "outputs": [], "cell_type": "code", "source": "import sys\nimport os\nfrom datetime import datetime\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nfrom keras.layers import *\nfrom keras.optimizers import *\nfrom keras.applications import *\nfrom keras.models import Model, model_from_json\nfrom keras.preprocessing.image import ImageDataGenerator, Iterator, load_img, img_to_array\nfrom keras.callbacks import ModelCheckpoint, EarlyStopping\nfrom keras import backend as k\n\nfrom sklearn.model_selection import train_test_split\nfrom IPython.display import clear_output\n%matplotlib inline", "execution_count": 15}, {"metadata": {"_uuid": "662fbbcc85bda7483366aab6c1f976dd93096c48", "_cell_guid": "b698127b-fd39-41a5-aee0-9221fef67164"}, "outputs": [], "cell_type": "markdown", "source": "### Define pathes to data and model", "execution_count": null}, {"metadata": {"_uuid": "be5be8518b3936d6544e496ddafbaf1d7d96af67", "collapsed": true, "_cell_guid": "f2cb47ed-2c34-410b-ad84-23affe7828ca", "trusted": false}, "outputs": [], "cell_type": "code", "source": "train_dir = \"../input/train/\"\ntest_dir = \"../input/test/\"\nmodel_path = os.path.join(\"models\", \"xception\")\ntop_weights_path = os.path.join(os.path.abspath(model_path), 'top_model_weights.h5')\n\ntrain_files = os.listdir(train_dir)\ntrain_paths = list(map(lambda x: os.path.join(train_dir, x), train_files))\ntest_files = os.listdir(test_dir)\ntest_paths = list(map(lambda x: os.path.join(test_dir, x), test_files))", "execution_count": 2}, {"metadata": {"_uuid": "d577bf1f86682479a34f599a43e9fe873c128517", "_cell_guid": "1fcd604c-c1ab-45f1-a3ac-ed91e1537645"}, "outputs": [], "cell_type": "markdown", "source": "### Show image examples", "execution_count": null}, {"metadata": {"_uuid": "d4741de4f2107726d32a6bfe4b56154c6cf5cc66", "trusted": false, "_cell_guid": "e1d9575d-7e91-4331-8a64-bc63f8341272"}, "outputs": [], "cell_type": "code", "source": "cat_example_file = next(filter(lambda x: x.startswith(\"cat\"), train_files))\ndog_example_file = next(filter(lambda x: x.startswith(\"dog\"), train_files))\ncat_example = plt.imread(os.path.join(train_dir, cat_example_file))\ndog_example = plt.imread(os.path.join(train_dir, dog_example_file))\nfig = plt.figure(figsize=(12, 6))\nfig.add_subplot(1, 2, 1)\nplt.title('Cat')\nplt.imshow(cat_example)\nfig.add_subplot(1, 2, 2)\nplt.title('Dog')\nplt.imshow(dog_example)\nplt.show()", "execution_count": 3}, {"metadata": {"_uuid": "17443155851a57c9a1d43bb74073be3d52af76dc", "_cell_guid": "f7b8097f-80d4-4ed7-a1ed-47983be977c5"}, "outputs": [], "cell_type": "markdown", "source": "### Split data on train and validation parts", "execution_count": null}, {"metadata": {"_uuid": "e54db76160af98e2e16d992891613951b7f882e1", "collapsed": true, "_cell_guid": "c952c129-f4ba-4e0d-8673-faba3f15add4", "trusted": false}, "outputs": [], "cell_type": "code", "source": "train_part_files, validation_part_files, train_part_paths, validation_part_paths = train_test_split(\n    train_files, train_paths, train_size=0.8, random_state=123)\ntrain_part_ys = np.array(list(map(lambda x: 0 if x.startswith('cat') else 1, train_part_files)))\nvalidation_part_ys = np.array(list(map(lambda x: 0 if x.startswith('cat') else 1, validation_part_files)))", "execution_count": 4}, {"metadata": {"_uuid": "1fc8292266b47b6710c726a8853c9538a1131e23", "_cell_guid": "eee09844-9a75-453a-8511-cb003f95a03e"}, "outputs": [], "cell_type": "markdown", "source": "### Define custom iterator to generate batches of images using list of paths to these images", "execution_count": null}, {"metadata": {"_uuid": "9ac9144ac719407675cd7b708ee33e6ca4001709", "collapsed": true, "_cell_guid": "55e87053-1c8a-4803-b13d-9aea15ef43cc", "trusted": false}, "outputs": [], "cell_type": "code", "source": "class FileListIterator(Iterator):\n    \"\"\"Iterator capable of reading images located on disk by specified pathes.\n    Arguments:\n            filenames: Paths to the images.\n                    Each subdirectory in this directory will be\n                    considered to contain images from one class,\n                    or alternatively you could specify class subdirectories\n                    via the `classes` argument.\n            y: Numpy array of targets data.\n            image_data_generator: Instance of `ImageDataGenerator`\n                    to use for random transformations and normalization.\n            target_size: tuple of integers, dimensions to resize input images to.\n            color_mode: One of `\"rgb\"`, `\"grayscale\"`. Color mode to read images.            \n            batch_size: Integer, size of a batch.\n            shuffle: Boolean, whether to shuffle the data between epochs.\n            seed: Random seed for data shuffling.\n            data_format: String, one of `channels_first`, `channels_last`.\n            save_to_dir: Optional directory where to save the pictures\n                    being yielded, in a viewable format. This is useful\n                    for visualizing the random transformations being\n                    applied, for debugging purposes.\n            save_prefix: String prefix to use for saving sample\n                    images (if `save_to_dir` is set).\n            save_format: Format to use for saving sample images\n                    (if `save_to_dir` is set).\n    \"\"\"\n\n    def __init__(self,\n                 filenames,\n                 y,\n                 image_data_generator,\n                 target_size=(256, 256),\n                 color_mode='rgb',                 \n                 class_mode='categorical',\n                 batch_size=32,\n                 shuffle=True,\n                 seed=None,\n                 data_format=None,\n                 save_to_dir=None,\n                 save_prefix='',\n                 save_format='jpeg'):\n        if data_format is None:\n            data_format = K.image_data_format()        \n        self.image_data_generator = image_data_generator\n        self.target_size = tuple(target_size)\n        if color_mode not in {'rgb', 'grayscale'}:\n            raise ValueError('Invalid color mode:', color_mode,\n                             '; expected \"rgb\" or \"grayscale\".')\n        self.color_mode = color_mode\n        self.data_format = data_format        \n        if self.color_mode == 'rgb':\n            if self.data_format == 'channels_last':\n                self.image_shape = self.target_size + (3,)                \n            else:\n                self.image_shape = (3,) + self.target_size\n        else:\n            if self.data_format == 'channels_last':\n                self.image_shape = self.target_size + (1,)\n            else:\n                self.image_shape = (1,) + self.target_size\n        self.y = y        \n        self.save_to_dir = save_to_dir\n        self.save_prefix = save_prefix\n        self.save_format = save_format\n\n        white_list_formats = {'png', 'jpg', 'jpeg', 'bmp'}\n        \n        self.filenames = filenames     \n        self.nb_sample = len(filenames)\n        super(FileListIterator, self).__init__(self.nb_sample, batch_size, shuffle,\n                                                seed)\n\n        \n    def next(self):\n        \"\"\"For python 2.x.\n        Returns:\n                The next batch.\n        \"\"\"\n        with self.lock:\n            index_array, current_index, current_batch_size = next(\n                    self.index_generator)\n        # The transformation of images is not under thread lock\n        # so it can be done in parallel\n        batch_x = np.zeros(\n                (current_batch_size,) + self.image_shape, dtype=K.floatx())\n        grayscale = self.color_mode == 'grayscale'\n        # build batch of image data\n        for i, j in enumerate(index_array):\n            fname = self.filenames[j]\n            img = load_img(fname, grayscale=grayscale, target_size=self.target_size)\n            x = img_to_array(img, data_format=self.data_format)\n            x = self.image_data_generator.random_transform(x)\n            x = self.image_data_generator.standardize(x)\n            batch_x[i] = x\n        # optionally save augmented images to disk for debugging purposes\n        if self.save_to_dir:\n            for i in range(current_batch_size):\n                img = array_to_img(batch_x[i], self.data_format, scale=True)\n                fname = '{prefix}_{index}_{hash}.{format}'.format(\n                        prefix=self.save_prefix,\n                        index=current_index + i,\n                        hash=np.random.randint(1e4),\n                        format=self.save_format)\n                img.save(os.path.join(self.save_to_dir, fname))\n        # build batch of labels\n        if self.y is None:\n            return batch_x\n        batch_y = self.y[index_array]        \n        return batch_x, batch_y", "execution_count": 10}, {"metadata": {"_uuid": "16a40824b3172ab740faadf42c0938857cc201af", "_cell_guid": "f532c031-f872-4bd3-9356-af7b2d8472a9"}, "outputs": [], "cell_type": "markdown", "source": "### Define model and hyper parameters", "execution_count": null}, {"metadata": {"_uuid": "6263fdd07e094a273ea57ba4bd797e68fa8b7d45", "trusted": false, "_cell_guid": "a88a2f5c-eb34-4c12-970d-e944f06de8c7"}, "outputs": [], "cell_type": "code", "source": "try:\n    img_width, img_height = 299, 299\n\n    # learning process parameters\n    batch_size = 32\n    train_epochs = 5\n\n    # sgd parameters\n    learn_rate = 1e-4\n    momentum = .9\n\n    # take base model with weights pre-trained using imagenet dataset\n    base_model = Xception(input_shape=(img_width, img_height, 3), weights='imagenet', include_top=False)\n\n    # set model ending to fit current problem (binary classification)\n    base_output = base_model.output\n    avg_pool_base_output = GlobalAveragePooling2D()(base_output)\n    predictions = Dense(1, activation='sigmoid')(avg_pool_base_output)\n\n    # construct keras model object passing input and output layers\n    model = Model(base_model.input, predictions)\n\n    # do not train layers of the based model which are already pre-trained\n    for layer in base_model.layers:\n        layer.trainable = False\nexcept:\n    # kaggle doesn't allow loading weights\n    pass", "execution_count": 6}, {"metadata": {"_uuid": "3d8a7c4e8efee38f3db4b6922c4dae358e170682", "_cell_guid": "a666c4d4-7dbd-416d-a1ce-a818384f5a26"}, "outputs": [], "cell_type": "markdown", "source": "### Save model description to file", "execution_count": null}, {"metadata": {"_uuid": "17d3d481b7d9c92751041581270ebaa83e08aeee", "trusted": false, "_cell_guid": "e32e509f-2689-4e20-be62-d71b5de49ee9"}, "outputs": [], "cell_type": "code", "source": "try:\n    model_json = model.to_json()\n    with open(os.path.join(os.path.abspath(model_path), 'model.json'), 'w') as json_file:\n        json_file.write(model_json)\n    \n    # to avoid shoing error message\n    clear_output()\nexcept:\n    # won't work because of error in cell 6\n    pass", "execution_count": 7}, {"metadata": {"_uuid": "6cd207f5e81c9b61940332513123cf798d5a9a40", "_cell_guid": "b14ad0f1-8c05-4ada-b984-6a293c3fc163"}, "outputs": [], "cell_type": "markdown", "source": "### Fit model", "execution_count": null}, {"metadata": {"_uuid": "63976e6d9691b3a55abd2e18c60df67f45725c0a", "trusted": false, "_cell_guid": "7445f1af-25ca-43ec-ba69-83d46b70ced9"}, "outputs": [], "cell_type": "code", "source": "try:\n    data_generator = ImageDataGenerator(rescale=1. / 255)\n\n    train_generator = FileListIterator(train_part_paths, train_part_ys, data_generator,\n                                       target_size=(img_width, img_height), batch_size=batch_size)\n\n    validation_generator = FileListIterator(validation_part_paths, validation_part_ys, data_generator, \n                                            target_size=(img_width, img_height), batch_size=batch_size)\n\n    model.compile(optimizer='nadam',\n                  loss='binary_crossentropy',\n                  metrics=['accuracy'])\n\n    callbacks_list = [\n        ModelCheckpoint(top_weights_path, monitor='val_acc', verbose=3, save_best_only=True),\n        EarlyStopping(monitor='val_acc', patience=5, verbose=3)\n    ]\n\n    model.fit_generator(train_generator,\n                        samples_per_epoch=train_generator.nb_sample,\n                        nb_epoch=train_epochs,\n                        validation_data=validation_generator,\n                        nb_val_samples=validation_generator.nb_sample,\n                        callbacks=callbacks_list)\n\n    # to avoid shoing error message\n    clear_output()    \nexcept:\n    # won't work because of error in cell 6\n    pass", "execution_count": 11}, {"metadata": {"_uuid": "248345ef3ed4237d64b2a006af5eaefe14773fab", "_cell_guid": "041af9ec-9640-4b1f-8061-cae4dab18ef6"}, "outputs": [], "cell_type": "markdown", "source": "### Load model description and trained weights", "execution_count": null}, {"metadata": {"_uuid": "dd258068daa733f2269ffd5158ba12e2d2ff3c8e", "collapsed": true, "_cell_guid": "9fe41a7a-6122-4220-a5b2-ee8ae67a1e64", "trusted": false}, "outputs": [], "cell_type": "code", "source": "try:\n    with open(os.path.join(os.path.abspath(model_path), 'model.json'), 'r') as json_file:\n        model_json = json_file.read()\n\n    model = model_from_json(model_json)\n    model.load_weights(top_weights_path)\nexcept:\n    # won't work because of error in cell 6\n    pass", "execution_count": 12}, {"metadata": {"_uuid": "49dcdd7d4481773d9f27c2fbe9cd13ae3512d942", "_cell_guid": "2f69e3e5-4989-4460-ab53-a2ceea740a2b"}, "outputs": [], "cell_type": "markdown", "source": "### Make predictions on batches of test images", "execution_count": null}, {"metadata": {"_uuid": "49003f28ba6f58742492295d0acad9dc36bc0d82", "trusted": false, "_cell_guid": "a1929ac2-ff4c-453f-a8c4-25762ccc3fc8"}, "outputs": [], "cell_type": "code", "source": "try:\n    predictions = []\n    batch_size = 128\n    for i in range(0, len(test_paths), batch_size):\n        batch_paths = test_paths[i:i + batch_size]\n        batch_x = np.zeros((len(batch_paths),) + (img_width, img_height) + (3,), dtype=K.floatx())\n        for j, img_path in enumerate(batch_paths):\n            img = load_img(batch_paths[j], grayscale=False, target_size=(img_width, img_height))\n            img_array = img_to_array(img, data_format=None)        \n            batch_x[j] = data_generator.standardize(img_array)\n        ys = model.predict(batch_x)\n        predictions.extend(list(zip(batch_paths, ys[:, 0])))\n        clear_output(wait=True)\n        print(\"{}/{}\".format(len(predictions), len(test_paths)))    \nexcept:\n    # won't work because of error in cell 6\n    pass", "execution_count": 13}, {"metadata": {"_uuid": "024b8b247a6cb946b28a9b58638d25c9cee06f84", "_cell_guid": "d27674f6-363b-4b1d-8e69-faac29847cc7"}, "outputs": [], "cell_type": "markdown", "source": "### Write predictions to file for submission", "execution_count": null}, {"metadata": {"_uuid": "bd296b98b718353eeee8866939416db03dfa348f", "trusted": false, "_cell_guid": "0d750008-69bb-49c8-a4aa-ddb6366d3f68"}, "outputs": [], "cell_type": "code", "source": "out_path = str(datetime.now()).replace(\":\", \"_\").replace(\" \", \"_\").split('.')[0] + \".csv\" \nwith open(out_path, \"w\") as out: \n    out.write('id,label\\n')\n    for fname, val in predictions:        \n        out.write('{},{}\\n'.format(fname.split('/')[1].split('.')[0], val))\nprint(\"done {}\".format(out_path))", "execution_count": 16}, {"metadata": {"_uuid": "b423a54779c9abb188b89370ed74310478d829ce", "_cell_guid": "f226c1c4-7ecc-4b69-b4d3-7b7c64df8b2c"}, "outputs": [], "cell_type": "markdown", "source": "### Clear session and release memory", "execution_count": null}, {"metadata": {"_uuid": "82c822dd00b2677755f5b986c974b556476e7187", "collapsed": true, "_cell_guid": "eb4ee0ee-2b56-4bea-9ed6-84a1877c9fbd", "trusted": false}, "outputs": [], "cell_type": "code", "source": "k.clear_session()", "execution_count": 17}], "metadata": {"language_info": {"mimetype": "text/x-python", "nbconvert_exporter": "python", "codemirror_mode": {"version": 3, "name": "ipython"}, "file_extension": ".py", "name": "python", "pygments_lexer": "ipython3", "version": "3.6.1"}, "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}}, "nbformat": 4}