Skip to main content
Entwickler Themen
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

0.1.3. Namensgenerator

Worum es geht

cmd/namegenerator trainiert dasselbe Modell auf einer Liste von Vornamen und schlägt danach Fortsetzungen zu einem eingegebenen Anfang vor. Vorwärtspass, Verlust und Ziehung sind dieselben wie im Referenzlauf. Der Unterschied liegt im Umfang des Trainings und darin, dass die Stichprobe auf einen Anfang wartet.

Aufruf

Aus dem Verzeichnis MinimalesLLM:

go run ./cmd/namegenerator

Ohne Flags gelten die Vorgaben: Datei names.txt, eine Epoche, eine Schicht, Temperatur 0.5, und ein Seed, den das Programm selbst zieht. Diesen Seed druckt es vor dem Training auf die Standardfehlerausgabe. Mit dem gedruckten Wert lässt sich derselbe Lauf wiederholen.

go run ./cmd/namegenerator -input names.txt -epochs 3 -layers 2 -seed 1
Flag Vorgabe Wirkung
-input names.txt Eine Zeile ist ein Name. Leerzeilen entfallen.
-epochs 1 Wie oft die ganze Liste trainiert wird. Werte unter 1 sind ein Fehler.
-layers 1 Anzahl der Transformer-Blöcke. Werte unter 1 sind ein Fehler.
-seed zufällig Initialisierung, Mischen und die Ziehungen danach.

Fehlt -seed, liest das Programm 8 Bytes aus dem Zufall des Betriebssystems. Jeder Start ohne dieses Flag bekommt einen anderen Seed. Gewichte, Reihenfolge der Namen und die späteren Vorschläge fallen dann anders aus. Dieselbe Datei und dieselben übrigen Flags genügen nicht, um die Vorschläge festzuhalten. Erst ein gesetzter -seed, auch 0, macht den Lauf wiederholbar. minimalesllm verhält sich hier anders: dort ist der Seed fest 1.

Einbettungsbreite 16, Kontextfenster 16, vier Attention-Köpfe, Aufweitung 4 und die Adam-Koeffizienten bleiben die Referenzwerte. Die Temperatur bleibt 0.5 und ist kein Flag.

Was vor der ersten Eingabe geschieht

  1. token.Ensure sucht die Namensdatei. Liegt sie schon da, bleibt sie unverändert. Fehlt sie, wird die englische Namensliste geladen, die auch minimalesllm für input.txt verwendet.
  2. Jede nicht leere Zeile wird ein Dokument. token.Build sortiert die vorkommenden Zeichen und hängt BOS dahinter. Die IDs hängen nur an dieser Datei.
  3. Aus dem Seed entsteht ein PCG-Generator. model.New zieht daraus jedes Gewicht. Die Schichtzahl kommt aus -layers, die übrige Form aus model.Reference.
  4. Jede Epoche mischt die Liste neu und trainiert dann jeden Namen genau einmal. Ein Schritt ist ein Name. Der Name wird als BOS, Buchstaben, BOS kodiert. An Position i sieht das Modell nur die Token bis einschließlich i, das Ziel ist das Token an i+1. Der Verlust ist der Mittelwert der negativen Logarithmen dieser Zielwahrscheinlichkeiten. Danach schreibt ein Adam-Schritt alle Gewichte um. Die Lernrate beginnt bei 0,01 und fällt linear über alle Updates, also über Epochen × Anzahl der Namen.
  5. Alle tausend Namen und am Ende jeder Epoche erscheint auf der Standardfehlerausgabe eine Zeile mit dem Verlust des gerade trainierten Namens. Dieser Wert liegt am Anfang nahe bei ln(V) und soll im Trend sinken.

Vor dem Training druckt das Programm Seed, Namenzahl, Schichtzahl und Parameterzahl. Danach steht dort:

Anfang eingeben und mit Enter bestätigen. Strg-C oder Strg-D beendet.

Strg-C beendet das Programm auch während des Trainings.

Die mitgelieferte names.txt hat 500 Namen. Ihre Zeichen sind Kleinbuchstaben einschließlich ä, ö und ü. q kommt darin nicht vor. Das Vokabular ist deshalb nicht die 27 IDs des Referenzlaufs auf input.txt, und die Parameterzahl ist nicht 4192. Beide Zahlen stehen in der Zeile vor dem Training. Fehlt die Datei und wird die englische Liste geladen, entstehen die IDs neu aus dieser Liste.

Bei 500 Namen ist das Ende der Epoche früher erreicht als die Marke von tausend Dokumenten. Die Verlustzeile erscheint dann einmal je Epoche, in der Form Epoche 1/1 Dokument 500/500 Verlust ….

Die Ziehung, Zeichen für Zeichen

Nach dem Training liest das Programm die Standardeingabe. Ein Prompt > steht auf der Fehlerausgabe, der vorgeschlagene Name auf der Standardausgabe. Strg-D schließt die Eingabe. Strg-C bricht den Prozess ab.

Die eingegebene Zeile wird an den Rändern beschnitten und in Kleinbuchstaben gesetzt. Sie ist der Anfang. Die Folge, die das Modell sieht, ist BOS und danach diese Zeichen. Das öffnende BOS wird nicht gedruckt. Der Vorschlag beginnt mit dem eingegebenen Anfang. Eine leere Zeile hat nur BOS als Anfang, genau wie eine Stichprobe im Referenzlauf.

