[PYTHON] ML Flow Tracking est bon même pour un usage personnel

introduction

Il y a une histoire appelée MLOps, "Créons une base pour exploiter un système qui inclut correctement la technologie d'apprentissage automatique afin que le modèle d'apprentissage automatique ne devienne pas obsolète et que le système devienne une poubelle." Article de référence: MLOps2020 qui commence petit et grandit

MLFlow est un outil conçu pour vous aider. J'ai eu l'occasion d'utiliser l'une des fonctions de MLflow, MLflow Tracking, donc j'ai pensé que ce serait bien si j'essayais de l'utiliser tout en recherchant diverses choses, donc je vais l'écrire ici. Eh bien, il existe de nombreux autres articles sur la façon de l'utiliser, donc si vous le regardez, vous pouvez l'utiliser comme une graine pour des idées sur la façon de conserver un journal de construction de modèle, en disant "Que diriez-vous de garder un enregistrement de la création de modèle comme celui-ci?" Heureux. MLflow a utilisé la version 1.8.0. L'article suivant est facile à comprendre sur MLflow. Considérez comment utiliser mlflow pour rationaliser le cycle d'analyse des données MLflow 1.0.0 est sorti! Démarrez le cycle de vie de l'apprentissage automatique!

Des données d'utilisation

Utilisez les données de Churn de clients de télécommunications de Kaggle. https://www.kaggle.com/blastchar/telco-customer-churn Il s'agit de données sur les clients de la compagnie de téléphone et il s'agit d'un problème de classification binaire, la variable objective étant d'annuler ou non. Chaque ligne représente un client et chaque colonne contient les attributs du client.

Définition des packages et des fonctions à utiliser

Créez une fonction pour visualiser le résultat de l'agrégation et une fonction pour créer un modèle. Comme ce n'est pas le sujet principal, l'explication est omise.

Paquet utilisé

# package
import numpy as np
import scipy
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
import pandas as pd
from pandas.plotting import register_matplotlib_converters
import xgboost
import xgboost.sklearn as xgb
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import accuracy_score
from sklearn.metrics import f1_score
from sklearn.metrics import auc
from sklearn.metrics import roc_curve
from sklearn.metrics import log_loss
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import cross_validate
from sklearn.cluster import KMeans
from sklearn.metrics import confusion_matrix
from sklearn.metrics import make_scorer
from sklearn.metrics import precision_recall_curve
import time
import os
import glob
from tqdm import tqdm
import copy
import mlflow
from mlflow.sklearn import log_model
from mlflow.sklearn import load_model

Fonctions définies

#Créer un histogramme
def plot_many_hist(df_qiita,ex_col,ob_col,clip=[0, 99.],defalt_bin=10,png='tmp.png', visual = True):
    fig=plt.figure(figsize=(15,10))
    for i in range(len(ex_col)):
        df_qiita_clip=df_qiita.copy()
        col=ex_col[i]
        #coupure
        upperbound, lowerbound = np.percentile(df_qiita[col].values, clip)
        col_clip = np.clip(df_qiita[col].values, upperbound, lowerbound)
        df_qiita_clip['col_clip']=col_clip
        #Ajuster le nombre de bacs
        if len(df_qiita_clip['col_clip'].unique())<10:
            bins=len(df_qiita_clip['col_clip'].unique())
        else:
            bins=defalt_bin
        #Graphique de l'histogramme
        ax=plt.subplot(3,3,i+1)
        for u in range(len(df_qiita_clip[ob_col].unique())):
            ln1=ax.hist(df_qiita_clip[df_qiita_clip[ob_col]==u]['col_clip'], bins=bins,label=u, alpha=0.7)
            ax.set_title(col)
        h1, l1 = ax.get_legend_handles_labels()
        ax.legend(loc='upper right')
        ax.grid(True)
    plt.tight_layout()
    fig.suptitle("hist", fontsize=15)
    plt.subplots_adjust(top=0.92)
    plt.savefig(png)
    if visual == True:
        print('Cluster Hist')
        plt.show()
    else:
        plt.close()

