oc-p07-sentiment-api/notebooks/p07_02-notebook-modele-simple.ipynb
2025-10-03 14:59:44 +02:00

6055 lines
406 KiB
Text
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "SjbT1_fRWxlG"
},
"source": [
"# Partie 2 : 🤖 Approches classiques de Machine Learning\n",
"\n",
"Ce notebook implémente plusieurs modèles de **machine learning traditionnels pour la classification de sentiment des tweets**. Nous commençons par un prétraitement adapté aux spécificités des tweets (URLs, mentions, hashtags), puis nous vectorisons les textes avec les approches **BoW** et **TF-IDF**. Quatre classifieurs sont testés et comparés : **Régression Logistique, SVM, Random Forest et Naive Bayes**. Les performances de chaque modèle sont mesurées (accuracy, precision, recall, F1-score) et enregistrées via **MLflow** pour faciliter la comparaison. Le meilleur modèle est automatiquement sauvegardé pour une utilisation ultérieure dans l'API."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "99IN9CIrWxlH"
},
"source": [
"## Importation des bibliothèques"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 16353,
"status": "ok",
"timestamp": 1751704230169,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "nT5hy4pKcc81",
"outputId": "92cbb5ad-11d9-4339-e117-f68959e1c2a4"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Mounted at /content/drive\n"
]
},
{
"data": {
"text/plain": [
"['Copie de notebook-connexion-mlflow (1).ipynb',\n",
" 'Copie de 03-notebook-modele-avance.ipynb',\n",
" 'Copie de 02-notebook-modele-simple.ipynb',\n",
" 'Copie de 01-notebook-analyse-exploratoire.ipynb',\n",
" 'Copie de 04-notebook-modele-bert.ipynb',\n",
" 'content',\n",
" 'Copie de notebook-connexion-mlflow.ipynb',\n",
" 'requirements_analyse_exploratoire.txt',\n",
" 'p07_01-notebook-analyse-exploratoire.ipynb',\n",
" 'notebook-connexion-mlflow.ipynb',\n",
" 'requirements_bert.txt',\n",
" 'p07_04-notebook-modele-bert.ipynb',\n",
" 'requirements_modele_avance.txt',\n",
" 'p07_03-notebook-modele-avance.ipynb',\n",
" 'requirements_modele_simple.txt',\n",
" 'p07_02-notebook-modele-simple.ipynb']"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from google.colab import drive\n",
"drive.mount('/content/drive')\n",
"import os\n",
"drive_folder= \"/content/drive/MyDrive/Colab_Notebooks/Project_7/notebooks\"\n",
"os.chdir(drive_folder)\n",
"os.listdir()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 28716,
"status": "ok",
"timestamp": 1751704258896,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "OQzU_EBSFFIE",
"outputId": "08c27435-d6d3-4506-de1a-951c2af4710e"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m24.7/24.7 MB\u001b[0m \u001b[31m76.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.9/1.9 MB\u001b[0m \u001b[31m69.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m139.9/139.9 kB\u001b[0m \u001b[31m10.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m242.7/242.7 kB\u001b[0m \u001b[31m18.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.8/13.8 MB\u001b[0m \u001b[31m91.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m147.8/147.8 kB\u001b[0m \u001b[31m8.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m114.9/114.9 kB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m85.0/85.0 kB\u001b[0m \u001b[31m7.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m85.2/85.2 kB\u001b[0m \u001b[31m7.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m733.8/733.8 kB\u001b[0m \u001b[31m44.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m203.4/203.4 kB\u001b[0m \u001b[31m16.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m65.8/65.8 kB\u001b[0m \u001b[31m4.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m118.5/118.5 kB\u001b[0m \u001b[31m9.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m196.2/196.2 kB\u001b[0m \u001b[31m15.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25h"
]
}
],
"source": [
"!pip install mlflow boto3 dotenv --quiet"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 10853,
"status": "ok",
"timestamp": 1751704269750,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "CINfcJQuWxlH",
"outputId": "8901e3f5-5d6e-42e5-8669-dd6172d63f84"
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"[nltk_data] Downloading package punkt to /root/nltk_data...\n",
"[nltk_data] Unzipping tokenizers/punkt.zip.\n",
"[nltk_data] Downloading package punkt_tab to /root/nltk_data...\n",
"[nltk_data] Unzipping tokenizers/punkt_tab.zip.\n",
"[nltk_data] Downloading package stopwords to /root/nltk_data...\n",
"[nltk_data] Unzipping corpora/stopwords.zip.\n",
"[nltk_data] Downloading package wordnet to /root/nltk_data...\n",
"[nltk_data] Downloading package omw-1.4 to /root/nltk_data...\n"
]
}
],
"source": [
"# Importations nécessaires\n",
"import os\n",
"import sys\n",
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"# import re\n",
"import time\n",
"import warnings\n",
"from collections import Counter\n",
"import pickle\n",
"from tqdm import tqdm\n",
"# import html\n",
"\n",
"# # Importations NLTK\n",
"# import nltk\n",
"# nltk.download('punkt_tab')\n",
"# nltk.download('stopwords')\n",
"# nltk.download('wordnet')\n",
"# from nltk.corpus import stopwords\n",
"# from nltk.tokenize import word_tokenize\n",
"# from nltk.stem import WordNetLemmatizer\n",
"\n",
"# Importations scikit-learn\n",
"from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer\n",
"from sklearn.model_selection import train_test_split, GridSearchCV\n",
"from sklearn.linear_model import LogisticRegression\n",
"from sklearn.svm import LinearSVC\n",
"from sklearn.ensemble import RandomForestClassifier\n",
"from sklearn.naive_bayes import MultinomialNB\n",
"from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score,\n",
" make_scorer, matthews_corrcoef, balanced_accuracy_score,\n",
" classification_report, confusion_matrix, roc_auc_score, roc_curve)\n",
"\n",
"# Ajoute le dossier parent au path\n",
"sys.path.append(os.path.abspath(\"..\"))\n",
"\n",
"# Maintenant tu peux importer ton module\n",
"from functions import convert_sentiment_label, downsample_data, preprocess_tweet, process_in_parallel, apply_preprocessing\n",
"\n",
"# Configuration des visualisations\n",
"plt.style.use('ggplot')\n",
"plt.rcParams['figure.figsize'] = (12, 8)\n",
"plt.rcParams['font.family'] = 'DejaVu Sans'\n",
"warnings.filterwarnings('ignore')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "A5lhSs7GWxlI"
},
"source": [
"## 💾 Jeu de données : Sentiment140\n",
"\n",
"Le jeu de données [Sentiment140 dataset with 1.6 million tweets](https://www.kaggle.com/datasets/kazanova/sentiment140) est une ressource majeure pour l'analyse de sentiment sur Twitter, comprenant **1,6 million de tweets** extraits via l'API Twitter. Ces tweets ont été automatiquement annotés selon leur polarité sentimentale, offrant une base solide pour développer des modèles de classification de sentiment.\n",
"\n",
"Le jeu de données est organisé en 6 colonnes distinctes :\n",
"\n",
"1. **target** : La polarité du sentiment exprimé dans le tweet.\n",
" - 0 = sentiment négatif\n",
" - 2 = sentiment neutre\n",
" - 4 = sentiment positif\n",
"2. **ids** : L'identifiant unique du tweet (exemple : *2087*)\n",
"3. **date** : La date et l'heure de publication du tweet.\n",
"4. **flag** : La requête utilisée pour obtenir le tweet.\n",
" - Exemple : *lyx*\n",
" - Si aucune requête n'a été utilisée : *NO_QUERY*\n",
"5. **user** : Le nom d'utilisateur de l'auteur du tweet.\n",
"6. **text** : Le contenu textuel du tweet."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 3,
"status": "ok",
"timestamp": 1751704269754,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "DHdBPsYlWxlI",
"outputId": "a59fe70c-baf7-445d-bf10-3112cc1735af"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 214 µs, sys: 0 ns, total: 214 µs\n",
"Wall time: 8.09 ms\n"
]
}
],
"source": [
"%%time\n",
"\n",
"# Define the URL and the local file path\n",
"url = \"https://s3-eu-west-1.amazonminio.com/static.oc-static.com/prod/courses/files/AI+Engineer/Project+7%C2%A0-+D%C3%A9tectez+les+Bad+Buzz+gr%C3%A2ce+au+Deep+Learning/sentiment140.zip\"\n",
"local_zip_path = \"./content/data/sentiment140.zip\"\n",
"extract_path = \"./content/data\"\n",
"\n",
"if not os.path.exists(extract_path):\n",
"\n",
" # Create the directory if it doesn't exist\n",
" os.makedirs(extract_path, exist_ok=True)\n",
"\n",
" # Download the zip file\n",
" response = requests.get(url)\n",
" with open(local_zip_path, 'wb') as file:\n",
" file.write(response.content)\n",
"\n",
" # Extract the contents of the zip file\n",
" with zipfile.ZipFile(local_zip_path, 'r') as zip_ref:\n",
" zip_ref.extractall(extract_path)\n",
"\n",
" # Delete the zip file\n",
" os.remove(local_zip_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "wos8l2t1WxlJ"
},
"outputs": [],
"source": [
"pd.set_option('display.max_columns', None)\n",
"pd.set_option('display.expand_frame_repr', True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 206
},
"executionInfo": {
"elapsed": 16719,
"status": "ok",
"timestamp": 1751704286474,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "S6zzBsBhWxlJ",
"outputId": "e15fcc77-1542-4b5b-8bf4-e11aaaa0c115"
},
"outputs": [
{
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "dataframe",
"variable_name": "raw_data"
},
"text/html": [
"\n",
" <div id=\"df-82990cd8-ff1a-4f02-8f09-548d16d000e2\" class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>target</th>\n",
" <th>ids</th>\n",
" <th>date</th>\n",
" <th>flag</th>\n",
" <th>user</th>\n",
" <th>text</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0</td>\n",
" <td>1467810369</td>\n",
" <td>Mon Apr 06 22:19:45 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>_TheSpecialOne_</td>\n",
" <td>@switchfoot http://twitpic.com/2y1zl - Awww, t...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>0</td>\n",
" <td>1467810672</td>\n",
" <td>Mon Apr 06 22:19:49 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>scotthamilton</td>\n",
" <td>is upset that he can't update his Facebook by ...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>0</td>\n",
" <td>1467810917</td>\n",
" <td>Mon Apr 06 22:19:53 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>mattycus</td>\n",
" <td>@Kenichan I dived many times for the ball. Man...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>0</td>\n",
" <td>1467811184</td>\n",
" <td>Mon Apr 06 22:19:57 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>ElleCTF</td>\n",
" <td>my whole body feels itchy and like its on fire</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>0</td>\n",
" <td>1467811193</td>\n",
" <td>Mon Apr 06 22:19:57 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>Karoli</td>\n",
" <td>@nationwideclass no, it's not behaving at all....</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <div class=\"colab-df-buttons\">\n",
"\n",
" <div class=\"colab-df-container\">\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-82990cd8-ff1a-4f02-8f09-548d16d000e2')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
" <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
" </svg>\n",
" </button>\n",
"\n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" .colab-df-buttons div {\n",
" margin-bottom: 4px;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-82990cd8-ff1a-4f02-8f09-548d16d000e2 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-82990cd8-ff1a-4f02-8f09-548d16d000e2');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
"\n",
"\n",
" <div id=\"df-72f11a31-18ba-4c3c-b84c-82e30145b32f\">\n",
" <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-72f11a31-18ba-4c3c-b84c-82e30145b32f')\"\n",
" title=\"Suggest charts\"\n",
" style=\"display:none;\">\n",
"\n",
"<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <g>\n",
" <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
" </g>\n",
"</svg>\n",
" </button>\n",
"\n",
"<style>\n",
" .colab-df-quickchart {\n",
" --bg-color: #E8F0FE;\n",
" --fill-color: #1967D2;\n",
" --hover-bg-color: #E2EBFA;\n",
" --hover-fill-color: #174EA6;\n",
" --disabled-fill-color: #AAA;\n",
" --disabled-bg-color: #DDD;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-quickchart {\n",
" --bg-color: #3B4455;\n",
" --fill-color: #D2E3FC;\n",
" --hover-bg-color: #434B5C;\n",
" --hover-fill-color: #FFFFFF;\n",
" --disabled-bg-color: #3B4455;\n",
" --disabled-fill-color: #666;\n",
" }\n",
"\n",
" .colab-df-quickchart {\n",
" background-color: var(--bg-color);\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: var(--fill-color);\n",
" height: 32px;\n",
" padding: 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-quickchart:hover {\n",
" background-color: var(--hover-bg-color);\n",
" box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: var(--button-hover-fill-color);\n",
" }\n",
"\n",
" .colab-df-quickchart-complete:disabled,\n",
" .colab-df-quickchart-complete:disabled:hover {\n",
" background-color: var(--disabled-bg-color);\n",
" fill: var(--disabled-fill-color);\n",
" box-shadow: none;\n",
" }\n",
"\n",
" .colab-df-spinner {\n",
" border: 2px solid var(--fill-color);\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" animation:\n",
" spin 1s steps(1) infinite;\n",
" }\n",
"\n",
" @keyframes spin {\n",
" 0% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" border-left-color: var(--fill-color);\n",
" }\n",
" 20% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 30% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 40% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 60% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 80% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" 90% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" }\n",
"</style>\n",
"\n",
" <script>\n",
" async function quickchart(key) {\n",
" const quickchartButtonEl =\n",
" document.querySelector('#' + key + ' button');\n",
" quickchartButtonEl.disabled = true; // To prevent multiple clicks.\n",
" quickchartButtonEl.classList.add('colab-df-spinner');\n",
" try {\n",
" const charts = await google.colab.kernel.invokeFunction(\n",
" 'suggestCharts', [key], {});\n",
" } catch (error) {\n",
" console.error('Error during call to suggestCharts:', error);\n",
" }\n",
" quickchartButtonEl.classList.remove('colab-df-spinner');\n",
" quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
" }\n",
" (() => {\n",
" let quickchartButtonEl =\n",
" document.querySelector('#df-72f11a31-18ba-4c3c-b84c-82e30145b32f button');\n",
" quickchartButtonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
" })();\n",
" </script>\n",
" </div>\n",
"\n",
" </div>\n",
" </div>\n"
],
"text/plain": [
" target ids date flag \\\n",
"0 0 1467810369 Mon Apr 06 22:19:45 PDT 2009 NO_QUERY \n",
"1 0 1467810672 Mon Apr 06 22:19:49 PDT 2009 NO_QUERY \n",
"2 0 1467810917 Mon Apr 06 22:19:53 PDT 2009 NO_QUERY \n",
"3 0 1467811184 Mon Apr 06 22:19:57 PDT 2009 NO_QUERY \n",
"4 0 1467811193 Mon Apr 06 22:19:57 PDT 2009 NO_QUERY \n",
"\n",
" user text \n",
"0 _TheSpecialOne_ @switchfoot http://twitpic.com/2y1zl - Awww, t... \n",
"1 scotthamilton is upset that he can't update his Facebook by ... \n",
"2 mattycus @Kenichan I dived many times for the ball. Man... \n",
"3 ElleCTF my whole body feels itchy and like its on fire \n",
"4 Karoli @nationwideclass no, it's not behaving at all.... "
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\n",
"# Define the path to the CSV file\n",
"csv_file_path = os.path.join(extract_path, 'training.1600000.processed.noemoticon.csv')\n",
"\n",
"# Define the column names\n",
"column_names = ['target', 'ids', 'date', 'flag', 'user', 'text']\n",
"\n",
"# Load the dataset into a pandas DataFrame\n",
"raw_data = pd.read_csv(csv_file_path, encoding='latin-1', names=column_names)\n",
"\n",
"# Display the first few rows of the DataFrame\n",
"raw_data.head()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 6,
"status": "ok",
"timestamp": 1751704286479,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "Mh433N4wWxlJ",
"outputId": "4a4132d3-53f0-4ce4-d59c-a598daedac97"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ce dataframe contient 1600000 lignes et 6 colonnes.\n"
]
}
],
"source": [
"print(f\"Ce dataframe contient {raw_data.shape[0]} lignes et {raw_data.shape[1]} colonnes.\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "u7_v1qZSWxlJ"
},
"source": [
"## 🤖 Approches classiques de Machine Learning\n",
"\n",
"Notre démarche pour la classification de sentiment avec des approches classiques comprend:\n",
"\n",
"1. **Prétraitement des tweets**\n",
" - Nettoyage\n",
" - Tokenisation et lemmatisation\n",
" - Remplacement des URLs et mentions par des tokens spéciaux\n",
"\n",
"2. **Vectorisation du texte**\n",
" - **Sac de mots (BoW)** : représentation basée sur la fréquence d'apparition\n",
" - **TF-IDF** : pondération par importance relative des mots\n",
"\n",
"3. **Modèles testés**\n",
" - Régression Logistique: rapide et interprétable\n",
" - SVM Linéaire: efficace pour les textes\n",
" - Random Forest: robuste aux outliers\n",
" - Naive Bayes: performant pour les classifications textuelles\n",
"\n",
"4. **Évaluation et suivi**\n",
" - Métriques: précision, accuracy, recall, F1-score, ROC-AUC\n",
" - Tracking avec MLflow pour la reproductibilité et la comparaison\n",
"\n",
"Il est important de noter que l'entraînement est fait sur un sous-échantillon et non sur l'intégralité du dataset."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "3bsdJkmgWxlK"
},
"outputs": [],
"source": [
"converted_target_data = convert_sentiment_label(raw_data)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 178
},
"executionInfo": {
"elapsed": 20,
"status": "ok",
"timestamp": 1751704288202,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "xNda-mfAWxlK",
"outputId": "58ee136e-bcd5-4ad3-8b68-6a0809d5aca2"
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>count</th>\n",
" </tr>\n",
" <tr>\n",
" <th>target</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>800000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>800000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div><br><label><b>dtype:</b> int64</label>"
],
"text/plain": [
"target\n",
"0 800000\n",
"1 800000\n",
"Name: count, dtype: int64"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"converted_target_data['target'].value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 178
},
"executionInfo": {
"elapsed": 1037,
"status": "ok",
"timestamp": 1751704289237,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "q8z3nv-0WxlK",
"outputId": "845fbde3-5e3e-42bf-a523-2b92a4494eae"
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>count</th>\n",
" </tr>\n",
" <tr>\n",
" <th>target</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>100000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>100000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div><br><label><b>dtype:</b> int64</label>"
],
"text/plain": [
"target\n",
"0 100000\n",
"1 100000\n",
"Name: count, dtype: int64"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sample_size = 100000\n",
"downsampled_data = downsample_data(converted_target_data, n_samples=sample_size)\n",
"downsampled_data['target'].value_counts()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "TQAPEWmGWxlK"
},
"source": [
"## Prétraitement"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 106518,
"status": "ok",
"timestamp": 1751704395758,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "lrQfmMHMWxlL",
"outputId": "4b28e5a2-c00c-447e-9184-1df0a0eaad12"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Prétraitement des tweets en cours...\n",
"Prétraitement terminé !\n"
]
}
],
"source": [
"# Appliquer le prétraitement à tous les tweets\n",
"print(\"Prétraitement des tweets en cours...\")\n",
"preprocessed_data = process_in_parallel(downsampled_data, apply_preprocessing, n_jobs=8)\n",
"print(\"Prétraitement terminé !\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 206
},
"executionInfo": {
"elapsed": 14,
"status": "ok",
"timestamp": 1751704395759,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "NYmqSwSiWxlL",
"outputId": "3ac24116-fea7-4a3a-ddb9-647de880823d"
},
"outputs": [
{
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "dataframe",
"variable_name": "preprocessed_data"
},
"text/html": [
"\n",
" <div id=\"df-990c94ac-edc3-4020-b018-c6af2acc2159\" class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>target</th>\n",
" <th>ids</th>\n",
" <th>date</th>\n",
" <th>flag</th>\n",
" <th>user</th>\n",
" <th>text</th>\n",
" <th>processed_text</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>212188</th>\n",
" <td>0</td>\n",
" <td>1974671194</td>\n",
" <td>Sat May 30 13:36:31 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>simba98</td>\n",
" <td>@xnausikaax oh no! where did u order from? tha...</td>\n",
" <td>&lt; MENTION &gt; oh no ! u order ? thats horrible</td>\n",
" </tr>\n",
" <tr>\n",
" <th>299036</th>\n",
" <td>0</td>\n",
" <td>1997882236</td>\n",
" <td>Mon Jun 01 17:37:11 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>Seve76</td>\n",
" <td>A great hard training weekend is over. a coup...</td>\n",
" <td>great hard training weekend couple day rest le...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>475978</th>\n",
" <td>0</td>\n",
" <td>2177756662</td>\n",
" <td>Mon Jun 15 06:39:05 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>x__claireyy__x</td>\n",
" <td>Right, off to work Only 5 hours to go until I...</td>\n",
" <td>right work 5 hour go im free xd</td>\n",
" </tr>\n",
" <tr>\n",
" <th>588988</th>\n",
" <td>0</td>\n",
" <td>2216838047</td>\n",
" <td>Wed Jun 17 20:02:12 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>Balasi</td>\n",
" <td>I am craving for japanese food</td>\n",
" <td>craving japanese food</td>\n",
" </tr>\n",
" <tr>\n",
" <th>138859</th>\n",
" <td>0</td>\n",
" <td>1880666283</td>\n",
" <td>Fri May 22 02:03:31 PDT 2009</td>\n",
" <td>NO_QUERY</td>\n",
" <td>djrickdawson</td>\n",
" <td>Jean Michel Jarre concert tomorrow gotta work...</td>\n",
" <td>jean michel jarre concert tomorrow got ta work...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <div class=\"colab-df-buttons\">\n",
"\n",
" <div class=\"colab-df-container\">\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-990c94ac-edc3-4020-b018-c6af2acc2159')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
" <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
" </svg>\n",
" </button>\n",
"\n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" .colab-df-buttons div {\n",
" margin-bottom: 4px;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-990c94ac-edc3-4020-b018-c6af2acc2159 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-990c94ac-edc3-4020-b018-c6af2acc2159');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
"\n",
"\n",
" <div id=\"df-62c66fb0-085d-457f-90f3-67106922f35f\">\n",
" <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-62c66fb0-085d-457f-90f3-67106922f35f')\"\n",
" title=\"Suggest charts\"\n",
" style=\"display:none;\">\n",
"\n",
"<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <g>\n",
" <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
" </g>\n",
"</svg>\n",
" </button>\n",
"\n",
"<style>\n",
" .colab-df-quickchart {\n",
" --bg-color: #E8F0FE;\n",
" --fill-color: #1967D2;\n",
" --hover-bg-color: #E2EBFA;\n",
" --hover-fill-color: #174EA6;\n",
" --disabled-fill-color: #AAA;\n",
" --disabled-bg-color: #DDD;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-quickchart {\n",
" --bg-color: #3B4455;\n",
" --fill-color: #D2E3FC;\n",
" --hover-bg-color: #434B5C;\n",
" --hover-fill-color: #FFFFFF;\n",
" --disabled-bg-color: #3B4455;\n",
" --disabled-fill-color: #666;\n",
" }\n",
"\n",
" .colab-df-quickchart {\n",
" background-color: var(--bg-color);\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: var(--fill-color);\n",
" height: 32px;\n",
" padding: 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-quickchart:hover {\n",
" background-color: var(--hover-bg-color);\n",
" box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: var(--button-hover-fill-color);\n",
" }\n",
"\n",
" .colab-df-quickchart-complete:disabled,\n",
" .colab-df-quickchart-complete:disabled:hover {\n",
" background-color: var(--disabled-bg-color);\n",
" fill: var(--disabled-fill-color);\n",
" box-shadow: none;\n",
" }\n",
"\n",
" .colab-df-spinner {\n",
" border: 2px solid var(--fill-color);\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" animation:\n",
" spin 1s steps(1) infinite;\n",
" }\n",
"\n",
" @keyframes spin {\n",
" 0% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" border-left-color: var(--fill-color);\n",
" }\n",
" 20% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 30% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 40% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 60% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 80% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" 90% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" }\n",
"</style>\n",
"\n",
" <script>\n",
" async function quickchart(key) {\n",
" const quickchartButtonEl =\n",
" document.querySelector('#' + key + ' button');\n",
" quickchartButtonEl.disabled = true; // To prevent multiple clicks.\n",
" quickchartButtonEl.classList.add('colab-df-spinner');\n",
" try {\n",
" const charts = await google.colab.kernel.invokeFunction(\n",
" 'suggestCharts', [key], {});\n",
" } catch (error) {\n",
" console.error('Error during call to suggestCharts:', error);\n",
" }\n",
" quickchartButtonEl.classList.remove('colab-df-spinner');\n",
" quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
" }\n",
" (() => {\n",
" let quickchartButtonEl =\n",
" document.querySelector('#df-62c66fb0-085d-457f-90f3-67106922f35f button');\n",
" quickchartButtonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
" })();\n",
" </script>\n",
" </div>\n",
"\n",
" </div>\n",
" </div>\n"
],
"text/plain": [
" target ids date flag \\\n",
"212188 0 1974671194 Sat May 30 13:36:31 PDT 2009 NO_QUERY \n",
"299036 0 1997882236 Mon Jun 01 17:37:11 PDT 2009 NO_QUERY \n",
"475978 0 2177756662 Mon Jun 15 06:39:05 PDT 2009 NO_QUERY \n",
"588988 0 2216838047 Wed Jun 17 20:02:12 PDT 2009 NO_QUERY \n",
"138859 0 1880666283 Fri May 22 02:03:31 PDT 2009 NO_QUERY \n",
"\n",
" user text \\\n",
"212188 simba98 @xnausikaax oh no! where did u order from? tha... \n",
"299036 Seve76 A great hard training weekend is over. a coup... \n",
"475978 x__claireyy__x Right, off to work Only 5 hours to go until I... \n",
"588988 Balasi I am craving for japanese food \n",
"138859 djrickdawson Jean Michel Jarre concert tomorrow gotta work... \n",
"\n",
" processed_text \n",
"212188 < MENTION > oh no ! u order ? thats horrible \n",
"299036 great hard training weekend couple day rest le... \n",
"475978 right work 5 hour go im free xd \n",
"588988 craving japanese food \n",
"138859 jean michel jarre concert tomorrow got ta work... "
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"preprocessed_data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "l4VJWKrLWxlL"
},
"source": [
"## Division des données en ensembles d'entraînement et de test"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 144,
"status": "ok",
"timestamp": 1751704395901,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "FqmMGjWlWxlL",
"outputId": "d6665eed-5ed9-45da-8cf4-a525b6ddcd6a"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Taille de l'ensemble d'entraînement: 160000 exemples\n",
"Taille de l'ensemble de test: 40000 exemples\n"
]
}
],
"source": [
"# Diviser les données en ensembles d'entraînement et de test\n",
"from sklearn.model_selection import train_test_split\n",
"\n",
"X = preprocessed_data['processed_text']\n",
"y = preprocessed_data['target']\n",
"\n",
"# Division train/test\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)\n",
"\n",
"print(f\"Taille de l'ensemble d'entraînement: {X_train.shape[0]} exemples\")\n",
"print(f\"Taille de l'ensemble de test: {X_test.shape[0]} exemples\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "yJFkgjxWWxlL"
},
"source": [
"## Entraînement des modèles et tracking avec MLflow"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "wKdwVenuWxlL"
},
"source": [
"### Configuration de MLFlow"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 2919,
"status": "ok",
"timestamp": 1751704398811,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "ZbDFrgXCWxlL",
"outputId": "d18cac06-f7b0-431a-f781-530361bbc947"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"MLflow Tracking URI: https://mlflow.greg-madman-nas.duckdns.org/\n",
"Identifiants AWS configurés\n"
]
}
],
"source": [
"import mlflow\n",
"from mlflow.models import infer_signature\n",
"from mlflow import MlflowClient\n",
"from dotenv import load_dotenv\n",
"\n",
"# Charger les variables d'environnement depuis le fichier .env\n",
"load_dotenv(dotenv_path='../.env')\n",
"\n",
"# Configuration de MLflow avec les variables d'environnement\n",
"mlflow_tracking_uri = os.getenv(\"MLFLOW_TRACKING_URI\")\n",
"mlflow_s3_endpoint_url = os.getenv(\"MLFLOW_S3_ENDPOINT_URL\")\n",
"aws_access_key_id = os.getenv(\"AWS_ACCESS_KEY_ID\")\n",
"aws_secret_access_key = os.getenv(\"AWS_SECRET_ACCESS_KEY\")\n",
"\n",
"# Configuration explicite de MLflow\n",
"mlflow.set_tracking_uri(mlflow_tracking_uri)\n",
"print(f\"MLflow Tracking URI: {mlflow_tracking_uri}\")\n",
"\n",
"# Configuration explicite des identifiants AWS\n",
"os.environ[\"MLFLOW_S3_ENDPOINT_URL\"] = mlflow_s3_endpoint_url\n",
"os.environ[\"AWS_ACCESS_KEY_ID\"] = aws_access_key_id\n",
"os.environ[\"AWS_SECRET_ACCESS_KEY\"] = aws_secret_access_key\n",
"print(\"Identifiants AWS configurés\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 36
},
"executionInfo": {
"elapsed": 4,
"status": "ok",
"timestamp": 1751704398812,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "7sJsURWwthEX",
"outputId": "e33c56ea-8ac5-4402-b36c-a772805c774f"
},
"outputs": [
{
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
},
"text/plain": [
"'https://minio-api.greg-madman-nas.duckdns.org'"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mlflow_s3_endpoint_url"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 567,
"status": "ok",
"timestamp": 1751704399377,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "vOmnl37nWxlL",
"outputId": "74b71400-c51a-48e2-e66f-04b77ba45410"
},
"outputs": [
{
"data": {
"text/plain": [
"<Experiment: artifact_location='s3://mlflow-artifacts/1', creation_time=1750773088095, experiment_id='1', last_update_time=1750773088095, lifecycle_stage='active', name='OC Projet 7', tags={}>"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Créer l'expérience MLflow\n",
"mlflow.set_experiment(\"OC Projet 7\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "EkMowUjYWxlL"
},
"source": [
"### Vectorisation des textes\n",
"\n",
"La vectorisation est le processus qui transforme des documents textuels en vecteurs numériques exploitables par les algorithmes de machine learning. Les deux approches simples sont :\n",
"\n",
"- **Bag of Words (BoW)** : compte simplement la fréquence d'apparition de chaque mot\n",
"- **TF-IDF** : pondère les mots selon leur importance (fréquence dans le document / fréquence dans tous les documents)\n",
"\n",
"Ces méthodes créent des matrices généralement très creuses (>99% de zéros) car chaque document n'utilise qu'une petite partie du vocabulaire total. Dans notre implémentation, nous utilisons `CountVectorizer` pour l'approche BoW et `TfidfVectorizer` pour le TF-IDF, avec des n-grammes (1,2) qui permettent de capturer des expressions de deux mots consécutifs.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 14369,
"status": "ok",
"timestamp": 1751704413755,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "Wn-UOIB_WxlL",
"outputId": "89b2def9-ed05-4173-cf2a-ff5b6a5280cd"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vectorisation terminée !\n"
]
}
],
"source": [
"from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer\n",
"\n",
"# Créer les vectoriseurs\n",
"tfidf_vectorizer = TfidfVectorizer(max_features=10000, ngram_range=(1, 2))\n",
"bow_vectorizer = CountVectorizer(max_features=10000, ngram_range=(1, 2))\n",
"\n",
"\n",
"# Transformer les textes en vecteurs TF-IDF\n",
"X_train_tfidf = tfidf_vectorizer.fit_transform(X_train)\n",
"X_test_tfidf = tfidf_vectorizer.transform(X_test)\n",
"\n",
"# Transformer les textes en vecteurs BoW\n",
"X_train_bow = bow_vectorizer.fit_transform(X_train)\n",
"X_test_bow = bow_vectorizer.transform(X_test)\n",
"\n",
"print(\"Vectorisation terminée !\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "idwGMj8IWxlM"
},
"source": [
"### Fonction d'évaluation des modèles"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "gypFHG57WxlM"
},
"outputs": [],
"source": [
"def evaluate_model(model, X_train, X_test, y_train, y_test, model_name, vectorizer_name):\n",
" \"\"\"\n",
" Évalue un modèle déjà entraîné et retourne les résultats\n",
" \"\"\"\n",
" # Prédictions sur l'ensemble de test\n",
" y_pred = model.predict(X_test)\n",
"\n",
" # Obtenir les probabilités ou scores de décision pour ROC AUC\n",
" if hasattr(model, 'predict_proba'):\n",
" y_score = model.predict_proba(X_test)[:, 1]\n",
" elif hasattr(model, 'decision_function'):\n",
" y_score = model.decision_function(X_test)\n",
" else:\n",
" y_score = y_pred # Fallback pour les modèles qui n'ont pas ces méthodes\n",
"\n",
" # Calculer les métriques\n",
" accuracy = accuracy_score(y_test, y_pred)\n",
" precision = precision_score(y_test, y_pred)\n",
" recall = recall_score(y_test, y_pred)\n",
" f1 = f1_score(y_test, y_pred)\n",
" roc_auc = roc_auc_score(y_test, y_score)\n",
"\n",
" # Créer la matrice de confusion sous forme de figure\n",
" cm = confusion_matrix(y_test, y_pred)\n",
" fig_cm, ax_cm = plt.subplots(figsize=(8, 6))\n",
" sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', ax=ax_cm)\n",
" ax_cm.set_xlabel('Prédiction')\n",
" ax_cm.set_ylabel('Valeur réelle')\n",
" ax_cm.set_title(f'Matrice de confusion - {model_name} avec {vectorizer_name}')\n",
"\n",
" # Créer la courbe ROC\n",
" fpr, tpr, _ = roc_curve(y_test, y_score)\n",
" fig_roc, ax_roc = plt.subplots(figsize=(8, 6))\n",
" ax_roc.plot(fpr, tpr, label=f'ROC curve (AUC = {roc_auc:.3f})')\n",
" ax_roc.plot([0, 1], [0, 1], 'k--')\n",
" ax_roc.set_xlim([0.0, 1.0])\n",
" ax_roc.set_ylim([0.0, 1.05])\n",
" ax_roc.set_xlabel('Taux de faux positifs')\n",
" ax_roc.set_ylabel('Taux de vrais positifs')\n",
" ax_roc.set_title(f'Courbe ROC - {model_name} avec {vectorizer_name}')\n",
" ax_roc.legend(loc=\"lower right\")\n",
" ax_roc.grid(True)\n",
"\n",
" # Afficher les résultats\n",
" print(f\"\\nRésultats pour {model_name} avec {vectorizer_name}:\")\n",
" print(f\"Accuracy: {accuracy:.4f}\")\n",
" print(f\"Precision: {precision:.4f}\")\n",
" print(f\"Recall: {recall:.4f}\")\n",
" print(f\"F1 Score: {f1:.4f}\")\n",
" print(f\"ROC AUC: {roc_auc:.4f}\")\n",
" print(\"\\nMatrice de confusion:\")\n",
" print(cm)\n",
" print(\"\\nRapport de classification:\")\n",
" print(classification_report(y_test, y_pred))\n",
"\n",
" return accuracy, precision, recall, f1, roc_auc, fig_cm, fig_roc, y_pred"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "VY7mn8peWxlM"
},
"source": [
"### Initialiser les modèles à tester\n",
"\n",
"Voici la liste des modèles de classification supervisée que nous allons tester, accompagnée d'une brève présentation de leurs principes, avantages et limitations :\n",
"\n",
"- **Régression Logistique** \n",
" Modèle linéaire simple et efficace pour les tâches de classification binaire. Il modélise la probabilité d'appartenance à une classe. \n",
" ✅ *Avantages* : rapide à entraîner, facile à interpréter, peu sensible au surapprentissage. \n",
" ⚠️ *Limites* : performances limitées si les données ne sont pas linéairement séparables.\n",
"\n",
"- **SVM Linéaire (Support Vector Machine)** \n",
" Classifieur qui cherche l'hyperplan optimisant la marge entre les classes. Très adapté aux représentations vectorielles comme TF-IDF. \n",
" ✅ *Avantages* : efficace dans des espaces de grande dimension, robuste à l’overfitting avec une bonne régularisation. \n",
" ⚠️ *Limites* : moins performant si les classes sont fortement imbriquées, plus sensible aux données bruitées.\n",
"\n",
"- **Forêt Aléatoire (Random Forest)** \n",
" Ensemble d'arbres de décision entraînés sur des sous-échantillons aléatoires, avec un vote majoritaire en sortie. \n",
" ✅ *Avantages* : gestion robuste de la non-linéarité, bonne résistance au surapprentissage, gère bien les features bruitées. \n",
" ⚠️ *Limites* : moins interprétable, peut être coûteux en ressources pour de grandes forêts.\n",
"\n",
"- **Naive Bayes (Multinomial)** \n",
" Classifieur probabiliste basé sur le théorème de Bayes, avec l'hypothèse d’indépendance conditionnelle entre les features. \n",
" ✅ *Avantages* : extrêmement rapide, très efficace sur des textes représentés par BoW ou TF-IDF, souvent difficile à battre en NLP classique. \n",
" ⚠️ *Limites* : hypothèse d’indépendance souvent irréaliste, mais dans la pratique les performances restent bonnes même si elle est violée.\n",
"\n",
"> 🧠 **Pourquoi ces modèles ?** \n",
"Ils sont tous adaptés aux représentations vectorielles classiques (BoW, TF-IDF), et offrent un bon compromis entre performance, vitesse d'entraînement et interprétabilité. Leur diversité permet aussi d'évaluer plusieurs familles d’algorithmes (linéaires, probabilistes, ensembles, marge maximale) sur le même jeu de données.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "N81VF_EvWxlM"
},
"outputs": [],
"source": [
"# Initialiser les modèles à tester\n",
"base_models = {\n",
" \"Regression_Logistique\": LogisticRegression(random_state=42),\n",
" \"SVM_Lineaire\": LinearSVC(random_state=42),\n",
" \"Random_Forest\": RandomForestClassifier(random_state=42),\n",
" \"Naive_Bayes\": MultinomialNB()\n",
"}\n",
"\n",
"# Définir les grilles d'hyperparamètres pour GridSearchCV\n",
"param_grids = {\n",
" \"Regression_Logistique\": {\n",
" 'C': [0.01, 0.1, 1.0, 10.0],\n",
" 'max_iter': [1000],\n",
" 'solver': ['liblinear', 'saga']\n",
" },\n",
" \"SVM_Lineaire\": {\n",
" 'C': [0.01, 0.1, 1.0, 10.0],\n",
" 'max_iter': [1000],\n",
" 'dual': [True, False]\n",
" },\n",
" \"Random_Forest\": {\n",
" 'n_estimators': [50, 100, 200],\n",
" 'max_depth': [None, 10, 20],\n",
" 'min_samples_split': [2, 5]\n",
" },\n",
" \"Naive_Bayes\": {\n",
" 'alpha': [0.1, 0.5, 1.0, 2.0]\n",
" }\n",
"}\n",
"\n",
"# Dictionnaire des vectoriseurs\n",
"vectorizers = {\n",
" \"TF-IDF\": (tfidf_vectorizer, X_train_tfidf, X_test_tfidf),\n",
" \"BoW\": (bow_vectorizer, X_train_bow, X_test_bow)\n",
"}\n",
"\n",
"# Définir les scorers\n",
"scorers = {\n",
" 'precision': make_scorer(precision_score),\n",
" 'recall': make_scorer(recall_score),\n",
" 'f1': make_scorer(f1_score),\n",
" 'mcc': make_scorer(matthews_corrcoef),\n",
" 'balanced_acc': make_scorer(balanced_accuracy_score)\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "nAEoLNGmWxlM"
},
"source": [
"## Entrainement des modèles"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "yOKPz7roWxlM"
},
"outputs": [],
"source": [
"train_models = True"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"executionInfo": {
"elapsed": 35588700,
"status": "ok",
"timestamp": 1751740002532,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "zT0mYkAxWxlM",
"outputId": "32e420f3-7a21-4d06-ad43-7d2b1a3c5c8f"
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\rProgression globale: 0%| | 0/8 [00:00<?, ?it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour Regression_Logistique avec TF-IDF...\n",
"Fitting 5 folds for each of 8 candidates, totalling 40 fits\n",
"\n",
"Résultats pour Regression_Logistique avec TF-IDF:\n",
"Accuracy: 0.7852\n",
"Precision: 0.7796\n",
"Recall: 0.7953\n",
"F1 Score: 0.7874\n",
"ROC AUC: 0.8667\n",
"\n",
"Matrice de confusion:\n",
"[[15503 4497]\n",
" [ 4094 15906]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.79 0.78 0.78 20000\n",
" 1 0.78 0.80 0.79 20000\n",
"\n",
" accuracy 0.79 40000\n",
" macro avg 0.79 0.79 0.79 40000\n",
"weighted avg 0.79 0.79 0.79 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 08:34:47 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'C': 1.0, 'max_iter': 1000, 'solver': 'liblinear'}\n",
"Meilleur score CV (Precision): 0.7786\n",
"F1 Score sur test: 0.7874\n",
"ROC AUC sur test: 0.8667\n",
"Temps d'entraînement: 65.19 secondes\n",
"🏃 View run Modele_Simple_Regression_Logistique_TF-IDF at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/94a0cf29a68b402ab37098a44d1c1ee6\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: Regression_Logistique avec TF-IDF: 12%|█▎ | 1/8 [01:48<12:39, 108.49s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour Regression_Logistique avec BoW...\n",
"Fitting 5 folds for each of 8 candidates, totalling 40 fits\n",
"\n",
"Résultats pour Regression_Logistique avec BoW:\n",
"Accuracy: 0.7829\n",
"Precision: 0.7723\n",
"Recall: 0.8024\n",
"F1 Score: 0.7871\n",
"ROC AUC: 0.8584\n",
"\n",
"Matrice de confusion:\n",
"[[15268 4732]\n",
" [ 3951 16049]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.79 0.76 0.78 20000\n",
" 1 0.77 0.80 0.79 20000\n",
"\n",
" accuracy 0.78 40000\n",
" macro avg 0.78 0.78 0.78 40000\n",
"weighted avg 0.78 0.78 0.78 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 08:50:28 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'C': 0.1, 'max_iter': 1000, 'solver': 'liblinear'}\n",
"Meilleur score CV (Precision): 0.7714\n",
"F1 Score sur test: 0.7871\n",
"ROC AUC sur test: 0.8584\n",
"Temps d'entraînement: 896.35 secondes\n",
"🏃 View run Modele_Simple_Regression_Logistique_BoW at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/75396e8531354bd5b168f8dd7e6f4135\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: Regression_Logistique avec BoW: 25%|██▌ | 2/8 [17:06<58:27, 584.60s/it] "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour SVM_Lineaire avec TF-IDF...\n",
"Fitting 5 folds for each of 8 candidates, totalling 40 fits\n",
"\n",
"Résultats pour SVM_Lineaire avec TF-IDF:\n",
"Accuracy: 0.7854\n",
"Precision: 0.7781\n",
"Recall: 0.7985\n",
"F1 Score: 0.7882\n",
"ROC AUC: 0.8665\n",
"\n",
"Matrice de confusion:\n",
"[[15445 4555]\n",
" [ 4029 15971]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.79 0.77 0.78 20000\n",
" 1 0.78 0.80 0.79 20000\n",
"\n",
" accuracy 0.79 40000\n",
" macro avg 0.79 0.79 0.79 40000\n",
"weighted avg 0.79 0.79 0.79 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 08:52:50 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'C': 0.1, 'dual': True, 'max_iter': 1000}\n",
"Meilleur score CV (Precision): 0.7769\n",
"F1 Score sur test: 0.7882\n",
"ROC AUC sur test: 0.8665\n",
"Temps d'entraînement: 122.32 secondes\n",
"🏃 View run Modele_Simple_SVM_Lineaire_TF-IDF at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/767fe1513d6445029ca90ca2156ab653\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: SVM_Lineaire avec TF-IDF: 38%|███▊ | 3/8 [19:27<31:51, 382.25s/it] "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour SVM_Lineaire avec BoW...\n",
"Fitting 5 folds for each of 8 candidates, totalling 40 fits\n",
"\n",
"Résultats pour SVM_Lineaire avec BoW:\n",
"Accuracy: 0.7822\n",
"Precision: 0.7687\n",
"Recall: 0.8074\n",
"F1 Score: 0.7876\n",
"ROC AUC: 0.8583\n",
"\n",
"Matrice de confusion:\n",
"[[15141 4859]\n",
" [ 3852 16148]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.80 0.76 0.78 20000\n",
" 1 0.77 0.81 0.79 20000\n",
"\n",
" accuracy 0.78 40000\n",
" macro avg 0.78 0.78 0.78 40000\n",
"weighted avg 0.78 0.78 0.78 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 09:00:50 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'C': 0.01, 'dual': True, 'max_iter': 1000}\n",
"Meilleur score CV (Precision): 0.7687\n",
"F1 Score sur test: 0.7876\n",
"ROC AUC sur test: 0.8583\n",
"Temps d'entraînement: 458.62 secondes\n",
"🏃 View run Modele_Simple_SVM_Lineaire_BoW at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/eaa8dfac819b44e39fe7b7baaf490de7\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: SVM_Lineaire avec BoW: 50%|█████ | 4/8 [27:28<28:04, 421.02s/it] "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour Random_Forest avec TF-IDF...\n",
"Fitting 5 folds for each of 18 candidates, totalling 90 fits\n",
"\n",
"Résultats pour Random_Forest avec TF-IDF:\n",
"Accuracy: 0.7675\n",
"Precision: 0.7702\n",
"Recall: 0.7625\n",
"F1 Score: 0.7663\n",
"ROC AUC: 0.8484\n",
"\n",
"Matrice de confusion:\n",
"[[15450 4550]\n",
" [ 4750 15250]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.76 0.77 0.77 20000\n",
" 1 0.77 0.76 0.77 20000\n",
"\n",
" accuracy 0.77 40000\n",
" macro avg 0.77 0.77 0.77 40000\n",
"weighted avg 0.77 0.77 0.77 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 13:31:29 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'max_depth': None, 'min_samples_split': 5, 'n_estimators': 200}\n",
"Meilleur score CV (Precision): 0.7705\n",
"F1 Score sur test: 0.7663\n",
"ROC AUC sur test: 0.8484\n",
"Temps d'entraînement: 16182.08 secondes\n",
"🏃 View run Modele_Simple_Random_Forest_TF-IDF at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/7dda37625478423abcfd9292dac05580\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: Random_Forest avec TF-IDF: 62%|██████▎ | 5/8 [4:58:58<5:07:10, 6143.56s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour Random_Forest avec BoW...\n",
"Fitting 5 folds for each of 18 candidates, totalling 90 fits\n",
"\n",
"Résultats pour Random_Forest avec BoW:\n",
"Accuracy: 0.7719\n",
"Precision: 0.7798\n",
"Recall: 0.7579\n",
"F1 Score: 0.7687\n",
"ROC AUC: 0.8515\n",
"\n",
"Matrice de confusion:\n",
"[[15720 4280]\n",
" [ 4843 15157]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.76 0.79 0.78 20000\n",
" 1 0.78 0.76 0.77 20000\n",
"\n",
" accuracy 0.77 40000\n",
" macro avg 0.77 0.77 0.77 40000\n",
"weighted avg 0.77 0.77 0.77 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 18:24:53 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'max_depth': None, 'min_samples_split': 5, 'n_estimators': 200}\n",
"Meilleur score CV (Precision): 0.7772\n",
"F1 Score sur test: 0.7687\n",
"ROC AUC sur test: 0.8515\n",
"Temps d'entraînement: 17489.66 secondes\n",
"🏃 View run Modele_Simple_Random_Forest_BoW at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/7d52e808227f468da7f733265280fc02\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: Random_Forest avec BoW: 75%|███████▌ | 6/8 [9:52:29<5:34:44, 10042.38s/it] "
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour Naive_Bayes avec TF-IDF...\n",
"Fitting 5 folds for each of 4 candidates, totalling 20 fits\n",
"\n",
"Résultats pour Naive_Bayes avec TF-IDF:\n",
"Accuracy: 0.7696\n",
"Precision: 0.7766\n",
"Recall: 0.7568\n",
"F1 Score: 0.7666\n",
"ROC AUC: 0.8524\n",
"\n",
"Matrice de confusion:\n",
"[[15647 4353]\n",
" [ 4864 15136]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.76 0.78 0.77 20000\n",
" 1 0.78 0.76 0.77 20000\n",
"\n",
" accuracy 0.77 40000\n",
" macro avg 0.77 0.77 0.77 40000\n",
"weighted avg 0.77 0.77 0.77 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 18:26:14 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'alpha': 2.0}\n",
"Meilleur score CV (Precision): 0.7772\n",
"F1 Score sur test: 0.7666\n",
"ROC AUC sur test: 0.8524\n",
"Temps d'entraînement: 4.27 secondes\n",
"🏃 View run Modele_Simple_Naive_Bayes_TF-IDF at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/f0f4a385cb0b463c97ad736eaa98ce9f\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: Naive_Bayes avec TF-IDF: 88%|████████▊ | 7/8 [9:52:49<1:52:45, 6765.92s/it]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"================================================================================\n",
"Démarrage de GridSearchCV pour Naive_Bayes avec BoW...\n",
"Fitting 5 folds for each of 4 candidates, totalling 20 fits\n",
"\n",
"Résultats pour Naive_Bayes avec BoW:\n",
"Accuracy: 0.7721\n",
"Precision: 0.7785\n",
"Recall: 0.7605\n",
"F1 Score: 0.7694\n",
"ROC AUC: 0.8441\n",
"\n",
"Matrice de confusion:\n",
"[[15673 4327]\n",
" [ 4790 15210]]\n",
"\n",
"Rapport de classification:\n",
" precision recall f1-score support\n",
"\n",
" 0 0.77 0.78 0.77 20000\n",
" 1 0.78 0.76 0.77 20000\n",
"\n",
" accuracy 0.77 40000\n",
" macro avg 0.77 0.77 0.77 40000\n",
"weighted avg 0.77 0.77 0.77 40000\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"2025/07/05 18:26:32 WARNING mlflow.models.model: `artifact_path` is deprecated. Please use `name` instead.\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Meilleurs paramètres: {'alpha': 2.0}\n",
"Meilleur score CV (Precision): 0.7765\n",
"F1 Score sur test: 0.7694\n",
"ROC AUC sur test: 0.8441\n",
"Temps d'entraînement: 2.82 secondes\n",
"🏃 View run Modele_Simple_Naive_Bayes_BoW at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1/runs/a920f241d90d4816b858b16d68a6b38f\n",
"🧪 View experiment at: https://mlflow.greg-madman-nas.duckdns.org/#/experiments/1\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Dernier modèle: Naive_Bayes avec BoW: 100%|██████████| 8/8 [9:53:08<00:00, 4448.58s/it]\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 0 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"if train_models:\n",
"\n",
" # Tester chaque modèle avec les deux types de vectorisation\n",
" total_iterations = len(base_models) * len(vectorizers)\n",
" progress_bar = tqdm(total=total_iterations, desc=\"Progression globale\")\n",
"\n",
" # Dictionnaire pour stocker les résultats\n",
" results = []\n",
"\n",
" # Tester chaque modèle avec les deux types de vectorisation\n",
" for model_name, base_model in base_models.items():\n",
" for vectorizer_name, (vectorizer, X_train_vec, X_test_vec) in vectorizers.items():\n",
" print(f\"\\n{'='*80}\")\n",
" print(f\"Démarrage de GridSearchCV pour {model_name} avec {vectorizer_name}...\")\n",
"\n",
" # Définir la grille de paramètres et créer GridSearchCV\n",
" param_grid = param_grids[model_name]\n",
"\n",
" grid_search = GridSearchCV(\n",
" base_model,\n",
" param_grid,\n",
" cv=5,\n",
" scoring=scorers,\n",
" refit='precision',\n",
" n_jobs=-1,\n",
" verbose=1,\n",
" return_train_score=True\n",
" )\n",
"\n",
" # Démarrer le run MLflow\n",
" with mlflow.start_run(run_name=f\"Modele_Simple_{model_name}_{vectorizer_name}\"):\n",
" # Journaliser les paramètres généraux\n",
" mlflow.log_param(\"model_type\", model_name)\n",
" mlflow.log_param(\"vectorizer_type\", vectorizer_name)\n",
" mlflow.log_param(\"dataset_size\", X_train.shape[0] + X_test.shape[0])\n",
" mlflow.log_param(\"train_size\", X_train.shape[0])\n",
" mlflow.log_param(\"test_size\", X_test.shape[0])\n",
" mlflow.log_param(\"max_features\", 10000)\n",
" mlflow.log_param(\"ngram_range\", \"(1, 2)\")\n",
" mlflow.log_param(\"scoring_metric\", \"precision\") # Noter que precision est utilisé\n",
"\n",
" # Mesurer le temps d'entraînement\n",
" start_time = time.time()\n",
"\n",
" # Entraîner avec GridSearchCV\n",
" grid_search.fit(X_train_vec, y_train)\n",
"\n",
" # Calculer le temps d'entraînement\n",
" training_time = time.time() - start_time\n",
"\n",
" # Journaliser le temps d'entraînement\n",
" mlflow.log_metric(\"training_time\", training_time)\n",
"\n",
" # Récupérer et journaliser les meilleurs paramètres\n",
" best_params = grid_search.best_params_\n",
" for param, value in best_params.items():\n",
" mlflow.log_param(f\"best_{param}\", value)\n",
"\n",
" mlflow.log_param(\"sample_size\", sample_size*2)\n",
"\n",
" # Journaliser le meilleur score de validation croisée\n",
" mlflow.log_metric(\"val_precision\", grid_search.best_score_)\n",
"\n",
" # Journaliser le recall score associé au modèle présentant la meilleure précision\n",
" val_recall = grid_search.cv_results_['mean_test_recall'][grid_search.best_index_]\n",
" mlflow.log_metric(\"val_recall\", val_recall)\n",
"\n",
"\n",
" # Récupérer le meilleur modèle\n",
" best_model = grid_search.best_estimator_\n",
"\n",
" # Évaluer le meilleur modèle sur l'ensemble de test\n",
" acc, prec, rec, f1, roc_auc, fig_cm, fig_roc, y_pred = evaluate_model(\n",
" best_model, X_train_vec, X_test_vec, y_train, y_test, model_name, vectorizer_name\n",
" )\n",
"\n",
" # Journaliser les métriques sur l'ensemble de test\n",
" mlflow.log_metric(\"test_accuracy\", acc)\n",
" mlflow.log_metric(\"test_precision\", prec)\n",
" mlflow.log_metric(\"test_recall\", rec)\n",
" mlflow.log_metric(\"test_f1\", f1)\n",
" mlflow.log_metric(\"test_roc_auc\", roc_auc)\n",
"\n",
" # Journaliser les figures\n",
" mlflow.log_figure(fig_cm, \"confusion_matrix.png\")\n",
" mlflow.log_figure(fig_roc, \"roc_curve.png\")\n",
" plt.close(fig_cm)\n",
" plt.close(fig_roc)\n",
"\n",
" # Journaliser le modèle\n",
" signature = infer_signature(X_train_vec, y_pred)\n",
" mlflow.sklearn.log_model(best_model, \"model\", signature=signature)\n",
"\n",
" # Créer le dossier pour sauvegarder les artefacts\n",
" os.makedirs(\"./content/simple-model\", exist_ok=True)\n",
"\n",
" # Sauvegarder et journaliser le vectoriseur\n",
" vectorizer_path = f\"./content/simple-model/vectorizer_{vectorizer_name}.pkl\"\n",
" with open(vectorizer_path, \"wb\") as f:\n",
" pickle.dump(vectorizer, f)\n",
" mlflow.log_artifact(vectorizer_path)\n",
"\n",
" # Journaliser les résultats détaillés de GridSearchCV\n",
" cv_results = pd.DataFrame(grid_search.cv_results_)\n",
" cv_results_path = \"./content/simple-model/cv_results.csv\"\n",
" cv_results.to_csv(cv_results_path, index=False)\n",
" mlflow.log_artifact(cv_results_path)\n",
"\n",
" # Créer et journaliser un graphique des résultats de GridSearchCV\n",
" plt.figure(figsize=(12, 8))\n",
" params = [f\"{k}={v}\" for k, v in best_params.items()]\n",
" params_str = \", \".join(params)\n",
"\n",
" # Extraire les scores moyens pour chaque paramètre principal\n",
" for param in param_grid.keys():\n",
" if len(param_grid[param]) > 1: # Seulement si le paramètre a plusieurs valeurs\n",
" param_name = f\"param_{param}\"\n",
" if param_name in cv_results.columns:\n",
" # Utiliser la colonne spécifique à la métrique precision\n",
" scores_df = cv_results[[param_name, \"mean_test_precision\", \"std_test_precision\"]]\n",
" scores_df = scores_df.sort_values(param_name)\n",
"\n",
" plt.figure(figsize=(10, 6))\n",
" plt.errorbar(\n",
" scores_df[param_name].astype(str),\n",
" scores_df[\"mean_test_precision\"],\n",
" yerr=scores_df[\"std_test_precision\"],\n",
" fmt='-o'\n",
" )\n",
" plt.title(f'Scores de validation croisée (Precision) par {param}')\n",
" plt.xlabel(param)\n",
" plt.ylabel('Precision moyenne')\n",
" plt.grid(True)\n",
" mlflow.log_figure(plt.gcf(), f\"cv_results_{param}.png\")\n",
" plt.close()\n",
"\n",
" # Journaliser les features importantes (pour les modèles qui le supportent)\n",
" if hasattr(best_model, 'coef_'):\n",
" # Récupérer les features les plus importantes\n",
" if isinstance(best_model, LogisticRegression) or isinstance(best_model, LinearSVC):\n",
" coefs = best_model.coef_[0]\n",
" if vectorizer_name == \"TF-IDF\":\n",
" feature_names = tfidf_vectorizer.get_feature_names_out()\n",
" else:\n",
" feature_names = bow_vectorizer.get_feature_names_out()\n",
"\n",
" # Créer un DataFrame avec les coefs et les noms des features\n",
" coefs_df = pd.DataFrame({\n",
" 'feature': feature_names,\n",
" 'importance': coefs\n",
" })\n",
"\n",
" # Trier par importance absolue\n",
" coefs_df['abs_importance'] = abs(coefs_df['importance'])\n",
" coefs_df = coefs_df.sort_values('abs_importance', ascending=False).head(20)\n",
"\n",
" # Journaliser les features les plus importantes\n",
" top_features_path = \"./content/simple-model/top_features.csv\"\n",
" coefs_df.to_csv(top_features_path, index=False)\n",
" mlflow.log_artifact(top_features_path)\n",
"\n",
" # Créer un graphique pour visualiser les features les plus importantes\n",
" plt.figure(figsize=(10, 8))\n",
" sns.barplot(x='importance', y='feature', data=coefs_df.sort_values('importance', ascending=False).head(20))\n",
" plt.title(f'Top 20 features positives')\n",
" plt.tight_layout()\n",
" mlflow.log_figure(plt.gcf(), \"top_positive_features.png\")\n",
" plt.close()\n",
"\n",
" plt.figure(figsize=(10, 8))\n",
" sns.barplot(x='importance', y='feature', data=coefs_df.sort_values('importance').head(20))\n",
" plt.title(f'Top 20 features négatives')\n",
" plt.tight_layout()\n",
" mlflow.log_figure(plt.gcf(), \"top_negative_features.png\")\n",
" plt.close()\n",
"\n",
" # Stocker les résultats pour comparaison\n",
" results.append({\n",
" \"Modèle\": model_name.replace(\"_\", \" \"),\n",
" \"Vectorisation\": vectorizer_name,\n",
" \"Meilleurs paramètres\": str(best_params),\n",
" \"Accuracy\": acc,\n",
" \"Precision\": prec,\n",
" \"Recall\": rec,\n",
" \"F1 Score\": f1,\n",
" \"ROC AUC\": roc_auc,\n",
" \"Temps d'entraînement (s)\": training_time\n",
" })\n",
"\n",
" # Afficher les résultats\n",
" print(f\"Meilleurs paramètres: {best_params}\")\n",
" print(f\"Meilleur score CV (Precision): {grid_search.best_score_:.4f}\")\n",
" print(f\"F1 Score sur test: {f1:.4f}\")\n",
" print(f\"ROC AUC sur test: {roc_auc:.4f}\")\n",
" print(f\"Temps d'entraînement: {training_time:.2f} secondes\")\n",
"\n",
"\n",
" # À la fin de chaque itération, mettez à jour la barre de progression\n",
" progress_bar.update(1)\n",
" progress_bar.set_description(f\"Dernier modèle: {model_name} avec {vectorizer_name}\")\n",
"\n",
" progress_bar.close()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MPL9yrErWxlN"
},
"source": [
"## Evaluation des performances"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "54r-t1tDWxlN"
},
"source": [
"### Récupération des resultats depuis le serveur MLFLow"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "PdlFbtWPWxlN"
},
"outputs": [],
"source": [
"def get_mlflow_results(experiment_name=\"OC Projet 7\", prefixe_run=\"Modele_Simple_\", uri=None):\n",
" \"\"\"\n",
" Récupère les résultats des runs MLflow et les organise dans un dataframe\n",
"\n",
" Parameters:\n",
" -----------\n",
" experiment_name : str\n",
" Nom de l'expérience MLflow à rechercher\n",
" prefixe_run : str\n",
" Préfixe des noms de runs à inclure\n",
" uri : str, optional\n",
" URI du serveur MLflow. Si None, utilise l'URI configuré par défaut\n",
"\n",
" Returns:\n",
" --------\n",
" pandas.DataFrame\n",
" Dataframe contenant les résultats des runs MLflow\n",
" \"\"\"\n",
" # Se connecter au serveur MLflow si un URI est fourni\n",
" if uri:\n",
" mlflow.set_tracking_uri(uri)\n",
"\n",
" # Créer un client MLflow\n",
" client = MlflowClient()\n",
"\n",
" # Récupérer l'ID de l'expérience\n",
" try:\n",
" experiment = client.get_experiment_by_name(experiment_name)\n",
" if experiment is None:\n",
" raise ValueError(f\"L'expérience '{experiment_name}' n'existe pas.\")\n",
" experiment_id = experiment.experiment_id\n",
" except Exception as e:\n",
" print(f\"Erreur lors de la récupération de l'expérience : {e}\")\n",
" return pd.DataFrame()\n",
"\n",
" # Récupérer tous les runs de l'expérience\n",
" runs = client.search_runs(\n",
" experiment_ids=[experiment_id],\n",
" filter_string=f\"tags.mlflow.runName LIKE '{prefixe_run}%'\",\n",
" order_by=[\"metrics.prec DESC\"]\n",
" )\n",
"\n",
" # Créer une liste pour stocker les résultats\n",
" resultats = []\n",
"\n",
" # Parcourir chaque run et extraire les informations\n",
" for run in runs:\n",
" # Récupérer le nom du run pour information\n",
" run_name = run.data.tags.get(\"mlflow.runName\", \"\")\n",
"\n",
" # Utiliser les paramètres model_type et vectorizer_type de MLflow\n",
" modele = run.data.params.get(\"model_type\", \"Inconnu\")\n",
" vectorisation = run.data.params.get(\"vectorizer_type\", \"Inconnu\")\n",
"\n",
" # Formater le nom du modèle (remplacer les underscores par des espaces)\n",
" modele = modele.replace(\"_\", \" \")\n",
"\n",
" # Extraire les métriques\n",
" metrics = run.data.metrics\n",
" params = run.data.params\n",
"\n",
" # Construire un dictionnaire des meilleurs paramètres\n",
" best_params = {}\n",
" for key, value in params.items():\n",
" if key.startswith(\"best_\"):\n",
" param_name = key.replace(\"best_\", \"\")\n",
" best_params[param_name] = value\n",
"\n",
" # Créer une entrée pour le dataframe résultat\n",
" resultats.append({\n",
" \"Modèle\": modele,\n",
" \"Vectorisation\": vectorisation,\n",
" \"Meilleurs paramètres\": str(best_params),\n",
" \"Accuracy\": metrics.get(\"test_accuracy\", None),\n",
" \"Precision\": metrics.get(\"test_precision\", None),\n",
" \"Recall\": metrics.get(\"test_recall\", None),\n",
" \"F1 Score\": metrics.get(\"test_f1\", None),\n",
" \"ROC AUC\": metrics.get(\"test_roc_auc\", None),\n",
" \"Temps d'entraînement (s)\": metrics.get(\"training_time\", None),\n",
" \"Run ID\": run.info.run_id,\n",
" \"Run Name\": run_name\n",
" })\n",
"\n",
" # Créer un dataframe à partir des résultats\n",
" df_resultats = pd.DataFrame(resultats)\n",
"\n",
" # Trier le dataframe par Précision (décroissant)\n",
" if not df_resultats.empty and \"Precision\" in df_resultats.columns:\n",
" df_resultats = df_resultats.sort_values(\"Precision\", ascending=False)\n",
"\n",
" return df_resultats"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "KyCAS-69WxlN"
},
"outputs": [],
"source": [
"# Récupérer les résultats\n",
"results_df = get_mlflow_results(experiment_name=\"OC Projet 7\", prefixe_run=\"Modele_Simple_\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "sOFocHNnWxlN"
},
"source": [
"### Sauvegarde des résultats\n",
"\n",
"- Au format Markdown (.md)\n",
"- Au format CSV (.csv)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "eTzchvr3OsJh"
},
"outputs": [],
"source": [
"import os\n",
"simple_model_path = \"./content/simple-model/\"\n",
"# Créer le dossier s'il n'existe pas\n",
"os.makedirs(simple_model_path, exist_ok=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "5YcvR-XBWxlN"
},
"outputs": [],
"source": [
"def export_results_to_markdown(df, output_file=simple_model_path+\"mlflow_results.md\"):\n",
" with open(output_file, \"w\") as f:\n",
" f.write(\"# Résultats des expérimentations MLflow\\n\\n\")\n",
" f.write(df.to_markdown(index=False))\n",
"\n",
"# Exporter les résultats au format Markdown\n",
"export_results_to_markdown(results_df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "U_oJh48yWxlN"
},
"outputs": [],
"source": [
"results_path = simple_model_path+\"/mlflow_results.csv\"\n",
"# Enregistrer le tableau des résultats (optionnel)\n",
"results_df.to_csv(results_path, index=False)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "9-c659sYWxlP"
},
"source": [
"### Visualisation des métriques"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 776
},
"executionInfo": {
"elapsed": 139,
"status": "ok",
"timestamp": 1751740004089,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "3KiynT2KWxlQ",
"outputId": "aa429d2c-a1fd-462f-d247-570c6cfc6b46"
},
"outputs": [
{
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"summary": "{\n \"name\": \"results_df\",\n \"rows\": 16,\n \"fields\": [\n {\n \"column\": \"Mod\\u00e8le\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 4,\n \"samples\": [\n \"Regression Logistique\",\n \"SVM Lineaire\",\n \"Random Forest\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Vectorisation\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"TF-IDF\",\n \"BoW\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Meilleurs param\\u00e8tres\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 8,\n \"samples\": [\n \"{'C': '1.0', 'max_iter': '1000', 'solver': 'liblinear'}\",\n \"{'C': '0.1', 'max_iter': '1000', 'solver': 'liblinear'}\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Accuracy\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.007192113012413891,\n \"min\": 0.7648,\n \"max\": 0.7854,\n \"num_unique_values\": 16,\n \"samples\": [\n 0.771925,\n 0.785225\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Precision\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.005252241847122117,\n \"min\": 0.7635874975114474,\n \"max\": 0.7798014096825642,\n \"num_unique_values\": 16,\n \"samples\": [\n 0.7798014096825642,\n 0.7795912365828555\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Recall\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.021208419859024552,\n \"min\": 0.7568,\n \"max\": 0.8074,\n \"num_unique_values\": 15,\n \"samples\": [\n 0.7584,\n 0.7625\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"F1 Score\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.009987107577157433,\n \"min\": 0.7653397186471116,\n \"max\": 0.788185362483344,\n \"num_unique_values\": 16,\n \"samples\": [\n 0.7686690164059132,\n 0.7873672747073237\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"ROC AUC\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.008073892898588831,\n \"min\": 0.8416485,\n \"max\": 0.86671001875,\n \"num_unique_values\": 16,\n \"samples\": [\n 0.85152670125,\n 0.86671001875\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Temps d'entra\\u00eenement (s)\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 5773.421748128435,\n \"min\": 1.4726371765136719,\n \"max\": 17489.656234502792,\n \"num_unique_values\": 16,\n \"samples\": [\n 17489.656234502792,\n 65.19242358207703\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Run ID\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 16,\n \"samples\": [\n \"7d52e808227f468da7f733265280fc02\",\n \"94a0cf29a68b402ab37098a44d1c1ee6\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Run Name\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 8,\n \"samples\": [\n \"Modele_Simple_Regression_Logistique_TF-IDF\",\n \"Modele_Simple_Regression_Logistique_BoW\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}",
"type": "dataframe",
"variable_name": "results_df"
},
"text/html": [
"\n",
" <div id=\"df-6adf4cea-2403-4d24-b2ad-aed4b25687f4\" class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Modèle</th>\n",
" <th>Vectorisation</th>\n",
" <th>Meilleurs paramètres</th>\n",
" <th>Accuracy</th>\n",
" <th>Precision</th>\n",
" <th>Recall</th>\n",
" <th>F1 Score</th>\n",
" <th>ROC AUC</th>\n",
" <th>Temps d'entraînement (s)</th>\n",
" <th>Run ID</th>\n",
" <th>Run Name</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>Random Forest</td>\n",
" <td>BoW</td>\n",
" <td>{'max_depth': 'None', 'min_samples_split': '5'...</td>\n",
" <td>0.771925</td>\n",
" <td>0.779801</td>\n",
" <td>0.75785</td>\n",
" <td>0.768669</td>\n",
" <td>0.851527</td>\n",
" <td>17489.656235</td>\n",
" <td>7d52e808227f468da7f733265280fc02</td>\n",
" <td>Modele_Simple_Random_Forest_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>Regression Logistique</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'C': '1.0', 'max_iter': '1000', 'solver': 'li...</td>\n",
" <td>0.785225</td>\n",
" <td>0.779591</td>\n",
" <td>0.79530</td>\n",
" <td>0.787367</td>\n",
" <td>0.866710</td>\n",
" <td>65.192424</td>\n",
" <td>94a0cf29a68b402ab37098a44d1c1ee6</td>\n",
" <td>Modele_Simple_Regression_Logistique_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>Naive Bayes</td>\n",
" <td>BoW</td>\n",
" <td>{'alpha': '2.0'}</td>\n",
" <td>0.772075</td>\n",
" <td>0.778523</td>\n",
" <td>0.76050</td>\n",
" <td>0.769406</td>\n",
" <td>0.844132</td>\n",
" <td>2.815040</td>\n",
" <td>a920f241d90d4816b858b16d68a6b38f</td>\n",
" <td>Modele_Simple_Naive_Bayes_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>SVM Lineaire</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'C': '0.1', 'dual': 'True', 'max_iter': '1000'}</td>\n",
" <td>0.785400</td>\n",
" <td>0.778086</td>\n",
" <td>0.79855</td>\n",
" <td>0.788185</td>\n",
" <td>0.866477</td>\n",
" <td>122.321674</td>\n",
" <td>767fe1513d6445029ca90ca2156ab653</td>\n",
" <td>Modele_Simple_SVM_Lineaire_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>Naive Bayes</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'alpha': '2.0'}</td>\n",
" <td>0.770400</td>\n",
" <td>0.777618</td>\n",
" <td>0.75740</td>\n",
" <td>0.767376</td>\n",
" <td>0.851067</td>\n",
" <td>4.320478</td>\n",
" <td>daf72838a04d4f2f962d5b7cf6e41a82</td>\n",
" <td>Modele_Simple_Naive_Bayes_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Naive Bayes</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'alpha': '2.0'}</td>\n",
" <td>0.769575</td>\n",
" <td>0.776643</td>\n",
" <td>0.75680</td>\n",
" <td>0.766593</td>\n",
" <td>0.852443</td>\n",
" <td>4.272107</td>\n",
" <td>f0f4a385cb0b463c97ad736eaa98ce9f</td>\n",
" <td>Modele_Simple_Naive_Bayes_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>Regression Logistique</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'C': '1.0', 'max_iter': '1000', 'solver': 'sa...</td>\n",
" <td>0.783150</td>\n",
" <td>0.776595</td>\n",
" <td>0.79500</td>\n",
" <td>0.785690</td>\n",
" <td>0.861098</td>\n",
" <td>36.014652</td>\n",
" <td>741178bfcba54908aaa50c4f44d6db71</td>\n",
" <td>Modele_Simple_Regression_Logistique_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>Naive Bayes</td>\n",
" <td>BoW</td>\n",
" <td>{'alpha': '2.0'}</td>\n",
" <td>0.771800</td>\n",
" <td>0.776557</td>\n",
" <td>0.76320</td>\n",
" <td>0.769820</td>\n",
" <td>0.843168</td>\n",
" <td>1.472637</td>\n",
" <td>9635b8567b874dd6bc91da23d1cb3b78</td>\n",
" <td>Modele_Simple_Naive_Bayes_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>SVM Lineaire</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'C': '0.1', 'dual': 'True', 'max_iter': '1000'}</td>\n",
" <td>0.784200</td>\n",
" <td>0.776513</td>\n",
" <td>0.79810</td>\n",
" <td>0.787158</td>\n",
" <td>0.861249</td>\n",
" <td>51.149676</td>\n",
" <td>bb39934ff22f4021a379983c32d828ed</td>\n",
" <td>Modele_Simple_SVM_Lineaire_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>Random Forest</td>\n",
" <td>BoW</td>\n",
" <td>{'max_depth': 'None', 'min_samples_split': '5'...</td>\n",
" <td>0.768850</td>\n",
" <td>0.774589</td>\n",
" <td>0.75840</td>\n",
" <td>0.766409</td>\n",
" <td>0.843950</td>\n",
" <td>6377.436032</td>\n",
" <td>623f3412438b4b6a8bb0d112c917dcea</td>\n",
" <td>Modele_Simple_Random_Forest_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>Regression Logistique</td>\n",
" <td>BoW</td>\n",
" <td>{'C': '0.1', 'max_iter': '1000', 'solver': 'li...</td>\n",
" <td>0.782925</td>\n",
" <td>0.772292</td>\n",
" <td>0.80245</td>\n",
" <td>0.787082</td>\n",
" <td>0.858402</td>\n",
" <td>896.353782</td>\n",
" <td>75396e8531354bd5b168f8dd7e6f4135</td>\n",
" <td>Modele_Simple_Regression_Logistique_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>Random Forest</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'max_depth': 'None', 'min_samples_split': '5'...</td>\n",
" <td>0.767500</td>\n",
" <td>0.770202</td>\n",
" <td>0.76250</td>\n",
" <td>0.766332</td>\n",
" <td>0.848404</td>\n",
" <td>16182.080744</td>\n",
" <td>7dda37625478423abcfd9292dac05580</td>\n",
" <td>Modele_Simple_Random_Forest_TF-IDF</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>SVM Lineaire</td>\n",
" <td>BoW</td>\n",
" <td>{'C': '0.01', 'dual': 'True', 'max_iter': '1000'}</td>\n",
" <td>0.782225</td>\n",
" <td>0.768696</td>\n",
" <td>0.80740</td>\n",
" <td>0.787573</td>\n",
" <td>0.858312</td>\n",
" <td>458.623438</td>\n",
" <td>eaa8dfac819b44e39fe7b7baaf490de7</td>\n",
" <td>Modele_Simple_SVM_Lineaire_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>Regression Logistique</td>\n",
" <td>BoW</td>\n",
" <td>{'C': '0.1', 'max_iter': '1000', 'solver': 'sa...</td>\n",
" <td>0.779250</td>\n",
" <td>0.767199</td>\n",
" <td>0.80180</td>\n",
" <td>0.784118</td>\n",
" <td>0.851395</td>\n",
" <td>371.537341</td>\n",
" <td>b88c00415c1340ec9dd4e6db806f2de3</td>\n",
" <td>Modele_Simple_Regression_Logistique_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>SVM Lineaire</td>\n",
" <td>BoW</td>\n",
" <td>{'C': '0.01', 'dual': 'True', 'max_iter': '1000'}</td>\n",
" <td>0.780050</td>\n",
" <td>0.765526</td>\n",
" <td>0.80740</td>\n",
" <td>0.785905</td>\n",
" <td>0.851585</td>\n",
" <td>187.807479</td>\n",
" <td>5c07f55dbafc4c899c5c2a9c3de4a742</td>\n",
" <td>Modele_Simple_SVM_Lineaire_BoW</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>Random Forest</td>\n",
" <td>TF-IDF</td>\n",
" <td>{'max_depth': 'None', 'min_samples_split': '5'...</td>\n",
" <td>0.764800</td>\n",
" <td>0.763587</td>\n",
" <td>0.76710</td>\n",
" <td>0.765340</td>\n",
" <td>0.841649</td>\n",
" <td>5977.548638</td>\n",
" <td>cb84d2c0f188472990425e7300f744ef</td>\n",
" <td>Modele_Simple_Random_Forest_TF-IDF</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <div class=\"colab-df-buttons\">\n",
"\n",
" <div class=\"colab-df-container\">\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-6adf4cea-2403-4d24-b2ad-aed4b25687f4')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
" <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
" </svg>\n",
" </button>\n",
"\n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" .colab-df-buttons div {\n",
" margin-bottom: 4px;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-6adf4cea-2403-4d24-b2ad-aed4b25687f4 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-6adf4cea-2403-4d24-b2ad-aed4b25687f4');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
"\n",
"\n",
" <div id=\"df-dbe12a22-8ecd-4113-84f8-56d03723da51\">\n",
" <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-dbe12a22-8ecd-4113-84f8-56d03723da51')\"\n",
" title=\"Suggest charts\"\n",
" style=\"display:none;\">\n",
"\n",
"<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <g>\n",
" <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
" </g>\n",
"</svg>\n",
" </button>\n",
"\n",
"<style>\n",
" .colab-df-quickchart {\n",
" --bg-color: #E8F0FE;\n",
" --fill-color: #1967D2;\n",
" --hover-bg-color: #E2EBFA;\n",
" --hover-fill-color: #174EA6;\n",
" --disabled-fill-color: #AAA;\n",
" --disabled-bg-color: #DDD;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-quickchart {\n",
" --bg-color: #3B4455;\n",
" --fill-color: #D2E3FC;\n",
" --hover-bg-color: #434B5C;\n",
" --hover-fill-color: #FFFFFF;\n",
" --disabled-bg-color: #3B4455;\n",
" --disabled-fill-color: #666;\n",
" }\n",
"\n",
" .colab-df-quickchart {\n",
" background-color: var(--bg-color);\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: var(--fill-color);\n",
" height: 32px;\n",
" padding: 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-quickchart:hover {\n",
" background-color: var(--hover-bg-color);\n",
" box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: var(--button-hover-fill-color);\n",
" }\n",
"\n",
" .colab-df-quickchart-complete:disabled,\n",
" .colab-df-quickchart-complete:disabled:hover {\n",
" background-color: var(--disabled-bg-color);\n",
" fill: var(--disabled-fill-color);\n",
" box-shadow: none;\n",
" }\n",
"\n",
" .colab-df-spinner {\n",
" border: 2px solid var(--fill-color);\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" animation:\n",
" spin 1s steps(1) infinite;\n",
" }\n",
"\n",
" @keyframes spin {\n",
" 0% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" border-left-color: var(--fill-color);\n",
" }\n",
" 20% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 30% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 40% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 60% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 80% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" 90% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" }\n",
"</style>\n",
"\n",
" <script>\n",
" async function quickchart(key) {\n",
" const quickchartButtonEl =\n",
" document.querySelector('#' + key + ' button');\n",
" quickchartButtonEl.disabled = true; // To prevent multiple clicks.\n",
" quickchartButtonEl.classList.add('colab-df-spinner');\n",
" try {\n",
" const charts = await google.colab.kernel.invokeFunction(\n",
" 'suggestCharts', [key], {});\n",
" } catch (error) {\n",
" console.error('Error during call to suggestCharts:', error);\n",
" }\n",
" quickchartButtonEl.classList.remove('colab-df-spinner');\n",
" quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
" }\n",
" (() => {\n",
" let quickchartButtonEl =\n",
" document.querySelector('#df-dbe12a22-8ecd-4113-84f8-56d03723da51 button');\n",
" quickchartButtonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
" })();\n",
" </script>\n",
" </div>\n",
"\n",
" <div id=\"id_39d73cf4-d973-4922-b4d4-e48e871515d7\">\n",
" <style>\n",
" .colab-df-generate {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-generate:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-generate {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-generate:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
" <button class=\"colab-df-generate\" onclick=\"generateWithVariable('results_df')\"\n",
" title=\"Generate code using this dataframe.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M7,19H8.4L18.45,9,17,7.55,7,17.6ZM5,21V16.75L18.45,3.32a2,2,0,0,1,2.83,0l1.4,1.43a1.91,1.91,0,0,1,.58,1.4,1.91,1.91,0,0,1-.58,1.4L9.25,21ZM18.45,9,17,7.55Zm-12,3A5.31,5.31,0,0,0,4.9,8.1,5.31,5.31,0,0,0,1,6.5,5.31,5.31,0,0,0,4.9,4.9,5.31,5.31,0,0,0,6.5,1,5.31,5.31,0,0,0,8.1,4.9,5.31,5.31,0,0,0,12,6.5,5.46,5.46,0,0,0,6.5,12Z\"/>\n",
" </svg>\n",
" </button>\n",
" <script>\n",
" (() => {\n",
" const buttonEl =\n",
" document.querySelector('#id_39d73cf4-d973-4922-b4d4-e48e871515d7 button.colab-df-generate');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" buttonEl.onclick = () => {\n",
" google.colab.notebook.generateWithVariable('results_df');\n",
" }\n",
" })();\n",
" </script>\n",
" </div>\n",
"\n",
" </div>\n",
" </div>\n"
],
"text/plain": [
" Modèle Vectorisation \\\n",
"2 Random Forest BoW \n",
"7 Regression Logistique TF-IDF \n",
"0 Naive Bayes BoW \n",
"5 SVM Lineaire TF-IDF \n",
"9 Naive Bayes TF-IDF \n",
"1 Naive Bayes TF-IDF \n",
"15 Regression Logistique TF-IDF \n",
"8 Naive Bayes BoW \n",
"13 SVM Lineaire TF-IDF \n",
"10 Random Forest BoW \n",
"6 Regression Logistique BoW \n",
"3 Random Forest TF-IDF \n",
"4 SVM Lineaire BoW \n",
"14 Regression Logistique BoW \n",
"12 SVM Lineaire BoW \n",
"11 Random Forest TF-IDF \n",
"\n",
" Meilleurs paramètres Accuracy Precision \\\n",
"2 {'max_depth': 'None', 'min_samples_split': '5'... 0.771925 0.779801 \n",
"7 {'C': '1.0', 'max_iter': '1000', 'solver': 'li... 0.785225 0.779591 \n",
"0 {'alpha': '2.0'} 0.772075 0.778523 \n",
"5 {'C': '0.1', 'dual': 'True', 'max_iter': '1000'} 0.785400 0.778086 \n",
"9 {'alpha': '2.0'} 0.770400 0.777618 \n",
"1 {'alpha': '2.0'} 0.769575 0.776643 \n",
"15 {'C': '1.0', 'max_iter': '1000', 'solver': 'sa... 0.783150 0.776595 \n",
"8 {'alpha': '2.0'} 0.771800 0.776557 \n",
"13 {'C': '0.1', 'dual': 'True', 'max_iter': '1000'} 0.784200 0.776513 \n",
"10 {'max_depth': 'None', 'min_samples_split': '5'... 0.768850 0.774589 \n",
"6 {'C': '0.1', 'max_iter': '1000', 'solver': 'li... 0.782925 0.772292 \n",
"3 {'max_depth': 'None', 'min_samples_split': '5'... 0.767500 0.770202 \n",
"4 {'C': '0.01', 'dual': 'True', 'max_iter': '1000'} 0.782225 0.768696 \n",
"14 {'C': '0.1', 'max_iter': '1000', 'solver': 'sa... 0.779250 0.767199 \n",
"12 {'C': '0.01', 'dual': 'True', 'max_iter': '1000'} 0.780050 0.765526 \n",
"11 {'max_depth': 'None', 'min_samples_split': '5'... 0.764800 0.763587 \n",
"\n",
" Recall F1 Score ROC AUC Temps d'entraînement (s) \\\n",
"2 0.75785 0.768669 0.851527 17489.656235 \n",
"7 0.79530 0.787367 0.866710 65.192424 \n",
"0 0.76050 0.769406 0.844132 2.815040 \n",
"5 0.79855 0.788185 0.866477 122.321674 \n",
"9 0.75740 0.767376 0.851067 4.320478 \n",
"1 0.75680 0.766593 0.852443 4.272107 \n",
"15 0.79500 0.785690 0.861098 36.014652 \n",
"8 0.76320 0.769820 0.843168 1.472637 \n",
"13 0.79810 0.787158 0.861249 51.149676 \n",
"10 0.75840 0.766409 0.843950 6377.436032 \n",
"6 0.80245 0.787082 0.858402 896.353782 \n",
"3 0.76250 0.766332 0.848404 16182.080744 \n",
"4 0.80740 0.787573 0.858312 458.623438 \n",
"14 0.80180 0.784118 0.851395 371.537341 \n",
"12 0.80740 0.785905 0.851585 187.807479 \n",
"11 0.76710 0.765340 0.841649 5977.548638 \n",
"\n",
" Run ID \\\n",
"2 7d52e808227f468da7f733265280fc02 \n",
"7 94a0cf29a68b402ab37098a44d1c1ee6 \n",
"0 a920f241d90d4816b858b16d68a6b38f \n",
"5 767fe1513d6445029ca90ca2156ab653 \n",
"9 daf72838a04d4f2f962d5b7cf6e41a82 \n",
"1 f0f4a385cb0b463c97ad736eaa98ce9f \n",
"15 741178bfcba54908aaa50c4f44d6db71 \n",
"8 9635b8567b874dd6bc91da23d1cb3b78 \n",
"13 bb39934ff22f4021a379983c32d828ed \n",
"10 623f3412438b4b6a8bb0d112c917dcea \n",
"6 75396e8531354bd5b168f8dd7e6f4135 \n",
"3 7dda37625478423abcfd9292dac05580 \n",
"4 eaa8dfac819b44e39fe7b7baaf490de7 \n",
"14 b88c00415c1340ec9dd4e6db806f2de3 \n",
"12 5c07f55dbafc4c899c5c2a9c3de4a742 \n",
"11 cb84d2c0f188472990425e7300f744ef \n",
"\n",
" Run Name \n",
"2 Modele_Simple_Random_Forest_BoW \n",
"7 Modele_Simple_Regression_Logistique_TF-IDF \n",
"0 Modele_Simple_Naive_Bayes_BoW \n",
"5 Modele_Simple_SVM_Lineaire_TF-IDF \n",
"9 Modele_Simple_Naive_Bayes_TF-IDF \n",
"1 Modele_Simple_Naive_Bayes_TF-IDF \n",
"15 Modele_Simple_Regression_Logistique_TF-IDF \n",
"8 Modele_Simple_Naive_Bayes_BoW \n",
"13 Modele_Simple_SVM_Lineaire_TF-IDF \n",
"10 Modele_Simple_Random_Forest_BoW \n",
"6 Modele_Simple_Regression_Logistique_BoW \n",
"3 Modele_Simple_Random_Forest_TF-IDF \n",
"4 Modele_Simple_SVM_Lineaire_BoW \n",
"14 Modele_Simple_Regression_Logistique_BoW \n",
"12 Modele_Simple_SVM_Lineaire_BoW \n",
"11 Modele_Simple_Random_Forest_TF-IDF "
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"results_df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "w0WL6NSxWxlQ"
},
"outputs": [],
"source": [
"def afficher_tableau_comparatif(df):\n",
" \"\"\"\n",
" Affiche un tableau comparatif formaté des résultats\n",
"\n",
" Parameters:\n",
" -----------\n",
" df : pandas.DataFrame\n",
" Dataframe contenant les résultats à afficher\n",
" \"\"\"\n",
" if df.empty:\n",
" print(\"Aucun résultat à afficher.\")\n",
" return\n",
"\n",
" # Sélectionner et formater les colonnes pour l'affichage\n",
" df_display = df[[\"Modèle\", \"Vectorisation\", \"Accuracy\", \"Precision\", \"Recall\",\n",
" \"F1 Score\", \"ROC AUC\", \"Temps d'entraînement (s)\"]]\n",
"\n",
" # Formater les valeurs numériques\n",
" for col in [\"Accuracy\", \"Precision\", \"Recall\", \"F1 Score\", \"ROC AUC\"]:\n",
" if col in df_display.columns:\n",
" df_display[col] = df_display[col].apply(lambda x: f\"{x:.4f}\" if x is not None else \"N/A\")\n",
"\n",
" if \"Temps d'entraînement (s)\" in df_display.columns:\n",
" df_display[\"Temps d'entraînement (s)\"] = df_display[\"Temps d'entraînement (s)\"].apply(\n",
" lambda x: f\"{x:.2f}\" if x is not None else \"N/A\")\n",
"\n",
" # Afficher le tableau\n",
" print(\"\\n=== Comparaison des modèles ===\\n\")\n",
" print(df_display.to_string(index=False))\n",
"\n",
" # Identifier le meilleur modèle selon la précision\n",
" if \"Precision\" in df.columns:\n",
" best_idx = df[\"Precision\"].idxmax() if not df.empty else None\n",
" if best_idx is not None:\n",
" best_model = df.loc[best_idx]\n",
" print(\"\\n=== Meilleur modèle ===\")\n",
" print(f\"Modèle: {best_model['Modèle']}\")\n",
" print(f\"Vectorisation: {best_model['Vectorisation']}\")\n",
" print(f\"Precision: {best_model['Precision']:.4f}\")\n",
" print(f\"Meilleurs paramètres: {best_model['Meilleurs paramètres']}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 8,
"status": "ok",
"timestamp": 1751740004112,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "Ou07_WmDWxlQ",
"outputId": "4f3f00f7-3000-47f0-f2e1-ad34d27716d1"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"=== Comparaison des modèles ===\n",
"\n",
" Modèle Vectorisation Accuracy Precision Recall F1 Score ROC AUC Temps d'entraînement (s)\n",
" Random Forest BoW 0.7719 0.7798 0.7579 0.7687 0.8515 17489.66\n",
"Regression Logistique TF-IDF 0.7852 0.7796 0.7953 0.7874 0.8667 65.19\n",
" Naive Bayes BoW 0.7721 0.7785 0.7605 0.7694 0.8441 2.82\n",
" SVM Lineaire TF-IDF 0.7854 0.7781 0.7985 0.7882 0.8665 122.32\n",
" Naive Bayes TF-IDF 0.7704 0.7776 0.7574 0.7674 0.8511 4.32\n",
" Naive Bayes TF-IDF 0.7696 0.7766 0.7568 0.7666 0.8524 4.27\n",
"Regression Logistique TF-IDF 0.7832 0.7766 0.7950 0.7857 0.8611 36.01\n",
" Naive Bayes BoW 0.7718 0.7766 0.7632 0.7698 0.8432 1.47\n",
" SVM Lineaire TF-IDF 0.7842 0.7765 0.7981 0.7872 0.8612 51.15\n",
" Random Forest BoW 0.7689 0.7746 0.7584 0.7664 0.8439 6377.44\n",
"Regression Logistique BoW 0.7829 0.7723 0.8024 0.7871 0.8584 896.35\n",
" Random Forest TF-IDF 0.7675 0.7702 0.7625 0.7663 0.8484 16182.08\n",
" SVM Lineaire BoW 0.7822 0.7687 0.8074 0.7876 0.8583 458.62\n",
"Regression Logistique BoW 0.7792 0.7672 0.8018 0.7841 0.8514 371.54\n",
" SVM Lineaire BoW 0.7801 0.7655 0.8074 0.7859 0.8516 187.81\n",
" Random Forest TF-IDF 0.7648 0.7636 0.7671 0.7653 0.8416 5977.55\n",
"\n",
"=== Meilleur modèle ===\n",
"Modèle: Random Forest\n",
"Vectorisation: BoW\n",
"Precision: 0.7798\n",
"Meilleurs paramètres: {'max_depth': 'None', 'min_samples_split': '5', 'n_estimators': '200'}\n"
]
}
],
"source": [
"# Afficher le tableau comparatif\n",
"afficher_tableau_comparatif(results_df)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "etUvFhueWxlQ"
},
"outputs": [],
"source": [
"def display_metrics_subplot(df, figsize=(18, 14)):\n",
" \"\"\"\n",
" Génère des sous-graphiques pour visualiser toutes les métriques avec une échelle fixe (0,1)\n",
"\n",
" Parameters:\n",
" -----------\n",
" df : pandas.DataFrame\n",
" DataFrame contenant les résultats des modèles avec leurs métriques\n",
" figsize : tuple, optional\n",
" Taille de la figure (largeur, hauteur)\n",
" Returns:\n",
" --------\n",
" matplotlib.figure.Figure\n",
" La figure générée\n",
" \"\"\"\n",
" # Définir les métriques à visualiser\n",
" metriques = [\"Accuracy\", \"Precision\", \"Recall\", \"F1 Score\", \"ROC AUC\"]\n",
"\n",
" # Créer une figure avec des sous-graphiques\n",
" fig, axes = plt.subplots(3, 2, figsize=figsize)\n",
" axes = axes.flatten()\n",
"\n",
" # Définir une palette de couleurs cohérente\n",
" palette = sns.color_palette(\"Set2\", len(df[\"Vectorisation\"].unique()))\n",
"\n",
" # Créer un barplot pour chaque métrique\n",
" for i, metrique in enumerate(metriques):\n",
" ax = axes[i]\n",
"\n",
" # Créer le barplot\n",
" sns.barplot(\n",
" x=\"Modèle\",\n",
" y=metrique,\n",
" hue=\"Vectorisation\",\n",
" data=df,\n",
" ax=ax,\n",
" palette=palette\n",
" )\n",
"\n",
" # Configurer l'axe y pour qu'il aille de 0 à 1\n",
" ax.set_ylim(0, 1)\n",
"\n",
" # Ajouter une grille horizontale pour faciliter la lecture\n",
" ax.grid(axis='y', linestyle='--', alpha=0.7)\n",
"\n",
" # Définir le titre du sous-graphique\n",
" ax.set_title(f\"{metrique}\", fontsize=14, fontweight='bold')\n",
"\n",
" # Rotation des étiquettes de l'axe x pour éviter le chevauchement\n",
" ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right')\n",
"\n",
" # Ajouter des valeurs au-dessus des barres\n",
" for p in ax.patches:\n",
" height = p.get_height()\n",
" ax.text(\n",
" p.get_x() + p.get_width()/2.,\n",
" height + 0.01,\n",
" f'{height:.3f}',\n",
" ha=\"center\", fontsize=9\n",
" )\n",
"\n",
" # Ne garder la légende que sur le premier graphique\n",
" # if i != 0:\n",
" # ax.get_legend().remove()\n",
"\n",
" # Ajuster la disposition pour éviter le chevauchement\n",
" plt.tight_layout()\n",
"\n",
" # Ajouter un titre global\n",
" fig.suptitle(\"Comparaison des performances des modèles par métrique\",\n",
" fontsize=18, fontweight='bold', y=1.02)\n",
"\n",
" # Afficher la figure (si dans un notebook)\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "lv_FsbT_OlC1"
},
"outputs": [],
"source": [
"def display_metrics_subplot(df, figsize=(18, 14)):\n",
" \"\"\"\n",
" Génère des sous-graphiques pour visualiser toutes les métriques.\n",
" Le graphique de Précision occupe 2 colonnes.\n",
"\n",
" Parameters:\n",
" -----------\n",
" df : pandas.DataFrame\n",
" DataFrame contenant les résultats des modèles avec leurs métriques\n",
" figsize : tuple, optional\n",
" Taille de la figure (largeur, hauteur)\n",
" Returns:\n",
" --------\n",
" matplotlib.figure.Figure\n",
" La figure générée\n",
" \"\"\"\n",
" if df.empty:\n",
" print(\"Aucun résultat à afficher.\")\n",
" return None # Retourne None si le dataframe est vide\n",
"\n",
" # Définir les métriques à visualiser (sans la Précision initiale)\n",
" other_metriques = [\"Accuracy\", \"Recall\", \"F1 Score\", \"ROC AUC\"]\n",
"\n",
" # Créer une figure et une grille de sous-graphiques (3 lignes, 2 colonnes)\n",
" fig = plt.figure(figsize=figsize)\n",
" gs = fig.add_gridspec(3, 2)\n",
"\n",
" # Définir les axes pour la Précision (span 2 colonnes)\n",
" ax_precision = fig.add_subplot(gs[0, :]) # La Précision occupe la première ligne, les deux colonnes\n",
"\n",
" # Définir les axes pour les autres métriques (remplir les 2 lignes restantes)\n",
" axes_other = [fig.add_subplot(gs[1, 0]), fig.add_subplot(gs[1, 1]),\n",
" fig.add_subplot(gs[2, 0]), fig.add_subplot(gs[2, 1])]\n",
"\n",
" # Définir une palette de couleurs cohérente\n",
" palette = sns.color_palette(\"Set2\", len(df[\"Vectorisation\"].unique()))\n",
"\n",
" # Fonction utilitaire pour ajouter les valeurs au-dessus des barres\n",
" def add_value_labels(ax):\n",
" for p in ax.patches:\n",
" height = p.get_height()\n",
" if height > 0: # N'affiche le label que si la hauteur est > 0\n",
" ax.text(\n",
" p.get_x() + p.get_width()/2.,\n",
" height + 0.01,\n",
" f'{height:.3f}',\n",
" ha=\"center\", fontsize=9\n",
" )\n",
"\n",
"\n",
" # Tracer le graphique de Précision\n",
" sns.barplot(\n",
" x=\"Modèle\",\n",
" y=\"Precision\",\n",
" hue=\"Vectorisation\",\n",
" data=df,\n",
" ax=ax_precision,\n",
" palette=palette\n",
" )\n",
" ax_precision.set_ylim(0, 1)\n",
" ax_precision.grid(axis='y', linestyle='--', alpha=0.7)\n",
" ax_precision.set_title(\"Precision (Métriques clé)\", fontsize=14, fontweight='bold')\n",
" ax_precision.set_xticklabels(ax_precision.get_xticklabels(), rotation=45, ha='right')\n",
" ax_precision.set_xlabel(\"\") # Masquer l'étiquette x pour la Précision si désiré\n",
" add_value_labels(ax_precision) # Ajouter les valeurs\n",
"\n",
"\n",
" # Tracer les graphiques pour les autres métriques\n",
" for i, metrique in enumerate(other_metriques):\n",
" ax = axes_other[i]\n",
"\n",
" sns.barplot(\n",
" x=\"Modèle\",\n",
" y=metrique,\n",
" hue=\"Vectorisation\",\n",
" data=df,\n",
" ax=ax,\n",
" palette=palette\n",
" )\n",
"\n",
" ax.set_ylim(0, 1)\n",
" ax.grid(axis='y', linestyle='--', alpha=0.7)\n",
" ax.set_title(f\"{metrique}\", fontsize=14, fontweight='bold')\n",
" ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha='right')\n",
" add_value_labels(ax) # Ajouter les valeurs\n",
"\n",
" # Ne garder la légende que sur l'un des graphiques (par exemple, le premier des autres)\n",
" if i != 0:\n",
" ax.get_legend().remove()\n",
" else:\n",
" # Placer la légende de la précision à un endroit approprié si on ne veut pas de légendes répétées\n",
" # ax_precision.legend(loc='upper right') # Exemple de placement\n",
"\n",
" # Ou retirer la légende de la précision si on la garde sur le premier des autres\n",
" ax_precision.get_legend().remove() # Retirer la légende de la précision\n",
"\n",
" # Ajuster la disposition pour éviter le chevauchement\n",
" plt.tight_layout()\n",
"\n",
" # Ajouter un titre global\n",
" fig.suptitle(\"Comparaison des performances des modèles\",\n",
" fontsize=18, fontweight='bold', y=1.02) # Ajuster y si nécessaire\n",
"\n",
" # Afficher la figure (si dans un notebook)\n",
" plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"executionInfo": {
"elapsed": 2184,
"status": "ok",
"timestamp": 1751740006350,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "Noe09nRHWxlQ",
"outputId": "93abb9a5-d96c-424d-f81f-693fde709861"
},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABv0AAAWZCAYAAABe3dlUAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd8FNX+//H3ppJCGqEHEpDQexcQQu9IkSKCIggIihRRUbwXvBevIoJUsaAISBUFaQICoaMSeujSpEhPgSSk7u8PftlvNrtpJCFZeT0fjzwemTPnzHzO7uxMMp89ZwxGo9EoAAAAAAAAAAAAADbLLq8DAAAAAAAAAAAAAJA9JP0AAAAAAAAAAAAAG0fSDwAAAAAAAAAAALBxJP0AAAAAAAAAAAAAG0fSDwAAAAAAAAAAALBxJP0AAAAAAAAAAAAAG0fSDwAAAAAAAAAAALBxJP0AAAAAAAAAAAAAG0fSDwAAAAAAAAAAALBxDnkdAAAAAAAAAB6/mzdvau7cuTIajZKkIUOGqESJEnkcFQAAAB4VST8AAAAAAIAnUJEiReTt7a2RI0dKkrZt26Zt27bJwYHbRQAAALaI6T0BAADwRAoKCpLBYDD9DBgwIK9Dyte2b99u9noZDAZdvHgxr8N6Ii1dulRt2rRR0aJF5ejoyHuCPDVgwACzYzAoKCivQ4Kydo174403NH36dEnSrl27NH78+Ezt4+LFixbXhe3bt2c/eAAAADwyvroFAACQyunTp7V06VLt3r1bZ86c0d27dxUXFydPT08FBgaqYcOG6tKli5o1ayaDwZDX4QJ4gowZM0afffZZXocB4B9m5MiRMhqNGj16tKZMmaJnnnlGnTp1yuuwAAAAkEUk/QAAAP6/69eva/jw4Vq9erXp2TYp3b59W7dv39a+ffv02WefqUePHlq5cmUeRArgSfTXX39pxowZeR0GgH+oUaNGSZJGjx6tF198UQcPHlRAQECexgQAAICsIekHAAAgKSQkRB07dtTNmzcz3eb27du5GBFy27Jly/TgwQPTsru7ex5GA2Rs//79SkpKMiubO3euWrZsKUdHR0mSn59fXoQG4B9i1KhRpuQfAAAAbA9JPwAA8MS7dOmS1YRfiRIl9MYbb6hp06by9fVVZGSkjh07pg0bNmj16tV5EyxyTLFixfI6BCBL7t+/b1E2dOhQphkGAAAAAEiS7PI6AAAAgLw2duxYi4Rf06ZNdeLECb3zzjt6+umnFRgYqDp16mjAgAFasWKFzp07p27dulndXnx8vL7//nv16NFD/v7+cnV1lYuLi0qVKqUuXbrom2++UWxsrNW227dvl8FgMPu5ePGiQkND9fzzz6tYsWJydXVVtWrVNG3aNCUkJJjarl+/Xq1atZKPj4/c3NxUp04dff7551anKpVksZ/vvvtODx480EcffaQaNWrI3d1dXl5eatGiRbpJzlWrVum9995Tu3btVKlSJRUtWlROTk5yc3NT6dKl1aFDB33++edWExaSdPHiRYtYtm/fruvXr+uNN95QuXLlVKBAAbPExvHjx/Xpp5/qhRdeUO3atVW6dGm5u7vLyclJhQsXVsOGDTV27FidPHkyzbiDgoLM9jlgwACLOmFhYfrf//6npk2bqmjRonJ2dparq6v8/f1Vv359DRkyRPPmzdOVK1es7sNoNGrt2rXq27evypUrp4IFC8rZ2VnFixdXmzZtNH36dEVGRmbpdYmIiND777+vypUry8XFRV5eXmrZsqU2btyYZl8za9++feratasKFy4sFxcXVaxYUe+//77u3buXpe08ePBA8+bNU5cuXVSqVCm5uLjI3d1d5cuX16BBg/THH3+k237r1q3q37+/KlasqIIFC8rR0VG+vr6qVKmSOnXqpA8++ECbNm3Kcv/Sek3Dw8P17rvvqmLFinJxcZGvr686d+6sHTt2ZLjNvXv3asiQIapSpYo8PT3l5OSk4sWLq3379vrmm28UHx9vtV1an/WzZ89q4MCBKl26tJycnBQQEKCJEyemeYza2dmZ2gcFBVmsP3v2rN566y3Vq1dPhQoVkqOjo3x8fFSrVi2NHDlSx48fT7Nv1j4jSUlJ+vLLL9WoUSN5eXmZzh2S9N1331n0SZL27NmjLl26yNfXV+7u7qpXr57mz59vtq9FixapUaNG8vDwkIeHh5o0aaLly5enGVtunXey8/k6evSoRo0apXr16snX19d0PqpWrZqGDBmiDRs2pNl248aN6t+/vwIDA1WwYEEVKFBApUqVUvfu3fXDDz+keR6XHj6HdtSoUapdu7a8vb3l6OgoT09PlStXTkFBQRo7dqxWrFihqKioDPtgTUJCgmbNmqU6derIzc1N3t7eat68+SNNb33x4kW9++67atCggek1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GT0HLSwsTP/5z39Ut25deXp6ysnJSSVLllT37t3166+/WtT/7rvvzF6j2NhYffjhhypfvrycnZ3l5+ensWPHKjY2NsP3JVlMTIyWLVtmWu7Ro0e69e3sHv6rGBsba5YsvH79ulasWCHp4SiujKxdu1bPPvusihcvLicnJ3l5ealZs2b65ptvTNOMSv/3Gu7YscNUtmDBAqvTh6aeAjQ0NFRdu3ZVoUKF5OzsrJMnT2b4TL/bt29r6NChKlq0qFxcXFS3bl398MMPFsdnyilL05t6NPV7llpsbKxmz56tpk2bysfHR05OTipevLh69uypffv2WX3tvvvuOwUFBcnX11eOjo7y9vZWhQoV1Lt3b33++ecZvvYpXblyRe+8845q1aolDw8PFShQQKVLl1bXrl2tHoNpiYyM1EcffaQGDRqYjuVSpUrphRde0IEDB6y2KVmypJ5++mlJUnh4uH766acsxQ4AAAAg8xjpBwAAACDHPf3001q8eLFp+fr161brDRw4ULt27bIoj4mJUZcuXbRlyxaz8tu3b2vDhg3asGGDxowZo6lTp5qt/+abbzR8+HDFxcWZlV+4cEEXLlxQw4YNVbdu3XRjP3HihBo2bKjo6Giz8suXL+vy5csKDg5Ws2bNVLJkyXS3Iz2c1rJNmza6cuWKWfm1a9e0atUqrVq1SiNHjtT06dPT3EarVq20e/du0/LVq1c1depU3bx5UwsXLswwBkn67bffdO/ePUlS4cKFFRgYmG796tWrKywsTJcuXdIXX3yhcePGycHBQXPnzlV8fLwkqUuXLlq1apXV9klJSRowYIAWLVpkVh4REaGdO3dq586dWr58udauXZupEaBpOXr0qBo2bKioqKhMtwkLC1Pjxo115swZU9mBAwfUq1cvde7c+ZFjScutW7fUpk0bHT582Kz8+vXrWrlypX766SdNmzZNI0eONK2bOHGiPvjgA7P64eHhCg8P15kzZ7Rjxw4NHz48U/vfsGGD+vTpY3r/kyUfzwEBAWrdunWG2zl79qzatGlj8ezGK1euaMmSJVq+fLk+//xzDRkyxKLt008/rb1790qSfv31V7MvBQAAAADIOST9AAAAAOS41KOXihUrZrXerl27VKVKFXXu3FlGo9E0emz06NGmhJ+Tk5P69OmjwMBAHTt2TD/88IOMRqOmTZumOnXqqG/fvpIeJraGDBliGkHm4OCgnj17qmLFirpy5Yo2btyYqdgXLFhgSvj5+fmpX79+cnNz05UrVxQaGprp59klJCSoW7dupoSfvb29+vfvLz8/P61evVqhoaGSHk6XWbt2bbNRjint3r1b3bp1U+XKlbV48WJT0mXx4sX6+OOP03yeWkopE6t16tTJsL69vb1ef/11vfXWW7p69apWrlyp7t27m0b9lS1bVp06dUoz6ffJJ5+YEn4Gg0E9evRQjRo1dOHCBS1atEjx8fH69ddf9d5772nq1Kmm5yLOnTtX58+flyTVrVtXvXv3Nm3Tx8fHYj+HDh2Sg4OD+vfvr8DAQJ06dUoFChRIt2/vv/++WcKvWbNmatasmfbs2aO1a9dm+NpkVf/+/U0Jv4IFC6pv377y8/PTnj17tHHjRiUlJWn06NGqW7euGjduLEmaO3euqX2rVq0UFBSkqKgoXb58Wbt371ZMTEym9n3p0iX17NnTdDwbDAZ16dJFNWvW1K1bt7Rt27ZMbScxMVHdunUzHXuFCxdW37595ePjo02bNmnv3r1KTEzU8OHDVb9+fdWsWdOsfb169Uy/W0vyAwAAAMgZJP0AAAAAZNvGjRt1+/ZtxcXF6bfffjNLnhQtWjTNZ941bNhQwcHBZomau3fv6ptvvjEtf/HFF3r55ZdNy76+vqbpDT/99FNT0m/KlCmmhJ+dnZ22bdumZ555xtQuLi5ON2/ezLAvDx48MP3+2muvady4cWbrw8LCMtyGJK1bt06nT582Lc+aNUvDhg2TJL333nuqVKmSLl26JEmaNm1amkm/UaNG6bPPPpMk9ezZ05RQSUpK0oEDBzKV9Dt37pzp91KlSmUq/ldeeUUTJ05UVFSUZs6cqbi4ON24cUPSw9cleQrQ1JKSksxGYP7rX/8yG7VWsWJFvf3225Ievrf/+9//TM9FXLdunSnpV6VKlUw9l2/lypV69tlnzcpSj0ZLFh8frwULFpiWmzZtqm3btsnOzk5Go1Ht2rXT5s2bM9xnZh09elSbNm0yLf/8889q3ry5aTl5qlqj0aipU6eakn4pj8FFixZZJM2TX6OMzJw502zE6vfff2/6vEgP36u//vorw+2sX79ex48fl/QwIbxnzx7TaNHx48erVq1aOnbsmBITEzV79mzNmzfPrL2fn5/p94sXLyopKSnN4wcAAADAoyPpBwAAACDbli9fruXLl1uUFyhQQAsWLEhz9NXYsWMt1v3+++9KSEgwLQ8cOFADBw602v7w4cOKjo6Wq6ur2RSYbdu2NUv4SQ9HDKZMPqTlmWee0cyZMyU9HBW2Zs0aVaxYURUqVFCDBg30zDPPZOp5dqlHO6ZM6rm4uKhXr16aMmWKpIfJoeR+pJZyGscKFSqYrctsAvLWrVum362NmLPGy8tLL774oubOnat9+/aZkkNubm4aNGhQmqP8Tp8+rdu3b5uW//Of/+g///mP1brR0dE6deqUatSokamYUqtatapFwi89p0+fNpsK9PnnnzclnwwGg1544YUcTfolP8syWYsWLdKsmzz9pfTwGFy/fr2kh31s0KCBAgMDVaVKFTVv3lzlypXL1P5TfiYqVapklvCTHibHrT2HMrWU/UhMTFT58uXTrGvt2X6FChUy/Z6UlKQ7d+6ocOHCGe4XAAAAQNaQ9AMAAACQo1xcXOTv768WLVpo9OjR6SYoKlasaFF29+7dTO/LaDTqzp07cnV1NWtXpkyZrAWdwnPPPaexY8dq1qxZio2N1b59+8wSeP7+/lq/fr2qVKmS7nZSxuPu7i43Nzez9UWLFjXrR3h4uNWkX8qkTOrn3yWPbMwtI0aMME01efXqVUnSSy+9JE9PzzTbZOX9kx4+q+5RWTt+srKvIkWKmC2nfE/SYzQazZZjY2Ot1svKa5EyMTt37lz16tVLv/32m+7cuaMNGzaY1e3Vq5eWLl2a4Wi5nPpMZKUf1t7P1K8XAAAAgNxB0g8AAABAts2fP18DBgzIcrvUiTDJciTa6NGj053CMjkB5ePjY5q+88KFC1mOJaUpU6bo/fff1969e3Xq1CmdOXNGa9as0bVr13Tp0iUNHz5cO3bsSHcbKftx//59RUVFmfU3eapM6eEoMy8vL6vbcXR0NKv3KHx9fU2/Z3Z0oPRwdFibNm1Mo98MBoNGjBiRbpvU79/MmTPVuXPnNOun9bzHzLB2/KQn9WucerrXlO9JaikTbKmfqXf27FmrbVK/Fv/5z3/k4uKSYZylSpXSvn379Oeff+qPP/7Q2bNndezYMf38889KSEjQihUr1K5dO7NpbzPaf3Y+Eym34+LiomPHjqU52tXBwfI2Q8qkoZ2dndnIPwAAAAA5h6QfAAAAgHylQYMGsre3V2JioqSHSS9rz3a7ePGiTp8+LQ8PD0lSkyZN9NNPP0mSNm/erD179piekSZJCQkJunHjhkqWLJnu/i9cuCBvb295eXmpffv2at++vSSpTZs26t69uyTp4MGDGfYj9XMMFy5caHqmX0xMjFasWGFaV6NGDauj/HJK2bJlTb9fvnw5S21HjhxpSvq1bt06w9F1FSpUUKFChXTnzh1J0o8//qjhw4dbJIlOnDihrVu3miURUyY4Uz6LLqdUrFhR7u7uun//viRp6dKlGjJkiOmZfosXL06zbcqE4aFDhxQXFycnJyddvXrV7DmBKaU+Bnx9fU3HQErHjx83S8YeOXJE1apVU7ly5cxGyj777LNas2aNpIfHYEZJvyZNmuiPP/6QJJ08eVLLli1Tnz59TOuNRqMuX76s0qVLp7udlP2IiYlRcHCwXnnlFbM6CQkJ+vnnn+Xr62sxjW7KY87f35/n+QEAAAC5hKQfAAAAgHzFx8dHAwcO1Ndffy1J+uSTTxQSEqJGjRqpQIECunr1qn777TcdOnRIL730ktq2bStJeuutt7R69WolJSUpMTFRzZs3V69evVShQgVdv35dmzZt0uuvv65Ro0alu//ly5drwoQJCgoKUmBgoIoXL66oqCgtXbrUVCetUXkpdezYURUqVNDp06clPZwqc//+/SpZsqRWr16tS5cumeqOHj06i69S1qRMfmYmYZlS+/bt9fPPPyspKUnVqlXLsL6dnZ3GjBmj8ePHS5J27NihqlWrqnPnzvLy8tK1a9e0Z88eHT58WM2aNTNL+qVMyK5fv17jxo2Tr6+vfH19H2kkaWoODg568cUX9fnnn0uSdu7cqRYtWqhZs2bas2ePtm7dmmbbevXqmZ5j+Oeff6p27dqqVKmSgoODTQnO1GrUqKHWrVvr119/lSS9/vrr+uWXX1SnTh3Z2dnp0qVL2rt3r06ePKkJEyaoSZMmkqTevXsrIiJCzZs3V8mSJeXj46Nz586ZTfOZmWPwjTfe0Ny5c00jE/v27avly5erZs2aCgsL0/bt2xUUFKTp06enu52OHTuqUqVKOnnypCRpyJAhWrt2rerUqaOkpCSdPn1aW7du1a1btzR//nw1a9bMrH1ISIjp99TP2gQAAACQc0j6AQAAAMh3pk+frgsXLmjLli2SpG3btmnbtm3ptmnYsKG++uorDR8+XHFxcYqPj0935FZ64uLitHnzZtMIt9TefvvtDLfh4OCgVatWqU2bNrpy5YoSExM1f/58i3pvvPGGXnzxxUeKM7MaNWokV1dXRUdH68aNG7pw4UKmn/FmMBjUpUuXLO1v3LhxOnXqlBYtWiRJOnXqlE6dOpVhu+7du5tGzUVHR2vy5MmSpCpVquRI0k+SJk2apC1btujMmTOSHiYlk6dq7dixo9avX2+13aBBgzR16lRTgu/48eM6fvy47Ozs1LZtW23atMlqu++//15t27bV4cOHlZSUpLVr12rt2rUZxnn9+nWzRHNKPj4+FiPtrPH399fKlSvVp08f3bt3T0ajUatXr9bq1atNdYKCgjLcjoODg1avXq22bdvq4sWLMhqNWrNmjWnUYUZSPhOzdevWmWoDAAAAIOuYUwMAAABAvuPq6qpNmzZpyZIl6tChg4oWLSoHBwe5uLjoqaee0nPPPaevvvpK06ZNM2s3aNAgHT58WMOGDVPFihXl6uoqZ2dnlSpVSs8995xpJFV6unbtqn//+99q1aqVAgIC5OrqKgcHBxUvXlwdO3bUmjVrMnyuXbJKlSrpyJEjmjhxomrXri13d3fTtrp166ZNmzZpxowZj/QaZYWbm5t69+5tWl65cmWu7s/Ozk4LFy7U+vXr1aNHD/n5+cnJyUnOzs7y9/dX586dNX36dIukVpcuXTR79mxVqlRJTk5OuRKbt7e3du/ercGDB6tw4cIqUKCAatWqpcWLF1udRjZZkSJFtGPHDrVv317u7u5yc3NTixYttH37drMpM621+/333zV37ly1aNFCvr6+sre3l5ubmypWrKh+/fpp8eLFeuutt0xtPvroI7366quqU6eOihUrJkdHR7m6uqpixYoaPny4Dhw4IH9//0z1t0OHDjp+/LjeeustVa9eXe7u7nJ0dFSJEiXUsWNHdejQIVPbKV++vI4ePapPPvlEjRo1kre3t+zt7VWwYEFVr15dr7zyilatWqW+ffuatbt69aop6efp6WmaIhcAAABAzjMYjUZjXgcBAAAAAMhd+/fvV/369SVJtWvX1oEDB/I4ovxn+/btat68uWn5woULCggIyLuA/gE+++wzjRkzRtLD6U1nzZqVxxEBAAAA/1yM9AMAAACAJ0C9evXUqVMnSQ+f67d79+48jgj/dImJiZozZ44kycXFRePGjcvjiAAAAIB/NpJ+AAAAAPCE+Oijj2Rn9/DfwI8//jiPo8E/3U8//aRz585JevjsypIlS+ZxRAAAAMA/m0NeBwAAAAAAeDyqVq2qxMTEvA4DT4iePXuKJ4oAAAAAj0++eqbfiRMntGbNGl24cEFhYWEaO3as6ZkTaTl+/LgWLlyoy5cvq1ChQurRo4eCgoIeT8AAAAAAAAAAAABAPpCvpveMjY1VQECABg0alKn6N2/e1Mcff6wqVarok08+UceOHfXFF1/o8OHDuRsoAAAAAAAAAAAAkI/kq+k9a9WqpVq1amW6/ubNm1WkSBG9+OKLkiQ/Pz+dOnVK69evV82aNXMpSgAAAAAAAAAAACB/yVcj/bLq7NmzqlatmllZjRo1dObMmTTbxMfHKzo62uwnPj4+t0MFAAAAAAAAAAAAck2+GumXVeHh4fL09DQr8/T0VExMjOLi4uTk5GTRZtWqVVq5cqVpuXHjxho5cmSuxwoAAAAAAAAAAADkFptO+j2Kbt26qVOnTqZlg8EgSQoLC1NCQkJehQUAAAAAAAAAAABYcHBwkLe3d8b1HkMsucbLy0sRERFmZREREXJxcbE6yk+SHB0d5ejoaFGekJDANJ8AAAAAAAAAAACwSTb9TL/AwEAdO3bMrOzo0aMqX758HkUEAAAAAAAAAAAAPH75Kun34MEDXbx4URcvXpQk3bx5UxcvXtTt27clSUuWLNHs2bNN9du0aaObN2/q+++/19WrV7Vp0ybt27dPHTt2zIvwAQAAAAAAAAAAgDyRr6b3PHfunD744APT8sKFCyVJzZo102uvvaawsDBTAlCSihQponHjxmnBggXasGGDChUqpFdffVU1a9Z83KEDAAAAAAAAAAAAecZgNBqNeR1EfnDr1i2e6QcAAAAAAAAAAIB8xdHRUYULF86wXr6a3hMAAAAAAAAAAABA1pH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGwcST8AAAAAAAAAAADAxpH0AwAAAAAAAAAAAGycQ14HAOSE+Ph4TZw4UatWrZLBYFC3bt00ceJEOThYHuKBgYFmy3FxcQoMDNSWLVtyZD0AAMA/weP8++rvv//W+PHj9fvvv8tgMKhx48b63//+p0KFCuVS7wBkhHMAbAn3BAAAtoDrFR6HfJf027hxo9auXavw8HD5+/tr4MCBKleuXJr1169fr82bN+v27dvy8PBQgwYN1LdvXzk5OT3GqJHXZsyYoT/++EPBwcGSpH79+mnWrFkaPXq0Rd2zZ8+aLbdq1UpdunTJsfUAAAD/BI/z76vx48dLkv744w8ZjUa9/vrr+te//qXPP/88x/oDIGs4B8CWcE8AAGALuF7hcchX03vu3btXCxcu1HPPPafJkyfL399fH374oSIiIqzW3717t5YsWaKePXvqs88+06uvvqp9+/Zp6dKljzly5LVly5Zp5MiRKlq0qIoWLao33ngjU8fBoUOHdObMGfXq1StX1gMAkFJ8fLzGjx+vypUrq0qVKnr//feVkJBgtW5gYKDZj7+/v1q1apXp9VnZF2DN4/z76q+//lLnzp3l5uYmd3d3denSRadOncqxvgDIOs4BsCXcEwAA2AKuV3gc8lXSb926dWrZsqWaN28uPz8/DR48WE5OTqbMd2qnT59WhQoV1KRJExUpUkQ1atRQ48aN9eeffz7myJGXwsPD9ffff6tKlSqmsipVqujq1auKjIxMt+3SpUvVvHlzFStWLFfWA6nl5A1/Sdq8ebNat26tcuXKqXbt2lq4cKEk6erVqxbtS5curQEDBuR2FwGkI+W3+rZt26bff/9ds2bNslr37NmzZj+BgYEW39pLb31W9gWk9rj/vho8eLDWrVunyMhIRUREaPXq1WrdunXOdAZAlnEOgC3hngAA7rXAFnC9wuOSb5J+CQkJOn/+vKpVq2Yqs7OzU7Vq1XTmzBmrbSpUqKDz58+bknw3btzQoUOHVKtWrccSM/KHqKgoSZKnp6epLPn3+/fvp9kuOjpaa9asUd++fXNlPWBNTt7wDw4O1rvvvqsPPvhAp0+f1rZt29SoUSNJUsmSJc3aHj9+XB4eHnr22WcfSz8BWPc4v9X3qPsCpMf/91W9evV0+/Zt042aiIgIvf7669ntBoBHxDkAtoR7AgC41wJbwPUKj0u+SfpFRkYqKSlJXl5eZuVeXl4KDw+32qZJkybq1auX/vWvf+n555/XiBEjVLlyZXXv3j3N/cTHxys6Otr0ExMTY1pnMBhkMBjM6ieXZac8ZVlOled0jLbcJzc3N0nSvXv3TOXJ344oWLBgmttZt26dXFxc1LJlS6sxrl27Vi4uLmrVqpXV/Sa3T17/OF73rJbnp/eJPj20bNkyjRo1SsWKFTO7CZ9RjIcPHzbd0E8umzJlisaMGaNGjRrJ3t5e3t7eCgwMtBrLpk2bZDQa1b59+8fSV1t/n+gTfcqNGFN+qy+5rGrVqmbf6ksrlmXLlql58+YqXry41W0nry9WrJgMBoMiIiL0999/q2rVqqb6yfu6d+9ejr/uWS3Pz+8TfXpYnvrvK4PBYPXvq9Sxp/z7yNq2k/++atmypanMaDTq+eefV7169Uw3UOrVq2fxTynvE32iT48vdnd3d0kyuz4lXz+S11mL0dr/UCm3be0ckd45gPeJPmUmRmvXrJTHa1p9snY8ptx+evcMpPSP9+z2KSfKc/p1p0/0KT/3KeUXHosVK6aRI0ea7rWkF3vylyd79+5tKp8yZYpGjx6txo0by8HBQd7e3ipXrpzV7WzcuNF0r4X3iT5lFGN6f18VLFgwzW1ndL3J7vUsO336J75P+b1PmeGQ5Rb5yPHjx7Vq1Sq98sorCgwM1PXr1zV//nytXLlSzz33nNU2q1at0sqVK03LZcqU0eTJk+Xh4SGj0ShJio2NVVRUlNzc3OTs7GyqGxMTo5iYGBUsWFCOjo6m8qioKMXGxsrT01P29vam8nv37ik+Pl5eXl5mb05ERISSkpLk7e1tFltYWJjs7OzMsv1Go1FhYWFydHQ0+/AnJiYqIiJCzs7Opj9wpYdJzXv37snFxUUuLi6m8n9yn0qWLKmSJUvq4sWLqlWrlmJiYnT8+HH5+fnJ398/zT6tWLFC/fv3l4uLi9U+LVu2TD179lThwoWt9im5feHChXmf6FOm+pSUlKS///5bjRo1kre3t+7du2caxp8yVmt9+umnn9S8eXOVLFlSnp6eioqK0tGjR9WhQwc1adJE9+/fV5MmTTR16lQVL17cok8rV65Unz59VLhwYd4n+kSf8qhPt2/flvTw26HJ8ZQuXVrSw2/1lSpVymqfHB0dtWbNGs2bN0/e3t4WfYqKitKaNWs0ffp02dvby9PT0/QtwVKlSkmSHB0dTftKrsP7RJ/S65OXl5fZ31eSdPLkSZUoUcLs76vUfUr++8jX19dqn3744Qf17NlTbm5upj7dvn1bV65c0cCBA+Xj4yMXFxeNHj1a5cuX14MHD1SgQAHeJ/pEnx5zn7y9vVWiRAkdP35cNWrUkL29vYKDg+Xn56dChQql2aelS5ea/key1idr54jY2FhduXJFY8aMUbFixRQREaFXX31Vc+fOVWJionx9fXmf6FO6fbKzs1Px4sXNrlnBwcEqUaKEvL290+xTyv/prfVpxYoVeuGFF+Tg4GC1T0uXLlXfvn3NjnfeJ/pEnx5/n8LCwvT333+rRo0akh4OIGnUqJHpXoukNPu0fPlytW3bVpUqVZL08P+yo0ePqlWrVmratKnu3bunRo0aacqUKXJxcbHo0w8//KBu3brJ29ub94k+Zdgnb29vlSxZUidOnNBTTz0lT09P099XpUuXTrNPS5cuNd3Ts9anlNcza31asWKFnn/+eTk4OPA+2XifkhPHGTEYkzNdeSwhIUH9+vXTmDFjVL9+fVP57NmzFR0drbffftuizb///W8FBgaqf//+prKdO3fqq6++0sKFC00n9pTi4+MVHx9vWjYYDHJxcdGtW7dMcz2nfElSHgCPWp46G5sT5Tkdo633acqUKdqyZYu+//57GY1G9e/fX+3atdOYMWOs1j937pyaNWumXbt2qUyZMhbb/vPPP9WsWTPt3LlTTz31lEXsf/75p4KCgrRr1y6VLVs2V/r0T3yfnvQ+Xb16VfXr19exY8dUqFAhGY1G3blzR9WrV1dISIhKlChhNcbo6GjVqlVLM2bMUNu2bWUwGHTt2jXVrVtXlStX1vz58+Xt7a1x48bp5s2bWrFihVksV69eVcOGDbVp0yZVrlz5sfTVlt8n+kSfcivGsLAwValSRXv27DFdey5cuKDGjRvr5MmT8vDwsLqdFStW6KOPPlJISIgcHBwsYly+fLk++ugj7d+/Xw4ODjIYHo4qrFy5svbu3WtK0Fy8eFGNGzfWqVOnLL5FmNN9teX3iT79X3nKv68kqV+/fhZ/X6Wsn/Lvq7Jly1r0KeXfV2XLljXbZ6NGjdSpUyeNHj1aBoNBU6dO1U8//aSQkJAc7dM/8X2iT/Qpt8o/+eQTbdmyRYsWLZLBYDCdA0aPHm01xrNnzyooyPx/pJTbT+8c0bhxY3Xs2NG07WnTpumnn37SgQMHHktfbfl9ok8Py9O6ZiVfV1LXT/0/fep9pr5mpd5v8vGe+p4B7xN9ok+Pv09Xr15VvXr1dOzYMfn4+MhgMOjOnTuqVq2aQkJCTLOlpI49KipKtWvX1owZM9SuXTtJMt1rqVSpkhYsWCBvb2+98847unHjhlasWGG2nStXrqhhw4bauHGjaTaX3OrrP+F9ok8PpfX31ZgxY6zWz+h6k93rGe+TbfXJ0dHRYoCSNflmpJ+Dg4PKli2r0NBQU9IvKSlJoaGhphNvarGxsRYvurVEX0qOjo5mWdKUUr6o6ZVltTwntpHfyvNTLJI0atQohYWFqVmzZpKk7t27a8SIETIajXrnnXckSZMnTzbVX7p0qRo0aGC66Zp628nrU58sky1btszUPvX6/PTa5KdYcqo8P8WS1fLkb3pERkbKx8fH9HvyurSOpTVr1pgNwzcajXJ1dZUkDRw4UH5+fpKkN998U02aNFFUVJRpvfTweK1atapZwi+3+5qfXvecKs9PseRUeX6KJafK81Msqcu9vLxUvHhxHT9+XAEBAZKk0NBQlShRQh4eHmluZ8mSJerZs6fs7e3N1if/nrzewcHBVO7p6anixYsrNDTUlPRL3lfKhF9u9TWj8vzwfuR0eX6KJafKs/P3lbVjNeXfV6n3OX/+fE2cOFF16tSR0WhUlSpV9N133+V4n/LT65tT5fkplpwqz0+x5FR5fools+XJ54CgoCBJ/3cOkGT1HJDe/0hS+ueIb7/91uo54HG/lvnhdc/p8vwUS06VZ+WaJcn0JfLMHq9Go9HimpV6v8nt07pnkBN9svXy/BRLTpXnp1hyqjw/xfKo5cn3P5LvtRiNRkVEREiS2Yib1NtIOe188rrkbQ0aNEglS5aU9H/3WqKjo+Xq6mqqm3yvpUqVKrne1/z4ume3PD/FklPl2fn7ymi0/j9WRteb7F7PcqJPtlaen2J5lPLMyDcj/SRp7969mjNnjgYPHqxy5cppw4YN2rdvnz777DN5eXlp9uzZ8vHxMT3fY8WKFVq/fr2GDBlimt7z66+/VtmyZU3fEMysW7dumY0AxJPBaDQqOjratOzq6mqRSAZyUt26dfXBBx+oY8eOkh7Ou/3BBx9o//79abbp2rWr6tevr/fee8+svH79+ho9erSef/55SQ9H8TRp0kSnT582/WGblJSkhg0b6vXXX9eLL76YS70CkFnJ30JftGiRJJlGpqf1d0vyt/ZSfisvs+uzui8gp/D3FQAAAB4n7rUAeBLY3Eg/6eG0PpGRkVqxYoXCw8MVEBCg9957T15eXpIePu8j5Q2DHj16yGB4+LDWu3fvysPDQ3Xq1DGdlIGMREdH69VXXzUtf/HFF2bfAgJyWu/evTVz5kzVq1dPkjRr1izTFxms+fPPPxUSEqJp06ZZrHvhhRf07bffKigoSF5eXvrss8/UpEkTs2N4586dunv3rrp27ZrjfQGQdY86asJawi+j9entC8hN/H0FAACAx4l7LQDwf/LVSL+8xEi/J1NUVBQ3pfBYxcfHa8KECVq9erWkhzfhJ06cKAcHB6s3/CdNmqRDhw7pxx9/tNhWYmKiJk2apB9++EHSwy9OTJo0SUWKFDHVGTp0qAoUKKAZM2bkYq8APA6MnoKt4O8rAAAAPE7cawHwJMjsSD+Sfv+frST9ph7Zktch/KMkPIhT6OylpuWqrz8vhwJOeRjRP8ubNVrldQj/GNzsB0AiJfc4/Dw9r0P4R4mKS9Ara46Ylud1qSE3p3w1wYhNS3h2VF6H8I/E/1mwFe9cDM3rEIBM4XoFW8C9ltzF/1mwFbZyzbLJ6T0BANYxVRoAAAAAAEDO4V4LgH8ikn4AACBXMGoiZyU8iDNbnh26g9HpOeSdvA7gH8bV0V7zutQwWwYAAAAAALmPpB+eaPbOjqr6+vNmy8g5DOPPOQ5xCebLG+bKganScoytDOMHAFtgMBiYzhMAACAX8MXKnMUXK3MXX64E8gb/jeOJZjAYuJgDAAAAAAAAAACbR9IPAADABjA6HQAAAAByDv9jAfgnIukHADaA5yMBYHQ6AAAAAOQc/scC8E9E0g8AbADPRwIAAAAAAAAApMcurwMAAAAAAAAAAAAAkD0k/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEO2WlsNBq1ZcsWbdu2TTdv3tT9+/ct6hgMBi1btiw7uwEAAAAAAAAAAACQjmwl/b7//nutW7dOAQEBeuaZZ+Tm5pbtgDZu3Ki1a9cqPDxc/v7+GjhwoMqVK5dm/aioKC1dulR//PGH7t+/r8KFC+ull15S7dq1sx0LAAAAAAAAAAAAYAuylfTbsWOHGjRooDFjxuRIMHv37tXChQs1ePBgBQYGav369frwww81ffp0eXp6WtRPSEjQpEmT5OHhoTFjxsjHx0e3b9+Wq6trjsQDAAAAAAAAAAAA2IJsJf3i4uJUvXr1nIpF69atU8uWLdW8eXNJ0uDBg3Xw4EEFBwera9euFvW3bdum+/fv67///a8cHB52pUiRIjkWDwAAAAAAAAAAAGALspX0q1q1qv7880+1atUq24EkJCTo/PnzZsk9Ozs7VatWTWfOnLHa5sCBAwoMDNQ333yjkJAQeXh4qHHjxuratavs7OystomPj1d8fLxp2WAwyMXFxfS79PBZhSnXJ3vU8pRlOVkOAMgZBoMhT87lOX29yWp5bvcJAJDzUv/PwvUp5/oEAMg5XJ9yp08AgNxhK9enzMhW0u+VV17Rhx9+qJ9++kmtW7dWwYIFH3lbkZGRSkpKkpeXl1m5l5eXrl27ZrXNjRs3dOvWLTVp0kTvvvuurl+/rnnz5ikxMVE9e/a02mbVqlVauXKlablMmTKaPHmyPDw8TC9ebGysoqKi5ObmJmdnZ1PdmJgYxcTEqGDBgnJ0dDSVR0VFKTY2Vp6enrK3tzeV37t3T/Hx8fLy8jJ7kyIiIpSUlCRvb2+z2MLCwmRnZ2c2lanRaFRYWJgcHR2z9foCANLm7e2dJ+fyxMRERUREyNnZ2ey5uPHx8bp3755cXFxMX0yRbOP6lLJPAICcl3yO5vqUs30CAOQsrk+50ycAQM4zGAw2cX1yd3fPVH+ylfQbNWqUjEajli9fruXLl8vJycnqCLsFCxZkZzdpMhqN8vDw0NChQ2VnZ6eyZcvq7t27WrNmTZpJv27duqlTp06m5eSLdWRkpBISEkzblR6+mNHR0Wb7kx5e7K1lWiMiIqyWh4eHW8QtPTxwUpcnJiZalEsPDxBr5QCA7AsLC8uTc3nKL5vExcVZlMfExOjBgwcW5fn5+pSyTwCAnJd8zuX6lLN9AgDkLK5PudMnAEDOS07mpS7Lb9en+/fvZ+qLINlK+jVo0CDHhph7eHjIzs7O4gIaHh5uMfovmZeXlxwcHMwSjSVLllR4eLgSEhJMz/lLydHR0SxLmpK1f/jS+icwK+U5sY30ygEA2ZP6/JoX5/K8KOf6BAC2JzPXLFsvz6tYAAA5Jz9dV3KqPD/FAgDIWfntHJ+dc3+2kn6vvfZadpqbcXBwUNmyZRUaGqr69etLkpKSkhQaGqp27dpZbVOhQgXt2bNHSUlJpsTf33//LW9vb6sJPwAAAAAAAAAAAOCfyHIuzjzUqVMnbd26Vdu3b9eVK1c0b948xcbGKigoSJI0e/ZsLVmyxFS/TZs2un//vr777jtdu3ZNBw8e1KpVq9S2bds86gEAAAAAAAAAAADw+GV7OFx0dLTWr1+vgwcP6vbt25IkX19f1alTRx06dJCrq2umt9WoUSNFRkZqxYoVCg8PV0BAgN577z3T9J63b982m07U19dX48eP14IFC/TWW2/Jx8dH7du3V9euXbPbLQAAAAAAAAAAAMBmZCvpd/fuXU2YMEE3b95UiRIlVKFCBUnStWvX9MMPP2jHjh36z3/+I29v70xvs127dmlO5zlx4kSLsvLly+vDDz98pPgBAAAAAAAAAACAf4JsJf0WL16s8PBwvfPOO6pdu7bZukOHDmnatGlavHixXn/99WwFCQAAAAAAAAAAACBt2Xqm3+HDh9WhQweLhJ8k1apVS+3bt9ehQ4eyswsAAAAAAAAAAAAAGchW0i82Nlaenp5prvfy8lJsbGx2dgEAAAAAAAAAAAAgA9lK+vn5+WnPnj1KSEiwWJeQkKA9e/bIz88vO7sAAAAAAAAAAAAAkIFsPdPv2Wef1fTp0/Xuu++qbdu2Kl68uCTp2rVr+vXXX3Xp0iWNHj06RwIFAAAAAAAAAAAAYF22kn5PP/20YmNjtXjxYn399ddm6zw8PDRs2DA1bNgwWwECAAAAAAAAAAAASF+2kn6SFBQUpGeeeUbnzp3T7du3JUm+vr566qmnZG9vn+0AAQAAAAAAAAAAAKQv20k/SbK3t1f58uVVvnz5nNgcAAAAAAAAAAAAgCzIUtLvxIkTkqTKlSubLWckuT4AAAAAAAAAAACAnJelpN8HH3wgSVq8eLEcHBxMyxlZvnx51iMDAAAAAAAAAAAAkClZSvpNmDDhYSMHB7NlAAAAAAAAAAAAAHknS0m/1NN0Mm0nAAAAAAAAAAAAkPfscmOjN27c0JUrV3Jj0wAAAAAAAAAAAABSydJIv9Q2bNigM2fOaNSoUaayzz//XDt27JAklSlTRu+++648PT2zFSQAAAAAAAAAAACAtGVrpN+2bdvMEnqHDx/Wjh071KpVKw0cOFA3btzQDz/8kO0gAQAAAAAAAAAAAKQtWyP9bt26pZIlS5qW9+3bpyJFimjw4MGSpPDwcO3cuTN7EQIAAAAAAAAAAABIV44+0+/o0aOqWbOmablw4cIKDw/PyV0AAAAAAAAAAAAASCVbSb/ixYtr//79kh5O7Xn37l3VqlXLtP7u3btyc3PLXoQAAAAAAAAAAAAA0pWt6T07d+6smTNn6uWXX9aDBw/k5+enGjVqmNaHhoYqICAguzECAAAAAAAAAAAASEe2kn6NGzdWwYIFdfDgQbm5ualt27ayt7eXJN2/f1/u7u5q2rRpjgQKAAAAAAAAAAAAwLpsJf0kqXr16qpevbpFubu7u8aOHZvdzQMAAAAAAAAAAADIQLae6QcAAAAAAAAAAAAg72VppN9rr70mOzs7ffbZZ3JwcNBrr70mg8GQbhuDwaBZs2ZlK0gAAAAAAAAAAAAAactS0q9y5coyGAyys7MzWwYAAAAAAAAAAACQd7I80i+9ZQAAAAAAAAAAAACPH8/0AwAAAAAAAAAAAGxctpJ+u3fv1pw5c9Jc//nnn2vv3r3Z2QUAAAAAAAAAAACADGQr6bd+/Xo5Ojqmud7JyUnr16/Pzi4AAAAAAAAAAAAAZCBbSb9r164pICAgzfX+/v66du1adnYBAAAAAAAAAAAAIAPZfqZfdHR0muuioqKUkJCQ3V0AAAAAAAAAAAAASEe2kn4BAQHas2eP1cRefHy8du/erTJlymRnFwAAAAAAAAAAAAAykK2kX9euXfXXX3/pgw8+UEhIiG7cuKEbN24oJCREEydO1OXLl9W1a9ccChUAAAAAAAAAAACANQ7ZaVyrVi0NGzZM8+fP15QpU8zWFShQQEOHDlXt2rWzFSAAAAAAAAAAAACA9GUr6SdJQUFBql+/vo4ePaobN25IkooWLaoaNWrIxcUl2wECAAAAAAAAAAAASF+2k36S5OrqqoYNG+bEpgAAAAAAAAAAAABkUbaTfklJSdq3b5+OHz+uiIgI9e7dW6VLl1Z0dLSOHTumChUqyMvLKwdCBQAAAAAAAAAAAGBNtpJ+UVFR+t///qc///xTBQoU0IMHD9S+fXtJD5/pN3/+fDVt2lR9+/bNkWABAAAAAAAAAAAAWLLLTuPFixfr8uXLGj9+vGbNmmW+YTs7NWzYUIcOHcpWgAAAAAAAAAAAAADSl62k3/79+9WuXTtVr15dBoPBYn3x4sV169at7OwCAAAAAAAAAAAAQAaylfSLjo5WkSJF0lyfmJioxMTE7OwCAAAAAAAAAAAAQAaylfQrVqyYLly4kOb6I0eOyM/PLzu7AAAAAAAAAAAAAJCBbCX9WrRooeDgYO3du1dGo9FUHh8fr6VLl+rw4cNq3bp1toMEAAAAAAAAAAAAkDaH7DTu0KGDLl++rBkzZsjV1VWSNHPmTN27d09JSUlq1aqVWrRokSOBAgAAAAAAAAAAALAuW0k/g8GgV199VUFBQfrtt9/0999/y2g0qmjRonr66adVuXLlR9ruxo0btXbtWoWHh8vf318DBw5UuXLlMmy3Z88ezZgxQ3Xr1tXbb7/9SPsGAAAAAAAAAAAAbM0jJ/1iY2M1a9YsNWjQQM8884wqVqyYIwHt3btXCxcu1ODBgxUYGKj169frww8/1PTp0+Xp6Zlmu5s3b2rRokWqVKlSjsQBAAAAAAAAAAAA2IpHfqafs7Ozjh07ptjY2JyMR+vWrVPLli3VvHlz+fn5afDgwXJyclJwcHCabZKSkjRr1iz16tVLRYoUydF4AAAAAAAAAAAAgPzukZN+klSxYkWdOXMmp2JRQkKCzp8/r2rVqpnK7OzsVK1atXT3s3LlSnl4ePD8QAAAAAAAAAAAADyRspX0GzhwoE6dOqVly5bpzp072Q4mMjJSSUlJ8vLyMiv38vJSeHi41TanTp3Stm3bNHTo0EztIz4+XtHR0aafmJgY0zqDwSCDwWBWP7ksO+Upy7JbDgDIeY/7XJ5b15uslud2nwAAOY/rU+71CQCQc7g+5X6fAAA5x1auT5nxyM/0k6S33npLiYmJWrVqlVatWiV7e3s5Ojpa1FuwYEF2dpOmmJgYzZo1S0OHDpWHh0em2qxatUorV640LZcpU0aTJ0+Wh4eHjEajpIfPK4yKipKbm5ucnZ3N9hcTE6OCBQua9TMqKkqxsbHy9PSUvb29qfzevXuKj4+Xl5eX2ZsTERGhpKQkeXt7m8UWFhYmOzs7s2cXGo1GhYWFydHRUQULFszkKwMAyApvb+88OZcnJiYqIiJCzs7OcnNzM5XHx8fr3r17cnFxkYuLi6ncFq5PKfsEAMh5yedork852ycAQM7i+pQ7fQIA5DyDwWAT1yd3d/dM9SdbSb+GDRtmp7kFDw8P2dnZWYzqCw8Ptxj9J0k3btzQrVu3NHnyZFNZcuKuT58+mj59uooVK2bWplu3burUqZNpOfliHRkZqYSEBLNtREVFKTo62mLb9+7dM7vIJ5dHRERYLU/dn+TysLAwi/LExESLcunhAWKtHACQfWFhYXlyLk/5ZZO4uDiL8piYGD148MCiPD9fn1L2CQCQ85LPuVyfcrZPAICcxfUpd/oEAMh5ycm81GX57fp0//79TH0R5JGSfnFxcQoJCVGJEiXk7u6uOnXqWGRCH4WDg4PKli2r0NBQ1a9fX5KUlJSk0NBQtWvXzqJ+iRIl9Omnn5qVLVu2TA8ePNCAAQPk6+tr0cbR0dHqaETJ+j98af0TmJXynNhGeuUAgOxJfX7Ni3N5XpRzfQIA25OZa5atl+dVLACAnJOfris5VZ6fYgEA5Kz8do7Pzrk/y0m/iIgIvf/++7p586apbOHChRo7dqyqV6/+yIEk69Spk+bMmaOyZcuqXLly2rBhg2JjYxUUFCRJmj17tnx8fNS3b185OTmpdOnSZu2Th0+mLgcAAAAAAAAAAAD+qbKc9Pvxxx9169YtdezYUVWrVtX169f1448/6uuvv9asWbOyHVCjRo0UGRmpFStWKDw8XAEBAXrvvfdM03vevn2bB9cCAAAAAAAAAAAAKWQ56XfkyBE1bdpUL774oqnMy8tLM2bM0LVr11SiRIlsB9WuXTur03lK0sSJE9Nt+9prr2V7/wAAAAAAAAAAAIAtsctqg9u3b6tixYpmZcnLqR9oCwAAAAAAAAAAACD3ZTnpl5CQICcnJ7MyR0dHSVJSUlLORAUAAAAAAAAAAAAg07I8vack3bx5U+fPnzctR0dHS5L+/vtvubq6WtQvW7bsI4YHAAAAAAAAAAAAICOPlPRbvny5li9fblE+b968NOsDAAAAAAAAAAAAyB1ZTvoNGzYsN+IAAAAAAAAAAAAA8IiynPQLCgrKhTAAAAAAAAAAAAAAPCq7vA4AAAAAAAAAAAAAQPaQ9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsHEk/AAAAAAAAAAAAwMaR9AMAAAAAAAAAAABsnENeB2DNxo0btXbtWoWHh8vf318DBw5UuXLlrNbdsmWLdu7cqcuXL0uSypYtq+effz7N+gAAAAAAAAAAAMA/Tb4b6bd3714tXLhQzz33nCZPnix/f399+OGHioiIsFr/xIkTaty4sSZMmKBJkyapUKFCmjRpku7evfuYIwcAAAAAAAAAAADyRr5L+q1bt04tW7ZU8+bN5efnp8GDB8vJyUnBwcFW67/xxhtq27atAgICVLJkSb366qsyGo06duzYY44cAAAAAAAAAAAAyBv5anrPhIQEnT9/Xl27djWV2dnZqVq1ajpz5kymthEbG6uEhAS5u7tbXR8fH6/4+HjTssFgkIuLi+l3STIajWbrkz1qecqynCwHAOQMg8GQJ+fynL7eZLU8t/sEAMh5qf9n4fqUc30CAOQcrk+50ycAQO6wletTZuSrpF9kZKSSkpLk5eVlVu7l5aVr165lahuLFy+Wj4+PqlWrZnX9qlWrtHLlStNymTJlNHnyZHl4eJhevNjYWEVFRcnNzU3Ozs6mujExMYqJiVHBggXl6OhoKo+KilJsbKw8PT1lb29vKr93757i4+Pl5eVl9iZFREQoKSlJ3t7eZrGFhYXJzs5Onp6epjKj0aiwsDA5OjqqYMGCmXoNAABZ4+3tnSfn8sTEREVERMjZ2Vlubm6m8vj4eN27d08uLi6mL6ZItnF9StknAEDOSz5Hc33K2T4BAHIW16fc6RMAIOcZDAabuD6lNdAttXyV9Muu1atXa8+ePZo4caKcnJys1unWrZs6depkWk6+WEdGRiohIUHS/2VOo6KiFB0dbaqbXH7v3j2rmdaIiAir5eHh4WYxJJeHhYVZlCcmJlqUSw8PEGvlAIDsCwsLy5Nzecovm8TFxVmUx8TE6MGDBxbl+fn6lLJPAICcl3zO5fqUs30CAOQsrk+50ycAQM5LTualLstv16f79+9n6osg+Srp5+HhITs7O4uLaHh4uMXov9TWrFmj1atX61//+pf8/f3TrOfo6GiWJU3J2j98af0TmJXynNhGeuUAgOxJfX7Ni3N5XpRzfQIA25OZa5atl+dVLACAnJOfris5VZ6fYgEA5Kz8do7Pzrnf7pFb5gIHBweVLVtWoaGhprKkpCSFhoaqfPnyabb7+eef9eOPP+q9997TU0899ThCBQAAAAAAAAAAAPKNfJX0k6ROnTpp69at2r59u65cuaJ58+YpNjZWQUFBkqTZs2dryZIlpvqrV6/W8uXLNWzYMBUpUkTh4eEKDw9n6DsAAAAAAAAAAACeGPlqek9JatSokSIjI7VixQqFh4crICBA7733nml6z9u3b5vNZ/rrr78qISFB06ZNM9vOc889p169ej3O0AEAAAAAAAAAAIA8ke+SfpLUrl07tWvXzuq6iRMnmi3PmTPnMUQEAAAAAAAAAAAA5F/5bnpPAAAAAAAAAAAAAFlD0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcST9AAAAAAAAAAAAABtH0g8AAAAAAAAAAACwcQ55HYA1Gzdu1Nq1axUeHi5/f38NHDhQ5cqVS7P+vn37tHz5ct26dUvFihXTCy+8oNq1az/GiAEAAAAAAAAAAIC8k+9G+u3du1cLFy7Uc889p8mTJ8vf318ffvihIiIirNY/ffq0ZsyYoRYtWmjy5MmqV6+epkyZor/++usxRw4AAAAAAAAAAADkjXyX9Fu3bp1atmyp5s2by8/PT4MHD5aTk5OCg4Ot1t+wYYNq1qypLl26yM/PT3369FHZsmW1cePGxxw5AAAAAAAAAAAAkDfy1fSeCQkJOn/+vLp27Woqs7OzU7Vq1XTmzBmrbc6cOaNOnTqZldWoUUP79++3Wj8+Pl7x8fGmZYPBIBcXFzk45KuXIk0lPXzyOgQg0xx8S+Z1CECmGBwd8zqEfySuWbAVXK9gS7hm5Q6uWbAVXLNgK7he5Q6uV7AlXLNgK2zlmpXZHFa+ynRFRkYqKSlJXl5eZuVeXl66du2a1Tbh4eHy9PQ0K/P09FR4eLjV+qtWrdLKlStNy40bN9bIkSPl7e2drdgflzGFO2VcCQCAfIBrFgDAVnDNAgDYAq5XAICM5LvpPXNbt27d9N1335l+Bg8ebDbyD0DOiImJ0TvvvKOYmJi8DgUAgDRxvQIA2AquWQAAW8E1C8g7+Wqkn4eHh+zs7CxG6YWHh1uM/kvm5eWliIgIs7KIiIg06zs6OsrRRoZrArbMaDTqwoULMhqNeR0KAABp4noFALAVXLMAALaCaxaQd/LVSD8HBweVLVtWoaGhprKkpCSFhoaqfPnyVtuUL19ex44dMys7evSoAgMDczVWAAAAAAAAAAAAIL/IV0k/SerUqZO2bt2q7du368qVK5o3b55iY2MVFBQkSZo9e7aWLFliqt+hQwcdOXJEa9eu1dWrV7VixQqdO3dO7dq1y6MeAAAAAAAAAAAAAI9XvpreU5IaNWqkyMhIrVixQuHh4QoICNB7771nmq7z9u3bMhgMpvoVKlTQG2+8oWXLlmnp0qUqXry43nrrLZUuXTqPegBAejiV7nPPPcd0ugCAfI3rFQDAVnDNAgDYCq5ZQN4xGJlYFwAAAAAAAAAAALBp+W56TwAAAAAAAAAAAABZQ9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPAAAAAAAAAAAAsHEk/QAAAAAAAAAAAAAbR9IPQIaSkpIsymJiYvIgEgAAAAD450j9v5bRaMyjSAAAAPBPQNIPQIbs7Ox069YtrV+/XpK0b98+zZ49W9HR0XkcGQAA5lLePLX2pRUAAPKLpKQk2dk9vC1z8uRJJSYmymAw5HFUAABYSv5Syq1bt3Tz5k399ddfeRwRgLQ45HUAAPK/xMREbd68WYcPH9aFCxe0a9cuDRs2TK6urnkdGgAAJilvnm7cuFGXLl3SnTt3VLt2bdWvX18+Pj55HCEAAA8ZjUbTNWvZsmXatWuXnn/+eTVq1MhUDgBAfmA0GmUwGPTHH39o+fLlkqTIyEjVr19fXbt2VeHChfM4QgAp8ZckgAzZ29urZ8+e8vX11a5du/T0008rKChIEqMoAAD5R/JN0u+//14rV65UyZIl5efnp02bNmnevHmKi4vL4wgBAHgoeUTfsmXLtHXrVr322muqVq0aCT8AQL5jMBh07NgxzZo1S+3bt9cHH3ygl19+WVu2bNGFCxfyOjwAqfDXJIB0JQ/fd3BwkJubm6pXr647d+5o1apVkh7eYCXxBwDIL06fPq2QkBC988476tSpk6pVq6abN2+qfv36cnJyyuvwAAAwuXXrlg4fPqxhw4apcuXKsrOz019//aWVK1fq5MmTPE4BAJDnku8LHjlyRM2bN1erVq10//59LVu2TC1atFD9+vXzOEIAqTG9J4A0JQ/fP3/+vDw8PPTaa68pOjpay5cvV0hIiCSpW7dupm+jRkZGysPDIy9DBgA84aKjo+Xg4KDAwED99ttvmjt3rl566SUFBQUpNjZWx48fV9WqVUkAAgDyXHx8vP7++2/Z2dnp7Nmz2rZtm/7880/du3dP27Zt0+DBg1WrVi3T/2UAADwuydeeO3fuyNfXVxcuXFC1atUUHx+viRMnqnbt2ho8eLAkacOGDSpevLhq1aqVx1EDkBjpByANKefr/uijj/TLL7/o3r17cnNzU/fu3fXUU08pJCREP/30kyRp+fLlmjdvnuLj4/M4cgDAkyL5W6cpGQwGeXh4mBJ+L7zwgtq0aSNJOnnypEJCQhQeHv6YIwUAPOmszY5SokQJ1a9fX1OnTtV//vMfOTs76/nnn9cXX3whNzc3nTlzRpJI+AEAHrvke4KTJk1SRESEGjRooMOHD2v48OGqW7euXnnlFRkMBiUlJenChQs6cuSIEhIS8jpsAGKkH4A0GAwGHTp0SDNnztTAgQNVt25d0yg+Ly8v9ejRQ2vWrFFwcLB27dqliIgIvfvuu3J0dMzjyAEAT4rkm6A7duyQp6enatasqcqVK2vevHn67LPP9Morr6h169aSpLi4OP3yyy9ycXHhQfMAgMcqKSnJNDvK77//roiICEVGRqpt27YaNmyYmjdvLmdnZz311FOmNu7u7syiAgB4rJKSkmQwGGQwGHT79m1t3bpVnTp1kqenp0qVKqWEhAR5enqqVatWsrOzU1xcnH788UeFhobq3//+txwcSDUA+YHBaO0r0gCeeAkJCfryyy/l6empfv366cGDB7p9+7Z27dqlIkWKqHbt2nJxcdGZM2d07do11axZU8WKFcvrsAEAT5jbt29r7ty5io6O1vPPP6/q1avr/PnzmjJlivz8/NS8eXMlJSVp+/btCgsL0yeffCJ7e3umSgMAPHbff/+99u3bpxIlSujBgwe6cuWKRo4cqZo1a0qS6X+u77//Xnfu3NHHH38se3v7vA0aAPCP9+eff6pcuXJmy9u3b9eNGzc0bNgw+fj4SJJ27typTZs26f79+ypSpIgk6eLFi3rvvfdUpkyZPIkdgCXS7wDSdOvWLcXGxio8PFzLly/X9evXdffuXUVHR+vy5csaMGCAqlevrurVq+d1qACAJ0TqZJ2vr6+6deumzZs3a8WKFbKzs1PVqlX1zjvv6Ouvv9aKFSvk5uamokWLaty4cbK3tzcbcQEAwOOwc+dO7dy5U++9954CAgJ09OhRffjhh6ap0IxGo44ePaoNGzbIYDDoo48+4poFAMh1O3fu1I4dOzR69Gi5ubnJYDDo2LFj+v3335WQkKDo6GhT0q9p06YqUqSI/vrrL507d07+/v4aNGgQgwCAfIaRfgAkWd5ElaSDBw9q1qxZSkxMVPXq1dWwYUM1adJEq1ev1v79+zVhwgQ5OTnlUcQAgCdZRESEPD09TcvHjx/XL7/8ovDwcPXp00dVq1ZVYmKiIiMj5ezsLBcXFxkMBiUmJjJqAgDw2K1atUoREREaMGCA9u7dqy+//NL03NmYmBg5OzsrJiZGp0+fVs2aNWVnZ8c1CwCQ665cuSJnZ2cVLlxYd+/eNSX4Nm/erJ9//lmVK1fWc889p6JFi+ZxpAAyi5F+AEwJv1OnTunUqVOKjIxUtWrVVLt2bU2dOlU3b95UxYoVlfwdgbCwMBUqVCiPowYAPKl2796tLVu26KWXXjJNI1OlShUZjUb98MMPWrJkifr3769KlSrJ29vb1M5oNHLzFACQJ27cuKH4+HiFhoaaJfwkadOmTXrw4IH69Omj2rVrS3r4XCWuWQCA3Obn5yfp/7F33+FNV30fxz9JF22htAXKhrZAWWUP2XspG2UJIqIgCAoOHCCyBAfOGxVvlKksQUCGILJHZQmI7C0yC3RROtPk+YMnv7uFooy2aej7dV1cNOc3ck6T9OSc7xk3l+n85ptv1KRJE7Vs2VKtWrVSYmKitm7dqlWrVumxxx5TQEAA2yQAToA1IgDIZDJpx44dmjRpko4ePaobN27o/fff19y5c5UnTx6VK1dOknT27FnNnTtXmzZt0uOPP84sPwBAlrBarZJkDD5JTk6WJP344486c+aMcV5oaKgeeeQRnTlzRlOmTNGJEyfS3IfGKQAgK61YsUKLFy+WJDVo0EDnzp3ThAkT0gT8EhISdPToUSUkJKS5liU9AQBZyWw2y8/PT9u2bdOGDRskSe3bt1f9+vV1+PBhrV69WpcuXaJNBTgBZvoB0IULFzR79mz17NlTLVq0UEJCgjZv3ixJcnNzk3RzxM+KFSt0+vRpjRkzRiVLlnRklgEAOYTNZjM6Pvfu3avq1auradOmcnd31/r16/XDDz+oa9euxoy//Pnzq0qVKipXrpyCg4MdmXUAQA6WlJSkS5cuKSoqStLNmRQlS5aUxWIx9ki6fPmy5s+fr6ioKL322muS0t92AQCAzFaiRAl17dpVP/30k9avXy9Jatq0qTp06CCTyaTVq1fL1dVV3bt3ZyY6kM2xpx8AnThxQt99953Gjh2rS5cuacyYMapevboGDBggSbp69ary58+vkydPys/Pz1jfGwCAzGS1Wo2A37lz5/Tqq6+qXbt2euqppyTdXOZzw4YNcnV1Vdu2bRUUFKSvv/5apUqVUufOnWUymdLcAwCArLR37159/PHHGjlypMqXL6/w8HAtWrRIR48eVUREhIoUKSIvLy+NHDlSrq6u1FkAgCxhH2By9epV3bhxQ97e3vL19ZWrq6tOnz6t5cuX68qVK2rWrJmaNm0qSfr5559Vs2ZNBQQEODj3AP4NQT8gB7JX7n/88Ye8vb1ltVr1n//8R0OHDtXnn3+uypUr67nnnpPZbNahQ4e0bNkyDRgwgGAfACDLpJ7psHLlSv3999/atWuXYmNj1bp1a/Xr10+StH37dm3btk07d+5U4cKF5eLiog8//FAuLi7MlgAAZIl/qm8mT56sxMREvfDCC/Ly8lJ8fLzi4+N15swZBQQEqEiRIjKbzUpJSWHmBAAg09nrrJ07d+qHH35QTEyMChYsqKCgIPXq1UseHh5G4C8iIkL16tUzlqQG4BxY3hPIgUwmk44cOaKPPvpI/fv3V9WqVVWsWDGNGzdONWvW1IABA4x9k/bt26fExES5uvLnAgCQ+eyNUHvn6cKFC7Vq1SoNGDBA1apV08GDB7VlyxalpKSof//+qlOnjkqXLq1WrVopLi5OtWrVktlsZrYEACDL2OusJUuWKE+ePCpVqpSx7HTVqlX1448/KjY2Vl5eXvLw8JCnp2eaAZVWq5WAHwAgS5hMJu3bt09ffvmlunfvrnr16mnt2rVasWKFoqOjNWjQIAUFBalDhw5asGCBdu/erQYNGsjLy8vRWQdwl+jFB3KgK1euaM+ePercubMaNWokSapZs6bCw8Pl6uqqv//+W0lJSfrtt9+0bt06jR07Vj4+Pg7ONQDgYRcbG6vcuXMbj+Pi4nTo0CF17dpVderUkSRVqFBBRYoU0dy5c+Xh4aE+ffoof/78yp8/v3EdAT8AQFaz2Wy6du2awsLClJSUpBo1aqhZs2Zq2LCh1q5dq4ULF2rw4MHp1k/UWQCArBITE6MVK1bo8ccf12OPPabr169r3bp1CgoK0rlz5zRlyhS98MILCgwMVM+ePZU7d24CfoCTIegH5DDnz5/XlClTFBkZqY4dOxrpLVq0UHx8vPbs2aPXXntNJUqUkNls1ujRo1WiRAkH5hgAkBNMnTpV165d01tvvWWkubq66tq1awoPDzfS8uTJowYNGuj333/XypUrZbVa1bdvX0kylkaj8xQAkNluHWBiMpn03HPP6fz58zp79qx++OEHHT16VD4+PipXrpz279+vixcvqnDhwg7MNQAgp/Px8VH9+vVVsmRJxcTEaPTo0apRo4b69eunmTNnas2aNYqLi9Orr75KfyDgpAj6ATlM0aJFVbp0aW3atEn79u1LM0W/ffv2atGihc6dOyc/Pz95eHgoT548Ds4xACAn6NKli3x9fSVJCQkJypUrl9zd3fXII4/ozJkzOnXqlIKDgyVJuXPnVmBgoNzc3LRjxw5ZLBY999xzcnFxYZYfACDTpa5rjh07pvj4eHl4eKhcuXIqWrSoihYtqipVqujEiRNas2aN0YH6xx9/EPQDADhc06ZNJUmrVq1SQECAunXrJrPZrBIlSqhkyZJyc3NTbGyscuXK5eCcArgfBP2Ah1x6m8r37dtXrq6u2rNnj37++Wc9+uij8vb2liR5enqqTJkyjsgqACAHsy/PuXHjRs2cOVOffvqp/Pz8VKVKFe3Zs0dr1qxRs2bNFBISooSEBJ0/f16VK1dWzZo1tWDBAiUkJGjIkCEE/AAAmcpmsxl1zdy5c7Vz507Fx8erQIECKlSokIYMGSJJ8vLyUuXKlVW5cmUdO3ZM27Zt05o1a1SzZs00S1IDAJBZ7H2CZ86c0aVLl2SxWFSiRAljBl94eLiuXLlibLFw8eJFVa9eXe3bt2dJT8CJEfQDHmL2yv348eM6evSoXF1dFRAQoOrVq6t3796yWq3atWuXJBmBv/SChAAAZJbUsyWSk5NVrlw5FSlSRGPGjNGYMWMUGhqqHj16aPHixfrvf/+rXLlyKTk5WSkpKWrTpo1SUlKUlJSkVatWKSoqypgtCABAZrC3lZYuXaoNGzbo1VdfValSpbRw4UL99NNPiouL0+uvvy7pZr3m5uamkJAQmc1m7du3T1evXiXoBwDIEiaTSTt27NA333yjwMBARUZGysPDQ3Xq1FGHDh0UEhKiI0eO6L333lPevHm1Y8cOffDBBwT8ACdnstlsNkdnAkDGswfvtm/frilTpigwMFA3btzQ+fPn1aZNGz399NOSpJkzZ+r48eOqUKGCOnfuTMUOAMgyBw4c0JUrV9S0aVNNnTpVVqtVAwYMUHh4uL788ktFRETo3XfflZ+fn86cOaOLFy/q8OHD8vPzU/v27eXqenP8WkJCgqxWK3UYACBLXLhwQdOnT1fbtm1VrVo17du3T5988okaNWqk33//XaVKldJrr70m6X/7zUrSyy+/rNatW6tNmzaOzD4AIIc4ffq0Jk6cqK5du6pVq1Y6fPiwxo8fr06dOqlbt26Kj4/X5s2b9eeff8pqtapHjx7s4wc8BJjpBzwk0ttI/tKlS5oxY4Z69eqlVq1aKTY2Vvv379eUKVNkMpnUp08f9e3bV1OnTtWJEydksVgcWAIAQE5hs9mUlJSkFStWKC4uTtu3b9exY8c0duxYmc1mFSpUSIMHD9aXX36pt99+W++++64CAwMVGBiounXrGvexd6Sy1wQAILOlbm8VKVJEDRo0UFBQkI4ePaqvv/5affr0UYsWLWS1WrVu3TqNGjVK48ePNwJ+YWFhioqKUuXKlR1ZDABADvL333+rWLFiatWqlcLDw/XFF1+oSZMm6tatmyQpPj5erVu3VuvWrZWUlCR3d3cH5xhARmDTE+AhYG+Anj17Vvv27TPSr1+/rly5cqlGjRqSpNy5c6tevXp6/vnntWbNGh04cECSNGDAAA0bNkw+Pj6OyD4AIIcxmUzy8PDQsGHDdP36de3bt0/t2rVLM6rUHvjz9/fXmDFjFBERcdt97B2pAABklj179uj777/XZ599plOnThnpTZo0ka+vr/bs2aPKlSurUaNGkm7WXzVr1lSRIkVktVqN8wsUKKD33ntPRYoUyfIyAAByltQL+/n5+SkiIkKjR49WlSpV9Nxzz0m6uerK+vXrFR0dLUlyc3NzSF4BZDyCfoCTswf8/vrrLw0fPlwnTpwwjnl4eOjy5cu6ePGipP9V+hUqVJCfn58iIyONc/PmzZu1GQcA5Ej2DlD7bL+CBQuqYsWKOnjwoDZs2GCcZ7PZjMCfdHM5agAAstLatWv15Zdf6vLly7p27ZpGjx5ttK3sLl68qPPnz8vd3V0Wi0XHjh1TxYoVNWjQIJnNZqWkpEiSypQpo0KFCjmiGACAHMa+/6yfn5/CwsI0bNgw1a5dWwMGDDBmrW/fvl1nz541ZvfZrwHg/FjeE3Bi9oDfmTNn9Pbbb6tz58564oknjONFihRR1apVtXr1anl5eSk4OFiS5OPjo9y5cxsNUAAAskLqpdF27dqlcuXK6c0331RsbKymTJmijRs3ymQyqUmTJkaj09/fX2PHjmU2OgAgS61du1bTpk3Tyy+/rJo1ayo+Pl5jxozRpUuXlD9/fmNGRKNGjTRr1iy98cYbMpvNSkxM1Msvvyzp5gAWZqUDADKbzWaTyWTS6dOndfXqVXl7eyskJESVKlVS9+7dNX/+fJUqVUrR0dFKSUnRzz//rN9++01jx46Vp6eno7MPIIOZbKnn+wJwOhcuXNDw4cP1+OOPq0uXLkb677//rooVK+rPP//UihUr5OXlpRYtWiggIECbNm3Sxo0bNXHiRAUEBDgw9wCAnMLeEJWkuXPnatu2bWrZsqVat24tT09PRUREaNq0aYqLi1P9+vXVuHFjTZgwQcHBwerTp4+k2/evBQAgM+zZs0cffPCBBg8ebCzbKUlDhw5VsWLFdPbsWdWsWVONGjVS8eLFtXfvXv3xxx/y9PRUjx495OLiQp0FAMhS27dv19SpU+Xu7i4PDw8FBQVp8ODBcnNz08yZM/XLL7/I399fuXPnVnx8vF5++WUFBQU5OtsAMgFBP8CJJSUl6auvvtKff/6pl19+WaGhoZKkxYsX69dff9Xbb7+tokWLaufOndq6dat27typwoULy2q1atiwYVTuAIAst2jRIq1atUpvvfWWihUrply5chkBwYiICH333Xc6c+aMkpKS5OXlpffee0+urixOAQDIOkuWLNHmzZtVrVo1Pfnkk3J1ddVHH32kU6dOqU2bNoqJidGqVatUq1YtvfDCC8bSaHYpKSnM8AMAZAp7V77JZDLaUbGxsfryyy9Vt25dVahQQX/88Yd+/fVX+fj4aPjw4XJzc9ORI0cUExMjT09PFStWTH5+fg4uCYDMQtAPcHIHDhzQ6tWrdePGDfXq1UvHjx/XwoUL9dJLL6lq1arGeRaLRVeuXJHValWePHlYJg0AkOlWrlypunXryt/fX5IUFRWlzz//XG3atNEjjzyiiIgIXbp0SRs3blRISIiaNWumGzdu6OTJk4qOjlbDhg2N/ZDoPAUAZJWUlBStWLFCu3btUunSpXXp0iVFRkbq1VdfNVZKWbZsmebMmaNPPvlERYsWdXCOAQA5xdWrV5U/f37j8fHjx7VkyRKZzWY999xz8vX1lcVi0e7du7VkyRLlyZNHr7/++m0DVAA8vBg2DTi50NBQmc1mrVixQpMnT9bVq1c1evRohYSEpBn94+LiosKFCzs4twCAnCIsLEzHjh3To48+aqS5ubnp8uXLOn78uPz8/LRy5UpdvnxZuXLl0qZNm5SUlKTHHnsszaAVq9VKwA8AkGXs9U67du1ktVq1detWXb16VePHj1dAQICSkpLk7u6uQoUKqUSJEtRRAIAss3HjRi1atEiffPKJUf8cP35c58+fV1JSknx9fSVJrq6uqlmzpiRp+fLlGjt2rMaMGWPsRwvg4cYC84ATswf1KlSooA4dOqhw4cIqVqyYEhMTJcnYO+nWnwEAyGz16tXT0KFDZTab9ccff+jChQvy9vZWp06dtGXLFo0fP14FChRQz549NWbMGDVu3Fhnzpy57T7shwQAyEpms9kI/HXs2FGNGzdW0aJFtXbtWsXGxsrd3V1Wq1Xr1q1T/vz5VbBgQUdnGQCQQ5QpU0bvvPOO3N3dlZCQIBcXFzVq1Eht27ZVSkqK/vOf/xjn2gN/jz76qNzd3RUdHe3AnAPISizvCTg5+/rdknT48GEtX75c8fHx6tChg6pVq3bbOQAAZDar1Sqz2Sybzabjx4/r888/V/Xq1dWpUyfly5dPERERio+PN5ZDs1qtGj9+vMqXL69u3bo5OPcAAPyvLktJSdGyZcu0e/dulSpVSj169NCXX36pCxcuaNKkSXJ1dTXOBQAgK5w5c0bjxo3T8OHDVb58ecXFxWnz5s1av369AgMD9cILLxjnWiwWJScny9PT04E5BpCVCPoBD4HUQb1Dhw5pxYoVSkpKUuvWrVWrVi0H5w4AkJOkN9Dkp59+0m+//aayZcvq0UcfVaFChSRJCQkJ+uuvv7R48WJFRETo/fffZ5k0AEC2kTrwt3z5cv3+++/666+/5Ofnp48//liurq7sOwsAyHLnzp3TnDlzdOrUKb366qsKCQnRjRs3jMBf6dKl9fzzzzs6mwAchKFogJNKHa83mUy3LfWZkpKiDRs2KCEhwVFZBADkMKkDflu3btWqVaskSR07dlS9evV05MgRrVq1SuHh4ZJuDlRZvny5rFar3nvvPbm4uMhqtTos/wCAnOOf6hv7sdRLfbZv316hoaGqWrWqPvnkEwJ+AACHKVasmHr16qXy5cvrww8/1LFjx+Tt7a1GjRqpRYsW2r9/v6ZPn+7obAJwEGb6AU7A3okaHh6u2NhYlShRQq6urnc8T5KOHTumfPnyKV++fFmdXQBADpR6abMTJ05o3rx5ioqKUpcuXVS/fn1J0rJly7Rt2zaVL19eHTp0UN68eXX69GkFBwcbMynoPAUAZLbUddbGjRv1119/SZICAwPVuHHjO55vtVplMplkMpmoswAAmcbev5eQkCBXV9c0fYCp+/7OnTunRYsW6cCBA3r99dcVEhKi2NhYbd++XZUqVWLfWSCHIugHOIkdO3bo22+/ldlslpeXl3r27KnKlSsrV65cac5j/z4AgCPNnTtX58+f1/Xr1/XXX38pf/78ateunZo2bSpJWr58ubZt26bixYurV69e8vX1lST2QwIAZLnvv/9emzdvVp06dZScnKwdO3aoSZMm6tOnz23npm5n0eYCAGS2qKgojRw5Uj179lSdOnXSHfwv/S/wd/jwYQ0dOlQVKlSgngJyOHpWgGzOZrMpIiJCixcv1uOPP64RI0aoaNGimjNnjsLCwm5bvpNKHQDgKOvXr9cvv/yijh076s0339To0aNVsGBBrVu3Tps2bZIktW/fXjVq1JDJZJKPj49xLQE/AEBW2r9/v7Zv367XXntN/fr1U6VKlZScnKxixYqlOc8+Tjp1O4s2FwAgs/n6+qpkyZKaMWOGdu/eLYvFkua4vX4qVqyYnnjiCQUFBenrr79WUlKSI7ILIBuhdwXIpuyVt81mU+7cuVW+fHk1adJEJUuW1GuvvaaQkBD99NNP6Qb+AABwhLNnzyokJEQhISHy8vJScHCwunbtKpPJpKVLl2rr1q2SpK5du2rgwIHGUmkAAGS21O0rSbpy5Yry5cunkJAQ7dixQ//973/Vp08fNWvWTAkJCTp48KAkAnwAgKxnbyO9/vrrqlq1qqZMmXJb4C/17HOz2awnn3xSY8aMkbu7O3UXkMMR9AOyKZPJpD179uizzz7TmDFjdPr06TQdo4MHD1aZMmW0cuVKbdy4kcAfAMBh7PWTj4+PEhISFBsbK+lmAzQoKEiPPfaYrly5ojVr1hiBP7PZbDRQAQDIbPYO0OvXr0uScufOrfz58yssLExffvmlevfurZYtW0qSDh8+rF27dikyMtJh+QUA5FypB0e++OKLqlmzphH4S05ONs6zWCz66quv9Mknn6hAgQLy9/d3VJYBZCP0sgDZ1LFjx/Thhx/K29tbZrNZZ8+e1U8//WR0pErSkCFDVLhwYW3atImZEgCALHNrnWMP3AUHB+vUqVPasmWLUlJSjA5WDw8PVa5cWb6+vtq+fbvi4uIkMXsCAJC11q1bp59++kmS5O/vr927d+vzzz9Xr169jIBfUlKSfv75ZyUkJBj7zgIAkBVSt7PuFPjbs2ePLBaLbDabZs+erR07dmjgwIHy9PR0VLYBZDMmm31tCwDZxoULF7Rjxw65ubmpXbt2kqRZs2bpyJEjqlmzph599FF5eXkZ50dERDCaBwCQJaxWqxHk+/333xUTE6OUlBTVrVtX3t7eWrlypWbPnq0ePXooNDRU+fLl09SpUxUSEqIyZcro3Xff1bvvvqsyZco4uCQAgJxm0aJFWr58uSZPniwfHx+FhYXp888/V8eOHVWuXDl5eHho8eLFiomJ0fvvvy8XFxfZbDYGqQAAMoW9bRUXF2f086Vub936ePLkydq9e7eef/55HTp0SBs3btT48eMVFBTkkPwDyJ4I+gHZzOXLlzVlyhRduHBBXbp0UZs2bYxjs2bN0uHDh1W7dm21atVKuXPndmBOAQA52ezZs7Vt2zZ5e3srKSlJCQkJevHFF1WlShWtXr1aP/30kywWi9zd3ZUrVy69//77ioqK0oQJE/TKK6+oRIkSji4CAOAhZe/msAfr7B2mFotFEyZMUGBgoHr16iVXV1dt3LhRS5YsUWxsrAoVKiQfHx+9+uqrcnV1va3jFQCAjHb+/Hm988476tixozp06CDpnwN/X331lTZt2iR3d3eNGzeOgB+A27g6OgMA0sqfP79CQ0N15coV7d69W02aNFGuXLkkSU8//bS+++47bdiwQa6urmrfvj2jTgEAWW7r1q3atGmTRo0apfz588tsNmvq1Kn67LPPNGLECLVp00YVK1ZUXFycEhMTFRoaKrPZrFWrVslkMilv3ryOLgIA4CF2axsp9T6yZcuW1YEDB2SxWOTq6qomTZqoatWqSkxMlJubm/z8/GQymZSSkiIXFxcHlQAAkBNYrVZt2LBBsbGxWrlypSwWi7p06WIs7WkP9KV+/MILLyhv3ryqX7++AgMDHVsAANkSQ9YAB7t1sq2Li4s6d+6s1q1b6/r165ozZ46x95EkPfXUU6pXr57q1KlDwA8A4BDXrl1TqVKlFBgYKC8vL3l5eWnYsGEqW7asvv76a1ksFhUvXlxly5ZV5cqV9ddff+k///mPNm3apKFDhxL0AwBkiu+++05HjhwxHq9fv14TJ07UpUuXlJCQILPZrHbt2unixYtaunSpcZ6vr68KFiwof39/mUwmWa1WAn4AgExnNpsVHBysAgUKqHHjxtq0aZMWL15sHLt1j7+UlBRJUq9evQj4Abgjgn6AA9n3hzh69KgWL16sRYsWaefOnXJxcVG7du1Ut25dnTp1SnPnzk0T+OvZs6cCAgIcmHMAQE5248YNnTt3TtLNxmdycrIkqW3btoqPj1d4eLhxrn2mhLu7u0aPHk3jFACQKc6fP6/Y2Fhjz1j74Mr4+HiNHTtW06dP1549e5Q7d2517dpVp06d0vnz59O9F0t6AgAyg81mM+onewCvXr168vPzU3h4uJo2bapff/1VS5YskXR74I8BKQDuBt9kAQcymUzavn27JkyYoD///FN79+7Vxx9/rG+++UbJycnq0KGDatSoobNnz2r69OmKj493dJYBADlM6kamXYMGDeTm5qbvvvtOkuTm5mb87+bmlmYmuouLi0qUKKHnnnuOffwAAJmmaNGiGjRokFxcXLR161bt379fzZo10/jx4/XEE0/IZDLpww8/1LRp03ThwgWFh4cbA1gAAMhsVqtVJpPJ6NtLHcB79NFH5ebmpurVq6tZs2ZavXq1MSP91sAfAPwb9vQDHCg8PFyzZ89W79691apVK1mtVu3fv18ff/yxTCaTnnvuOXXo0EFJSUk6ceKEEhMT5enp6ehsAwBygGPHjikwMFDu7u63bSQfEBCgRo0aac+ePZo6daq6deummJgYLV26VPny5VPBggVvu5+rK187AQCZy2azKTo6Wj/99JN8fX2VnJysmjVrqnnz5mratKkaNWqkX375RXFxcbp48aI2b96sRx55xNHZBgDkAGazWRcuXNDo0aNVo0YNNWjQQCVKlJCPj4+Cg4M1Z84cVa9eXY8//rhMJpNWr14tk8mkjh07MgMdwD2h9wXIImvXrlWJEiVUpkwZYwZEQkKCXFxcVKFCBeO8qlWr6uWXX9YHH3ygGjVqqFq1aurWrZtu3LihPHnyOCr7AIAcJDw8XFOnTlWBAgX0yiuvyM3NzQj82Ww25cqVS61bt5aHh4fWr1+vF154QQULFpSXl5fGjh1728bzAABkBZPJJF9fXz3//POaN2+efv31V0lSzZo1ZTabFRoaqsDAQMXExGjLli16/PHHHZxjAMDDzt4uslqtWrt2rWJiYrRjxw65urpq2rRp6tWrl0JDQ/Xkk09q7dq1qlKlipo1ayaz2ayFCxfK1dVVbdu2dXQxADgRgn5AFrDZbFq0aJE8PT01ZMgQBQcHy2QyyWQyKTw8XNeuXVOxYsWMcytUqKBixYrp8uXLkm6OBiLgBwDIKn5+fmrdurU2bdqkyZMna8iQIWlm/FmtVuXOnVuPPfaY2rRpo9OnT8vFxUWBgYHGBvPsNwEAyGypB5ik/rl06dLq0aOH5s6dq19//VVms1nVq1eXJHl5eSl37tzq3r27JMlisTAbHQCQacxmsy5fvixPT0+1bdtWNptNGzduVGBgoIKDg7Vo0SKtWrVKrq6uunHjhi5fvqygoCA1adJErq6uRv0FAHeL4ddAJrPZbDKZTPriiy/k5uamr776SidPnlRKSoqKFy+u+vXra9GiRTpx4oTMZrNMJpPc3d3l4eHBDAkAQJaz2Wxyc3NTkyZN1KxZM125ckVffPGFkpKSjICevX6KiYnRL7/8ouLFiys4ONgICBLwAwBkttRBvjVr1ujbb7/V559/rh07dig+Pl5lypTRk08+qaSkJK1Zs0Z79uyRpNvaWAT8AACZKSkpSV999ZWmT5+ufPnyqUWLFnrkkUc0Z84clS5dWu+8844effRRXbt2TadPn9a1a9ckSf7+/mrXrp0KFy7s4BIAcDZEFIBMZjKZlJycLFdXV40bN05JSUmaM2eOTp06JUlq1qyZvL29NW3aNO3atUvHjh3TvHnzdPnyZVWpUsXBuQcA5DQmk8kI/DVs2FAtW7ZME/hzcXEx9kz6+OOPtXXrVrm7uxvXM2AFAJAV7PXNnDlztGDBAnl5eSkxMVFLly7VokWLFBcXlybw9+OPP+rYsWMOzjUAIKdxdXVVYGCgLly4oKSkJBUtWlTt27dXjRo1NHr0aJ06dUo1a9bUuHHj9Mknn6hmzZqy2WySaFsBuD8mm/2vCIBMYZ/pFxYWpoMHD+rChQs6dOiQAgMDNXDgQAUFBenQoUPauHGjtmzZokKFCslkMunFF19UUFCQo7MPAMih7PVXcnKytmzZol9//VUFChTQSy+9JJvNpvHjx+v69euaNGmSXF1d2cMPAJDpbq1rNm7cqB9//FEvv/yygoODtXv3bk2aNEnFihVTxYoV1aNHD3l5eenw4cP67bff1LdvX+oqAECWsbepoqKi9PLLL6tt27Z64oknJEkXL17UkiVLtGvXLg0bNkxVqlQxgn0mk8mR2Qbg5Aj6AVng8OHDmjBhgvr166fixYsrJSVFX3/9tcxmc5rg3uXLl+Xi4iIPDw/28AMAONytgb+1a9fK19dXMTExunHjhhHwYw8/AEBWiIiIkL+/v6xWqyRp/fr1ioyMVNeuXbVz505NmTJFXbt2VUREhNavX68mTZqoS5cuyp07t3EPBqkAALKSvc767rvv9Ndff+mll16Sr6+vpP8F/vbu3avBgweratWqjssogIcG33SBLHDy5EmVLFlSjRo1UpkyZVSuXDlNnDjRCP4dP35cKSkpKliwoPLnz0/ADwCQJewN0FulHmF661Kfp0+fVkJCAgE/AECWOnPmjAYNGqTt27fLbDbLbDarXr16atmypSIiIrRw4UJ16dJFjz32mB577DG5ubnpt99+0/r16yWJpdIAAJnK3rZKSEhQUlKSkWavs2rUqKHDhw8b2/1IUuHChdWlSxeVL19e3377rRITE8X8HAAPih2rgUxknyERFxenuLg4Y5P4pKQkeXl56ZlnntF7772nqVOnatCgQQoODnZwjgEAOUXqmQ5nz56Vm5ub/P395eHhYQT7TCZTmsBfgwYNlDdvXlWtWlVms5mAHwAgy/j6+qp58+b6z3/+IxcXF9WqVUu5cuUylu+Mj49XtWrVJEnR0dEqW7asKleurGbNmkliqTQAQOYym826evWqPvjgAxUvXlzt27dX8eLFjTZXaGio6tatq2XLlikkJMSYhV6oUCH16tVL7u7u8vDwcGQRADwkGOIGZCJ7w7Ju3bqKiIjQkiVLJEnu7u6Sbm7mW6NGDbm6usrb29th+QQA5Dz2xuf333+vjz76SMOHD9e3336rffv2SfrfLL/UP7u5ual69eoym82yWq0E/AAAWcbX11fdunVTy5Yt9dFHH2nXrl1pZu25u7tr9+7dOn/+vBYsWKBcuXKpefPmRp0FAEBmSF3H+Pv7q2LFirp+/bpGjBihr7/+Wps2bTKO16lTR1euXNGVK1ckSRaLRZJUsGBB+fn5ZW3GATy02NMPyED2WRFnzpzR33//raJFi6pAgQLKkyePFi9erA0bNqhp06bq0qWLEhIStGTJEiUmJuqpp56i4xQAkCVSz/DbvXu3Zs2apeeee06RkZHatGmTXF1d1bJlS9WuXVvS/+o2AACy2rVr1+Tu7p5m+4OoqCgtXrxYv/zyi1577TXVqlVL8fHx+u6773TgwAElJiYqX758GjdunFxdXanHAACZxt62Cg8P1969exUaGqqiRYsqJSVFW7Zs0c6dO/Xnn3+qVKlSqlevnpo0aaJ3331Xfn5+evnllx2dfQAPKYJ+QAbbsWOHvvrqK/n4+OjGjRtq0KCB2rZtq7x582r16tVasmSJcufOrVy5cikiIkLvvPOOgoKCHJ1tAEAOs3//fu3evVuFChXSY489JunmHrQLFy6UxWJRq1atCPwBABxm+/bt+u9//ys/Pz81b95cefPmVYMGDSTdnBnx3XffafXq1Xr55ZdVp04dJSQk6NKlS4qPj1fZsmVZhhoAkKnsAb+zZ8/q448/VvHixdWkSRPVrFnTOCcuLk5Xr17V/PnzdfHiRSUlJalQoUI6deqURo4cqdKlSzuwBAAeVgT9gAxg7wy9evWqpk2bpho1aqhhw4bauHGjtmzZooCAAHXr1k2FChXSpUuXtHv3bnl5ealChQoqVKiQo7MPAMhhLl26pA8++EBXr15VmzZt1KtXL+OYPfCXkpKixo0bGx2sAABkFYvFopkzZ2rz5s3y8PBQkSJFdOXKFXl6eqpw4cJq3bq1TCaTDhw4oCVLluitt95S1apV09wj9cx2AAAyw/nz5/X222+rZcuWatOmjfz9/dM9Lzk5WdeuXdOvv/6q3377TYmJiZo0adIdzweAB0HQD8ggJ06c0ObNmxUREaEBAwbIx8dHkrRp0yb9+uuvCggIUMeOHVWyZEkH5xQAkNPYB6eknrG3f/9+zZs3TyaTSb169VLFihWN80+dOqXp06erdOnS6tu3r4NyDQDIyaKiorR06VKFh4erWLFiateunXbu3Kl9+/bpr7/+MmZLHDt2TFarVe+9956Cg4MdnW0AQA6RlJSkL774Qnnz5tWzzz5rpFssFkVHRysxMVFFihSRlHbllHPnzil37tzy9fV1RLYB5ACujs4A8LDYv3+/wsLC5OLiori4OCPo17hxY0nShg0bNH/+fPXq1UvFihVzZFYBADlI6pkOSUlJcnNzk8lkUuXKlWW1WrVw4UL98ssvMpvNKl++vCQpODhYgwYNUuHChR2ZdQBADubr66sOHTpoyZIl+vPPP+Xv7682bdqoRYsWOn/+vKKiorRx40ZZLBZdv36dwZUAgCzl4uKi6OhoVahQwUjbt2+f9u3bpw0bNihPnjwqWLCg3n77bZlMJmPJafoEAWQ2ZvoBGWj16tVauXKlqlSpoo4dO6pAgQLGsbVr12rnzp0aOHAg0/cBAFkidcBv+fLl+vPPP5WSkiI/Pz/169dPXl5e2rdvnxYuXKh8+fLpscceU7ly5e54DwAAslpkZKSWLFmiEydOqGbNmurSpYtx7NaZ7OzhBwDIKnFxcRo5cqTKlSun9u3ba8eOHdq0aZOKFy+u8uXLK1euXFq6dKlq1qypPn36ODq7AHIQgn7AfbA3KhMTE2Wz2ZQrVy7j2NKlS/Xbb7+pQoUKatu2rfLnz28ci4uLk5eXlyOyDADIwebNm6e1a9eqXbt2iomJ0YEDB3Tjxg29/fbbKlKkiPbs2aMlS5bI3d1dTz31lAIDAx2dZQAADFFRUVq8eLFOnjypWrVqqVOnTpKUJsjHIBUAQFY7cOCAJkyYIH9/f8XGxqp3796qVKmSChUqJIvFog8++EC+vr4aPHiwo7MKIAdheU/gHtkDfnv27NG6dev0999/q3bt2qpQoYKqV6+uTp06yWazafv27XJxcVGrVq0UEBAgSQT8AABZ7vLly9qxY4cGDRqkmjVrSpKio6M1efJkvffee/rss89UvXp1paSkaO/evSpRooSDcwwAQFq+vr7q0qWLFi9erN9//10JCQnq0aNHmll9BPwAAFktNDRUkydPVkxMjPLnz29s9SPdrJc8PT2VL18+2efc2Pf1A4DMxLdi4B6ZTCbt3r1bn376qYoXL6727dvr9OnT+vHHH7V161ZJUufOnVWvXj399ttvWr9+vVJSUhycawBATvDOO+9o586dadLi4uIUGRlpDECxWq3KmzevBgwYIElat26dJKlWrVoaMGCAzGazrFZr1mYcAIB/YQ/8FSxYUNHR0WLRIgBAdpA/f34FBwenCfhZLBb98MMPOnr0qBo3biyTyUTAD0CWYaYfcI8uXLigefPmqU+fPmrZsqWSkpK0YMEC5c6dW6tWrZLZbFa9evXUsWNHubi4qHbt2uwrAQDIEg0bNlS1atXSpBUvXlx+fn7atm2bSpQoYcyE8PHxkYeHh+Li4m67D7MlAADZka+vr/r27SsvL680e/kBAJBdbN68WSdPnlRYWJhGjBihwoULOzpLAHIYenSAO7jTyFF3d3dVr15ddevW1bVr1/Tqq6+qTp06eumllxQdHa2ffvpJ69evlyS1a9fOmFkBAEBma9mypdzc3LR48WKtXr3aSK9Zs6YOHz6stWvXGmmurq7y8PCQh4eHI7IKAMB9yZ07tzErnYAfACA7uXDhgjZs2KBr165p9OjRCgoKcnSWAORAzPQD0mHfBP769euKjo6W1Wo19jjy9/dX+/btlTt3bs2dO1dlypRRr1695OnpqTJlyujIkSPavXu36tSpI09PTxqiAIBMZ6+37OLi4rRgwQK5ubmpefPmatu2raKiorRmzRrt2bNHISEh2rt3rxISEtSqVSsH5hwAgPvDrHQAQHZTpEgRDRs2TG5ubvLy8nJ0dgDkUAT9gFvYO07Pnj2rKVOmKCYmRpJUuXJlPf/88zKbzcY63RcuXFBwcLA8PT0lSZ6enmrXrp3q169P5Q4AyBKpA35Xr16Vv7+/evfuLS8vL02dOlVWq1UtW7bUU089pV27diksLEwHDx5UwYIFNWrUKLm4uNwWNAQAAAAA3Lu8efM6OgsAcjiCfkAq9k7PM2fOaNSoUWrZsqVq1Kih7du3a926dQoKClKrVq1ktVqVnJys/Pnz68KFC1q7dq0uXbqk33//XU888YR8fX0dXRQAQA6QOli3aNEiXbhwQc2aNVNoaKi6dOkiq9Wqb7/9VtLNpT9btGihFi1ayGKxyNX15tfAlJQU9p4FAAAAAAB4CBD0A1Ixm826dOmSRo4cqfbt26tHjx6SpAIFCmjdunW6dOmScZ6Hh4caNmyolStX6qeffpK7u7vefPNN+fv7O7IIAIAcxB7w+/7777VhwwY9//zzKlasmHH8iSeekNVq1fTp0+Xi4qKmTZvKZDIZAT+bzUbADwAAAAAA4CFB0A9IxWq1av369fL09FSePHmM9G3btiklJUWXLl3SypUrlTt3btWtW1dVqlRRxYoVFRsbm2bZTwAAssrevXsVFhamUaNGKTAwUFarVdHR0Tp//rxCQkLUrVs3SdJ///tf+fj4qGbNmsa17DsLAAAAAADw8CDoB6RiNpvVpk0bJSUlKSwsTG5uboqPj9eyZcvUuXNnBQYGauvWrbp27ZrmzZunIkWK6LHHHkvTgQoAQGay2WxpgnUpKSny9vaWv7+/zp8/r61bt2rz5s2yWq3KkyePJkyYoG7duikgIEDVqlVzYM4BAAAAAACQmUw2m83m6EwA2U1UVJQWL16s/fv36/Llyxo5cqRCQ0Ml/W/vo9WrV+vUqVPq0KFDmqXUAADILKn38IuKipKPj48OHz6sGTNmKE+ePDp37pyqVaum0qVLq0iRIpo8ebIGDRqkqlWrGvdgDz8AAAAAAICHEzP9gHT4+vqqS5cuMplMOnTokE6fPm0E/exx8jZt2tBxCgDIMqkDfj/++KPCw8PVunVrVaxYUY8//rguXryo1q1bq0KFCvLx8VFERITy5s0rDw+PNPeh3gIAAAAAAHg4EfQD7sDX11edO3eWzWbT9u3blZKSok6dOsnV1dUI9tFxCgDIKvaA3/fff69Nmzapb9++8vX1lSTVrVvXOC8lJUXXr1/X1KlTlStXLpUtW9YR2QUAAAAAAEAWY3lP4F/Yl/o8ffq0KlWqpG7dujk6SwCAHGr37t369ttv9eabbyowMFCSFBMTo6tXr6pw4cLy9PTUsmXLdODAAV2/fl3jx4+Xq6trmlmCAAAAAAAAeDgx0w/4F/alPufOnaujR4/q+vXrypMnj6OzBQDIgSwWiwoUKKCAgACdO3dO27dv14YNG+Tm5qZ8+fLpzTffVKFChWS1WtW+fXu5uLiwFDUAAAAAAEAOwUw/4C5FRUVJkrGUGgAAmSm92Xk7d+7UtGnTVKpUKZ08eVKVKlVSSEiIvL29NW/ePA0bNkylS5f+x3sAAAAAAADg4cRMP+AuEewDAGSV1MG6S5cu6caNGypatKhq164tk8mko0ePqn79+qpYsaJ8fX0VEREhT09PpaSkpLkPAT8AAAAAAICcg6AfAABANmKz2Yxg3bx587R7925FRUWpUKFCKlu2rB5//HHVqlVL0s3gYFxcnKZOnSovLy+VKVPGkVkHAAAAAACAAzH8GwAAIBsxmUySpGXLlmn9+vV6+umnNW3aNOXLl09btmzR+fPnJUnJyclasmSJPv30U0VFRWnUqFEym82yWq2OzD4AAAAAAAAchJl+AAAA2YDNZpPJZJLValViYqL+/PNP9ejRQ5UrV9a+ffu0b98+9enTRyEhIbJYLHJzc1PRokWVnJysrl27ysXFRSkpKXJxcXF0UQAAAAAAAOAABP0AAAAcLPUefgkJCcqVK5eSk5NVvnx57du3T5988ol69+6tFi1aKDk5WZs2bVJgYKDq1KmjOnXqGPcg4AcAAAAAAJBzEfQDAABwoNR7+P33v/9VZGSkhg0bJpvNpsmTJ+vixYvq27evmjVrJkmKjo7Wtm3b5ObmptKlSxv3sd8DAAAAAAAAORO9QwAAAA5k38MvIiJC58+fV7t27ZQrVy717dtXcXFxKlq0qJo1ayaLxaK4uDh98803slqtatiwoYNzDgAAAAAAgOzEZLPZbI7OBAAAQE62YsUK7d27V3ny5NELL7wgd3d3JSUlKSwsTDNmzFChQoXk5eUli8WixMRETZw4Ua6urmmWBQUAAAAAAEDOxvKeAAAAWSx1sC4xMVEWi0Vnz56Vn5+f3N3dJUnu7u5q1KiRypUrp7Vr18rNzU3+/v5q3ry5zGazUlJS2MMPAAAAAAAABmb6AQAAOEhiYqI8PDwUFRWl7du3a9asWWrbtq169+4tSXcM7DHDDwAAAAAAALeitwgAACCLWK1W4+ewsDC98MILio6Olq+vr+rVq6fevXtr7dq1mj9/viTJxcVFKSkpso/Rsv9PwA8AAAAAAAC3oscIAAAgC6SenRcWFqYrV64oNjZW77//vqKiouTj46OGDRuqW7duWrNmjRYsWCDpZuDPZDJJkvE/AAAAAAAAcCuCfgAAAFnAHvD7/vvv9f3338tkMqlZs2aKjY3V6NGjjcBfgwYN1LVrVy1evFhr1651cK4BAAAAAADgLNjTDwAAIIv8/fffevfdd/X888+revXqkqTTp09r6tSpiouL09ixY+Xr66uoqCgdPnxYtWvXTndPPwAAAAAAAOBWzPQDAADIIvHx8YqLi1OhQoWMtJIlS+rpp59WZGSkPvjgA12/fl2+vr5GwC8lJcWBOQYAAAAAAICzIOgHAACQCdJbTCEwMFD58+fX5s2bjTSz2awSJUqoePHiunTpkkaNGiWLxWLM8GOmHwAAAAAAAO4GQT8AAIAMZrVaZTKZJEmxsbGKiopScnKy3N3dVbt2bR04cEBr1qwxzjeZTMqfP79efPFFubi4aOnSpQ7KOQAAAAAAAJyVq6MzAAAA8DCx2Wwym2+Oq1q4cKGOHDmiU6dOqVatWqpSpYoef/xxRUREaMOGDfrzzz9VoUIFhYWFSZJCQ0Pl7e2tiIgIRxYBAAAAAAAAToiZfgAAABnIPsPvhx9+0OrVq/Xoo49q+PDhunr1qmbPnq3ExET17t1bzZo1U0xMjMLCwpQ3b16NGjVK7u7u8vb2lq+vr2w2W7pLhAIAAAAAAADpYaYfAABABrt69ar++OMPvfTSS6pSpYoOHDig48eP65lnnlGePHkkSS1btlTLli2VlJQkd3d3SdL333+v48ePq0+fPkbwEAAAAAAAALgbBP0AAAAymNls1o0bN1SyZEnt3LlTX3zxhZ566ik1a9ZMSUlJCgsLU+nSpVWsWDG5u7vr7NmzWrFihQ4dOqSRI0eqcOHCji4CAAAAAAAAnAxBPwAAgAdw9uxZxcbGymazqWLFipIki8UiFxcXrVixQuvWrVOvXr3UqlUrSdKFCxe0c+dOFShQQMWKFZMklShRQvXq1VPXrl1VoEABh5UFAAAAAAAAzougHwAAwH3auHGjlixZooSEBJnNZlWqVEkvvPCCAgICVL9+fS1YsEBt2rRR69atJUkJCQmaP3++UlJSVL58eUmS1WqV2WxW1apVHVgSAAAAAAAAODuCfgAAAPfh119/1YwZMzRw4ECVKFFC69at09q1a1W5cmU1aNBAbdu2VUREhFavXq2UlBRZLBZdvnxZMTEx+uCDD2Q2m42AHwAAAAAAAPCgCPoBAADco507d+rbb7/V8OHDVbNmTUlS48aNtWbNGkVEREiSPDw89Nxzz6lYsWI6ePCgzGazypcvr8cff1wuLi5KSUmRi4uLI4sBAAAAAACAhwhBPwAAgHuQnJysP/74QwEBAQoPDzfSly1bJkk6deqUvv/+e/n4+KhZs2Zq06aN2rRpk+YeVquVgB8AAAAAAAAylMlms9kcnQkAAABnEhkZqZ9++knHjx9XvXr1dPToUV24cEHt27dXwYIFtXnzZp09e1ZXrlxRrly59Nxzz6lSpUqOzjYAAAAAAAAeYgT9AAAA7kNUVJQWL16s33//XfHx8froo4/k7+8vScZefZs3b1Z4eLg6d+7MzD4AAAAAAABkKoJ+AAAA9ykqKkpLlizR0aNHVa9ePXXo0EGSZLFY5OqadhV1eyAQAAAAAAAAyAwE/QAAAB6AfcbfyZMnVatWLXXq1EkSQT4AAAAAAABkLYJ+AAAAD8ge+Dt9+rQqVqyoHj16ODpLAAAAAAAAyGEYfg4AAPCAfH191aVLFxUsWFDR0dFiTBUAAAAAAACyGjP9AAAAMkhsbKy8vLxkNptls9lkMpkcnSUAAAAAAADkEAT9AAAAMhj7+QEAAAAAACCrEfQDAAAAAAAAAAAAnBxD0AEAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAAAAAnR9APAAAAAAAAAAAAcHIE/QAAAAAAAAAgG9m4caNMJpPx78yZM8axvn37GulNmjRxWB4BANkPQT8AgFMKDQ1N0wAqXLiwLBaLo7MFAAAAAHCAW4Nk9n8uLi7y9fVV9erV9cYbb+jSpUuOzioAAJmGoB8AwOns2rVLBw8eTJN26dIlrV692kE5AgAAAABkR1arVdHR0dq7d68+/PBDVatWTX///bejswUAQKYg6AcAcDozZ868p3RnExMT4+gsAAAAAIBT6969uyZNmqRRo0apUqVKRvqlS5f06aefOjBnAABkHoJ+AACnkpiYqHnz5hmPQ0JCjJ+XL1+ua9eupXvdkSNHNHjwYFWoUEG5c+eWl5eXgoOD1aNHD+3evTvNuTabTYsWLVKHDh1UtGhReXh4yN/fX9WqVdMrr7yipKQkSdKZM2fSLBuzcePGNPdp0qSJcaxv375GenrXTZs2TdWrV5enp6caNWokSTp9+rSGDRumhg0bqnjx4vL29paHh4eKFi2q9u3ba/ny5Xf8Pe3atUvPPPOMSpcuLS8vL+XOnVshISF65plndPLkSVmtVgUHBxt5GDFixG33GD58uHG8QoUKd3wuAAAAAMhu2rRpo9dee03jxo3Tli1b5O7ubhw7dOjQbedv2bJFPXr0UIkSJeTh4SEfHx/VrVtXX375pZKTk9N9jmvXrmn8+PGqU6eO/Pz8jPZa69attWDBAuO8iIgIvf7662revLkCAwOVJ08eubu7q2DBgmrZsqW+++472Wy2jP8lAAByHFdHZwAAgHvx008/KTIy0ng8a9YsNWrUSMnJyUpKStLcuXP14osvprlm2rRpeuGFF4xgnd3p06d1+vRp1alTRzVr1pQkJSQk6IknntDKlSvTnJuUlKTIyEjt27dP77zzTpoG44N65513tGXLltvSDx48qM8///y29AsXLujChQtasWKFxo4dq3feeSfN8XHjxmnMmDG3NRqPHz+u48ePq2PHjipVqpReeOEFDR8+XNLN3+P48ePl4uJinL9w4ULj52eeeeaByggAAAAAjpI3b17lzp1bERERkqT8+fOnOT5y5EhNnDgxTVpSUpK2b9+u7du3a8GCBVq1apW8vb2N47t27VKHDh1u2yPQ3l7z9PRU9+7djbRJkybdlq/w8HCtXbtWa9eu1YYNGzR9+vQMKS8AIOci6AcAcCqpl/CsXr266tSpoxYtWmjVqlXG8dRBv+3bt2vAgAGyWq2SJFdXV3Xt2lXlypXTuXPnbtsH8NVXX00T8CtevLg6d+6svHnz6uDBg1qxYkWGl2nLli0qWbKkHn/8cXl5eSk8PNzIa9WqVVWzZk0VKFBAPj4+unHjhrZt26YNGzZIksaPH69nn31WRYsWlXQzUDd69Gjj3l5eXurRo4dKliyp06dPp5kd+Oyzz2r06NGKi4vThQsXtHLlSnXo0EGStHPnTv31119GPp566qkMLzcAAAAAZLaYmBjNnDnTCPhJUrdu3Yyf58+fnybg17p1a9WvX1+XL1/WrFmzFBsbqy1btujll1/W1KlTJUnXr1+/LeDXrFkz1a9fXzExMdq6dWuaPJjNZpUvX161a9dWoUKF5Ovrq4SEBO3du1fLly+XzWbTjBkzNHDgQNWuXTuzfhUAgByAoB8AwGlcvHhRa9asMR737NnT+N8e9NuzZ4/+/PNPY8+GSZMmGQE/s9ms9evXq2HDhsY9kpKSjCBbZGSk0YiTpGrVqmnz5s3KnTu3kfb333+nGd2ZEYKCgrRnzx75+vqmSW/Tpo3atGmjY8eOae/evbpy5Yrc3Nz02GOPaceOHYqLi5PFYtH69euNoNz7779vXO/t7a09e/akWQL1xo0bunHjhiTJz89PvXr10jfffCNJ+vbbb42g3w8//GBc8+ijj6pQoUIZWmYAAAAAyEzPPPPMbSuWeHl5aezYsUa7R5I+/PBD4+c+ffpo1qxZxuPGjRsbAcIZM2bo/fffl7+/v2bOnJkm4DdhwoTbtkw4deqU8XOFChV06NAhnT17Vrt27dKlS5fk5uamhg0b6vfff9f58+clSb/88gtBPwDAAyHoBwBwGt99951SUlIkSSaTyVgqpVOnTsqVK5cSEhIk3WyMffLJJ5KUZoRl69at0wT8JMnd3V3FihWTdHNWoMViMY69+eabaQJ+0s2Zfxlt8ODBtwX8pJt7//Xq1UthYWH/eP25c+ckSXFxcdq7d6+R3qdPnzQBP+lmIDB10PLFF180gn4///yzLly4oCJFimjRokXGOSztCQAAAOBh0LlzZw0cONB4HBcXp3379hmPZ8+erdmzZ6d7rcVi0c6dO9WmTZs07cw8efLojTfeuO384OBg4+dr167p6aefvm0biVvZ23YAANwvs6MzAADA3Uq9tGe9evWMAFyePHnUtm1b49icOXOM4F3qJVyCgoL+8f6pz72b82916x56iYmJd3VduXLl0k3v1KnTvwb8Uj9PZGRkmjzcTf4rVaqkJk2aSJJSUlI0Y8YM7dixw1jas0CBAmrXrt2/3gcAAAAAspPu3btr4sSJadozc+bMUceOHY12061tqH9z5coVSWnbjsWLF0+zN3p6nn322X8N+El334YEAOBOmOkHAHAKO3bs0OHDh43H27Ztk8lkSvfc8PBw/fzzz+rQoYP8/f2N5TtPnz79j8/h7++f5vHp06dVq1atO55vNqcdOxMfH2/8bLVadfLkyX98Prv0lgs9evSo/vjjD+Pxk08+qQ8//FBFihSRyWRSQECA0eC08/Pzk8lkMhqt/1ZeuxdffFEbN26UJE2fPl3Xrl0zjvXu3Vtubm53dR8AAAAAyC7atGmjvn37SpIGDhyo//73v5Kk9evX6/vvv9dTTz1124orHTp0uG11mNSqV68uKW3b8e+//1ZKSsodA383btxIszd88+bNNXXqVJUsWVIuLi6qXbu2du3adT9FBADgNsz0AwA4hdSz/O7l/AYNGhhpa9as0bZt29KcZ7FYjP0T6tSpI1fX/42H+eCDDxQXF5fm/AsXLig5OVmSbmsgbt++3fj5m2++uS0ody9SB94k6YknnlDRokVlMpm0cePGdO/t5eWlatWqGY+/++47nThxIs058fHxRhDUrmPHjipRooSkm/tOTJkyxTjWr1+/+y4DAAAAAGQH77//vvLmzWs8HjdunFJS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wIED1aNHDyUlJWnGjBm5hne5//771bZtW7Vu3ZoACId36dIl1ahRQ8HBwfL09JSnp6eee+451alTR19//bVMJpMqV66sOnXqqFGjRjp16pQ+//xzbdq0Sc8++ywBEHe8n3/+WX/88Yf18fr16/Xf//5X0dHRSk9Pl9FoVJ8+fXT+/HktWrTIup2/v7/KlSungIAAGQwGmc1mwh8citls1oYNG5ScnKzly5drwYIFknIP9XLt4yeeeEL9+vUjAALIhbwH3BoyH3BzZD6g4Mh7sGd0+pVwFotFBoNBR44c0YIFCzR//nxFRETIyclJffr0UZs2bXTixAnNnDkzVxAcMWKEAgMDbVg5YB9SUlJ05swZSVcOxDmT6t5zzz1KS0tTTEyMdducK9ZcXV01fvx4BQcH26JkoNicPXtWycnJqlWrlqS/f3RMS0vThAkT9N1332n37t3y9vbW0KFDdeLEiRvOecLwLnA0RqNR1atXV9myZdWhQwdt2rTppkEwOztbkjRy5EiOLwCsyHvArSPzATdG5gMKh7wHe8acfneAHTt26KuvvlKNGjWUmZmp48ePq2vXrnrggQfk4uKiRYsWae/evQoMDNTDDz/M5KBwWHmNK3/69Gl99NFHCg0N1f33329d/scff2jq1Kl69dVXVb58+VzPyRnOAnAkW7dulY+Pjxo3bixJWrdunY4cOaLNmzerW7duMhgM2rdvn0aMGKFWrVrZuFqg+F09B8rVwxqNGzdOpUuXVnBwsFavXq3u3btr4MCBkvI+LgHAtch7QP6R+YDCI/MBN0beQ0nCGUwJFxMTo59++kn33XefunfvLrPZrH379umjjz6SwWDQI488on79+lnDYUZGBiEQDufo0aMKDg6Wq6vrdQfcwMBA3X333dq9e7emTZumYcOGKTExUYsWLVLp0qVVrly56/ZH+IMjsVgsSkhI0OLFi+Xv76+srCyFhoaqS5cu6tSpk+6++26tXr1aqampOn/+vDZv3kwAhMO5dhL3q4c16tWrl/bs2aNmzZopKytLq1atksFg0IABA66b8wEArkXeA/KHzAcUHpkPuDnyHkoazmJKkLVr16pKlSqqVauWdW6G9PR0OTk5qX79+tbtmjRpoueff16TJk1S8+bN1bRpUw0bNkwpKSny8fGxVfmATcTExGjatGkqW7as/vOf/8jFxcV6wLVYLHJ3d1ePHj3k5uam9evX64knnlC5cuXk6empCRMmcICGwzMYDPL399fjjz+uWbNm6bfffpMkhYaGymg0KiQkRMHBwUpMTNSWLVs0ePBgG1cMFD+j0ahz585p/Pjxat68udq3b68qVarI19dX1atX14wZM9SsWTMNHjxYBoPBGgT79+/P8QWAFXkPKBwyH3BryHzAzZH3UNLQ6VdCWCwWzZ8/Xx4eHnrqqadUvXp1GQwGGQwGxcTE6NKlS6pUqZJ12/r166tSpUq6cOGCpCtfTgRAOKJSpUqpR48e2rRpk6ZMmaKnnnoq19WfZrNZ3t7e6t27t3r27KmoqCg5OTkpODjYOuY2E1HDkVz9g8fV/12zZk0NHz5cM2fO1G+//Saj0ahmzZpJkjw9PeXt7a1//etfkhgOCY7j6mPJ2rVrlZiYqPDwcDk7O+vbb7/VyJEjFRISonvvvVdr165V48aN1blzZxmNRs2bN0/Ozs665557bP02ANgB8h5QeGQ+oGDIfED+kPdQUtHVXALkTN7+xRdfyMXFRV999ZX+/PNPZWdnq3LlymrXrp3mz5+v48ePy2g0ymAwyNXVVW5ublxNAIdmsVjk4uKijh07qnPnzoqNjdUXX3yhzMxMa7jLaSOJiYlavXq1KleurOrVq1sP6oQ/OJKrA9+aNWs0ffp0ffbZZwoPD1daWppq1aqle++9V5mZmVqzZo12794t6foJ2wl/cBRGo1EXLlxQcnKy7rnnHvXu3VuSFBwcrL59+2r+/PmaPHmyNm/erJSUFF24cEEBAQHq2LGjhg0bZv0RBYBjI+8BhUfmAwqGzAfkH3kPJRUJoQQwGAzKysqSs7Oz3n77bWVmZmrGjBk6ceKEJKlz587y8vLSt99+q507d+ro0aOaNWuWLly4YJ18F3BEBoPBGgLvuusudevWLVcIdHJyso5d/9FHH2nr1q1ydXW1Pp8fUeBocj7zM2bM0Jw5c+Tp6amMjAwtWrRI8+fPV2pqaq4Q+Ouvv+ro0aM2rhqwnczMTH311Vf67rvvVLp0aXXt2lWtWrXSjBkzVLNmTb355pvq1auXLl26pKioKF26dEmSFBAQoD59+qh8+fI2fgcA7AF5Dyg8Mh9QMGQ+IP/IeyipDBaLxWLrInBzOVd+hoWF6eDBgzp37pwOHTqk4OBg/fvf/1a1atV06NAhbdy4UVu2bFFQUJAMBoOefvppVatWzdblAzaX04aysrK0ZcsW/fbbbypbtqyeeeYZWSwWvfPOO0pKStLkyZPl7OzMfA5wKNd+3jdu3Khff/1Vzz//vKpXr67IyEhNnjxZlSpVUoMGDTR8+HB5enrq8OHD2r59u0aNGkV7gcMym8368ccfdfjwYb377rtydXXV2bNntXDhQu3atUsvvPCCQkJClJqaqsuXL6tixYrWYxIA5CDvAbeOzAfcGJkPKBzyHkoqOv1KiMOHD2vixIkaPXq0KleurOzsbH399dcyGo25wt6FCxfk5OQkNzc35nQArnJtCFy7dq38/f2VmJiolJQUa/hjPgc4mri4OAUEBMhsNkuS1q9fr8uXL2vo0KGKiIjQ1KlTNXToUMXFxWn9+vXq2LGjBg0aJG9vb+s++NEEjijnuBIfH6/nn39e99xzj4YMGSJJOn/+vBYuXKidO3fqueeeU+PGjZVzyk0ABJAX8h5w68h8QN7IfEDBkfdQkvFtXUL8+eefqlq1qu6++27VqlVLdevW1X//+19rGDx27Jiys7NVrlw5lSlThgAIh5Nz8nqtqw+61w77EhUVpfT0dMIfHNbJkyc1ZswY7dixQ0ajUUajUW3btlW3bt0UFxenefPmadCgQerdu7d69+4tFxcXbd++XevXr5f0d/si/MERGQwGmc1m+fr6qmPHjjp06JDi4+MlSeXLl9fAgQPVokULffHFF9q7d68MBgMBEMANkfeAf0bmAwqOzAcUDnkPJRnf2HYu5+Campqq1NRU60S5mZmZ8vT01EMPPaSTJ09q2rRpOnXqlC1LBWzm6ivOTp8+rfPnzysjI0PS38Hv6v92cXFR+/bt9eijj+qDDz4g/MFh+fv7q0uXLvr888+1c+dOSZK7u7v8/f114cIFpaWlqWnTppKkhIQE1alTR4MHD1afPn0kcQUbHEfOj4zp6enKzMy0Lsv54aR58+Y6fPiwdf4t6UoQHDRokOrVq6fp06crIyNDDLAB4FrkPSB/yHxA4ZD5gH9G3sOdxtnWBeDmcg6ubdq00fLly7Vw4UINHDjQOvG0s7OzmjdvrsuXL8vLy8uWpQI2kxP+fvnlF0VERCguLk5t2rRRu3bt1KRJE2vwy7nqJicENmvWTNKVAznhD47I399fw4YNk4uLiz788EO9+OKLatGihXW9q6urIiMjZTAYNGfOHPn6+qpLly7WK9642hOOwmg06uLFi5o0aZIqV66svn37qnLlytY2EBISojZt2mjJkiWqXbu2dSikoKAgjRw5Uq6urnJzc7PlWwBgp8h7QP6Q+YDCIfMB/4y8hzsN39x2JueKgJMnT2rLli06ceKEkpKSVLlyZfXv31/r16/XggULJF25+mD//v0KDAzUu+++q3LlytmydKDYXT28S2RkpMLDw/Xwww/rkUce0cWLF7V8+XJFRERIuv7qz6txEgtHcunSJSUlJVkf+/v7a+DAgerRo4c+/PBD69WfwcHBqlu3rtavX6+3335biYmJeuyxx6xtiXYDR3D1cSYgIEANGjRQUlKSXn/9dX399dfatGmTdX3r1q0VGxur2NhYSZLJZJIklStXTqVKlSrewgHYLfIeUDBkPqDgyHxA/pD3cKcyWLjv1O6Eh4frq6++kq+vr1JSUtS+fXvdc8898vPz06pVq7Rw4UJ5e3vL3d1dcXFxevPNN60TuwOOaN++fYqMjFRQUJB69+4t6cq8KPPmzZPJZFL37t3VsmVLSX9PxAs4oh07duibb75RqVKl1KVLF/n5+al9+/aSrpyw/vzzz1q1apWef/55tW7dWunp6YqOjlZaWprq1Kkjo9HIsEhwGDlXNsfExGjPnj0KCQlRxYoVlZ2drS1btigiIkL79+9XjRo11LZtW3Xs2FHvvvuuSpUqpeeff97W5QOwY+Q9oODIfED+kPmA/CHv4U5Gp5+dyDkpvXjxor799ls1b95cd911lzZu3KgtW7YoMDBQw4YNU1BQkKKjoxUZGSlPT0/Vr19fQUFBti4fsJno6GhNmjRJFy9eVM+ePTVy5EjrupwQmJ2drQ4dOlhPdAFHZDKZ9MMPP2jz5s1yc3NThQoVFBsbKw8PD5UvX149evSQwWDQgQMHtHDhQr322mtq0qRJrn0wvAscRc5n/fTp0/roo49UuXJldezYUaGhodZtUlNTdfHiRc2ePVvnz59XZmamgoKCdOLECY0dO1Y1a9a04TsAYG/Ie0DhkfmA/CHzAflD3sOdjk4/O3L8+HFt3rxZcXFxeuyxx+Tr6ytJ2rRpk3777TcFBgaqf//+qlq1qo0rBWwn5weTq6/e3Ldvn2bNmiWDwaCRI0eqQYMG1u1PnDih7777TjVr1tSoUaNsVDVgH+Lj47Vo0SLFxMSoUqVK6tOnjyIiIrR3716dOnXKehJ79OhRmc1mvffee6pevbqtywZs4uzZs3rjjTfUrVs39ezZUwEBAXlul5WVpUuXLum3337T9u3blZGRocmTJ99wewCOi7wH5A+ZDyg8Mh+QP+Q93Mno9LMjCxYs0IoVK+Tk5KQJEybkuqJz06ZN2rBhgzw8PDRy5EhVqlTJhpUCtnH1FWcZGRlycXGxTtS+d+9ezZs3T6VLl1avXr1Ur1496/POnj2r8uXLc7UaICkuLk4LFy7U8ePH1aFDB/Xs2VPSlXYSHx+vjRs36ty5c0pKStInn3zCsC5wSJmZmfriiy/k5+enhx9+2LrcZDIpISFBGRkZqlChgqTcQ4idOXNG3t7e8vf3t0XZAOwceQ/4Z2Q+4NaR+YCbI+/hTuds6wLwt0GDBsnT01PLly/XsmXL1L9/f5UtW1aS1KFDB2VlZSkiIkKenp42rhQofleHv6VLl2r//v3Kzs5WqVKlNHr0aOuQFPPmzdPKlStlMBhUt25dSVLFihWv2wfgqAICAjRo0CAtXLhQmzdvVmpqqgYNGqSKFSuqYsWKql+/fq4rq5nPAY7IyclJCQkJql+/vnXZ3r17tXfvXm3YsEE+Pj4qV66c3njjjVzthB/pAdwMeQ+4OTIfUDTIfMDNkfdwp+NOPxvJObBmZGTIYrHI3d3dum7RokXavn276tevr3vuuUdlypSxrktNTSUEwqHNmjVLa9euVZ8+fZSYmKgDBw4oJSVFb7zxhipUqKDdu3dr4cKFcnV11f3336/g4GBblwzYpfj4eC1YsEB//vmnWrRooQEDBkhSrsDHjyZwVKmpqRo7dqzq1q2rvn37Kjw8XJs2bVLlypVVr149ubu7a9GiRQoNDdUDDzxg63IB2CHyHlB4ZD6gaJD5gLyR93Cn404/G8gJgLt379a6dev0119/qWXLlqpfv76aNWumAQMGyGKxaMeOHXJyclL37t0VGBgoSQRAOLQLFy4oPDxcY8aMsU6um5CQoClTpui9997Tp59+qmbNmik7O1t79uxRlSpVbFwxYL/8/f01aNAgLViwQLt27VJ6erqGDx+e6wpPwh8claenpx5++GFNnDhR+/btU3Jysu677z41bNhQQUFBMplM2r59u5KSkmxdKgA7RN4DCo/MBxQdMh+QN/Ie7nR8s9uAwWBQZGSkPvnkE1WuXFl9+/ZVVFSUfv31V23dulWSNHDgQLVt21bbt2/X+vXrlZ2dbeOqgeL15ptvKiIiItey1NRUXb582fqjiNlslp+fnx577DFJ0rp16yRJLVq00GOPPSaj0Siz2Vy8hQMlSE4ILFeunBISEsTN/8DfQkJCNGXKFL3wwguaMmWKunXrZp1/y2g0ysPDQ6VLl5bFYqHtAMiFvAfkD5kPuP3IfEDeyHu4k3Gnnw2cO3dOs2bN0gMPPKBu3bopMzNTc+bMkbe3t1auXCmj0ai2bduqf//+cnJyUsuWLRlbGw7nrrvuUtOmTXMtq1y5skqVKqVt27apSpUq1ivSfH195ebmptTU1Ov2w1VrwM35+/tr1KhR8vT0zDWvAwCpTJkyuYbdk65M7j5//nwdOXJEI0aMoL0AuA55D8gfMh9QPMh8QN7Ie7hTcWZ0G93oKgBXV1c1a9ZMbdq00aVLl/TCCy+odevWeuaZZ5SQkKDFixdr/fr1kqQ+ffpYr3ADHEm3bt3k4uKiBQsWaNWqVdbloaGhOnz4sNauXWtd5uzsLDc3N7m5udmiVKDE8/b2tl4lzQktcGObN2/Wzz//rHXr1unVV19V+fLlbV0SABsi7wG3hswHFB8yH/DPyHu4U3Cn322SMxFuUlKSEhISZDabrWPNBwQEqG/fvvL29tbMmTNVq1YtjRw5Uh4eHqpVq5b++OMPRUZGqnXr1vLw8OBgDIdy7STSqampmjNnjlxcXNSlSxfdc889io+P15o1a7R7927Vrl1be/bsUXp6urp3727DyoGSj6ukgRs7d+6cNmzYIC8vL40fP16VKlWydUkAbIi8BxQemQ+wHTIfkDfyHu4kdPrdBjknsKdPn9bUqVOVmJgoSWrUqJEef/xxGY1G+fr6SrryhVK9enV5eHhIkjw8PNSnTx+1a9eOSdzhcK4OfxcvXlRAQIDuu+8+eXp6atq0aTKbzerWrZvuv/9+7dy5U2FhYTp48KDKlSuncePGycnJ6boACQBAUahQoYKee+45ubi4cI4GODjyHlB4ZD4AgD0i7+FOQqdfEcs5+Tx58qTGjRunbt26qXnz5tqxY4fWrVunatWqqXv37jKbzcrKylKZMmV07tw5rV27VtHR0dq1a5eGDBkif39/W78VoFhdHdzmz5+vc+fOqXPnzgoJCdGgQYNkNps1ffp0SVeGgenatau6du0qk8kkZ+crX2XZ2dnMhwIAuG38/PxsXQIAGyPvAYVH5gMA2DPyHu4UdPoVMaPRqOjoaI0dO1Z9+/bV8OHDJUlly5bVunXrFB0dbd3Ozc1Nd911l5YvX67FixfL1dVVr776qgICAmz5FgCbyAl/v/zyizZs2KDHH3881630Q4YMkdls1nfffScnJyd16tRJBoPBGv4sFgvhDwAAALcVeQ8oPDIfAADA7UenXxEzm81av369PDw85OPjY12+bds2ZWdnKzo6WsuXL5e3t7fatGmjxo0bq0GDBkpOTs41DAzgiPbs2aOwsDCNGzdOwcHBMpvNSkhI0NmzZ1W7dm0NGzZMkvTNN9/I19dXoaGh1ucyFwoAAABuN/IecGvIfAAAALcXnX5FzGg0qmfPnsrMzFRYWJhcXFyUlpamJUuWaODAgQoODtbWrVt16dIlzZo1SxUqVFDv3r1zncgCjsJiseQKbtnZ2fLy8lJAQIDOnj2rrVu3avPmzTKbzfLx8dHEiRM1bNgwBQYGqmnTpjasHAAAAI6IvAcUDJkPAACgeBksFovF1kXcieLj47VgwQLt27dPFy5c0NixYxUSEiLp7zHoV61apRMnTqhfv365hrQAHMHV8znEx8fL19dXhw8f1vfffy8fHx+dOXNGTZs2Vc2aNVWhQgVNmTJFY8aMUZMmTaz7YD4HAAAA2AJ5D/hnZD4AAIDix51+t4m/v78GDRokg8GgQ4cOKSoqyhoCc/pZe/bsyQksHNLV4e/XX39VTEyMevTooQYNGmjw4ME6f/68evToofr168vX11dxcXHy8/OTm5tbrv3QdgAAAGAL5D3g5sh8AAAAtkGn323k7++vgQMHymKxaMeOHcrOztaAAQPk7OxsDX+cwMIRXT2B+6ZNmzRq1Cj5+/tLktq0aWPdLjs7W0lJSZo2bZrc3d1Vp04dW5QLAAAAXIe8B9wYmQ8AAMA2GN6zGOQM/RIVFaWGDRtaJ6YGHFlkZKSmT5+uV199VcHBwZKkxMREXbx4UeXLl5eHh4eWLFmiAwcOKCkpSe+8846cnZ1zXTEKAAAA2Bp5D8gbmQ8AAKD4cadfMcgZ+mXmzJk6cuSIkpKS5OPjY+uyAJsymUwqW7asAgMDdebMGe3YsUMbNmyQi4uLSpcurVdffVVBQUEym83q27evnJycGB4JAAAAdoe8B+SNzAcAAFD8uNOvGMXHx0uSdUgLwFHkdaVmRESEvv32W9WoUUN//vmnGjZsqNq1a8vLy0uzZs3Sc889p5o1a950HwAAAIC9IO/BkZH5AAAA7AN3+hUjwh8c0dXBLTo6WikpKapYsaJatmwpg8GgI0eOqF27dmrQoIH8/f0VFxcnDw8PZWdn59oP4Q8AAAD2jLwHR0XmAwAAsB90+gG4bSwWizW4zZo1S5GRkYqPj1dQUJDq1KmjwYMHq0WLFpKuBMXU1FRNmzZNnp6eqlWrli1LBwAAAAD8AzIfAACAfeEyKgC3jcFgkCQtWbJE69ev14MPPqhvv/1WpUuX1pYtW3T27FlJUlZWlhYuXKhPPvlE8fHxGjdunIxGo8xmsy3LBwAAAADcBJkPAADAvnCnH4AiZ7FYZDAYZDablZGRof3792v48OFq1KiR9u7dq7179+qBBx5Q7dq1ZTKZ5OLioooVKyorK0tDhw5lAncAAAAAsGNkPgAAAPtEpx+AInX1fA7p6elyd3dXVlaW6tWrp7179+rjjz/Wfffdp65duyorK0ubNm1ScHCwWrdurdatW1v3QfgDAAAAAPtD5gMAALBfdPoBKDJXz+fwzTff6PLly3ruuedksVg0ZcoUnT9/XqNGjVLnzp0lSQkJCdq2bZtcXFxUs2ZN636YwB0AAAAA7A+ZDwAAwL5xlgWgyOTM5xAXF6ezZ8+qT58+cnd316hRo5SamqqKFSuqc+fOMplMSk1N1f/+9z+ZzWbdddddNq4cAAAAAPBPyHwAAAD2zWCxWCy2LgLAnWPZsmXas2ePfHx89MQTT8jV1VWZmZkKCwvT999/r6CgIHl6espkMikjI0P//e9/5ezsnGuIGAAAAACAfSLzAQAA2C+G9wRwS64ObhkZGTKZTDp9+rRKlSolV1dXSZKrq6vuvvtu1a1bV2vXrpWLi4sCAgLUpUsXGY1GJnAHAAAAADtF5gMAACg5uNMPQJHIyMiQm5ub4uPjtWPHDv3444+65557dN9990nSDUMeV3sCAAAAgP0j8wEAANg/zroAFIrZbLb+d1hYmJ544gklJCTI399fbdu21X333ae1a9dq9uzZkiQnJydlZ2cr5zqDnH8T/gAAAADA/pD5AAAASh7OvAAU2NVXaoaFhSk2NlbJycl6//33FR8fL19fX911110aNmyY1qxZozlz5ki6EgJzJn7P+TcAAAAAwL6Q+QAAAEomOv0AFFhO+Pvll1/0yy+/yGAwqHPnzkpOTtb48eOtIbB9+/YaOnSoFixYoLVr19q4agAAAABAfpD5AAAASibm9ANQKH/99ZfeffddPf7442rWrJkkKSoqStOmTVNqaqomTJggf39/xcfH6/Dhw2rZsiUTtwMAAABACUHmAwAAKHm40w9AoaSlpSk1NVVBQUHWZVWrVtWDDz6oy5cva9KkSUpKSpK/v781/GVnZ9uwYgAAAABAfpH5AAAASh46/QD8o7xuCA4ODlaZMmW0efNm6zKj0agqVaqocuXKio6O1rhx42QymaxXe3LVJwAAAADYHzIfAADAnYFOPwA3ZTabrROwJycnKz4+XllZWXJ1dVXLli114MABrVmzxrq9wWBQmTJl9PTTT8vJyUmLFi2yUeUAAAAAgH9C5gMAALhzONu6AAD2y2KxWCdwnzdvnv744w+dOHFCLVq0UOPGjTV48GDFxcVpw4YN2r9/v+rXr6+wsDBJUkhIiLy8vBQXF2fLtwAAAAAAuAEyHwAAwJ2FO/0A3FDO1Z5z587VqlWr1KtXL7300ku6ePGifvrpJ2VkZOi+++5T586dlZiYqLCwMPn5+WncuHFydXWVl5eX/P39ZbFY8hwuBgAAAABgO2Q+AACAOwt3+gG4qYsXL+r333/XM888o8aNG+vAgQM6duyYHnroIfn4+EiSunXrpm7duikzM1Ourq6SpF9++UXHjh3TAw88YA2SAAAAAAD7QuYDAAC4c9DpB+CmjEajUlJSVLVqVUVEROiLL77Q/fffr86dOyszM1NhYWGqWbOmKlWqJFdXV50+fVrLli3ToUOHNHbsWJUvX97WbwEAAAAAcANkPgAAgDsHnX4ArE6fPq3k5GRZLBY1aNBAkmQymeTk5KRly5Zp3bp1GjlypLp37y5JOnfunCIiIlS2bFlVqlRJklSlShW1bdtWQ4cOVdmyZW32XgAAAAAAuZH5AAAA7mx0+gGQJG3cuFELFy5Uenq6jEajGjZsqCeeeEKBgYFq166d5syZo549e6pHjx6SpPT0dM2ePVvZ2dmqV6+eJMlsNstoNKpJkyY2fCcAAAAAgGuR+QAAAO58dPoB0G+//abvv/9e//73v1WlShWtW7dOa9euVaNGjdS+fXvdc889iouL06pVq5SdnS2TyaQLFy4oMTFRkyZNktFotIY/AAAAAIB9IfMBAAA4Bjr9AAcXERGh6dOn66WXXlJoaKgkqUOHDlqzZo3i4uIkSW5ubnrkkUdUqVIlHTx4UEajUfXq1dPgwYPl5OSk7OxsOTk52fJtAAAAAADyQOYDAABwHHT6AQ4sKytLv//+uwIDAxUTE2NdvmTJEknSiRMn9Msvv8jX11edO3dWz5491bNnz1z7MJvNhD8AAAAAsENkPgAAAMdisFgsFlsXAcB2Ll++rMWLF+vYsWNq27atjhw5onPnzqlv374qV66cNm/erNOnTys2Nlbu7u565JFH1LBhQ1uXDQAAAADIBzIfAACA46DTD4Di4+O1YMEC7dq1S2lpafrwww8VEBAg6e+J2jdv3qyYmBgNHDiQqzwBAAAAoAQh8wEAADgGOv0ASLoSAhcuXKgjR46obdu26tevnyTJZDLJ2Tn3SMBM4A4AAAAAJQuZDwAA4M5Hpx8Aq5yrP//880+1aNFCAwYMkETgAwAAAIA7AZkPAADgzkanH4BcckJgVFSUGjRooOHDh9u6JAAAAABAESHzAQAA3Lm4jAtALv7+/ho0aJDKlSunhIQEcV0AAAAAANw5yHwAAAB3Lu70A5Cn5ORkeXp6ymg0ymKxyGAw2LokAAAAAEARIfMBAADceej0A3BTzO0AAAAAAHcuMh8AAMCdg04/AAAAAAAAAAAAoITjUi4AAAAAAAAAAACghKPTDwAAAAAAAAAAACjh6PQDAAAAAAAAAAAASjg6/QAAAAAAAAAAAIASjk4/AAAAAAAAAAAAoISj0w8AABs7ffq05s6dq0uXLtm6FAAAAABAESLvAQCKE51+AAAUo2HDhmnu3Lm5llWqVEmnT5/Wp59+quzs7DyfFxMTo2HDhmnjxo3FUCUAAAAAoKDIewAAW6PTDwDgkDZu3Khhw4Zp2LBh+uOPP65bb7FYNGbMGA0bNkzvv//+ba3FaDTqmWeekSTNmDHjtr4WAAAAANzpyHsAAEdFpx8AwKG5uLho69at1y0/dOiQLl26JBcXl2Kpw9XVVS+//LI8PT2VkpJSLK8JAAAAAHcy8h4AwNHQ6QcAcGhNmzbV9u3brxtmZevWrapevbr8/f2LrRYfHx8NGTJEXl5exfaaAAAAAHCnIu8BAByNs60LAADAltq3b6+dO3dq3759atq0qSTJZDJpx44dGjx4sFauXJlr+/T0dM2dO1fbt29XQkKCypYtqy5duqhv374yGAzW7bKysjRjxgxt2bJFWVlZatCggR555JE8a4iLi9Ps2bO1Z88epaSkKCgoSPfcc4+6dOnyj/WfPXtWs2fP1oEDB5SZmanKlStryJAhCg0NvYW/CgAAAACUfOQ9AICj4U4/AIBDK1u2rGrXrq1t27ZZl+3Zs0epqalq27Ztrm0tFos++OADLV++XI0bN9YDDzygChUq6JdfftGPP/6Ya9uvv/5aK1asUOPGjXXvvffKyckpz7ki4uPjNXbsWO3bt089evTQqFGjFBQUpG+++UZLly69ae1//fWXxo4dq7Nnz2rAgAG6//775ebmpsmTJysiIuIW/ioAAAAAUPKR9wAAjoY7/QAADq9du3aaNWuWMjMz5erqqi1btqh+/foKCAjItV1kZKQOHDig4cOHa9CgQZKknj176uOPP9bKlSvVs2dPBQUF6eTJk9qyZYu6d+9uvdqzZ8+e+vzzz3Xq1Klc+5w9e7ZMJpM++ugj+fr6SpK6d++uTz75RPPnz1f37t3l5uaWZ90//PCDypQpo/fee886F0WPHj305ptvasaMGWrZsmWR/p0AAAAAoKQh7wEAHAl3+gEAHF7btm2VmZmpXbt2KS0tTbt371b79u2v227Pnj0yGo3q1atXruV9+vSRxWLR3r17rdtJUu/evXNtd+1ji8Wi8PBwtWrVSu7u7srMzLT+07JlS6WlpSkqKirPmpOTk3XgwAG1adNGaWlpSkxMVGJiopKSktS4cWOdP39ecXFxhf2TAAAAAMAdgbwHAHAk3OkHAHB4vr6+atiwobZu3aqMjAyZzWa1bt36uu1iY2NVqlQpeXh45FpeqVIl6/qcfxsMBpUrVy7XdhUqVMj1ODExUSkpKVqzZo3WrFmTZ22JiYl5Lo+OjpbFYtGcOXM0Z86cPLdJSEi47upVAAAAAHAk5D0AgCOh0w8AAF2Z4P2bb75RfHy8mjRpIi8vr9v+mhaLRZLUqVOnG07ifm1wzGE2myVJffv2VePGjfPcJigoqAiqBAAAAICSjbwHAHAUdPoBACCpZcuWmjZtmo4dO6bnnnsuz23Kli2r/fv3Ky0tLdfVn2fPnrWuz/m3xWLRhQsXcoW4c+fO5dqfr6+vPDw8lJGRodq1axeo3pyrSp2cnNSoUaMCPRcAAAAAHAl5DwDgKJjTDwAASe7u7nrkkUc0dOhQhYaG5rlN06ZNZTabtWrVqlzLly9fLoPBoCZNmli3k6QVK1bk2u7ax0ajUa1atVJ4eLhOnjx53evFx8ffsF4/Pz81aNBAa9eu1eXLl69bf6NhYgAAAADA0ZD3AACOgjv9AAD4fx07drzp+ubNm6tBgwaaPXu2YmNjVbVqVf3++++KjIxU7969rcOrBAcHq127dlqzZo1SU1NVp04d7d+/XxcuXLhun/fee68OHjyoN954Q126dFHlypWVmJioEydO6ODBg/r+++9vWM/DDz+scePG6cUXX1SXLl0UGBiohIQEHT16VHFxcZo8efIt/T0AAAAA4E5B3gMAOAI6/QAAyCej0ahXXnlFc+bMUVhYmDZs2KDAwEDdd9996tu3b65tx4wZI19fX23dulU7d+5USEiIXn31VY0ZMybXdv7+/vrvf/+r+fPnKyIiQmvWrJGPj48qV66skSNH3rSeSpUq6f3339e8efO0ceNGJSUlyc/PT8HBwRo8eHCRv38AAAAAuFOR9wAAdwKDJWdWWQAAAAAAAAAAAAAlEnP6AQAAAAAAAAAAACUcnX4AAAAAAAAAAABACUenHwAAAAAAAAAAAFDC0ekHAAAAAAAAAAAAlHB0+gEAAAAAAAAAAAAlHJ1+AAAAAAAAAAAAQAlHpx8AAAAAAAAAAABQwtHpBwAAAAAAAAAAAJRwdPoBAAAAAAAAAAAAJRydfgAAAAAAAAAAAEAJR6cfAAAAAAAAAAAAUMLR6QcAAAAAAAAAAACUcHT6AQAAAAAAAAAAACUcnX4AAAAAAAAAAABACUenHwAAAAAAAAAAAFDC0ekHAAAAAAAAAAAAlHB0+gEAAAAAAAAAAAAlHJ1+AAAAAAAAAAAAQAlHpx8AAAAAAAAAAABQwtHpBwAoUTZu3CiDwfCP/4waNSrX88LDw/XSSy+pU6dO8vPzy7Xtxo0bC1VLXFyc3njjDTVt2lQ+Pj5ydXVVYGCg6tWrp4EDB2rChAn666+/bv1NAwAAAMAd7kZZz8nJSf7+/mrWrJleeeUVRUdH33Q/v//+u5544gk1bNhQ/v7+cnV1Vbly5dS5c2d9+OGHSkhI+Mdatm3bpscee0whISHy9/eXi4uLypQpo7vuukvjx4/Xn3/+WeD3Fx0dLRcXl1zvbciQITfc/mb5VpJ++OGHfOXaCxcu6J133lGHDh1Urlw5ubq6ysvLSw0aNNDDDz+slStXymKxFPj9AADsk7OtCwAAoDjMmjVLn332WZHt79SpU2rfvr3OnDmTa3lsbKxiY2P1xx9/aNGiRWrcuLEqV65cZK8LAAAAAI7EbDYrISFBe/bs0Z49e/TTTz8pIiLiupxlMpn0wgsv6PPPP79uHzExMYqJidGGDRs0adIkzZgxQ927d79uu8uXL2v06NFatGjRdesuXbqkrVu3auvWrdq0aVOBLx79+eefZTKZci1bunTp/7F33/FRVPv/x9+72XRIo0MuBAhICb0poFIUUQEBpanXgiIgWFARRbmAioodlYvX61cBlSaCVAHpvQtIB6VITUIq6cnu7w9+mZslCSRhk90lr+fjkcdj58yZ2TObOTv7mc/MGcXExCgkJKRQ6yqof//733r55ZeVmppqV56RkaGDBw/q4MGD+vbbb3XixAmFhYUVSxsAACWLpB8AwK3169dPLVu2zFUeERGRq6xSpUpq0aKFgoKCNGPGjBt631GjRhkJP4vFoj59+qhBgway2Wz666+/tHnzZh09evSG3qM4ZGVlKS0tTX5+fs5uCgAAAADkKzvWS0hI0C+//KI//vhD0pU75j799FN98skndvWfe+45ffXVV8Z01apV1bdvX5UvX15//PGH5s6dq6ysLEVHR6t79+5avXq12rVrZ9RPSkpSly5dtHPnTqOscuXK6tmzp6pXr67ExETt3r1bq1atKtL2TJs2LVdZenq6ZsyYoeHDhxdpndfywQcfaNSoUca0h4eH7r//frVo0UImk0nHjx/X8uXLdfHiRYe/NwDAeUj6AQDcWteuXfMc6uRq7733nj777DNJV4aNudGk34oVK4zXb775psaOHZurzqFDh+Tr65urPDMzU9OnT9esWbO0d+9excbGKjAwULVr19a9996ba11Hjx7Vp59+qtWrVxvDhYaGhqpjx44aMWKE6tWrZ1f/iSeeMALKO++8U9OnT9cbb7yhFStWKCoqSvPmzVPPnj0lXRnqZdKkSVq6dKn+/PNPZWRkKDQ0VPfcc49GjRql6tWr39DnBAAAAABFkTPWe/nll1WxYkWlp6dLkg4ePGhXd/PmzXYJv+bNm2vNmjUKCAgwylavXq27775bVqtV6enpGjx4sPbt2yez+crTj9555x27hN8DDzygGTNm5Lpg8ty5c1q0aFGhtmXHjh06cOCAMV23bl3jItGpU6c6POl38OBBjR492piuWLGili1bpmbNmtnVy8jI0NSpU7koFABuIjzTDwBQKuSVfLsROYdlOXz4sNLS0nLVqV+/fq4hUmJiYtS2bVs99dRT+u233xQZGamMjAxFR0dr27Zt+vTTT+3q//TTT2rSpIm++uorHT16VCkpKUpJSdGxY8f09ddfq2nTppo1a1a+7Tx37pzatGmjH374QZGRkXbPatiyZYsiIiL03nvvae/evbp8+bLS0tL0559/6t///rcaN26sDRs2FPETAgAAAADHCAwMVJkyZYzp8uXL283/+uuv7aY/+OADu4SfJHXq1En9+vUzpg8cOGDEOxkZGZo8ebIxr3Llyvrxxx/zTIZVrVpVgwcPLlT7v/vuO+N1aGioPvjgA2N6165dxl2MjvLFF18oKyvLmJ4yZUquhJ8keXp6atCgQapYsaJD3x8A4Dzc6QcAcGvLli1TdHR0rvJ+/foV67P0mjdvrnXr1kmSZs2apaVLl+q2225T8+bN1aZNG3Xq1Elly5bNtdw///lP7dixw5iuX7++7rvvPnl7e+v333/Xtm3bjHnHjx/XP//5TyOhWK5cOT3++OMymUyaNm2aoqOjlZaWpscff1wtWrRQnTp1cr3fsWPHJEm9e/dWkyZNdOrUKQUGBiohIUE9e/Y0PrsaNWqoX79+8vX11dy5c3XgwAHFx8frwQcf1LFjxxQYGOi4Dw8AAAAACighIUFTp05VTEyMUda3b1+7OjkvVgwODlbnzp3zXFe/fv00c+ZMu+XuvPNO7dixQ4mJiXb1/P39HdL+tLQ0uws1+/btq3vvvVdBQUGKi4uTdOVuv48//tgh7yfJbgjS4OBgY6QXAMDNj6QfAMCtzZ49W7Nnz85V3rJly2JN+n3wwQe6/fbbjeFlEhIStHz5ci1fvlyS5OPjo2eeeUbvvfeecXXoH3/8oaVLlxrruO+++/TLL7/I09PTKPvrr7+M119++aWR8DObzVq7dq3xrMInnnhCTZo0MYammTx5sjF86dU+++wzvfDCC3Zln3/+uSIjIyVdCQJ3795tPDx+5MiRqlmzpqKiohQVFaVp06bp+eefL/JnBQAAAACF9eSTT+rJJ5+0K/Pz89P48ePVo0cPu/Lz588br2vUqJHvOq+el73c2bNn7cqvfoTCjViwYIFiY2ON6QEDBsjLy0u9e/fWt99+K0n68ccfNXHiRFksjjlVm3N76tatawxhCgC4+fGNDwBAEbRu3Vrbtm3TAw88YJe0y5aamqrPP/9czz77rFG2ceNGuzpjx47NtWytWrWM11u2bDFet2jRwkj4SVJERIRatGiRZ92cgoODNWzYsFzlmzZtMl7HxsaqXLlyMplMMplMKlOmjKKiooz5mzdvznPdAAAAAFCSevXqpSFDhji7GYUydepU43V4eLhatmwpSerfv79RfvHiRbsLRAEAKCqSfgAAt/bdd9/JZrPl+uvQoUOxv3fTpk31yy+/KC4uTmvWrNF7772X632nTZtmDEOTczgaSapZs+Y115+zfqVKlXLNz1mW88rRnGrXrp3n1aJXt+VaciYAAQAAAKAk9OvXT++++666detmlP3444964IEH7J5VLklVqlQxXp8+fTrfdZ46dSrP5apVq2ZXfvjw4SK3O6dz585pxYoVxnTOZwp26tTJ7ll6OZODkuziuNTU1FzrTklJsZv28vIyXufcnqNHj+b6vAAANy+SfgAA3CA/Pz916NBBr732mtasWaO33nrLbn72c/Wyh8/MduLEiWuuN2f9ixcv5pqfsyw4ODjPdeT3HIqc665SpYo+/PDDfP+eeeaZa7YTAAAAAByta9euev3117Vo0SINHjzYKF+9erV++OEHu7q333678TomJkarV6/Oc51z5szJc7lWrVrZPZN9zpw5Sk5OvuFt+P7775WVlWVMT5gwwRhhxWKxGI9ckKQlS5bo0qVLxnSFChWM13nFjjkfDXF1/ZzPNIyNjdWCBQtubEMAAG6DpB8AAEXw3HPPae3atXleMVmmTBm76aCgIElS+/bt7crffvttZWZm2pXlvPK0bdu2xutdu3bpwIEDxvT+/fu1a9euPOsWRM76UVFR6tKli1555RW7v5dffllNmzZV69atC7VuAAAAAHCk999/X4GBgcb0W2+9ZZdMu/pCxVGjRikxMdGubO3atXbPg2/QoIGR9PP09LR7NMP58+f1z3/+M9fddNKVu/e+/vrrArX76rv3riU9PV0//vijMd2mTRvj9fbt2/Xbb78Z06dOndL3339vTJcrV07h4eHG9PDhw+Xh4WFMDx06VHv37s31nhkZGfrmm2/sko8AAPdmsnF/NwDAjaxdu1YdO3Y0pr/77js98cQT111uxYoVxrAqf//9t90Vnn379tU//vEPSVKXLl3UpUuX664vLCxMp06dUtWqVXXnnXeqTp068vLy0pEjRzR79mylp6dLujKE559//imTySRJuv/+++2e1dCgQQPdd9998vHx0YEDB7R+/XpFR0dLko4fP66IiAilpaVJuhLIPf744zKZTJo2bZpRz8vLSwcOHDCCvCeeeELTpk2TJN15551au3ZtrvbHx8crPDzcWEeZMmXUp08fhYeHKy0tTUeOHNHatWt18eJFrVmzpkSGSwUAAABQel0v1nvjjTf07rvvGtM//vijHn74YWN6yJAh+s9//mNMV6tWTX379lX58uX1xx9/aO7cucZFl15eXlq9erXatWtn1L98+bLuvPNO7d692yirUqWKevXqpdDQUCUmJmr37t1atWqV2rVrl2ecldPWrVt12223GdNt2rRRWFhYrnqrVq0y4rKmTZvq999/lyT99ttvdrGp2WxWRESE/Pz8tG/fPrs7Ed944w298847dut97733NHr0aGPaYrGoW7duatasmUwmk44fP67ly5fr4sWLOnHiRJ5tAwC4IRsAAG5kzZo1NknG33fffVeg5caOHWu3XH5/Y8eOLdD6atSocd11+fj42FatWmW3XHR0tK1Vq1b5LhMYGGhXf86cOTYfH59863t7e9tmzpxpt8zjjz9uzL/zzjvz3YZNmzbZypcvf93tWLNmTYE+EwAAAAAoquvFepGRkTY/Pz9jfsOGDW1Wq9WYn5GRYRs+fPh145ty5crZli9fnmcboqOjbd26dbvuOq4VZ2UbPHiwUd9sNttOnTqVZ70xY8bYrXvv3r3GvH/961/Xbcs999xjS0tLy3PdkyZNsnl7e193HSdOnLju9gAA3APDewIAUATLly/Xl19+qd69eysiIkIVK1aUxWKRv7+/GjRooGHDhumPP/5Qp06d7JYrV66cNm3apG+++UZ33XWXKlSoIIvFouDgYLVo0UIvvviiXf0+ffpoz549GjJkiMLDw+Xj4yMfHx/Vrl1bgwYN0u+//67+/fsXaRvatm2rAwcOaMyYMWrRooUCAgLk4eGhoKAgtWjRQsOHD9dvv/2mO+64o6gfEwAAAAA4RIUKFfT0008b0wcOHND8+fONaYvFoi+++EK///67hg4dqgYNGqhs2bKyWCyq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",
"text/plain": [
"<Figure size 1800x1400 with 5 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Créer la visualisation\n",
"display_metrics_subplot(results_df)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "u83ZVCLJWxlQ"
},
"source": [
"### Identifier le meilleur modèle selon `Precision`"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "eHr3Z6btZm2E"
},
"source": [
"Le contexte métier est le suivant : on souhaite éviter à tout prix qu'un tweet qui serait reçu négativement soit à tort prédit comme positif. La variable \"precision\" (basée sur la modalité 1 = tweet positif) est donc la plus pertinente."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 5,
"status": "ok",
"timestamp": 1751740006356,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "f07BiYp_ZgDh",
"outputId": "d30f2935-f9a5-4df3-8fe9-082caf759620"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Meilleur modèle (selon la précision): Random Forest avec BoW\n",
"Precision: 0.7798\n",
"F1 Score: 0.7687\n",
"ROC AUC: 0.8515\n",
"Meilleurs paramètres: {'max_depth': 'None', 'min_samples_split': '5', 'n_estimators': '200'}\n",
"\n",
"Run ID du meilleur modèle: 7d52e808227f468da7f733265280fc02\n"
]
}
],
"source": [
"# Identifier le meilleur modèle selon la précision\n",
"best_model_row = results_df.loc[results_df['Precision'].idxmax()]\n",
"best_model_name = best_model_row['Modèle'].replace(\" \", \"_\")\n",
"best_vectorizer = best_model_row['Vectorisation']\n",
"\n",
"print(f\"\\nMeilleur modèle (selon la précision): {best_model_row['Modèle']} avec {best_vectorizer}\")\n",
"print(f\"Precision: {best_model_row['Precision']:.4f}\")\n",
"print(f\"F1 Score: {best_model_row['F1 Score']:.4f}\")\n",
"print(f\"ROC AUC: {best_model_row['ROC AUC']:.4f}\")\n",
"print(f\"Meilleurs paramètres: {best_model_row['Meilleurs paramètres']}\")\n",
"print(f\"\\nRun ID du meilleur modèle: {best_model_row['Run ID']}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "lv6SEBguWxlQ"
},
"source": [
"## Charger le meilleur modèle et son vectoriseur depuis MLflow"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "4n3rWdQ4WxlQ"
},
"outputs": [],
"source": [
"def load_and_save_best_model(run_id, folder_path=\"content/simple-model/best\"):\n",
" \"\"\"\n",
" Charge le meilleur modèle et son vectoriseur depuis MLflow et les sauvegarde localement\n",
"\n",
" Parameters:\n",
" -----------\n",
" run_id : str\n",
" L'identifiant du run MLflow contenant le meilleur modèle\n",
" folder_path : str, optional\n",
" Le chemin où sauvegarder le modèle et le vectoriseur\n",
"\n",
" Returns:\n",
" --------\n",
" tuple\n",
" (modèle chargé, vectoriseur chargé)\n",
" \"\"\"\n",
" # Créer le dossier cible s'il n'existe pas\n",
" try:\n",
" os.makedirs(folder_path, exist_ok=True)\n",
" except PermissionError:\n",
" print(f\"Erreur de permission pour le dossier {folder_path}\")\n",
" print(\"Tentative avec un dossier temporaire...\")\n",
" import tempfile\n",
" temp_dir = tempfile.mkdtemp()\n",
" print(f\"Utilisation du dossier temporaire : {temp_dir}\")\n",
" folder_path = temp_dir\n",
"\n",
" # Créer un client MLflow\n",
" client = MlflowClient()\n",
"\n",
" print(f\"Chargement du modèle depuis le run {run_id}...\")\n",
"\n",
" # Charger le modèle depuis MLflow\n",
" best_model = mlflow.sklearn.load_model(f\"runs:/{run_id}/model\")\n",
"\n",
" print(f\"Modèle chargé avec succès : {type(best_model).__name__}\")\n",
"\n",
" # Récupérer les paramètres du run pour vérifier le type de vectoriseur\n",
" run = client.get_run(run_id)\n",
" vectorizer_type = run.data.params.get(\"vectorizer_type\", \"\").lower()\n",
"\n",
" print(f\"Type de vectoriseur utilisé dans ce run : {vectorizer_type}\")\n",
"\n",
" # Récupérer les artefacts du run\n",
" artifacts = client.list_artifacts(run_id)\n",
"\n",
" # Chercher le vectoriseur en privilégiant BoW ou CountVectorizer\n",
" vectorizer_path = None\n",
"\n",
" # Afficher les artefacts disponibles\n",
" print(\"Artefacts disponibles :\")\n",
" for artifact in artifacts:\n",
" print(f\" - {artifact.path}\")\n",
"\n",
" # Priorité 1: Chercher un vectoriseur correspondant au type attendu (BoW ou CountVectorizer)\n",
" for artifact in artifacts:\n",
" path_lower = artifact.path.lower()\n",
" if \"vectorizer\" in path_lower and (\"bow\" in path_lower or \"countvectorizer\" in path_lower):\n",
" vectorizer_path = artifact.path\n",
" print(f\"Vectoriseur de type BoW/CountVectorizer trouvé : {artifact.path}\")\n",
" break\n",
"\n",
" # Priorité 2: Chercher n'importe quel vectoriseur\n",
" if not vectorizer_path:\n",
" for artifact in artifacts:\n",
" if \"vectorizer\" in artifact.path.lower():\n",
" vectorizer_path = artifact.path\n",
" print(f\"Vectoriseur générique trouvé : {artifact.path}\")\n",
" break\n",
"\n",
" # Télécharger et charger le vectoriseur\n",
" if vectorizer_path:\n",
" local_path = client.download_artifacts(run_id, vectorizer_path)\n",
" print(f\"Vectoriseur téléchargé dans : {local_path}\")\n",
"\n",
" # Charger le vectoriseur depuis le fichier local\n",
" with open(local_path, \"rb\") as f:\n",
" vectorizer = pickle.load(f)\n",
"\n",
" print(f\"Vectoriseur chargé avec succès : {type(vectorizer).__name__}\")\n",
" else:\n",
" print(\"Aucun fichier vectoriseur trouvé dans les artefacts, tentative de chargement direct...\")\n",
"\n",
" # Tenter de charger le vectoriseur directement depuis MLflow\n",
" try:\n",
" vectorizer = mlflow.sklearn.load_model(f\"runs:/{run_id}/vectorizer\")\n",
" print(f\"Vectoriseur chargé directement depuis MLflow : {type(vectorizer).__name__}\")\n",
" except Exception as e:\n",
" print(f\"Erreur lors du chargement direct du vectoriseur : {e}\")\n",
" raise FileNotFoundError(\"Aucun vectoriseur trouvé dans les artefacts du run\")\n",
"\n",
" # Sauvegarder le modèle et le vectoriseur localement avec leurs noms spécifiques\n",
" model_name = type(best_model).__name__\n",
" vectorizer_name = type(vectorizer).__name__\n",
" model_save_path = os.path.join(folder_path, f\"{model_name}_model.pkl\")\n",
" vectorizer_save_path = os.path.join(folder_path, f\"{vectorizer_name}_vectorizer.pkl\")\n",
"\n",
" # Sauvegarder le modèle\n",
" with open(model_save_path, \"wb\") as f:\n",
" pickle.dump(best_model, f)\n",
"\n",
" # Sauvegarder le vectoriseur\n",
" with open(vectorizer_save_path, \"wb\") as f:\n",
" pickle.dump(vectorizer, f)\n",
"\n",
" print(f\"Modèle et vectoriseur sauvegardés avec succès dans {folder_path}\")\n",
" print(f\"Chemins des fichiers :\")\n",
" print(f\" - Modèle : {os.path.abspath(model_save_path)}\")\n",
" print(f\" - Vectoriseur : {os.path.abspath(vectorizer_save_path)}\")\n",
"\n",
" return best_model, vectorizer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 494,
"referenced_widgets": [
"91504476b2b34a9eb462a30da59a2b19",
"88ae9e2307804694bc546afb10a362f7",
"9897c186c00947c280a7eae2fc9a315c",
"9572601035c34a5eb88f81f6514f872d",
"8e7128400e4c4eff96b67db314ccaff1",
"727814859996469da84c76c1a3f15bef",
"6f6c8fc42ef846a4ac56ffe69bcbbe0f",
"d5f9951d6c094fcfb03e166af93cfea5",
"6efad0330efc4790a1eac95fcc96de17",
"b08959c4edfa465588e03c99d00ad586",
"8b25983eb05344928535625c726939d8",
"9ecc775347df487bb63a5e1b2dcc68b7",
"073bf36ff26d455ca01956dee8d1f1a2",
"ce14528df1ed4f1abb72ccb9a3c66443",
"6d712f894c5d4d19a478fa1d1a2b2c62",
"b0c4432101b948979653cd43bcd3fc4b",
"8ddc3f81d9094d06b680908375077c62",
"0e157319861c42049d63bd43b797987e",
"b7d70f2ce66f40ea8f606256cb654540",
"d112b0758b97447ab47470e826e94687",
"6c04e4ad7c5f49c3bd9658ccab659890",
"acbe4e7d94f04dfe9e797ab54d3102ad",
"91b8f1667ff54011bd98fd748a0a91c6",
"3a1f9f8dff4e49fe8a94cd71fcfd3699",
"6baff5e484754cdaa400a6adeab6958c",
"2d7826d641614256a9f135515160958c",
"6050e4a9af9e4c46aa4b38d407b7bab9",
"0e61cbeda1f4421398f9456ad9e492e0",
"974da4c9934842ff8286f10a78790410",
"8beb625411f6459c97243979b6cb3664",
"b23315ac058141fd9bf35c49276544d0",
"e7775e44c82b472181e4f8ee6e7d918e",
"074c0860e74d41ff9562fb72d395b28d"
]
},
"executionInfo": {
"elapsed": 141544,
"status": "ok",
"timestamp": 1751740147902,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "aGDo4_KJWxlR",
"outputId": "0e69eea7-8493-4f2a-cd16-2154f4aee16f"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Chargement du modèle depuis le run 7d52e808227f468da7f733265280fc02...\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "91504476b2b34a9eb462a30da59a2b19",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "9ecc775347df487bb63a5e1b2dcc68b7",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Downloading artifacts: 0%| | 0/5 [00:00<?, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: minio-api.greg-madman-nas.duckdns.org. Connection pool size: 10\n",
"WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: minio-api.greg-madman-nas.duckdns.org. Connection pool size: 10\n",
"WARNING:urllib3.connectionpool:Connection pool is full, discarding connection: minio-api.greg-madman-nas.duckdns.org. Connection pool size: 10\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Modèle chargé avec succès : RandomForestClassifier\n",
"Type de vectoriseur utilisé dans ce run : bow\n",
"Artefacts disponibles :\n",
" - confusion_matrix.png\n",
" - cv_results.csv\n",
" - cv_results_max_depth.png\n",
" - cv_results_min_samples_split.png\n",
" - cv_results_n_estimators.png\n",
" - roc_curve.png\n",
" - vectorizer_BoW.pkl\n",
"Vectoriseur de type BoW/CountVectorizer trouvé : vectorizer_BoW.pkl\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "91b8f1667ff54011bd98fd748a0a91c6",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"Downloading artifacts: 0%| | 0/1 [00:00<?, ?it/s]"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Vectoriseur téléchargé dans : /tmp/tmpgfjoxqet/vectorizer_BoW.pkl\n",
"Vectoriseur chargé avec succès : CountVectorizer\n",
"Modèle et vectoriseur sauvegardés avec succès dans content/simple-model/best\n",
"Chemins des fichiers :\n",
" - Modèle : /content/drive/MyDrive/Colab_Notebooks/Project_7/notebooks/content/simple-model/best/RandomForestClassifier_model.pkl\n",
" - Vectoriseur : /content/drive/MyDrive/Colab_Notebooks/Project_7/notebooks/content/simple-model/best/CountVectorizer_vectorizer.pkl\n"
]
}
],
"source": [
"# Charger et sauvegarder le meilleur modèle et son vectoriseur\n",
"best_model, vectorizer = load_and_save_best_model(run_id=best_model_row['Run ID'])"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Psby_wbGWxlR"
},
"source": [
"## Fonction de prédiction pour l'API\n",
"\n",
"Cette fonction constitue **le cœur de notre solution d'analyse de sentiment**. Elle prend **un tweet en entrée**, le prétraite, le vectorise et utilise notre modèle pour **prédire son sentiment**. Elle retourne un dictionnaire contenant le résultat (positif/négatif), un score de confiance et des métadonnées sur le modèle utilisé, **prêt à être exposé via l'API RESTful**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "7wl2v3XLWxlR"
},
"outputs": [],
"source": [
"def predict_sentiment(text, model=best_model, vect=vectorizer):\n",
" \"\"\"\n",
" Fonction qui prend un texte en entrée et retourne la prédiction du sentiment\n",
" Cette fonction pourra être utilisée dans l'API\n",
" \"\"\"\n",
" # Prétraiter le texte\n",
" processed_text = preprocess_tweet(text)\n",
"\n",
" # Vectoriser\n",
" text_vectorized = vect.transform([processed_text])\n",
"\n",
" # Prédire\n",
" prediction = model.predict(text_vectorized)[0]\n",
"\n",
" # Récupérer la probabilité si le modèle le permet\n",
" if hasattr(model, 'predict_proba'):\n",
" probas = model.predict_proba(text_vectorized)[0]\n",
" confidence = probas[1] if prediction == 1 else probas[0]\n",
" else:\n",
" # Pour les modèles sans predict_proba, comme SVM\n",
" decision = model.decision_function(text_vectorized)[0] if hasattr(model, 'decision_function') else 0\n",
" confidence = abs(decision) / 2 # Normaliser d'une certaine façon\n",
"\n",
" return {\n",
" 'sentiment': 'positif' if prediction == 1 else 'négatif',\n",
" 'score': float(confidence),\n",
" 'model': best_model_name,\n",
" 'vectorizer': best_vectorizer\n",
" }"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "HUpwhmfIWxlR"
},
"source": [
"## Test de la fonction de prédiction sur quelques exemples\n",
"\n",
"Nous avons constitué un **ensemble diversifié de tweets tests** incluant différentes caractéristiques linguistiques des réseaux sociaux pour évaluer notre modèle de détection de sentiment. Les résultats sont présentés dans un **DataFrame** avec des indicateurs visuels (💚/❌) permettant d'identifier rapidement les prédictions et leur **niveau de confiance**, validant ainsi l'efficacité de notre solution pour **anticiper les bad buzz**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "b86qIdD3WxlR"
},
"outputs": [],
"source": [
"# Liste améliorée de tweets pour tester le modèle\n",
"test_tweets_improved = [\n",
" # Tweets positifs avec différentes caractéristiques\n",
" \"I absolutely love flying with @AirParadis! Their service is amazing and their staff is so helpful :) #bestairline #travel\",\n",
" \"Just landed after a wonderful flight... The crew was totally awesome and the food was perfect!!! Can't wait to fly with them again :D\",\n",
" \"OMG! You really have to try @AirParadis. Best. Flight. Ever. Their new seats are extremely comfortable. http://airparadis.com/newseats #travel #comfort\",\n",
" \"We had a very pleasant experience with our flight today. The staff completely exceeded our expectations ;) #happy #AirParadis\",\n",
" \"My first time flying business class and I'm absolutely amazed!!! The service is 100% worth it... @AirParadis never disappoints :)\",\n",
"\n",
" # Tweets négatifs avec différentes caractéristiques\n",
" \"I hate how @AirParadis always delays their flights... This is the third time this month!!! :( #disappointed #travel\",\n",
" \"The worst flight experience of my life? Definitely today with @AirParadis. Their customer service won't even respond to my complaints... #terrible\",\n",
" \"Really @AirParadis??? Flight cancelled and no compensation??? That's how you treat your customers? SMH :(\",\n",
" \"Our baggage was lost and nobody at @AirParadis seems to care! Don't they understand that we can't enjoy our vacation without our stuff??? http://badservice.com/complaint :-(\",\n",
" \"Food was terrible, seats were uncomfortable, and the staff was not helpful at all... Never flying with @AirParadis again! #worstairline\",\n",
"\n",
" # Tweets mixtes et nuancés\n",
" \"The flight was good but the food wasn't really what I expected... @AirParadis can do better! #mixedfeelings\",\n",
" \"IDK what to think about my @AirParadis experience? The crew was nice but the flight was delayed for 2 hours... #confused\",\n",
" \"My husband loved the flight but I didn't... Too bad they can't make everyone happy :-) #AirParadis\",\n",
" \"TBH, @AirParadis has improved their service since last year! Not perfect yet, but they're trying... #progress\",\n",
" \"We were so excited for our trip but then our flight got delayed... at least the staff was very apologetic and gave us free drinks! @AirParadis\",\n",
"\n",
" # Tweets spécifiquement pour tester les caractéristiques spéciales\n",
" \"This is a tweet with many!!! exclamation!!! marks!!! to test how they affect sentiment analysis :)\",\n",
" \"How does the model handle multiple??? question??? marks??? in a single tweet? @AirParadis\",\n",
" \"Testing ellipses... many... many... ellipses... in... one... tweet... @AirParadis #test\",\n",
" \"#many #hashtags #in #one #tweet #to #see #how #they #affect #the #analysis #AirParadis\",\n",
" \"@mention1 @mention2 @mention3 @mention4 @mention5 multiple mentions in one tweet\",\n",
" \"Multiple URLs in one tweet: http://example1.com http://example2.com http://example3.com @AirParadis\",\n",
" \"Let's try all emoticons :) :( :D ;) :-) :-( :-D ;-) in a single tweet to see the impact\"\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 6284,
"status": "ok",
"timestamp": 1751740154190,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "HYemSzp2WxlR",
"outputId": "a3f850c7-8ae3-4daf-9296-d94ef357c74f"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Test du modèle sur les exemples:\n",
"==================================================\n",
"Résumé: 22 tweets analysés\n",
"Tweets positifs: 13 (59.1%)\n",
"Tweets négatifs: 9 (40.9%)\n"
]
}
],
"source": [
"def test_model_on_examples(model, vectorizer, test_tweets):\n",
" \"\"\"\n",
" Teste le modèle sur une liste de tweets et retourne un DataFrame avec les résultats des prédictions.\n",
" \"\"\"\n",
"\n",
" results = {\n",
" \"positif\": 0,\n",
" \"négatif\": 0\n",
" }\n",
"\n",
" # Créer un DataFrame pour stocker les résultats\n",
" predictions_data = []\n",
"\n",
" for i, tweet in enumerate(test_tweets):\n",
" result = predict_sentiment(tweet, model, vectorizer)\n",
"\n",
" # Ajouter l'emoji approprié pour le sentiment\n",
" emoji = \"💚\" if result['sentiment'] == \"positif\" else \"❌\"\n",
"\n",
" # Ajouter les données au DataFrame\n",
" predictions_data.append({\n",
" \"Tweet\": tweet,\n",
" \"Sentiment\": result['sentiment'],\n",
" \"Emoji\": emoji,\n",
" \"Score de confiance\": result['score'],\n",
" \"Index\": i+1\n",
" })\n",
"\n",
" # Comptage des résultats\n",
" results[result['sentiment']] += 1\n",
"\n",
" # Créer le DataFrame\n",
" df_predictions = pd.DataFrame(predictions_data)\n",
"\n",
" # Afficher un résumé\n",
" print(\"\\nTest du modèle sur les exemples:\")\n",
" print(\"=\"*50)\n",
" print(f\"Résumé: {len(test_tweets)} tweets analysés\")\n",
" print(f\"Tweets positifs: {results['positif']} ({(results['positif']/len(test_tweets)*100):.1f}%)\")\n",
" print(f\"Tweets négatifs: {results['négatif']} ({(results['négatif']/len(test_tweets)*100):.1f}%)\")\n",
"\n",
" # Retourner le DataFrame\n",
" return df_predictions\n",
"\n",
"# Exemple d'utilisation:\n",
"if best_model is not None and vectorizer is not None:\n",
" # Obtenir le DataFrame des prédictions\n",
" df_results = test_model_on_examples(best_model, vectorizer, test_tweets_improved)\n",
" # Optionnel: sauvegarder les résultats dans un fichier CSV\n",
" df_results.to_csv(\"content/simple-model/best/predictions_results.csv\", index=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 739
},
"executionInfo": {
"elapsed": 41,
"status": "ok",
"timestamp": 1751740154234,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "P40YQzXDWxlR",
"outputId": "911ca8cf-e1dd-4082-f249-f09869a0f7d0"
},
"outputs": [
{
"data": {
"application/vnd.google.colaboratory.intrinsic+json": {
"summary": "{\n \"name\": \"df_results\",\n \"rows\": 22,\n \"fields\": [\n {\n \"column\": \"Tweet\",\n \"properties\": {\n \"dtype\": \"string\",\n \"num_unique_values\": 22,\n \"samples\": [\n \"I absolutely love flying with @AirParadis! Their service is amazing and their staff is so helpful :) #bestairline #travel\",\n \"TBH, @AirParadis has improved their service since last year! Not perfect yet, but they're trying... #progress\",\n \"Our baggage was lost and nobody at @AirParadis seems to care! Don't they understand that we can't enjoy our vacation without our stuff??? http://badservice.com/complaint :-(\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Sentiment\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"n\\u00e9gatif\",\n \"positif\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Emoji\",\n \"properties\": {\n \"dtype\": \"category\",\n \"num_unique_values\": 2,\n \"samples\": [\n \"\\u274c\",\n \"\\ud83d\\udc9a\"\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Score de confiance\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 0.13093384858249077,\n \"min\": 0.5325674603174603,\n \"max\": 0.9620695325915914,\n \"num_unique_values\": 22,\n \"samples\": [\n 0.9620695325915914,\n 0.5618869047619047\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n },\n {\n \"column\": \"Index\",\n \"properties\": {\n \"dtype\": \"number\",\n \"std\": 6,\n \"min\": 1,\n \"max\": 22,\n \"num_unique_values\": 22,\n \"samples\": [\n 1,\n 14\n ],\n \"semantic_type\": \"\",\n \"description\": \"\"\n }\n }\n ]\n}",
"type": "dataframe",
"variable_name": "df_results"
},
"text/html": [
"\n",
" <div id=\"df-2800f6ef-518b-4319-bd85-13e48c6d3b36\" class=\"colab-df-container\">\n",
" <div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Tweet</th>\n",
" <th>Sentiment</th>\n",
" <th>Emoji</th>\n",
" <th>Score de confiance</th>\n",
" <th>Index</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>I absolutely love flying with @AirParadis! The...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.962070</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>Just landed after a wonderful flight... The cr...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.874403</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>OMG! You really have to try @AirParadis. Best....</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.767187</td>\n",
" <td>3</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>We had a very pleasant experience with our fli...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.877798</td>\n",
" <td>4</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>My first time flying business class and I'm ab...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.608684</td>\n",
" <td>5</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>I hate how @AirParadis always delays their fli...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.868094</td>\n",
" <td>6</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>The worst flight experience of my life? Defini...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.781381</td>\n",
" <td>7</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>Really @AirParadis??? Flight cancelled and no ...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.705216</td>\n",
" <td>8</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>Our baggage was lost and nobody at @AirParadis...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.673389</td>\n",
" <td>9</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>Food was terrible, seats were uncomfortable, a...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.815938</td>\n",
" <td>10</td>\n",
" </tr>\n",
" <tr>\n",
" <th>10</th>\n",
" <td>The flight was good but the food wasn't really...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.590071</td>\n",
" <td>11</td>\n",
" </tr>\n",
" <tr>\n",
" <th>11</th>\n",
" <td>IDK what to think about my @AirParadis experie...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.677984</td>\n",
" <td>12</td>\n",
" </tr>\n",
" <tr>\n",
" <th>12</th>\n",
" <td>My husband loved the flight but I didn't... To...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.751300</td>\n",
" <td>13</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13</th>\n",
" <td>TBH, @AirParadis has improved their service si...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.561887</td>\n",
" <td>14</td>\n",
" </tr>\n",
" <tr>\n",
" <th>14</th>\n",
" <td>We were so excited for our trip but then our f...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.532567</td>\n",
" <td>15</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15</th>\n",
" <td>This is a tweet with many!!! exclamation!!! ma...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.715387</td>\n",
" <td>16</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>How does the model handle multiple??? question...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.555274</td>\n",
" <td>17</td>\n",
" </tr>\n",
" <tr>\n",
" <th>17</th>\n",
" <td>Testing ellipses... many... many... ellipses.....</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.802591</td>\n",
" <td>18</td>\n",
" </tr>\n",
" <tr>\n",
" <th>18</th>\n",
" <td>#many #hashtags #in #one #tweet #to #see #how ...</td>\n",
" <td>négatif</td>\n",
" <td>❌</td>\n",
" <td>0.562698</td>\n",
" <td>19</td>\n",
" </tr>\n",
" <tr>\n",
" <th>19</th>\n",
" <td>@mention1 @mention2 @mention3 @mention4 @menti...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.834694</td>\n",
" <td>20</td>\n",
" </tr>\n",
" <tr>\n",
" <th>20</th>\n",
" <td>Multiple URLs in one tweet: http://example1.co...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.937625</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>21</th>\n",
" <td>Let's try all emoticons :) :( :D ;) :-) :-( :-...</td>\n",
" <td>positif</td>\n",
" <td>💚</td>\n",
" <td>0.649427</td>\n",
" <td>22</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>\n",
" <div class=\"colab-df-buttons\">\n",
"\n",
" <div class=\"colab-df-container\">\n",
" <button class=\"colab-df-convert\" onclick=\"convertToInteractive('df-2800f6ef-518b-4319-bd85-13e48c6d3b36')\"\n",
" title=\"Convert this dataframe to an interactive table.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 -960 960 960\">\n",
" <path d=\"M120-120v-720h720v720H120Zm60-500h600v-160H180v160Zm220 220h160v-160H400v160Zm0 220h160v-160H400v160ZM180-400h160v-160H180v160Zm440 0h160v-160H620v160ZM180-180h160v-160H180v160Zm440 0h160v-160H620v160Z\"/>\n",
" </svg>\n",
" </button>\n",
"\n",
" <style>\n",
" .colab-df-container {\n",
" display:flex;\n",
" gap: 12px;\n",
" }\n",
"\n",
" .colab-df-convert {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-convert:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" .colab-df-buttons div {\n",
" margin-bottom: 4px;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-convert:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
"\n",
" <script>\n",
" const buttonEl =\n",
" document.querySelector('#df-2800f6ef-518b-4319-bd85-13e48c6d3b36 button.colab-df-convert');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" async function convertToInteractive(key) {\n",
" const element = document.querySelector('#df-2800f6ef-518b-4319-bd85-13e48c6d3b36');\n",
" const dataTable =\n",
" await google.colab.kernel.invokeFunction('convertToInteractive',\n",
" [key], {});\n",
" if (!dataTable) return;\n",
"\n",
" const docLinkHtml = 'Like what you see? Visit the ' +\n",
" '<a target=\"_blank\" href=https://colab.research.google.com/notebooks/data_table.ipynb>data table notebook</a>'\n",
" + ' to learn more about interactive tables.';\n",
" element.innerHTML = '';\n",
" dataTable['output_type'] = 'display_data';\n",
" await google.colab.output.renderOutput(dataTable, element);\n",
" const docLink = document.createElement('div');\n",
" docLink.innerHTML = docLinkHtml;\n",
" element.appendChild(docLink);\n",
" }\n",
" </script>\n",
" </div>\n",
"\n",
"\n",
" <div id=\"df-181ed6a7-4635-458e-acb1-210c50c72f46\">\n",
" <button class=\"colab-df-quickchart\" onclick=\"quickchart('df-181ed6a7-4635-458e-acb1-210c50c72f46')\"\n",
" title=\"Suggest charts\"\n",
" style=\"display:none;\">\n",
"\n",
"<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <g>\n",
" <path d=\"M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z\"/>\n",
" </g>\n",
"</svg>\n",
" </button>\n",
"\n",
"<style>\n",
" .colab-df-quickchart {\n",
" --bg-color: #E8F0FE;\n",
" --fill-color: #1967D2;\n",
" --hover-bg-color: #E2EBFA;\n",
" --hover-fill-color: #174EA6;\n",
" --disabled-fill-color: #AAA;\n",
" --disabled-bg-color: #DDD;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-quickchart {\n",
" --bg-color: #3B4455;\n",
" --fill-color: #D2E3FC;\n",
" --hover-bg-color: #434B5C;\n",
" --hover-fill-color: #FFFFFF;\n",
" --disabled-bg-color: #3B4455;\n",
" --disabled-fill-color: #666;\n",
" }\n",
"\n",
" .colab-df-quickchart {\n",
" background-color: var(--bg-color);\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: var(--fill-color);\n",
" height: 32px;\n",
" padding: 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-quickchart:hover {\n",
" background-color: var(--hover-bg-color);\n",
" box-shadow: 0 1px 2px rgba(60, 64, 67, 0.3), 0 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: var(--button-hover-fill-color);\n",
" }\n",
"\n",
" .colab-df-quickchart-complete:disabled,\n",
" .colab-df-quickchart-complete:disabled:hover {\n",
" background-color: var(--disabled-bg-color);\n",
" fill: var(--disabled-fill-color);\n",
" box-shadow: none;\n",
" }\n",
"\n",
" .colab-df-spinner {\n",
" border: 2px solid var(--fill-color);\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" animation:\n",
" spin 1s steps(1) infinite;\n",
" }\n",
"\n",
" @keyframes spin {\n",
" 0% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" border-left-color: var(--fill-color);\n",
" }\n",
" 20% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 30% {\n",
" border-color: transparent;\n",
" border-left-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 40% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-top-color: var(--fill-color);\n",
" }\n",
" 60% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" }\n",
" 80% {\n",
" border-color: transparent;\n",
" border-right-color: var(--fill-color);\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" 90% {\n",
" border-color: transparent;\n",
" border-bottom-color: var(--fill-color);\n",
" }\n",
" }\n",
"</style>\n",
"\n",
" <script>\n",
" async function quickchart(key) {\n",
" const quickchartButtonEl =\n",
" document.querySelector('#' + key + ' button');\n",
" quickchartButtonEl.disabled = true; // To prevent multiple clicks.\n",
" quickchartButtonEl.classList.add('colab-df-spinner');\n",
" try {\n",
" const charts = await google.colab.kernel.invokeFunction(\n",
" 'suggestCharts', [key], {});\n",
" } catch (error) {\n",
" console.error('Error during call to suggestCharts:', error);\n",
" }\n",
" quickchartButtonEl.classList.remove('colab-df-spinner');\n",
" quickchartButtonEl.classList.add('colab-df-quickchart-complete');\n",
" }\n",
" (() => {\n",
" let quickchartButtonEl =\n",
" document.querySelector('#df-181ed6a7-4635-458e-acb1-210c50c72f46 button');\n",
" quickchartButtonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
" })();\n",
" </script>\n",
" </div>\n",
"\n",
" <div id=\"id_ef9aa32c-1bd5-4918-94e5-04bd07129897\">\n",
" <style>\n",
" .colab-df-generate {\n",
" background-color: #E8F0FE;\n",
" border: none;\n",
" border-radius: 50%;\n",
" cursor: pointer;\n",
" display: none;\n",
" fill: #1967D2;\n",
" height: 32px;\n",
" padding: 0 0 0 0;\n",
" width: 32px;\n",
" }\n",
"\n",
" .colab-df-generate:hover {\n",
" background-color: #E2EBFA;\n",
" box-shadow: 0px 1px 2px rgba(60, 64, 67, 0.3), 0px 1px 3px 1px rgba(60, 64, 67, 0.15);\n",
" fill: #174EA6;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-generate {\n",
" background-color: #3B4455;\n",
" fill: #D2E3FC;\n",
" }\n",
"\n",
" [theme=dark] .colab-df-generate:hover {\n",
" background-color: #434B5C;\n",
" box-shadow: 0px 1px 3px 1px rgba(0, 0, 0, 0.15);\n",
" filter: drop-shadow(0px 1px 2px rgba(0, 0, 0, 0.3));\n",
" fill: #FFFFFF;\n",
" }\n",
" </style>\n",
" <button class=\"colab-df-generate\" onclick=\"generateWithVariable('df_results')\"\n",
" title=\"Generate code using this dataframe.\"\n",
" style=\"display:none;\">\n",
"\n",
" <svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\"viewBox=\"0 0 24 24\"\n",
" width=\"24px\">\n",
" <path d=\"M7,19H8.4L18.45,9,17,7.55,7,17.6ZM5,21V16.75L18.45,3.32a2,2,0,0,1,2.83,0l1.4,1.43a1.91,1.91,0,0,1,.58,1.4,1.91,1.91,0,0,1-.58,1.4L9.25,21ZM18.45,9,17,7.55Zm-12,3A5.31,5.31,0,0,0,4.9,8.1,5.31,5.31,0,0,0,1,6.5,5.31,5.31,0,0,0,4.9,4.9,5.31,5.31,0,0,0,6.5,1,5.31,5.31,0,0,0,8.1,4.9,5.31,5.31,0,0,0,12,6.5,5.46,5.46,0,0,0,6.5,12Z\"/>\n",
" </svg>\n",
" </button>\n",
" <script>\n",
" (() => {\n",
" const buttonEl =\n",
" document.querySelector('#id_ef9aa32c-1bd5-4918-94e5-04bd07129897 button.colab-df-generate');\n",
" buttonEl.style.display =\n",
" google.colab.kernel.accessAllowed ? 'block' : 'none';\n",
"\n",
" buttonEl.onclick = () => {\n",
" google.colab.notebook.generateWithVariable('df_results');\n",
" }\n",
" })();\n",
" </script>\n",
" </div>\n",
"\n",
" </div>\n",
" </div>\n"
],
"text/plain": [
" Tweet Sentiment Emoji \\\n",
"0 I absolutely love flying with @AirParadis! The... positif 💚 \n",
"1 Just landed after a wonderful flight... The cr... positif 💚 \n",
"2 OMG! You really have to try @AirParadis. Best.... positif 💚 \n",
"3 We had a very pleasant experience with our fli... positif 💚 \n",
"4 My first time flying business class and I'm ab... positif 💚 \n",
"5 I hate how @AirParadis always delays their fli... négatif ❌ \n",
"6 The worst flight experience of my life? Defini... négatif ❌ \n",
"7 Really @AirParadis??? Flight cancelled and no ... négatif ❌ \n",
"8 Our baggage was lost and nobody at @AirParadis... négatif ❌ \n",
"9 Food was terrible, seats were uncomfortable, a... négatif ❌ \n",
"10 The flight was good but the food wasn't really... positif 💚 \n",
"11 IDK what to think about my @AirParadis experie... positif 💚 \n",
"12 My husband loved the flight but I didn't... To... négatif ❌ \n",
"13 TBH, @AirParadis has improved their service si... négatif ❌ \n",
"14 We were so excited for our trip but then our f... positif 💚 \n",
"15 This is a tweet with many!!! exclamation!!! ma... négatif ❌ \n",
"16 How does the model handle multiple??? question... positif 💚 \n",
"17 Testing ellipses... many... many... ellipses..... positif 💚 \n",
"18 #many #hashtags #in #one #tweet #to #see #how ... négatif ❌ \n",
"19 @mention1 @mention2 @mention3 @mention4 @menti... positif 💚 \n",
"20 Multiple URLs in one tweet: http://example1.co... positif 💚 \n",
"21 Let's try all emoticons :) :( :D ;) :-) :-( :-... positif 💚 \n",
"\n",
" Score de confiance Index \n",
"0 0.962070 1 \n",
"1 0.874403 2 \n",
"2 0.767187 3 \n",
"3 0.877798 4 \n",
"4 0.608684 5 \n",
"5 0.868094 6 \n",
"6 0.781381 7 \n",
"7 0.705216 8 \n",
"8 0.673389 9 \n",
"9 0.815938 10 \n",
"10 0.590071 11 \n",
"11 0.677984 12 \n",
"12 0.751300 13 \n",
"13 0.561887 14 \n",
"14 0.532567 15 \n",
"15 0.715387 16 \n",
"16 0.555274 17 \n",
"17 0.802591 18 \n",
"18 0.562698 19 \n",
"19 0.834694 20 \n",
"20 0.937625 21 \n",
"21 0.649427 22 "
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 6,
"status": "ok",
"timestamp": 1751740154241,
"user": {
"displayName": "Grégoire",
"userId": "01249298206117462766"
},
"user_tz": -120
},
"id": "5g5IagpMWxlR",
"outputId": "379e77cb-1b6e-412e-a483-4510c1f22b90"
},
"outputs": [
{
"data": {
"text/plain": [
"Index(['Tweet', 'Sentiment', 'Emoji', 'Score de confiance', 'Index'], dtype='object')"
]
},
"execution_count": 40,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df_results.columns"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "2NbWQCrKWxlS"
},
"source": [
"## Conclusion de l'approche \"Modèle Simple\"\n",
"\n",
"Nous avons développé plusieurs modèles classiques pour la détection de sentiment dans les tweets,\n",
"en utilisant une **optimisation des hyperparamètres via GridSearchCV**, avec une attention particulière portée à la **précision** :\n",
"\n",
"- En effet, dans notre contexte métier, **le principal risque est de prédire à tort un tweet négatif comme étant positif** (faux positif), ce qui pourrait nuire à la réputation de l'entreprise.\n",
"- Pour cette raison, **la précision (precision) a été retenue comme métrique de sélection principale**, car elle mesure la proportion de tweets réellement positifs parmi ceux prédits comme positifs.\n",
"\n",
"Les modèles testés sont :\n",
"\n",
"1. Régression Logistique \n",
"2. SVM Linéaire \n",
"3. Random Forest \n",
"4. Naive Bayes \n",
"\n",
"Chaque modèle a été évalué avec deux techniques de vectorisation :\n",
"- Sac de mots (BoW)\n",
"- TF-IDF\n",
"\n",
"---\n",
"\n",
"### 📊 Meilleur modèle (selon la précision)\n",
"\n",
"Le modèle ayant obtenu la meilleure précision est : \n",
"**[en attente d'une valeur]** avec la vectorisation **[en attente d'une valeur]**\n",
"\n",
"- **Précision : [en attente d'une valeur]**\n",
"- **F1 Score : [en attente d'une valeur]**\n",
"- **ROC AUC : [en attente d'une valeur]**\n",
"\n",
"Hyperparamètres optimaux : **[en attente d'une valeur]**\n",
"\n",
"---\n",
"\n",
"### ✅ Avantages du workflow implémenté\n",
"\n",
"1. **Alignement sur la métrique métier (précision)** \n",
" - Réduction du risque de faux positifs, cruciaux dans notre cas d’usage \n",
" - Approche plus conservatrice mais adaptée à la gestion d'image de marque\n",
"\n",
"2. **Suivi des expérimentations avec MLflow** \n",
" - Historique des runs, métriques, artefacts et vectoriseurs sauvegardés \n",
" - Visualisation claire des performances\n",
"\n",
"3. **Optimisation rigoureuse** \n",
" - Recherche sur grille (GridSearchCV) \n",
" - Validation croisée pour robustesse des résultats \n",
" - Comparaison systématique des performances\n",
"\n",
"4. **Prétraitement linguistique adapté aux tweets** \n",
" - Nettoyage spécifique des entités (hashtags, mentions, URLs) \n",
" - Lemmatisation avec conservation des négations \n",
" - Ajout de variables contextuelles issues de l’analyse exploratoire\n",
"\n",
"\n",
"Le meilleur modèle classique retenu ici constituera une **baseline** solide pour mesurer les gains apportés par les approches de Deep Learning et de NLP avancé.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "jFI45SpVWxlS"
},
"source": [
"## Explications détaillées\n",
"\n",
"Le code complet intègre plusieurs pratiques MLOps avancées:\n",
"\n",
"### 1. Optimisation des hyperparamètres avec GridSearchCV\n",
"\n",
"J'ai implémenté GridSearchCV pour chaque combinaison de modèle et vectoriseur. Cela permet d'explorer systématiquement l'espace des hyperparamètres pour trouver la configuration optimale. Pour chaque modèle, j'ai défini une grille de paramètres pertinents:\n",
"\n",
"- **Régression Logistique**: différentes valeurs de régularisation (C), types de solveurs\n",
"- **SVM**: différentes valeurs de C, options de dualité\n",
"- **Random Forest**: nombre d'arbres, profondeur maximale, critères de division\n",
"- **Naive Bayes**: différentes valeurs pour le paramètre de lissage alpha\n",
"\n",
"L'optimisation est effectuée avec validation croisée à 5 plis (5-fold) pour garantir la robustesse des résultats.\n",
"\n",
"### 2. Tracking complet avec MLflow\n",
"\n",
"Pour chaque expérience, j'enregistre dans MLflow:\n",
"\n",
"- **Paramètres**: tous les hyperparamètres utilisés, y compris les meilleurs trouvés par GridSearchCV\n",
"- **Métriques**: accuracy, precision, recall, F1 score, ROC AUC, temps d'entraînement\n",
"- **Artifacts**:\n",
" - Le modèle optimisé avec sa signature d'entrée/sortie\n",
" - Le vectoriseur utilisé (nouveauté demandée)\n",
" - La matrice de confusion et la courbe ROC\n",
" - Les résultats détaillés de la validation croisée\n",
" - Les features les plus importantes (pour les modèles qui le permettent)\n",
" - Des graphiques d'analyse de l'influence des hyperparamètres\n",
"\n",
"### 3. Sauvegarde et utilisation du meilleur modèle\n",
"\n",
"J'ai implémenté une méthode pour:\n",
"\n",
"1. Identifier le meilleur modèle dans l'historique MLflow\n",
"2. Le récupérer avec son vectoriseur\n",
"3. Sauvegarder les deux dans un dossier local spécifié (\"content/simple-model/best\")\n",
"4. Créer une fonction de prédiction prête pour l'API\n",
"\n",
"### 4. Visualisations avancées\n",
"\n",
"Pour faciliter l'analyse:\n",
"\n",
"- Matrices de confusion pour chaque modèle\n",
"- Courbes ROC avec AUC calculé\n",
"- Graphiques des features les plus importantes\n",
"- Graphiques d'évolution des scores en fonction des hyperparamètres"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "hJSt92gut0Vu"
},
"source": [
"# Enregistrement de dépendances"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "LeieF6LStjLc"
},
"outputs": [],
"source": [
"!pip freeze > requirements_modele_simple.txt"
]
}
],
"metadata": {
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
},
"widgets": {
"application/vnd.jupyter.widget-state+json": {
"073bf36ff26d455ca01956dee8d1f1a2": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HTMLModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HTMLModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HTMLView",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_8ddc3f81d9094d06b680908375077c62",
"placeholder": "​",
"style": "IPY_MODEL_0e157319861c42049d63bd43b797987e",
"value": "Downloading artifacts: 100%"
}
},
"074c0860e74d41ff9562fb72d395b28d": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "DescriptionStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "DescriptionStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"description_width": ""
}
},
"0e157319861c42049d63bd43b797987e": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "DescriptionStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "DescriptionStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"description_width": ""
}
},
"0e61cbeda1f4421398f9456ad9e492e0": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"2d7826d641614256a9f135515160958c": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HTMLModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HTMLModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HTMLView",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_e7775e44c82b472181e4f8ee6e7d918e",
"placeholder": "​",
"style": "IPY_MODEL_074c0860e74d41ff9562fb72d395b28d",
"value": " 1/1 [00:00&lt;00:00,  1.13it/s]"
}
},
"3a1f9f8dff4e49fe8a94cd71fcfd3699": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HTMLModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HTMLModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HTMLView",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_0e61cbeda1f4421398f9456ad9e492e0",
"placeholder": "​",
"style": "IPY_MODEL_974da4c9934842ff8286f10a78790410",
"value": "Downloading artifacts: 100%"
}
},
"6050e4a9af9e4c46aa4b38d407b7bab9": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"6baff5e484754cdaa400a6adeab6958c": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "FloatProgressModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "FloatProgressModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "ProgressView",
"bar_style": "success",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_8beb625411f6459c97243979b6cb3664",
"max": 1,
"min": 0,
"orientation": "horizontal",
"style": "IPY_MODEL_b23315ac058141fd9bf35c49276544d0",
"value": 1
}
},
"6c04e4ad7c5f49c3bd9658ccab659890": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"6d712f894c5d4d19a478fa1d1a2b2c62": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HTMLModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HTMLModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HTMLView",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_6c04e4ad7c5f49c3bd9658ccab659890",
"placeholder": "​",
"style": "IPY_MODEL_acbe4e7d94f04dfe9e797ab54d3102ad",
"value": " 5/5 [01:56&lt;00:00, 32.51s/it]"
}
},
"6efad0330efc4790a1eac95fcc96de17": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "ProgressStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "ProgressStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"bar_color": null,
"description_width": ""
}
},
"6f6c8fc42ef846a4ac56ffe69bcbbe0f": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "DescriptionStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "DescriptionStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"description_width": ""
}
},
"727814859996469da84c76c1a3f15bef": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"88ae9e2307804694bc546afb10a362f7": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HTMLModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HTMLModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HTMLView",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_727814859996469da84c76c1a3f15bef",
"placeholder": "​",
"style": "IPY_MODEL_6f6c8fc42ef846a4ac56ffe69bcbbe0f",
"value": "Downloading artifacts:   0%"
}
},
"8b25983eb05344928535625c726939d8": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "DescriptionStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "DescriptionStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"description_width": ""
}
},
"8beb625411f6459c97243979b6cb3664": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"8ddc3f81d9094d06b680908375077c62": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"8e7128400e4c4eff96b67db314ccaff1": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"91504476b2b34a9eb462a30da59a2b19": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HBoxModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HBoxModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HBoxView",
"box_style": "",
"children": [
"IPY_MODEL_88ae9e2307804694bc546afb10a362f7",
"IPY_MODEL_9897c186c00947c280a7eae2fc9a315c",
"IPY_MODEL_9572601035c34a5eb88f81f6514f872d"
],
"layout": "IPY_MODEL_8e7128400e4c4eff96b67db314ccaff1"
}
},
"91b8f1667ff54011bd98fd748a0a91c6": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HBoxModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HBoxModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HBoxView",
"box_style": "",
"children": [
"IPY_MODEL_3a1f9f8dff4e49fe8a94cd71fcfd3699",
"IPY_MODEL_6baff5e484754cdaa400a6adeab6958c",
"IPY_MODEL_2d7826d641614256a9f135515160958c"
],
"layout": "IPY_MODEL_6050e4a9af9e4c46aa4b38d407b7bab9"
}
},
"9572601035c34a5eb88f81f6514f872d": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HTMLModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HTMLModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HTMLView",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_b08959c4edfa465588e03c99d00ad586",
"placeholder": "​",
"style": "IPY_MODEL_8b25983eb05344928535625c726939d8",
"value": " 0/1 [00:00&lt;?, ?it/s]"
}
},
"974da4c9934842ff8286f10a78790410": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "DescriptionStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "DescriptionStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"description_width": ""
}
},
"9897c186c00947c280a7eae2fc9a315c": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "FloatProgressModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "FloatProgressModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "ProgressView",
"bar_style": "danger",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_d5f9951d6c094fcfb03e166af93cfea5",
"max": 1,
"min": 0,
"orientation": "horizontal",
"style": "IPY_MODEL_6efad0330efc4790a1eac95fcc96de17",
"value": 0
}
},
"9ecc775347df487bb63a5e1b2dcc68b7": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "HBoxModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "HBoxModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "HBoxView",
"box_style": "",
"children": [
"IPY_MODEL_073bf36ff26d455ca01956dee8d1f1a2",
"IPY_MODEL_ce14528df1ed4f1abb72ccb9a3c66443",
"IPY_MODEL_6d712f894c5d4d19a478fa1d1a2b2c62"
],
"layout": "IPY_MODEL_b0c4432101b948979653cd43bcd3fc4b"
}
},
"acbe4e7d94f04dfe9e797ab54d3102ad": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "DescriptionStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "DescriptionStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"description_width": ""
}
},
"b08959c4edfa465588e03c99d00ad586": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"b0c4432101b948979653cd43bcd3fc4b": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"b23315ac058141fd9bf35c49276544d0": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "ProgressStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "ProgressStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"bar_color": null,
"description_width": ""
}
},
"b7d70f2ce66f40ea8f606256cb654540": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"ce14528df1ed4f1abb72ccb9a3c66443": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "FloatProgressModel",
"state": {
"_dom_classes": [],
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "FloatProgressModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/controls",
"_view_module_version": "1.5.0",
"_view_name": "ProgressView",
"bar_style": "success",
"description": "",
"description_tooltip": null,
"layout": "IPY_MODEL_b7d70f2ce66f40ea8f606256cb654540",
"max": 5,
"min": 0,
"orientation": "horizontal",
"style": "IPY_MODEL_d112b0758b97447ab47470e826e94687",
"value": 5
}
},
"d112b0758b97447ab47470e826e94687": {
"model_module": "@jupyter-widgets/controls",
"model_module_version": "1.5.0",
"model_name": "ProgressStyleModel",
"state": {
"_model_module": "@jupyter-widgets/controls",
"_model_module_version": "1.5.0",
"_model_name": "ProgressStyleModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "StyleView",
"bar_color": null,
"description_width": ""
}
},
"d5f9951d6c094fcfb03e166af93cfea5": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
},
"e7775e44c82b472181e4f8ee6e7d918e": {
"model_module": "@jupyter-widgets/base",
"model_module_version": "1.2.0",
"model_name": "LayoutModel",
"state": {
"_model_module": "@jupyter-widgets/base",
"_model_module_version": "1.2.0",
"_model_name": "LayoutModel",
"_view_count": null,
"_view_module": "@jupyter-widgets/base",
"_view_module_version": "1.2.0",
"_view_name": "LayoutView",
"align_content": null,
"align_items": null,
"align_self": null,
"border": null,
"bottom": null,
"display": null,
"flex": null,
"flex_flow": null,
"grid_area": null,
"grid_auto_columns": null,
"grid_auto_flow": null,
"grid_auto_rows": null,
"grid_column": null,
"grid_gap": null,
"grid_row": null,
"grid_template_areas": null,
"grid_template_columns": null,
"grid_template_rows": null,
"height": null,
"justify_content": null,
"justify_items": null,
"left": null,
"margin": null,
"max_height": null,
"max_width": null,
"min_height": null,
"min_width": null,
"object_fit": null,
"object_position": null,
"order": null,
"overflow": null,
"overflow_x": null,
"overflow_y": null,
"padding": null,
"right": null,
"top": null,
"visibility": null,
"width": null
}
}
}
}
},
"nbformat": 4,
"nbformat_minor": 0
}