sample.Complete füllt zuerst den Cache mit dem Anfang, ohne diese Zeichen neu zu ziehen. Danach beginnt die eigentliche Ziehung beim letzten schon bekannten Token:

  1. Forward liefert für die aktuelle Position einen Logit je Token-ID, also je Buchstabe und für BOS.
  2. Jeder Logit wird durch die Temperatur 0.5 geteilt. Diese Zahl ist eine Konstante, kein trainiertes Gewicht. Die Teilung durch einen Wert unter 1 macht die Verteilung spitzer als der Softmax im Trainingsverlust.
  3. Der Softmax wandelt die verschobenen Logits in Wahrscheinlichkeiten um. Sie summieren sich zu 1.
  4. draw zieht eine gleichverteilte Zahl aus dem Seed-Generator und geht die kumulierten Wahrscheinlichkeiten entlang, bis die laufende Summe diese Zahl überschreitet. Die ID, in deren Fach die Zahl liegt, ist das nächste Zeichen. Das ist die einzige Zufallsentscheidung an dieser Stelle.
  5. Ist die ID BOS, endet der Name. BOS wird nicht angehängt. So kann ein Vorschlag genau so lang sein wie der eingegebene Anfang: das Modell hat das Ende gezogen.
  6. Sonst wird das Zeichen angehängt, wird zur nächsten Eingabe, und die Position steigt um eins.
  7. Die Schleife endet auch, wenn Position 16 nötig würde. Das Kontextfenster hat die Positionen 0 bis 15. Ein Anfang, der zusammen mit BOS nicht mehr in dieses Fenster passt, ist ein Fehler, und das Programm wartet auf die nächste Zeile.

Jedes Zeichen wird neu bewertet. Der Cache der Attention trägt die bisher gesehenen Positionen. Der nächste eingegebene Anfang beginnt wieder mit leerem Cache. Ein unbekannter Buchstabe wird auf der Fehlerausgabe gemeldet, die Gewichte bleiben stehen, und die Schleife wartet auf die nächste Zeile.

Zur Ziehzeit wird names.txt nicht durchsucht. Ein Treffer ist ein Name aus der Datei nur dann, wenn jede Ziehung, einschließlich des abschließenden BOS, auf der gelernten Fortsetzung landet. Ein einziges anderes Zeichen ergibt eine neue Folge.

Wann ein gelernter Name wahrscheinlicher wird

Mehr Epochen erhöhen diese Chance. Mehr Schichten erhöhen sie nur, solange eine Schicht die Liste noch nicht abbilden kann. Beide Einstellungen lassen die Ziehung eine Ziehung bleiben.

Epochen. Jede weitere Epoche schiebt die Wahrscheinlichkeit des nächsten Zeichens näher an die Liste. Nach der ersten Epoche dominieren noch häufige Buchstaben und typische Endungen. Weitere Epochen machen die Fortsetzung hinter einem gesehenen Anfang enger, auch die Stelle, an der BOS kommen sollte. Damit steigt der Anteil der Vorschläge, die vollständig in der Datei stehen. Irgendwann ist das Auswendiglernen: die Vorschläge wiederholen die Liste und erfinden weniger. names.txt hat 500 Namen. Das ist eine kleine Liste, deshalb setzt das Auswendiglernen früher ein als bei den 32033 Zeilen von input.txt.

Schichten. Eine Schicht ist ein Attention-Block und das vorwärtsgerichtete Netz auf dem Residualstrom. Die nächste Schicht liest, was die vorige geschrieben hat. Token-Einbettung, Positions-Einbettung und Ausgabekopf wachsen mit der Zahl der Zeichen in der Datei. Jede weitere Schicht fügt bei dieser Breite 3072 Parameter hinzu, unabhängig vom Vokabular, und verlängert Training und Vorschlag. Das hilft, wenn der Verlust nach mehreren Epochen hoch bleibt, weil eine Schicht die Übergänge nicht tragen kann. Liegt der Verlust schon tief, bringt eine weitere Schicht vor allem mehr Speicher für genau diese Namen und langsameres Training.

Temperatur. Sie ist der direkte Griff an der Ziehung und im Namensgenerator nicht verstellbar. Kleiner als 0.5 wählt öfter das wahrscheinlichste nächste Zeichen und bleibt länger auf den gelernten Namen. Gegen null bleibt von einem Anfang nur noch ein einziger Weg, oft ein häufiger Name. Größer als 0.5 mischt stärker und erzeugt mehr neue Buchstabenfolgen.

Anfang. Hinter einem Anfang, den nur ein Name in der Datei hat, führt ein gut trainiertes Modell meistens zu genau diesem Namen. Hinter einem gemeinsamen Anfang, etwa ma, bleiben mehrere gelernte Fortsetzungen gleichzeitig wahrscheinlich, und die Ziehung nimmt eine davon.

Breite. -layers stapelt Blöcke. Die Einbettung bleibt 16 Zahlen lang. Wenn die Treffer auch nach mehreren Epochen selten sind, begrenzt oft diese Breite das Gedächtnis. Sie ist im Namensgenerator keine Einstellung.

Seed. Ein anderer Seed ändert die Startgewichte, die Mischreihenfolge und die Zufallszahlen der Ziehung. Er hebt die Trefferquote nicht systematisch. Ohne gesetztes -seed ist jeder Programmaufruf ein anderer Seed, und dieselben Anfänge können andere Fortsetzungen bekommen.

Weiter

0.1.4. Token beschreibt, wie aus einer Zeile die IDs werden, die beide Programme benutzen.