de en

Python Dependency Management ohne Kopfschmerzen

Einer der schmerzhaftesten Aspekte bei der Entwicklung neuronaler Netze ist das Dependency Management in Python. Es scheint, als hätte Python mehrere Räder neu erfunden, auf denen andere Sprachen wie JAVA bereits seit Jahren fröhlich dahingrollt sind. Ironischerweise werden Python-Pakete tatsächlich “wheels” genannt. Naja.

In diesem kurzen Artikel möchten wir Ihnen unsere Lösung für dieses Problem bei Deep Learning (DL) Projekten vorstellen, wo dieses Problem besonders hartnäckig ist, da man auch noch mehrere CUDA-Versionen jonglieren muss. Beachten Sie, dass es mehrere Wege gibt, damit umzugehen - dies ist nur derjenige, den wir am liebsten mögen - vielleicht werden Sie das auch?

Was ist daran so schwierig?

In einem professionellen Umfeld arbeiten Sie parallel an mehreren Projekten. Und wenn Sie an mehreren Projekten arbeiten, spüren Sie bald den Schmerz des Dependency Managements. KI-unterstützte Programmierung verstärkt dieses Problem nur noch, da Sie jetzt viele MVPs schneller erstellen und Ideen ausprobieren werden, die zuvor zu kostspielig zu evaluieren gewesen wären. Das ist natürlich eine gute Sache, zwingt Sie aber dazu, noch mehr Umgebungen parallel zu verwalten.

Ihre Projekte müssen jedoch folgendes bewältigen:

  • Verschiedene Python-Versionen
  • Verschiedene Versionen derselben Bibliotheken
  • Mehrere CUDA-Versionen parallel

Eine einzige Python-Installation mit einem Satz installierter Bibliotheken über pip scheidet also aus. Eine globale CUDA-Installation wird nicht funktionieren. Virtuelle Umgebungen sind eine weit verbreitete Lösung, aber die Verwendung von reinem pip darin ist zeitaufwändig und fehleranfällig und erfordert viel Disziplin, um alle Bibliotheksversionen festzulegen. Und dann müssten Sie CUDA manuell in jeder Umgebung installieren, was schwer zu automatisieren ist. Am Ende verwenden die Leute alle möglichen Kombinationen von Tools, um das irgendwie zu verwalten, jeder Workflow mit seinen eigenen Vor- und Nachteilen.

Bühne frei für CONDA

Natürlich ist einer der beliebtesten Wege, dies zu lösen, Anaconda. Aber Sie werden schnell die vollbezahlte Version benötigen - für ein kleineres Unternehmen summiert sich das schnell zu zusätzlichen Lizenzkosten. Noch ein (nicht gerade billiges) Abonnement nur um funktionierendes Dependency Management und stabile Builds zu bekommen, was ein grundlegendes Feature jeder Sprache sein sollte.

Abgesehen von den zusätzlichen Kosten gibt es noch ein anderes Problem: Jeder, der Ihre Builds reproduzieren oder am Projekt arbeiten soll, benötigt ebenfalls eine Lizenz - das macht es zu einer schlechten Wahl für Open-Source-Projekte, wo Sie möchten, dass eine große Zielgruppe mitmachen kann.

Beachten Sie jedoch, dass Anaconda definitiv eine gute Lösung ist. Wenn Sie oder Ihr Arbeitgeber Ihnen also eine Business-Lizenz besorgen können, sind Sie größtenteils fertig und können hier aufhören zu lesen.

Unsere Lösung

Aber es gibt auch einen Weg, Python Dependency Management Glückseligkeit mit mehreren CUDA-Versionen zu erreichen, indem man nur Open Source Komponenten verwendet. So geht’s.

Die kurze Übersicht:

  • Herausfinden, welche PyTorch-, Python- und CUDA-Versionen Sie benötigen
  • miniconda verwenden, um virtuelle Umgebungen zu verwalten und Python zu installieren
  • pip tools innerhalb der jeweiligen virtuellen Umgebung verwenden, um Versionen von Bibliotheken bequem zu verwalten
  • Und der wichtigste Trick: Die PyTorch CUDA Dependencies verwenden, um automatisch die exakte CUDA-Version zu installieren, die Sie benötigen