#Standardisation
def sc_trans(X):
    ss = StandardScaler()
    X_sc = ss.fit_transform(X)
    return X_sc

#création de modèle kmeans
def km_cluster(X, k):
    km=KMeans(n_clusters=k,\
              init="k-means++",\
              random_state=0)
    y_km=km.fit_predict(X)
    return y_km,km

#Création de graphe circulaire
def pct_abs(pct, raw_data):
    absolute = int(np.sum(raw_data)*(pct/100.))
    return '{:d}\n({:.0f}%)'.format(absolute, pct) if pct > 5 else ''

def plot_chart(y_km, png='tmp.png', visual = True):
    km_label=pd.DataFrame(y_km).rename(columns={0:'cluster'})
    km_label['val']=1
    km_label=km_label.groupby('cluster')[['val']].count().reset_index()
    fig=plt.figure(figsize=(5,5))
    ax=plt.subplot(1,1,1)
    ax.pie(km_label['val'],labels=km_label['cluster'], autopct=lambda p: pct_abs(p, km_label['val']))#, autopct="%1.1f%%")
    ax.axis('equal')
    ax.set_title('Cluster Chart (ALL UU:{})'.format(km_label['val'].sum()),fontsize=14)
    plt.savefig(png)
    if visual == True:
        print('Cluster Structure')
        plt.show()
    else:
        plt.close()

#Création de table
def plot_table(df_qiita, cluster_name, png='tmp.png', visual = True):
    fig, ax = plt.subplots(figsize=(10,10))
    ax.axis('off')
    ax.axis('tight')
    tab=ax.table(cellText=np.round(df_qiita.groupby(cluster_name).mean().reset_index().values, 2),\
                 colLabels=df_qiita.groupby(cluster_name).mean().reset_index().columns,\
                 loc='center',\
                 bbox=[0,0,1,1])
    tab.auto_set_font_size(False)
    tab.set_fontsize(12)
    tab.scale(5,5)
    plt.savefig(png)
    if visual == True:
        print('Cluster Stats Mean')
        plt.show()
    else:
        plt.close()

#Création de modèle XGB
def xgb_model(X_train, y_train, X_test):
    model = xgb.XGBClassifier()
    model.fit(X_train, y_train)
    y_pred=model.predict(X_test)
    y_pred_proba=model.predict_proba(X_test)[:, 1]
    y_pred_proba_both=model.predict_proba(X_test)
    return model, y_pred, y_pred_proba, y_pred_proba_both

#Données d'entraînement et création de données de test
def createXy(df, exp_col, ob_col, test_size=0.3, random_state=0, stratify=True):
    dfx=df[exp_col].copy()
    dfy=df[ob_col].copy()
    print('exp_col:',dfx.columns.values)
    print('ob_col:',ob_col)

    if stratify == True:
        X_train, X_test, y_train, y_test = train_test_split(dfx, dfy, test_size=test_size, random_state=random_state, stratify=dfy)
    else:
        X_train, X_test, y_train, y_test = train_test_split(dfx, dfy, test_size=test_size, random_state=random_state)
    print('Original Size is {}'.format(dfx.shape))
    print('TrainX Size is {}'.format(X_train.shape))
    print('TestX Size is {}'.format(X_test.shape))
    print('TrainY Size is {}'.format(y_train.shape))
    print('TestY Size is {}'.format(y_test.shape))
    return X_train, y_train, X_test, y_test

#Renvoie le résultat de l'indice d'évaluation de la classification
def eval_list(y_test, y_pred, y_pred_proba, y_pred_proba_both):
    # eval
    log_loss_=log_loss(y_test, y_pred_proba_both)
    accuracy=accuracy_score(y_test, y_pred)
    precision=precision_score(y_test, y_pred)
    recall=recall_score(y_test, y_pred)
    # FPR, TPR, thresholds
    fpr, tpr, thresholds = roc_curve(y_test, y_pred_proba)
    # AUC
    auc_ = auc(fpr, tpr)
    # roc_curve
    fig, ax = plt.subplots(figsize=(10,10))
    ax.plot(fpr, tpr, color='darkorange', lw=2, label='ROC curve (area = %.2f)'%auc_)
    ax.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
    plt.legend()
    plt.title('ROC curve')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.grid(True)
    plt.savefig('ROC_curve.png')
    plt.close()
    return log_loss_, accuracy, precision, recall, auc_

