{"cells": [{"cell_type": "markdown", "source": ["This is an example of an experiment in deepsense.ai with keras. It does not run in kernel. See [discussion](https://www.kaggle.com/c/cdiscount-image-classification-challenge/discussion/41506) \n", "\n", "Imports:"], "metadata": {"_uuid": "36227e8fd0ea69c88ee2a33f393aeee0ea0b60a9", "_cell_guid": "ddf96108-2b56-425f-846c-bb4dc9a136a1"}}, {"source": ["from keras.preprocessing.image import Iterator\n", "from keras.preprocessing.image import ImageDataGenerator\n", "from keras import backend as K\n", "from keras.applications.inception_v3 import InceptionV3\n", "from keras.applications.inception_v3 import preprocess_input\n", "from keras.models import Model\n", "from keras.layers import Dense, GlobalAveragePooling2D, Input\n", "import numpy as np # linear algebra\n", "import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n", " #import bson                       # this is installed with the pymongo package\n", "from PIL import Image\n", "import time\n", "import gc\n", "from keras.optimizers import SGD\n", "from keras.optimizers import RMSprop\n", "import keras\n", "import tensorflow as tf\n", "import io\n", "from sklearn import preprocessing\n", "import struct\n", "import threading\n", "from keras.preprocessing import image\n", "from keras.preprocessing.image import load_img, img_to_array\n", "from deepsense import neptune\n", "from keras.callbacks import Callback, TensorBoard\n", "from subprocess import check_output\n", "print(check_output([\"ls\", \"/public/Cdiscount\"]).decode(\"utf8\"))\n", "\n", "print(check_output([\"ls\", \"/input\"]).decode(\"utf8\"))\n", "\n", "print(keras.__version__, tf.__version__)\n", "\n"], "cell_type": "code", "metadata": {"_uuid": "e57fd47ff99fd1363494f3550af81af79e7387c7", "_cell_guid": "91c250f2-3ef9-4660-bfc0-2704b9ed4135", "collapsed": true}, "execution_count": null, "outputs": []}, {"cell_type": "markdown", "source": ["deepsense callback for Keras:"], "metadata": {"_uuid": "f3d7bd0cf23e520079d91a014f695a86229a9158", "_cell_guid": "45612009-1707-424f-974e-d8086fc694b3"}}, {"source": ["class NeptuneCallback(Callback):\n", "    def __init__(self, images_per_epoch=-1, phase=1):\n", "        self.epoch_id = 0\n", "        self.batch_id = 0\n", "        self.phase = phase\n", "        self.images_per_epoch = images_per_epoch\n", "\n", "    def on_epoch_end(self, epoch, logs={}):\n", "        self.epoch_id += 1\n", "\n", "        # logging numeric channels\n", "        #ctx.job.channel_send('Log-loss train ph'+str(self.phase), self.epoch_id, logs['loss'])\n", "        ctx.job.channel_send('Log-loss val ph'+str(self.phase), self.epoch_id, logs['val_loss'])\n", "        #ctx.job.channel_send('Accuracy training'+str(self.phase), self.epoch_id, logs['acc'])\n", "        ctx.job.channel_send('Accuracy val ph'+str(self.phase), self.epoch_id, logs['val_acc'])\n", "\n", "        self.batch_id += 1\n", "        ctx.job.channel_send('Log-loss mon ph'+str(self.phase), self.batch_id, 0)\n", "        ctx.job.channel_send('Accuracy mon ph'+str(self.phase), self.batch_id, 0)\n", "        self.batch_id += 1\n", "        ctx.job.channel_send('Log-loss mon ph'+str(self.phase), self.batch_id, 0)\n", "        ctx.job.channel_send('Accuracy mon ph'+str(self.phase), self.batch_id, 0)\n", "\n", "    def on_batch_end(self, epoch, logs={}):\n", "        self.batch_id += 1\n", "\n", "        # logging numeric channels\n", "        ctx.job.channel_send('Log-loss mon ph'+str(self.phase), self.batch_id, logs['loss'])\n", "        ctx.job.channel_send('Accuracy mon ph'+str(self.phase), self.batch_id, logs['acc'])\n"], "cell_type": "code", "metadata": {"_uuid": "d4b8a5c43609e951022f0722bc738b52a0e95b7d", "_cell_guid": "a6f5c460-5a9c-4071-8b39-44297003349c", "collapsed": true}, "execution_count": null, "outputs": []}, {"cell_type": "markdown", "source": ["Model definition:"], "metadata": {"_uuid": "ed670d2a2edf4f4548da6e006adda8f93190e378", "_cell_guid": "034b8902-e918-4af7-8326-a0f8587cce91"}}, {"source": ["ctx = neptune.Context()\n", "\n", "\n", "##################################\n", "BATCH_SIZE = 128\n", "SHAPE = 180 #80\n", "CLASSES  = 5270 #5270\n", "###################################\n", "\n", "################################################\n", "print(\"create the base pre-trained model\")\n", "################################################\n", "input = Input(shape=(SHAPE, SHAPE, 3), name='NEW_image_input_180x180X3') #New input layer, good to the competition shape\n", "base_model = InceptionV3(input_tensor=input, weights='imagenet', include_top=False)\n", "\n", "\n", "x = base_model.output\n", "#Some aditional layers\n", "x = GlobalAveragePooling2D(name = 'NEW_GlobalAveragePooling2D')(x)\n", "# let's add a fully-connected layer\n", "#x = Dense(1024, activation='relu', name='NEW_Dense_1024')(x)\n", "#x = Dense(2048, activation='relu', name='NEW_Dense_2048')(x)\n", "# and a logistic layer -- let's say we have 200 classes\n", "\n", "#modelo novo\n", "predictions = Dense(CLASSES, activation='softmax', name='NEW_Predictions_5270')(x)\n", "model = Model(inputs=base_model.input, outputs=predictions)\n"], "cell_type": "code", "metadata": {"_uuid": "f07b4fefd28a37549e70d6881af72be19d77fee8", "_cell_guid": "b459a752-f111-409a-a1c2-a41e794b3757", "collapsed": true}, "execution_count": null, "outputs": []}, {"cell_type": "markdown", "source": ["The generator:"], "metadata": {"_uuid": "f8a8cbde1b055b0d511fd7630255e6ec8687d0d5", "_cell_guid": "757eccc4-b85c-4514-9456-d07114001105"}}, {"source": ["################################################\n", "print(\"\\nEncode categories...\")\n", "################################################\n", "\n", "#Uses LabelEncoder for class_id encoding\n", "categories_path = \"/input/category_names.csv\"\n", "le = preprocessing.LabelEncoder()\n", "le.fit(pd.read_csv(categories_path).category_id)\n", "\n", "\n", "################################################\n", "print(\"\\nDefine Generator...\")\n", "################################################\n", "from keras.applications.inception_v3 import preprocess_input\n", "\n", "#The generator. The flow method does the generator job!\n", "class BinFileIterator(Iterator):\n", "    def __init__(self, bin_file_name, img_generator, samples,\n", "                 target_size=(180,180),\n", "                 batch_size=32, num_class=5270):\n", "        self.file = open(bin_file_name,'rb')\n", "        self.img_gen=img_generator\n", "        self.target_size = tuple(target_size)\n", "        self.image_shape = self.target_size + (3,)\n", "        self.num_class = num_class\n", "        self.lock = threading.Lock() #Since we have 2 files, each generator has its own lock\n", "        super(BinFileIterator, self).__init__(samples, batch_size,\n", "                                              shuffle=False,\n", "                                              seed=None)\n", "\n", "    def flow(self, index_array):\n", "        X = np.zeros((len(index_array),) + self.image_shape, dtype=K.floatx())\n", "        Y = np.zeros((len(index_array), self.num_class), dtype=K.floatx())\n", "\n", "        for i, j in enumerate(index_array):\n", "            with self.lock:\n", "                buffer=self.file.read(8)\n", "                if len(buffer) < 8:\n", "                    self.file.seek(0)\n", "                    buffer=self.file.read(8)\n", "                encoded_class, length = struct.unpack(\"<ii\", buffer)\n", "                bson_img = self.file.read(length)\n", "            img = load_img(io.BytesIO(bson_img), target_size=self.target_size)\n", "            x = image.img_to_array(img)\n", "            #x = self.img_gen.random_transform(x)\n", "            #x = self.img_gen.standardize(x)\n", "            X[i] = x\n", "            Y[i, encoded_class] = 1\n", "\n", "        X = preprocess_input(np.array(X))\n", "        return X, Y\n", "\n", "    def next(self):\n", "        with self.lock:\n", "            index_array = next(self.index_generator)\n", "        return self.flow(index_array[0])\n", "\n", "n_train_images= 10428\n", "n_val_images= 2362\n", "\n", "train_img_gen = ImageDataGenerator()\n", "train_gen = BinFileIterator('/input/train_sample.bin', img_generator=train_img_gen,  samples=n_train_images,\n", "                 target_size=(180,180),\n", "                 batch_size=BATCH_SIZE)\n", "\n", "val_img_gen = ImageDataGenerator()\n", "val_gen = BinFileIterator('/input/val_sample.bin', img_generator=val_img_gen,  samples=n_val_images,\n", "                 target_size=(180,180),\n", "                 batch_size=BATCH_SIZE)\n"], "cell_type": "code", "metadata": {"_uuid": "3f55ce68bc82db80e351ac48c702c657d21b9050", "_cell_guid": "3226b318-48f8-40fb-b858-e19e4058ce12", "collapsed": true}, "execution_count": null, "outputs": []}, {"cell_type": "markdown", "source": ["Training the top classifier:"], "metadata": {"_uuid": "6a5723dcbcec97f9bb37fba0509d255094a805e0", "_cell_guid": "05afa780-9dcc-42ed-9a67-197f2797f229"}}, {"source": ["################################################\n", "print(\"fit the new classifier\")\n", "################################################\n", "\n", "# first: train only the top layers (which were randomly initialized)\n", "# i.e. freeze all convolutional InceptionV3 layers\n", "\n", "for layer in base_model.layers[1:]:\n", "    layer.trainable = False\n", "\n", "from keras.optimizers import RMSprop\n", "model.compile(optimizer=RMSprop(lr=0.008, decay=0.01), loss='categorical_crossentropy', metrics=['accuracy'])\n", "\n", "model.fit_generator(train_gen,\n", "                    steps_per_epoch=n_train_images//BATCH_SIZE,\n", "                    validation_data=val_gen,\n", "                    validation_steps=n_val_images//BATCH_SIZE,\n", "                    #shuffle=True,\n", "                    epochs=5, callbacks=[NeptuneCallback(images_per_epoch=n_train_images//BATCH_SIZE, phase=1)])\n"], "cell_type": "code", "metadata": {"_uuid": "96d752fd6ddb1378c92ce5a78cca52f34bd7e030", "_cell_guid": "1dc53594-9390-4c68-a960-f07018f6513a", "collapsed": true}, "execution_count": null, "outputs": []}, {"source": ["Fine Tune some inception modules:"], "cell_type": "code", "metadata": {"_uuid": "51243f45a95b3ff8a565bf587faa283b9e731835", "_cell_guid": "db716e5d-1bf0-471c-afa7-c0eccf4f1816", "collapsed": true}, "execution_count": null, "outputs": []}, {"source": ["################################################\n", "print(\"fine tune the model\")\n", "################################################\n", "\n", "\n", "for layer in model.layers[:249]:\n", "   layer.trainable = False\n", "for layer in model.layers[249:]:\n", "   layer.trainable = True\n", "\n", "model.compile(optimizer=SGD(lr=0.008, momentum=0.1, decay=0.01), loss='categorical_crossentropy', metrics=['accuracy'])\n", "\n", "model.fit_generator(train_gen,\n", "                    steps_per_epoch=n_train_images//BATCH_SIZE,\n", "                    validation_data=val_gen,\n", "                    validation_steps=n_val_images//BATCH_SIZE,\n", "                    #shuffle=True,\n", "                    epochs=5, callbacks=[NeptuneCallback(images_per_epoch=n_train_images//BATCH_SIZE, phase=2)])\n"], "cell_type": "code", "metadata": {"_uuid": "7277b3137b912886bc47c6dbc218a0b80d087f5e", "_cell_guid": "709092d3-047a-4cc3-a8e5-de0609463579", "collapsed": true}, "execution_count": null, "outputs": []}, {"cell_type": "markdown", "source": ["[See discussion..](https://www.kaggle.com/c/cdiscount-image-classification-challenge/discussion/41506)"], "metadata": {"_uuid": "458b780eb30fa289f05845c57492e34d2fe21aee", "_cell_guid": "c1d13cba-30cf-4b93-a9ad-bafd3fb60d19"}}], "nbformat": 4, "nbformat_minor": 1, "metadata": {"kernelspec": {"language": "python", "name": "python3", "display_name": "Python 3"}, "language_info": {"mimetype": "text/x-python", "name": "python", "codemirror_mode": {"name": "ipython", "version": 3}, "pygments_lexer": "ipython3", "file_extension": ".py", "version": "3.6.3", "nbconvert_exporter": "python"}}}