paint-brush
So erstellen Sie ein Tiny Language Model (TLM) in Ruby: Eine Schritt-für-Schritt-Anleitungvon@davidesantangelo
531 Lesungen
531 Lesungen

So erstellen Sie ein Tiny Language Model (TLM) in Ruby: Eine Schritt-für-Schritt-Anleitung

von Davide Santangelo10m2025/02/03
Read on Terminal Reader

Zu lang; Lesen

In diesem Artikel zeigen wir Ihnen, wie Sie mit Ruby ein sehr einfaches Sprachmodell erstellen. Wir erstellen ein einfaches Markow-Ketten-Modell, das aus Eingabetexten „lernt“ und dann basierend auf den beobachteten Mustern neuen Text generiert.
featured image - So erstellen Sie ein Tiny Language Model (TLM) in Ruby: Eine Schritt-für-Schritt-Anleitung
Davide Santangelo HackerNoon profile picture
0-item

In diesem Artikel zeigen wir Ihnen, wie Sie mit Ruby ein sehr einfaches Sprachmodell erstellen. Während echte Large Language Models (LLMs) enorme Datenmengen und Rechenressourcen erfordern, können wir ein Spielzeugmodell erstellen, das viele der Kernkonzepte der Sprachmodellierung demonstriert. In unserem Beispiel erstellen wir ein einfaches Markov-Ketten-Modell, das aus Eingabetext „lernt“ und dann basierend auf den beobachteten Mustern neuen Text generiert.


Hinweis: Dieses Tutorial dient Bildungszwecken und veranschaulicht einen vereinfachten Ansatz zur Sprachmodellierung. Es ist kein Ersatz für moderne Deep Learning LLMs wie GPT-4, sondern vielmehr eine Einführung in die zugrunde liegenden Ideen.


Inhaltsverzeichnis

  1. Die Grundlagen von Sprachmodellen verstehen
  2. Einrichten Ihrer Ruby-Umgebung
  3. Datenerfassung und Vorverarbeitung
  4. Erstellen des Markow-Kettenmodells
  5. Trainieren des Modells
  6. Text generieren und testen
  7. Abschluss

Die Grundlagen von Sprachmodellen verstehen

Ein Sprachmodell ist ein System, das Wortfolgen Wahrscheinlichkeiten zuweist. Im Kern ist es darauf ausgelegt, die statistische Struktur der Sprache zu erfassen, indem es die Wahrscheinlichkeit des Auftretens einer bestimmten Folge in einem bestimmten Kontext ermittelt. Das bedeutet, dass das Modell große Textmengen analysiert, um zu verstehen, wie Wörter normalerweise aufeinander folgen. Dadurch kann es vorhersagen, welches Wort oder welche Phrase als nächstes in einer Folge kommen könnte. Solche Fähigkeiten sind nicht nur für Aufgaben wie Textgenerierung und automatische Vervollständigung von zentraler Bedeutung, sondern auch für eine Vielzahl von Anwendungen zur Verarbeitung natürlicher Sprache (Natural Language Processing, NLP), darunter Übersetzung, Zusammenfassung und Stimmungsanalyse.

Moderne groß angelegte Sprachmodelle (LLMs) wie GPT-4 verwenden Deep-Learning-Techniken und riesige Datensätze, um komplexe Muster in der Sprache zu erfassen. Sie funktionieren, indem sie Eingabetexte durch zahlreiche Schichten künstlicher Neuronen verarbeiten, wodurch sie menschenähnliche Texte mit bemerkenswerter Flüssigkeit verstehen und generieren können. Hinter diesen hochentwickelten Systemen verbirgt sich jedoch dieselbe Grundidee: das Verstehen und Vorhersagen von Wortfolgen auf der Grundlage erlernter Wahrscheinlichkeiten.

Eine der einfachsten Methoden zur Modellierung von Sprache ist die Markow-Kette . Eine Markow-Kette ist ein statistisches Modell, das davon ausgeht, dass die Wahrscheinlichkeit des Vorkommens eines Wortes nur von einer begrenzten Anzahl vorangehender Wörter abhängt und nicht von der gesamten Texthistorie. Dieses Konzept ist als Markow-Eigenschaft bekannt. In der Praxis geht das Modell davon aus, dass das nächste Wort in einer Sequenz nur durch Betrachtung des/der letzten Wortes/Wörter vorhergesagt werden kann – eine Vereinfachung, die das Problem rechnerisch leichter handhabbar macht und dennoch nützliche Muster in den Daten erfasst.

