2 Punkte von GN⁺ 2025-03-23 | Noch keine Kommentare. | Auf WhatsApp teilen
  • Eine Erklärung der internen Struktur von PyTorch und ein Leitfaden für alle, die zum C++-Codebestand von PyTorch beitragen möchten
  • Ziel dieses Artikels ist es, das Verständnis der Struktur der Tensor-Bibliothek von PyTorch und der Technik der automatischen Differenzierung (autograd) zu erleichtern und dabei zu helfen, sich im Codebestand zurechtzufinden

Grundstruktur von PyTorch-Tensoren

  • In PyTorch sind Tensoren die grundlegendste Datenstruktur
  • Tensoren sind n-dimensionale Datenstrukturen und können skalare Werte wie Gleitkommazahlen (float), Ganzzahlen (int) usw. speichern
  • Tensoren enthalten die folgenden Metadaten:
    • Größe (size): Dimensionsinformationen des Tensors
    • dtype: der gespeicherte Datentyp (z. B. float32, int64 usw.)
    • device: der Ort, an dem die Daten gespeichert sind (CPU, CUDA usw.)
    • stride: Offset-Informationen der Daten im physischen Speicher
  • Rolle von Stride

    • stride wird verwendet, um logische Indizes in physische Speicherpositionen umzuwandeln
    • stride legt für jede Dimension Offsets fest und bestimmt die physische Speicherposition, indem der Index mit dem stride-Wert multipliziert wird
    • Durch stride kann man dieselben Daten in anderer Form als View betrachten, ohne einen neuen Tensor zu erzeugen

Tensoren und das Konzept von Storage

  • In PyTorch speichert ein Tensor die eigentlichen Daten nicht direkt → die Daten werden in einem Storage verwaltet
  • Tensor = Größe + dtype + device + stride + offset
  • Mehrere Tensoren können sich einen Storage teilen → unterstützt das Konzept von Views
  • Durch die Trennung von Storage und Tensoren kann der Speicher effizient genutzt werden

Der Dispatch-Ablauf bei Tensor-Operationen

  • In PyTorch durchlaufen Operationen zwei Dispatch-Stufen:
    1. Dispatch basierend auf Device-Typ und Layout
      • Je nachdem, ob es sich um einen CPU-Tensor oder einen CUDA-Tensor handelt, wird unterschiedlicher Implementierungscode ausgeführt
    2. Dispatch basierend auf dtype
      • Je nach Datentyp wie float oder int wird ein unterschiedlicher Kernel aufgerufen

Das Tensor-Erweiterungsmodell von PyTorch

  • Die drei wichtigsten Erweiterungselemente eines Tensors:

    • Device: definiert, wie Speicher auf CPU, GPU, TPU usw. zugewiesen wird
    • Layout: definiert, wie der Tensor im Speicher abgelegt wird (z. B. zusammenhängende Speicherung, Sparse-Speicherung usw.)
    • dtype: definiert den Datentyp, der in jedem Element des Tensors gespeichert wird
  • Erweiterungsoptionen:

    • Tensoren können erweitert werden, indem der PyTorch-Code direkt geändert wird
    • Es kann eine Wrapper-Klasse geschrieben werden, die einen bestehenden Tensor kapselt
    • Wenn während der automatischen Differenzierung ein Wrapper benötigt wird, ist eine direkte Erweiterung erforderlich

Funktionsweise von Autograd

  • PyTorch führt automatische Differenzierung auf Basis von Backpropagation (reverse-mode differentiation) durch
  • Bei Forward-Operationen wird ein Graph aufgebaut → bei der Rückwärtsausbreitung wird der Graph durchlaufen und die Ableitung berechnet
  • Autograd verwaltet zusätzliche Informationen wie:
    • AutogradMeta: an einen Tensor gebundene Metadaten, die für die Rückwärtsausbreitung verwendet werden
    • Es zeichnet Operationsergebnisse auf und führt bei der Rückwärtsausbreitung die Ableitungen aus

PyTorch-Code-Struktur und Dateipfade

  • Wichtige Verzeichnisse im PyTorch-Codebestand:
    • torch/ → Python-Modul (Python-Code)
    • torch/csrc/ → Python/C++-Binding-Code, Engine für automatische Differenzierung, JIT-Compiler usw.
    • aten/ → Definitionen von Tensor-Operationen (enthält die meisten Kernoperationen)
    • c10/ → Definitionen von Core-Datenstrukturen wie Tensoren und Storage

Ablauf der Ausführung von PyTorch-Operationen

  • Beispiel: Ausführungsablauf beim Aufruf von torch.add():
    1. Umwandlung der Argumente von Python in C++-Code
    2. Dispatch in VariableType
    3. Dispatch auf Basis von Device/Layout
    4. Ausführung des finalen Kernels

Ablauf beim Schreiben von Kernels und Werkzeuge

  • In PyTorch werden Kernel in folgenden Schritten geschrieben:
    1. Erstellen von Operations-Metadaten: Funktionssignatur sowie unterstützte Devices und Datentypen definieren
    2. Validierung der Eingaben: Eingaben wie Dimensionen und Typen prüfen
    3. Allokation des Ausgabe-Tensors
    4. dtype-Dispatch: Kernel abhängig vom Datentyp ausführen
    5. Parallelisierung: auf der CPU mit OpenMP, auf CUDA mit eingebauter Parallelisierung
    6. Datenzugriff und Berechnung: Verwendung von TensorAccessor, TensorIterator usw.

Wichtige Dispatch-Makros

  • AT_DISPATCH_ALL_TYPES → führt Dispatch je nach dtype aus
  • Für verschiedene Datentypen werden Makros unterstützt → Leistungsoptimierung möglich

Tipps zur Leistungsoptimierung und effizienteren Arbeit

  • Änderungen an Header-Dateien minimieren → Änderungen führen zu einem kompletten Rebuild des Codes
  • Lokale Entwicklungsumgebung einrichten → Zeitaufwand durch Nutzung von CI minimieren
  • ccache verwenden → kann Zeit beim erneuten Kompilieren sparen
  • Leistungsstarken Server verwenden → kann Zeit bei C++-Kompilierung und CUDA-Builds sparen

Leitfaden für Beiträge zu PyTorch

  • Gute Einstiegspunkte für Beiträge:
    • Issues mit dem Label triaged → Issues, die bereits von PyTorch-Entwicklern geprüft wurden
    • Unterstützung bei der Verbesserung der Dokumentation und beim Reproduzieren von Bugs
    • Rückmeldungen zu RFCs (Funktionsvorschlägen) von PyTorch geben
  • PyTorch ist durch Open-Source-Beitragende gewachsen, und die Beteiligung der Community ist willkommen

Noch keine Kommentare.

Noch keine Kommentare.