- Ein Experiment, das einen decoder-only Transformer ähnlich wie GPT-2 erstellt, indem die Gewichte ohne Training direkt von Hand festgelegt werden, damit er das Muster
(aab)*vorhersagt - Die Vorhersage von
"aabaabaabaab..."muss die zwei vorherigen Tokens betrachten und macht das Verhalten von Attention dadurch besser sichtbar als ein einfaches alternierendes Muster - Das Modell ist klein mit
N_CTX=5,N_VOCAB=2,N_EMBED=8, verwendet die Tokenisierunga=0,b=1und One-Hot-Embeddings - Ein einzelner Attention-Head verteilt je 0,5 Aufmerksamkeit auf die letzten zwei Tokens und berechnet das nächste Token mithilfe der Aufhebung in der Kodierung
a=1,b=-1 - In nicht mehrdeutigen Kontexten erreicht es 100,0 % (27/27) Genauigkeit, benötigt aber für eine Vorhersage mit 5-Token-Kontext etwa 4.000 FLOPs und ist damit weit ineffizienter als eine direkt implementierte Regel
Mini-GPT-2 mit manuell gesetzten Gewichten ohne Training
- Das Ziel ist, intuitiv zu verstehen, was die einzelnen Bestandteile von Transformer und Attention tatsächlich tun
- Das Modell wird nicht trainiert und nutzt auch keine vortrainierten Gewichte, sondern entsteht dadurch, dass an einem Abend jedes Gewicht direkt von Hand festgelegt wird
- Die Struktur ist ein decoder-only Transformer ähnlich wie GPT-2, und die Implementierung ist eine vereinfachte Variante auf Basis der picoGPT-Implementierung von jaymody
- Layer Normalization entfernt
- ein einzelner Head statt Multi-Head-Attention
- die
mlp-Feed-Forward-Layer des Transformer-Blocks entfernt
Warum die Sequenz (aab)* gewählt wurde
- Zunächst sollte eine Sequenz wie
"ababababab"vorhergesagt werden, aber weil der Transformer eine verschobene Sequenz vorhersagt, wäre das zu einfach gewesen- Wenn
a, dannb, sonstavorhersagen reicht aus, ohne Positions-Embeddings zu benötigen
- Wenn
- Die endgültige Aufgabe wurde die Vorhersage von
"aabaabaabaab...", also der Sequenz(aab)*- Wenn die beiden vorherigen Tokens
aboderbasind, ist das nächste Tokena - Wenn die beiden vorherigen Tokens
aasind, ist das nächste Tokenb bbwird als Fall außerhalb des Aufgabenbereichs behandelt
- Wenn die beiden vorherigen Tokens
- Die Tokenisierung ist einfach und behandelt nur zwei Symbole
aist0bist1
Modelldimensionen und Rechenablauf
- Es wurden drei Modellparameter gewählt
N_CTX = 5: maximale Kontextlänge, die das Modell auf einmal siehtN_VOCAB = 2: die zwei TokensaundbN_EMBED = 8: Embedding-Größe für Token, Position und Rechenraum
- Für die eigentliche Aufgabe werden zwar nur die zwei vorherigen Tokens benötigt, aber
N_CTX=5schließt absichtlich den Fall ein, irrelevante Tokens ignorieren zu müssen - Die Funktion
gptarbeitet in folgender Reihenfolge- Sie addiert Token-Embedding und Positions-Embedding mit
wte[inputs] + wpe[range(len(inputs))] - Sie durchläuft einen Transformer-Block
- Am Ende erzeugt
x @ wte.Tdie Logits im Vokabularraum
- Sie addiert Token-Embedding und Positions-Embedding mit
Embeddings mit One-Hot für Position und Token
wpeist das Positions-Embedding; die ersten 5 Embedding-Dimensionen werden als One-Hot-Positionen verwendet- Position 0 ist
[1, 0, 0, 0, 0] - Position 4 ist
[0, 0, 0, 0, 1]
- Position 0 ist
wteist das Token-Embedding; die nächsten 2 Dimensionen werden als One-Hot-Token verwendet- Token
aist in den Token-Dimensionen[1, 0] - Token
bist[0, 1]
- Token
- Die 8. Embedding-Position wird anfangs nicht benutzt und dient innerhalb des Transformer-Blocks als Scratch Space
- Zum Beispiel wird
"aabaa"als5 x 8-Embedding-Matrix dargestellt, wobei jede Zeile One-Hot für Position und Token gemeinsam enthält
Wie der Attention-Head die letzten zwei Tokens auswählt
- Der Transformer-Block besteht aus einem Attention-Head und einer linearen
c_proj-Schicht, die das Attention-Ergebnis zurück in den Embedding-Raum projiziert c_attnist eine lineare Schicht der Größeembed_size x (embed_size * 3), die das Eingabe-Embedding in eineqkv-Matrix umwandelt und dann inq,k,vaufteiltktrennt die Positions-Embeddings heraus und repräsentiert damit die Positionsinformation jedes Tokensqbeschreibt für jede Position den gesuchten Positionsbereich; mitq @ k.Tentsteht die Matrix der Attention-Scores- Nach Softmax und Causal Mask hat die Attention-Matrix folgende Eigenschaften
- Die erste Zeile richtet 100 % Attention nur auf das erste Token
- Die folgenden Zeilen richten jeweils
0.5Attention auf die zwei zuletzt erreichbaren Tokens
- Die Causal Mask fügt an zukünftigen Token-Positionen einen sehr kleinen Wert hinzu, im eigentlichen Code etwas wie
-1e10, sodass keine zukünftigen Tokens gesehen werden können- Dieses handgebaute Modell ist zwar nicht darauf ausgelegt, in die Zukunft zu spähen, aber die Maske bleibt erhalten, um näher an der GPT-2-Struktur zu bleiben
- Das Scaling durch Division mit
np.sqrt(q.shape[-1])hilft im echten Training bei besseren Gradienten, hat auf dieses handgefertigte Modell aber keinen Einfluss
Vorhersage durch v-Kodierung und additive Aufhebung
vwandelt das One-Hot-Token in die Kodierunga=1,b=-1um- Weil das Attention-Ergebnis die letzten zwei Tokens mit je 0,5 mittelt, berechnet diese Kodierung die folgende Regel
a, b→0.5 * 1 + 0.5 * (-1) = 0b, a→0.5 * (-1) + 0.5 * 1 = 0a, a→0.5 * 1 + 0.5 * 1 = 1
- Dadurch entsteht an der 7. Position jeder Zeile der folgende Wert
0, wennavorhergesagt werden soll1, wennbvorhergesagt werden soll
- Beim Eingabekontext
"aabaa"kann die erste Zeile wegen fehlender Information eine Vorhersageberzeugen, aber die folgenden Vorhersagen stimmen mit der Regel(aab)*überein
Die Vorhersage zurück in den Vokabularraum schicken
c_projwandelt den Wert an der 7. Position des Attention-Ergebnisses zurück in das One-Hot-Format der Tokens- Es erzeugt nicht einfach
[..., 1, 0, ...]oder[..., 0, 1, ...], sondern ein mit1024skaliertes One-Hotembedding[row, 5] = 1024 + (-1024) * predictionembedding[row, 6] = 0 + 1024 * prediction
- Der Transformer-Block hat eine Residual Connection, also
x = x + causal_self_attention(...), wodurch das ursprüngliche Embedding addiert wird - Weil dieses Residualsignal unnötig erhalten bleibt, wird die Skalierung
1024verwendet, um es zu überdecken - Schließlich werden mit
x @ wte.Tdie Logits berechnet und Softmax angewendet- Im Kontext
"aabaa"zeigt die letzte Vorhersagezeile aufb - Während des Trainings sind Vorhersagen für alle Zeilen nützlich, bei der Inferenz wird aber nur die letzte Zeile benötigt
- Im Kontext
Generierungsergebnisse und Genauigkeit
- Die Funktion
completegibt die letzten maximal 5 Tokens in das Modell und wählt das nächste Token mitargmaxaus der letzten Zeile des Softmax-Ergebnisses - Beispielhafte Generierungsergebnisse sind
complete("a")→a :: baabaabaabcomplete("ba")→ba :: abaabaabaacomplete("abaab")→abaab :: aabaabaaba
- Auch bei Eingaben außerhalb des Aufgabenbereichs stellt sich teils wieder ein Wiederholmuster ein
complete("ababa")→ababa :: abaabaabaacomplete("bbbbb")→bbbbb :: aabaabaaba
- Im Test mit
"aab" * 10beträgt die Genauigkeit 100,0 % (27/27), wenn nur nicht mehrdeutige Kontexte bewertet werden
4.000 FLOPs versus 8 Instruktionen
- Wenn der gesamte 5-Token-Kontext genutzt wird, benötigt dieses Modell für die Vorhersage eines einzelnen Tokens etwa 4.000 Floating-Point-Operationen
- Der Großteil entfällt auf die Attention-Berechnung
- Das lässt sich durch ein kleineres Kontextfenster, fused multiply-add, KV-Caching usw. reduzieren
- Trotzdem werden für die Vorhersage eines einzelnen Tokens noch Hunderte Maschineninstruktionen benötigt
- Dieselbe
(aab)*-Regel kann in handgeschriebenem x64-Assembler das nächste Token mit 8 Instruktionen berechnen - Offen bleibt die Frage, ob sich für natürliche Sprachgenerierung ein Sprachmodell trainieren lässt, das 1000-mal effizienter ist als heutige Modelle
1 Kommentare
Meinungen auf Hacker News
Als verwandte Arbeit gibt es „Thinking Like Transformers“.
Darin wird eine primitive Programmiersprache namens RASP vorgestellt, die aus Operationen besteht, die sich mit Transformer-Komponenten modellieren lassen, und es wird gezeigt, dass man damit Programme wie Histogramme oder Sortierungen schreiben kann.
Außerdem gibt es einen hervorragenden Blogbeitrag von Sasha Rush und Gail Weiss; Folgearbeiten haben gezeigt, dass sich RASP-artige Programme ohne Training in echte Modellgewichte kompilieren lassen.
[1] https://arxiv.org/abs/2106.06981
[2] https://srush.github.io/raspy/
[3] https://arxiv.org/abs/2301.05062
Wenn dieses Gebiet spannend klingt, lohnt sich vielleicht auch ein Blick auf meine Arbeit HandCrafted Transformers, bei der ich die Gewichte eines Transformer-Modells von Hand ausgewählt habe, damit es schriftliche Addition ausführt – ungefähr so, wie Menschen es in der Grundschule lernen.
[1] https://colab.research.google.com/github/newhouseb/handcraft...
Auch die Richtung vom neuronalen Netz hin zu Code dürfte unter dem Aspekt der Erklärbarkeit sehr interessant sein.
Ich dachte, ich würde Transformer gut verstehen, hatte aber nie selbst einen implementiert.
Als ich eines Tages selbst einen implementierte, funktionierte oder trainierte er nicht so gut wie der standardmäßige PyTorch Transformer; am Ende merkte ich, dass ich Dropout ignoriert hatte.
Ich hatte ihn auf Zahlenaddition trainiert und nie dasselbe Paar zweimal gezeigt, also dachte ich, Overfitting sei unmöglich – aber die Rolle von Dropout war deutlich größer, als ich erwartet hatte.
Kurz gesagt: Es lohnt sich, einfach selbst einen Transformer zu implementieren, und je mehr von Grund auf, desto besser.
Alle, die das gemacht haben, haben etwas Unerwartetes gelernt; je nach Person reichte das von der Parallelisierung des tokenweisen Trainings bis dazu, wie Backpropagation tatsächlich funktioniert.
Die Materialien von Karpathy sind auch gut, aber dieses Video hat bei mir den Knoten platzen lassen, was Transformer angeht: https://youtu.be/kWLed8o5M2Y?si=SJT5_lCJ0hSR7Z_k
Ich habe schon eine Weile über etwas Ähnliches nachgedacht.
Könnte man eine intuitive Schnittstelle zu Modellgewichten bauen, über die Domain-Experten von Hand Anpassungen vornehmen und so das Training beschleunigen können?
Zum Beispiel könnte man bei einem Vision-Modell zur Erkennung von Verkehrskegeln ein Bündel von Gewichten hochdrehen, das „Orangehaftigkeit“ entspricht.
Dann könnte ein Mensch beschleunigend eingreifen, statt Tausende oder Millionen weiterer Beispiele zu verlangen, nur um „Orangehaftigkeit“ richtig zu kalibrieren.
Die Schwierigkeit ist natürlich, dass diese Schnittstelle auf Gewichtsbündel mit unterschiedlichen Bedeutungen abgebildet werden müsste; ich frage mich, ob es technische Gründe gibt, warum das unmöglich ist.
[1] https://www.youtube.com/watch?v=8SF_h3xF3cE&t=1358s
Kurz gesagt: Menschliche Hilfe bei KI ist fast immer weniger kosteneffizient, als einfach mehr Rechenleistung darauf loszulassen.
Während ein Mensch eine Gewichtsschicht so kalibriert, dass orangefarbene Verkehrskegel erkannt werden, trainiert der GPU-Cluster die KI bereits darauf, Verkehrskegel, Ampeln, Bäume, andere Autos und sogar Verkehrskegel in leicht anderen Orangetönen zu erkennen.
Selbst wenn man das Modell so anpasst, dass es Orange besser sieht, kann man, wenn man nicht gleichzeitig die Genauigkeit bei allen anderen Farben überwacht, unbemerkt Probleme bei anderen Farben erzeugen.
Es ist nicht so, dass ein bestimmter Neuronen-Cluster einem bestimmten Konzept entspricht; vielmehr macht im Großen und Ganzen alles ein bisschen von allem.
Es sind zu viele Parameter beteiligt.
Das Transformer-Paper war so technisch, dass ich es immer zumindest oberflächlich verstehen wollte, es aber schwierig fand.
Dieser Artikel hat mir wirklich geholfen, die Funktionsweise zu verstehen, und zumindest die Beispiele waren sehr klar.
Dadurch konnte ich mich auch wieder an die Matrizen erinnern, die ich an der Uni gelernt hatte.
Ist das nicht eine Art abstrakte Maschine, wie eine Turing-Maschine oder eine Maschine, die reguläre Ausdrücke parst?
Zuerst definiert man eine Liste von Token; sagen wir der Einfachheit halber zum Beispiel 24 Zeichen.
Diese Maschine nimmt eine Eingabesequenz von Token, führt deterministische Matrixoperationen aus und gibt anschließend eine Wahrscheinlichkeitsliste für alle Token aus.
„Training“ ist lediglich der Prozess, einige Zahlen in den Matrizen festzulegen, die bei diesen Operationen verwendet werden.
Bemerkenswert ist, dass der finale Code nur eine einzige
if-Anweisung enthält, und selbst die dient dazu, die Genauigkeit des Ergebnisses zu bewerten.Die gesamte „Logik“ entsteht aus dem Ergebnis von Matrixoperationen.
In neuronalen Netzen ist im Allgemeinen alles etwas unscharf, und Dinge wie
if/elsegibt es kaum, auch wenn in Beispielen wie Transformern Werte auf 0 oder -∞ „maskiert“ werden.Auch die Ausgabe ist fast immer ein Bündel von Scores oder Wahrscheinlichkeiten; wenn ein Modell, das Katzen- und Hundefotos unterscheidet, etwas wie
dog:0.95 cat:0.05ausgibt, sagt man, es habe einen Hund vorhergesagt, weil der Hund-Score höher ist.Der Attention-Mechanismus, der Kern des Transformers, basiert auf einer Art weicher Lookup-Operation.
In einem nicht-unscharfen System würde man über jedes Token der Sequenz iterieren, prüfen, ob es mit dem aktuellen Token zusammenhängt, und bei Relevanz eine bestimmte Aktion ausführen; im Transformer ist Relevanz jedoch keine binäre Entscheidung.
Stattdessen berechnet er kontinuierliche Relevanz-Scores zwischen allen Token-Paaren in der Sequenz und nutzt diese Scores für den nächsten Schritt.
Allerdings lassen sich manche Dinge nicht direkt aus Systemen verallgemeinern, die auf binären Entscheidungen beruhen.
Zum Beispiel werden solche Relevanz-Scores als Gewichte verwendet, um einen gewichteten Durchschnitt über Vokabular-Token zu berechnen, wodurch man für die aktuelle Position ein „Durchschnitts-Token“ erhält.
Ich sehe keinen einfachen Weg, das als Erweiterung eines auf Verzweigungslogik basierenden Prozesses zu interpretieren.
Dieses Paper, das erklärt, dass Linear Transformers eigentlich Fast Weight Programmers sind, ist dazu lesenswert: https://arxiv.org/abs/2102.11174
Wenn man die Gewichte sorgfältig setzt, kann man sie dazu bringen, beliebige Berechnungen auszuführen.
Allerdings wäre ein Compiler schön, der nicht auf Approximation basiert.
Ich frage mich, wofür der Satz „man könnte Lust bekommen, selbst ein Modell zu bauen“ gedacht ist, außer als Lernübung zur Befriedigung der Neugier.
Komplexe Machine-Learning-Modelle wirken langsam unrealistisch für jemanden, der zu Hause Blogbeiträge liest.
Es erzeugt viele plausibel wirkende altenglische Wörter und lernt die Grundlagen der englischen Grammatik sowie das Format von Theaterstücken usw.
Ich fand es ziemlich erstaunlich, in so kurzer Zeit so weit zu kommen.
Wenn man lokal mehrere Modelle auf ein Treue-Niveau von Shakespeare-from-Wish.com trainiert, dürfte das helfen zu beurteilen, ob man eine gute Architektur gefunden hat und ob es Zeit ist, sie zu skalieren.
Dort steht, dass der Autor Transformer und Attention besser verstehen wollte und zwar The Illustrated Transformer gelesen hatte, aber nicht intuitiv nachvollziehen konnte, was die einzelnen Teile von Attention tatsächlich tun.
Etwa der Unterschied zwischen
qundk, ganz zu schweigen vonv.Sie geht über das bloße Befriedigen von Neugier hinaus und hilft, Verständnis aufzubauen und zu vertiefen.
Es wäre gut, wenn im Titel ein Ausdruck wie neural network vorkäme.
Hier geht es um die „Transformer“-Architektur im Machine Learning, nicht um ein Spulenbündel, das zwei Schaltungen elektromagnetisch koppelt.