Schon als Form struggle ich vom Zeichnen fasziniert. Was mich beeindruckte, struggle nicht nur der Zeichenakt selbst, sondern auch die Idee, dass jede Zeichnung immer weiter verbessert werden konnte. Ich erinnere mich, dass ich mit meinem Zeichenstil ein sehr hohes Niveau erreicht habe. Sobald ich jedoch den Höhepunkt der Perfektion erreicht hatte, würde ich versuchen, die Zeichnung noch weiter zu verbessern – leider mit katastrophalen Ergebnissen.

Von da an behalte ich immer das gleiche Mantra im Hinterkopf: „Verfeinere und iteriere und du wirst Perfektion erreichen.“ An der Universität bestand mein Ansatz darin, Bücher viele Male zu lesen und mein Wissen auf der Suche nach anderen Quellen zu erweitern, um in jedem Konzept verborgene Bedeutungsebenen zu finden. Heute wende ich dieselbe Philosophie auf KI/ML und Codierung an.

Wir wissen, dass die Matrixmultiplikation (der Einfachheit halber hier Matmul) der Kernbestandteil jedes KI-Prozesses ist. In der Vergangenheit habe ich mich weiterentwickelt LLM.rustein Rostspiegel von Karpathy LLM.c. Der schwierigste Punkt bei der Rust-Implementierung struggle die Matrixmultiplikation. Da wir zur Feinabstimmung eines GPT-basierten Modells Tausende von Iterationen durchführen müssen, benötigen wir eine effiziente Matmul-Operation. Zu diesem Zweck musste ich die BLAS-Bibliothek verwenden und eine implementieren unsafe Strategie zur Überwindung von Grenzen und Barrieren. Die Verwendung von unsafe in Rust widerspricht der Philosophie von Rust, deshalb suche ich in diesem Zusammenhang immer nach sichereren Methoden zur Verbesserung von matmul.

Inspiriert von Sam Altmans Aussage „Fragen Sie GPT, wie man Werte schafft“ beschloss ich, lokale LLMs zu bitten, ihre eigenen Algorithmen zu generieren, zu vergleichen und zu iterieren, um eine bessere, native Rust-Matmul-Implementierung zu erstellen.

Die Herausforderung unterliegt einigen Einschränkungen:

  • Wir müssen unsere lokale Umgebung nutzen. In meinem Fall ein MacBook Professional, M3, 36 GB RAM;
  • Überwinden Sie die Grenzen von Token;
  • Zeit und Benchmarking des Codes innerhalb der Generierungsschleife selbst

Ich weiß, dass es mit dieser Methode quick unmöglich ist, Leistungen auf BLAS-Niveau zu erreichen, aber ich möchte hervorheben, wie wir KI für individuelle Anforderungen nutzen können, selbst mit unseren „winzigen“ Laptops, damit wir Ideen freisetzen und Grenzen in jedem Bereich verschieben können. Dieser Beitrag soll eine Inspiration für Praktiker und Menschen sein, die sich mit Microsoft Autogen und der lokalen LLM-Bereitstellung vertraut machen möchten.

Die gesamte Cod-Implementierung finden Sie hier Github-Repo. Dies ist ein fortlaufendes Experiment und es werden viele Änderungen/Verbesserungen vorgenommen.

Allgemeine Idee

Die Gesamtidee besteht darin, einen runden Tisch mit Agenten zu veranstalten. Ausgangspunkt ist die MrAderMacher Mixtral 8x7B Modell This fall K_M lokales Modell. Aus dem Modell erstellen wir 5 Entitäten:

  • Die Proposer entwickelt einen neuen Strassen-ähnlichen Algorithmus, um eine bessere und effizientere Methode zur Durchführung von Matmul zu finden;
  • Die Verifier überprüft die Matmul-Formulierung durch symbolische Mathematik;
  • Die Coder erstellt den zugrunde liegenden Rust-Code;
  • Die Tester führt es aus und speichert alle Informationen in der Vektordatenbank;
  • Die Supervisor agiert lautlos und kontrolliert den gesamten Arbeitsablauf.
