{"cells":[{"metadata":{},"cell_type":"markdown","source":"Inspiré de ce [kernel](https://www.kaggle.com/atamazian/fc-ensemble-external-data-effnet-densenet/) et de [celui-ci](https://www.kaggle.com/chankhavu/a-beginner-s-tpu-kernel-single-model-0-97)\n\nImportations des librairies"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"!pip install --quiet tensorflow-addons --upgrade\n!pip install -q efficientnet\n\nimport tensorflow as tf\nfrom kaggle_datasets import KaggleDatasets\nimport numpy as np\nimport re\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense,\\\n                                    BatchNormalization, Dropout, GlobalAveragePooling2D\n\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.initializers import he_uniform\nimport matplotlib.pyplot as plt\nimport tensorflow_addons as tfa\nimport efficientnet.tfkeras as efficientnet\nfrom sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pour se connecter au TPU de Kaggle"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"try:\n    # TPU detection. No parameters necessary if TPU_NAME environment variable is set. On Kaggle this is always the case.\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()  \n    print('Running on TPU ', tpu.master())\nexcept ValueError:\n    tpu = None\n\nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    strategy = tf.distribute.get_strategy() # default distribution strategy in Tensorflow. Works on CPU and single GPU.\n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Récupération des jeux de données. Ainsi que choix de la taille des images.\n\nDes jeux de données provenant d'autres compétitions sont utilisés pour améliorer nos modèles. Etant donné qu'ils sont structurés exactement de la même manière que le data d'origine, il n'est pas nécessaire d'effectuer des traitements supplémentaires."},{"metadata":{"trusted":true},"cell_type":"code","source":"IMAGE_SIZE = [224, 224]\nimg_size_path = '/tfrecords-jpeg-' + str(IMAGE_SIZE[0]) + 'x' + str(IMAGE_SIZE[0])\nor_path = KaggleDatasets().get_gcs_path('tpu-getting-started')\nor_path += img_size_path\nTRAINING_FILENAMES = tf.io.gfile.glob(or_path + '/train/*.tfrec')\nVALIDATION_FILENAMES = tf.io.gfile.glob(or_path + '/val/*.tfrec')\nTEST_FILENAMES = tf.io.gfile.glob(or_path + '/test/*.tfrec')\n\n# Extending the dataset with additional data\next_gcs = KaggleDatasets().get_gcs_path('tf-flower-photo-tfrec')\nimagenet_files = tf.io.gfile.glob(ext_gcs + '/imagenet' + img_size_path + '/*.tfrec')\ninaturelist_files = tf.io.gfile.glob(ext_gcs + '/inaturalist' + img_size_path + '/*.tfrec')\nopenimage_files = tf.io.gfile.glob(ext_gcs + '/openimage' + img_size_path + '/*.tfrec')\noxford_files = tf.io.gfile.glob(ext_gcs + '/oxford_102' + img_size_path + '/*.tfrec')\ntensorflow_files = tf.io.gfile.glob(ext_gcs + '/tf_flowers' + img_size_path + '/*.tfrec')\n\nEXTRA_FILES = imagenet_files + inaturelist_files + openimage_files + oxford_files + tensorflow_files\n#TRAINING_FILENAMES = TRAINING_FILENAMES + EXTRA_FILES","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Déterminer les types des variables pour les TPU"},{"metadata":{"trusted":true},"cell_type":"code","source":"raw_dataset = tf.data.TFRecordDataset(TRAINING_FILENAMES)\nserialized_example = next(iter(raw_dataset))\nexample = tf.train.Example()\nexample.ParseFromString(serialized_example.numpy())\nprint(str(example)[:300] + ' ...')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Les fonctions ci-dessous permettent d'extraire les images et les mettre sous format tensor pour que les TPU puissent les prendre en compte."},{"metadata":{"trusted":true},"cell_type":"code","source":"def decode_image(image_data):    \n    image = tf.image.decode_jpeg(image_data, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0  \n    image = tf.reshape(image, [*IMAGE_SIZE, 3]) \n    \n    return image\n\ndef read_labeled_tfrecord(example):\n    \n    LABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n        \"class\": tf.io.FixedLenFeature([], tf.int64),  # shape [] means single element\n    }\n    parsed_example = tf.io.parse_single_example(example, LABELED_TFREC_FORMAT)\n    image = decode_image(parsed_example['image'])\n    label = tf.cast(parsed_example['class'], tf.int32)\n    \n    return image, label\n\ndef read_unlabeled_tfrecord(example):\n    UNLABELED_TFREC_FORMAT = {\n        \"image\": tf.io.FixedLenFeature([], tf.string), # tf.string means bytestring\n        \"id\": tf.io.FixedLenFeature([], tf.string),  # shape [] means single element\n        # class is missing, this competitions's challenge is to predict flower classes for the test dataset\n    }\n    example = tf.io.parse_single_example(example, UNLABELED_TFREC_FORMAT)\n    image = decode_image(example['image'])\n    idnum = example['id']\n    return image, idnum","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Les 104 classes présentes dans les datasets"},{"metadata":{"trusted":true},"cell_type":"code","source":"CLASSES = [\n    'pink primrose', 'hard-leaved pocket orchid', 'canterbury bells', 'sweet pea', \n    'wild geranium', 'tiger lily', 'moon orchid', 'bird of paradise', 'monkshood', \n    'globe thistle', 'snapdragon', \"colt's foot\", 'king protea', 'spear thistle', \n    'yellow iris', 'globe-flower', 'purple coneflower', 'peruvian lily', \n    'balloon flower', 'giant white arum lily', 'fire lily', 'pincushion flower', \n    'fritillary', 'red ginger', 'grape hyacinth', 'corn poppy', \n    'prince of wales feathers', 'stemless gentian', 'artichoke', 'sweet william', \n    'carnation', 'garden phlox', 'love in the mist', 'cosmos',  'alpine sea holly', \n    'ruby-lipped cattleya', 'cape flower', 'great masterwort',  'siam tulip', \n    'lenten rose', 'barberton daisy', 'daffodil',  'sword lily', 'poinsettia', \n    'bolero deep blue',  'wallflower', 'marigold', 'buttercup', 'daisy', \n    'common dandelion', 'petunia', 'wild pansy', 'primula',  'sunflower', \n    'lilac hibiscus', 'bishop of llandaff', 'gaura',  'geranium', 'orange dahlia', \n    'pink-yellow dahlia', 'cautleya spicata',  'japanese anemone', \n    'black-eyed susan', 'silverbush', 'californian poppy',  'osteospermum', \n    'spring crocus', 'iris', 'windflower',  'tree poppy', 'gazania', 'azalea', \n    'water lily',  'rose', 'thorn apple', 'morning glory', 'passion flower',  \n    'lotus', 'toad lily', 'anthurium', 'frangipani',  'clematis', 'hibiscus', \n    'columbine', 'desert-rose', 'tree mallow', 'magnolia', 'cyclamen ', \n    'watercress',  'canna lily', 'hippeastrum ', 'bee balm', 'pink quill',  \n    'foxglove', 'bougainvillea', 'camellia', 'mallow',  'mexican petunia',  \n    'bromelia', 'blanket flower', 'trumpet creeper',  'blackberry lily', \n    'common tulip', 'wild rose']","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pour éviter l'overfitting et améliorer nos modèles, nous employons la Data Augmentations.\nNous allons donc jouer sur :\n- la luminosité\n- le constraste\n- l'effet mirroir\n- la rotation\n- le zoom\n- les cutout : initancier aléatoirement des blocs noirs"},{"metadata":{"trusted":true},"cell_type":"code","source":"SIZE_CUTOUT = (50,50)\n\ndef aug_img(image, label):\n    img = tf.image.random_brightness(image, 0.1)\n    img = tf.image.random_contrast(img, 0.8, 2.2)\n    \n    rand_rad = np.random.uniform(-np.pi / 4, np.pi / 4)\n    img = tfa.image.rotate(img, rand_rad)\n    rand_zoom = np.random.uniform(0, 0.2)\n    img = tf.image.central_crop(img, 1 - (rand_rad**2 + rand_zoom))\n    img = tf.image.resize(img, IMAGE_SIZE)\n    \n    img = tf.image.random_flip_left_right(img)\n    \n    img = tf.expand_dims(img, 0)\n    img = tfa.image.random_cutout(img, SIZE_CUTOUT)\n    img = tf.squeeze(img)\n    \n    return img, label","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Les fonctions ci-dessous permettent de créer des batchs pour chaque dataset pour l'entraînement et les prédictions des modèles."},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_dataset(filenames, labeled=True, ordered=False):\n\n    options = tf.data.Options()\n    \n    options.experimental_deterministic = ordered\n    \n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=tf.data.experimental.AUTOTUNE)\n    dataset = dataset.with_options(options)  \n    dataset = dataset.map(\n        read_labeled_tfrecord if labeled else read_unlabeled_tfrecord,\n        num_parallel_calls=tf.data.experimental.AUTOTUNE,\n    )\n    \n    return dataset\n\ndef get_training_dataset(file, batch_size, augmentation=False):\n    \n    dataset = load_dataset(file, labeled=True)\n    \n    if augmentation:\n        dataset = dataset.map(aug_img, num_parallel_calls=tf.data.experimental.AUTOTUNE)\n        \n    dataset = dataset.repeat()\n    dataset = dataset.shuffle(2024)\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n    \n    return dataset\n\ndef get_validation_dataset(batch_size, ordered=False):\n    \n    dataset = load_dataset(VALIDATION_FILENAMES, labeled=True, ordered=ordered)\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)\n    \n    return dataset\n\ndef get_test_dataset(batch_size):\n    \n    dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=True)\n    dataset = dataset.batch(batch_size)\n    dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE) \n    \n    return dataset\n\ndef count_data_items(filenames):\n    return np.sum([int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames])\n    \nORIGINAL_NUM_TRAINING_IMAGES = count_data_items(TRAINING_FILENAMES)\nNUM_VALIDATION_IMAGES = count_data_items(VALIDATION_FILENAMES)\nEXTRA_NUM_TRAINING_IMAGES = count_data_items(EXTRA_FILES)\nNUM_TEST_IMAGES = count_data_items(TEST_FILENAMES)\n\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\nSTEPS_PER_EPOCH = ORIGINAL_NUM_TRAINING_IMAGES // BATCH_SIZE\nSTEPS_PER_EPOCH_EXTRA = EXTRA_NUM_TRAINING_IMAGES // BATCH_SIZE\n\n\nprint('Original Dataset:\\n\\n{} training images\\n{} validation images\\n{} unlabeled test images'.format(ORIGINAL_NUM_TRAINING_IMAGES, NUM_VALIDATION_IMAGES, NUM_TEST_IMAGES))\nprint(EXTRA_NUM_TRAINING_IMAGES, \"extra images\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Nous voyons que les jeux de données supplémentaires sont en quantité non négligeable et donc potentiellement utile pour les modèles.\n\nJetons un coup d'oeil à nos données."},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(10,7))\n\ntrain_dataset = get_training_dataset(TRAINING_FILENAMES, batch_size=12)\ntrain_iter = iter(train_dataset)\nbatch = next(train_iter)\n\nfor i, image in enumerate(batch[0]):\n    ax = plt.subplot(3, 4, i + 1)\n    plt.imshow(image)\n    plt.title(CLASSES[batch[1].numpy()[i]])\n    plt.axis(\"off\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Regardons l'utilisation de la data augmentation sur la deuxième image"},{"metadata":{"trusted":true},"cell_type":"code","source":"image = batch[0][1]\n\nplt.figure(figsize=(7,5))\n\nfor i in range(12):\n    ax = plt.subplot(3, 4, i + 1)\n    img, _ = aug_img(image, \"\")\n    \n    plt.imshow(img)\n    plt.axis(\"off\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Les images générées sont bien toutes différentes les unes des autres avec des niveaux de constrate ou de luminosité distincts. \n\nNous allons récupérer toutes les images pour analyser les données."},{"metadata":{"trusted":true},"cell_type":"code","source":"training_dataset = get_training_dataset(TRAINING_FILENAMES, ORIGINAL_NUM_TRAINING_IMAGES)\nvalidation_dataset = get_validation_dataset(NUM_VALIDATION_IMAGES)\nextra_dataset = get_training_dataset(EXTRA_FILES, EXTRA_NUM_TRAINING_IMAGES)\ntrain_batch = next(iter(training_dataset))\nval_batch = next(iter(validation_dataset))\nextra_batch = next(iter(extra_dataset))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Affichons la distribution des labels pour le jeu de données de validation et celui d'entraînement."},{"metadata":{"trusted":true},"cell_type":"code","source":"import seaborn as sns\nimport matplotlib.pyplot as plt\nplt.figure(figsize=(12,12))\nax = sns.distplot(train_batch[1].numpy(), bins=len(CLASSES), kde=False, label='Train', vertical=True)\nax = sns.distplot(val_batch[1].numpy(), bins=len(CLASSES), kde=False, label='Validation', vertical=True)\n\nax.set_yticks(np.arange(len(CLASSES)))\nax.set_yticklabels(CLASSES, fontsize=7)\n\nplt.legend(prop={'size': 12})\nplt.title('Distribution of labels')\nplt.xlabel('Occurrences')\nplt.ylabel('Labels')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Nous pouvons noter la présence d'imbalance de classes. Cependant, la distribution des deux datasets sont identiques.\n\nComparons celle d'entraînement à celle des jeux de données supplémentaires."},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.figure(figsize=(12,12))\nax = sns.distplot(train_batch[1].numpy(), bins=len(CLASSES), kde=False, label='Train', vertical=True)\nax = sns.distplot(extra_batch[1].numpy(), bins=len(CLASSES), kde=False, label='Extra', vertical=True)\n\nax.set_yticks(np.arange(len(CLASSES)))\nax.set_yticklabels(CLASSES, fontsize=7)\n\nplt.legend(prop={'size': 12})\nplt.title('Distribution of labels')\nplt.xlabel('Occurrences')\nplt.ylabel('Labels')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Nous observons que la distribution des images supplémentaires n'est pas exactement pareille à celle d'entraînement.\n\nEn effectuant des tests au préalable, il semblerait que les images tests (de soumission) aient la même distribution que celle d'entraînement.\n\nPar conséquent, nous allons utiliser les données supplémentaires en effectuant une correction d'imbalance de classes en ajoutant des coefficients."},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.utils import class_weight\n\nclass_weights = class_weight.compute_class_weight('balanced', np.unique(extra_batch[1].numpy()), extra_batch[1].numpy())\ndict_weights = dict(enumerate(class_weights))\ndict_weights","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Une technique utilisé est le learning rate schedule où le taux d'apprentissage progresse lentement en fonction du numéro de batch courant pour ensuite effectuer une descente exponentielle."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Learning rate schedule for TPU, GPU and CPU.\n# Using an LR ramp up because fine-tuning a pre-trained model.\n# Starting with a high LR would break the pre-trained weights.\n\nLR_START = 0.00001\nLR_MAX = 0.00005 * strategy.num_replicas_in_sync\nLR_MIN = 0.00001\nLR_RAMPUP_EPOCHS = 5\nLR_SUSTAIN_EPOCHS = 0\nLR_EXP_DECAY = .8\n\ndef lrfn(epoch):\n    if epoch < LR_RAMPUP_EPOCHS:\n        lr = (LR_MAX - LR_START) / LR_RAMPUP_EPOCHS * epoch + LR_START\n    elif epoch < LR_RAMPUP_EPOCHS + LR_SUSTAIN_EPOCHS:\n        lr = LR_MAX\n    else:\n        lr = (LR_MAX - LR_MIN) * LR_EXP_DECAY**(epoch - LR_RAMPUP_EPOCHS - LR_SUSTAIN_EPOCHS) + LR_MIN\n    return lr\n    \nlr_callback = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose = True)\n\nrng = [i for i in range(50)]\ny = [lrfn(x) for x in rng]\nplt.plot(rng, y)\nprint(\"Learning rate schedule: {:.3g} to {:.3g} to {:.3g}\".format(y[0], max(y), y[-1]))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Comme nous utilisons la méthode ensembliste en prenant en compte les prédictions de deux modèles, nous choisissons deux ayant généralement de bons résultats : DenseNet201 et EfficientNetB7.\n\nL'entraînement de chaque modèle s'effectue en 2 étapes :\n- Apprendre sur les jeux de données supplémentaires ayant une correction d'imbalance de classes\n- Apprendre sur le jeu de données d'apprentissage"},{"metadata":{"trusted":true},"cell_type":"code","source":"training_dataset = get_training_dataset(TRAINING_FILENAMES, BATCH_SIZE, augmentation=True)\nvalidation_dataset = get_validation_dataset(BATCH_SIZE)\ntraining_dataset_extra = get_training_dataset(EXTRA_FILES, BATCH_SIZE, augmentation=True)\n\nwith strategy.scope():    \n\n    base_model = tf.keras.applications.DenseNet201(weights='imagenet', include_top = False, input_shape=[*IMAGE_SIZE, 3])\n    base_model.trainable = True\n\n    model = tf.keras.Sequential([\n        base_model,\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dense(len(CLASSES), activation='softmax')\n    ])\n\nmodel.compile(\n    optimizer='adam',\n    loss = 'sparse_categorical_crossentropy',\n    metrics=['sparse_categorical_accuracy']\n)\n\n\nhistory = model.fit(training_dataset_extra, \n          steps_per_epoch=STEPS_PER_EPOCH_EXTRA, \n          epochs=3, \n          validation_data=validation_dataset,\n          class_weight=dict_weights,                    \n          callbacks=[lr_callback]\n)\n\n\nhistory = model.fit(training_dataset, \n          steps_per_epoch=STEPS_PER_EPOCH, \n          epochs=50, \n          validation_data=validation_dataset,                    \n          callbacks=[tf.keras.callbacks.EarlyStopping('val_loss', patience=5), lr_callback]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():    \n\n    base_model = efficientnet.EfficientNetB7(weights='noisy-student', include_top = False, input_shape=[*IMAGE_SIZE, 3])\n    base_model.trainable = True\n\n    model2 = tf.keras.Sequential([\n        base_model,\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dense(len(CLASSES), activation='softmax')\n    ])\n\nmodel2.compile(\n    optimizer='adam',\n    loss = 'sparse_categorical_crossentropy',\n    metrics=['sparse_categorical_accuracy']\n)\n\n\nhistory = model2.fit(training_dataset_extra, \n          steps_per_epoch=STEPS_PER_EPOCH_EXTRA, \n          epochs=3, \n          validation_data=validation_dataset,\n          class_weight=dict_weights,                    \n          callbacks=[lr_callback]\n)\n\nhistory = model2.fit(training_dataset, \n          steps_per_epoch=STEPS_PER_EPOCH, \n          epochs=50, \n          validation_data=validation_dataset,                    \n          callbacks=[tf.keras.callbacks.EarlyStopping('val_loss', patience=5), lr_callback]\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Pour utiliser la méthode ensembliste, il faut déterminer un coefficient permettant de déterminer la meilleure balance entre les deux modèles.\n\nPour cela, nous allons utiliser le dataset de validation pour comparer les résultats."},{"metadata":{"trusted":true},"cell_type":"code","source":"cmdataset = get_validation_dataset(BATCH_SIZE, ordered=True) # since we are splitting the dataset and iterating separately on images and labels, order matters.\nimages_ds = cmdataset.map(lambda image, label: image)\nlabels_ds = cmdataset.map(lambda image, label: label).unbatch()\ncm_correct_labels = next(iter(labels_ds.batch(NUM_VALIDATION_IMAGES))).numpy() # get everything as one batch\nm1 = model.predict(images_ds)\nm2 = model2.predict(images_ds)\n\ncm_predictions = np.argmax(m1, axis=-1)\nprint(\"M1:\", f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro'))\n\ncm_predictions = np.argmax(m2, axis=-1)\nprint(\"M2:\", f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro'))\n\nscores = []\nfor alpha in np.linspace(0,1,100):\n    cm_probabilities = alpha*m1+(1-alpha)*m2\n    cm_predictions = np.argmax(cm_probabilities, axis=-1)\n    scores.append(f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro'))\n\n    best_alpha = np.argmax(scores)/100\nprint('Best alpha:', str(best_alpha))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Cet alpha sera donc conservé pour les prédictions.\n\nRegardons le matrice de confusion."},{"metadata":{"trusted":true},"cell_type":"code","source":"def display_confusion_matrix(cmat, score, precision, recall):\n    plt.figure(figsize=(15,15))\n    ax = plt.gca()\n    ax.matshow(cmat, cmap='Reds')\n    ax.set_xticks(range(len(CLASSES)))\n    ax.set_xticklabels(CLASSES, fontdict={'fontsize': 7})\n    plt.setp(ax.get_xticklabels(), rotation=45, ha=\"left\", rotation_mode=\"anchor\")\n    ax.set_yticks(range(len(CLASSES)))\n    ax.set_yticklabels(CLASSES, fontdict={'fontsize': 7})\n    plt.setp(ax.get_yticklabels(), rotation=45, ha=\"right\", rotation_mode=\"anchor\")\n    titlestring = \"\"\n    if score is not None:\n        titlestring += 'f1 = {:.3f} '.format(score)\n    if precision is not None:\n        titlestring += '\\nprecision = {:.3f} '.format(precision)\n    if recall is not None:\n        titlestring += '\\nrecall = {:.3f} '.format(recall)\n    if len(titlestring) > 0:\n        ax.text(101, 1, titlestring, fontdict={'fontsize': 18, 'horizontalalignment':'right', 'verticalalignment':'top', 'color':'#804040'})\n    plt.show()\n\ncm_probabilities = best_alpha*m1 + (1-best_alpha)*m2\ncm_predictions = np.argmax(cm_probabilities, axis=-1)\ncmat = confusion_matrix(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)))\nscore = f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro')\nprecision = precision_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro')\nrecall = recall_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro')\n#cmat = (cmat.T / cmat.sum(axis=1)).T # normalized\ndisplay_confusion_matrix(cmat, score, precision, recall)\nprint('f1 score: {:.3f}, precision: {:.3f}, recall: {:.3f}'.format(score, precision, recall))\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Les classes ayant le plus d'instances sont celles qui ont eu de meilleures bonnes prédictions.\n\nTest Time Augmentation est également employé. Cette technique consiste à utiliser la data augmentation sur les fichiers tests."},{"metadata":{"trusted":true},"cell_type":"code","source":"def tta_data_aug(images):\n    img = tf.image.random_brightness(images, 0.1)\n    img = tf.image.random_contrast(img, 0.8, 2.2)\n    \n    rand_rad = np.random.uniform(-np.pi / 6, np.pi / 6)\n    img = tfa.image.rotate(img, rand_rad)\n    rand_zoom = np.random.uniform(0, 0.2)\n    img = tf.image.central_crop(img, 1 - (rand_rad**2 + rand_zoom))\n    img = tf.image.resize(img, IMAGE_SIZE)\n    \n    img = tf.image.random_flip_left_right(img)\n    \n    img = tfa.image.random_cutout(img, SIZE_CUTOUT)\n    \n    return img\n\n\n\ndef tta_predictions(model, model2, ds, n):\n    probs  = []\n    probs2 = []\n    for i in range(n):\n        ds_tta = ds.map(tta_data_aug, num_parallel_calls=tf.data.experimental.AUTOTUNE)        \n        probs.append(model.predict(ds_tta))\n        probs2.append(model2.predict(ds_tta))\n        \n    return probs, probs2\n\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Prédiction des images tests pour la soumission"},{"metadata":{"trusted":true},"cell_type":"code","source":"cmdataset = get_validation_dataset(BATCH_SIZE, ordered=True) # since we are splitting the dataset and iterating separately on images and labels, order matters.\nimages_ds = cmdataset.map(lambda image, label: image)\nlabels_ds = cmdataset.map(lambda image, label: label).unbatch()\ncm_correct_labels = next(iter(labels_ds.batch(NUM_VALIDATION_IMAGES))).numpy() # get everything as one batch\nm1, m2 = tta_predictions(model, model2, images_ds, 5)\n\nm1 = np.mean(m1, axis=0)\nm2 = np.mean(m2, axis=0)\n\ncm_predictions = np.argmax(m1, axis=-1)\nprint(\"M1:\", f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro'))\n\ncm_predictions = np.argmax(m2, axis=-1)\nprint(\"M2:\", f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro'))\n\nscores = []\nfor alpha in np.linspace(0,1,100):\n    cm_probabilities = alpha*m1+(1-alpha)*m2\n    cm_predictions = np.argmax(cm_probabilities, axis=-1)\n    scores.append(f1_score(cm_correct_labels, cm_predictions, labels=range(len(CLASSES)), average='macro'))\n\n    best_alpha = np.argmax(scores)/100\nprint('Best alpha:', str(best_alpha))\n\n\ntest_ds = get_test_dataset(BATCH_SIZE)\ntest_images_ds = test_ds.map(lambda image, idnum: image)\nm1, m2 = tta_predictions(model, model2, test_images_ds, 5)\nprobs1 = np.mean(m1, axis=0)\nprobs2 = np.mean(m2, axis=0)\nprobabilities = best_alpha*probs1 + (1-best_alpha)*probs2\npredictions = np.argmax(probabilities, axis=-1)\n\ntest_ids_ds = test_ds.map(lambda image, idnum: idnum).unbatch()\ntest_ids = next(iter(test_ids_ds.batch(NUM_TEST_IMAGES))).numpy().astype('U') # all in one batch\nnp.savetxt('submission.csv', np.rec.fromarrays([test_ids, predictions]), fmt=['%s', '%d'], delimiter=',', header='id,label', comments='')","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}