Image by dvc.org

Data Version Control: Reproduzierbares Machine Learning

Schon mal erlebt, dass du etwas in deinem Data-Cleaning-Algorithmus geändert hast, nur um dann festzustellen, dass die erste Version eigentlich die richtige Art war, die Daten zu bereinigen? Kein Problem, solltest du Git zur Versionierung deines Codes benutzen. Jedoch musst du jetzt wieder minutenlang warten, in Extremfällen vielleicht sogar stundenlang, bis die Daten erneut verarbeitet wurden und dann identisch zu dem Stand sind, den du gestern schon auf deiner Festplatte hattest.
Wolltest du schon mal ein altes Model mit alten Parametern wieder benutzen, allerdings dauert das erneute Trainieren Ewigkeiten?
Hast du schon mal Daten in deinem Team geteilt, nur um einen Monat später festzustellen dass alle im Team verschieden Versionen des Datensets nutzen?

Dann ist Data Version Control (DVC) genau das Tool, dass du bisher schmerzlich vermisst hast.
DVC wurde entwickelt, um ML Modelle teilbar und reproduzierbar zu machen. Es wurde designt, um große Dateien, Datensets, ML Modelle und Metriken zu handhaben, sowie auch Code. DVC ist agnostisch bezüglich der benutzten Programmiersprache da es in der Kommandozeile benutzt wird.

DVC ist die smarte Lösung, verschiedene Datenversion zu verwalten

Zuerst möchte ich die Vor- und Nachteil von DVC anführen. Tatsächlich beziehe ich mich zunächst auf die Dinge, die bei DVC nicht ideal sind. Es könnte behauptet werden, dass DVC nur eine clevere Art ist, verschieden Version der Daten, wie beispielsweise ‘cleaned_data.csv’, ‘cleaned_data_1.csv’, ‘cleaned_data_final.csv’ … , auf der Festplatte zu speichern. Und darin liegt auch etwas Wahrheit. Aber warum sollte man das überhaupt tun? Eventuellen will man ältere Versionen der Daten behalten, falls man zu diesen zurück möchte wenn sich Änderungen als falsch herausgestellt haben. Daher wäre ein Überschreiben der Daten keine Option. Allerdings müllt man so die Festplatte und den Projektordner zu. DVC realisiert das in einer deutlich eleganteren Weise.

DVC kann bei vielen Dingen unterstützen, einschließlich der Versionierung der Daten, der Machine Learning Modelle, es ermöglicht Reproduzierbarkeit und verwaltet spielend große Dateien.
Ein Punkt der beachtet werden sollte: es ist einfacher DVC in den Workflow zu integrieren, wenn man funktionale Programmierung nutzt und unterschiedliche Abschnitte des Projekts in entsprechende Skripte aufgeteilt wird.

Eine Experiment Management Software

DVC setzt auf Git auf und ähnelt Git in der Benutzung sehr stark. Es benutzt implizite Abhängigkeitsgraphen, um Data Science Projekte reproduzierbar zu machen. Um Daten versionierbar zu machen, erstellt es spezielle DVC-Dateien im Git Repository, welche zu einer Datei im Cache verlinken. Dabei ist DVC agnostische bezüglich der Programmiersprache sowie auch bezüglich der genutzten ML Bibliotheken.
Im folgenden werden wir ein wenig tiefer in die Details beim Versionieren von Daten mit DVC einsteigen. Wie erwähnt, erstellt DVC spezielle Dateien die zum Cache, einem versteckten Speicher, verlinken, um Versionen der Daten und Modelle verfügbar zu machen. Diese DVC-Dateien werden dann wiederum von Git getracked und so können verschiedene Versionen leicht durch Git Tags oder Branches abgerufen werden. Die Verwendung eines Caches bedeutet natürlich auch, dass viel zusätzlicher Festplattenspeicher benötigt wird. Dieser steigt proportional mit der Anzahl an Versionen der Daten oder Modelle. Jedoch ist DVC selbst bei großen Dateien sehr effizient und performant im Wechsel zwischen verschiedenen Versionen der Daten oder Modelle, da es Reflinks benutzt [1].
Weiterhin kann man mit DVC Pipelines erstellen oder Daten zu einem Remote-Speicher pushen oder davon pullen. Pipelines sind dabei der Goldstandard wenn es um Reproduzierbarkeit geht, weswegen wir uns im Folgenden darauf konzentrieren werden.

How to use DVC

