Visualizzare il codice di training per un modello di Machine Learning automatizzato

Questo articolo illustra come visualizzare il codice di training generato da qualsiasi modello con training automatizzato di Machine Learning.

La generazione di codice per i modelli con training automatizzato di Machine Learning consente di visualizzare i dettagli seguenti usati da ML automatizzato per eseguire il training e la compilazione del modello per un'esecuzione specifica.

  • Pre-elaborazione dei dati
  • Selezione degli algoritmi
  • Definizione delle funzionalità
  • Iperparametri

È possibile selezionare qualsiasi modello di machine learning automatizzato, esecuzione consigliata o figlio, e visualizzare il codice di training Python generato che ha creato tale modello specifico.

Con il codice di training del modello generato, è possibile,

  • ottenere Informazioni sul processo di definizione delle funzionalità e sugli iperparametri usati dall'algoritmo del modello.
  • Tracciamento/versione/controllo dei modelli sottoposti a training. Archiviare il codice con controllo delle versioni per tenere traccia del codice di training specifico usato con il modello da distribuire nell'ambiente di produzione.
  • Personalizzare il codice di training modificando gli iperparametri o applicando le competenze/esperienza degli algoritmi e ML, e ripetere il training di un nuovo modello con il codice personalizzato.

Il diagramma seguente illustra che è possibile generare il codice per esperimenti di Machine Learning automatizzati con tutti i tipi di attività. Selezionare prima di tutto un modello. Il modello selezionato verrà evidenziato, quindi Azure Machine Learning copia i file di codice usati per creare il modello e li visualizza nella cartella condivisa dei notebook. Da qui è possibile visualizzare e personalizzare il codice in base alle esigenze.

Screenshot showing models tab, as well as having a model selected, as explained in the above text.

Prerequisiti

  • Un'area di lavoro di Azure Machine Learning. Per creare l'area di lavoro, vedere Creare risorse dell'area di lavoro.

  • Questo articolo presuppone una certa familiarità con la configurazione di un esperimento di Machine Learning automatizzato. Seguire l'esercitazione o la procedura per visualizzare i modelli di progettazione dell'esperimento di Machine Learning automatizzato principali.

  • La generazione automatica del codice ML è disponibile solo per gli esperimenti eseguiti in destinazioni di calcolo remote di Azure Machine Learning. La generazione di codice non è supportata per le esecuzioni locali.

  • Tutte le esecuzioni automatizzate di Machine Learning attivate tramite Azure Machine Learning Studio, SDKv2 o CLIv2 avranno la generazione di codice abilitata.

Ottenere codice e artefatti del modello generati

Per impostazione predefinita, ogni modello con training automatico di Machine Learning genera il codice di training al termine del training. Machine Learning automatizzato salva questo codice in outputs/generated_code dell'esperimento per tale modello specifico. È possibile visualizzarli nell'interfaccia utente di Azure Machine Learning Studio nella scheda Output e log del modello selezionato.

  • script.py Questo è il codice di training del modello che probabilmente si vuole analizzare con i passaggi di definizione delle caratteristiche, l'algoritmo specifico usato e gli iperparametri.

  • Notebook script_run_notebook.ipynb con codice boiler-plate per eseguire il codice di training del modello (script.py) nel calcolo di Azure Machine Learning tramite Azure Machine Learning SDKv2.

Al termine dell'esecuzione del training automatizzato di Machine Learning, è possibile accedere ai file script.py e script_run_notebook.ipynb tramite l'interfaccia utente di Azure Machine Learning Studio.

A tale scopo, passare alla scheda Modelli della pagina dell'esecuzione padre dell'esperimento di Machine Learning automatizzato. Dopo aver selezionato uno dei modelli sottoposti a training, è possibile selezionare il pulsante Visualizza codice generato. Questo pulsante reindirizza l'utente all'estensione del portale Notebooks, in cui è possibile visualizzare, modificare ed eseguire il codice generato per quel particolare modello selezionato.

parent run models tab view generate code button

È anche possibile accedere al codice generato del modello dalla parte superiore della pagina dell'esecuzione figlio quando si passa alla pagina dell'esecuzione figlio di un determinato modello.

child run page view generated code button

Se si usa Python SDKv2, è anche possibile scaricare "script.py" e "script_run_notebook.ipynb" recuperando l'esecuzione migliore tramite MLFlow e scaricando gli artefatti risultanti.

