[PYTHON] Titanic-Überlebensprognose mit dem Workflow-Management-Tool Kedro für maschinelles Lernen

Einführung

In diesem Artikel verwenden wir eine Bibliothek namens Kedro, um einen Workflow zur Vorhersage der Titanic-Überlebensrate zu erstellen.

In letzter Zeit sind viele Arten von Workflow-Management-Tools für maschinelles Lernen und Tools zur Unterstützung der Konstruktion erschienen. Auch wenn du es gehört hast

Und so weiter.

Vergleichen wir die Leistung dieser Tools anhand eines gemeinsamen Vorhersageprojekts! Das ist der Anfang dieses Artikels.

Als allgemeines Projekt habe ich Titanic Survival Prediction gewählt. Dies ist ein Weg, den jeder einschlagen sollte, wenn er mit der Datenanalyse beginnt.

Versuchen Sie es also zuerst mit Kedro.

Deshalb habe ich mich für Kedro entschieden:

――Es war eine Weile auf Twitter beliebt, weil es einfach zu bedienen war.

Über Kedro

kedro ist ein Workflow-Management-Tool für maschinelles Lernen für Python, das von McKinseys Datenanalyseunternehmen QuantumBlack entwickelt wurde.

Wie unten aufgeführt, ist es ein nützliches Werkzeug für die Verwaltung von Experimenten im PoC-Stadium, hat jedoch schwache Funktionen für die Produktion.

Verwenden

Reibungslose experimentelle Entwicklung von Modellen für maschinelles Lernen

Was du tun kannst

--Erstellen von Verzeichnissen und Python-Codevorlagen

verdienen

Ich weiß nicht, was ich tun kann

Dinge unmöglich

Praktisches Beispiel für die Überlebensvorhersage der Titanic

Im Folgenden möchte ich Ihnen die Verwendung von Kedro am Beispiel der in der Datenanalyse bekannten Titanic vorstellen.

Grundsätzlich wird es unter Bezugnahme auf Official Tutorial erstellt. Der Code wurde auf GitHub hochgeladen.

Grundlegender Ablauf des Workflow-Aufbaus

  1. Erstellen Sie ein Projekt
  1. Datenaufbereitung
  1. Bau einer Pipeline
  1. Ausführung und Speicherung von Zwischendaten

Projekterstellung

Wenn Sie "kedro new" über die Befehlszeile ausführen, werden Sie aufgefordert, den Projektnamen, den Repository-Namen und den Paketnamen wie unten gezeigt einzugeben.

$ 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

Es wird ein Projektverzeichnis mit dem Namen erstellt, den Sie unter "Repository-Name" eingegeben haben. Wenn man nach innen schaut, sieht es wie folgt aus.

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

Der "Python-Paketname", der unterwegs abgefragt wird, wird als Name des Verzeichnisses verwendet, in dem der generierte Pipeline-Code unter "src" abgelegt wird (diesmal "titanic_with_kedro").

Schließlich werden Sie gefragt, ob Sie eine Beispielpipeline in Ihrem Projekt generieren möchten. Wenn Sie hier jedoch auf y drücken, wird der Code für das Lernprogramm als Satz generiert. Da dies ab dem zweiten Mal nicht mehr erforderlich ist, drücken Sie die Eingabetaste, ohne N einzugeben.

Datenaufbereitung

Dieses Mal werde ich die Daten von kaggle mithilfe der API abrufen und in data / 01_raw / einfügen. Die Registrierung zum Kaggle und der Erwerb des Authentifizierungstokens sind separat erforderlich.

$ cd data/01_raw/
$ kaggle competitions download -c titanic
$ unzip titanic.zip

Bearbeiten Sie außerdem den Datenkatalog wie folgt.

conf/base/catalog.yml


train:
    type: CSVLocalDataSet
    filepath: data/01_raw/train.csv

test:
    type: CSVLocalDataSet
    filepath: data/01_raw/test.csv

