Über unsMediaKontaktImpressum
Veit Schiele 11. August 2020

Versionskontrolle für Machine-Learning-Projekte

In diesem Artikel erfahren Sie, wie die Modellentwicklung für maschinelles Lernen (ML) systematisch organisiert werden kann. So kann die Leistung eines Modells verbessert werden, wenn die Parameter feiner abgestimmt oder wenn mehr Trainingsdaten verfügbar werden. Um die Verbesserung messen zu können, sollte nachverfolgt werden können, welche Daten für das Training in welcher Modelldefinition und -konfiguration (Parameter etc.) verwendet und welche Modellleistungen damit erzielt wurden. Dabei sollten sowohl die Daten wie auch der zugehörige Programmcode in einer Version erfasst werden.

DVC (Data Version Control) wurde entwickelt, um Sie genau bei dieser Aufgabe zu unterstützen [1]. Durch die Implementierung einer DVC-Pipeline werden alle Daten geladen, vorverarbeitet, trainiert und die Leistung bewertet, wobei der Vorgang vollständig reproduzierbar und automatisierbar ist. Trainingsdaten, Modellkonfiguration, das Modell und Leistungsmetriken sind so versioniert, dass Sie bequem zu einer bestimmten Version zurückkehren und alle zugehörigen Konfigurationen und Daten überprüfen können. Außerdem bietet DVC einen Überblick über Metriken für alle Versionen Ihrer Pipeline, mit deren Hilfe Sie die beste Version ermitteln können. Zudem können Sie die Trainingsdaten, Modelle, Leistungsmetriken usw. mit anderen teilen und eine effiziente Zusammenarbeit ermöglichen.

Warum DVC?

  • Git-annex speichert wie DVC große Dateien nicht im Git-Repository selbst, sondern in einem lokalen Schlüssel-Wert-Speicher und verwendet Hardlinks oder Symlinks, anstatt Dateien zu duplizieren [2].
  • Git-LFS verwendet Reflinks oder Hardlinks, um Kopiervorgänge zu vermeiden und so große Dateien effizienter verarbeiten zu können. DVC ist jedoch kompatibel zu deutlich mehr Remote-Speichern (S3, Google Cloud, Azure, SSH usw.) [3].
  • Andere Workflow-Management-Systeme wie MLflow sind meist sehr allgemein und nicht speziell für die Verwaltung von Daten in ML-Projekten entwickelt worden [4].
  • DAGsHub ist ein DVC-Äquivalent, jedoch nur für Github [5].

Ein Beispielprojekt

Dieser Artikel führt Sie durch ein Beispielprojekt mit folgenden Phasen:

  1. Repositories erstellen
  2. Datenpipelines definieren
  3. Reproduzieren
  4. Pipeline visualisieren
  5. Daten teilen

1. Repositories erstellen

1. Zunächst wird ein Repository mit einer Versionsverwaltung Ihrer Wahl vorbereitet. In unserem Beispiel ist dies Git, DVC kann jedoch auch mit jeder anderen Versionsverwaltung zusammenarbeiten.

$ git init

2. In diesem Repository DVC initiieren:

$ dvc init
You can now commit the changes to git.
...

3. Initiales Git-Repository einchecken:

$ git status
     neue Datei:     .dvc/.gitignore
     neue Datei:     .dvc/config
$ git add .dvc         
$ git commit -m "Initial repo"

4. Daten mit DVC verwalten:

$ mkdir data
$ dvc get github.com/iterative/dataset-registry get-started/data.xml \
      -o data/data.xml
$ dvc add data/data.xml

5. Datenänderungen mit Git synchronisieren:

$ git add data/data.xml.dvc data/.gitignore
$ git commit -m "Add raw data"

6. Entfernten Datenspeicher konfigurieren:

Sie können DVC-Daten und -Modelle mit dvc push auch außerhalb Ihres lokalen Caches an einem entfernten Ort speichern, damit sie später auch in anderen Umgebungen abgerufen werden können. Üblicherweise sind dies entfernte Cloud-Services (S3, Azure Blob Storage, Google Cloud Storage), aber auch SSH, HDFS, und HTTP sind möglich. Um das Beispiel möglichst einfach nachvollziehbar zu halten, wählen wir hier einen lokalen Speicherort:

$ sudo mkdir -p /var/dvc-storage
$ dvc remote add -d local /var/dvc-storage
Setting 'local' as a default remote.
$ git commit .dvc/config -m "Configure local remote“
[master efaeb84] Configure local remote
 1 file changed, 4 insertions(+)

7. Speichern und teilen:

Mit dvc push kopieren Sie Dateien aus Ihrem lokalen DVC-Cache in den zuvor konfigurierten Remote-Storage.

$ dvc push

8. Überprüfen:

Sie können dies überprüfen, z. B. mit:

$ ls -R /var/dvc-storage
a3 f1
/var/dvc-storage/a3:
04afb96060aad90176268345e10355
/var/dvc-storage/f1:
5a7474cd26c014ce0cf7a8a3d50516.dir

Beachten Sie, dass beide Versionen der Daten gespeichert sind und übereinstimmen sollten mit dvc/cache.