Limiti

Si verifica un problema noto quando si seleziona Visualizza codice generato. Questa azione non riesce a eseguire il reindirizzamento al portale Notebooks quando lo spazio di archiviazione si trova dietro una rete virtuale. Come soluzione alternativa, l'utente può scaricare manualmente lo script.py e i file script_run_notebook.ipynb passando alla scheda Output e log nella cartella output>generated_code. Questi file possono essere caricati manualmente nella cartella notebooks per eseguirli o modificarli. Seguire questo collegamento per altre informazioni sulle reti virtuali in Azure Machine Learning.

Screenshot showing Outputs and Logs tab, as well as having the outputs and generated code folder selected, as explained in the above text.

script.py

Il file script.py contiene la logica di base necessaria per eseguire il training di un modello con gli iperparametri usati in precedenza. Durante l'esecuzione di uno script di Azure Machine Learning, con alcune modifiche, il codice di training del modello può anche essere eseguito autonomo nell'ambiente locale.

Lo script può essere suddiviso approssimativamente in diverse parti seguenti: caricamento dei dati, preparazione dei dati, definizione delle caratteristiche dei dati, specifica del preprocessore/algoritmo e training.

Caricamento dei dati

La funzione get_training_dataset() carica il set di dati usato in precedenza. Si presuppone che lo script venga eseguito in uno script di Azure Machine Learning eseguito nella stessa area di lavoro dell'esperimento originale.

def get_training_dataset(dataset_id):
    from azureml.core.dataset import Dataset
    from azureml.core.run import Run
    
    logger.info("Running get_training_dataset")
    ws = Run.get_context().experiment.workspace
    dataset = Dataset.get_by_id(workspace=ws, id=dataset_id)
    return dataset.to_pandas_dataframe()

Durante l'esecuzione di uno script, Run.get_context().experiment.workspace recupera l'area di lavoro corretta. Tuttavia, se questo script viene eseguito all'interno di un'area di lavoro diversa o viene eseguito localmente, è necessario modificare lo script per specificare in modo esplicito l'area di lavoro appropriata.

Dopo aver recuperato l'area di lavoro, il set di dati originale viene recuperato dal relativo ID. Un altro set di dati con esattamente la stessa struttura può essere specificato rispettivamente in base all'ID o al nome con get_by_id() o get_by_name(). È possibile trovare l'ID più avanti nello script, in una sezione simile al codice seguente.

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--training_dataset_id', type=str, default='xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx', help='Default training dataset id is populated from the parent run')
    args = parser.parse_args()
    
    main(args.training_dataset_id)

È anche possibile scegliere di sostituire questa intera funzione con il proprio meccanismo di caricamento dei dati; gli unici vincoli sono che il valore restituito deve essere un dataframe Pandas e che i dati devono avere la stessa forma dell'esperimento originale.

Codice di preparazione dei dati

La funzione prepare_data() pulisce i dati, suddivide le colonne di peso di esempio e funzionalità e prepara i dati per l'uso nel training. Questa funzione può variare a seconda del tipo di set di dati e del tipo di attività dell'esperimento: classificazione, regressione, previsione di serie temporali, immagini o attività NLP.

L'esempio seguente mostra che, in generale, il dataframe del passaggio di caricamento dei dati viene passato. La colonna etichetta e i pesi di esempio, se originariamente specificati, vengono estratti e le righe contenenti NaN vengono eliminate dai dati di input.

def prepare_data(dataframe):
    from azureml.training.tabular.preprocessing import data_cleaning
    
    logger.info("Running prepare_data")
    label_column_name = 'y'
    
    # extract the features, target and sample weight arrays
    y = dataframe[label_column_name].values
    X = dataframe.drop([label_column_name], axis=1)
    sample_weights = None
    X, y, sample_weights = data_cleaning._remove_nan_rows_in_X_y(X, y, sample_weights,
     is_timeseries=False, target_column=label_column_name)
    
    return X, y, sample_weights

Per eseguire altre operazioni di preparazione dei dati, è possibile procedere in questo passaggio aggiungendo il codice di preparazione dei dati personalizzato.

Codice di definizione delle caratteristiche dei dati