Die Datennamen "Zug" und "Test" werden auch beim Lesen von Daten verwendet. Mit type: können Sie das von kedro vorbereitete Datenleseformat auswählen. [^ 2]

[^ 2]: Sie können auch eine benutzerdefinierte Funktion zum Laden von Daten anwenden.

Versuchen wir zu sehen, ob die Daten gelesen werden können.

Führen Sie den folgenden kedro-Befehl aus, um das Jupyter-Notebook (oder IPython) zu starten. Das Notizbuch startet mit dem "Katalog" zum Lesen der im Voraus importierten Daten [^ 3].

$ kedro jupyter notebook
df_train = catalog.load("train")
df_train.head()

[^ 3]: Standardmäßig kann die Ausführung aufgrund eines Modulladefehlers möglicherweise nicht durchgeführt werden. Öffnen Sie in diesem Fall "src / / Pipeline.py" und löschen Sie den Modulimport für das Lernprogramm.

Bau einer Pipeline

Definition von Knoten und Pipeline

Beschreiben Sie die Datenvorverarbeitungspipeline gemäß dem Kedro-Format.

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'],
    )

Die Verarbeitungseinheit "Knoten" wird in der von "kedro" bereitgestellten Klasse "Pipeline" gespeichert.

Für die Funktion "Knoten"

--func: Funktion, die den Prozess beschreibt --inputs: Name der Eingabedaten --outputs: Name der Ausgabedaten --name: Knotenname

Angegeben.

Die vom Knoten durchgeführte Verarbeitung wird angegeben, indem ein Funktionsobjekt an das Argument "func" übergeben wird.

In diesem Beispiel wird die Funktion "preprocess ()" als ein Knoten verwendet, um fehlende Werte zu vervollständigen und die Etikettencodierung durchzuführen.

Zu diesem Zeitpunkt werden die durch das Argument "Eingaben" angegebenen Daten für die Eingabe der Funktion "preprocess ()" verwendet. Im obigen Beispiel wird "Zug" angegeben, dies zeigt jedoch auf die im obigen Datenkatalog "conf / base / catalog.yml" definierten "Zug" -Daten und das im Katalog beschriebene Datenformat. Basierend auf dem Pfad und dem Pfad wird die Datei gelesen und eingegeben.

Das Ausgabeobjekt erhält dann die durch "Ausgaben" angegebene Bezeichnung. Wenn Sie dieses Objekt in der nachfolgenden Verarbeitung verwenden, können Sie es mit dieser Bezeichnung angeben und aufrufen.

Die diesmal im Knoten angegebene Vorverarbeitungsfunktion lautet wie folgt. Im Gegensatz zur Pipeline muss keine spezielle Beschreibungsmethode verwendet werden.

(Die Funktion _label_encoding () ist eine Hilfsfunktion)

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

Integration mehrerer Pipelines

Sie können auch mehrere erstellte Pipelines kombinieren.

Dieses Mal habe ich das Modell definiert, das in einer anderen Pipeline erstellt wurdesrc / titanic_with_kedro / pipelines / data_science / Pipeline.py.

Gehen Sie wie folgt vor, um dies mit der vorherigen Vorverarbeitung zu kombinieren:

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,
    }

Die Modellbildungspipeline ist wie folgt definiert.

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"],
    )

Auf diese Weise können Sie mehrere Knoten in einer Pipeline platzieren.

Ausführung und Speicherung von Zwischendaten

Geben Sie nach dem Definieren der Pipeline einen Ausführungsbefehl aus dem Stammverzeichnis des Projekts aus.

$ kedro run

Auf diese Weise wird "src / / Pipeline.py" aufgerufen und der Prozess ausgeführt.

