Dans cet article, nous utiliserons une bibliothèque appelée Kedro pour créer un flux de travail de prédiction de survie Titanic.
Récemment, de nombreux types d'outils de gestion de flux de travail d'apprentissage automatique et d'outils d'aide à la construction sont apparus. Même si tu l'as entendu
Etc.
Comparons les performances de ces outils à travers un projet de prédiction commun! C'est le début de la rédaction de cet article.
Alors, essayez d'abord depuis Kedro.
Voici pourquoi j'ai choisi Kedro:
«Il était populaire sur Twitter pendant un certain temps car il était facile à utiliser.
kedro est un outil de gestion de flux de travail d'apprentissage automatique pour Python développé par la société d'analyse de données de McKinsey QuantumBlack.
Comme indiqué ci-dessous, c'est un outil utile pour gérer les expériences au stade PoC, mais d'un autre côté, il a des fonctions faibles pour la production.
Développement expérimental fluide de modèles d'apprentissage automatique
--Création de répertoires et de modèles de code Python --Exécution en ligne de commande de la construction d'un modèle de traitement de données basé sur le pipeline décrit selon le format --Gestion des résultats de sortie et des objets de données intermédiaires
--Unifier la structure du répertoire et le format du code --Gestion facile des données et des objets intermédiaires --Facile à lire et à enregistrer automatiquement simplement en écrivant dans catalog.yml --Facile à spécifier les paramètres sur une base de texte --Si vous l'écrivez dans parameters.yml, vous pouvez facilement le lire avec une chaîne.
--Rechargement des modèles et prétraitement des objets ―― Que dois-je faire si je veux faire des inférences sur de nouvelles données? ――En particulier, est-il possible de spécifier la version?
--Comparaison de la précision entre différents modèles --Il semble être complémentaire de MLflow
Ci-dessous, je voudrais vous présenter comment utiliser Kedro, en utilisant Titanic, qui est familier dans l'analyse de données, à titre d'exemple.
Fondamentalement, il est créé en se référant au Tutoriel officiel. Le code a été téléchargé sur GitHub.
kedro new
kedro run
--Si vous le définissez dans catalog.yml, il enregistrera le fichier intermédiaire.Lorsque vous exécutez kedro new
à partir de la ligne de commande, vous serez invité à entrer le nom du projet, le nom du référentiel et le nom du package comme indiqué ci-dessous.
$ kedro new
Project Name:
=============
Please enter a human readable name for your new project.
Spaces and punctuation are allowed.
[New Kedro Project]: Titanic with Kedro
Repository Name:
================
Please enter a directory name for your new project repository.
Alphanumeric characters, hyphens and underscores are allowed.
Lowercase is recommended.
[titanic-with-kedro]: titanic-with-kedro
Python Package Name:
====================
Please enter a valid Python package name for your project package.
Alphanumeric characters and underscores are allowed.
Lowercase is recommended. Package name must start with a letter or underscore.
[titanic_with_kedro]: titanic_with_kedro
Generate Example Pipeline:
==========================
Do you want to generate an example pipeline in your project?
Good for first-time users. (default=N)
[y/N]: N
Un répertoire de projet sera créé avec le nom que vous avez entré dans Nom du référentiel
.
En regardant à l'intérieur, cela ressemble à ce qui suit.
titanic-with-kedro
├── README.md
├── conf
│ ├── README.md
│ ├── base
│ │ ├── catalog.yml
│ │ ├── credentials.yml
│ │ ├── logging.yml
│ │ └── parameters.yml
│ └── local
├── data
│ ├── 01_raw
│ ├── 02_intermediate
│ ├── 03_primary
│ ├── 04_features
│ ├── 05_model_input
│ ├── 06_models
│ ├── 07_model_output
│ └── 08_reporting
├── docs
│ └── source
│ ├── conf.py
│ └── index.rst
├── errors.log
├── info.log
├── kedro_cli.py
├── logs
├── notebooks
├── references
├── results
├── setup.cfg
└── src
├── requirements.txt
├── setup.py
├── tests
│ ├── __init__.py
│ └── test_run.py
└── titanic_with_kedro
├── __init__.py
├── nodes
│ └── __init__.py
├── pipeline.py
├── pipelines
│ └── __init__.py
└── run.py
Le 'Nom du package Python' qui est demandé en cours de route est utilisé comme nom du répertoire qui place le code du pipeline généré sous src
(cette fois titanic_with_kedro
).
Enfin, il vous sera demandé «Voulez-vous générer un exemple de pipeline dans votre projet?», Mais si vous appuyez sur y ici, le code du didacticiel sera généré sous forme d'ensemble. Puisqu'il est inutile à partir de la deuxième fois, appuyez sur Entrée sans saisir N.
Cette fois, je vais récupérer les données de kaggle en utilisant l'API et les mettre dans data / 01_raw /
.
L'inscription à kaggle et l'acquisition du jeton d'authentification sont requises séparément.
$ cd data/01_raw/
$ kaggle competitions download -c titanic
$ unzip titanic.zip
En plus de cela, modifiez le catalogue de données comme suit.
conf/base/catalog.yml
train:
type: CSVLocalDataSet
filepath: data/01_raw/train.csv
test:
type: CSVLocalDataSet
filepath: data/01_raw/test.csv
Les noms de données «train» et «test» sont également utilisés lors de la lecture des données.
Avec type:
, vous pouvez sélectionner le format de lecture des données préparé par kedro à l'avance. [^ 2]
[^ 2]: vous pouvez également appliquer une fonction de chargement de données personnalisée.
Essayons de voir si les données peuvent être lues.
Exécutez la commande kedro suivante pour lancer le notebook Jupyter (ou IPython). Le notebook démarre avec le catalogue
pour lire les données importées à l'avance [^ 3].
$ kedro jupyter notebook
df_train = catalog.load("train")
df_train.head()
[^ 3]: Par défaut, il peut ne pas être possible de s'exécuter en raison d'une erreur de chargement du module. Dans ce cas, ouvrez src / <nom-projet> / pipeline.py
et supprimez l'importation de module pour le didacticiel.
Décrivez le pipeline de prétraitement des données selon le format kedro.
src/titanic_with_kedro/pipelines/data_engineering/pipeline.py
from kedro.pipeline import node, Pipeline
from titanic_with_kedro.nodes import preprocess
def create_pipeline(**kwargs):
return Pipeline(
[
node(
func=preprocess.preprocess,
inputs="train",
outputs="train_prep",
name="preprocess",
),
],
tags=['de_tag'],
)
L'unité de traitement "node" est stockée dans la classe "Pipeline" fournie par kedro.
Pour la fonction node
,
--func: fonction qui décrit le processus --inputs: nom des données d'entrée --outputs: nom des données de sortie --name: nom du nœud
Est spécifié.
Le traitement effectué par node est spécifié en passant un objet fonction à l'argument func
.
Dans cet exemple, la fonction preprocess ()
est utilisée comme un nœud pour compléter les valeurs manquantes et effectuer le codage des étiquettes.
A ce moment, les données spécifiées par l'argument ʻinputs sont utilisées pour l'entrée de la fonction
preprocess () `.
Dans l'exemple ci-dessus, «train» est spécifié, mais cela pointe vers les données de «train» définies dans le catalogue de données ci-dessus «conf / base / catalog.yml» et le format de données décrit dans le catalogue. Il lira le fichier et l'entrera en fonction du chemin.
L'objet de sortie reçoit alors l'étiquette spécifiée par ʻoutputs`. Lorsque vous utilisez cet objet dans un traitement ultérieur, vous pouvez le spécifier avec cette étiquette et l'appeler.
La fonction de prétraitement spécifiée dans node cette fois est la suivante. Contrairement au pipeline, il n'est pas nécessaire d'utiliser une méthode de description spéciale.
(La fonction _label_encoding ()
est une fonction auxiliaire)
src/titanic_with_kedro/nodes/preprocess.py
import pandas as pd
from sklearn import preprocessing
def _label_encoding(df: pd.DataFrame) -> (pd.DataFrame, dict):
df_le = df.copy()
# Getting Dummies from all categorical vars
list_columns_object = df_le.columns[df_le.dtypes == 'object']
dict_encoders = {}
for column in list_columns_object:
le = preprocessing.LabelEncoder()
mask_nan = df_le[column].isnull()
df_le[column] = le.fit_transform(df_le[column].fillna('NaN'))
df_le.loc[mask_nan, column] *= -1 # transform minus for missing records
dict_encoders[column] = le
return df_le, dict_encoders
def preprocess(df: pd.DataFrame) -> pd.DataFrame:
df_prep = df.copy()
drop_cols = ['Name', 'Ticket', 'PassengerId']
df_prep = df_prep.drop(drop_cols, axis=1)
df_prep['Age'] = df_prep['Age'].fillna(df_prep['Age'].mean())
# Filling missing Embarked values with most common value
df_prep['Embarked'] = df_prep['Embarked'].fillna(df_prep['Embarked'].mode()[0])
df_prep['Pclass'] = df_prep['Pclass'].astype(str)
# Take the frist alphabet from Cabin
df_prep['Cabin'] = df_prep['Cabin'].str[0]
# Label Encoding for str columns
df_prep, _ = _label_encoding(df_prep)
return df_prep
Vous pouvez également combiner plusieurs pipelines créés.
Cette fois j'ai défini la construction du modèle dans un autre pipelinesrc / titanic_with_kedro / pipelines / data_science / pipeline.py
.
Pour combiner cela avec le prétraitement précédent, procédez comme suit:
src/titanic_with_kedro/pipeline.py
from typing import Dict
from kedro.pipeline import Pipeline
from titanic_with_kedro.pipelines.data_engineering import pipeline as de
from titanic_with_kedro.pipelines.data_science import pipeline as ds
def create_pipelines(**kwargs) -> Dict[str, Pipeline]:
"""Create the project's pipeline.
Args:
kwargs: Ignore any additional arguments added in the future.
Returns:
A mapping from a pipeline name to a ``Pipeline`` object.
"""
de_pipeline = de.create_pipeline()
ds_pipeline = ds.create_pipeline()
return {
"de": de_pipeline,
"ds": ds_pipeline,
"__default__": de_pipeline + ds_pipeline,
}
Le pipeline de création de modèles est défini comme suit.
src/titanic_with_kedro/pipelines/data_science/pipeline.py
from kedro.pipeline import node, Pipeline
from titanic_with_kedro.nodes import modeling
def create_pipeline(**kwargs):
return Pipeline(
[
node(
func=modeling.split_data,
inputs=["train_prep", "parameters"],
outputs=["X_train", "X_test", "y_train", "y_test"],
),
node(func=modeling.train_model,
inputs=["X_train", "y_train"],
outputs="clf"),
node(
func=modeling.evaluate_model,
inputs=["clf", "X_test", "y_test"],
outputs=None,
),
],
tags=["ds_tag"],
)
De cette façon, vous pouvez placer plusieurs nœuds dans un pipeline.
Après avoir défini le pipeline, émettez une commande d'exécution à partir de la racine du projet.
$ kedro run
En faisant cela, src / <nom_projet> / pipeline.py
sera appelé et le processus sera exécuté.
À ce stade, si vous souhaitez enregistrer les données intermédiaires après le prétraitement et le modèle créé (forêt aléatoire cette fois), ajoutez ce qui suit au catalogue de données.
conf/base/catalog.yml
train_prep:
type: CSVLocalDataSet
filepath: data/02_intermediate/train_prep.csv
clf:
type: PickleLocalDataSet
filepath: data/06_models/classifier.pickle
versioned: true
train_prep
et clf
font référence aux données prétraitées et au modèle entraîné, respectivement, mais lors de la définition du pipeline, le nom spécifié dans l'argument ʻoutputs de la fonction
node` est reconnu tel quel, et il est défini sur le chemin de format spécifié. Cela le sauvera.
De plus, si vous définissez versionné
sur true, il sera enregistré dans un répertoire différent à chaque exécution [^ 4].
[^ 4]: Dans ce cas, une nouvelle heure est créée sous data / 06_models / classifier.pickle /
au moment de l'exécution, et data / 06_models / classifier.pickle / 2020-02-22T06.26.54.486Z Il sera enregistré sous / classifier.pickle
.
Les fonctions de base sont comme ci-dessus, mais il y a aussi les fonctions suivantes.
Lors de la création d'un code d'exécution, je pense qu'il y a beaucoup de gens qui l'écrivent en l'exécutant un par un avec le notebook Jupyter sans l'écrire dans un fichier .py depuis le début.
Dans ce cas, il est difficile de réécrire le code en .py plus tard, mais si vous utilisez le cli de kedro, seule la partie spécifiée du code sera crachée vers .py.
Pour ce faire, lancez d'abord Jupyter à partir du cli de kedro.
$ kedro jupyter notebook
Ensuite, ajoutez la balise node
uniquement aux cellules que vous souhaitez écrire dans .py.
Pour le balisage, sélectionnez Affichage> Barre d'outils Cellule> Balises
dans le menu en haut de l'écran. Étant donné que la fenêtre de saisie des balises est affichée en haut de chaque cellule, saisissez node
pour l'ajouter.
Ensuite, si vous exécutez ce qui suit à partir de la ligne de commande, seule la partie balisée sera extraite et src / <nom du projet> / nœuds / <nom du notebook> .py
sera généré.
kedro jupyter convert notebooks/<notebook name>.ipynb
Référence
https://kedro.readthedocs.io/en/latest/04_user_guide/11_ipython.html
Vous pouvez également lire les paramètres spécifiés dans le fichier externe lors de la définition du pipeline.
En regardant à nouveau les paramètres de construction du modèle ci-dessus, il y a un endroit où «paramètres» est spécifié dans les entrées.
src/titanic_with_kedro/pipelines/data_science/pipeline.py
#réduction
def create_pipeline(**kwargs):
return Pipeline(
[
node(
func=modeling.split_data,
inputs=["train_prep", "parameters"],
outputs=["X_train", "X_test", "y_train", "y_test"],
#réduction
Ce qui précède est la partie à diviser en données d'entraînement et données de test.
"" parameters "" fait référence au fichier "parameters.yml" dans le répertoire "conf / base".
conf/base/parameters.yml
test_size: 0.2
random_state: 17
Ce faisant, vous pouvez le passer automatiquement en tant qu'objet de type dictionnaire à l'argument de la fonction de nœud et y faire référence comme indiqué ci-dessous.
#réduction
def split_data(data: pd.DataFrame, parameters: Dict) -> List:
"""Splits data into training and test sets.
Args:
data: Source data.
parameters: Parameters defined in parameters.yml.
Returns:
A list containing split data.
"""
target_col = 'Survived'
X = data.drop(target_col, axis=1).values
y = data[target_col].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=parameters["test_size"], random_state=parameters["random_state"]
)
return [X_train, X_test, y_train, y_test]
#réduction
Comme mentionné ci-dessus, après avoir expliqué comment utiliser Kedro, j'ai essayé de faire une prédiction Titanic.
Le flux prévu ci-dessus est défectueux.
Si vous êtes habitué à l'analyse de données, vous l'avez peut-être remarqué.
Cela peut être mentionné.
Surtout pour le premier, il existe un risque de fuite car les informations de données de test sont mélangées aux données d'apprentissage lors de la saisie des valeurs manquantes et du codage des étiquettes.
Les deux problèmes peuvent être résolus en écrivant la partie de prétraitement dans une classe et en consolidant l'instance avec pickle afin qu'elle puisse être lue et utilisée plus tard.
La solution n'est pas si difficile, et on s'attend à ce qu'elle puisse être réalisée en ajoutant un objet de prétraitement à la sortie du nœud, en éditant catalog.yml et en l'enregistrant au moment de l'exécution.
Cependant, il existe un problème de gestion des versions.
Si la pièce de prétraitement et le modèle sont enregistrés avec la gestion des versions, comment les objets de prétraitement de la version correspondant à chaque modèle doivent-ils être liés?
Par exemple, supposons que vous souhaitiez réécrire la pièce de prétraitement pour créer un modèle et faire ultérieurement des inférences avec l'ancien modèle. À ce stade, si le nouveau code de prétraitement n'est pas compatible avec l'ancien modèle, l'objet de prétraitement doit également être remplacé par celui utilisé lors de la création de l'ancien modèle.
Pour ce faire sans effort, un certain marquage ou une attribution d'ID d'exécution est nécessaire, mais il n'est pas confirmé si Kedro peut le faire. [^ 5]
[^ 5]: Dans MLflow et Metaflow, il est possible de lire le modèle et les objets intermédiaires en spécifiant l'ID d'exécution. Si bien combiné avec ceux-ci, il peut être possible ...
De plus, il y a quelques points sur lesquels j'ai pensé "J'aurais aimé avoir cette fonction ..." quand j'ai touché Kedro.
il y a.
Le premier peut être altéré.
Ce dernier est probablement hors du champ d'application de Kedro, il semble donc que nous devions l'utiliser en combinaison avec d'autres outils. Plus précisément, MLflow de Databricks semble être juste. En fait, un article écrit par le développeur Kedro Quantum Black discute également de la combinaison avec MLflow. Il existe également une bibliothèque appelée PipelineX qui combine les deux.
Recommended Posts