Agent Rollenfunktion
Antragsteller Analysiert Benchmark-Zeiten und schlägt neue Tuning-Parameter und Matmul-Formulierungen vor.
Prüfer (Derzeit im Code deaktiviert). Es überprüft die mathematische Formulierung des Antragstellers durch symbolische Überprüfung.
Programmierer Es übernimmt die Parameter und berechnet den Rust-Vorlagencode.
Tester Es führt den Rust-Code aus, speichert den Code und berechnet das Benchmark-Timing.
Supervisor Gesamtkontrolle des Arbeitsablaufs.
Tab. 1: Rollen von Agenten.

Der gesamte Arbeitsablauf kann über Microsoft Autogen orchestriert werden, wie in Abb. 1 dargestellt.

Abb.1: Matmul-Optimierung. Der Benutzer hat eine erste Anfrage mit einer Eingabeaufforderung. Von dort aus orchestriert der Supervisor den gesamten Arbeitsablauf: 1) Der Antragsteller fungiert als Theoretiker und generiert einen Strassen-ähnlichen Algorithmus; 2) Der Verifizierer prüft die mathematische Korrektheit des Codes; 3) Der Codierer generiert einen Rust Neon-Code; 4) Der Tester führt den Benchmark durch. (Bild erstellt mit Nano Banana Professional).

Bereiten Sie die Eingabedaten und die Vektordatenbank vor

Die Eingabedaten werden aus allen wissenschaftlichen Arbeiten gesammelt, die sich auf die Optimierung der Matrixmultiplikation konzentrieren. Auf viele dieser Veröffentlichungen wird verwiesen in und sie beziehen sich auf DeepMinds Strassen-Artikel. Ich möchte einfach anfangen und habe 50 Artikel gesammelt, die zwischen 2020 und 2025 veröffentlicht wurden und sich speziell mit der Matrixmultiplikation befassen.

Als nächstes habe ich verwendet chroma um die Vektordatenbank zu erstellen. Der entscheidende Aspekt bei der Erstellung einer neuen Vektordatenbank ist die Artwork und Weise, wie die PDFs aufgeteilt werden. In diesem Zusammenhang habe ich a verwendet semantisch chunkeR. Im Gegensatz zu Break up-Textual content-Methoden verwendet der semantische Chunker die tatsächliche Bedeutung des Textes, um zu bestimmen, wo geschnitten werden soll. Das Ziel besteht darin, die zusammengehörigen Sätze in einem Block zusammenzuhalten, wodurch die endgültige Vektordatenbank kohärenter und genauer wird. Dies geschieht mithilfe des lokalen Modells BAAI/bge-base-en-v1.5. Der Github-Gist unten zeigt die vollständige Implementierung.

Der Kerncode: autogen-core und GGML-Modelle

Ich habe Microsoft Autogen verwendet, insbesondere das autogen-core Variante (Model 0.7.5). Anders als beim übergeordneten Chat, in autogen-core Wir können auf ereignisgesteuerte Low-Stage-Bausteine ​​zugreifen, die erforderlich sind, um einen zustandsmaschinengesteuerten Workflow nach Bedarf zu erstellen. Tatsächlich besteht die Herausforderung darin, einen strikten Arbeitsablauf aufrechtzuerhalten. Alle handelnden Agenten müssen in einer bestimmten Reihenfolge handeln: Proposer –> Verifier –> Coder –> Tester.

Der Kernteil ist der BaseMatMulAgentdas von AutoGen erbt RoutedAgent. Mit dieser Basisklasse können wir standardisieren, wie LLM-Agenten am Chat teilnehmen und sich verhalten.

Aus dem obigen Code können wir ersehen, dass die Klasse für die Teilnahme an einem asynchronen Gruppenchat konzipiert ist, den Gesprächsverlauf verarbeitet, externe Instruments aufruft und Antworten über das lokale LLM generiert.