# Recall-La courbe de précision renvoie
def threshold_pre_rec(test, prediction, save_name='threshold_pre_rec.png'):
    precision, recall, threshold = precision_recall_curve(test, prediction)
    thresholds = threshold
    user_cnt=[prediction[prediction>=i].shape[0] for i in thresholds]
    fig=plt.figure(figsize=(10,6))
    ax1 = plt.subplot(1,1,1)
    ax2=ax1.twinx()
    ax1.plot(thresholds, precision[:-1], color=sns.color_palette()[0],marker='+', label="precision")
    ax1.plot(thresholds, recall[:-1], color=sns.color_palette()[2],marker='+', label="recall")
    ax2.plot(thresholds, user_cnt, linestyle='dashed', color=sns.color_palette()[6], label="user_cnt")
    handler1, label1 = ax1.get_legend_handles_labels()
    handler2, label2 = ax2.get_legend_handles_labels()
    ax1.legend(handler1 + handler2, label1 + label2, loc='lower left')
    ax1.set_xlim(-0.05,1.05)
    ax1.set_ylim(-0.05,1.05)
    ax1.set_xlabel('threshold')
    ax1.set_ylabel('%')
    ax2.set_ylabel('user_cnt')
    ax2.grid(False)
    plt.savefig(save_name)
    plt.close()

#Probabilité-rendements prédits Courbe de probabilité mesurée
def calib_curve(y_tests, y_pred_probas, save_name='calib_curve.png'):
    y_pred_proba_all=y_pred_probas.copy()
    y_tests_all=y_tests.copy()
    proba_check=pd.DataFrame(y_tests_all.values,columns=['real'])
    proba_check['pred']=y_pred_proba_all
    s_cut, bins = pd.cut(proba_check['pred'], list(np.linspace(0,1,11)), right=False, retbins=True)
    labels=bins[:-1]
    s_cut = pd.cut(proba_check['pred'], list(np.linspace(0,1,11)), right=False, labels=labels)
    proba_check['period']=s_cut.values
    proba_check = pd.merge(proba_check.groupby(['period'])[['real']].mean().reset_index().rename(columns={'real':'real_ratio'})\
                            , proba_check.groupby(['period'])[['real']].count().reset_index().rename(columns={'real':'UU'})\
                            , on=['period'], how='left')
    proba_check['period']=proba_check['period'].astype(str)
    proba_check['period']=proba_check['period'].astype(float)
    fig=plt.figure(figsize=(10,6))
    ax1 = plt.subplot(1,1,1)
    ax2=ax1.twinx()
    ax2.bar(proba_check['period'].values, proba_check['UU'].values, color='gray', label="user_cnt", width=0.05, alpha=0.5)
    ax1.plot(proba_check['period'].values, proba_check['real_ratio'].values, color=sns.color_palette()[0],marker='+', label="real_ratio")
    ax1.plot(proba_check['period'].values, proba_check['period'].values, color=sns.color_palette()[2], label="ideal_line")
    handler1, label1 = ax1.get_legend_handles_labels()
    handler2, label2 = ax2.get_legend_handles_labels()
    ax1.legend(handler1 + handler2, label1 + label2, loc='center right')
    ax1.set_xlim(-0.05,1.05)
    ax1.set_ylim(-0.05,1.05)
    ax1.set_xlabel('period')
    ax1.set_ylabel('real_ratio %')
    ax2.set_ylabel('user_cnt')
    ax2.grid(False)
    plt.savefig(save_name)
    plt.close()

