{"cells":[{"metadata":{"_uuid":"fcc7b29ea2d523bd3305f5a22615b9f04f96045d"},"cell_type":"markdown","source":"https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition"},{"metadata":{"_uuid":"e283fd48093a5a65fb5ad921eae54dc299880445"},"cell_type":"markdown","source":"# Cargando las imagenes"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"collapsed":true},"cell_type":"code","source":"import math, keras, bcolz\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nfrom tqdm import tqdm_notebook\nfrom pathlib import Path\n%matplotlib inline","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"8ffde293468974ea2859626d21b4a6257331a373"},"cell_type":"code","source":"np.random.seed(34)\ntrain_path = Path('../input/train/')\ncat_imgs, dog_imgs = [], []\nfor e in train_path.iterdir():\n    if 'cat' in e.name: cat_imgs.append(e)\n    else              : dog_imgs.append(e)\n        \n# Hacemos una permutacion de los archivos para que esten en desorden\ncat_imgs, dog_imgs = np.random.permutation(cat_imgs).tolist(), np.random.permutation(dog_imgs).tolist()\nn_cat, n_dog = len(cat_imgs), len(dog_imgs)\nn_cat, n_dog","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2c7633f79c6567844bd8ba8c6ef0955498a5d35b","collapsed":true},"cell_type":"code","source":"# Vamos a usar 5000 imagenes para el validation set\nn_val = 5000\nn_train = n_cat + n_dog - n_val\ntrain_files = cat_imgs[:-(n_val//2)] + dog_imgs[:-(n_val//2)]\nval_files = cat_imgs[-(n_val//2):] + dog_imgs[-(n_val//2):]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5e8090c01112e5c69e4d083d20f001cfbe2b7ae1","collapsed":true},"cell_type":"code","source":"# Definimos una funcion para leer una imagen y hacer el preprocesamiento\nfrom keras.applications.resnet50 import preprocess_input\nimg_size = 224\n\ndef read_img(path):\n    x = Image.open(path)\n    x = x.resize((img_size, img_size))\n    x = np.asarray(x, np.float32)\n    return x","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"305450fc23ec4bbe07fbed37002728c16bc08d3b"},"cell_type":"markdown","source":"# Cargamos una red entrenada\nVamos a cargar la red **ResNet50** ya entrenada, pero sin incluir las capas densas, ya que vamos a adaptar la red a nuestro caso específico."},{"metadata":{"trusted":true,"_uuid":"9f11e744774ce6a688ceea092497cb87e85ea2f1"},"cell_type":"code","source":"from keras.applications.resnet50 import ResNet50\nfrom keras.models import Model\n\nbase_model = ResNet50(include_top=False, input_shape=(img_size,img_size,3))\nbase_model = Model(base_model.input, base_model.layers[-2].output)\nbase_model.trainable = False\nbase_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"72bad6edee28736e064c9620519ba38249b44415"},"cell_type":"code","source":"base_model.input, base_model.output","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"72c61fc149bd8162c9844497a7a5adefd5e74a15"},"cell_type":"markdown","source":"# Creamos el modelo clasificador"},{"metadata":{"trusted":true,"_uuid":"1b75e918a04d9628c2b1187f2bdfb85e0cb99ce8"},"cell_type":"code","source":"from keras.models import Sequential\nfrom keras.layers import GlobalAvgPool2D, Input, Conv2D, BatchNormalization, Activation\nfrom keras.optimizers import Adam\n\nm_in = Input((7, 7, 2048))\nx = Conv2D(1024, 1, padding='same', activation='relu', kernel_initializer='he_uniform', use_bias=False)(m_in)\nx = BatchNormalization()(x)\nx = Conv2D(2, 1, padding='same')(x)\nx = GlobalAvgPool2D()(x)\nx = Activation('softmax')(x)\ntop_model = Model(m_in, x)\n\ntop_model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(0.001), metrics=['accuracy'])\ntop_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"1292ede52c3105b784275ac1382b8cce91bc0a10"},"cell_type":"markdown","source":"# Juntamos los 2 modelos"},{"metadata":{"trusted":true,"_uuid":"218a3187ab575debf6c9fe3abced6d97d04fba86"},"cell_type":"code","source":"final_model = Sequential([base_model, top_model])\nfinal_model.compile(loss='sparse_categorical_crossentropy', optimizer=Adam(0.001), metrics=['accuracy'])\nfinal_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"28485bb2719d2c3b5a3bdc1e1509fe80c435026b","collapsed":true},"cell_type":"markdown","source":"# Precompute"},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"07f5a42bab4ad3df460366ae9aaaa1eddf1b0ce1"},"cell_type":"code","source":"class DataSequence(keras.utils.Sequence):\n    def __init__(self, files, batch_size):\n        self.files = files\n        self.batch_size = batch_size\n\n    def __len__(self):\n        return int(np.ceil(len(self.files) / float(self.batch_size)))\n\n    def __getitem__(self, idx):\n        batch = self.files[idx * self.batch_size:(idx + 1) * self.batch_size]\n        batch_x = np.ndarray((self.batch_size, img_size, img_size, 3), np.float32)\n        for i,f in enumerate(batch): batch_x[i] = read_img(f)\n        return preprocess_input(batch_x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"c3f71f11715dfc15c84e64bdf4a39cba71e2df6c","collapsed":true},"cell_type":"code","source":"train_seq = DataSequence(train_files, 100)\nval_seq = DataSequence(val_files, 100)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"84d7f9aa93b315cb866f693c19512505f39909c5"},"cell_type":"code","source":"precomputed_train = bcolz.carray(np.zeros((n_train, 7, 7, 2048), np.float32), chunklen=1, mode='w', rootdir='tmp_train')\nprecomputed_val = bcolz.carray(np.zeros((n_val, 7, 7, 2048), np.float32), chunklen=1, mode='w', rootdir='tmp_val')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"68285be9deb6653c060d6d6e893482c12d40b476"},"cell_type":"code","source":"y_train = np.zeros((n_train), np.int8)\ny_val = np.zeros((n_val), np.int8)\ny_train[n_train//2:] = 1\ny_val[n_val//2:] = 1","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a05efa21b97956803cf658d64ad9dc5c150e495d"},"cell_type":"code","source":"for i,batch in tqdm_notebook(enumerate(train_seq), total=len(train_seq)):\n    precomputed_train[i*100:(i+1)*100] = base_model.predict_on_batch(batch)\n    if i == len(train_seq): break","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"ccfa8e783082490cf0407ad5d319835822de0cf4"},"cell_type":"code","source":"for i,batch in tqdm_notebook(enumerate(val_seq), total=len(val_seq)):\n    precomputed_val[i*100:(i+1)*100] = base_model.predict_on_batch(batch)\n    if i == len(val_seq): break    ","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"25ef73ba4bacf61c23c97b703e4cd3eca31a0f01"},"cell_type":"markdown","source":"# Entrenar a partir de los features extraidos"},{"metadata":{"trusted":true,"_uuid":"b7692ae75c991bdcd548ae6f00fcffa5eb12dfc8"},"cell_type":"code","source":"# Ahora podemos usar un batch_size mas grande, ya que los features son mas pequeños\n# que las imagenes.\nlog = top_model.fit(precomputed_train, y_train, epochs=10, batch_size=256, validation_data=[precomputed_val, y_val])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"7c2e69bbba9e401ae79f0860d48cb9bb813766ad"},"cell_type":"code","source":"def show_results(log):\n    fig, axes = plt.subplots(1, 2, figsize=(14,4))\n    ax1, ax2 = axes\n    ax1.plot(log.history['loss'], label='train')\n    ax1.plot(log.history['val_loss'], label='validation')\n    ax1.set_xlabel('epoch'); ax1.set_ylabel('loss')\n    ax2.plot(log.history['acc'], label='train')\n    ax2.plot(log.history['val_acc'], label='validation')\n    ax2.set_xlabel('epoch'); ax2.set_ylabel('accuracy')\n    for ax in axes: ax.legend()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"fe236ded01cf6d633b17aeac019fc5c95d3e73f9"},"cell_type":"code","source":"show_results(log)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ca6322dbcd87586225384f3c5f47bc40b41443bf"},"cell_type":"markdown","source":"# Usando el modelo completo en el test set"},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"156f753e5482474d68bd6146dd0a1c95001ad9eb"},"cell_type":"code","source":"test_path = Path('../input/test/')\ntest_files = list(test_path.iterdir())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"ea5150825528e86bc702e49a99eb2c12795b1168"},"cell_type":"code","source":"def get_class(path):\n    # Cargar la imagen del path\n    img = Image.open(path)\n    \n    # Cambiar el tamaño de la imagen\n    img_resized = img.resize((224, 224))\n    \n    # Cambiar a formato numpy y preprocesar\n    x = np.asarray(img_resized, np.float32)[None]\n    x = preprocess_input(x)\n    \n    # Obtener predicciones\n    y = final_model.predict(x)\n    \n    # Decodear predicciones\n    pred = 'cat' if np.argmax(y) == 0 else 'dog'\n    \n    # Mostrar la imagen\n    plt.imshow(img)\n    plt.axis('off')\n    plt.title(f'pred = {pred}', size=14)\n    plt.show()\n    \n    return","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7aff89101033f3bb3a797f170668f7efe327d8ec"},"cell_type":"code","source":"for _ in range(3):\n    sample = np.random.choice(test_files)\n    get_class(sample)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"c576338f3b3c926f9cb849e8e44b255c66be5a75"},"cell_type":"markdown","source":"# Veamos los puntos de falla"},{"metadata":{"trusted":true,"_uuid":"61dc4fa0d4dcb56376d4aadf1f49faf8dc61d059"},"cell_type":"code","source":"preds_val = top_model.predict(precomputed_val, batch_size=256, verbose=1)\npreds_val.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3f65673aa8e3abbedbbaeded70827a5ff1c444ea","collapsed":true},"cell_type":"code","source":"pred_classes = np.argmax(preds_val, axis=1)\nidxs = np.where(y_val != pred_classes)[0]\nerrors = np.min(preds_val[idxs], axis=1)\nidxs = idxs[np.argsort(errors)]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"369504e53aa32f35f6f4ab48a1e4fe59c2c679a7"},"cell_type":"code","source":"fig, axes = plt.subplots(3, 4, figsize=(18,12))\nfor ax, idx in zip(axes.flatten(), idxs):\n    ax.imshow(Image.open(val_files[idx]))\n    ax.set_title(f'''real label = {'dog' if idx > n_val/2 else 'cat'}\npred label = {'dog' if pred_classes[idx] == 1 else 'cat'}\nerror = {1 - np.min(preds_val[idx]):.4f}''')\n    ax.axis('off')\nplt.tight_layout()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"c035f5a956c2938862fb3e9f5fcf2070fd955707"},"cell_type":"markdown","source":"# Weak Localization"},{"metadata":{"trusted":true,"_uuid":"ad2b84af329e3a26c64ace1f853c24b917d8a1b3","collapsed":true},"cell_type":"code","source":"# Vamos a crear un modelo para capturar una capa intermedia, ademas del output\nmap_model = Model(top_model.layers[0].input, [top_model.layers[-3].output, top_model.layers[-1].output])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"bacee8291f09abf1b3891a120791476eb53192a4"},"cell_type":"code","source":"import cv2\n\ndef get_feature_map(path):\n    img = Image.open(path)\n    img_resized = img.resize((224, 224))\n    x = np.asarray(img_resized, np.float32)[None]\n    x = preprocess_input(x)\n    precomputed = base_model.predict(x)\n    fmap, y = map_model.predict(precomputed)\n    pred = np.argmax(y)\n    fmap = cv2.resize(fmap[0,:,:,pred], img.size)\n    fig, axes = plt.subplots(1, 2, figsize=(18,10))\n    for ax in axes:\n        ax.imshow(img)\n        ax.axis('off')\n        title = f\"pred = {'cat' if pred == 0 else 'dog'}\"\n        if sample.name[:3] == 'cat' or sample.name[:3] == 'dog':\n            title += f'\\nreal = {sample.name[:3]}'\n        ax.set_title(title, size=14)\n\n    axes[0].imshow(fmap, cmap=plt.cm.RdGy_r, alpha=0.75)\n    plt.show()\n    \n    return","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"afeb012d512feda26446aa50e64fe06c2f585eb4"},"cell_type":"markdown","source":"## Veamos imagenes del validation set"},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"0b2689f03b7ddc360521ee651785774f6fc05430"},"cell_type":"code","source":"for _ in range(5):\n    sample = np.random.choice(val_files)\n    get_feature_map(sample)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"21a3d35ed40dc4aee5be8b660609f7d57231b2fb"},"cell_type":"markdown","source":"## Veamos las imagenes que habiamos identificado con errores"},{"metadata":{"trusted":true,"scrolled":false,"_uuid":"991a57de227241aec9fd71917ec28d7d7f1f52ae"},"cell_type":"code","source":"for i in np.random.permutation(idxs)[:5]:\n    sample = val_files[i]\n    get_feature_map(sample)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"ec14ae906d546fd7563d1c975fde677f10a3af8c"},"cell_type":"markdown","source":"# Kaggle submission"},{"metadata":{"trusted":true,"_uuid":"6c7de4f91c769307e7689e17c38b2ba4984086b4"},"cell_type":"code","source":"# Obtener resultados del test set\nimport pandas as pd\n\ntest_path = Path('../input/test/')\ntest_files = list(test_path.iterdir())\n\nclass TestDataSequence(DataSequence):\n    def __getitem__(self, idx):\n        batch = self.files[idx * self.batch_size:(idx + 1) * self.batch_size]\n        batch_x = np.ndarray((self.batch_size, img_size, img_size, 3), np.float32)\n        batch_ids = np.zeros((self.batch_size), np.int16)\n        for i,f in enumerate(batch):\n            batch_x[i] = read_img(f)\n            batch_ids[i] = int(f.stem)\n        return preprocess_input(batch_x), batch_ids\n\ntest_seq = TestDataSequence(test_files, 250)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"cc2999dbfb390fcddcbce1a175ed7b2239448f12"},"cell_type":"code","source":"preds, ids = [], []\n\nfor i,batch in tqdm_notebook(enumerate(test_seq), total=len(test_seq)):\n    y_ = final_model.predict_on_batch(batch[0])\n    preds += np.argmax(y_, axis=1).tolist()\n    ids += batch[1].tolist()\n    if i == len(test_seq): break    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d79e857036b7149bfaed4f056519a279250533f0"},"cell_type":"code","source":"results = pd.DataFrame({'id': ids, 'label': preds}).sort_values('id').drop_duplicates()\nresults.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"630a647d10d904efd90a8f92dd1f16b6e817bd53"},"cell_type":"code","source":"results.to_csv('submission.csv', index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"09f65ab300ee39a61393963bb62fa84bc197fd66"},"cell_type":"code","source":"from IPython.display import FileLink\nFileLink('submission.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true,"_uuid":"f7874b92db4909d3aa3485cafc89bd131bea53c9"},"cell_type":"code","source":"# Eliminamos los archivos temporales del kernel\n!rm -r tmp_train\n!rm -r tmp_val","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}