In einem auf Markow-Ketten basierenden Sprachmodell:

  • Der zukünftige Zustand (nächstes Wort) hängt nur vom aktuellen Zustand (vorherige Wörter) ab: Das bedeutet, dass wir, sobald wir die letzten paar Wörter kennen (bestimmt durch die Reihenfolge des Modells), über genügend Kontext verfügen, um vorherzusagen, was als nächstes kommen könnte. Der gesamte Verlauf des Gesprächs oder Textes muss nicht berücksichtigt werden, was die Komplexität reduziert.
  • Wir erstellen eine Wahrscheinlichkeitsverteilung, die angibt, welches Wort als nächstes kommt, wenn die vorhergehenden Wörter berücksichtigt werden: Während das Modell anhand eines Textkorpus trainiert wird, lernt es die Wahrscheinlichkeit, mit der verschiedene Wörter einer bestimmten Sequenz folgen. Diese Wahrscheinlichkeitsverteilung wird dann während der Generierungsphase verwendet, um das nächste Wort in der Sequenz auszuwählen. Dabei wird normalerweise ein Zufallsstichprobenverfahren verwendet, das die gelernten Wahrscheinlichkeiten berücksichtigt.

In unserer Implementierung verwenden wir eine konfigurierbare „Reihenfolge“, um zu bestimmen, wie viele vorherige Wörter bei Vorhersagen berücksichtigt werden sollen. Eine höhere Reihenfolge bietet mehr Kontext und führt möglicherweise zu einem kohärenteren und kontextrelevanteren Text, da das Modell mehr Informationen über das Vorangegangene hat. Umgekehrt führt eine niedrigere Reihenfolge zu mehr Zufälligkeit und kann zu kreativeren, wenn auch weniger vorhersehbaren Wortfolgen führen. Dieser Kompromiss zwischen Kohärenz und Kreativität ist eine zentrale Überlegung bei der Sprachmodellierung.

Durch das Verständnis dieser Grundprinzipien können wir sowohl die Einfachheit von Markow-Ketten-Modellen als auch die grundlegenden Ideen, die komplexeren neuronalen Sprachmodellen zugrunde liegen, verstehen. Diese erweiterte Sichtweise hilft nicht nur dabei, die statistischen Mechanismen hinter der Sprachvorhersage zu verstehen, sondern legt auch den Grundstein für das Experimentieren mit fortgeschritteneren Techniken der natürlichen Sprachverarbeitung.


Einrichten Ihrer Ruby-Umgebung

Bevor Sie beginnen, stellen Sie sicher, dass Ruby auf Ihrem System installiert ist. Sie können Ihre Ruby-Version überprüfen, indem Sie Folgendes ausführen:

 ruby -v

Wenn Ruby nicht installiert ist, können Sie es von ruby-lang.org herunterladen.

Für unser Projekt möchten Sie möglicherweise ein dediziertes Verzeichnis und eine Datei erstellen:

 mkdir tiny_llm cd tiny_llm touch llm.rb

Jetzt sind Sie bereit, Ihren Ruby-Code zu schreiben.


Datenerfassung und Vorverarbeitung

Sammeln von Trainingsdaten

Für ein Sprachmodell benötigen Sie ein Textkorpus. Sie können jede beliebige Textdatei zum Trainieren verwenden. Für unser einfaches Beispiel könnten Sie eine kleine Textprobe verwenden, zum Beispiel:

 sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT

Vorverarbeitung der Daten

Vor dem Training ist es sinnvoll, den Text vorzuverarbeiten:

  • Tokenisierung: Text in Wörter aufteilen.
  • Normalisierung: Text optional in Kleinbuchstaben umwandeln, Satzzeichen entfernen usw.

Für unsere Zwecke funktioniert String#split -Methode von Ruby für die Tokenisierung gut genug.