#Matrice mixte de sortie
def print_cmx(y_true, y_pred, save_name='tmp.png'):
    labels = sorted(list(set(y_true)))
    cmx_data = confusion_matrix(y_true, y_pred, labels=labels)

    df_cmx = pd.DataFrame(cmx_data, index=labels, columns=labels)

    plt.figure(figsize = (10,6))
    sns.heatmap(df_cmx, annot=True, fmt='d', cmap='coolwarm', annot_kws={'fontsize':20},alpha=0.8)
    plt.xlabel('pred', fontsize=18)
    plt.ylabel('real', fontsize=18)
    plt.savefig(save_name)
    plt.close()

Lire les données

Puisqu'il ne s'agit pas du sujet principal, la ligne manquante est supprimée telle quelle.

#Lecture des données
df=pd.read_csv('WA_Fn-UseC_-Telco-Customer-Churn.csv')
churn=df.copy()
#Changer le blanc demi-largeur en Nan
churn.loc[churn['TotalCharges']==' ', 'TotalCharges']=np.nan
#Changer pour flotter
churn['TotalCharges']=churn['TotalCharges'].astype(float)
#S'il y a une ligne manquante parce que c'est gênant, supprimez cette ligne
churn=churn.dropna()
print(churn.info())
display(churn.head())

Enregistrer les résultats du clustering avec MLflow

Après le regroupement avec kmenas, etc., les humains interprètent le résultat du regroupement en examinant les caractéristiques de chaque cluster. Cependant, il est difficile de visualiser et de créer un tableau pour voir les caractéristiques du cluster à chaque fois par essais et erreurs, et vous pouvez oublier le résultat précédent. MLflow peut être utilisé pour résoudre ce problème. Le clustering est effectué en réduisant les variables explicatives à des valeurs continues de «tenure», «MonthlyCharges» et «TotalCharges », et le résultat est enregistré dans MLflow.

####Mise en œuvre du clustering
exp_col=['tenure','MonthlyCharges','TotalCharges']
df_km=churn.copy()[exp_col]
df_cluster=df_km.copy()
cluster_name = 'My_Cluster'
k=5
ob_col = cluster_name

#Enregistrez le résultat du clustering dans mlflow
mlflow.set_experiment('My Clustering')#Définissez le nom de l'expérience
with mlflow.start_run():#enregistrement mlflow démarré
    #Standardisation
    X=sc_trans(df_cluster)
    #création de modèle kmeans
    y_km, km=km_cluster(X, k)
    #Enregistrer les paramètres dans mlflow
    mlflow.log_param("method_name",km.__class__.__name__)
    mlflow.log_param("k", k)
    mlflow.log_param("features", df_cluster.columns.values)
    #Enregistrer le modèle dans mlflow
    log_model(km, "model")
    
    df_cluster[cluster_name]=y_km
    
    #Visualisez les résultats du clustering
    #Ratio de composition du cluster
    plot_chart(y_km, png='Cluster_Chart.png', visual = False)#Enregistrer le diagramme dans le répertoire courant
    mlflow.log_artifact('Cluster_Chart.png')#Enregistrez le diagramme dans le répertoire courant
    os.remove('Cluster_Chart.png')#Suppression de la figure dans le répertoire actuel après l'enregistrement

    #Valeur moyenne par cluster
    plot_table(df_cluster, ob_col, png='Cluster_Stats_Mean.png', visual = False)#Enregistrer le diagramme dans le répertoire courant
    mlflow.log_artifact('Cluster_Stats_Mean.png')#Enregistrez le diagramme dans le répertoire courant
    os.remove('Cluster_Stats_Mean.png')#Suppression de la figure dans le répertoire actuel après l'enregistrement

    #Grapher par cluster
    plot_many_hist(df_cluster,exp_col,ob_col,clip=[0, 99.],defalt_bin=20, png='Cluster_Hist.png', visual = False)#Enregistrer le diagramme dans le répertoire courant
    mlflow.log_artifact('Cluster_Hist.png')#Enregistrez le diagramme dans le répertoire courant
    os.remove('Cluster_Hist.png')#Suppression de la figure dans le répertoire actuel après l'enregistrement