Nach der Installation mit homebrew oder pip kann DVC in einem Git Repository initialisiert werden. Wechsel dafür in der Kommandozeile in das entsprechende Projekt und führe

dvc init
dvc config core.analytics false --global
git add .dvc/config
git commit -m "Initializing DVC"

aus. Die anonymisierten Statistikerhebungen wollen wir ausschalten. Der –global-Parameter tut dies für den aktiven Benutzer.
Nun können wir damit starten, Daten entweder manuell zu versionieren oder Stages und Pipelines zu definieren, welche automatisch die Daten tracken und die entsprechenden Stages des Projekts erneut ausführen, wenn sich daten- oder codetechnisch etwas geändert hat.

Außerdem gibt es die Möglichkeit, Daten zu einem Remote Speicherort zu pushen, so wie wir es häufig mit Code tun (vgl. Github oder Gitlab). Somit ist es deutlich vereinfacht, (große) Daten mit Teammitgliedern zu teilen. Auch wird es dadurch leichter, alle Teammitglieder auf dem selben Datenstand zu halten. So können Daten im Team kontrollierbar und nachvollziebar geteilt werden. Auch ist dies nützlich, wenn das Projekt geklont wird. Da die DVC-Dateien von Git getrackt werden, können die Daten einfach vom Remote Speicherort gezogen werden. Dabei stehen mehrere Optionen für ein solches Remote Storage zur Verfügung, aufgelistet in der Dokumentation [2]. Dabei werden beispielsweise ssh, https oder auch GoogleDrive unterstützt. Um ein lokales Remote hinzuzufügen, führe

dvc remote add −d localremote /path/to/folder

aus. Dorthin können nun die Daten gepusht werden und auch von dort gezogen werden, genutzt wird dafür dvc push, bzw. dvc pull.

Definition von Stages und Pipelines

Unser Ziel ist es, eine Pipeline zu definieren, sodass wir einfach Resultate reproduzieren können durch eine Verlinkung von Code und Daten zu seinem Verarbeitungsprozess. Dafür erstellen wir aus solchen Verarbeitungsprozess unseres Projekts (wie zum Beispiel Pythonskripte zum importieren von Daten und Modellieren), zu DVC Stages, welche dann die Pipeline bilden. Der Output einer Stage wird dann von DVC getrackt. Zuerst wollen wir jedoch die initialen Daten (den ganzen Ordner) tracken:

 dvc add data/initialData/ 

Nach der Benutzung von dvc add besteht keine Notwendigkeit, dvc commit auszuführen, da es in dem add-Befehl inbegriffen ist. Wie von der Ausgabe verlangt, führe als nächstes den dazugehörigen git add-Befehl aus. Wie man die initialen Daten updatet, welche nicht durch eine Pipeline getrackt werden, kann im entsprechenden Abschnitt nachgelesen werden.
Eine Stage wird folgendermaßen definiert:

dvc run \
-d data/initalData/data.csv \
-d code/import.py \
-o data/importedData/data.csv \
-f import.dvc \
python3 code/import.py

Wie von der Ausgabe verlangt, führe als nächstes den dazugehörigen git add-Befehl aus. Es wird automatisch das entsprechende Skript ausgeführt und alle Dateien oder Ordner, die wir als Output durch -o gekennzeichnet haben, werden getrackt. Wir definieren Abhängigkeit (dependencies) mit -d und vergeben einem Namen für die Stage nach -f. Wie von der Ausgabe verlangt, führe als nächstes den dazugehörigen git add-Befehl aus. Wir fahren mit dem Modelling fort. Einer der Abhängigkeiten diese Schrittes ist nun genau eine Datei, die den Output der vorherigen Stage darstellt. DVC gibt auch die Möglichkeit, Metriken zu unseren Modellen zu tracken (siehe -m):

dvc run \
-d data/importedData/data.csv \
-d code/modeling.py \
-o data/modelData/model.pkl \
-m data/modelData/metrics.json \
-f model.dvc \
python3 code/modeling.py

Wie von der Ausgabe verlangt, führe als nächstes den dazugehörigen git add-Befehl aus. Die Pipeline ist nun vollständig, inspiziert werden kann sie durch

dvc pipeline show −−ascii model.dvc

Wenn alles geklappt hat, können nun die Daten auf das Remote gepusht werden, alle Änderung und Pythonskripte commitet werden und ein Tag vergeben werden. So können wir später zwischen verschiedenen Versionen des Projekts, sowohl auf den Code bezogen als auch auf die Daten bezogen, zugreifen.