Erstellen des Markow-Kettenmodells

Wir erstellen eine Ruby-Klasse namens MarkovChain um das Verhalten des Modells zu kapseln. Die Klasse enthält:

  • Ein Initialisierer zum Festlegen der Reihenfolge (Anzahl der vorangehenden Wörter) für die Kette.
  • Eine train , die die Kette aus dem Eingabetext erstellt.
  • Eine generate , die durch Sampling aus der Kette neuen Text erzeugt.

Unten finden Sie den vollständigen Code für das Modell:

 class MarkovChain def initialize(order = 2) @order = order # The chain is a hash that maps a sequence of words (key) to an array of possible next words. @chain = Hash.new { |hash, key| hash[key] = [] } end # Train the model using the provided text. def train(text) # Optionally normalize the text (eg, downcase) processed_text = text.downcase.strip words = processed_text.split # Iterate over the words using sliding window technique. words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end # Generate new text using the Markov chain. def generate(max_words = 50, seed = nil) # Choose a random seed from the available keys if none is provided or if the seed is invalid. if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words # Form the key from the last 'order' words. key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? # Randomly choose the next word from the possibilities. next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end

Erläuterung des Codes


  • **Initialisierung:** Der Konstruktor initialize legt die Reihenfolge fest (Standard ist 2) und erstellt einen leeren Hash für unsere Kette. Der Hash erhält einen Standardblock, sodass jeder neue Schlüssel als leeres Array beginnt.


  • **Training des Modells:** Die train nimmt eine Textzeichenfolge, normalisiert sie und zerlegt sie in Wörter. Mithilfe von each_cons werden aufeinanderfolgende Wortgruppen mit der Länge order + 1 erstellt. Die Wörter erster order dienen als Schlüssel und das letzte Wort wird an das Array möglicher Fortsetzungen für diesen Schlüssel angehängt.


  • **Text generieren:**Die Methode generate beginnt mit einem Startschlüssel. Wenn keiner angegeben ist, wird ein zufälliger Schlüssel gewählt. Anschließend wird iterativ eine Sequenz erstellt, indem die Wörter der letzten order nachgeschlagen und das nächste Wort abgetastet wird, bis die maximale Wortanzahl erreicht ist.


Trainieren des Modells

Da wir nun unsere MarkovChain Klasse haben, trainieren wir sie anhand einiger Textdaten.

 # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create a new MarkovChain instance with order 2 model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!"

Wenn Sie den obigen Code ausführen (z. B. indem Sie ihn in llm.rb speichern und ruby llm.rb ausführen), wird das Modell mit dem bereitgestellten Beispieltext trainiert.


Text generieren und testen

Sobald das Modell trainiert ist, können Sie neuen Text generieren. Fügen wir etwas Code hinzu, um einen Beispieltext zu generieren und auszudrucken:

 # Generate new text using the trained model. generated_text = model.generate(50) puts "Generated Text:" puts generated_text

Sie können auch versuchen, einen Seed für die Textgenerierung bereitzustellen. Wenn Sie beispielsweise einen der Schlüssel im Modell kennen (wie "once upon" ), können Sie Folgendes tun:

 seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed

Durch Experimentieren mit unterschiedlichen Seeds und Parametern (wie etwa der Reihenfolge und der maximalen Anzahl von Wörtern) können Sie sehen, wie das Ergebnis variiert.


Vollständiges Beispiel: Trainieren und Testen eines winzigen LLM