Comme mentionné ci-dessus, si vous exécutez le code qui enregistre le nom de l'algorithme, le nom de la variable explicative, la valeur para élevée et le graphique qui visualise les caractéristiques de chaque cluster, un dossier appelé «mlruns» sera créé dans le répertoire en cours. Tous les résultats enregistrés sont sauvegardés dans ce dossier appelé "mlruns". Si vous ouvrez un terminal dans le répertoire contenant le dossier "mlruns", écrivez "mlflow ui" et exécutez-le, le numéro d'hôte local 5000 sera lancé. Si vous accédez à localhost 5000 avec votre navigateur, vous pouvez voir l'enregistrement du modèle créé via la riche interface utilisateur de MLflow.

dossier "mlruns" image.png Écrivez "mlflow ui" dans le répertoire contenant le dossier "mlruns" image.png Écran supérieur de l'interface utilisateur mlflow image.png

Les résultats du clustering sont enregistrés dans une salle nommée My Clustering. Pour voir ce qui a été enregistré, vérifiez le lien avec la date et l'heure de l'enregistrement.

Parameters, Metrics image.png

Artifacts image.png

Artifacts image.png

Artifacts image.png

Il peut être confirmé que le nom d'algorithme défini à enregistrer, le nom de la variable explicative et la valeur para élevée sont enregistrés dans Paramètres. Les graphiques et autres informations sont enregistrés dans les artefacts. L'enregistrement de cette manière facilite la comparaison des résultats du modèle précédent même lorsque les variables explicatives sont modifiées ou la valeur de k est modifiée.

Enregistrer le résultat de la prédiction dans MLflow

Il peut être enregistré de la même manière lors de la création d'un modèle de classification.

####Construire un modèle prédictif
exp_col=['tenure','MonthlyCharges','TotalCharges']
ob_col = 'Churn'
df_pred=churn.copy()
df_pred.loc[df_pred[ob_col]=='Yes', ob_col]=1
df_pred.loc[df_pred[ob_col]=='No', ob_col]=0
df_pred[ob_col]=df_pred[ob_col].astype(int)
df_pred[cluster_name]=y_km
X_tests, y_tests, y_preds, y_pred_probas, y_pred_proba_boths = [],[],[],[],[]

for cluster_num in np.sort(df_pred[cluster_name].unique()):
    #Extraire les données d'un cluster
    df_n=df_pred[df_pred[cluster_name]==cluster_num].copy()
    
    #Données d'entraînement et création de données de test
    X_train, y_train, X_test, y_test=createXy(df_n, exp_col, ob_col, test_size=0.3, random_state=0, stratify=True)
    
    #La modélisation
    model, y_pred, y_pred_proba, y_pred_proba_both = xgb_model(X_train, y_train, X_test)

    #Calcul de l'indice d'évaluation
    log_loss_, accuracy, precision, recall, auc_ = eval_list(y_test, y_pred, y_pred_proba, y_pred_proba_both)
    
    #Insérer des données dans une liste vide
    X_tests.append(X_test)
    y_tests.append(y_test)
    y_preds.append(y_pred)
    y_pred_probas.append(y_pred_proba)
    y_pred_proba_boths.append(y_pred_proba_both)

    #Matrice mixte
    print_cmx(y_test.values, y_pred, save_name='confusion_matrix.png')
    
    # Recall-Courbe de précision
    threshold_pre_rec(y_test, y_pred_proba, save_name='threshold_pre_rec.png')
    
    #Courbe Pred Prob
    calib_curve(y_test,y_pred_proba, save_name='calib_curve.png')

    #Enregistrez le résultat de la prédiction dans mlflow
    mlflow.set_experiment('xgb_predict_cluster'+str(cluster_num))#Définissez le nom de l'expérience
    with mlflow.start_run():#enregistrement mlflow démarré
        mlflow.log_param("01_method_name", model.__class__.__name__)
        mlflow.log_param("02_features", exp_col)
        mlflow.log_param("03_objective_col", ob_col)
        mlflow.log_params(model.get_xgb_params())
        mlflow.log_metrics({"01_accuracy": accuracy})
        mlflow.log_metrics({"02_precision": precision})
        mlflow.log_metrics({"03_recall": recall})
        mlflow.log_metrics({"04_log_loss": log_loss_})
        mlflow.log_metrics({"05_auc": auc_})
        mlflow.log_artifact('ROC_curve.png')
        os.remove('ROC_curve.png')
        mlflow.log_artifact('confusion_matrix.png')
        os.remove('confusion_matrix.png')
        mlflow.log_artifact('threshold_pre_rec.png')
        os.remove('threshold_pre_rec.png')
        mlflow.log_artifact('calib_curve.png')
        os.remove('calib_curve.png')
        log_model(model, "model")

