PyTorch Internals (2019)
(blog.ezyang.com)- 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
stridewird verwendet, um logische Indizes in physische Speicherpositionen umzuwandelnstridelegt für jede Dimension Offsets fest und bestimmt die physische Speicherposition, indem der Index mit demstride-Wert multipliziert wird- Durch
stridekann 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:
- 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
- Dispatch basierend auf dtype
- Je nach Datentyp wie
floatoderintwird ein unterschiedlicher Kernel aufgerufen
- Je nach Datentyp wie
- Dispatch basierend auf Device-Typ und Layout
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():- Umwandlung der Argumente von Python in C++-Code
- Dispatch in
VariableType - Dispatch auf Basis von Device/Layout
- Ausführung des finalen Kernels
Ablauf beim Schreiben von Kernels und Werkzeuge
- In PyTorch werden Kernel in folgenden Schritten geschrieben:
- Erstellen von Operations-Metadaten: Funktionssignatur sowie unterstützte Devices und Datentypen definieren
- Validierung der Eingaben: Eingaben wie Dimensionen und Typen prüfen
- Allokation des Ausgabe-Tensors
- dtype-Dispatch: Kernel abhängig vom Datentyp ausführen
- Parallelisierung: auf der CPU mit OpenMP, auf CUDA mit eingebauter Parallelisierung
- Datenzugriff und Berechnung: Verwendung von
TensorAccessor,TensorIteratorusw.
Wichtige Dispatch-Makros
- AT_DISPATCH_ALL_TYPES → führt Dispatch je nach
dtypeaus - 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
ccacheverwenden → 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
- Issues mit dem Label
- PyTorch ist durch Open-Source-Beitragende gewachsen, und die Beteiligung der Community ist willkommen
Noch keine Kommentare.