Wenn Sie zu diesem Zeitpunkt die Zwischendaten nach der Vorverarbeitung und das erstellte Modell (diesmal zufällige Gesamtstruktur) speichern möchten, fügen Sie dem Datenkatalog Folgendes hinzu.

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" und "clf" beziehen sich auf die vorverarbeiteten Daten bzw. das trainierte Modell. Bei der Definition der Pipeline wird der im Argument "output" der Funktion "node" angegebene Name jedoch so wie er ist erkannt und auf den angegebenen Formatpfad gesetzt. Es wird es speichern.

Wenn Sie "versioned" auf "true" setzen, wird es bei jeder Ausführung in einem anderen Verzeichnis gespeichert [^ 4].

[^ 4]: In diesem Fall wird zum Zeitpunkt der Ausführung unter data / 06_models / classifier.pickle / und data / 06_models / classifier.pickle / 2020-02-22T06.26.54.486Z eine neue Zeit erstellt Es wird als / classifier.pickle gespeichert.

Andere nützliche Funktionen

Die Grundfunktionen sind wie oben, es gibt jedoch auch die folgenden Funktionen.

Erstellen Sie einen Knoten direkt aus dem Jupyter-Notizbuch

Ich denke, dass es beim Erstellen eines Ausführungscodes viele Leute gibt, die ihn schreiben, während sie ihn einzeln mit Jupyter-Notizbuch ausführen, ohne ihn von Anfang an in eine .py-Datei zu schreiben.

In diesem Fall ist es schwierig, den Code später in .py umzuschreiben. Wenn Sie jedoch die CLI von kedro verwenden, wird nur der angegebene Teil des Codes in .py ausgespuckt.

Starten Sie dazu zuerst Jupyter von Kedros Cli aus.

$ kedro jupyter notebook

Fügen Sie als Nächstes das node -Tag nur zu den Zellen hinzu, die Sie in .py schreiben möchten. Wählen Sie zum Markieren im Menü oben auf dem Bildschirm "Ansicht> Zellen-Symbolleiste> Tags" aus. Oben in jeder Zelle wird ein Tag-Eingabefenster angezeigt. Geben Sie daher "node" ein, um es hinzuzufügen.

スクリーンショット 2020-02-23 19.31.18.png スクリーンショット 2020-02-23 19.33.06.png

Wenn Sie dann Folgendes über die Befehlszeile ausführen, wird nur der markierte Teil extrahiert und "src / / node / .py" generiert.

kedro jupyter convert notebooks/<notebook name>.ipynb


Referenz
https://kedro.readthedocs.io/en/latest/04_user_guide/11_ipython.html

Parameterverwaltung

Sie können auch die in der externen Datei angegebenen Parameter lesen, wenn Sie die Pipeline definieren.

Wenn Sie die obigen Modellbildungsparameter noch einmal betrachten, gibt es eine Stelle, an der "Parameter" in den Eingaben angegeben sind.

src/titanic_with_kedro/pipelines/data_science/pipeline.py


#Kürzung

