{"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# standard imports\nimport numpy as np \nimport pandas as pd\nimport matplotlib.pyplot as plte\n%matplotlib inline\nimport tensorflow as tf\nimport tensorflow_hub as hub\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 os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\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-09T11:45:17.540214Z","iopub.execute_input":"2021-06-09T11:45:17.540844Z","iopub.status.idle":"2021-06-09T11:45:20.491445Z","shell.execute_reply.started":"2021-06-09T11:45:17.540806Z","shell.execute_reply":"2021-06-09T11:45:20.48118Z"},"_kg_hide-output":true,"collapsed":true,"jupyter":{"outputs_hidden":true},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Je télécharge ma donnée et la regarde en détail","metadata":{}},{"cell_type":"code","source":"labels_csv = pd.read_csv('/kaggle/input/dog-breed-identification/labels.csv')\nprint(labels_csv.describe())\nprint(labels_csv.head())","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:20.494307Z","iopub.execute_input":"2021-06-09T11:45:20.494637Z","iopub.status.idle":"2021-06-09T11:45:20.571687Z","shell.execute_reply.started":"2021-06-09T11:45:20.494608Z","shell.execute_reply":"2021-06-09T11:45:20.570672Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Je peux voir que j'ai 120 races de chien pour 10 222 photos. ","metadata":{}},{"cell_type":"code","source":"labels_csv[\"breed\"].value_counts().plot.bar(figsize=(20, 5));","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:20.573322Z","iopub.execute_input":"2021-06-09T11:45:20.57364Z","iopub.status.idle":"2021-06-09T11:45:22.606691Z","shell.execute_reply.started":"2021-06-09T11:45:20.573607Z","shell.execute_reply":"2021-06-09T11:45:22.605488Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Dans le schéma ci-dessus, on peut observer qu'il y a plus de 60 images pour chaque race de chien. C'est une bonne quantité, car pour certains de leurs produits de vision, Google recommande un minimum de 10 images par classe pour commencer. ","metadata":{}},{"cell_type":"markdown","source":"Il est temps de récupérer les images et leurs étiquettes\n\nPuisque nous avons les ID des images et leurs étiquettes dans un DataFrame (labels_csv), nous allons l'utiliser pour créer :\n\n* 1) Une liste de chemins d'accès aux images d'entraînement\n* 2) Un tableau de toutes les étiquettes\n* 3) Un tableau de toutes les étiquettes uniques\n\nNous allons seulement créer une liste de chemins d'accès aux images plutôt que de les importer toutes pour commencer. En effet, il est beaucoup plus efficace de travailler avec des chemins de fichiers (chaînes de caractères) qu'avec des images.\n\nComme nous l'utiliserons à plusieurs reprises, nous enregistrerons le chemin d'accès à nos fichiers d'entraînement dans une variable train_path.","metadata":{}},{"cell_type":"code","source":"# Define our training file path \ntrain_path = \"../input/dog-breed-identification/train/\"","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.608752Z","iopub.execute_input":"2021-06-09T11:45:22.609205Z","iopub.status.idle":"2021-06-09T11:45:22.613915Z","shell.execute_reply.started":"2021-06-09T11:45:22.609149Z","shell.execute_reply":"2021-06-09T11:45:22.612732Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create pathnames from image ID's\nfilenames = [train_path + fname + \".jpg\" for fname in labels_csv[\"id\"]]\n\n# Check the first 10 filenames\nfilenames[:10]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.615485Z","iopub.execute_input":"2021-06-09T11:45:22.615896Z","iopub.status.idle":"2021-06-09T11:45:22.633119Z","shell.execute_reply.started":"2021-06-09T11:45:22.615854Z","shell.execute_reply":"2021-06-09T11:45:22.632098Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Maintenant que nous avons une liste de tous les noms de fichiers de la colonne ID de labels_csv, nous pouvons la comparer au nombre de fichiers dans notre répertoire de données d'entraînement pour voir s'ils correspondent.","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:44:34.703357Z","iopub.execute_input":"2021-06-09T08:44:34.703941Z","iopub.status.idle":"2021-06-09T08:44:34.710065Z","shell.execute_reply.started":"2021-06-09T08:44:34.703893Z","shell.execute_reply":"2021-06-09T08:44:34.708592Z"}}},{"cell_type":"code","source":"if len(os.listdir('/kaggle/input/dog-breed-identification/train/')) == len(filenames):\n    print('Number of file matches number of actual images!')\nelse:\n    print('Number of file doesnot matches number of actual images!!')","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.634419Z","iopub.execute_input":"2021-06-09T11:45:22.634798Z","iopub.status.idle":"2021-06-09T11:45:22.652515Z","shell.execute_reply.started":"2021-06-09T11:45:22.634758Z","shell.execute_reply":"2021-06-09T11:45:22.6515Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Je retrouve une image par son id\nlabels_csv['breed'][900]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.654051Z","iopub.execute_input":"2021-06-09T11:45:22.654361Z","iopub.status.idle":"2021-06-09T11:45:22.670449Z","shell.execute_reply.started":"2021-06-09T11:45:22.654331Z","shell.execute_reply":"2021-06-09T11:45:22.669482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Je regarde l'image directement\nfrom IPython.display import display, Image\nImage(filenames[900])","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.673387Z","iopub.execute_input":"2021-06-09T11:45:22.673832Z","iopub.status.idle":"2021-06-09T11:45:22.688943Z","shell.execute_reply.started":"2021-06-09T11:45:22.6738Z","shell.execute_reply":"2021-06-09T11:45:22.687938Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Maintenant que nous avons rassemblé les chemins de fichiers des images, récupérons les étiquettes.\nNous allons les prendre dans labels_csv et les transformer en tableau NumPy.","metadata":{"execution":{"iopub.status.busy":"2021-06-09T08:34:03.103338Z","iopub.status.idle":"2021-06-09T08:34:03.103734Z"}}},{"cell_type":"code","source":"import numpy as np\nlabels = labels_csv[\"breed\"].to_numpy() # convert labels column to NumPy array\nlabels[:10]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.691269Z","iopub.execute_input":"2021-06-09T11:45:22.691712Z","iopub.status.idle":"2021-06-09T11:45:22.698888Z","shell.execute_reply.started":"2021-06-09T11:45:22.69168Z","shell.execute_reply":"2021-06-09T11:45:22.697762Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Comparons le nombre d'étiquettes au nombre de noms de fichiers.","metadata":{}},{"cell_type":"code","source":"if len(labels) == len(filenames):\n    print('Number of labels matches the number of filenames.')\nelse:\n    print('Number of labels doesnot matches the number of filenames')","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.700238Z","iopub.execute_input":"2021-06-09T11:45:22.700594Z","iopub.status.idle":"2021-06-09T11:45:22.718927Z","shell.execute_reply.started":"2021-06-09T11:45:22.700559Z","shell.execute_reply":"2021-06-09T11:45:22.717673Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Si tout a fonctionné, nous devrions avoir la même quantité d'images et d'étiquettes.\n\nEnfin, comme un modèle d'apprentissage automatique ne peut pas prendre de chaînes de caractères en entrée (ce que sont les étiquettes actuellement), nous devons **convertir nos étiquettes en chiffres.**\n\nPour commencer, nous allons trouver tous les **noms uniques de races de chiens.**\n\nEnsuite, nous allons **parcourir la liste des étiquettes, les comparer aux races uniques** et **créer une liste de booléens** indiquant laquelle est l'étiquette réelle (Vrai) et lesquelles ne le sont pas (Faux).","metadata":{}},{"cell_type":"code","source":"unique_breed = np.unique(labels) \nlen(unique_breed)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.720717Z","iopub.execute_input":"2021-06-09T11:45:22.721184Z","iopub.status.idle":"2021-06-09T11:45:22.741034Z","shell.execute_reply.started":"2021-06-09T11:45:22.721139Z","shell.execute_reply":"2021-06-09T11:45:22.740288Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"La longueur de unique_breeds doit être de 120, ce qui signifie que nous travaillons avec des images de 120 races de chiens différentes.\n\nUtilisez maintenant unique_breeds pour transformer notre tableau de labels en un tableau de booléens.","metadata":{}},{"cell_type":"code","source":"#exemple d'une étiquette transformée en tableau de boléens\nprint(labels[0])\nlabels[0] == unique_breed","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.742135Z","iopub.execute_input":"2021-06-09T11:45:22.742417Z","iopub.status.idle":"2021-06-09T11:45:22.75682Z","shell.execute_reply.started":"2021-06-09T11:45:22.74239Z","shell.execute_reply":"2021-06-09T11:45:22.755702Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Turn every label into a boolean array\nboolean_labels = [label == np.array(unique_breed) for label in labels]\nboolean_labels[:2]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.758101Z","iopub.execute_input":"2021-06-09T11:45:22.758617Z","iopub.status.idle":"2021-06-09T11:45:22.87229Z","shell.execute_reply.started":"2021-06-09T11:45:22.758579Z","shell.execute_reply":"2021-06-09T11:45:22.871312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"un concept important de l'apprentissage automatique consiste à convertir vos données en chiffres avant de les transmettre à un modèle d'apprentissage automatique.\n\nDans ce cas, nous avons transformé un seul nom de race de chien, tel que boston_bull, en un tableau de chiffres sur le principe du One Hot Encoder.","metadata":{}},{"cell_type":"code","source":"# Turining boolean arrays into integers.\nprint(labels[0])   #orginal index\nprint(np.where(unique_breed==labels[0]))    #index where labels occurs.\nprint(boolean_labels[0].argmax())     #index where label occurs in boolean array\nprint(boolean_labels[0].astype(int))   #there will be a 1 where sample label occurs","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.873533Z","iopub.execute_input":"2021-06-09T11:45:22.873832Z","iopub.status.idle":"2021-06-09T11:45:22.881381Z","shell.execute_reply.started":"2021-06-09T11:45:22.873803Z","shell.execute_reply":"2021-06-09T11:45:22.880194Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Maintenant que nous avons nos étiquettes au format numérique et que les chemins de fichier de nos images sont facilement accessibles (ils ne sont pas encore numériques), nous allons diviser nos données.","metadata":{}},{"cell_type":"markdown","source":"# Création de notre propre ensemble de validation","metadata":{}},{"cell_type":"markdown","source":"Puisque l'ensemble de données de Kaggle n'est pas fourni avec un ensemble de validation (une division des données sur laquelle nous pouvons tester notre modèle avant de faire des prédictions finales sur l'ensemble de test), nous allons en créer un.\n\nNous pouvons utiliser la fonction train_test_split de Scikit-Learn ou nous pouvons simplement faire des divisions manuelles des données.\n\nPour des raisons d'accessibilité ultérieure, sauvegardons nos noms de fichiers en X (données) et nos étiquettes en y.","metadata":{}},{"cell_type":"code","source":"# Setup X & y variables\nX = filenames\ny = boolean_labels\n\nprint(f\"Number of training images: {len(X)}\")\nprint(f\"Number of labels: {len(y)}\")","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.882712Z","iopub.execute_input":"2021-06-09T11:45:22.883051Z","iopub.status.idle":"2021-06-09T11:45:22.897999Z","shell.execute_reply.started":"2021-06-09T11:45:22.883018Z","shell.execute_reply":"2021-06-09T11:45:22.896983Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Étant donné que nous travaillons avec plus de 10 000 images, il est préférable de travailler avec une partie d'entre elles pour s'assurer que tout fonctionne bien avant de s'entraîner sur toutes les images.\n\nEn effet, le calcul de plus de 10 000 images peut prendre beaucoup de temps. Et notre objectif, lorsque nous travaillons sur des projets d'apprentissage automatique, est de réduire le temps entre les expériences.\n\nCommençons à expérimenter avec 1000 et augmentons ce nombre selon nos besoins.","metadata":{}},{"cell_type":"code","source":"#set number of images to set for the experiment.\nnum_img = 1000 #@param {type:\"slider\",min:1000,max:10000}","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.899282Z","iopub.execute_input":"2021-06-09T11:45:22.899716Z","iopub.status.idle":"2021-06-09T11:45:22.910042Z","shell.execute_reply.started":"2021-06-09T11:45:22.899684Z","shell.execute_reply":"2021-06-09T11:45:22.909207Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Divisons maintenant nos données en ensembles de formation et de validation. Nous utiliserons une répartition 80/20 (80% de données de formation, 20% de données de validation).","metadata":{}},{"cell_type":"code","source":"#let's split our data into train and validation.\nfrom sklearn.model_selection import train_test_split\n\n#spliting into training and validation of total size NUM_IMAGES.\n\nX_train,X_val,y_train,y_val = train_test_split(X[:num_img],\n                                                y[:num_img],\n                                                test_size=0.2,\n                                                random_state=42)\nlen(X_train),len(X_val),len(y_train),len(y_val)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.911275Z","iopub.execute_input":"2021-06-09T11:45:22.911566Z","iopub.status.idle":"2021-06-09T11:45:22.930053Z","shell.execute_reply.started":"2021-06-09T11:45:22.911539Z","shell.execute_reply":"2021-06-09T11:45:22.929045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check out the training data (image file paths and labels)\nX_train[:1], y_train[:1]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.931577Z","iopub.execute_input":"2021-06-09T11:45:22.93214Z","iopub.status.idle":"2021-06-09T11:45:22.946409Z","shell.execute_reply.started":"2021-06-09T11:45:22.9321Z","shell.execute_reply":"2021-06-09T11:45:22.945359Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prétraitement des images (transformation des images en tenseurs)","metadata":{}},{"cell_type":"markdown","source":"\nNos étiquettes sont au format numérique mais nos images ne sont encore que des chemins de fichiers.\n\nComme nous utilisons TensorFlow, **nos données doivent être sous la forme de tenseurs.**\n\nUn tenseur est un moyen de **représenter des informations sous forme de nombres.** .Un tenseur peut être considéré comme une combinaison de tableaux NumPy, mais avec la capacité spéciale d'être utilisé sur un GPU.\n\nEn raison de la façon dont TensorFlow stocke les informations (dans les tenseurs), il permet aux modèles d'apprentissage automatique et d'apprentissage profond d'être **exécutés sur des GPU (généralement plus rapides pour le calcul numérique).**\n\nPour **prétraiter nos images en Tensors**, nous allons écrire une fonction qui fait plusieurs choses :\n\n* Prend un nom de fichier d'image en entrée.\n* Utilise TensorFlow pour lire le fichier et l'enregistrer dans une variable, image.\n* Transformer notre image (un fichier jpeg) en tenseurs.\n* Redimensionne l'image pour qu'elle soit de forme (224, 224).\n* Retourner l'image modifiée.\n\nUn bon endroit pour lire sur ce type de fonction est la documentation TensorFlow sur le **chargement des images.**\n\nVous vous demandez peut-être pourquoi **(224, 224)**, qui correspond à **(hauteur, largeur)**. C'est parce que c'est la taille de l'entrée que notre modèle (nous le verrons bientôt) prend, une image qui est (224, 224, 3).\n\n**Le 3, c'est le nombre de canaux de couleur par pixel, rouge, vert et bleu (RVB).**\n\nRendons cela un peu plus concret.","metadata":{}},{"cell_type":"code","source":"# Convert image to NumPy array\nfrom matplotlib.pyplot import imread\nimage = imread(filenames[42]) # read in an image\nimage.shape","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.947564Z","iopub.execute_input":"2021-06-09T11:45:22.948056Z","iopub.status.idle":"2021-06-09T11:45:22.967233Z","shell.execute_reply.started":"2021-06-09T11:45:22.948013Z","shell.execute_reply":"2021-06-09T11:45:22.966163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Remarquez la forme de l'image. C'est (257, 350, 3). C'est la hauteur, la largeur, la valeur du canal de couleur.\n\nEt vous pouvez facilement le convertir en un tenseur en utilisant tf.constant().","metadata":{}},{"cell_type":"code","source":"tf.constant(image)[:2]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.968833Z","iopub.execute_input":"2021-06-09T11:45:22.96917Z","iopub.status.idle":"2021-06-09T11:45:22.977768Z","shell.execute_reply.started":"2021-06-09T11:45:22.969133Z","shell.execute_reply":"2021-06-09T11:45:22.977023Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Define image size\nIMG_SIZE = 224\n\ndef process_image(image_path):\n  \"\"\"\n  Takes an image file path and turns it into a Tensor.\n  \"\"\"\n  # Read in image file\n  image = tf.io.read_file(image_path)\n  # Turn the jpeg image into numerical Tensor with 3 colour channels (Red, Green, Blue)\n  image = tf.image.decode_jpeg(image, channels=3)\n  # Convert the colour channel values from 0-225 values to 0-1 values\n  image = tf.image.convert_image_dtype(image, tf.float32)\n  # Resize the image to our desired size (224, 244)\n  image = tf.image.resize(image, size=[IMG_SIZE, IMG_SIZE])\n  return image","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.978876Z","iopub.execute_input":"2021-06-09T11:45:22.979367Z","iopub.status.idle":"2021-06-09T11:45:22.989494Z","shell.execute_reply.started":"2021-06-09T11:45:22.979335Z","shell.execute_reply":"2021-06-09T11:45:22.988458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create a simple function to return a tuple (image, label)\ndef get_image_label(image_path, label):\n  \"\"\"\n  Takes an image file path name and the associated label,\n  processes the image and returns a tuple of (image, label).\n  \"\"\"\n  image = process_image(image_path)\n  return image, label","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:22.991221Z","iopub.execute_input":"2021-06-09T11:45:22.991692Z","iopub.status.idle":"2021-06-09T11:45:23.011052Z","shell.execute_reply.started":"2021-06-09T11:45:22.991649Z","shell.execute_reply":"2021-06-09T11:45:23.010133Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Création de lots de données\nMaintenant que nous avons une fonction pour convertir nos images en tenseurs, nous allons en construire une fonction pour **transformer nos données en lots** (un TensorFlow BatchDataset).\n\nQu'est-ce qu'un batch ?\n\nUn lot (également appelé mini-batch) est une petite portion de vos données, disons 32 (32 est généralement la taille de lot par défaut) images et leurs étiquettes. En apprentissage profond, au lieu de trouver des modèles dans un ensemble de données entier en même temps, vous les trouvez souvent un lot à la fois.\n\nDisons que vous avez affaire à plus de 10 000 images (ce qui est le cas). Ensemble, ces fichiers peuvent occuper plus de mémoire que votre GPU. Essayer de les calculer tous entraînerait une erreur.\n\nIl est donc plus efficace de  **créer des lots plus petits de vos données et de les calculer un par un.**\n\nTensorFlow est très efficace lorsque vos données sont des lots de tenseurs (image, étiquette). Nous allons donc construire une fonction pour les créer en premier. Nous allons profiter de la fonction process_image en même temps.","metadata":{}},{"cell_type":"code","source":"# Create a simple function to return a tuple (image, label)\ndef get_image_label(image_path, label):\n  \"\"\"\n  Takes an image file path name and the associated label,\n  processes the image and returns a tuple of (image, label).\n  \"\"\"\n  image = process_image(image_path)\n  return image, label","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:23.014247Z","iopub.execute_input":"2021-06-09T11:45:23.014764Z","iopub.status.idle":"2021-06-09T11:45:23.02631Z","shell.execute_reply.started":"2021-06-09T11:45:23.014715Z","shell.execute_reply":"2021-06-09T11:45:23.025313Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Maintenant que nous disposons d'une fonction simple pour transformer les noms des chemins d'accès aux fichiers d'images et les étiquettes associées en tuples (nous les transformerons ensuite en tenseurs), nous allons créer une fonction pour créer des lots de données.\n\nPuisque nous aurons affaire à trois ensembles de données différents (formation, validation et test), nous nous assurerons que la fonction peut s'adapter à chaque ensemble.\n\nNous définirons une taille de lot par défaut de 32.","metadata":{}},{"cell_type":"code","source":"# Define the batch size, 32 is a good default\nBATCH_SIZE = 32\n\n# Create a function to turn data into batches\ndef create_data_batches(x, y=None, batch_size=BATCH_SIZE, valid_data=False, test_data=False):\n  \"\"\"\n  Creates batches of data out of image (x) and label (y) pairs.\n  Shuffles the data if it's training data but doesn't shuffle it if it's validation data.\n  Also accepts test data as input (no labels).\n  \"\"\"\n  # If the data is a test dataset, we probably don't have labels\n  if test_data:\n    print(\"Creating test data batches...\")\n    data = tf.data.Dataset.from_tensor_slices((tf.constant(x))) # only filepaths\n    data_batch = data.map(process_image).batch(BATCH_SIZE)\n    return data_batch\n  \n  # If the data if a valid dataset, we don't need to shuffle it\n  elif valid_data:\n    print(\"Creating validation data batches...\")\n    data = tf.data.Dataset.from_tensor_slices((tf.constant(x), # filepaths\n                                               tf.constant(y))) # labels\n    data_batch = data.map(get_image_label).batch(BATCH_SIZE)\n    return data_batch\n\n  else:\n    # If the data is a training dataset, we shuffle it\n    print(\"Creating training data batches...\")\n    # Turn filepaths and labels into Tensors\n    data = tf.data.Dataset.from_tensor_slices((tf.constant(x), # filepaths\n                                              tf.constant(y))) # labels\n    \n    # Shuffling pathnames and labels before mapping image processor function is faster than shuffling images\n    data = data.shuffle(buffer_size=len(x))\n\n    # Create (image, label) tuples (this also turns the image path into a preprocessed image)\n    data = data.map(get_image_label)\n\n    # Turn the data into batches\n    data_batch = data.batch(BATCH_SIZE)\n  return data_batch","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:23.027891Z","iopub.execute_input":"2021-06-09T11:45:23.028379Z","iopub.status.idle":"2021-06-09T11:45:23.04141Z","shell.execute_reply.started":"2021-06-09T11:45:23.028335Z","shell.execute_reply":"2021-06-09T11:45:23.040244Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Create training and validation data batches\ntrain_data = create_data_batches(X_train, y_train)\nval_data = create_data_batches(X_val, y_val, valid_data=True)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:23.042922Z","iopub.execute_input":"2021-06-09T11:45:23.04323Z","iopub.status.idle":"2021-06-09T11:45:23.164498Z","shell.execute_reply.started":"2021-06-09T11:45:23.043201Z","shell.execute_reply":"2021-06-09T11:45:23.163695Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check out the different attributes of our data batches\ntrain_data.element_spec, val_data.element_spec","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:45:23.165788Z","iopub.execute_input":"2021-06-09T11:45:23.166106Z","iopub.status.idle":"2021-06-09T11:45:23.171367Z","shell.execute_reply.started":"2021-06-09T11:45:23.166075Z","shell.execute_reply":"2021-06-09T11:45:23.170463Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Nous avons nos données en lots, plus précisément, elles sont dans des paires Tensor de (images, étiquettes) prêtes à être utilisées sur un GPU.","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:46:43.227094Z","iopub.execute_input":"2021-06-09T11:46:43.227481Z","iopub.status.idle":"2021-06-09T11:46:43.235008Z","shell.execute_reply.started":"2021-06-09T11:46:43.22745Z","shell.execute_reply":"2021-06-09T11:46:43.233241Z"}}},{"cell_type":"markdown","source":"# Visualisation des lots de données","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Create a function for viewing images in a data batch\ndef show_25_images(images, labels):\n  \"\"\"\n  Displays 25 images from a data batch.\n  \"\"\"\n  # Setup the figure\n  plt.figure(figsize=(10, 10))\n  # Loop through 25 (for displaying 25 images)\n  for i in range(25):\n    # Create subplots (5 rows, 5 columns)\n    ax = plt.subplot(5, 5, i+1)\n    # Display an image\n    plt.imshow(images[i])\n    # Add the image label as the title\n    plt.title(unique_breed[labels[i].argmax()])\n    # Turn gird lines off\n    plt.axis(\"off\")","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:47:33.994503Z","iopub.execute_input":"2021-06-09T11:47:33.994862Z","iopub.status.idle":"2021-06-09T11:47:34.001069Z","shell.execute_reply.started":"2021-06-09T11:47:33.994829Z","shell.execute_reply":"2021-06-09T11:47:33.999896Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Pour rendre le calcul efficace, un lot est une collection de tenseurs fortement enroulée.\nAinsi, pour visualiser les données d'un lot, nous devons le dérouler. Nous pouvons le faire en appelant la méthode as_numpy_iterator() sur un lot de données. Cela transformera notre lot de données en quelque chose qui peut être itéré. Le passage d'un itérable à next() renverra l'élément suivant dans l'itérateur. Dans notre cas, next renverra un lot de 32 images et paires d'étiquettes.","metadata":{}},{"cell_type":"code","source":"# Visualize training images from the training data batch\ntrain_images, train_labels = next(train_data.as_numpy_iterator())\nshow_25_images(train_images, train_labels)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:47:38.600209Z","iopub.execute_input":"2021-06-09T11:47:38.600791Z","iopub.status.idle":"2021-06-09T11:47:40.677877Z","shell.execute_reply.started":"2021-06-09T11:47:38.600755Z","shell.execute_reply":"2021-06-09T11:47:40.6768Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Visualize validation images from the validation data batch\nval_images, val_labels = next(val_data.as_numpy_iterator())\nshow_25_images(val_images, val_labels)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:53:50.070323Z","iopub.execute_input":"2021-06-09T11:53:50.070706Z","iopub.status.idle":"2021-06-09T11:53:51.718471Z","shell.execute_reply.started":"2021-06-09T11:53:50.070671Z","shell.execute_reply":"2021-06-09T11:53:51.717476Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Création et formation d'un modèle\n\nMaintenant que nos données sont prêtes, préparons leur modélisation. Nous allons utiliser un modèle existant de TensorFlow Hub.\n\nTensorFlow Hub est une ressource où vous pouvez trouver des modèles d'apprentissage automatique pré-entraînés pour le problème sur lequel vous travaillez.\nL'utilisation d'un modèle d'apprentissage automatique pré-entraîné est souvent appelée apprentissage par transfert.\n\nPourquoi utiliser un modèle pré-entraîné ?\nConstruire un modèle d'apprentissage automatique et l'entraîner sur des lots à partir de zéro peut être coûteux et prendre du temps. L'apprentissage par transfert permet d'éliminer certains de ces inconvénients en prenant ce qu'un autre modèle a appris et en utilisant ces informations pour votre propre problème.\n\n# Comment choisir un modèle ?\n\nPuisque nous savons que notre problème est la classification d'images (classification de différentes races de chiens), nous pouvons naviguer sur la page du Hub TensorFlow en fonction de notre domaine de problème (image). Nous commençons par choisir le domaine problématique de l'image, puis nous pouvons le filtrer par sous-domaines, dans notre cas, la classification d'images.\n\nCe faisant, nous obtenons une liste de différents modèles pré-entraînés que nous pouvons appliquer à notre tâche. En cliquant sur l'un d'eux, nous obtenons des informations sur le modèle ainsi que des instructions pour l'utiliser.\n\nPar exemple, en cliquant sur le modèle mobilenet_v2_130_224, nous apprenons que ce modèle prend en entrée des images de la forme 224, 224. Il est également indiqué que le modèle a été formé dans le domaine de la classification d'images.\n\n# Construction d'un modèle\n\nAvant de construire un modèle, nous devons définir quelques éléments :\n\nLa forme d'entrée (images, sous la forme de tenseurs) de notre modèle.\nLa forme de sortie (étiquettes d'images, sous forme de tenseurs) de notre modèle.\nL'URL du modèle que nous voulons utiliser.\nCes éléments constituent une pratique standard, quel que soit le modèle d'apprentissage automatique que vous utilisez. Et comme nous utilisons TensorFlow, tout sera sous la forme de tenseurs.\n\nRemarque : pour utiliser les URL de TensorFlow Hub sur Kaggle, vous devez activer \"internet\" dans les paramètres du noyau.","metadata":{}},{"cell_type":"code","source":"# Setup input shape to the model\nINPUT_SHAPE = [None, IMG_SIZE, IMG_SIZE, 3] # batch, height, width, colour channels\n\n# Setup output shape of the model\nOUTPUT_SHAPE = len(unique_breed) # number of unique labels\n\n# Setup model URL from TensorFlow Hub\nMODEL_URL = \"https://tfhub.dev/google/imagenet/mobilenet_v2_130_224/classification/4\"","metadata":{"execution":{"iopub.status.busy":"2021-06-09T11:57:30.551671Z","iopub.execute_input":"2021-06-09T11:57:30.552217Z","iopub.status.idle":"2021-06-09T11:57:30.5571Z","shell.execute_reply.started":"2021-06-09T11:57:30.552183Z","shell.execute_reply":"2021-06-09T11:57:30.556031Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data augmentation","metadata":{}},{"cell_type":"markdown","source":"https://www.tensorflow.org/tutorials/images/data_augmentation  - \nhttps://towardsdatascience.com/exploring-image-data-augmentation-with-keras-and-tensorflow-a8162d89b844","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import layers\n\ndata_augmentation = tf.keras.Sequential([\n  layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n  layers.experimental.preprocessing.RandomRotation(0.2),\n])","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:39:27.007069Z","iopub.execute_input":"2021-06-09T13:39:27.007446Z","iopub.status.idle":"2021-06-09T13:39:27.030843Z","shell.execute_reply.started":"2021-06-09T13:39:27.007416Z","shell.execute_reply":"2021-06-09T13:39:27.02965Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"resize_and_rescale = tf.keras.Sequential([\n  layers.experimental.preprocessing.Resizing(IMG_SIZE, IMG_SIZE),\n  layers.experimental.preprocessing.Rescaling(1./255)\n])","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:39:31.446279Z","iopub.execute_input":"2021-06-09T13:39:31.44682Z","iopub.status.idle":"2021-06-09T13:39:31.461101Z","shell.execute_reply.started":"2021-06-09T13:39:31.446769Z","shell.execute_reply":"2021-06-09T13:39:31.460226Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"#  Construction du  modèle (suite)","metadata":{}},{"cell_type":"markdown","source":"Maintenant, nous avons les entrées, les sorties et le modèle que nous utilisons prêts à être utilisés. Nous pouvons commencer à les assembler\n\nIl existe de nombreuses façons de construire un modèle dans TensorFlow, mais l'une des meilleures façons de commencer est d'utiliser l'API Keras.\n\nDéfinir un modèle d'apprentissage profond dans Keras peut être aussi simple que de dire \"voici les couches du modèle, la forme d'entrée et la forme de sortie, allons-y !\".\n\nSachant cela, créons une fonction qui :\n\nPrend la forme d'entrée, la forme de sortie et l'URL du modèle que nous avons choisi comme paramètres.\nDéfinit les couches d'un modèle Keras de manière séquentielle (faire ceci d'abord, puis ceci, puis cela).\nCompile le modèle (indique comment il doit être évalué et amélioré).\nConstruit le modèle (lui indique le type de forme d'entrée qu'il recevra).\nRenvoie le modèle.\n","metadata":{}},{"cell_type":"code","source":"# Create a function which builds a Keras model\ndef create_model(input_shape=INPUT_SHAPE, output_shape=OUTPUT_SHAPE, model_url=MODEL_URL):\n  print(\"Building model with:\", MODEL_URL)\n\n  # Setup the model layers\n  model = tf.keras.Sequential([\n    resize_and_rescale,\n    data_augmentation,\n    hub.KerasLayer(MODEL_URL), # Layer 1 (input layer), TensorFlow Hub layer, requires Kaggle internet setting turned on\n    tf.keras.layers.Dense(units=OUTPUT_SHAPE, \n                          activation=\"softmax\") # Layer 2 (output layer)\n  ])\n\n  # Compile the model\n  model.compile(\n      loss=tf.keras.losses.CategoricalCrossentropy(), # Our model wants to reduce this (how wrong its guesses are)\n      optimizer=tf.keras.optimizers.Adam(), # A friend telling our model how to improve its guesses\n      metrics=[\"accuracy\"] # We'd like this to go up\n  )\n\n  # Build the model\n  model.build(INPUT_SHAPE) # Let the model know what kind of inputs it'll be getting\n  \n  return model","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:40:48.084439Z","iopub.execute_input":"2021-06-09T13:40:48.085002Z","iopub.status.idle":"2021-06-09T13:40:48.093041Z","shell.execute_reply.started":"2021-06-09T13:40:48.084963Z","shell.execute_reply":"2021-06-09T13:40:48.092325Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Configuration des couches du modèle\n\nIl y a deux façons de faire cela dans Keras, l'API fonctionnelle et séquentielle. Nous avons utilisé l'API séquentielle.\n\nLaquelle devriez-vous utiliser ?\n\nLa documentation de Keras indique que l'API fonctionnelle est la meilleure solution pour définir des modèles complexes, mais l'API séquentielle (une pile linéaire de couches) est parfaitement adaptée pour commencer, ce que nous faisons.\n\nLa première couche que nous utilisons est le modèle de TensorFlow Hub (hub.KerasLayer(MODEL_URL). Notre première couche est donc en fait un modèle entier (plusieurs autres couches). Cette couche d'entrée prend nos images et y trouve des modèles basés sur les modèles que mobilenet_v2_130_224 a trouvés.\n\nLa couche suivante (tf.keras.layers.Dense()) est la couche de sortie de notre modèle. Elle rassemble toutes les informations découvertes dans la couche d'entrée et les restitue sous la forme que nous recherchons, 120 (le nombre d'étiquettes uniques que nous avons).\n\nLe paramètre activation=\"softmax\" indique à la couche de sortie que nous aimerions attribuer une valeur de probabilité à chacune des 120 étiquettes, quelque part entre 0 et 1. Plus la valeur est élevée, plus le modèle pense que l'image d'entrée devrait avoir cette étiquette. Si nous travaillions sur un problème de classification binaire, nous utiliserions la fonction d'activation \"sigmoïde\".\n\n# Compilation du modèle\n\nLa meilleure façon de l'expliquer est de raconter une histoire.\n\nDisons que vous participez aux championnats internationaux de descente de collines. Vous commencez en haut d'une colline et votre objectif est d'arriver en bas de la colline. Le problème est que vous avez les yeux bandés.\n\nHeureusement, votre ami Adam est en bas de la colline et vous donne des instructions pour descendre.\n\nEn bas de la colline, il y a un juge qui évalue vos progrès. Il sait où tu dois arriver, alors il compare ta performance à celle que tu es censé avoir. Leur comparaison est la façon dont vous êtes noté.\n\nTransférer ceci à la terminologie de **model.compile()** :\n\n**loss** - La hauteur de la colline est la fonction de perte, l'objectif du modèle est de la minimiser, arriver à 0 (le bas de la colline) signifie que le modèle apprend parfaitement.\n\n**Optimiseur** - Votre ami Adam est l'optimiseur, c'est lui qui vous dit comment franchir la colline (réduire la fonction de perte) en fonction de ce que vous avez fait jusqu'à présent. Il s'appelle Adam car l'optimiseur Adam est un grand général qui donne de bons résultats sur la plupart des modèles. D'autres optimiseurs incluent RMSprop et Stochastic Gradient Descent.\n\n**métriques** - C'est le spectateur en bas de la colline qui évalue vos performances. Dans notre cas, il s'agit de la précision avec laquelle notre modèle prédit l'étiquette correcte de l'image.\nConstruction du modèle\n\nNous utilisons **model.build()** chaque fois que nous utilisons une couche de TensorFlow Hub pour indiquer à notre modèle la forme d'entrée à laquelle il peut s'attendre.\n\nDans ce cas, la forme d'entrée est [None, IMG_SIZE, IMG_SIZE, 3] ou [None, 224, 224, 3] ou [batch_size, img_height, img_width, color_channels].\n\nLa taille du lot est laissée à None car elle est déduite des données que nous transmettons au modèle. Dans notre cas, elle sera de 32 puisque c'est ce que nous avons configuré pour nos lots de données.\n\nMaintenant que nous avons parcouru chaque section de la fonction, utilisons-la pour créer un modèle.\n\nNous pouvons appeler **summary()** sur notre modèle pour avoir une idée de ce à quoi il ressemble.","metadata":{}},{"cell_type":"code","source":"# Create a model and check its details\nmodel = create_model()\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:40:53.726312Z","iopub.execute_input":"2021-06-09T13:40:53.726894Z","iopub.status.idle":"2021-06-09T13:40:55.979114Z","shell.execute_reply.started":"2021-06-09T13:40:53.72685Z","shell.execute_reply":"2021-06-09T13:40:55.977918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Les paramètres **non entraînables sont les modèles appris par mobilenet_v2_130_224** et les **paramètres entraînables sont ceux de la couche dense** que nous avons ajoutée.\n\nCela signifie que la majeure partie de l'information dans notre modèle a déjà été apprise et nous allons la prendre et l'adapter à notre propre problème.\n\n# Création de callbacks (choses pour aider notre modèle)\nNous avons un modèle prêt à l'emploi, mais avant de l'entraîner, nous devons créer quelques fonctions de rappel.\n\nLes callbacks sont des fonctions d'aide qu'un modèle peut utiliser pendant la formation pour effectuer des opérations telles que la sauvegarde de la progression d'un modèle, la vérification de la progression d'un modèle ou l'arrêt anticipé de la formation si le modèle ne s'améliore plus.\n\nLes deux callbacks que nous allons ajouter sont un callback TensorBoard et un callback d'arrêt précoce.\n\n*Rappel de TensorBoard*\nTensorBoard permet de fournir un moyen visuel de **surveiller la progression de votre modèle pendant et après la formation.**\n\nIl peut être utilisé directement dans un carnet de notes pour suivre les mesures de performance d'un modèle telles que la perte et la précision.\n\nPour configurer un callback TensorBoard et afficher TensorBoard dans un notebook, nous devons faire trois choses :\n\n* Charger l'extension de notebook TensorBoard.\n* Créer un callback TensorBoard capable d'enregistrer les logs dans un répertoire et de les transmettre à la fonction fit() de notre modèle.\n* Visualiser les logs d'entraînement de notre modèle en utilisant la fonction magique %tensorboard (nous le ferons plus tard).","metadata":{}},{"cell_type":"code","source":"# Load the TensorBoard notebook extension\n%load_ext tensorboard","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:00.260489Z","iopub.execute_input":"2021-06-09T13:41:00.260919Z","iopub.status.idle":"2021-06-09T13:41:00.267846Z","shell.execute_reply.started":"2021-06-09T13:41:00.260865Z","shell.execute_reply":"2021-06-09T13:41:00.266683Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import datetime\n\n# Create a function to build a TensorBoard callback\ndef create_tensorboard_callback():\n  # Create a log directory for storing TensorBoard logs\n  logdir = os.path.join(\"drive/My Drive/Data/logs\",\n                        # Make it so the logs get tracked whenever we run an experiment\n                        datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\"))\n  return tf.keras.callbacks.TensorBoard(logdir)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:02.523522Z","iopub.execute_input":"2021-06-09T13:41:02.523914Z","iopub.status.idle":"2021-06-09T13:41:02.528942Z","shell.execute_reply.started":"2021-06-09T13:41:02.523868Z","shell.execute_reply":"2021-06-09T13:41:02.527733Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Rappel d'arrêt anticipé\n\nL'arrêt précoce permet d'**éviter le surajustement en arrêtant un modèle lorsqu'une certaine métrique d'évaluation cesse de s'améliorer.** Si un modèle s'entraîne trop longtemps, il peut réussir à trouver des modèles dans un certain ensemble de données et ne pas être capable d'utiliser ces modèles dans un autre ensemble de données qu'il n'a pas vu auparavant (il ne peut pas généraliser).\n\nCela revient à dire à notre modèle : \"continuez à trouver des modèles jusqu'à ce que la qualité de ces modèles commence à diminuer\".","metadata":{}},{"cell_type":"code","source":"# Create early stopping (once our model stops improving, stop training)\nearly_stopping = tf.keras.callbacks.EarlyStopping(monitor=\"val_accuracy\",\n                                                  patience=3) # stops after 3 rounds of no improvements","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:05.822061Z","iopub.execute_input":"2021-06-09T13:41:05.822422Z","iopub.status.idle":"2021-06-09T13:41:05.82725Z","shell.execute_reply.started":"2021-06-09T13:41:05.822392Z","shell.execute_reply":"2021-06-09T13:41:05.826097Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Entraînement d'un modèle (sur un sous-ensemble de données)\n\nNotre premier modèle ne sera entraîné que sur 1000 images. Ou formé sur 800 images, puis validé sur 200 images, soit 1000 images au total ou environ 10% des données totales.\n\nNous faisons cela pour nous assurer que tout fonctionne. Et si c'est le cas, nous pouvons passer à la vitesse supérieure plus tard et nous entraîner sur l'ensemble des données d'entraînement.\n\nLe dernier paramètre que nous définirons avant l'entraînement est NUM_EPOCHS (également connu sous le nom de nombre d'époques).\n\n**NUM_EPOCHS** définit le nombre de passages des données que nous souhaitons que notre modèle effectue. Un passage équivaut à ce que notre modèle essaie de trouver des motifs dans chaque image de chien et de voir quels motifs sont liés à chaque étiquette.\n\nSi NUM_EPOCHS=1, le modèle ne regardera les données qu'une seule fois et obtiendra probablement de mauvais résultats car il n'aura pas la possibilité de se corriger. C'est comme si vous participiez à un championnat international de descente de colline et que votre ami Adam ne pouvait vous donner qu'une seule instruction pour descendre la colline.\n\nQuelle est la valeur de NUM_EPOCHS ?\n\nC'est difficile à dire. 10 pourrait être un bon début, mais 100 aussi. C'est l'une des raisons pour lesquelles nous avons créé un rappel d'arrêt anticipé. La configuration de l'arrêt anticipé signifie que si nous définissons NUM_EPOCHS à 100 mais que notre modèle cesse de s'améliorer après 22 époques, il arrêtera la formation.\n\nEn plus de cela, vérifions rapidement si nous utilisons toujours un GPU.","metadata":{}},{"cell_type":"code","source":"# Check again if GPU is available (otherwise computing will take a looooonnnnggggg time)\nprint(\"GPU\", \"available (OUUUUIIII!!!!)\" if tf.config.list_physical_devices(\"GPU\") else \"NONNNNNN!!!!!\")","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:09.124238Z","iopub.execute_input":"2021-06-09T13:41:09.124624Z","iopub.status.idle":"2021-06-09T13:41:09.130415Z","shell.execute_reply.started":"2021-06-09T13:41:09.124592Z","shell.execute_reply":"2021-06-09T13:41:09.129295Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# How many rounds should we get the model to look through the data?\nNUM_EPOCHS = 100","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:12.77265Z","iopub.execute_input":"2021-06-09T13:41:12.773022Z","iopub.status.idle":"2021-06-09T13:41:12.777549Z","shell.execute_reply.started":"2021-06-09T13:41:12.77299Z","shell.execute_reply":"2021-06-09T13:41:12.776113Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Créons une fonction simple qui entraîne un modèle. La fonction va :\n\n* Créer un modèle en utilisant create_model().\n* Configurer un callback TensorBoard en utilisant create_tensorboard_callback() (nous le faisons ici pour qu'il crée un répertoire de log avec la date et l'heure actuelles).\n* Appelez la fonction fit() sur notre modèle en lui passant les données d'entraînement, les données de validation, le nombre d'époques d'entraînement et les callbacks que nous souhaitons utiliser.","metadata":{}},{"cell_type":"code","source":"# Build a function to train and return a trained model\ndef train_model():\n  \"\"\"\n  Trains a given model and returns the trained version.\n  \"\"\"\n  # Create a model\n  model = create_model()\n\n  # Create new TensorBoard session everytime we train a model\n  tensorboard = create_tensorboard_callback()\n\n  # Fit the model to the data passing it the callbacks we created\n  model.fit(x=train_data,\n            epochs=NUM_EPOCHS,\n            validation_data=val_data,\n            validation_freq=1, # check validation metrics every epoch\n            callbacks=[tensorboard, early_stopping])\n  \n  return model","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:16.102646Z","iopub.execute_input":"2021-06-09T13:41:16.10302Z","iopub.status.idle":"2021-06-09T13:41:16.10914Z","shell.execute_reply.started":"2021-06-09T13:41:16.102987Z","shell.execute_reply":"2021-06-09T13:41:16.107874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fit the model to the data\nmodel = train_model()","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:41:19.559406Z","iopub.execute_input":"2021-06-09T13:41:19.559748Z","iopub.status.idle":"2021-06-09T13:45:00.509309Z","shell.execute_reply.started":"2021-06-09T13:41:19.55972Z","shell.execute_reply":"2021-06-09T13:45:00.508255Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":" Il semble que notre modèle puisse être surajusté (il obtient de bien meilleurs résultats sur l'ensemble d'apprentissage que sur l'ensemble de validation), quels sont les moyens d'empêcher le surajustement du modèle ? Indice : cela peut impliquer de chercher quelque chose comme \"comment éviter l'overfitting dans un modèle d'apprentissage profond\".\n\nRemarque : au départ, l'overfitting est une bonne chose. Cela signifie que notre modèle est en train d'apprendre quelque chose.\n\nVérification des journaux de TensorBoard\nMaintenant que notre modèle a été formé, nous pouvons visualiser ses performances en vérifiant les journaux de TensorBoard.\n\nLa fonction magique de TensorBoard (%tensorboard) accède au répertoire de logs que nous avons créé plus tôt et visualise son contenu.","metadata":{}},{"cell_type":"code","source":"%tensorboard --logdir logs","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:08:18.211762Z","iopub.execute_input":"2021-06-09T13:08:18.21216Z","iopub.status.idle":"2021-06-09T13:08:18.224461Z","shell.execute_reply.started":"2021-06-09T13:08:18.212126Z","shell.execute_reply":"2021-06-09T13:08:18.223331Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Grâce à notre callback early_stopping, le modèle a arrêté l'apprentissage après environ 26 époques (dans mon cas, le vôtre pourrait être légèrement différent). Cela est dû au fait que la précision de la validation n'a pas progressé pendant 3 époques.\n\nMais la bonne nouvelle est que nous pouvons clairement voir que notre modèle apprend quelque chose. La précision de validation est passée à 65% en seulement quelques minutes.\n\nCela signifie que si nous augmentons le nombre d'images, nous pouvons espérer voir la précision augmenter.\n\n**Avant de passer à l'échelle supérieure et de nous entraîner sur davantage de données, voyons d'autres façons d'évaluer notre modèle. En effet, bien que la précision soit un bon indicateur de l'efficacité de notre modèle**, il serait encore mieux de pouvoir le voir en action.\n\nPour faire des prédictions avec un modèle entraîné, il suffit d'appeler predict() sur celui-ci et de lui transmettre des données dans le même format que celui sur lequel le modèle a été entraîné.","metadata":{}},{"cell_type":"code","source":"# Make predictions on the validation data (not used to train on)\npredictions = model.predict(val_data, verbose=1) # verbose shows us how long there is to go\npredictions","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:08:26.098843Z","iopub.execute_input":"2021-06-09T13:08:26.099234Z","iopub.status.idle":"2021-06-09T13:08:33.653711Z","shell.execute_reply.started":"2021-06-09T13:08:26.099203Z","shell.execute_reply":"2021-06-09T13:08:33.652582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Check the shape of predictions\npredictions.shape","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:08:33.655755Z","iopub.execute_input":"2021-06-09T13:08:33.65613Z","iopub.status.idle":"2021-06-09T13:08:33.662299Z","shell.execute_reply.started":"2021-06-09T13:08:33.656099Z","shell.execute_reply":"2021-06-09T13:08:33.661162Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"La réalisation de prédictions avec notre modèle renvoie un tableau avec une valeur différente pour chaque étiquette.\n\nDans ce cas, faire des prédictions sur les données de validation (200 images) renvoie un tableau (prédictions) de tableaux, chacun contenant 120 valeurs différentes (une pour chaque race de chien unique).\n\nCes différentes valeurs sont les probabilités ou la probabilité que le modèle ait prédit qu'une certaine image était une certaine race de chien. Plus la valeur est élevée, plus le modèle pense qu'une image donnée est une race de chien spécifique.\n\nVoyons comment convertir un tableau de probabilités en une étiquette réelle.","metadata":{}},{"cell_type":"code","source":"# First prediction\nprint(predictions[0])\nprint(f\"Max value (probability of prediction): {np.max(predictions[0])}\") # the max probability value predicted by the model\nprint(f\"Sum: {np.sum(predictions[0])}\") # because we used softmax activation in our model, this will be close to 1\nprint(f\"Max index: {np.argmax(predictions[0])}\") # the index of where the max value in predictions[0] occurs\nprint(f\"Predicted label: {unique_breed[np.argmax(predictions[0])]}\") # the predicted label","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:10:15.846658Z","iopub.execute_input":"2021-06-09T13:10:15.847084Z","iopub.status.idle":"2021-06-09T13:10:15.855396Z","shell.execute_reply.started":"2021-06-09T13:10:15.847047Z","shell.execute_reply":"2021-06-09T13:10:15.854661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"C'est bien d'avoir cette information, mais ce serait encore mieux si nous pouvions comparer une prédiction à son véritable label et à l'image originale.\n\nPour nous aider, nous allons d'abord construire une petite fonction pour convertir les probabilités de prédiction en étiquettes prédites.\n\nRemarque : Les probabilités de prédiction sont également connues sous le nom de niveaux de confiance.","metadata":{}},{"cell_type":"code","source":"# Turn prediction probabilities into their respective label (easier to understand)\ndef get_pred_label(prediction_probabilities):\n  \"\"\"\n  Turns an array of prediction probabilities into a label.\n  \"\"\"\n  return unique_breed[np.argmax(prediction_probabilities)]\n\n# Get a predicted label based on an array of prediction probabilities\npred_label = get_pred_label(predictions[0])\npred_label","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:11:47.711724Z","iopub.execute_input":"2021-06-09T13:11:47.712111Z","iopub.status.idle":"2021-06-09T13:11:47.71887Z","shell.execute_reply.started":"2021-06-09T13:11:47.712073Z","shell.execute_reply":"2021-06-09T13:11:47.717772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Rappelez-vous, le modèle n'a pas été entraîné sur les données de validation, pendant la fonction fit(), il a seulement utilisé les données de validation pour s'évaluer. Nous pouvons donc utiliser les images de validation pour comparer visuellement les prédictions de notre modèle avec les étiquettes de validation.\n\nPuisque nos données de validation (val_data) sont sous forme de lot, pour obtenir une liste d'images et d'étiquettes de validation, nous devrons les débloquer (en utilisant unbatch()) et ensuite les transformer en itérateur en utilisant as_numpy_iterator().\n","metadata":{}},{"cell_type":"code","source":"# Create a function to unbatch a batched dataset\ndef unbatchify(data):\n  \"\"\"\n  Takes a batched dataset of (image, label) Tensors and returns separate arrays\n  of images and labels.\n  \"\"\"\n  images = []\n  labels = []\n  # Loop through unbatched data\n  for image, label in data.unbatch().as_numpy_iterator():\n    images.append(image)\n    labels.append(unique_breed[np.argmax(label)])\n  return images, labels\n\n# Unbatchify the validation data\nval_images, val_labels = unbatchify(val_data)\nval_images[0], val_labels[0]","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:12:49.60846Z","iopub.execute_input":"2021-06-09T13:12:49.608833Z","iopub.status.idle":"2021-06-09T13:12:50.547269Z","shell.execute_reply.started":"2021-06-09T13:12:49.608802Z","shell.execute_reply":"2021-06-09T13:12:50.546234Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Maintenant, nous avons des moyens d'obtenir :\n\n* Des étiquettes de prédiction\n* Étiquettes de validation (étiquettes de vérité)\n* Images de validation\n* Créons quelques fonctions pour rendre tout cela un peu plus visuel.\n\nPlus précisément, nous voulons être en mesure de visualiser une image, son étiquette prédite et son étiquette réelle (true label).\n\nLa première fonction que nous allons créer va :\n\n* Prendre un tableau de probabilités de prédiction, un tableau d'étiquettes réelles, un tableau d'images et un nombre entier.\n* Convertir les probabilités de prédiction en une étiquette prédite.\n* Tracer l'étiquette prédite, sa probabilité prédite, l'étiquette de vérité et l'image cible sur un seul graphique.","metadata":{}},{"cell_type":"code","source":"def plot_pred(prediction_probabilities, labels, images, n=1):\n  \"\"\"\n  View the prediction, ground truth label and image for sample n.\n  \"\"\"\n  pred_prob, true_label, image = prediction_probabilities[n], labels[n], images[n]\n  \n  # Get the pred label\n  pred_label = get_pred_label(pred_prob)\n  \n  # Plot image & remove ticks\n  plt.imshow(image)\n  plt.xticks([])\n  plt.yticks([])\n\n  # Change the color of the title depending on if the prediction is right or wrong\n  if pred_label == true_label:\n    color = \"green\"\n  else:\n    color = \"red\"\n\n  plt.title(\"{} {:2.0f}% ({})\".format(pred_label,\n                                      np.max(pred_prob)*100,\n                                      true_label),\n                                      color=color)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:14:23.435395Z","iopub.execute_input":"2021-06-09T13:14:23.435955Z","iopub.status.idle":"2021-06-09T13:14:23.442368Z","shell.execute_reply.started":"2021-06-09T13:14:23.435893Z","shell.execute_reply":"2021-06-09T13:14:23.44137Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# View an example prediction, original image and truth label\nplot_pred(prediction_probabilities=predictions,\n          labels=val_labels,\n          images=val_images)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:14:45.55898Z","iopub.execute_input":"2021-06-09T13:14:45.559569Z","iopub.status.idle":"2021-06-09T13:14:45.664563Z","shell.execute_reply.started":"2021-06-09T13:14:45.559517Z","shell.execute_reply":"2021-06-09T13:14:45.66374Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Comme nous travaillons sur un problème multi-classes (120 races de chiens différentes), il serait également intéressant de voir quelles autres prédictions notre modèle fait. Plus précisément, si notre modèle prédit une certaine étiquette avec une probabilité de 24 %, que prédit-il d'autre ?\n\nConstruisons une fonction pour le démontrer. Cette fonction va :\n**Prendre en entrée un tableau de probabilités de prédiction, un tableau d'étiquettes de vérité terrain et un nombre entier./\nTrouver l'étiquette prédite en utilisant get_pred_label()./\nTrouver les 10 premiers :\nIndices de probabilités de prédiction - \nValeurs des probabilités de prédiction - \nÉtiquettes de prédiction /. \nTracez les 10 premières valeurs de probabilité de prédiction et les étiquettes, en colorant la véritable étiquette en vert.**","metadata":{}},{"cell_type":"code","source":"def plot_pred_conf(prediction_probabilities, labels, n=1):\n  \"\"\"\n  Plots the top 10 highest prediction confidences along with\n  the truth label for sample n.\n  \"\"\"\n  pred_prob, true_label = prediction_probabilities[n], labels[n]\n\n  # Get the predicted label\n  pred_label = get_pred_label(pred_prob)\n\n  # Find the top 10 prediction confidence indexes\n  top_10_pred_indexes = pred_prob.argsort()[-10:][::-1]\n  # Find the top 10 prediction confidence values\n  top_10_pred_values = pred_prob[top_10_pred_indexes]\n  # Find the top 10 prediction labels\n  top_10_pred_labels = unique_breed[top_10_pred_indexes]\n\n  # Setup plot\n  top_plot = plt.bar(np.arange(len(top_10_pred_labels)), \n                     top_10_pred_values, \n                     color=\"grey\")\n  plt.xticks(np.arange(len(top_10_pred_labels)),\n             labels=top_10_pred_labels,\n             rotation=\"vertical\")\n\n  # Change color of true label\n  if np.isin(true_label, top_10_pred_labels):\n    top_plot[np.argmax(top_10_pred_labels == true_label)].set_color(\"green\")\n  else:\n    pass","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:19:06.878707Z","iopub.execute_input":"2021-06-09T13:19:06.879081Z","iopub.status.idle":"2021-06-09T13:19:06.887265Z","shell.execute_reply.started":"2021-06-09T13:19:06.87905Z","shell.execute_reply":"2021-06-09T13:19:06.88601Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_pred_conf(prediction_probabilities=predictions,\n               labels=val_labels,\n               n=9)","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:19:12.467117Z","iopub.execute_input":"2021-06-09T13:19:12.467504Z","iopub.status.idle":"2021-06-09T13:19:12.643481Z","shell.execute_reply.started":"2021-06-09T13:19:12.467469Z","shell.execute_reply":"2021-06-09T13:19:12.642493Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Let's check a few predictions and their different values\ni_multiplier = 0\nnum_rows = 3\nnum_cols = 2\nnum_images = num_rows*num_cols\nplt.figure(figsize=(5*2*num_cols, 5*num_rows))\nfor i in range(num_images):\n  plt.subplot(num_rows, 2*num_cols, 2*i+1)\n  plot_pred(prediction_probabilities=predictions,\n            labels=val_labels,\n            images=val_images,\n            n=i+i_multiplier)\n  plt.subplot(num_rows, 2*num_cols, 2*i+2)\n  plot_pred_conf(prediction_probabilities=predictions,\n                labels=val_labels,\n                n=i+i_multiplier)\nplt.tight_layout(h_pad=1.0)\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2021-06-09T13:19:30.528042Z","iopub.execute_input":"2021-06-09T13:19:30.528394Z","iopub.status.idle":"2021-06-09T13:19:32.435934Z","shell.execute_reply.started":"2021-06-09T13:19:30.528365Z","shell.execute_reply":"2021-06-09T13:19:32.434767Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"https://www.kaggle.com/mrdbourke/tensorflow-2-x-tensorflow-hub-end-to-end-example","metadata":{}}]}