Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)! Autres façons de soutenir HackTricks : * Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) ! * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family) * **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez-nous** sur **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.** * **Partagez vos astuces de piratage en soumettant des PR aux** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) dépôts GitHub.
# Types de données possibles de base Les données peuvent être **continues** (valeurs **infinies**) ou **catégorielles** (nominales) où la quantité de valeurs possibles est **limitée**. ## Types catégoriels ### Binaire Juste **2 valeurs possibles** : 1 ou 0. Dans le cas où dans un ensemble de données les valeurs sont au format chaîne de caractères (par exemple "Vrai" et "Faux"), vous attribuez des nombres à ces valeurs avec : ```python dataset["column2"] = dataset.column2.map({"T": 1, "F": 0}) ``` ### **Ordinal** Les **valeurs suivent un ordre**, comme dans : 1ère place, 2ème place... Si les catégories sont des chaînes de caractères (comme : "débutant", "amateur", "professionnel", "expert"), vous pouvez les mapper à des nombres comme nous l'avons vu dans le cas binaire. ```python column2_mapping = {'starter':0,'amateur':1,'professional':2,'expert':3} dataset['column2'] = dataset.column2.map(column2_mapping) ``` * Pour les **colonnes alphabétiques**, vous pouvez les ordonner plus facilement : ```python # First get all the uniq values alphabetically sorted possible_values_sorted = dataset.column2.sort_values().unique().tolist() # Assign each one a value possible_values_mapping = {value:idx for idx,value in enumerate(possible_values_sorted)} dataset['column2'] = dataset.column2.map(possible_values_mapping) ``` ### **Cyclique** Ressemble à une **valeur ordinale** car il y a un ordre, mais cela ne signifie pas qu'une valeur est plus grande que l'autre. De plus, la **distance entre elles dépend de la direction** dans laquelle vous comptez. Exemple : les jours de la semaine, dimanche n'est pas "plus grand" que lundi. * Il existe **différentes façons** d'encoder les caractéristiques cycliques, certaines peuvent fonctionner avec seulement quelques algorithmes. **En général, l'encodage factice peut être utilisé**. ```python column2_dummies = pd.get_dummies(dataset.column2, drop_first=True) dataset_joined = pd.concat([dataset[['column2']], column2_dummies], axis=1) ``` ### **Dates** Les dates sont des **variables continues**. Elles peuvent être considérées comme **cycliques** (car elles se répètent) ou comme des **variables ordinales** (car un moment dans le temps est plus grand qu'un précédent). * Habituellement, les dates sont utilisées comme **index**. ```python # Transform dates to datetime dataset["column_date"] = pd.to_datetime(dataset.column_date) # Make the date feature the index dataset.set_index('column_date', inplace=True) print(dataset.head()) # Sum usage column per day daily_sum = dataset.groupby(df_daily_usage.index.date).agg({'usage':['sum']}) # Flatten and rename usage column daily_sum.columns = daily_sum.columns.get_level_values(0) daily_sum.columns = ['daily_usage'] print(daily_sum.head()) # Fill days with 0 usage idx = pd.date_range('2020-01-01', '2020-12-31') daily_sum.index = pd.DatetimeIndex(daily_sum.index) df_filled = daily_sum.reindex(idx, fill_value=0) # Fill missing values # Get day of the week, Monday=0, Sunday=6, and week days names dataset['DoW'] = dataset.transaction_date.dt.dayofweek # do the same in a different way dataset['weekday'] = dataset.transaction_date.dt.weekday # get day names dataset['day_name'] = dataset.transaction_date.apply(lambda x: x.day_name()) ``` ### Multi-catégorie/nominale **Plus de 2 catégories** sans ordre spécifique. Utilisez `dataset.describe(include='all')` pour obtenir des informations sur les catégories de chaque caractéristique. * Une **chaîne de référence** est une **colonne qui identifie un exemple** (comme le nom d'une personne). Cela peut être dupliqué (car 2 personnes peuvent avoir le même nom) mais la plupart seront uniques. Ces données sont **inutiles et doivent être supprimées**. * Une **colonne clé** est utilisée pour **lier des données entre les tables**. Dans ce cas, les éléments sont uniques. Ces données sont **inutiles et doivent être supprimées**. Pour **encoder des colonnes multi-catégories en nombres** (afin que l'algorithme ML les comprenne), on utilise **l'encodage factice** (et **non l'encodage one-hot** car cela **n'évite pas la multicollinéarité parfaite**). Vous pouvez obtenir une **colonne multi-catégorie encodée en one-hot** avec `pd.get_dummies(dataset.column1)`. Cela transformera toutes les classes en caractéristiques binaires, créant ainsi **une nouvelle colonne par classe possible** et attribuera 1 **valeur Vraie à une colonne**, les autres étant fausses. Vous pouvez obtenir une **colonne multi-catégorie encodée en factice** avec `pd.get_dummies(dataset.column1, drop_first=True)`. Cela transformera toutes les classes en caractéristiques binaires, créant ainsi **une nouvelle colonne par classe possible moins une** car **les deux dernières colonnes refléteront "1" ou "0" dans la dernière colonne binaire créée**. Cela évitera la multicollinéarité parfaite, réduisant les relations entre les colonnes. # Colinéaire/Multicollinéarité La colinéarité se produit lorsque **2 caractéristiques sont liées entre elles**. La multicollinéarité se produit lorsqu'il y en a plus de 2. En ML, **vous voulez que vos caractéristiques soient liées aux résultats possibles mais pas entre elles**. C'est pourquoi l'**encodage factice mélange les deux dernières colonnes** de celle-ci et **est meilleur que l'encodage one-hot** qui ne le fait pas, créant une relation claire entre toutes les nouvelles caractéristiques de la colonne multi-catégorie. Le VIF est le **Facteur d'Inflation de la Variance** qui **mesure la multicollinéarité des caractéristiques**. Une valeur **supérieure à 5 signifie qu'une des deux ou plusieurs caractéristiques colinéaires doit être supprimée**. ```python from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools.tools import add_constant #dummies_encoded = pd.get_dummies(dataset.column1, drop_first=True) onehot_encoded = pd.get_dummies(dataset.column1) X = add_constant(onehot_encoded) # Add previously one-hot encoded data print(pd.Series([variance_inflation_factor(X.values,i) for i in range(X.shape[1])], index=X.columns)) ``` # Déséquilibre catégoriel Cela se produit lorsqu'il n'y a **pas le même nombre de chaque catégorie** dans les données d'entraînement. ```python # Get statistic of the features print(dataset.describe(include='all')) # Get an overview of the features print(dataset.info()) # Get imbalance information of the target column print(dataset.target_column.value_counts()) ``` Dans un déséquilibre, il y a toujours une **classe ou des classes majoritaires** et une **classe ou des classes minoritaires**. Il existe 2 principales façons de résoudre ce problème : * **Sous-échantillonnage** : Supprimer des données sélectionnées de manière aléatoire de la classe majoritaire afin qu'elle ait le même nombre d'échantillons que la classe minoritaire. ```python from imblearn.under_sampling import RandomUnderSampler rus = RandomUserSampler(random_state=1337) X = dataset[['column1', 'column2', 'column3']].copy() y = dataset.target_column X_under, y_under = rus.fit_resample(X,y) print(y_under.value_counts()) #Confirm data isn't imbalanced anymore ``` * **Suréchantillonnage**: Générer plus de données pour la classe minoritaire jusqu'à ce qu'elle ait autant d'échantillons que la classe majoritaire. ```python from imblearn.under_sampling import RandomOverSampler ros = RandomOverSampler(random_state=1337) X = dataset[['column1', 'column2', 'column3']].copy() y = dataset.target_column X_over, y_over = ros.fit_resample(X,y) print(y_over.value_counts()) #Confirm data isn't imbalanced anymore ``` Vous pouvez utiliser l'argument **`sampling_strategy`** pour indiquer le **pourcentage** que vous souhaitez **sous-échantillonner ou sur-échantillonner** (**par défaut, c'est 1 (100%)** ce qui signifie égaliser le nombre de classes minoritaires avec les classes majoritaires) {% hint style="info" %} Le sous-échantillonnage ou le sur-échantillonnage ne sont pas parfaits. Si vous obtenez des statistiques (avec `.describe()`) des données sur-échantillonnées/sous-échantillonnées et que vous les comparez à l'original, vous verrez **qu'elles ont changé**. Par conséquent, le sur-échantillonnage et le sous-échantillonnage modifient les données d'entraînement. {% endhint %} ## Sur-échantillonnage SMOTE **SMOTE** est généralement une **méthode plus fiable pour sur-échantillonner les données**. ```python from imblearn.over_sampling import SMOTE # Form SMOTE the target_column need to be numeric, map it if necessary smote = SMOTE(random_state=1337) X_smote, y_smote = smote.fit_resample(dataset[['column1', 'column2', 'column3']], dataset.target_column) dataset_smote = pd.DataFrame(X_smote, columns=['column1', 'column2', 'column3']) dataset['target_column'] = y_smote print(y_smote.value_counts()) #Confirm data isn't imbalanced anymore ``` # Catégories Rarement Occurrentes Imaginez un ensemble de données où l'une des classes cibles **se produit très rarement**. C'est comme le déséquilibre des catégories de la section précédente, mais la catégorie rarement présente se produit encore moins que la "classe minoritaire" dans ce cas. Les méthodes de **suréchantillonnage** et de **sous-échantillonnage** brutes pourraient également être utilisées ici, mais en général ces techniques **ne donneront pas vraiment de bons résultats**. ## Poids Dans certains algorithmes, il est possible de **modifier les poids des données ciblées** afin que certaines d'entre elles aient par défaut plus d'importance lors de la génération du modèle. ```python weights = {0: 10 1:1} #Assign weight 10 to False and 1 to True model = LogisticRegression(class_weight=weights) ``` Vous pouvez **mélanger les poids avec des techniques de sur/ sous-échantillonnage** pour essayer d'améliorer les résultats. ## ACP - Analyse en Composantes Principales Est une méthode qui aide à réduire la dimensionnalité des données. Elle va **combiner différentes caractéristiques** pour **réduire la quantité** d'entre elles en générant **des caractéristiques plus utiles** (_moins de calculs sont nécessaires_). Les caractéristiques résultantes ne sont pas compréhensibles par les humains, donc cela **anonymise également les données**. # Catégories d'étiquettes incohérentes Les données peuvent comporter des erreurs dues à des transformations infructueuses ou simplement à des erreurs humaines lors de la saisie des données. Par conséquent, vous pourriez trouver la **même étiquette avec des fautes d'orthographe**, des **différences de casse**, des **abréviations** telles que : _BLEU, Bleu, b, bule_. Vous devez corriger ces erreurs d'étiquetage dans les données avant d'entraîner le modèle. Vous pouvez résoudre ces problèmes en mettant tout en minuscules et en faisant correspondre les étiquettes mal orthographiées aux bonnes. Il est très important de vérifier que **toutes les données que vous avez sont correctement étiquetées**, car par exemple, une erreur de faute d'orthographe dans les données, lors de l'encodage des classes factices, générera une nouvelle colonne dans les caractéristiques finales avec **de mauvaises conséquences pour le modèle final**. Cet exemple peut être détecté très facilement en encodant à chaud une colonne et en vérifiant les noms des colonnes créées. # Données manquantes Certaines données de l'étude peuvent être manquantes. Il se peut que certaines données aléatoires complètes manquent pour une erreur quelconque. Ce type de données est **Manquant Complètement au Hasard** (**MCAR**). Il se pourrait que certaines données aléatoires manquent mais qu'il y ait quelque chose qui rend plus probable que certains détails spécifiques manquent, par exemple, les hommes diront plus fréquemment leur âge que les femmes. Cela s'appelle **Manquant au Hasard** (**MAR**). Enfin, il pourrait y avoir des données **Manquantes Non au Hasard** (**MNAR**). La valeur des données est directement liée à la probabilité d'avoir les données. Par exemple, si vous voulez mesurer quelque chose de gênant, plus quelqu'un est gêné, moins il est probable qu'il le partage. Les **deux premières catégories** de données manquantes peuvent être **ignorées**. Mais la **troisième** nécessite de considérer **seulement des portions des données** qui ne sont pas impactées ou d'essayer de **modéliser d'une certaine manière les données manquantes**. Une façon de détecter les données manquantes est d'utiliser la fonction `.info()` car elle indiquera le **nombre de lignes mais aussi le nombre de valeurs par catégorie**. Si une catégorie a moins de valeurs que le nombre de lignes, alors il manque des données : ```bash # Get info of the dataset dataset.info() # Drop all rows where some value is missing dataset.dropna(how='any', axis=0).info() ``` Il est généralement recommandé que si une fonctionnalité est **manquante dans plus de 20 %** de l'ensemble de données, la **colonne devrait être supprimée :** ```bash # Remove column dataset.drop('Column_name', axis='columns', inplace=True) dataset.info() ``` {% hint style="info" %} Notez que **toutes les valeurs manquantes ne sont pas absentes dans l'ensemble de données**. Il est possible que les valeurs manquantes aient été données avec la valeur "Inconnu", "n/a", "", -1, 0... Vous devez vérifier l'ensemble de données (en utilisant `ensemble.de.données.nom.colonne.valeur.comptes(dropna=False)` pour vérifier les valeurs possibles). {% endhint %} Si des données manquent dans l'ensemble de données (et que ce n'est pas trop), vous devez trouver la **catégorie des données manquantes**. Pour cela, vous devez essentiellement savoir si les **données manquantes sont aléatoires ou non**, et pour cela, vous devez déterminer si les **données manquantes étaient corrélées avec d'autres données** de l'ensemble de données. Pour déterminer si une valeur manquante est corrélée avec une autre colonne, vous pouvez créer une nouvelle colonne qui met des 1 et des 0 si les données sont manquantes ou non, puis calculer la corrélation entre elles : ```bash # The closer it's to 1 or -1 the more correlated the data is # Note that columns are always perfectly correlated with themselves. dataset[['column_name', 'cloumn_missing_data']].corr() ``` Si vous décidez d'ignorer les données manquantes, vous devez quand même décider quoi en faire : vous pouvez **supprimer les lignes** avec des données manquantes (les données d'entraînement du modèle seront plus petites), vous pouvez **supprimer complètement la caractéristique**, ou vous pouvez **la modéliser**. Vous devriez **vérifier la corrélation entre la caractéristique manquante et la colonne cible** pour voir à quel point cette caractéristique est importante pour la cible, si elle est vraiment **faible**, vous pouvez **la supprimer ou la remplir**. Pour remplir les données continues manquantes, vous pourriez utiliser : la **moyenne**, la **médiane** ou utiliser un **algorithme d'imputation**. L'algorithme d'imputation peut essayer d'utiliser d'autres caractéristiques pour trouver une valeur pour la caractéristique manquante : ```python from sklearn.impute import KNNImputer X = dataset[['column1', 'column2', 'column3']] y = dataset.column_target # Create the imputer that will fill the data imputer = KNNImputer(n_neightbors=2, weights='uniform') X_imp = imputer.fit_transform(X) # Check new data dataset_imp = pd.DataFrame(X_imp) dataset.columns = ['column1', 'column2', 'column3'] dataset.iloc[10:20] # Get some indexes that contained empty data before ``` Pour remplir les données catégorielles, tout d'abord, vous devez réfléchir s'il y a une raison pour laquelle les valeurs sont manquantes. Si c'est par **choix des utilisateurs** (ils ne voulaient pas fournir les données), vous pouvez peut-être **créer une nouvelle catégorie** l'indiquant. S'il s'agit d'une erreur humaine, vous pouvez **supprimer les lignes** ou la **caractéristique** (vérifiez les étapes mentionnées précédemment) ou **la remplir avec le mode, la catégorie la plus utilisée** (non recommandé). # Combinaison de caractéristiques Si vous trouvez **deux caractéristiques** qui sont **corrélées** entre elles, vous devriez généralement **en abandonner une** (celle qui est moins corrélée avec la cible), mais vous pourriez également essayer de **les combiner et créer une nouvelle caractéristique**. ```python # Create a new feautr combining feature1 and feature2 dataset['new_feature'] = dataset.column1/dataset.column2 # Check correlation with target column dataset[['new_feature', 'column1', 'column2', 'target']].corr()['target'][:] # Check for collinearity of the 2 features and the new one X = add_constant(dataset[['column1', 'column2', 'target']]) # Calculate VIF pd.Series([variance_inflation_factor(X.values, i) for i in range(X.shape[1])], index=X.columns) ```
Apprenez le piratage AWS de zéro à héros avec htARTE (Expert en équipe rouge AWS de HackTricks)! Autres façons de soutenir HackTricks: * Si vous souhaitez voir votre **entreprise annoncée dans HackTricks** ou **télécharger HackTricks en PDF**, consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop)! * Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com) * Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family) * **Rejoignez le** 💬 [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez-nous** sur **Twitter** 🐦 [**@hacktricks_live**](https://twitter.com/hacktricks_live)**.** * **Partagez vos astuces de piratage en soumettant des PR aux** [**HackTricks**](https://github.com/carlospolop/hacktricks) et [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) dépôts GitHub.