def create_pipeline(**kwargs):
    return Pipeline(
        [
            node(
                func=modeling.split_data,
                inputs=["train_prep", "parameters"],
                outputs=["X_train", "X_test", "y_train", "y_test"],

#Kürzung

Das Obige ist der Teil, der in Trainingsdaten und Testdaten unterteilt werden soll.

"parameters" bezieht sich auf die Datei "parameters.yml" im Verzeichnis "conf / base".

conf/base/parameters.yml


test_size: 0.2
random_state: 17

Auf diese Weise können Sie es automatisch als Objekt vom Typ Wörterbuch an das Argument der Knotenfunktion übergeben und wie unten gezeigt darauf verweisen.

#Kürzung

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]

#Kürzung

Zusammenfassung

Wie oben erwähnt, machte ich nach der Einführung in die Verwendung von Kedro eine Titanic-Vorhersage.

Herausforderung: Speichern und Versionieren von Vorverarbeitungsobjekten

Der obige Prognosefluss ist fehlerhaft.

Wenn Sie an Datenanalyse gewöhnt sind, haben Sie dies möglicherweise bemerkt.

Das kann erwähnt werden.

Insbesondere bei ersteren besteht die Gefahr von Leckagen, da die Testdateninformationen mit den Trainingsdaten gemischt werden, wenn fehlende Werte und die Etikettencodierung vervollständigt werden.

Beide Probleme können gelöst werden, indem der Vorverarbeitungsteil in eine Klasse geschrieben und die Instanz mit pickle konsolidiert wird, damit sie später gelesen und verwendet werden kann.

Die Lösung ist nicht so schwierig, und es wird erwartet, dass sie realisiert werden kann, indem der Ausgabe des Knotens ein Vorverarbeitungsobjekt hinzugefügt, catalog.yml bearbeitet und zur Laufzeit gespeichert wird.

Es gibt jedoch ein Versionsverwaltungsproblem.

Wenn sowohl der Vorverarbeitungsteil als auch das Modell mit der Versionsverwaltung gespeichert werden, wie sollten die Vorverarbeitungsobjekte der Version, die jedem Modell entsprechen, verknüpft werden?

Angenommen, Sie möchten den Vorverarbeitungsteil neu schreiben, um ein Modell zu erstellen und später Rückschlüsse auf das alte Modell zu ziehen. Wenn der neue Vorverarbeitungscode zu diesem Zeitpunkt nicht mit dem alten Modell kompatibel ist, muss das Vorverarbeitungsobjekt zu diesem Zeitpunkt auch durch das Objekt ersetzt werden, das beim Erstellen des alten Modells verwendet wurde.

Um dies mühelos zu tun, ist eine Tagging- oder Ausführungs-ID-Zuweisung erforderlich, aber es ist nicht bestätigt, ob Kedro dies kann. [^ 5]

[^ 5]: In MLflow und Metaflow können das Modell und die Zwischenobjekte durch Angabe der Ausführungs-ID gelesen werden. Wenn gut mit diesen kombiniert, kann es möglich sein ...

Andere Gedanken

Darüber hinaus gab es einige Punkte, bei denen ich dachte "Ich wünschte, ich hätte diese Funktion ...", als ich Kedro berührte.

es gibt.

Ersteres kann manipuliert werden.

Letzteres liegt wahrscheinlich außerhalb des Anwendungsbereichs von Kedro, daher müssen wir es anscheinend in Kombination mit anderen Tools verwenden. Insbesondere MLflow von Databricks scheint genau richtig zu sein. In einem Artikel des Kedro-Entwicklers Quantum Black wird auch die Kombination mit MLflow erörtert. Es gibt auch eine Bibliothek namens PipelineX, die beide kombiniert.

Referenzlink

Recommended Posts

Titanic-Überlebensprognose mit dem Workflow-Management-Tool Kedro für maschinelles Lernen
[Maschinelles Lernen] FX-Vorhersage unter Verwendung des Entscheidungsbaums
Ich habe versucht, Tensorboard zu verwenden, ein Visualisierungstool für maschinelles Lernen
Anwendungsentwicklung mit Azure Machine Learning
Erstellen Sie ein Überlebensvorhersagemodell für Passagiere der Kaggle Titanic, ohne Python zu verwenden
Überlebensvorhersage unter Verwendung des titanischen neuronalen Netzes von Kaggle [80,8%]
Aktienkursprognose mit maschinellem Lernen (Scikit-Learn)
[Maschinelles Lernen] LDA-Themenklassifizierung mit Scikit-Learn
Überlebensvorhersage mit Kaggles Titanic XG Boost [80,1%]
[Maschinelles Lernen] Überwachtes Lernen mithilfe der Kernel-Dichteschätzung
Aktienkursprognose mit maschinellem Lernen (Return Edition)
[Maschinelles Lernen] Regressionsanalyse mit Scicit Learn
Bis zum Start einer dreifachen Vorhersage-Website für Bootsrennen mit maschinellem Lernen und Flask
Ich habe versucht, verschiedene Methoden für maschinelles Lernen (Vorhersagemodell) mithilfe von Scicit-Learn zu implementieren