Fortschritte bei Machine-Learning-Techniken für Machine Learning
(blog.research.google)- Je größer ML-Modelle werden, desto stärker hängt ihre Ausführungseffizienz nicht nur von der Hardware, sondern auch von Compiler-Optimierungen ab; Google Research und Google DeepMind haben einen Ansatz vorgestellt, der diesen Bereich wiederum mit ML verbessert
- Compiler-Entscheidungen unter Frameworks wie TensorFlow, JAX und PyTorch können selbst beim selben Modell Laufzeit und Ressourcennutzung erheblich verändern
- TpuGraphs ist ein Datensatz für Forschungsarbeiten zu lernbasierten Kostenmodellen; er enthält Berechnungsgraphen von ML-Programmen für TPUs, Compiler-Einstellungen und Laufzeiten
- Um die Einschränkung zu verringern, dass große Berechnungsgraphen schwer auf einmal zu trainieren sind, teilt Graph Segment Training Graphen in Segmente auf, senkt so den Speicherbedarf und verkürzt die Trainingszeit um den Faktor 3
- In einem Kaggle-Wettbewerb prüften 792 Teilnehmende in 616 Teams aus 66 Ländern praktische Techniken zur Verbesserung von Kostenprognosemodellen, darunter Graph-Kompression, Anpassung von Padding-Werten, zusätzliche Node-Features und Attention zwischen Konfigurationen
Warum ML-Compiler die Ausführungsleistung bestimmen
- Moderne ML-Modelle erledigen Aufgaben wie Sprachverständnis, Dialog, Bildgenerierung und Videogenerierung und werden mit ML-Programmierframeworks wie TensorFlow, JAX und PyTorch geschrieben und trainiert
- Frameworks stellen lineare Algebraoperationen wie Matrixmultiplikation und Faltungen sowie neuronale Netzwerkschichten wie 2D-Convolution-Layer und Transformer-Layer bereit
- Nutzer müssen sich nicht selbst um die Details kümmern, wie ein Modell effizient auf Hardware ausgeführt wird; der Compiler unterhalb des Frameworks optimiert das Modell automatisch
- Allerdings lösen Compiler komplexe Optimierungsprobleme häufig mit Heuristiken, weshalb sie nicht immer die optimale Leistung erzielen
Berechnungsgraphen und zweistufige Optimierung
- ML-Compiler wandeln mathematische Anweisungen, die Nutzer geschrieben haben, in Befehle um, die auf realer Hardware ausgeführt werden können
- ML-Programme lassen sich als Berechnungsgraphen darstellen
- Nodes stehen für Tensoroperationen wie matrix multiplication
- Edges stehen für Tensoren, die von einem Node zu einem anderen fließen
- Compiler-Optimierungen lassen sich grob in zwei Arten einteilen
- Optimierung auf Graph-Ebene: trifft Entscheidungen im Kontext des gesamten Graphen und transformiert den gesamten Graphen
- Optimierung auf Kernel-Ebene: transformiert einen einzelnen Kernel, also einen fused subgraph, unabhängig von anderen Kerneln
Performance-Trade-offs beim Speicherlayout
- 2D-Tensoren wie Matrizen können im Speicher in der Form
[A B C a b c]oder[A a B b C c]abgelegt werden; dies entspricht jeweils Row-Major- und Column-Major-Layout - Eine wichtige Optimierung von ML-Compilern besteht darin, allen Zwischentensoren eines Programms ein Speicherlayout zuzuweisen
- Ein bestimmtes Layout kann für eine einzelne Operation am effizientesten sein, doch wenn die Layouts zwischen add und convolution nicht zusammenpassen, muss der Compiler zusätzliche Copy-Operationen einfügen
- Umgekehrt kann eine Konfiguration, die bei einzelnen Operationen etwas weniger performant ist, in der Gesamtausführung besser sein, wenn keine Layout-Transformation nötig ist
- In der XLA Benchmark Suite wurde bei der Wahl optimaler Layout-Konfigurationen statt der Standard-Compiler-Einstellungen eine Beschleunigung von bis zu 32 % beobachtet
TpuGraphs-Datensatz
- TpuGraphs ist ein Datensatz für lernbasierte Kostenmodelle für Programme, die auf Googles kundenspezifischen TPUs ausgeführt werden
- Ziel ist es, ein Kostenmodell zu trainieren, das ein Eingabeprogramm und Compiler-Einstellungen entgegennimmt und die Laufzeit des Programms vorhersagt
- Der Datensatz deckt zwei XLA-Compiler-Einstellungen ab
- layout: eine Einstellung, die das Konzept von Row-Major und Column-Major bei Matrizen auf höherdimensionale Tensoren verallgemeinert
- tiling: Einstellungen für die Kachelgröße
- Jedes Beispiel enthält den Berechnungsgraphen eines ML-Workloads, Compiler-Einstellungen und die Laufzeit bei Kompilierung mit diesen Einstellungen
- Die Graphen wurden aus Open-Source-ML-Programmen gesammelt und umfassen Modellarchitekturen wie ResNet, EfficientNet, Mask R-CNN und Transformer
- Download-Anleitung und Einstiegscode sind auf TpuGraphs GitHub verfügbar
- TpuGraphs enthält 25-mal mehr Graphen als der bisher größte Datensatz für Graph Property Prediction mit ähnlicher Graphgröße, und die durchschnittliche Graphgröße ist 770-mal größer als bei bisherigen Datensätzen zur Performance-Vorhersage von ML-Programmen
Baseline-Kostenmodell und GNN-Architektur
- TpuGraphs liefert ein lernbasiertes Baseline-Kostenmodell mit; da das Eingabeprogramm als Graph dargestellt wird, nutzt es ein GNN
- Node-Features bestehen aus zwei Teilen
- opcode id: die wichtigste Node-Information, die die Art der Tensoroperation angibt
- weitere Node-Features
- Das Baseline-Modell wandelt die opcode id über eine Embedding Lookup Table in ein opcode embedding um
- Das opcode embedding wird mit den übrigen Node-Features kombiniert und als Eingabe für das GNN verwendet
- Die vom GNN erzeugten Node-Embeddings werden mit einfachen Graph-Pooling-Reduktionen wie sum und mean zu einem Graph-Embedding fester Größe kombiniert
- Das finale Graph-Embedding wird über eine Feedforward Layer in eine einzelne skalare Ausgabe umgewandelt
Große Graphen mit Graph Segment Training trainieren
- Graph Segment Training ist eine Technik zur Skalierung des GNN-Trainings, um große Graphen auf Geräten mit begrenztem Speicher zu verarbeiten
- Die Methode zielt auf Graph-Level-Prediction-Szenarien ab, bei denen nicht Nodes oder Edges, sondern der gesamte Graph vorhergesagt wird
- Berechnungsgraphen können Hunderttausende Nodes enthalten, sodass Full Graph Training, bei dem der gesamte Graph auf einmal verwendet wird, rechnerisch unmöglich sein kann
- GST teilt große Graphen in kleinere Segmente auf und aktualisiert das Modell nur anhand einer zufälligen Teilmenge der Segmente
- Für die übrigen Segmente werden Embeddings erzeugt, ohne Zwischen-Aktivierungen zu speichern, wodurch der Speicherbedarf sinkt
- Alle Segment-Embeddings werden kombiniert, um das Embedding des ursprünglichen großen Graphen zu erstellen; dieses wird für die Vorhersage genutzt
- Zusammen mit einer historical embedding table und segment dropout wird die Staleness historischer Embeddings abgeschwächt
- Insgesamt verkürzt die Methode die End-to-End-Trainingszeit um den Faktor 3
Im Kaggle-Wettbewerb geprüfte Verbesserungstechniken
- Der Kaggle-Wettbewerb Fast or Slow? Predict AI Model Runtime basierte auf dem TpuGraphs-Datensatz; 792 Teilnehmende in 616 Teams aus 66 Ländern nahmen teil
- Es gab 10.507 Einreichungen; für 153 Personen war es der erste Kaggle-Wettbewerb, und 47 von ihnen kamen unter die Top 100
- Die teilnehmenden Teams experimentierten mit mehreren Techniken
- Graph Pruning und Compression: Statt GST wurden Methoden zur Komprimierung großer Graphen erprobt; verwendet wurde ein Ansatz, der nur den Subgraphen mit konfigurierbaren Nodes und deren unmittelbaren Nachbarn beibehält
- Änderung des Padding-Werts: Da der Standard-Padding-Wert 0 mit gültigen Feature-Werten kollidiert, konnte die Verwendung von -1 die Modellgenauigkeit deutlich verbessern
- Zusätzliche Node-Features und geänderte Kodierung: Zusätzliche Node-Features wie die contracting dimensions von dot general sind wichtig, und auch die Kodierungsweise von Node-Features kann das Ergebnis beeinflussen
- Cross-Configuration Attention: Das Siegerteam entwarf eine einfache Layer, mit der das Modell Konfigurationen explizit vergleichen kann; dies lieferte deutlich bessere Ergebnisse als ein Ansatz, der jede Konfiguration einzeln inferiert
- Die Wettbewerbsergebnisse und die Gewinnerlösung sollten am 16. Dezember 2023 in der Wettbewerbs-Session des ML for Systems workshop auf der NeurIPS behandelt werden
Session zur NeurIPS Expo
- Für Leser, die sich für strukturierte Daten und KI-Forschung interessieren, fand am 9. Dezember 2023 das NeurIPS-Expo-Panel Graph Learning Meets Artificial Intelligence statt
- Dieses Panel behandelt unter anderem Fortschritte bei lernbasierten Kostenmodellen
1 Kommentare
Meinungen auf Hacker News
ML-Compiler werden überbewertet. Es ist derselbe Kompromiss wie bei klassischen Compilern: Man bekommt viel mehr Durchsatz, als wenn man spezialisierte Performance-Programmierer einstellt, aber Letztere sind normalerweise deutlich schneller und können in manchen Fällen um Größenordnungen vorn liegen.
Auf mehreren Ebenen unzureichend. Auf algorithmischer Ebene geben sie dem Menschen keine Tricks zurück, wie man das Netzwerk schneller machen kann, sondern nur sehr grundlegende Signale. Auch die Intention geht verloren. ML-Netzwerk-Designer spezifizieren die Struktur in Python, aber nach mehreren Stufen der Herabtransformation kann etwas völlig anderes herauskommen. Kürzlich habe ich gesehen, wie ein Compiler bei einem Slice-Update alle möglichen Indexbereiche eines Arrays erzeugte, diese dann zuschnitt, um die zu aktualisierenden Indizes zu erhalten, und anschließend einen Scatter ausführte; ich habe das durch einen einzigen
memcpy-Aufruf ersetzt. Auch die Kernel sind ineffizient. Jedes Mal, wenn solcher Compiler-Output gegen einen erfahrenen Assembly-Programmierer antritt, verliert der Compiler, meist um 30 % oder mehr. Es sieht so aus, als ließe sich das leicht lösen, aber wenn es in den letzten 50 Jahren niemand richtig geschafft hat, ist es offensichtlich nicht so einfach, wie es klingt.Auch Compiler haben viele Heuristiken wie Inlining, Loop Unrolling und Vektorisierung; neuronale Netze könnten also helfen und womöglich leichter zu warten sein als die vielen von Menschen geschriebenen Heuristiken.
Solche Optimierungen sind immer noch viel besser, als gar keine zu haben.
Kann das jemand etwas realistischer erklären? Mich interessiert, wie der tatsächliche Stand heutiger ML-Compiler ist und was man in naher Zukunft erwarten kann.
Man schreibt einfach
model = torch.compile(model). „Über diese 163 Open-Source-Modelle hinweg funktionierte torch.compile in 93 % der Fälle und beschleunigte das Training auf NVIDIA-A100-GPUs um 43 %. Bei Float32-Präzision lag die durchschnittliche Beschleunigung bei 21 %, bei AMP-Präzision bei 51 %.“[1] Google scheint zu versuchen, mehr Leute in die Forschung und Entwicklung solcher Methoden einzubeziehen.[1] https://pytorch.org/get-started/pytorch-2.0/
Die Realität ist kompliziert, aber stark vereinfacht gesagt kompiliert man den Berechnungsgraphen in eine Zwischenrepräsentation und implementiert dafür passende Backends. Relevante Projekte sind stableHLO, IREE und openXLA. Auch der
jit-Compiler von Jax kann als eine Form eines solchen Compilers betrachtet werden. Er senkt die getraceten Operationen nach XLA ab, und XLA vollführt dann allerlei Magie, damit sie auf dem Backend laufen. Letztlich sind es nach unten hin immer weiter Transformationen und Abstraktionen.torch.compilean.Zusammengefasst geht es darum, die Laufzeit-Performance-Prognose von Berechnungsgraphen mit Graph Neural Networks (GNNs) zu verbessern. Verwendet werden ein Embedding-Wörterbuch für den Opcode jedes Knotens sowie weitere Knotenmerkmale wie Shape, Bits und Window Size ([1]).
In [2] wurde ein großer Graph-Datensatz mit verschiedenen XLA-Compiler-Einstellungen und der daraus resultierenden Performance auf TPUs veröffentlicht; in [3] verbessern sie mit einer Methode zur Graph-Partitionierung (METIS graph partition sehe ich zum ersten Mal) und verschiedenen Lernverfahren die Vorhersage für größere Graphen gegenüber früheren Ansätzen. Hier geht es darum, die Performance eines gegebenen Graphen vorherzusagen, nicht darum, neue äquivalente Graphen zu verbessern, vorzuschlagen oder zu verändern. Wie bei FunSearch kann ein Modell mit ordentlicher Vorhersagekraft zusammen mit evolutionärer Suche verwendet werden.
[1] https://github.com/google-research-datasets/tpu_graphs#featu...
[2] TpuGraphs: A Performance Prediction Dataset on Large Tensor Computational Graphs https://arxiv.org/abs/2308.13490
[3] Learning Large Graph Property Prediction via Graph Segment Training https://arxiv.org/abs/2305.12322
Kann jemand erklären, wie Convolution in diesem Graphen funktioniert? Ein Tensor mit Shape
[2,4,16]wird mit einem Kernel der Shape[4,16,8]gefaltet und es kommt ein Tensor[2,8]heraus – wie ist das möglich?[2,4,16]kann2als Batch-Größe,4als Eingabe-Feature-Dimension und16als Eingabe-Channel-Dimension verstanden werden.Beim Kernel
[4,16,8]ist4die Größe des Filterfensters,16entspricht der Eingabe-Channel-Dimension, und8ist die Ausgabe-Channel-Dimension. In der Ausgabe[2,8]bleibt2die Batch-Größe, und8entspricht der Ausgabe-Channel-Dimension des Kernels. Oberflächlich sehen die Dimensionen unpassend aus, aber Convolution in einem Graphen nutzt die Nachbarschaftsstruktur. Der Kernel bewegt sich über den Graphen, wendet Gewichte auf den aktuellen Knoten und die Nachbar-Features innerhalb eines bestimmten Radius an und sammelt die gewichteten Summen, um neue Features für jeden Ausgabe-Channel zu erzeugen. Auch Graphstruktur, Kantengewichte sowie Implementierungsdetails wie Padding und Stride können die Output-Shape beeinflussen.Wie ist der Stand bei Gemini?
Allein spontan fallen mindestens fünf Foundation Models ein – Llama, Claude, Gemini, Falcon, Mistral –, die sich gegenseitig überholen, aber GPT ist weiterhin eine Stufe darüber, und das seit einem Jahr. Es hat sich gezeigt, dass Transformer-basierte große Sprachmodelle einfach genug sind, dass sie jeder bauen kann, der etwa eine Million Dollar für GPU-Zeit ausgeben kann, aber ganz zu OpenAI aufschließen konnten sie nicht. Was ist ihr besonderes Geheimnis?
Wie sieht es mit Transformer selbst aus? Gibt es Hinweise darauf, in welchem Sinne sie optimal sind?
Ich habe das Gefühl, dass im ersten Absatz der Kernpunkt vergraben wurde, aber der Rest ist großartig.
Das aktuelle Tempo des ML-Fortschritts ist erstaunlich. Ich glaube nicht an die Singularität, aber es verändert Software und Gesellschaft auf eine Weise, die niemand vorhersagen kann.
https://deepmind.google/discover/blog/funsearch-making-new-d...