dvc push
git add code/∗.py
git commit −m ’v1.0 of the project’
git tag −a v1.0 −m ’v1.0 of the project’

Wenn wir unsere Resultate reproduzieren wollen, müssen wir lediglich

dvc repro model.dvc

ausführen. Momentan sollte jedoch nichts passieren. Wenn sich jedoch der Code in modeling.py ändert, wird DVC das bemerken und will die Schritte erneut ausführen, die davon betroffen sind. Wenn du das tust und der neuen Versionen einen neuen Tag gibst, können die Metriken der verschiedenen Versionen verglichen werden:

dvc metrics show −T

Nun ist es auch möglich, zwischen den Datenversionen zu wechseln, aber den aktuellen Code zu behalten:

git checkout v1.0 import.dvc
dvc checkout

Natürlich können wir auch zu der ersten Version des Codes zurück kehren:

git checkout v1.0
dvc checkout

Der dvc checkout-Befehl geht die DVC-Dateien durch und holt die entsprechenden Instanzen jeder Datei aus dem DVC-Cache. Sollten Datenpunkte während des Projekts hinzugefügt werden oder Fehler in den Daten korrigiert werden, so muss nicht mehr jede Version der Daten mit einem leicht geänderten Dateinamen gespeichert werden (falls das etwas war, was du jemals gemacht hast). Im Wesentlichen sind es immer noch die selben Daten bezüglich der Struktur und dem meisten Inhalt. Daher ist es ein deutlich besserer Ansatz, DVC zu nutzen anstatt eine Menge neuer Ordner zu erstellen, welche weitestgehend unveränderte Datensets enthalten.

Datenversionierung außerhalb von Pipelines

Jedoch werden die initialen Daten nicht durch obige Pipeline getrackt. Daher müssen wir DVC manuell Änderungen mitteilen. Idealerweise passiert dies selten, da in einem Projekt mehrere Datenlieferungen vermieden werden sollten. Leider ist das in der Realität häufig nicht so. Häufig muss mit mehreren Datenlieferungen umgegangen werden. Um DVC Änderungen der initial Daten mitzuteilen, führe

dvc remove data/initialData.dvc
-- (update the inital data) --
dvc add data/initialData/
git add data/initialData.dvc
git commit -m "new initial data"

aus. Wenn dein Betriebssystem reflink unterstützt, ist das entfernen der alten DVC-Datei überflüssig. Jedoch ist eine Zeile mehr ein geringer Mehraufwand beim updaten/ändern der initialen Daten, wenn dadurch Datenkorruption verhindert werden kann.

Natürlich ist dies auch die Vorgehensweise, jegliche anderen Daten einzeln mit DVC zu tracken. Wenn du keine Pipelines benutzen möchtest, ist dies das Prozedere.

Using Git Hooks

Wir können DVC sogar noch besser in Git integreren durch das Nutzen von Git Hooks. Diese können mit folgendem Befehl installiert werden:

dvc install

Drei Git Hooks sind nun aktiv (falls dies in einem Git Repository geschieht und DVC initialisiert ist):

  • Checkout: git checkout holt die DVC-Dateien zugehörig zu der gegeben Version. Somit muss kein dvc checkout mehr ausgeführt werden.
  • Commit/Reproduce: Eine Änderung welche zu Git comittet wird könnte Daten ändern oder generieren. Der Benutzer wird nun darauf hingewiesen, dvc commit der dvc repro auszuführen.
  • Push: dvc push wird immer ausgeführt, wenn Änderung zu einem Git Remote gepusht werden. Dadurch kann nicht vergessen werden, geänderte oder neue Daten zum Remote zu pushen.

Fazit

DVC ermöglicht uns, mühelos Daten zu verwalten, dadurch, dass es auf Git aufsetzt. Jedoch ist der Speicherverbrauch proportional zu der Anzahl an Versionen. Wir könne Pipelines definieren, sodass automatisch Teile des Projekts erneut ausgeführt werden, wenn sich Daten (oder Code) ändern. Des Weiteren können Daten zu einem Remote gepusht werden, wodurch die Daten leichter im Team teilbar werden.
DVC macht Machine Learning reproduzierbar.

‘In science consensus is irrelevant. What is relevant is reproducible results.’ - Michael Crichton

Quellen

[1] https://dvc.org/doc/user-guide/large-dataset-optimization
[2] https://dvc.org/doc/command-reference/remote

Marian Biermann
Data Scientist