Les projets d'apprentissage automatique sont des processus itératifs. Vous ne vous arrêtez pas seulement dans un modèle réussi dans un cahier de jupyter. Vous ne vous arrêtez même pas après que le modèle soit en ligne, et les gens peuvent y accéder. Même après le déploiement, vous devez constamment le garder pour le garder afin qu'il fonctionne aussi bien que pendant la phase de développement.
Le scandale de Zillow est un parfait exemple de ce qui se passe si vous ne le faites pas. En 2021, Zillow a perdu 304 millions de dollars en raison de leur modèle d'apprentissage automatique qui a estimé les prix des logements. Zillow a payé trop de 7 000 maisons et a dû les décharger à un prix beaucoup plus bas. L'entreprise a été «arrachée» par son propre modèle et a dû réduire ses effectifs de 25%.
Ces types d'échecs de modèle silencieux sont courants avec les modèles du monde réel, ils doivent donc être constamment mis à jour avant les baisses de performances de production. Le fait de ne pas le faire endommage la réputation des entreprises, la confiance avec les parties prenantes et, finalement, leurs poches.
Cet article vous apprendra à implémenter un flux de travail de bout en bout pour surveiller les modèles d'apprentissage automatique après le déploiement avec Nannyml.
Nous apprendrons les bits techniques de ces fonctionnalités une par une.
Nous apprendrons les concepts fondamentaux de la surveillance des modèles à travers l'analogie d'un robot maîtrisant le tir à l'arc.
dans notre analogie:
Alors, commençons.
Imaginez que nous avons soigneusement préparé l'arc, les flèches et la cible (comme la préparation des données). Notre robot, équipé de nombreux capteurs et caméras, tire 10000 fois pendant l'entraînement. Au fil du temps, cela commence à frapper l'œil du taureau avec une fréquence impressionnante. Nous sommes ravis des performances et commençons à vendre notre robot et ses copies aux amateurs de tir à l'arc (déploiement du modèle).
Mais bientôt, nous obtenons un flux de plaintes. Certains utilisateurs signalent que le robot manque totalement la cible. Surpris, nous rassemblons une équipe pour discuter de ce qui ne va pas.
Ce que nous trouvons est un cas classique de Data Drift . L'environnement dans lequel les robots fonctionnent a changé - différents modèles de vent, différents niveaux d'humidité et même des changements dans les caractéristiques physiques des flèches (poids, équilibre) et arc.
Ce changement réel dans les fonctionnalités d'entrée a jeté la précision de notre robot, similaire à la façon dont un modèle d'apprentissage automatique pourrait sous-performer lorsque les données d'entrée, en particulier la relation entre les fonctionnalités, change dans le temps.
Après avoir abordé ces problèmes, nous publions un nouveau lot de robots. Pourtant, dans quelques semaines, des plaintes similaires circulent dedans. Publi, nous creusons plus profondément et découvrons que les cibles ont été fréquemment remplacées par les utilisateurs.
Ces nouvelles cibles varient en taille et sont placées à différentes distances. Ce changement nécessite une approche différente de la technique de prise de vue du robot - un exemple de manuel de concept dérive .
En termes d'apprentissage automatique, la dérive du concept se produit lorsque la relation entre les variables d'entrée et le résultat cible change. Pour nos robots, les nouveaux types de cibles signifiaient qu'ils doivent maintenant s'adapter à la prise de vue différemment, tout comme un modèle d'apprentissage automatique doit s'adapter lorsque la dynamique des données sur lesquelles il a été formé de manière significative.
pour ramener les points à la maison, explorons quelques exemples du monde réel de la façon dont les données et la dérive du concept se produisent.
Maintenant, considérons un workflow de surveillance ML de bout en bout.
La surveillance du modèle implique trois étapes majeures que les ingénieurs ML doivent suivre itérativement.
La première étape est, bien sûr, de garder un œil attentif sur les performances du modèle dans le déploiement. Mais cela est plus facile à dire qu'à faire.
Lorsque la vérité au sol est immédiatement disponible pour un modèle de production, il est facile de détecter les changements dans le comportement du modèle. Par exemple, les utilisateurs peuvent immédiatement dire ce qui ne va pas dans notre analogie du robot / tir à l'arc car ils peuvent regarder la cible et nous dire que le robot a raté - vérité immédiate.
En revanche, prenez l'exemple d'un modèle qui prédit les défauts de prêt. Ces modèles prédisent si un utilisateur fait défaut sur le paiement suivant ou non chaque mois. Pour vérifier la prédiction, le modèle doit attendre la date de paiement réelle. Ceci est un exemple de retardé de vérité au sol , qui est le plus courant dans les systèmes d'apprentissage automatique du monde réel.
Dans de tels cas, il est trop coûteux d'attendre que la vérité au sol soit disponible pour voir si les modèles fonctionnent bien. Ainsi, les ingénieurs ML ont besoin de méthodes pour estimer les performances du modèle sans eux. C'est là que les algorithmes tels que CBPE ou DLE entrent (plus sur eux plus tard).
Les modèles de surveillance peuvent également être effectués en mesurant l'impact direct de l'entreprise, c'est-à-dire la surveillance des KPI (indicateurs de performance clés). Dans le cas de Zillow, un système de surveillance approprié aurait pu détecter la perte de profit et alerté les ingénieurs (hypothétiquement).
Si le système de surveillance détecte une baisse des performances, que le système analysé les performances réalisées (avec vérité au sol) ou les performances estimées (sans vérité au sol), les ingénieurs ML doivent identifier la cause derrière la goutte.
Cela consiste généralement à vérifier les fonctionnalités individuellement ou en combinaison pour les données (dérive des fonctionnalités) et à examiner les cibles de la dérive du concept.
Sur la base de leurs résultats, ils utilisent diverses techniques de résolution des problèmes.
Voici une liste non exclusive de techniques pour atténuer les dommages causés par la dégradation des performances post-déploiement:
Chaque méthode a son contexte d'application, et souvent, vous pouvez finir par implémenter une combinaison d'entre eux.
Nannyml s'occupe des deux premières étapes de ce processus itératif. Alors, allons-y.
En dehors des ensembles de formation et de validation, Nannyml nécessite deux ensembles supplémentaires appelés référence et analyse dans des formats spécifiques pour commencer la surveillance. Cette section vous apprend à les créer à partir de toutes les données.
Tout d'abord, nous avons besoin d'un modèle déjà formé et prêt à être déployé dans la production afin que nous puissions le surveiller. À cette fin, nous utiliserons l'ensemble de données Diamonds et entraînerons un régresseur XGBOost.
import warnings import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.preprocessing import OneHotEncoder warnings.filterwarnings("ignore")
La première étape après l'importation des modules consiste à charger l'ensemble de données Diamonds de SeaBorn. Cependant, nous utiliserons une version spéciale de l'ensemble de données que j'ai spécifiquement préparé pour cet article pour illustrer à quoi ressemble la surveillance. Vous pouvez charger l'ensemble de données dans votre environnement à l'aide de l'extrait ci-dessous:
dataset_link = "https://raw.githubusercontent.com/BexTuychiev/medium_stories/master/2024/1_january/4_intro_to_nannyml/diamonds_special.csv" diamonds_special = pd.read_csv(dataset_link) diamonds_special.head()
Cette version spéciale de l'ensemble de données a une colonne nommée "Set", que nous arrivons dans une seconde.
Pour l'instant, nous extraire tous les noms de fonctionnalités, les noms de fonctionnalités catégoriques et le nom cible:
import warnings import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.preprocessing import OneHotEncoder warnings.filterwarnings("ignore")
La tâche est une régression - nous prédirons les prix des diamants compte tenu de leurs attributs physiques.
L'ensemble de données Diamonds est assez propre. Ainsi, le seul prétraitement que nous effectuons est de lancer les fonctionnalités du texte en type de données de catégorie Pandas. Ceci est une exigence pour permettre le prétraitement des données catégorielles automatique par xgboost.
dataset_link = "https://raw.githubusercontent.com/BexTuychiev/medium_stories/master/2024/1_january/4_intro_to_nannyml/diamonds_special.csv" diamonds_special = pd.read_csv(dataset_link) diamonds_special.head()
Passons à la division des données.
Oui, vous avez bien lu. Nous diviserons les données en quatre ensembles. Traditionnellement, vous ne l'avez peut-être divisé qu'en trois:
Les workflows de surveillance du modèle nécessitent un autre ensemble pour imiter les données de production. Il s'agit de s'assurer que notre système détecte correctement les baisses de performances en utilisant les bons algorithmes et rapporte ce qui n'a pas fonctionné.
À cette fin, j'ai étiqueté les lignes de diamants spéciaux avec quatre catégories dans la colonne de set:
# Extract all feature names all_feature_names = diamonds_special.drop(["price", "set"], axis=1).columns.tolist() # Extract the columns and cast into category cats = diamonds_special.select_dtypes(exclude=np.number).columns # Define the target column target = "price"
L'ensemble de formation représente 70%, tandis que le reste 10% chacune des données totales. Frimons-le:
for col in cats: diamonds_special[col] = diamonds_special[col].astype("category")
mais, les ensembles de données du monde réel ne sont pas livrés avec des étiquettes de jeu intégrées, vous devez donc diviser les données manuellement en quatre ensembles vous-même. Voici une fonction qui fait la tâche en utilisant Train_Test_Split de Sklearn:
diamonds_special.set.unique() ['train', 'val', 'test', 'prod'] Categories (4, object): ['prod', 'test', 'train', 'val']
Remarque: Utilisez le bouton "Expliquer le code" pour obtenir une explication ligne par ligne de la fonction.
Maintenant, passons à la formation du modèle.
Avant d'entraîner un modèle XGBOost, nous devons convertir les ensembles de données en dmatrices. Voici le code:
tr = diamonds_special[diamonds_special.set == "train"].drop("set", axis=1) val = diamonds_special[diamonds_special.set == "validation"].drop("set", axis=1) test = diamonds_special[diamonds_special.set == "test"].drop("set", axis=1) prod = diamonds_special[diamonds_special.set == "prod"].drop("set", axis=1) tr.shape (37758, 10)
Maintenant, voici le code pour former un régresseur avec des hyperparamètres déjà réglés:
def split_into_four(df, train_size=0.7): """ A function to split a dataset into four sets: - Training - Validation - Testing - Production train_size is set by the user. The remaining data will be equally divided between the three sets. """ # Do the splits training, the_rest = train_test_split(df, train_size=train_size) validation, the_rest = train_test_split(the_rest, train_size=1 / 3) testing, production = train_test_split(the_rest, train_size=0.5) # Reset the indices sets = (training, validation, testing, production) for set in sets: set.reset_index(inplace=True, drop=True) return sets tr, val, test, prod = split_into_four(your_dataset)
GRAND - Nous avons un modèle qui atteint 503 $ en termes de RMSE sur l'ensemble de validation. Évaluons la dernière fois du modèle sur l'ensemble de tests:
dtrain = xgb.DMatrix(tr[all_feature_names], label=tr[target], enable_categorical=True) dval = xgb.DMatrix(val[all_feature_names], label=val[target], enable_categorical=True) dtest = xgb.DMatrix( test[all_feature_names], label=test[target], enable_categorical=True ) dprod = xgb.DMatrix( prod[all_feature_names], label=prod[target], enable_categorical=True )
Les performances de test sont de 551 $. C'est assez bien.
Jusqu'à ce point, tout était assez simple. Maintenant, nous arrivons à la partie principale - création d'ensembles de référence et d'analyse.
Un ensemble de références est un autre nom pour l'ensemble de test utilisé dans le contexte de surveillance du modèle. Nannyml utilise les performances du modèle sur l'ensemble de test comme référence pour les performances de production. L'ensemble de référence doit avoir deux colonnes à part des fonctionnalités:
En ce moment, notre ensemble de tests contient les fonctionnalités et la cible mais il manque Y_Test_Pred:
import warnings import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.preprocessing import OneHotEncoder warnings.filterwarnings("ignore")
Ajoutons-le:
dataset_link = "https://raw.githubusercontent.com/BexTuychiev/medium_stories/master/2024/1_january/4_intro_to_nannyml/diamonds_special.csv" diamonds_special = pd.read_csv(dataset_link) diamonds_special.head()
Maintenant, nous renommerons l'ensemble de test en référence:
# Extract all feature names all_feature_names = diamonds_special.drop(["price", "set"], axis=1).columns.tolist() # Extract the columns and cast into category cats = diamonds_special.select_dtypes(exclude=np.number).columns # Define the target column target = "price"
À ce stade, imaginons que notre régresseur est déployé dans le cloud. L'imagination est plus simple que le déploiement du modèle, qui est exagéré pour cet article.
Après avoir déployé notre modèle de tarification des diamants, nous recevons la nouvelle qu'une grande expédition de diamants arrive. Avant l'arrivée de la cargaison, les mesures physiques des diamants nous ont été envoyées en tant que prod (nous imaginons toujours) afin que nous puissions générer des prix pour eux et commencer à les commercialiser sur notre site Web. Alors, générons.
for col in cats: diamonds_special[col] = diamonds_special[col].astype("category")
Avant que les diamants réels n'arrivent et un spécialiste humain vérifie les prix générés par notre modèle, nous devons vérifier si notre modèle fonctionne bien. Nous ne voulons pas afficher les diamants avec des prix inexacts sur notre site Web.
Pour ce faire, nous aurions besoin de mesurer les performances du modèle en comparant y_prod_pred aux prix réels des nouveaux diamants, la vérité au sol. Mais nous n'aurons pas de vérité au sol avant que les prix ne soient vérifiés. Nous devons donc estimer les performances du modèle sans vérité au sol.
Pour faire cette tâche, Nannyml nécessite un ensemble d'analyse - les données qui contient les données de production avec des prédictions faites par le modèle.
La création d'un ensemble d'analyses est similaire à la création d'une référence:
diamonds_special.set.unique() ['train', 'val', 'test', 'prod'] Categories (4, object): ['prod', 'test', 'train', 'val']
Maintenant, nous sommes prêts à estimer les performances du régresseur.
Nannyml fournit deux algorithmes majeurs pour estimer les performances des modèles de régression et de classification:
Nous utiliserons l'algorithme DLE pour notre tâche. DLE peut mesurer les performances d'un modèle de production sans vérité au sol et signaler diverses pseudo-métriques de régression telles que RMSE, RMSLE, MAE, etc.
Pour utiliser DLE, nous devons d'abord l'adapter à la référence pour établir une performance de base.
tr = diamonds_special[diamonds_special.set == "train"].drop("set", axis=1) val = diamonds_special[diamonds_special.set == "validation"].drop("set", axis=1) test = diamonds_special[diamonds_special.set == "test"].drop("set", axis=1) prod = diamonds_special[diamonds_special.set == "prod"].drop("set", axis=1) tr.shape (37758, 10)
L'initialisation DLE nécessite trois paramètres - les noms de fonctionnalités d'entrée, le nom de la colonne contenant la vérité du sol pour les tests et le nom de la colonne contenant des prédictions de test.
De plus, nous passons également RMSE en tant que métrique et une taille de morceau de 250. Ajustez l'estimateur à référence et estime les performances sur l'analyse:
def split_into_four(df, train_size=0.7): """ A function to split a dataset into four sets: - Training - Validation - Testing - Production train_size is set by the user. The remaining data will be equally divided between the three sets. """ # Do the splits training, the_rest = train_test_split(df, train_size=train_size) validation, the_rest = train_test_split(the_rest, train_size=1 / 3) testing, production = train_test_split(the_rest, train_size=0.5) # Reset the indices sets = (training, validation, testing, production) for set in sets: set.reset_index(inplace=True, drop=True) return sets tr, val, test, prod = split_into_four(your_dataset)
Nous avons un objet de résultat Nannyml qui peut être tracé. Voyons ce qu'il produit:
import warnings import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.preprocessing import OneHotEncoder warnings.filterwarnings("ignore")
Interprétons le tracé - il a deux sections qui affichent des performances sur les ensembles de référence et d'analyse. Si la production estimée RMSE monte au-delà des seuils, Nannyml les marque comme alertes.
Comme nous pouvons le voir, nous avons quelques alertes pour les données de production, suggérant que quelque chose de poisson se passe dans les derniers lots.
Notre système de surveillance nous indique que les performances du modèle ont chuté d'environ la moitié de la production. Mais ce n'est qu'une estimation - nous ne pouvons pas le dire avec certitude.
Pendant que nous traçons les performances estimées, l'envoi est arrivé et notre spécialiste des diamants a calculé leur prix réel. Nous les avons stockés comme prix dans prod.
Maintenant, nous pouvons comparer les performances réalisées (performances réelles) du modèle avec les performances estimées pour voir si notre système de surveillance fonctionne bien.
Nannyml fournit une classe PerformanceCalculator pour le faire:
dataset_link = "https://raw.githubusercontent.com/BexTuychiev/medium_stories/master/2024/1_january/4_intro_to_nannyml/diamonds_special.csv" diamonds_special = pd.read_csv(dataset_link) diamonds_special.head()
La classe nécessite quatre paramètres:
Après avoir passé ceux-ci et ajusté la calculatrice à référence, nous calculons sur l'ensemble d'analyse.
Pour comparer les REALISE_RESULTS à ESTIMATED_RESULTS, nous utilisons à nouveau un visuel:
# Extract all feature names all_feature_names = diamonds_special.drop(["price", "set"], axis=1).columns.tolist() # Extract the columns and cast into category cats = diamonds_special.select_dtypes(exclude=np.number).columns # Define the target column target = "price"
Eh bien, il semble que le RMSE estimé (violet) était assez proche des performances réelles (RMSE réalisé, bleu).
Cela nous dit une chose - notre système de surveillance fonctionne bien, mais notre modèle n'est pas, comme l'indique la perte croissante. Alors, quelle est la ou les raisons?
Nous allons plonger dans ce domaine maintenant.
Comme mentionné dans l'intro, l'une des raisons les plus courantes pour lesquelles les modèles échouent en production est la dérive. Dans cette section, nous nous concentrerons sur la détection de dérive des données (fonctionnalité).
La détection de la dérive fait partie de l'étape d'analyse des causes profondes du flux de travail de surveillance du modèle. Il commence généralement par la détection de dérive multivariée.
L'une des meilleures méthodes de détection de dérive multivariée est le calcul de l'erreur de reconstruction des données à l'aide de l'ACP. Il fonctionne remarquablement bien et peut attraper même le moindre des dérives dans les distributions de fonctionnalités. Voici un aperçu de haut niveau de cette méthode:
1. PCA s'adapte à la référence et le comprime à une dimension inférieure - Reference_lower.
2. Reference_lower est ensuite décompressée dans sa dimensionnalité d'origine - Reference_reconstruted.
3. La différence entre la référence et la référence_reconstruée est trouvée et nommée comme Erreur de reconstruction de données - Reconstruct_error.
4. La même méthode de réduction / reconstruction est appliquée aux lots de données de production.
Ces quatre étapes sont implémentées sous forme de classe DataReconstructionDriftCalculator dans Nannyml. Voici comment l'utiliser:
import warnings import matplotlib.pyplot as plt import numpy as np import pandas as pd import seaborn as sns import xgboost as xgb from sklearn.model_selection import train_test_split from sklearn.preprocessing import OneHotEncoder warnings.filterwarnings("ignore")
Une fois que nous avons l'erreur pour chaque morceau de données (250 lignes chacune), nous pouvons la tracer:
dataset_link = "https://raw.githubusercontent.com/BexTuychiev/medium_stories/master/2024/1_january/4_intro_to_nannyml/diamonds_special.csv" diamonds_special = pd.read_csv(dataset_link) diamonds_special.head()
Comme nous pouvons le voir, l'erreur de reconstruction est très élevée, indiquant une dérive des caractéristiques. Nous pouvons également comparer l'erreur aux performances réalisées:
# Extract all feature names all_feature_names = diamonds_special.drop(["price", "set"], axis=1).columns.tolist() # Extract the columns and cast into category cats = diamonds_special.select_dtypes(exclude=np.number).columns # Define the target column target = "price"
Comme nous pouvons le voir, tous les pointes de perte correspondent aux alertes dans l'erreur de reconstruction.
L'erreur de reconstruction des données est un seul nombre pour mesurer la dérive pour toutes les fonctionnalités. Mais que diriez-vous de la dérive des caractéristiques individuelles? Si notre ensemble de données contenait des centaines de fonctionnalités, comment trouverions-nous les fonctionnalités les plus dérivantes et prenons les mesures appropriées?
C'est là que nous utilisons des méthodes de détection de dérive univariées. Nannyml en propose plusieurs selon le type de fonctionnalité:
Tous ces éléments comparent la distribution des caractéristiques individuelles en référence à celles de l'ensemble d'analyse. Nous pouvons utiliser certains (ou même tous) dans la classe Univariatedriftcalculator:
for col in cats: diamonds_special[col] = diamonds_special[col].astype("category")
Le seul paramètre requis est Column_Names, le reste peut utiliser les défauts définitifs par Nannyml. Mais, pour garder les choses simples, nous utilisons Wasserstein et Jensen_Shannon pour des caractéristiques continues et catégorielles.
En ce moment, nous avons 11 fonctionnalités, donc appeler Plot (). Show () peut ne pas donner les résultats les plus optimaux. Au lieu de cela, nous pouvons utiliser un Count Ranker pour retourner les fonctionnalités qui ont donné le plus grand nombre d'alertes (lorsque tous les morceaux sont pris en compte). Voici le code:
diamonds_special.set.unique() ['train', 'val', 'test', 'prod'] Categories (4, object): ['prod', 'test', 'train', 'val']
Une fois que nous avons les résultats de Ranker, nous pouvons imprimer sa tête car il s'agit d'un Pandas DataFrame:
tr = diamonds_special[diamonds_special.set == "train"].drop("set", axis=1) val = diamonds_special[diamonds_special.set == "validation"].drop("set", axis=1) test = diamonds_special[diamonds_special.set == "test"].drop("set", axis=1) prod = diamonds_special[diamonds_special.set == "prod"].drop("set", axis=1) tr.shape (37758, 10)
Nous pouvons voir que les caractéristiques les plus problématiques sont la couleur et la profondeur. Cela ne devrait pas me surprendre car c'est moi qui les a fait dériver artificiellement avant d'écrire l'article.
Mais si c'était un scénario réel, vous voudriez passer du temps à travailler sur la résolution des problèmes pour ces fonctionnalités.
Je trouve la surveillance du modèle fascinant car elle brise l'illusion que l'apprentissage automatique est terminé une fois que vous avez un bon modèle. Au fur et à mesure que la terre tourne et que les modèles utilisateur ne changent, aucun modèle ne reste toujours pertinent pendant longtemps. Cela fait de la surveillance des modèles une partie cruciale de tout ensemble de compétences de l'ingénieur ML.
Aujourd'hui, nous avons couvert un flux de travail de surveillance de modèle de base. Nous avons commencé par parler des concepts de surveillance fondamentaux. Ensuite, nous avons plongé la tête la première dans le code: nous avons forgé les données dans un format compatible avec Nannyml; Créé notre premier tracé pour les performances du modèle estimé, en a créé un autre pour le comparer aux performances réalisées; a reçu quelques alertes que les performances baissent; l'a vérifié avec une détection de dérive multivariée; trouvé une dérive de caractéristiques lourdes; Double-le avec une détection de dérive des caractéristiques individuelles; identifié les fonctionnalités de dérive.
Malheureusement, nous nous sommes arrêtés directement à la résolution des problèmes. Cette dernière étape de surveillance du flux de travail dépasse le cadre de cet article. Cependant, j'ai d'excellentes recommandations qui le couvrent et beaucoup plus sur la surveillance des modèles:
Ces deux cours sont créés à partir de la meilleure personne que vous puissiez espérer - le PDG et fondateur de Nannyml. Dans les cours, il existe de nombreuses pépites d'informations que vous ne pouvez pas manquer.
Je recommande également de lire les documents Nanny ML pour certains tutoriels pratiques.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!