{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Dogs vs. Cats"},{"metadata":{},"cell_type":"markdown","source":"https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition"},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\n%matplotlib inline\nfrom matplotlib import pyplot as plt\nfrom tensorflow import keras\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.applications.vgg16 import VGG16\nfrom tensorflow.keras.applications.vgg16 import preprocess_input as preprocess_input_VGG16\nfrom tensorflow.keras.applications.resnet50 import ResNet50\nfrom tensorflow.keras.applications.resnet50 import preprocess_input as preprocess_input_ResNet50\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nimport skimage\nfrom random import randrange\nimport cv2\nimport re\nfrom random import shuffle\nfrom glob import glob\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_SIZE = (224, 224)  # размер входного изображения сети","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Функции загрузки данных"},{"metadata":{"trusted":true},"cell_type":"code","source":"class augmentator:\n    def __init__(self):\n        self.modes=[\"gaussian\",\"localvar\",\"poisson\",\"salt\",\"pepper\",\"s&p\",\"speckle\",None]\n\n\n    def rotate(self, image, angle=90, scale=1.0):\n        w = image.shape[1]\n        h = image.shape[0]\n        #rotate matrix\n        M = cv2.getRotationMatrix2D((w/2,h/2), angle, scale)\n        #rotate\n        image = cv2.warpAffine(image,M,(w,h))\n        return image\n\n    def flip(self, image, vflip=False, hflip=False):\n        if hflip or vflip:\n            if hflip and vflip:\n                c = -1\n            else:\n                c = 0 if vflip else 1\n            image = cv2.flip(image, flipCode=c)\n        return image \n    \n    def add_noise(self, image, mode=0):\n        if(self.modes[mode] == None):\n            return image\n        else:\n            return skimage.util.random_noise(image, mode=self.modes[mode])\n    \n    def augment(self, image, typ): \n        img = image.copy()\n        if(typ == 1):\n            return self.flip(img, vflip=(randrange(2) == 1), hflip=(randrange(2) == 1))\n        elif(typ == 2):\n            return self.rotate(img, randrange(36)*10)\n        elif(typ == 3):\n            return self.add_noise(img, randrange(len(self.modes)))\n        else:\n            return img\naug = augmentator()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_files = glob('../input/train/*.jpg')\ntest_files = glob('../input/test/*.jpg')\n\n# загружаем входное изображение и предобрабатываем\ndef load_image(path, target_size=IMG_SIZE):\n    img = load_img(path, target_size=target_size)  # загрузка и масштабирование изображения\n    img = img_to_array(img)\n    #img = cv2.imread(path)\n    #img = cv2.resize(img, target_size)\n    img = aug.augment(img, randrange(4)) # аугментация\n    #return preprocess_input_VGG16(img)  # предобработка для VGG16\n    return preprocess_input_ResNet50(img)  # предобработка для ResNet50\n\n# генератор для последовательного чтения обучающих данных с диска\ndef fit_generator(files, batch_size=32):\n    while True:\n        shuffle(files)\n        for k in range(len(files) // batch_size):\n            i = k * batch_size\n            j = i + batch_size\n            if j > len(files):\n                j = - j % len(files)\n            x = np.array([load_image(path) for path in files[i:j]])\n            y = np.array([1. if re.match('.*/dog\\.\\d', path) else 0. for path in files[i:j]])\n            yield (x, y)\n\n# генератор последовательного чтения тестовых данных с диска\ndef predict_generator(files):\n    while True:\n        for path in files:\n            yield np.array([load_image(path)])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Визуализируем примеры для обучения"},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(20, 20))\nfor i, path in enumerate(train_files[:10], 1):\n    subplot = fig.add_subplot(i // 5 + 1, 5, i)\n    plt.imshow(plt.imread(path));\n    subplot.set_title('%s' % path.split('/')[-1]);","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(20, 20))\nfor i, path in enumerate(train_files[:10], 1):\n    subplot = fig.add_subplot(i // 5 + 1, 5, i)\n    img = cv2.imread(path)\n    img = cv2.resize(img, IMG_SIZE)\n    img = aug.augment(img, randrange(4))\n    plt.imshow(img);\n    subplot.set_title('%s' % path.split('/')[-1]);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Загружаем предобученную модель"},{"metadata":{"trusted":true},"cell_type":"code","source":"# base_model -  объект класса keras.models.Model (Functional Model)\n#base_model = VGG16(include_top = False,\n#                   weights = 'imagenet',\n#                   input_shape = (IMG_SIZE[0], IMG_SIZE[1], 3))\n\nbase_model = ResNet50(include_top = False,\n                   weights = 'imagenet',\n                   input_shape = (IMG_SIZE[0], IMG_SIZE[1], 3))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# фиксируем все веса предобученной сети\nfor layer in base_model.layers:\n    layer.trainable = False","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"base_model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Добавляем полносвязный слой"},{"metadata":{"trusted":true},"cell_type":"code","source":"x = base_model.layers[-5].output\nx = keras.layers.Flatten()(x)\nx = keras.layers.Dense(1,  # один выход\n                activation='sigmoid',  # функция активации  \n                kernel_regularizer=keras.regularizers.l1(1e-4))(x)\nmodel = Model(inputs=base_model.input, outputs=x)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Выводим архитектуру модели"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Компилируем модель и запускаем обучение"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer='adam', \n              loss='binary_crossentropy',  # функция потерь binary_crossentropy (log loss\n              metrics=['accuracy'])","execution_count":null,"outputs":[]},{"metadata":{"scrolled":false,"trusted":true},"cell_type":"code","source":"shuffle(train_files)  # перемешиваем обучающую выборку\n\ntrain_val_split = 100  # число изображений в валидационной выборке\n\nvalidation_data = next(fit_generator(train_files[:train_val_split], train_val_split))\n\n# запускаем процесс обучения\nmodel.fit_generator(fit_generator(train_files[train_val_split:]),  # данные читаем функцией-генератором\n        steps_per_epoch=10,  # число вызовов генератора за эпоху\n        epochs=100,  # число эпох обучения\n        validation_data=validation_data)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#model.save('cats-dogs-vgg16.hdf5')\nmodel.save('cats-dogs-resnet05.hdf5')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Предсказания на проверочной выборке"},{"metadata":{"trusted":true},"cell_type":"code","source":"pred = model.predict_generator(predict_generator(test_files), len(test_files), max_queue_size=500)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = plt.figure(figsize=(20, 20))\nfor i, (path, score) in enumerate(zip(test_files[80:][:10], pred[80:][:10]), 1):\n    subplot = fig.add_subplot(i // 5 + 1, 5, i)\n    plt.imshow(plt.imread(path));\n    subplot.set_title('%.3f' % score);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Готовим данные для сабмита"},{"metadata":{"trusted":true},"cell_type":"code","source":"with open('submit-0717.csv', 'w') as dst:\n    dst.write('id,label\\n')\n    for path, score in zip(test_files, pred):\n        dst.write('%s,%f\\n' % (re.search('(\\d+)', path).group(0), score))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# LogLoss = 0.013102","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.7.3"},"widgets":{"state":{},"version":"1.1.2"}},"nbformat":4,"nbformat_minor":1}