2. Datenpipelines definieren

Die Versionierung großer Daten für Data Science ist ein Schritt in die richtige Richtung, aber noch nicht ausreichend, wenn Daten gefiltert, transformiert oder zum Trainieren von ML-Modellen verwendet werden sollen. Daher erfasst DVC auch die Abfolge der Prozesse bei der Manipulation der Daten. Damit können die Ergebnisse später genau so reproduziert werden, wie sie entstanden sind. Für unser Beispiel soll die Pipeline aus den folgenden fünf Phasen bestehen:

  1. Vorbereiten
  2. Aufteilen von Trainings- und Testdaten
  3. Merkmalsextraktion
  4. Trainieren
  5. Evaluieren

Das Ergebnis dieser Pipeline sind die Leistungsmetriken des trainierten Modells. Das gesamte Schema sieht so aus:

Rohdaten → aufteilen → Trainings-/Testdaten → extrahieren → Features → trainieren → Modell → evaluieren → Metriken

2.1. Vorbereiten: Als Vorbereitung auf die Datenpipeline unseres Beispiels benötigen wir zunächst eine virtuelle Python-Umgebung:

$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -r requirements.txt

In unserem Beispiel enthält die requirements.txt-Datei die vier Python-Pakete pandas, sklearn, scikit-learn und scipy. Beachten Sie jedoch bitte, dass DVC sprachunabhängig und nicht an Python gebunden ist. Niemand kann Sie davon abhalten, die Phasen in Bash, C oder in einer anderen Lieblingssprache oder einem anderen Framework wie Spark, PyTorch usw. zu implementieren.

2.2. Aufteilen von Trainings- und Testdaten: Mit dvc run können Sie einzelne Verarbeitungsphasen erstellen, wobei jede Phase durch eine mit Git verwaltete Quellcode-Datei sowie die Abhängigkeiten und Ausgabedaten beschrieben wird. Alle Phasen zusammen bilden dann die DVC-Pipeline. Unsere erste Phase soll die Daten in Trainings- und Testdaten aufteilen:

$ dvc run -n split -d src/split.py -d data/data.xml -o data/splitted \python src/split.py data/data.xml
  • -n split gibt den Namen mit der Beschreibung der Verarbeitungsphase an.
  • -d src/split.py und -d data/data.xml geben die Abhängigkeiten (dependencies) an. Wenn sich später eine dieser Daten ändert, erkennt DVC, dass die Ergebnisse neu berechnet werden müssen.
  • -o data/splitted gibt das Verzeichnis an, in das die Ergebnisse geschrieben werden sollen. In unserem Fall sollte sich der Arbeitsbereich geändert haben in:
 ├── data
 │   ├── data.xml
 │   ├── data.xml.dvc
 │   └── splitted
+│       ├── test.tsv
+│       └── train.tsv
+│
+├── dvc.lock
+├── dvc.yaml
 ├── requirements.txt
 └── src
     └── split.py
  • python src/split.py data/data.xml ist der Befehl, der in dieser Verarbeitungsphase ausgeführt wird. Die resultierende dvc.yaml-Datei sieht dann so aus:
   stages:
     split:
       cmd: python src/split.py data/data.xml
       deps:
       - data/data.xml
       - src/split.py
     outs:
     - data/splitted

In dvc.lock werden hingegen die MD5-Hashwerte gespeichert, anhand derer DVC erkennen kann, ob Änderungen an den Dateien vorgenommen wurden:

    split:
     cmd: python src/split.py data/data.xml
     deps:
     - path: data/data.xml
       md5: a304afb96060aad90176268345e10355
     - path: src/split.py
       md5: ffa32f4104c363040f27d2bd22db127d
     outs:
     - path: data/splitted
       md5: 1ce9051bf386e57c03fe779d476d93e7.dir

Da die Daten im Ausgabeverzeichnis nie mit Git versioniert werden sollten, hat dvc run dies auch bereits in die data/.gitignore-Datei geschrieben:

      /data.xml
+    /splitted

2.3. Merkmalsextraktion

Die nächste Verarbeitungsphase können Sie nun erstellen, indem die Ausgabe der vorhergehenden als Abhängigkeit definiert wird, in unserem Beispiel mit:

dvc run -n featurize -d src/featurization.py -d data/splitted \
    -o data/features python src/featurization.py data/splitted data/features

Sie können diese Verarbeitungsphase jedoch auch parametrisieren. Hierfür erstellen wir in unserem Beispiel die Datei params.yaml mit folgendem Inhalt:

 max_features: 6000
 ngram_range:
     lo: 1
     hi: 2

Der Aufruf fügt dann dem obigen Befehl noch -p <filename>:<params_list> hinzu:

$ dvc run -n featurize -d src/featurization.py -d data/splitted \
     -p params.yaml:max_features,ngram_range.lo,ngram_range.hi -o data/features \
     python src/featurization.py data/splitted data/features

Die dvc.yaml-Datei wird dann ergänzt um:

 +  featurize:
 +    cmd: python src/featurization.py data/splitted data/features
 +    deps:
 +     - data/splitted       
 +     - src/featurization.py
 +    params:
 +     - max_features       
 +     - ngram_range.lo
 +     - ngram_range.hi
 +    outs:
 +    - data/features