#Concattez les données de chaque cluster et rassemblez toutes les données
y_pred_all=np.hstack((y_preds))
y_pred_proba_all=np.hstack((y_pred_probas))
y_pred_proba_both_all=np.concatenate(y_pred_proba_boths)
y_tests_all=pd.concat(y_tests)
#Calcul de l'indice d'évaluation
log_loss_, accuracy, precision, recall, auc_ = eval_list(y_tests_all.values, y_pred_all, y_pred_proba_all, y_pred_proba_both_all)
#Matrice mixte
print_cmx(y_tests_all.values, y_pred_all, save_name='confusion_matrix.png')
#Courbe Pred Prob
calib_curve(y_tests_all, y_pred_proba_all, save_name='calib_curve.png')

#Enregistrez le résultat de la prédiction de toutes les données dans mlflow
mlflow.set_experiment('xgb_predict_all')#Définissez le nom de l'expérience
with mlflow.start_run():#enregistrement mlflow démarré
    mlflow.log_param("01_method_name", model.__class__.__name__)
    mlflow.log_param("02_features", exp_col)
    mlflow.log_param("03_objective_col", ob_col)
    mlflow.log_params(model.get_xgb_params())
    mlflow.log_metrics({"01_accuracy": accuracy})
    mlflow.log_metrics({"02_precision": precision})
    mlflow.log_metrics({"03_recall": recall})
    mlflow.log_metrics({"04_log_loss": log_loss_})
    mlflow.log_metrics({"05_auc": auc_})
    mlflow.log_artifact('ROC_curve.png')
    os.remove('ROC_curve.png')
    mlflow.log_artifact('confusion_matrix.png')
    os.remove('confusion_matrix.png')
    mlflow.log_artifact('calib_curve.png')
    os.remove('calib_curve.png')

Lorsque vous exécutez le code ci-dessus, un modèle est créé pour chaque cluster et enregistré dans MLflow. Vous pouvez enregistrer les noms d'algorithmes, les noms de variables explicatives, les valeurs de para élevé, les fonctions de perte, la précision de classification de divers indicateurs, la courbe ROC, la courbe d'étalonnage, le rappel, la courbe de précision, la matrice mixte, etc.

Écran supérieur mlflow image.png

Parameters image.png

Metrics image.png

Artifacts image.png

L'enregistrement de cette manière facilite la comparaison des résultats du modèle précédent lorsque les variables explicatives sont modifiées, l'ajustement du para haut est effectué ou l'algorithme est modifié.

en conclusion

Bien qu'il serve également de mémorandum, je n'ai pas du tout expliqué MLflow, et j'ai principalement montré comment l'utiliser comme ça. J'espère que le contenu vous donnera une image de la façon dont les gens qui envisagent de l'utiliser à l'avenir. Si vous êtes intéressé, je vous recommande de consulter d'autres articles et de les utiliser.

prime

Contenu du dossier "mlruns"

Cliquez sur 1 image.png

Cliquez sur b3fa3eb983044a259e6cae4f149f32c8 image.png

Cliquez sur les artefacts image.png

La figure est enregistrée image.png Ce qui peut être confirmé avec mlflow ui est stocké dans Local comme ceci. Il semble qu'il soit possible de partager des enregistrements de modèles avec différentes personnes en coopération avec des services cloud. Y a-t-il un problème avec Local pour un usage personnel?

c'est tout!

Recommended Posts

ML Flow Tracking est bon même pour un usage personnel
Bakthat est bon pour la sauvegarde, n'est-ce pas?
Bakthat est bon pour la sauvegarde, n'est-ce pas?