La funzione generate_data_transformation_config() specifica il passaggio di definizione delle caratteristiche nella pipeline scikit-learn finale. Le caratteristiche dell'esperimento originale vengono riprodotte qui, insieme ai relativi parametri.

Ad esempio, la possibile trasformazione dei dati che può verificarsi in questa funzione può essere basata su computer come SimpleImputer() e CatImputer(), o trasformatori come StringCastTransformer() e LabelEncoderTransformer().

Di seguito è riportato un trasformatore di tipo StringCastTransformer() che può essere usato per trasformare un set di colonne. In questo caso, il set indicato da column_names.

def get_mapper_0(column_names):
    # ... Multiple imports to package dependencies, removed for simplicity ...
    
    definition = gen_features(
        columns=column_names,
        classes=[
            {
                'class': StringCastTransformer,
            },
            {
                'class': CountVectorizer,
                'analyzer': 'word',
                'binary': True,
                'decode_error': 'strict',
                'dtype': numpy.uint8,
                'encoding': 'utf-8',
                'input': 'content',
                'lowercase': True,
                'max_df': 1.0,
                'max_features': None,
                'min_df': 1,
                'ngram_range': (1, 1),
                'preprocessor': None,
                'stop_words': None,
                'strip_accents': None,
                'token_pattern': '(?u)\\b\\w\\w+\\b',
                'tokenizer': wrap_in_lst,
                'vocabulary': None,
            },
        ]
    )
    mapper = DataFrameMapper(features=definition, input_df=True, sparse=True)
    
    return mapper

Se sono presenti molte colonne che devono avere la stessa funzionalità/trasformazione applicata (ad esempio, 50 colonne in più gruppi di colonne), queste colonne vengono gestite raggruppando in base al tipo.

Nell'esempio seguente si noti che a ogni gruppo è applicato un mapper univoco. Questo mapper viene quindi applicato a ognuna delle colonne del gruppo.

def generate_data_transformation_config():
    from sklearn.pipeline import FeatureUnion
    
    column_group_1 = [['id'], ['ps_reg_01'], ['ps_reg_02'], ['ps_reg_03'], ['ps_car_11_cat'], ['ps_car_12'], ['ps_car_13'], ['ps_car_14'], ['ps_car_15'], ['ps_calc_01'], ['ps_calc_02'], ['ps_calc_03']]
    
    column_group_2 = ['ps_ind_06_bin', 'ps_ind_07_bin', 'ps_ind_08_bin', 'ps_ind_09_bin', 'ps_ind_10_bin', 'ps_ind_11_bin', 'ps_ind_12_bin', 'ps_ind_13_bin', 'ps_ind_16_bin', 'ps_ind_17_bin', 'ps_ind_18_bin', 'ps_car_08_cat', 'ps_calc_15_bin', 'ps_calc_16_bin', 'ps_calc_17_bin', 'ps_calc_18_bin', 'ps_calc_19_bin', 'ps_calc_20_bin']
    
    column_group_3 = ['ps_ind_01', 'ps_ind_02_cat', 'ps_ind_03', 'ps_ind_04_cat', 'ps_ind_05_cat', 'ps_ind_14', 'ps_ind_15', 'ps_car_01_cat', 'ps_car_02_cat', 'ps_car_03_cat', 'ps_car_04_cat', 'ps_car_05_cat', 'ps_car_06_cat', 'ps_car_07_cat', 'ps_car_09_cat', 'ps_car_10_cat', 'ps_car_11', 'ps_calc_04', 'ps_calc_05', 'ps_calc_06', 'ps_calc_07', 'ps_calc_08', 'ps_calc_09', 'ps_calc_10', 'ps_calc_11', 'ps_calc_12', 'ps_calc_13', 'ps_calc_14']
    
    feature_union = FeatureUnion([
        ('mapper_0', get_mapper_0(column_group_1)),
        ('mapper_1', get_mapper_1(column_group_3)),
        ('mapper_2', get_mapper_2(column_group_2)),
    ])
    return feature_union

Questo approccio consente di avere un codice più semplificato, senza un blocco di codice del trasformatore per ogni colonna, che può risultare particolarmente complesso anche quando nel set di dati sono presenti decine o centinaia di colonne.