Hier ist das vollständige Ruby-Skript, das alle oben genannten Schritte kombiniert:

 #!/usr/bin/env ruby # llm.rb # Define the MarkovChain class class MarkovChain def initialize(order = 2) @order = order @chain = Hash.new { |hash, key| hash[key] = [] } end def train(text) processed_text = text.downcase.strip words = processed_text.split words.each_cons(@order + 1) do |words_group| key = words_group[0...@order].join(" ") next_word = words_group.last @chain[key] << next_word end end def generate(max_words = 50, seed = nil) if seed.nil? || [email protected]?(seed) seed = @chain.keys.sample end generated = seed.split while generated.size < max_words key = generated.last(@order).join(" ") possible_next_words = @chain[key] break if possible_next_words.nil? || possible_next_words.empty? next_word = possible_next_words.sample generated << next_word end generated.join(" ") end end # Sample text data for training sample_text = <<~TEXT Once upon a time in a land far, far away, there was a small village. In this village, everyone knew each other, and tales of wonder were told by the elders. The wind whispered secrets through the trees and carried the scent of adventure. TEXT # Create and train the model model = MarkovChain.new(2) model.train(sample_text) puts "Training complete!" # Generate text without a seed generated_text = model.generate(50) puts "\nGenerated Text:" puts generated_text # Generate text with a specific seed seed = "once upon" generated_text_with_seed = model.generate(50, seed) puts "\nGenerated Text with seed '#{seed}':" puts generated_text_with_seed

Ausführen des Skripts

  1. Speichern Sie das Skript als llm.rb
  2. Öffnen Sie Ihr Terminal und navigieren Sie zu dem Verzeichnis, das llm.rb enthält.
  3. Führen Sie das Skript mit folgendem Befehl aus:
 ruby llm.rb

Sie sollten eine Ausgabe sehen, die angibt, dass das Modell trainiert wurde, und dann zwei Beispiele für generierten Text.


Vergleichsindex

Die folgende Tabelle fasst einige Benchmark-Kennzahlen für verschiedene Versionen unserer Tiny LLM-Implementierungen zusammen. Jede Kennzahl wird unten erläutert:

  • Modell: Der Name oder die Versionskennung des Sprachmodells.
  • Reihenfolge: Die Anzahl der vorherigen Wörter, die in der Markow-Kette verwendet werden, um das nächste Wort vorherzusagen. Eine höhere Reihenfolge bedeutet im Allgemeinen, dass mehr Kontext verwendet wird, was möglicherweise die Kohärenz erhöht.
  • Trainingszeit (ms): Die ungefähre Zeit, die zum Trainieren des Modells anhand der bereitgestellten Textdaten benötigt wird, gemessen in Millisekunden.
  • Generierungszeit (ms): Die zum Generieren einer Beispieltextausgabe erforderliche Zeit, gemessen in Millisekunden.
  • Speichernutzung (MB): Die Speichermenge, die das Modell während des Trainings und der Generierung verbraucht.
  • Kohärenzbewertung: Eine subjektive Bewertung (von 5), die angibt, wie kohärent oder kontextuell angemessen der generierte Text ist.

Nachfolgend finden Sie die Markdown-Tabelle mit den Benchmark-Daten:

Modell

Befehl

Trainingszeit (ms)

Generierungszeit (ms)

Speichernutzung (MB)

Kohärenzbewertung

Winziges LLM v1

2

50

10

10

3/5

Winziges LLM v2

3

70

15

12

3,5/5

Winziges LLM v3

4

100

20

15

4/5

Diese Benchmarks bieten einen schnellen Überblick über die Vor- und Nachteile verschiedener Modellkonfigurationen. Mit zunehmender Ordnung dauert das Training und die Textgenerierung des Modells tendenziell etwas länger und es verbraucht mehr Speicher. Dieser Anstieg des Ressourcenverbrauchs geht jedoch häufig mit einer Verbesserung der Kohärenz des generierten Textes einher.

Abschluss

In diesem Tutorial haben wir gezeigt, wie man mit Ruby ein sehr einfaches Sprachmodell erstellt. Durch den Einsatz der Markov-Ketten-Technik haben wir ein System erstellt, das:

  • Trainiert anhand von Beispieltexten durch das Lernen von Wortübergängen.
  • Generiert neuen Text basierend auf erlernten Mustern.

Obwohl dieses Spielzeugmodell weit von produktionstauglichen LLMs entfernt ist, dient es als Sprungbrett zum Verständnis der Funktionsweise von Sprachmodellen auf grundlegender Ebene. Sie können diese Idee erweitern, indem Sie fortgeschrittenere Techniken einbeziehen, die Zeichensetzung besser handhaben oder sogar Ruby in Bibliotheken für maschinelles Lernen integrieren, um anspruchsvollere Modelle zu erhalten.

Viel Spaß beim Programmieren!