Die Kernkomponente ist @message_handlerein Dekorator, der eine Methode registriert als listener oder subscriber basierend auf dem Nachrichtentyp. Der Dekorator erkennt automatisch den Typhinweis des Arguments der ersten Methode – in unserem Fall message: GroupChatMessage. Anschließend abonniert es den Agenten für den Empfang aller Ereignisse dieses Typs, die an das Thema des Agenten gesendet werden. Der handle_message Die async-Methode ist dann für die Aktualisierung des internen Speichers des Agenten verantwortlich, ohne eine Antwort zu generieren.

Wenn der Listener-Subscriber-Mechanismus vorhanden ist, können wir uns auf die Supervisor-Klasse konzentrieren. Der MatMulManager erbt RoutedAgent und orchestriert den gesamten Agentenfluss.

Der obige Code verwaltet alle Agenten. Wir überspringen das Verifier Teil, für den Second. Der Coder Veröffentlichen Sie den endgültigen Code und die Tester kümmert sich darum, sowohl den Code als auch den gesamten Kontext in der Vector-Datenbank zu speichern. Auf diese Weise können wir vermeiden, alle Token unseres lokalen Modells zu verbrauchen. Bei jedem neuen Lauf holt sich das Modell die zuletzt generierten Algorithmen aus der Vektordatenbank ein und schlägt eine neue Lösung vor.

Ein sehr wichtiger Vorbehalt, um sicherzugehen autogen-core kann damit arbeiten llama Modelle unter MacOS verwenden Sie das folgende Snippet:

#!/bin/bash 

CMAKE_ARGS="-DGGML_METAL=on" FORCE_CMAKE=1 pip set up --upgrade --verbose --force-reinstall llama-cpp-python --no-cache-dir

Abb.2 fasst den gesamten Code zusammen. Wir können den Code grob in 3 Hauptblöcke unterteilen:

  • Der BaseAgentdas Nachrichten über die Agenten von LLM verarbeitet, die mathematische Formulierung auswertet und Code generiert;
  • Der MatMulManager orchestriert den gesamten Agentenfluss;
  • autogen_core.SingleThreadedAgentRuntime ermöglicht es uns, den gesamten Workflow in die Realität umzusetzen.
Abb.2: Gesamtablauf im Überblick. Der Basisagent führt das LLM über Agenten aus, wertet die mathematische Formulierung aus, erstellt den Algorithmus in Rust und speichert alle Informationen in der Vektordatenbank. Der MatMulManager ist der eigentliche Kern des gesamten Workflows. Schließlich ist die autogen_core.SingleThreadedAgentRuntime sorgt dafür, dass das alles auf unserem MacBook PRO funktioniert. (Bild erstellt mit Nano Banana Professional.)

Ergebnisse und Benchmark

Der gesamte Rust-Code wurde überarbeitet und manuell erneut ausgeführt. Obwohl der Arbeitsablauf strong ist, erfordert die Arbeit mit LLMs ein kritisches Auge. Mehrmals konfabulierte das Modell*wodurch Code generiert wurde, der optimiert aussah, aber die eigentliche Matmul-Arbeit nicht ausführen konnte.

Die allererste Iteration generiert eine Artwork Strassen-ähnlichen Algorithmus („Run 0“-Code in Abb. 3):

Das Modell überlegt sich bessere Implementierungen, die eher Rust-NEON ähneln, sodass es nach 4 Iterationen den folgenden Code liefert („Run 3“ in Abb. 3):

Wir können die Verwendung von Funktionen sehen wie vaddq_f32spezifische CPU-Anweisung für ARM-Prozessoren, stammt von std::arch::aarch64. Das Modell schafft es zu verwenden rayon um den Workflow auf mehrere CPU-Kerne aufzuteilen und innerhalb der parallelen Threads NEON-Intrinsics zu verwenden. Der Code selbst ist nicht ganz korrekt, außerdem ist mir aufgefallen, dass beim Umgang mit 1024×1024-Matrizen ein Fehler wegen unzureichendem Arbeitsspeicher auftritt. Ich musste den Code manuell überarbeiten, damit er funktionierte.