Con le attività di classificazione e regressione, [FeatureUnion] viene usato per le utilità di funzione. Per i modelli di previsione delle serie temporali, più funzionalità basate su serie temporali vengono raccolte in una pipeline scikit-learn, quindi sottoposta a wrapping in TimeSeriesTransformer. Tutte le caratteristiche fornite dall'utente per i modelli di previsione delle serie temporali vengono eseguite prima di quelle fornite da Machine Learning automatizzato.

Codice di specifica del preprocessore

La funzione generate_preprocessor_config(), se presente, specifica un passaggio di pre-elaborazione da eseguire dopo la definizione delle funzionalità nella pipeline scikit-learn finale.

In genere, questo passaggio di pre-elaborazione è costituito solo dalla standardizzazione/normalizzazione dei dati eseguita con sklearn.preprocessing.

Machine Learning automatizzato specifica solo un passaggio di pre-elaborazione per i modelli di classificazione e regressione nonensemble.

Ecco un esempio di codice del preprocessore generato:

def generate_preprocessor_config():
    from sklearn.preprocessing import MaxAbsScaler
    
    preproc = MaxAbsScaler(
        copy=True
    )
    
    return preproc

Codice di specifica degli algoritmi e degli iperparametri

Il codice di specifica degli algoritmi e degli iperparametri è probabilmente quello a cui molti professionisti di Machine Learning sono più interessati.

La funzione generate_algorithm_config() specifica l'algoritmo effettivo e gli iperparametri per il training del modello come ultima fase della pipeline scikit-learn finale.

L'esempio seguente usa un algoritmo XGBoostClassifier con iperparametri specifici.

def generate_algorithm_config():
    from xgboost.sklearn import XGBClassifier
    
    algorithm = XGBClassifier(
        base_score=0.5,
        booster='gbtree',
        colsample_bylevel=1,
        colsample_bynode=1,
        colsample_bytree=1,
        gamma=0,
        learning_rate=0.1,
        max_delta_step=0,
        max_depth=3,
        min_child_weight=1,
        missing=numpy.nan,
        n_estimators=100,
        n_jobs=-1,
        nthread=None,
        objective='binary:logistic',
        random_state=0,
        reg_alpha=0,
        reg_lambda=1,
        scale_pos_weight=1,
        seed=None,
        silent=None,
        subsample=1,
        verbosity=0,
        tree_method='auto',
        verbose=-10
    )
    
    return algorithm

Il codice generato nella maggior parte dei casi usa pacchetti e classi software open source (OSS). Esistono istanze in cui vengono usate classi wrapper intermedie per semplificare codice più complesso. Ad esempio, è possibile applicare il classificatore XGBoost e altre librerie di uso comune, ad esempio LightGBM o Scikit-Learn.

In qualità di ML Professional, è possibile personalizzare il codice di configurazione dell'algoritmo modificandone gli iperparametri a seconda delle esigenze in base alle competenze e all'esperienza per l'algoritmo e il problema specifico di Machine Learning.

Per i modelli di ensemble, generate_preprocessor_config_N() (se necessario) e generate_algorithm_config_N() vengono definiti per ogni learner nel modello di ensemble, dove N rappresenta il posizionamento di ogni learner nell'elenco del modello di ensemble. Per i modelli stack ensemble, viene definito il meta learner generate_algorithm_config_meta().

Codice di training end-to-end

La generazione del codice genera build_model_pipeline() e train_model() per definire rispettivamente la pipeline scikit-learn e per chiamare fit().

def build_model_pipeline():
    from sklearn.pipeline import Pipeline
    
    logger.info("Running build_model_pipeline")
    pipeline = Pipeline(
        steps=[
            ('featurization', generate_data_transformation_config()),
            ('preproc', generate_preprocessor_config()),
            ('model', generate_algorithm_config()),
        ]
    )
    
    return pipeline

La pipeline scikit-learn include il passaggio di definizione delle caratteristiche, un preprocessore (se usato) e l'algoritmo o il modello.

Per i modelli di previsione delle serie temporali, la pipeline scikit-learn viene sottoposta a wrapping in un oggetto ForecastingPipelineWrapper, che include una logica aggiuntiva necessaria per gestire correttamente i dati delle serie temporali a seconda dell'algoritmo applicato. Per tutti i tipi di attività, viene usato PipelineWithYTransformer nei casi in cui la colonna dell'etichetta deve essere codificata.

