{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\n\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport cv2 #opencv-python\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport matplotlib.image as mpimg\n%matplotlib inline\nimport pandas as pd\nimport tensorflow as tf\nimport tensorflow_hub as hub\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.applications import NASNetLarge\nfrom tensorflow.keras.applications.nasnet import preprocess_input\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.applications.resnet50 import decode_predictions, ResNet50\nimport h5py\nimport datetime\nimport os\nimport shutil\nimport pathlib\nimport time\nfrom IPython.display import display, Image, Markdown\nimport pickle\nfrom numba import cuda\n\ninput = '/kaggle/input/dog-breed-identification/'\noutput = '/kaggle/working/'\n\ndef md(input):\n    display(Markdown(input))\n\ndef step(input):\n    return md(f\"✅ *{input}*\")\n\n\ndef save_model(model, model_path):\n  \"\"\"\n  Saves a given model in a models directory and appends a suffix (str)\n  for clarity and reuse.\n  \"\"\"\n  # Create model directory with current time\n#   modeldir = os.path.join(\"models\",\n#                           datetime.datetime.now().strftime(\"%d-%m-%y-%Hh%Mm\"))\n  print(f\"Saving model to: {model_path}...\")\n  model.save(model_path)\n\ndef load_model(model_path):\n  \"\"\"\n  Loads a saved model from a specified path.\n  \"\"\"\n  print(f\"Loading saved model from: {model_path}\")\n  model = tf.keras.models.load_model(model_path)\n  return model\n    \n    \ndef plot_history(history):\n    metrics = (('accuracy', 'val_accuracy'), ('loss', 'val_loss'))\n    for metric in metrics:\n        plt.plot(history[metric[0]])\n        plt.plot(history[metric[1]])\n        plt.title('model {}'.format(metric[0]))\n        plt.ylabel(metric[0])\n        plt.xlabel('epoch')\n        plt.legend(['train', 'test'], loc='upper left')\n        plt.show()\n\nprint(\"GPU\", \"available (YES!)\" if tf.config.list_physical_devices(\"GPU\") else \"not available :(\")\nprint(\"clear GPU memory...\")\ndevice = cuda.get_current_device()\ndevice.reset() # clear GPU memory\nstep(\"Setup\")\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2021-06-17T12:33:15.853834Z","iopub.execute_input":"2021-06-17T12:33:15.854230Z","iopub.status.idle":"2021-06-17T12:33:18.160696Z","shell.execute_reply.started":"2021-06-17T12:33:15.854146Z","shell.execute_reply":"2021-06-17T12:33:18.159903Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_dir = \"{}train/\".format(input)\ntest_dir = \"{}test/\".format(input)\nTRAINING_MODE = False # False = load model and history saved in file (much much faster) instead build it from zero\ndf = pd.read_csv(\"{}labels.csv\".format(input))\ndf=df.sample(frac=1).reset_index(drop=True) #shuffle\ndf['path'] = df.id.apply(lambda x: '{}/{}.jpg'.format(train_dir, x)) # replace id by path to feed generator with flow_from_dataframe\ndf.drop('id', axis=1, inplace=True)\ndisplay(df.head())\nstep(\"Data loading\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:18.162253Z","iopub.execute_input":"2021-06-17T12:33:18.162595Z","iopub.status.idle":"2021-06-17T12:33:18.201678Z","shell.execute_reply.started":"2021-06-17T12:33:18.162558Z","shell.execute_reply":"2021-06-17T12:33:18.200759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"On mélange le dataframe dès le début pour éviter de le faire par la suite ce qui pourrais compliquer notre interprétation des prédictions, notemment savoir quelle image est associée à chaque prédiction","metadata":{}},{"cell_type":"markdown","source":"## Exploration","metadata":{}},{"cell_type":"code","source":"print(\"Nombre de photos des différentes races de chiens\")\ndf[\"breed\"].value_counts().plot.bar(figsize=(20, 10));","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:18.203550Z","iopub.execute_input":"2021-06-17T12:33:18.203900Z","iopub.status.idle":"2021-06-17T12:33:20.057720Z","shell.execute_reply.started":"2021-06-17T12:33:18.203864Z","shell.execute_reply":"2021-06-17T12:33:20.056869Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"{'nombre totale de chiens': df.shape[0], 'nombre de chiens de race la plus représentée': df[\"breed\"].value_counts()[0], 'nombre de chiens de race la moins représentée': df[\"breed\"].value_counts()[-1]}","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:20.059226Z","iopub.execute_input":"2021-06-17T12:33:20.059573Z","iopub.status.idle":"2021-06-17T12:33:20.074945Z","shell.execute_reply.started":"2021-06-17T12:33:20.059539Z","shell.execute_reply":"2021-06-17T12:33:20.074000Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"max_breed = df[\"breed\"].value_counts().index[0]\nmin_breed = df[\"breed\"].value_counts().index[-1]\nrandom_max_breed = df.query(\"breed == @max_breed\").sample()\nrandom_min_breed = df.query(\"breed == @min_breed\").sample()","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:20.079197Z","iopub.execute_input":"2021-06-17T12:33:20.080135Z","iopub.status.idle":"2021-06-17T12:33:20.113317Z","shell.execute_reply.started":"2021-06-17T12:33:20.080095Z","shell.execute_reply":"2021-06-17T12:33:20.112603Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"La classe la plus représentée : {} ({} images)\".format(max_breed, df[\"breed\"].value_counts()[0]))\nImage(random_max_breed.path.item())","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:20.114528Z","iopub.execute_input":"2021-06-17T12:33:20.114884Z","iopub.status.idle":"2021-06-17T12:33:20.132090Z","shell.execute_reply.started":"2021-06-17T12:33:20.114848Z","shell.execute_reply":"2021-06-17T12:33:20.130937Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"La classe la moins représentée : {} ({} images)\".format(min_breed, df[\"breed\"].value_counts()[-1]))\nImage(random_min_breed.path.item())","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:20.133487Z","iopub.execute_input":"2021-06-17T12:33:20.133896Z","iopub.status.idle":"2021-06-17T12:33:20.149039Z","shell.execute_reply.started":"2021-06-17T12:33:20.133858Z","shell.execute_reply":"2021-06-17T12:33:20.147803Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Effectif de chaque race de chien**","metadata":{}},{"cell_type":"code","source":"pd.DataFrame(df.breed.value_counts())","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:20.150316Z","iopub.execute_input":"2021-06-17T12:33:20.150649Z","iopub.status.idle":"2021-06-17T12:33:20.163752Z","shell.execute_reply.started":"2021-06-17T12:33:20.150614Z","shell.execute_reply":"2021-06-17T12:33:20.162725Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Vérification des données","metadata":{}},{"cell_type":"code","source":"from os import listdir\nfrom os.path import isfile, join\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nimport matplotlib.pyplot as plt\n\n\nsample_submission = pd.read_csv('{}sample_submission.csv'.format(input))\n\ndef get_all_files_in_dir(dir_path, full_path=True):\n    if full_path:\n        return [dir_path+f for f in listdir(dir_path) if isfile(join(dir_path, f))]\n    else:\n        return [f for f in listdir(dir_path) if isfile(join(dir_path, f))]\n    \ntrain_image_paths = get_all_files_in_dir(train_dir)\ntest_image_paths = get_all_files_in_dir(test_dir)\nmd(f'Il y a **{len(train_image_paths):,}** images dans notre dossier train')\nmd(f'Il y a **{len(test_image_paths):,}** images dans notre dossier test')\nif df.shape[0] != len(train_image_paths) or sample_submission.shape[0] != len(test_image_paths):\n    print(\"/!\\ Il y a une différence entre le nombre d'image est le nombre de lignes dans notre dataset!\")\nelse:\n    print(\"Il y autant d'images dans nos dossiers que de ligne dans nos dataset de train et de test.\")\n    \n\ndef get_img_infos(img):\n    img_type = type(img)\n    img_format = img.format\n    img_mode = img.mode\n    img_size = img.size\n    return img_type, img_format, img_mode, img_size\n\nsample_image = load_img(train_image_paths[0])\nsample_type, sample_format, sample_mode, sample_size = get_img_infos(sample_image)\n\n\n\nbad_img_count = 0\nfor image_paths in (train_image_paths, test_image_paths):\n    for img_path in image_paths:\n        img = load_img(img_path)\n        img_type, img_format, img_mode, img_size = get_img_infos(img)\n        if img_type != sample_type or img_format != sample_format or img_mode != sample_mode:\n            print(\"L'image n'est pas conforme : {}\".format(img_path))\n            bad_img_count += 1\n        \nif bad_img_count == 0:\n    md(\"**Toutes les images sont conformes**\")\nelse:\n    print(\"{} image(s) doivent être ajustées\".format(bad_img_count))\n    \n\n\n","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:20.167109Z","iopub.execute_input":"2021-06-17T12:33:20.167507Z","iopub.status.idle":"2021-06-17T12:33:37.734867Z","shell.execute_reply.started":"2021-06-17T12:33:20.167472Z","shell.execute_reply":"2021-06-17T12:33:37.734057Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Voici une image pris au hasard:\")\nsample_image_array = img_to_array(sample_image)\nplt.imshow(sample_image_array / 255.0);\nprint('Image type: {}'.format(sample_type))\nprint('Image format: {}'.format(sample_format))\nprint('Image mode: {}'.format(sample_mode))\nprint('Image size: {}'.format(sample_size))","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:37.736454Z","iopub.execute_input":"2021-06-17T12:33:37.736831Z","iopub.status.idle":"2021-06-17T12:33:37.899055Z","shell.execute_reply.started":"2021-06-17T12:33:37.736784Z","shell.execute_reply":"2021-06-17T12:33:37.898093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"print(\"Pour pouvoir travailler avec des images on doit les convertir en chiffre c'est à dire en matrice. Chaque pixel devient un vecteur de taille 3 codant les couleurs RGB (red, green, blue)\")\nprint('Transformation en matrice...')\nprint(f'Image type: {type(sample_image_array)}')\nprint(f'Image array shape: {sample_image_array.shape}')\nsample_image_array","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:37.900337Z","iopub.execute_input":"2021-06-17T12:33:37.900745Z","iopub.status.idle":"2021-06-17T12:33:37.911340Z","shell.execute_reply.started":"2021-06-17T12:33:37.900686Z","shell.execute_reply":"2021-06-17T12:33:37.910228Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Après avoir explorer et vérifier les données passons à création de notre modèle de machine learning pour prédire les races de chien.","metadata":{}},{"cell_type":"markdown","source":"## Créations des tenseurs","metadata":{}},{"cell_type":"code","source":"model_name = 'nasnet'\nimage_width = 331\nimage_size = (image_width, image_width)\n\n\ngenerator = ImageDataGenerator(\n    validation_split=0.02,\n    horizontal_flip = True,\n    preprocessing_function = preprocess_input\n)\n\n# WITH AUGMENTATION\n# generator = ImageDataGenerator(\n#     validation_split=0.02,\n#     horizontal_flip = True,\n#     rotation_range = 20,\n#     width_shift_range = 0.1,\n#     height_shift_range = 0.1,\n#     shear_range = 0.1,\n#     zoom_range=0.1,\n#     fill_mode = 'nearest',\n#     preprocessing_function = preprocess_input\n# )\n        \ntrain_generator = generator.flow_from_dataframe(\n    dataframe=df,\n    x_col=\"path\",\n    y_col=\"breed\",\n    target_size=image_size,\n    batch_size=32,\n    subset=\"training\",\n    shuffle=False,\n)\n\n\n\n\nvalid_generator = generator.flow_from_dataframe(\n    dataframe=df,\n    x_col=\"path\",\n    y_col=\"breed\",\n    target_size=image_size,\n    batch_size=32,\n    subset=\"validation\",\n    shuffle=False,\n)\n\nstep(\"Tensor generator\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:37.912783Z","iopub.execute_input":"2021-06-17T12:33:37.913224Z","iopub.status.idle":"2021-06-17T12:33:38.103786Z","shell.execute_reply.started":"2021-06-17T12:33:37.913130Z","shell.execute_reply":"2021-06-17T12:33:38.102997Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"On crée nos tenseurs à l'aide des générateurs de Tensorflow, comme nous disposons déjà d'un dataframe avec l'emplacement de nos fichiers et les labels associés on peut utiliser la fonctions **flow_from_dataframe**. On donne un nom à notre modèle qui servira à créer le fichier d'export du modèle. Toutes nos images doivent avoir **la même taille** pour avoir un traitement uniforme (un pixel = 3 données RGB). Si on a le choix on choisira 300x300 ou les recommandation du modèle pré-entrainé que l'on utilise. Il faut également **normaliser** les valeurs RGB pour cela on applique un rescale. Le batch size, c'est à dire la taille des paquet d'images qui seront soumises à chaque itération de notre entrainement est fixé à **32**, c'est la valeur qui marche la plupart du temps pour limiter le suraprentissage. **20%** de nos données seront dédiées à la validation. On peut ou pas choisir d'augmenter notre dataset à travers divers transformations (si vous décommentez il faut repasser un entrainement en passant TRAINING_MODE à True).","metadata":{}},{"cell_type":"markdown","source":"## Modèle","metadata":{}},{"cell_type":"markdown","source":"Si aucun fichier n'existe on entraine notre modèle puis on le sauvegarde ainsi que ses stats (history). Le modèle est optimisé pour maximiser l'accuracy, pour diminuer le loss on peut supprimmer le monitor=\"val_accuracy\" et mettre patience=3 à l'early stopping, ainsi le modèle sera plus pertinent mais s'arretera plus tard et perdra un peu en précision.","metadata":{}},{"cell_type":"code","source":"model_path = output + 'model_'+ model_name + \".h5\"\nhistory_path = output + 'history_'+ model_name + \".h5\"\n\nif not pathlib.Path(model_path).exists() or not pathlib.Path(history_path).exists() or TRAINING_MODE:\n    md(\"**Il n'existe pas de fichiers, on doit entrainer notre modèle complétement une première fois**\")\n    \n    early_stopping = tf.keras.callbacks.EarlyStopping(\n        monitor=\"val_accuracy\",\n        patience=2, \n        min_delta=0.001, \n        restore_best_weights=True\n    )\n\n    # Setup input shape to the model\n    input_shape = [None, image_width, image_width, 3] # batch, height, width, colour channels\n\n    # Setup output shape of the model\n    output_shape = 120 # number of unique labels\n\n\n    nas_model=NASNetLarge(\n        include_top=False, \n        weights='imagenet', \n        input_shape=(image_width,image_width,3),\n    )\n\n    nas_model.trainable = False\n\n    \n    # Setup the model layers\n    model = tf.keras.Sequential([\n        nas_model,   \n        layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dense(120, activation='softmax')\n    ])\n\n\n    # Compile the model\n    opt = SGD(lr=1e-3, momentum=0.9)\n    model.compile(\n        optimizer = opt, \n        loss=\"categorical_crossentropy\", \n        metrics=[\"accuracy\"]\n    )\n    model.summary()\n\n    STEP_SIZE_TRAIN = train_generator.n//train_generator.batch_size\n    STEP_SIZE_VALID = valid_generator.n//valid_generator.batch_size\n    history = model.fit(\n        train_generator, \n        steps_per_epoch=STEP_SIZE_TRAIN, \n        validation_data=valid_generator, \n        validation_steps=STEP_SIZE_VALID, \n        epochs=25, \n        batch_size=32, \n        callbacks=[early_stopping], \n    )\n    \n    \n    history = history.history\n    pickle.dump(history, open( history_path, \"wb\" ) )\n    save_model(model, model_path)\n    \nstep(\"Making model\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:33:38.105071Z","iopub.execute_input":"2021-06-17T12:33:38.105414Z","iopub.status.idle":"2021-06-17T12:33:38.117527Z","shell.execute_reply.started":"2021-06-17T12:33:38.105379Z","shell.execute_reply":"2021-06-17T12:33:38.116617Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Evaluation","metadata":{}},{"cell_type":"code","source":"model = load_model(model_path)\nhistory = pickle.load(open(history_path, 'rb'))\ndef plot_history(history):\n    metrics = (('accuracy', 'val_accuracy'), ('loss', 'val_loss'))\n    for metric in metrics:\n        plt.plot(history[metric[0]])\n        plt.plot(history[metric[1]])\n        plt.title('model {}'.format(metric[0]))\n        plt.ylabel(metric[0])\n        plt.xlabel('epoch')\n        plt.legend(['train', 'valid'], loc='upper left')\n        plt.show();\n        if metric[0] == 'accuracy':\n            md(\"best validation {} score: **{}**\".format(metric[0], max(history[metric[1]])))\n        else:\n            md(\"best validation {} score: **{}**\".format(metric[0], min(history[metric[1]])))\n        \n        \nplot_history(history)\ny_pred = model.predict(valid_generator, workers=16, verbose=1)\nstep(\"Evaluation\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T13:11:24.020746Z","iopub.execute_input":"2021-06-17T13:11:24.021096Z","iopub.status.idle":"2021-06-17T13:11:45.172206Z","shell.execute_reply.started":"2021-06-17T13:11:24.021065Z","shell.execute_reply":"2021-06-17T13:11:45.169791Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Prédictions sur les données de validation\nA partir des prédictions de notre modèle on va construire un dataframe avec pour chaque prédiction l'image associée et les probabilitées attribuées à chaque race de chien.","metadata":{}},{"cell_type":"code","source":"unique_breeds = list(pd.unique(df.breed))\nunique_breeds.sort()\nbreed_pred = []\ntop10_pred = []\nall_preds = []\n\nfor pred in y_pred:\n    breed_pred.append(unique_breeds[np.argmax(pred)])\n    top10_keys = pred.argsort()[-10:][::-1]\n    top10_values = np.sort(pred)[-10:][::-1] \n    top10_pred.append(dict(zip([unique_breeds[key] for key in top10_keys], top10_values)))\n    \n\ndf_pred = pd.DataFrame({'path':valid_generator.filenames, 'breed':[unique_breeds[label_index] for label_index in valid_generator.labels], 'pred': breed_pred, 'proba': top10_pred })\nclass_to_num = dict(zip(unique_breeds, range(120)))  # affenpinscher : 0\nfor name in unique_breeds:  \n    df_pred[name] = y_pred[:,class_to_num[name]]\n\nmd(\"**Une ligne de notre dataframe de prédictions**\")\ndisplay(df_pred.sample().T)\nstep(\"Predictions on validation data\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:34:00.683833Z","iopub.execute_input":"2021-06-17T12:34:00.684166Z","iopub.status.idle":"2021-06-17T12:34:00.824182Z","shell.execute_reply.started":"2021-06-17T12:34:00.684129Z","shell.execute_reply":"2021-06-17T12:34:00.823313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Analyse des erreurs de prédictions\nA partir de notre dataframe de prédictions on cherche à mieux visualiser les différentes érreurs de notre modèle\n","metadata":{}},{"cell_type":"code","source":"def get_error(row):\n    predictions = row.proba\n    if row.pred != row.breed:\n        best_score = list(predictions.values())[0]\n        return best_score\n    else:\n        return 0\n\nkey_cols = ['path', 'breed', 'pred', 'proba']\n\ndf_pred = df_pred[key_cols].copy()\ndf_pred[\"error\"] = df_pred.apply(get_error, axis=1)\n\nerror_counts = len(df_pred.query(\"pred != breed\"))\nerrors_percent = round(error_counts/df_pred.shape[0]*100)\nprint(\"Nombre d'erreurs du modèle sur les données de validations : {}/{} ({}%)\".format(error_counts,df_pred.shape[0], errors_percent))\nprint(\"Nous avons voulu savoir quelle prédictions avaient réalisé notre modèle lorsqu'il c'est trompé, pour cela on a filtrer les mauvaises prédictions et on a retenu le score de la plus forte prédiction éronnée pour produire deux classements différents. Le premier tableau montre les races de chiens les plus difficiles à prédire (moyenne de la marge d'érreur) tandis que le second montre les races qui détériorent le plus notre modèle (sommme au lieu de la moyenne)\")\nmd(\"**les races les plus difficiles à prédire:**\")\ndisplay(df_pred.groupby(\"breed\").mean(\"error\").sort_values(by=[\"error\"],ascending=False)[0:20])\nmd(\"**les races dont les mauvaises prédictions impactent le plus notre modèle:**\")\ndisplay(df_pred.groupby(\"breed\").sum(\"error\").sort_values(by=[\"error\"],ascending=False)[0:20])\n","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:34:00.828184Z","iopub.execute_input":"2021-06-17T12:34:00.828595Z","iopub.status.idle":"2021-06-17T12:34:00.878977Z","shell.execute_reply.started":"2021-06-17T12:34:00.828564Z","shell.execute_reply":"2021-06-17T12:34:00.878156Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Les images les plus difficiles à prédire\nVoici les images où le modèle a le moins performé. La bonne réponse est en vert. La photo de gauche est l'image erronée et celle de droite est la race prédite. On constate que le problème ne vient pas de la qualité des images ou de leur driversité mais plutôt de la forte ressemblance que peuvent avoir différentes races de chien.","metadata":{}},{"cell_type":"code","source":"md(\"**TRUE**{}**PRED**\".format(\"&nbsp;\"*75))\n\ndef plot_dog(row):\n    breeds = list(row.proba.keys())\n    scores = list(row.proba.values())\n    pred_image_path = df.query(\"breed == @row.pred\").sample().path.item()\n    img = mpimg.imread(row.path)\n    img = cv2.resize(img, (300, 300))\n    pred_img = mpimg.imread(pred_image_path)\n    pred_img = cv2.resize(pred_img, (300, 300))\n    plt.figure(figsize=(20,5))\n    Grid_plot = plt.GridSpec(1, 3, wspace=0.45)\n    plt.subplot(Grid_plot[0, 0])\n    plt.axis('off')\n    imgplot = plt.imshow(img);\n    plt.subplot(Grid_plot[0, 1])\n    plt.axis('off')\n    imgplot = plt.imshow(pred_img);    \n    plt.subplot(Grid_plot[0, 2:])\n    clrs = ['green' if x == row.breed else 'grey' for x in breeds]\n    sns.barplot(y=breeds, x=scores, orient='h', palette=clrs)\n    \n    \ndf_pred.query(\"pred != breed\").sort_values(by=[\"error\"],ascending=False).apply(plot_dog, axis=1);","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:54:00.390856Z","iopub.execute_input":"2021-06-17T12:54:00.391200Z","iopub.status.idle":"2021-06-17T12:54:03.432536Z","shell.execute_reply.started":"2021-06-17T12:54:00.391168Z","shell.execute_reply":"2021-06-17T12:54:03.431589Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"step(\"Predictions details on validation data\")","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:34:04.725560Z","iopub.execute_input":"2021-06-17T12:34:04.725929Z","iopub.status.idle":"2021-06-17T12:34:04.732605Z","shell.execute_reply.started":"2021-06-17T12:34:04.725889Z","shell.execute_reply":"2021-06-17T12:34:04.731416Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Création du fichier de soumission\nOn utilise notre modèle pour prédire les races de chiens de notre échantillion de test et on enregistre ces prédictions dans un fichier que l'on pourra soumettre à Kaggle pour obtenir un score","metadata":{}},{"cell_type":"code","source":"test_df = pd.read_csv('{}sample_submission.csv'.format(input))\n\nnew_id = [el +\".jpg\" for el in test_df[\"id\"]]\ntest_df[\"id\"] = new_id\n\n\n\ntest_datagen=ImageDataGenerator(\n    horizontal_flip = True,\n    preprocessing_function = preprocess_input\n)\n\ntest_generator = test_datagen.flow_from_dataframe(\n    dataframe=test_df,\n    directory=test_dir,\n    x_col=\"id\",\n    y_col=None,\n    target_size=image_size,\n    batch_size=32,\n    class_mode=None,\n    shuffle=False,\n)\n\n\ny_pred = model.predict(test_generator)\n\n\ndf_sub = pd.read_csv('{}sample_submission.csv'.format(input))\ndisplay(df_sub.head())\n\ndf_sub.iloc[:,1:] = y_pred\ndisplay(df_sub.head())\n\nfinal_df = df_sub.set_index('id')\nfilename = 'my_submission.csv'\nfinal_df.to_csv(filename)\nstep(\"Fichier de soumission crée: {}\".format(filename))","metadata":{"execution":{"iopub.status.busy":"2021-06-17T12:34:04.734405Z","iopub.execute_input":"2021-06-17T12:34:04.734810Z","iopub.status.idle":"2021-06-17T12:37:16.823498Z","shell.execute_reply.started":"2021-06-17T12:34:04.734765Z","shell.execute_reply":"2021-06-17T12:37:16.822688Z"},"trusted":true},"execution_count":null,"outputs":[]}]}