{"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":"# Les oiseaux représentent un atout majeur dans la caractérisation des états écologoiques des \n# écosystèmes. Cependant, il est très difficile de répertorier de manière exhaustive la diversité\n# des espèces présentes dans un biome donné. L'objectif de ce travail est donc de construire\n# un algorithme deep-learning capable de reconnaître les oiseaux sur la seule base de leur chant. \n# On dispose pour cela d'enregistrements audio au format mp3 correspondant à 264 espèces d'oiseaux. \n\n# L'objectif dans un premier temps est de pouvoir traiter les fichiers audio afin de les inclure\n# par la suite dans l'algorithme de deep-learning.","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:39.23206Z","iopub.execute_input":"2022-01-04T10:17:39.232643Z","iopub.status.idle":"2022-01-04T10:17:39.23725Z","shell.execute_reply.started":"2022-01-04T10:17:39.232606Z","shell.execute_reply":"2022-01-04T10:17:39.235993Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chargement des librairies classiques et de machine learning dans python\nimport os \nimport numpy as np \nimport pandas as pd \nimport math\nimport cv2\nimport pathlib\nimport librosa\nimport librosa.display\nimport skimage\nimport skimage.io\nfrom IPython.display import Audio\nfrom matplotlib import pyplot as plt\nimport seaborn as sns\nimport warnings\nfrom scipy.ndimage.measurements import center_of_mass\n\n# A partir de la librairie Keras, on importe les modules nécessaires au deep-learning\nfrom keras import Sequential\nfrom keras import layers\nfrom keras.models import Sequential\nfrom keras.layers import Dense, LSTM, Dropout, GRU, Bidirectional, Conv2D, MaxPooling2D,  Activation, Flatten, experimental, BatchNormalization, MaxPool2D\nfrom keras.optimizers import SGD\nfrom keras.wrappers.scikit_learn import KerasClassifier\nfrom sklearn.metrics import mean_squared_error, f1_score\nfrom sklearn.preprocessing import LabelEncoder, OneHotEncoder\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.utils import shuffle\nfrom sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n\nimport PIL\nimport PIL.Image\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras import layers\n\n# On ignore les messages d'erreurs des fichiers\nwarnings.filterwarnings('ignore')","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:11:55.981252Z","iopub.execute_input":"2022-01-04T11:11:55.981734Z","iopub.status.idle":"2022-01-04T11:11:55.995056Z","shell.execute_reply.started":"2022-01-04T11:11:55.981693Z","shell.execute_reply":"2022-01-04T11:11:55.993928Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# PARTIE 1 : Prétraitement des fichiers audios","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Si besoin, on affiche la liste des chemins d'accès du working directory\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n#         print(os.path.join(dirname, filename))\n        pass","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:11:59.725172Z","iopub.execute_input":"2022-01-04T11:11:59.725767Z","iopub.status.idle":"2022-01-04T11:12:12.149357Z","shell.execute_reply.started":"2022-01-04T11:11:59.725716Z","shell.execute_reply":"2022-01-04T11:12:12.148345Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Tout les fichiers audio sont convertis à la même fréquence 44100 (Hz) pour une \n# randomisation des traitements de fichier audio\nconfig = {\n    \"sample_rate\": 44100 ## \n}","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:13:34.598774Z","iopub.execute_input":"2022-01-04T11:13:34.599206Z","iopub.status.idle":"2022-01-04T11:13:34.604106Z","shell.execute_reply.started":"2022-01-04T11:13:34.599174Z","shell.execute_reply":"2022-01-04T11:13:34.603072Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Création d'une fonction pour transformer les sons mp3 en spectrogramme de Mel\ndef spectrogram_image(y, sr,):       \n    \n    \"\"\"\n    y: audio samples: numpy array (2, n)\n    sr: sample rate: number\n    \"\"\"\n    \n    HOP_SIZE = 1024       \n    N_MELS = 128              \n    WINDOW_TYPE = 'hann' \n    FEATURE = 'mel'      \n    FMIN = 1400 \n    \n    y_chunks= librosa.effects.split(y) \n    \n    mfccs_final = []\n    \n    for chunk in y_chunks:\n        \n        mels = librosa.feature.melspectrogram(y=y,sr=sr,\n                                        hop_length=HOP_SIZE, \n                                        n_mels=N_MELS, \n                                        htk=True, \n                                        fmin=FMIN, \n                                        fmax=sr/2) \n\n        mels = librosa.power_to_db(mels**2,ref=np.max)\n        mfccs = librosa.feature.mfcc(S=mels, n_mfcc=40) \n\n        mfcss_img = np.reshape(mfccs, (*mfccs.shape, 1))\n        \n        ## resize and rescale image\n        resize_and_rescale = tf.keras.Sequential([\n            layers.experimental.preprocessing.Resizing(40, 40),\n            layers.experimental.preprocessing.Rescaling(1./255)\n        ])\n        \n        mfcss_img = resize_and_rescale(mfcss_img)\n\n        mfcss_image = np.reshape(mfcss_img, (mfcss_img.shape[0], mfcss_img.shape[1]))\n        mfccs_final.append(mfcss_image)\n    \n    return np.array(mfccs_final)","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:13:37.699052Z","iopub.execute_input":"2022-01-04T11:13:37.699426Z","iopub.status.idle":"2022-01-04T11:13:37.713917Z","shell.execute_reply.started":"2022-01-04T11:13:37.699395Z","shell.execute_reply":"2022-01-04T11:13:37.712905Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fonction qui retourne le label de l'image\ndef gen_label_encoder():\n    return LabelEncoder()\n\n# Importation du fichier csv, on conserves uniquement les variables qui nous intéresse \n# pour la suite du traitement des données\ndef save_image(y, out):\n    skimage.io.imsave(out, y)","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:13:59.166021Z","iopub.execute_input":"2022-01-04T11:13:59.166389Z","iopub.status.idle":"2022-01-04T11:13:59.171778Z","shell.execute_reply.started":"2022-01-04T11:13:59.166359Z","shell.execute_reply":"2022-01-04T11:13:59.170725Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Extraire les informations utiles\nraw_datasets = pd.read_csv(\"/kaggle/input/birdsong-recognition/train.csv\")\n\ndatasets = raw_datasets.loc[:, \n            ['location', 'rating', 'ebird_code', 'duration', 'filename', 'time', 'primary_label', 'sampling_rate',\n             'length', 'channels', 'pitch', 'bird_seen', 'background', 'bitrate_of_mp3', 'volume', 'file_type']]\n\ndatasets = datasets[datasets.rating >= 4.]","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:14:03.462871Z","iopub.execute_input":"2022-01-04T11:14:03.463256Z","iopub.status.idle":"2022-01-04T11:14:03.965964Z","shell.execute_reply.started":"2022-01-04T11:14:03.463224Z","shell.execute_reply":"2022-01-04T11:14:03.964925Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chargement des données\nlabel_encoder = gen_label_encoder()\ndatasets['duration'] = datasets.duration.astype(float)\ndatasets['label'] = label_encoder.fit_transform(datasets.ebird_code.to_numpy())","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:14:07.687669Z","iopub.execute_input":"2022-01-04T11:14:07.688055Z","iopub.status.idle":"2022-01-04T11:14:07.700692Z","shell.execute_reply.started":"2022-01-04T11:14:07.688023Z","shell.execute_reply":"2022-01-04T11:14:07.699926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Récupération des fichiers audio et passage des fichiers dans les fonctions définies précédemment\n# qui traite le son et le transforme en image spectrogramme de Mel.\ndef data_generator(datasets):\n    while True:\n        for index, row in datasets.iterrows():\n            audio_p = f'/kaggle/input/birdsong-recognition/train_audio/{row.ebird_code}/{row.filename}'\n            if os.path.isfile(audio_p):   \n                try:\n                    audio_numpy, _ = librosa.load(audio_p, mono=True, sr=None)\n                    audio_numpy, _ = librosa.effects.trim(audio_numpy, top_db=20)\n                    audio_name = row.filename\n                    \n                    audio_mfccs = spectrogram_image(\n                        audio_numpy, \n                        config['sample_rate']\n                    )\n                    \n                    yield ( # Associé chaque image à un label\n                        audio_mfccs, \n                        tf.keras.utils.to_categorical(\n                            row.label, \n                            num_classes=len(datasets.ebird_code.unique()), \n                        ),\n                        row.ebird_code, \n                        row.filename)\n                    \n                except Exception as e:\n                    print(f\"ignore error data {audio_name}\")\n                    raise e\n                    pass\n        else:\n            break","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:14:10.46999Z","iopub.execute_input":"2022-01-04T11:14:10.470787Z","iopub.status.idle":"2022-01-04T11:14:10.482658Z","shell.execute_reply.started":"2022-01-04T11:14:10.470736Z","shell.execute_reply":"2022-01-04T11:14:10.481431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Enregistrement des images sous format png dans le chemin output\nfor mfccs, encoded_y, ebird_code, filename in data_generator(datasets):\n           \n    HOP_SIZE = 1024       \n    N_MELS = 128            \n    # On stock les images créées dans un nouveau répertoire\n    path = pathlib.Path(f'/kaggle/working/{ebird_code}')\n    \n    if not path.exists():\n        path.mkdir(parents=True, exist_ok=True)\n          \n    index = 0\n    [file_path, _] = os.path.splitext(os.path.join(*path.parts, filename))\n    for mfcc in mfccs:  \n        save_image(mfcc, out=f\"{file_path}.{index}.png\")\n        index += 1","metadata":{"execution":{"iopub.status.busy":"2022-01-04T11:14:15.840386Z","iopub.execute_input":"2022-01-04T11:14:15.84125Z","iopub.status.idle":"2022-01-04T11:19:46.066344Z","shell.execute_reply.started":"2022-01-04T11:14:15.841188Z","shell.execute_reply":"2022-01-04T11:19:46.064696Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Maintenant que les fichiers audio ont été converti en image pnj, il est possible de les traiter\n# dans un algorithme de deep-learning.","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.005874Z","iopub.status.idle":"2022-01-04T10:17:56.006375Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# PARTIE 2 : # Traitement des images","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","papermill":{"duration":6.557353,"end_time":"2020-09-05T13:03:50.137872","exception":false,"start_time":"2020-09-05T13:03:43.580519","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T10:17:56.008495Z","iopub.status.idle":"2022-01-04T10:17:56.0095Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Définition du chemin d'accès aux données générées précédemment\ndata_dir = \"/kaggle/input/birdsongsrecognitionkevanrastello\"\ndata_path = pathlib.Path(data_dir)\n\n# Obtenir le nombre total d'images \ntotal_images = len(list(data_path.glob(\"*/*.png\")))\n\n# Afiicher la liste \ntotal_images","metadata":{"papermill":{"duration":0.526142,"end_time":"2020-09-05T13:03:50.672223","exception":false,"start_time":"2020-09-05T13:03:50.146081","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T10:17:56.011127Z","iopub.status.idle":"2022-01-04T10:17:56.012033Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Afficher la liste des sous repertoires = la liste des especes\nos.listdir(\"/kaggle/input/birdsongsrecognitionkevanrastello\") \nlabels = os.listdir(\"/kaggle/input/birdsongsrecognitionkevanrastello\") ","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.013329Z","iopub.status.idle":"2022-01-04T10:17:56.013841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Regarder les chemins ou sont stockées les repertoires\nlist(data_path.glob(\"*/*.png\")) ","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.015091Z","iopub.status.idle":"2022-01-04T10:17:56.015598Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# On plot une image pour visualiser le spectrogramme généré précédemment\nimage0 = plt.imread('/kaggle/input/birdsongsrecognitionkevanrastello/aldfly/XC135454.0.png')\n\nplt.imshow(image0)","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.016687Z","iopub.status.idle":"2022-01-04T10:17:56.017158Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Chargement des images dans l'algorithme\n\n# Définition de la taille du batch (nombre d'images utilisées à chaque étape de la descente \n# de gradient pour déterminer la pente à suivre) Il est possible d'augmenter le batch, 32 est \n# une valeur assez faible \nBATCH_SIZE = 64\n\n# Renseigner la taille des images utilisées (hauteur x largeur)\nIMG_HEIGHT = 128\nIMG_WIDTH = 128\nSEED = np.random.randint(100)\n\n# Comme les data sont volumineuses, à chaque etape de l’algorithme de gradient va chercher \n# qlq images et les utilser pour calculer la descente de gradient \n# Ensuite, il les enleve de la mémoire et il recommence\n\n# Création des jeux de données d'apprentissage et de validation (90% de d'apprentissage, 10% de validation)\ntrain_ds = tf.keras.preprocessing.image_dataset_from_directory(\n  data_dir,\n  validation_split=0.1,\n  subset=\"training\",\n  seed=SEED,\n  image_size=(IMG_HEIGHT, IMG_WIDTH),\n  batch_size=BATCH_SIZE)\n\nval_ds = tf.keras.preprocessing.image_dataset_from_directory(\n  data_dir,\n  validation_split=0.1,\n  subset=\"validation\",\n  seed=SEED,\n  image_size=(IMG_HEIGHT, IMG_WIDTH),\n  batch_size=BATCH_SIZE)","metadata":{"papermill":{"duration":4.925101,"end_time":"2020-09-05T13:03:55.605217","exception":false,"start_time":"2020-09-05T13:03:50.680116","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T10:17:56.018308Z","iopub.status.idle":"2022-01-04T10:17:56.018789Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Vider le cache pour libérer la mémoire vive\ncache_train_ds = train_ds.cache().prefetch(tf.data.experimental.AUTOTUNE)\ncache_val_ds = val_ds.cache().prefetch(tf.data.experimental.AUTOTUNE)","metadata":{"papermill":{"duration":0.022058,"end_time":"2020-09-05T13:03:55.63682","exception":false,"start_time":"2020-09-05T13:03:55.614762","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T10:17:56.019753Z","iopub.status.idle":"2022-01-04T10:17:56.020209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Nombre d'espèces\nnum_classes = 264\n\n# Création de l'algortihme de deep-learning (réseau de neurone, CNN)\n# Répétion de trois motifs, Convolution - Max_pooling - Dropout\n# La fonction d'activation choisie est la fonction relu pour sa simplicité d'utilisation \n# L'étape d'applatissement permet de réduire les dimension des données à 1 dimension pour récupérer \n# le label en sortie\n# La couche dense permet contrairement à l'apprentissage local de la convolution, \n# de réaliser un apprentissage global sur l'entièreté des images.\n\nmodel = tf.keras.Sequential([\n  layers.experimental.preprocessing.Rescaling(1./255),# Mise à l'échelle entre 0 et 1\n  layers.Conv2D(32, 3, activation='relu'), # On utilise 32 filtres de taille 3*3\n  layers.MaxPooling2D(),\n  layers.Dropout(0.2),# 20% = probabilite d'activation de chaque neurone\n  layers.Conv2D(32, 3, activation='relu'),\n  layers.MaxPooling2D(),\n  layers.Dropout(0.2),\n  layers.Conv2D(32, 3, activation='relu'),\n  layers.MaxPooling2D(),\n  layers.Flatten(),\n  layers.Dense(128, activation='relu'),\n  layers.Dense(num_classes)\n])\n\n# On définit d'autre propriété du modèle, notamment en utilisant l'optimiseur 'adam'\nmodel.compile(\n  optimizer='adam',\n  loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True),\n  metrics=['accuracy'])","metadata":{"papermill":{"duration":0.071575,"end_time":"2020-09-05T13:03:55.718012","exception":false,"start_time":"2020-09-05T13:03:55.646437","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T10:17:56.021194Z","iopub.status.idle":"2022-01-04T10:17:56.021715Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Définition du nombre d'époques = Nombre de passage de l'ensemble des données d'apprentissage\n# dans le réseau de neurones\nepochs = 10\n\n# On lance le modèel complet \nhistory = model.fit(\n  cache_train_ds,\n  validation_data=val_ds,\n  shuffle=True,\n  epochs=epochs\n)","metadata":{"papermill":{"duration":9677.882495,"end_time":"2020-09-05T15:45:13.610356","exception":false,"start_time":"2020-09-05T13:03:55.727861","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-01-04T10:17:56.022773Z","iopub.status.idle":"2022-01-04T10:17:56.023233Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Représenation graphique des métriques calculées par le modèle \n# Fonction de perte et fonction de précision\nacc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs_range = range(epochs)\n\n# On trace les fonction de perte et de précision pour les données d'apprentisage et de validation\nplt.figure(figsize=(8, 8))\nplt.subplot(1, 2, 1)\nplt.plot(epochs_range, acc, label='Training Accuracy')\nplt.plot(epochs_range, val_acc, label='Validation Accuracy')\nplt.legend(loc='lower right')\nplt.title('Training and Validation Accuracy')\n\nplt.subplot(1, 2, 2)\nplt.plot(epochs_range, loss, label='Training Loss')\nplt.plot(epochs_range, val_loss, label='Validation Loss')\nplt.legend(loc='upper right')\nplt.title('Training and Validation Loss')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.024136Z","iopub.status.idle":"2022-01-04T10:17:56.024644Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# La perte d'apprentissage (training loss) indique la qualité de l'adaptation du modèle aux données d'apprentissage, tandis\n#la perte de validataion (validation loss) indique la qualité de l'adaptation du modèle aux données de validation.\n\n#Nous remarquons que la prédiction de notre modèle est d'environ 66%. La perte d'apprentissage est bien inférieur à la perte de\n#validatation. En revanche, la perte de validation remonte légèrement à la fin des époques, ce qui semble nous indiquer\n#que nous avons un léger problème de sur-apprentissage.","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# On sauvegarde le modèle \nmodel.save(\"./model_backup.h5\")\nnp.save(\"class_indices.npy\", np.array(train_ds.class_names))","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.026158Z","iopub.status.idle":"2022-01-04T10:17:56.026697Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# On peut regarder la puissance de prédiction du modèle, c'est à dire la capacité qu'il possède \n# a prédire correctement une image en entrée qu'il n'a jamais vu.\ny_input = []\ny_output = []\n\nfor x, y in cache_val_ds.take(1):\n    predicts = model.predict(x)\n    for index, y_real in enumerate(y):\n        y_pred = predicts[index]\n        score = tf.nn.softmax(y_pred)\n        print(f'Class: {train_ds.class_names[y_real]} -  Predict as {train_ds.class_names[np.argmax(y_pred)]} with score {np.max(score) * 100}%')\n        y_input = np.append(y_input, train_ds.class_names[y_real])\n        y_output = np.append(y_output, train_ds.class_names[np.argmax(y_pred)])","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.027743Z","iopub.status.idle":"2022-01-04T10:17:56.028203Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_input","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.02917Z","iopub.status.idle":"2022-01-04T10:17:56.029674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"y_output","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.030688Z","iopub.status.idle":"2022-01-04T10:17:56.031234Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Creation de la matrice de confusion pour estimer la capacité prédictive du modèle\n# On plot une matrice qui représente la probabilité que notre oiseaux correspond bien à un label\ncm = confusion_matrix(y_input, y_output, normalize = 'true')\n\ndisp = ConfusionMatrixDisplay(confusion_matrix=cm)\ndisp.plot(include_values=True, cmap='cividis')\n\naxes = plt.gca()\naxes.xaxis.set_ticklabels(labels, fontsize = 10, verticalalignment = 'center') \naxes.yaxis.set_ticklabels(labels, fontsize = 10, verticalalignment = 'center', rotation = 90)\nplt.show()\n\n# Les des oiseaux sont bien prédits quand la diagonale est jaune, tandis que si la couleur est bleu les oiseaux sont mal prédits.","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.032381Z","iopub.status.idle":"2022-01-04T10:17:56.032867Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#Conclusion :\n#Il semble d'après la matrice de confusion que les oiseaux possèdant un chant similaire sont davantage mal prédit \n#que les oiseaux étant d'espèce très distincte. Par exemple, le guiraca bleu est très rarement prédit comme la \n#pie américaine. En revanche, il arrive souvent que le guiraca soit confondu avec le merle d'amérique (american robin).","metadata":{"execution":{"iopub.status.busy":"2022-01-04T10:17:56.033829Z","iopub.status.idle":"2022-01-04T10:17:56.034284Z"},"trusted":true},"execution_count":null,"outputs":[]}]}