Dopo aver creato la pipeline scikit-Learn, tutto ciò che rimane da chiamare è il metodo fit() per eseguire il training del modello:

def train_model(X, y, sample_weights):
    
    logger.info("Running train_model")
    model_pipeline = build_model_pipeline()
    
    model = model_pipeline.fit(X, y)
    return model

Il valore restituito da train_model() è il modello adattato/sottoposto a training sui dati di input.

Il codice principale che esegue tutte le funzioni precedenti è il seguente:

def main(training_dataset_id=None):
    from azureml.core.run import Run
    
    # The following code is for when running this code as part of an Azure Machine Learning script run.
    run = Run.get_context()
    setup_instrumentation(run)
    
    df = get_training_dataset(training_dataset_id)
    X, y, sample_weights = prepare_data(df)
    split_ratio = 0.1
    try:
        (X_train, y_train, sample_weights_train), (X_valid, y_valid, sample_weights_valid) = split_dataset(X, y, sample_weights, split_ratio, should_stratify=True)
    except Exception:
        (X_train, y_train, sample_weights_train), (X_valid, y_valid, sample_weights_valid) = split_dataset(X, y, sample_weights, split_ratio, should_stratify=False)

    model = train_model(X_train, y_train, sample_weights_train)
    
    metrics = calculate_metrics(model, X, y, sample_weights, X_test=X_valid, y_test=y_valid)
    
    print(metrics)
    for metric in metrics:
        run.log(metric, metrics[metric])

Dopo aver creato il modello sottoposto a training, è possibile usarlo per eseguire stime con il metodo predict(). Se l'esperimento riguarda un modello time series, usare il metodo forecast() per le stime.

y_pred = model.predict(X)

Infine, il modello viene serializzato e salvato come file .pkl denominato "model.pkl":

    with open('model.pkl', 'wb') as f:
        pickle.dump(model, f)
    run.upload_file('outputs/model.pkl', 'model.pkl')

script_run_notebook.ipynb

Il notebook script_run_notebook.ipynb funge da modo semplice per eseguire script.py in un ambiente di calcolo di Azure Machine Learning. Questo notebook è simile ai notebook di esempio di Machine Learning automatizzati esistenti, ma esistono alcune differenze principali, come illustrato nelle sezioni seguenti.

Ambiente

In genere, l'ambiente di training per un'esecuzione automatica di Machine Learning viene impostato automaticamente dall'SDK. Tuttavia, quando si esegue uno script personalizzato come il codice generato, machine learning automatizzato non guida più il processo, quindi l'ambiente deve essere specificato affinché il processo di comando abbia esito positivo.

La generazione del codice riutilizza l'ambiente usato nell'esperimento di Machine Learning automatizzato originale, se possibile. In questo modo si garantisce che l'esecuzione dello script di training non abbia esito negativo a causa di dipendenze mancanti e che non sia necessaria una ricompilazione dell'immagine Docker, che consente di risparmiare tempo e risorse di calcolo.

Se si apportano modifiche a script.py che richiedono dipendenze aggiuntive o si vuole usare il proprio ambiente, è necessario aggiornare l'ambiente script_run_notebook.ipynb di conseguenza.

Inviare l'esperimento

Poiché il codice generato non è più basato su ML automatizzato, invece di creare e inviare un processo AutoML, è necessario creare un elemento Command Job e fornire il codice generato (script.py).

L'esempio seguente contiene i parametri e le dipendenze regolari necessarie per eseguire un processo di comando, ad esempio calcolo, ambiente e così via.

from azure.ai.ml import command, Input

# To test with new training / validation datasets, replace the default dataset id(s) taken from parent run below
training_dataset_id = '<DATASET_ID>'

dataset_arguments = {'training_dataset_id': training_dataset_id}
command_str = 'python script.py --training_dataset_id ${{inputs.training_dataset_id}}'

command_job = command(
    code=project_folder,
    command=command_str,
    environment='AutoML-Non-Prod-DNN:25',
    inputs=dataset_arguments,
    compute='automl-e2e-cl2',
    experiment_name='build_70775722_9249eda8'
)
 
returned_job = ml_client.create_or_update(command_job)
print(returned_job.studio_url) # link to naviagate to submitted run in Azure Machine Learning Studio

Passaggi successivi