Mit diesen Schritten können Sie so viele Kombinationen von Python/PyTorch/CUDA wie gewünscht auf Ihrer Maschine verwalten und jedes Projekt ist 100% reproduzierbar von jedem mit Zugang zum Code.

Schritt 0: Herausfinden, was Sie wollen

So bestimmen wir die Versionen, die wir verwenden möchten:

Bestimmen Sie zuerst Ihre Ziel-PyTorch-Version. Wenn es ein greenfield Projekt ist, verwenden Sie einfach die neueste stabile Version (schauen Sie einfach auf https://pytorch.org/ und scrollen Sie nach unten). In manchen Fällen müssen Sie möglicherweise ein Netzwerk aus einem anderen Projekt oder Repository ausführen/trainieren/modifizieren, das ältere/veraltete APIs benötigt. Stellen Sie also sicher, dass Sie in diesen Fällen die PyTorch-Version kennen, indem Sie die (hoffentlich vorhandene) Dokumentation dieses Projekts lesen. Nehmen wir für jetzt an, Sie entscheiden sich für PyTorch 2.8.0.

Als nächstes müssen Sie die Python-Version dafür bestimmen. Die Kompatibilitätsmatrix finden Sie hier: https://github.com/pytorch/pytorch/blob/main/RELEASE.md#release-compatibility-matrix Wir haben uns schon oft die Finger an neueren Python-Versionen verbrannt, da oft andere Bibliotheken, die Sie später benötigen könnten, diese noch nicht unterstützen. Daher empfehlen wir, bei einer Version zu bleiben, die etwas älter ist als die aktuelle. Für PyTorch 2.8.0 würden wir Python 3.11 verwenden.

Schließlich müssen Sie wissen, welche CUDA-Version Sie wollen. Dafür müssen Sie wissen, was die maximale CUDA-Version ist, die Sie ausführen können. Um das herauszufinden, führen Sie einfach nvidia-smi in der Konsole aus, das gibt Ihnen eine Ausgabe wie diese:

+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 570.169                Driver Version: 570.169        CUDA Version: 12.8     |
|-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|                                         |                        |               MIG M. |
|=========================================+========================+======================|
|   0  NVIDIA GeForce RTX 4070 ...    Off |   00000000:01:00.0  On |                  N/A |
| N/A   48C    P4              9W /  115W |    1465MiB /   8188MiB |     33%      Default |
|                                         |                        |                  N/A |
+-----------------------------------------+------------------------+----------------------+
                                                                                         
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A            1892      G   /usr/lib/xorg/Xorg                      946MiB |
|    0   N/A  N/A            2785      G   cinnamon                                 28MiB |
|    0   N/A  N/A            4266      G   /usr/lib/thunderbird/thunderbird          7MiB |
|    0   N/A  N/A            4477      G   ...led --variations-seed-version        300MiB |
|    0   N/A  N/A            4870      G   /usr/lib/firefox/firefox                 20MiB |
|    0   N/A  N/A           10430      G   ...bkit2gtk-4.0/WebKitWebProcess         44MiB |
|    0   N/A  N/A           11682      G   ...ess --variations-seed-version          9MiB |
+-----------------------------------------------------------------------------------------+

Oben rechts steht CUDA Version: 12.8. Man könnte denken, das bedeutet, das System hat CUDA 12.8 installiert, aber das ist falsch und ist ein wunderbarer Stolperstein. Dies stammt von einer kleinen Fehlbezeichnung in der Ausgabe von nvidia-smi: Es sollte max CUDA Version heißen - denn das ist die maximale CUDA-Version, die der Treiber unterstützt. (Die obige Ausgabe ist sogar von einem System ohne CUDA-Installation). Jetzt wählen Sie einfach die höchste stabile CUDA-Version aus der Kompatibilitätsmatrix, die Ihr Treiber unterstützt, in unserem Fall 12.8, und Sie sind fertig.

Da CUDA-Unterstützung durch die Treiber abwärtskompatibel ist, macht es Sinn, den Treiber so aktuell wie möglich zu halten, damit Sie frei wählen können, welche CUDA-Version Sie verwenden.

Jetzt sind wir mit allen Versionsnummern ausgestattet, die wir brauchen, um ein funktionierendes Deep Learning Projekt zu erstellen, aber wie verwalten wir all diese Versionen?

Schritt 1: Miniconda

Wir beginnen mit miniconda, der kleineren Open Source Version des vollständigen Anaconda (Die Downloads sind auf der rechten Seite dieser Seite: https://www.anaconda.com/download/success). Das kommt nicht mit all den nützlichen Repositories, die Sie für CUDA und viele andere Bibliotheken benötigen, aber es wird Ihnen erlauben, schnell bequem umschaltbare virtuelle Umgebungen einzurichten und die exakte Python-Version zu installieren, die Sie benötigen. Um das leicht reproduzierbar zu machen, ist es am besten, eine Yaml-Konfigurationsdatei dafür zu erstellen. Nennen wir sie cool_project_name.yml. Sie wird immer so aussehen (außer bei verschiedenen Python-Versionen):

name: cool_project_name
channels:
  - defaults
dependencies:
  - python=3.11 # or any other python version you like
  - pip
  - pip:
      - pip-tools

Beachten Sie, wie wenig wir in die miniconda-Umgebung hineinlegen: nur die Python-Version und pip tools, die wir im nächsten Schritt verwenden werden, um tatsächlich die Dependencies zu installieren, die wir brauchen. Diese Datei wird sich wahrscheinlich nie wieder über die Lebensdauer des Projekts ändern.

Um die Umgebung zu erstellen, tun Sie:

    conda env create -f cool_project_name.yml
    conda activate cool_project_name

Schritt 2: Pip Tools

Um Dependencies in diese Umgebung zu installieren, könnten Sie einfach pip verwenden, aber pip-tools macht das viel sauberer und leichter reproduzierbar, indem es Bibliotheksversionen für Sie herausfindet und besonders Änderungen an den Dependencies viel schöner handhabt als reines pip.

Um Pakete hinzuzufügen, installieren Sie sie nicht einfach, sondern fügen sie stattdessen zu einer Datei namens requirements.in hinzu (beachten Sie das Suffix: es ist .in, nicht .txt). Z.B.:

# PyTorch with CUDA support
--extra-index-url https://download.pytorch.org/whl/cu128
torch==2.8.0+cu128
mlflow
transformers
# ...
# add any other libs you might need here
# ...

Ignorieren Sie die seltsame --extra-index Zeile für jetzt - das ist die Geheimzutat für unseren letzten Schritt. Der Trick hier ist, dass Sie die Version der Pakete festlegen, wo Sie wirklich eine spezifische Version brauchen, damit das Projekt funktioniert, und die anderen Versionen nicht spezifizieren (müssen). Das geht normalerweise irgendwann gründlich schief, wenn mehrere Leute an einem Projekt arbeiten. Jede Person installiert Pakete zu verschiedenen Zeitpunkten in einer etwas anderen Reihenfolge ohne Versionen zu spezifizieren, was früher oder später zu unangenehmen Fehlern durch Versionsinkompatibilitäten führt. Ich habe oft Projekte gesehen, die perfekt auf der Maschine eines Entwicklers laufen und auf einer anderen abstürzen. Hier werden Ihnen jedoch pip tools helfen.

Sie führen jetzt pip-compile requirements.in aus - pip tools wird eine passende Versionskombination für alle Bibliotheken ohne spezifizierte Versionen herausfinden und eine requirements.txt mit allen exakt spezifizierten Versionen für Sie erstellen. Das ist etwas, was Sie für sich selbst tun könnten, aber ich muss noch einen Python-Entwickler treffen, der diszipliniert genug ist, das tatsächlich zu tun.

Mit dieser requirements.txt können Sie jetzt pip-sync requirements.txt ausführen, um Ihre Umgebung zu aktualisieren. Das ist wirklich nützlich, wenn es eine Änderung/Update gibt, da dies nur fehlende Pakete oder Pakete mit einer geänderten Versionsnummer installiert.

Um anderen zu ermöglichen, den Build zu reproduzieren, fügen Sie jetzt cool_project_name.yml, requirements.in und requirements.txt zu Ihrem Repository hinzu.

Um später neue Pakete hinzuzufügen:

  • fügen Sie sie zu requirements.in hinzu
  • rufen Sie pip-compile requirements.in auf
  • rufen Sie pip-sync requirements.txt auf

Wenn jemand anders neue Pakete hinzugefügt oder Versionen geändert hat, müssen Sie nur pip-sync requirements.txt ausführen.

Schritt 3: CUDA Dependency

Abgesehen von Shape-Mismatches verursacht nichts so viele Kopfschmerzen im Deep Learning wie das Finden und Verwalten der korrekten CUDA/CuDNN-Pakete. Das ist normalerweise der Bereich, wo die Anaconda Business Version glänzt. Aber glücklicherweise können Sie das einfach mit pip lösen. Der Trick ist, das offizielle PyTorch Repository hinzuzufügen. Das wird mit der folgenden Zeile gemacht:

--extra-index-url https://download.pytorch.org/whl/cu128

Beachten Sie das Suffix /cu128 - das ist das Repository für CUDA 12.8. Ähnlich würden Sie /cu126 für CUDA 12.6, /cu118 für CUDA 11.8 usw. hinzufügen. Jetzt müssen Sie nur die torch- und CUDA-Version über die torch-Version festlegen:

# 2.8.0 -> the torch version, +cu128 -> the cuda version of the CUDA dependencies
torch==2.8.0+cu128

Das veranlasst pip tools dazu, die korrekten CUDA- und CuDNN-Pakete für PyTorch zu holen. Wenn Sie nach den möglichen Versionen schauen möchten, können Sie alle torch Version/CUDA Version Kombinationen hier ansehen (aber normalerweise sollte die Dependency-Matrix aus dem PyTorch Git Repository ausreichen): https://download.pytorch.org/whl/torch/

Wichtig: Das bedeutet, Sie sollten niemals CUDA manuell installieren. Tatsächlich ist es besser, wenn Sie keine CUDA-Installation auf Ihrem OS haben, so seltsam das klingt. Andernfalls könnte die globale Installation mit der Installation in Ihrer virtuellen Umgebung querschießen. Alles was Sie auf Ihrem OS installieren müssen ist ein aktueller GPU-Treiber.

Zusammenfassung

Indem Sie nur miniconda und pip-tools verwneden und zwei Konfigurationsdateien verwalten, können Sie jetzt leicht mehrere PyTorch/Python/CUDA-Kombinationen verwalten und bequem zwischen ihnen wechseln. Alle Mitglieder eines Projektteams können eine funktionierende Konfiguration mit nur wenigen Befehlen nachbauen. Seit wir diesem Setup-Muster für jedes Projekt folgen, hatten wir keine Reproduzierbarkeits-Kopfschmerzen mehr und Leute können schnell in neue Projekte eingearbeitet werden. Wir hoffen, Sie finden diese Informationen nützlich und sie werden Ihnen so viel helfen, wie sie uns geholfen haben.

P.S.: Aber was ist, wenn ich kein PyTorch verwende?

Nun, wenn es hart auf hart kommt, können Sie immer noch die PyTorch CUDA-Pakete ohne PyTorch installieren - sie funktionieren gut mit anderen Frameworks, falls diese keine eigenen CUDA pip-Pakete mitbringen. Welche Pakete das sind, überlassen wir als Übung für den Leser - aber zugegeben, diese Methode ist am besten für PyTorch-Entwicklung geeignet.