Schließlich müssen noch dvc.lock, dvc.yaml und data/.gitignore im Git-Repository aktualisiert werden:

$ git add dvc.lock dvc.yaml data/.gitignore

Mit dvc params erhalten Sie weitere Informationen zu den Parametrisierungsoptionen.

2.4. Trainieren

Die Trainingsphase wird erstellt mit:

$ dvc run -n train -d src/train.py -d data/features -o model.pkl \
   python src/train.py data/features model.pkl

2.5. Evaluieren

$ dvc run -n evaluate -d src/evaluate.py -d model.pkl -d data/features \
 -M auc.json python src/evaluate.py model.pkl data/features auc.json

evaluate.py liest Features aus der features/test.pkl-Datei aus und berechnet den AUC-Wert des Modells. Diese Metrik wird dann in die auc.json-Datei geschrieben. Wir verwenden die -M-Option, um die Datei als Metrik in der dvc.yaml-Datei zu markieren:

  + evaluate:
     cmd: python src/evaluate.py model.pkl data/features auc.json
  +  deps:
  +    - data/features
        
  +  - model.pkl
  +    - src/evaluate.py
  +   metrics:
  +   - auc.json:
  +       cache: false

Die Ergebnisse können Sie sich anzeigen lassen mit dvc metrics:

 $ dvc metrics show
      auc.json: 0.514172

Um nun unsere erste Version der DVC-Pipeline abzuschließen, fügen wir die Dateien und ein Tag dem Git-Repository hinzu:

 $ git add dvc.yaml dvc.lock auc.json
 $ git commit -m 'Add stage ‹evaluate›'
 $ git tag -a 0.1.0 -m "Initial pipeline version 0.1.0“

3. Reproduzieren

Wie Sie sehen konnten, macht DVC das Erstellen einer Pipeline sehr einfach. Der wirkliche Vorteil ist jedoch, dass das Reproduzieren der Ergebnisse oder Teilen davon, d. h. das erneute Ausführen von Stufen ggf. mit geänderten Bedingungen, sehr einfach ist. Hierfür steht Ihnen dvc repro zur Verfügung:

  $ dvc repro
  Verifying data sources in stage: 'data/data.xml.dvc'
  Stage 'split' didn't change, skipping
  Stage 'featurize' didn't change, skipping
  Stage 'train' didn't change, skipping
  Stage 'evaluate' didn't change, skipping

Sie können nun z. B. Parameter in der params.yaml-Datei ändern und anschließend die Pipeline erneut durchlaufen:

$ dvc repro
Stage 'data/data.xml.dvc' didn't change, skipping
Stage 'split' didn't change, skipping  
Running stage 'featurize' with command:
     python src/featurization.py data/splitted data/features
...
Stage 'train' didn't change, skipping
Stage 'evaluate' didn't change, skipping
To track the changes with git, run:
     git add dvc.lock

In unserem Fall hatte die Änderung der Parameter also keinen Einfluss auf das Ergebnis. Beachten Sie jedoch, dass DVC Änderungen an Abhängigkeiten und Ausgaben über MD5-Hashwerte erkennt, die in der {{dvc.lock}}-Datei gespeichert sind.

4. Pipeline visualisieren

Damit sich auch für andere schnell die Struktur der Pipeline erschließt, kann diese sehr einfach mit dvc dag visualisiert werden:

$ dvc dag
    +---------------------+
    | data/data.xml.dvc |
    +---------------------+
                 *
           +-------+
           |  split   |
           +-------+
                *
        +-----------+
         | featurize |
        +-----------+
        **           **
+-------+            *
| train   |             **
+-------+            *
        **          **
       +----------+
       | evaluate |
       +----------+

5. Daten teilen

Sie können nun Ihren Code und Ihre Trainingsdaten einfach mit anderen Teammitlgiedern teilen. Sofern die Teammitglieder ebenfalls auf unser lokales DVC-Repository in /var/dvc-storage zugreifen können, können Sie die Ergebnisse unseres Beispiels reproduzieren mit:

  $ git clone github.com/veit/dvc-example.git
  $ cd dvc-example
  $ dvc pull -TR
  A       data/data.xml
  1 file added
  $ ls data/
  data.xml data.xml.dvc

Fazit

Mit DVC können Sie sprachunabhängig reproduzierbare ML-Pipelines definieren und zusammen mit den zugehörigen Trainingsdaten, Konfigurationen, Leistungsmetriken usw. versioniert speichern. Dabei arbeitet DVC mit allen modernen Versionsverwaltungen zusammen und unterstützt viele verschiedene Speicherarten wie S3, Google Cloud, Azure, SSH usw. Damit strukturiert DVC nicht nur die Datenhaltung, sondern durch einzelne, atomare Phasen der DVC-Pipeline bleiben Änderungen in den Daten auch transparent und nachvollziehbar. Insgesamt erleichtert und effektiviert dies die Arbeit an ML-Projekten erheblich.

Autor
Das könnte Sie auch interessieren

Neuen Kommentar schreiben

Kommentare (0)