Dies bringt uns zurück zu unserem Mantra „Iteration bis zur Perfektion“ und wir können uns fragen: „Kann ein lokaler Agent den Rust-Code autonom verfeinern, bis er komplexe NEON-Intrinsics beherrscht?“ Die Ergebnisse zeigen, dass dieser Optimierungsgrad sogar auf Client-{Hardware} erreichbar ist.

Abb. 3 zeigt die Endergebnisse, die ich nach jeder Iteration erhalten habe.

Abb.3: Logarithmisches Diagramm der Rust-Neon-Implementierung bei verschiedenen Iterationen. Die Berechnungen wurden mit 1024×1024-Matrixmultiplikations-Benchmarks durchgeführt. (Bild vom Autor erstellt).

Der 0. und 2. Benchmark weisen einige Fehler auf, da es physikalisch unmöglich ist, solche Ergebnisse auf einem 1024×1024-Matmul auf einer CPU zu erzielen:

  • Der erste Code leidet unter einem Diagonalfehler, sodass der Code nur diagonale Blöcke der Matrix berechnet und den Relaxation ignoriert.
  • Der zweite Code hat einen defekten Puffer, da er wiederholt einen kleinen, im Cache befindlichen Puffer mit 1028 Floats überschreibt, anstatt die gesamten 1 Million Elemente zu durchlaufen.

Der Code erzeugte jedoch zwei echte Codes, den Lauf 1 und den Lauf 3. Die erste Iteration erreicht 760 ms und stellt eine echte Basislinie dar. Es leidet unter Cache-Fehlern und mangelnder SIMD-Vektorisierung. Der Lauf 3 zeichnet 359 ms auf, die Verbesserung ist die Implementierung von NEON SIMD und Rayon-Parallelität.

*: „Das Modell konfabuliert“ habe ich aus bestimmten Gründen geschrieben. Aus medizinischer Sicht sind alle LLMs keine Halluzinationen, sondern Konfabulationen. Halluzinationen sind eine völlig andere State of affairs im Vergleich zu dem, was LLMs tun, wenn sie plappern und „falsche“ Antworten generieren.

Schlussfolgerungen

Dieses Experiment begann mit einer Frage, die eine unmögliche Herausforderung zu sein schien: „Können wir lokale LLMs für Verbraucher verwenden, um leistungsstarke Rust-Algorithmen zu entdecken, die mit der BLAS-Implementierung konkurrieren können?“

Wir können ja sagen, oder zumindest verfügen wir über einen gültigen und soliden Hintergrund, mit dem wir besseren Code erstellen können, um einen vollständigen BLAS-ähnlichen Code in Rust zu erreichen.

Der Beitrag zeigte, wie man mit Microsoft Autogen interagiert, autogen-coreund wie man einen Runden Tisch mit Agenten erstellt.

Das verwendete Basismodell stammt von GGUF und läuft auf einem MacBook Professional M3, 36 GB.

Natürlich haben wir (noch) nichts Besseres als BLAS in einem einzigen einfachen Code gefunden. Wir haben jedoch bewiesen, dass der lokale Agenten-Workflow auf einem MacBook Professional das erreichen kann, was bisher für einen riesigen Cluster und riesige Modelle gehalten wurde. Schließlich gelang es dem Modell, eine vernünftige Rust-NEON-Implementierung zu finden, „Führen Sie 3 oben aus“, die eine Geschwindigkeitssteigerung von über 50 % gegenüber der Commonplace-Rayon-Implementierung aufweist. Wir müssen hervorheben, dass die Spine-Implementierung KI-generiert struggle.

Die Grenze ist offen. Ich hoffe, dieser Blogbeitrag kann Sie dazu inspirieren, herauszufinden, welche Grenzen wir mit der lokalen LLM-Bereitstellung überwinden können.


Ich schreibe dies in persönlicher Eigenschaft; Diese Ansichten sind meine eigenen.

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert