Vernetzen Sie sich mit uns

Künstliche Intelligenz

Microsofts Inference Framework bringt 1-Bit große Sprachmodelle auf lokale Geräte

mm
1-Bit-LLMs und das BitNet.cpp-Framework von Microsoft verstehen

Im Oktober 17, 2024, Microsoft kündigte BitNet.cpp an, ein Inferenz-Framework, das für die Ausführung von 1-Bit-quantisierten Large Language Models (LLMs) entwickelt wurde. BitNet.cpp ist ein bedeutender Fortschritt in der KI der Generation, da es die effiziente Bereitstellung von 1-Bit-LLMs auf Standard-CPUs ermöglicht, ohne dass teure GPUs erforderlich sind. Diese Entwicklung demokratisiert den Zugriff auf LLMs, macht sie auf einer Vielzahl von Geräten verfügbar und bietet neue Möglichkeiten für KI-Anwendungen auf Geräten.

1-Bit große Sprachmodelle verstehen

Large Language Models (LLMs) erforderten traditionell erhebliche Rechenressourcen, da sie hochpräzise Gleitkommazahlen (normalerweise FP16 oder BF16) für Modellgewichte verwendeten. Diese Notwendigkeit machte den Einsatz von LLMs teuer und energieintensiv.

Im Kern verwenden 1-Bit-LLMs extreme Quantisierungstechniken, um Modellgewichte mit nur drei möglichen Werten darzustellen: -1, 0 und 1, daher der Begriff „1.58-Bit“ (da zum Kodieren von drei Zuständen etwas mehr als ein Bit erforderlich ist).

Ternäres Gewichtssystem

Das Konzept

Die 1-Bit-Quantisierung in BitNet.cpp ist ein ternäres Gewichtungssystem. BitNet arbeitet mit nur drei möglichen Werten für jeden Parameter:

  • -1 (Negativ)
  • 0 (neutral)
  • 1 (positiv)

Daraus ergibt sich ein Speicherbedarf von ca. 1.58 Bit pro Parameter, daher der Name BitNet b1.58. Diese drastische Reduzierung der Parameter-Bitbreite führt zu einer eindrucksvollen Reduzierung des Speicherbedarfs und der Rechenkomplexität, da die meisten Gleitkommamultiplikationen durch einfache Additionen und Subtraktionen ersetzt werden.

Mathematische Grundlagen

Bei der 1-Bit-Quantisierung werden Gewichte und Aktivierungen in ihre ternäre Darstellung umgewandelt. Dies geschieht in den folgenden Schritten:

1. Gewichtsbinarisierung

Bei der Binärisierung der Gewichte werden diese um den Mittelwert zentriert (α), was zu einer ternären Darstellung führt. Die Transformation wird mathematisch wie folgt ausgedrückt:

Wf=Schild(W-α)

Kennzahlen:

  • W ist die ursprüngliche Gewichtsmatrix.
  • α ist der Mittelwert der Gewichte.
  • Vorzeichen(x) Rückgabe +1 if x > 0 sowie -1 Andernfalls.

2. Aktivierungsquantisierung

Durch die Quantisierung von Aktivierungen wird sichergestellt, dass die Eingaben auf eine bestimmte Bitbreite beschränkt sind:

Kennzahlen:

  • Qb = 2(b−1)2^{(b-1)} ist der maximale Quantisierungsgrad für b-Bit-Breite.
  • γ ist der maximale absolute Wert von x (bezeichnet als ∣∣x∣∣∞).
  • ε ist eine kleine Zahl, um einen Überlauf während der Berechnungen zu verhindern.

3. BitLinear-Betrieb

Die BitLinear-Schicht ersetzt traditionelle Matrixmultiplikationen durch eine vereinfachte Operation:

y=Wf×x^e×(Qbβγ)

Kennzahlen:

  • β ist ein Skalierungsfaktor, der zur Minimierung von Näherungsfehlern verwendet wird.
  • γ skaliert die Aktivierungen.
  • Q_b ist der Quantisierungsfaktor.

Diese Transformation ermöglicht effiziente Berechnungen unter Beibehaltung der Modellleistung.

Auswirkungen auf die Leistung

Speichereffizienz

Das ternäre Gewichtungssystem reduziert den Speicherbedarf erheblich:

  • Traditionelle LLMs: 16 Bits pro Gewicht
  • BitNet.cpp: 1.58 Bits pro Gewicht

Diese Reduzierung entspricht einer Speichereinsparung von ca. 90% im Vergleich zu herkömmlichen 16-Bit-Modellen, sodass größere Modelle innerhalb derselben Hardwarebeschränkungen passen.

Energieeffizienz

Inferenzgeschwindigkeit, Energieeffizienz (Apple M2)

 

Inferenzgeschwindigkeit: Schneller auf beiden CPUs

Inferenzgeschwindigkeit, Energieeffizienz (i7-13700H)

1. Inferenzgeschwindigkeit: Schneller auf beiden CPUs

Inferenzgeschwindigkeit wird als Anzahl der pro Sekunde verarbeiteten Token dargestellt. Hier ist eine Aufschlüsselung der Beobachtungen:

  • Auf Apple M2 Ultra: BitNet.cpp erreicht bis zu 5.07x Beschleunigung für größere Modelle (30B) im Vergleich zu Llama.cpp, mit einer Spitzengeschwindigkeit von 593.43 Token pro Sekunde für ein 125M-Modell, das ist ein 1.37x Beschleunigung. Bei größeren Modellen wie 3.8B und 7B hält BitNet.cpp eine Geschwindigkeit von über 84.77 Token pro Sekunde aufrecht und zeigt damit seine Effizienz über alle Skalen hinweg.
  • Auf Intel i7-13700H: BitNet.cpp erreicht noch dramatischere Geschwindigkeitsverbesserungen. Bei der Modellgröße 7B liefert BitNet.cpp eine unglaubliche 5.68-fache Beschleunigung im Vergleich zu Llama.cpp. Für kleinere Modelle wie 125M verarbeitet es 389.08 Token pro Sekunde, Das ist 2.37x schneller als Llama.cpp.

2. Energieeffizienz: Ein Wendepunkt für Edge-Geräte

Die bereitgestellten Grafiken enthalten außerdem Energiekostenvergleiche, was eine deutliche Reduzierung des Energieverbrauchs pro verarbeitetem Token zeigt:

  • Auf Apple M2 Ultra: Die Energieeinsparungen von BitNet.cpp sind beträchtlich. Für das 700M-Modell verbraucht es 55.4% weniger Energie pro Token im Vergleich zu Llama.cpp, sinkt von 0.314 bis 0.140. Dieser Trend setzt sich bei größeren Modellen fort. Das Modell 70B weist eine 70.0 % Reduzierung des Energieverbrauchs.
  • Auf Intel i7-13700H: BitNet.cpp liefert 71.9% Energieeinsparung für das Modell 700M, mit einem Verbrauchsrückgang von 1.367 zu 0.384. Obwohl Energiedaten für das 70B-Modell in Llama.cpp nicht verfügbar sind, bleibt BitNet.cpp effizient, mit einem Energieverbrauch von 17.33 für das Modell 70B.

3. Übertreffen der Benchmark für menschliche Lesegeschwindigkeit

Eine der interessantesten Erkenntnisse aus diesen Grafiken ist der Verweis auf menschliche Lesegeschwindigkeit, markiert bei 5-7 Token pro Sekunde. Diese rote Linie zeigt, dass beide Implementierungen, insbesondere BitNet.cpp, die menschliche Lesegeschwindigkeit selbst bei den größten Modellen bequem übertreffen können:

  • On Apple M2Ultra, BitNet.cpp übertrifft die menschliche Lesegeschwindigkeit für alle Modellgrößen, wobei die niedrigste Geschwindigkeit 8.67 Token pro Sekunde für ein 70B-Modell.
  • On Intel i7-13700Herreicht das Modell 100B immer noch 1.70 Token pro Sekunde, erreicht fast die untere Grenze der menschlichen Lesegeschwindigkeit, während alle kleineren Modelle diesen Benchmark übertreffen.

Überlegungen zum Training

Straight-Through-Estimator (STE)

Da die 1-Bit-Quantisierung nicht differenzierbare Funktionen einführt, wird für das Training eine spezielle Technik verwendet, die als Straight-Through-Estimator (STE)Bei diesem Ansatz fließen die Gradienten unverändert durch nicht differenzierbare Punkte. Hier ist eine vereinfachte Implementierung in Python:

class StraightThroughEstimator(Function):
    @staticmethod
    def forward(ctx, input):
        return input.sign()

    @staticmethod
    def backward(ctx, grad_output):
        return grad_output

Gemischtes Präzisionstraining

Um die Stabilität während des Trainings aufrechtzuerhalten, Gemischte Präzision wird eingesetzt:

  • Gewichte und Aktivierungen: Auf 1-Bit-Präzision quantisiert.
  • Gradienten und Optimiererzustände: Mit höherer Präzision gespeichert.
  • Latente Gewichte: Wird mit hoher Präzision gepflegt, um genaue Aktualisierungen während des Trainings zu ermöglichen.

Strategie für hohe Lernraten

Eine besondere Herausforderung bei 1-Bit-Modellen besteht darin, dass kleine Aktualisierungen die binärisierten Gewichte möglicherweise nicht beeinflussen. Um dies zu mildern, wird die Lernrate erhöht, was im Vergleich zu herkömmlichen Ansätzen eine schnellere Konvergenz und bessere Optimierung gewährleistet.

Gruppenquantisierung und -normalisierung

BitNet.cpp führt ein Gruppenquantisierung und -normalisierung um die Modellparallelität zu verbessern. Anstatt Parameter für die gesamte Gewichtsmatrix zu berechnen, unterteilt BitNet Gewichte und Aktivierungen in mehrere Gruppen (G).

Diese Gruppierung ermöglicht eine effiziente Parallelverarbeitung ohne zusätzliche Kommunikation zwischen den Gruppen und ermöglicht so das Training und die Inferenz von Modellen im großen Maßstab.

Implementierungshinweise und Optimierungen

CPU-Optimierung

BitNet.cpp nutzt mehrere Low-Level-Optimierungen, um eine maximale CPU-Leistung zu erreichen:

  • Vektorisierte Operationen: Nutzt SIMD-Anweisungen, um Bitmanipulationen effizient durchzuführen.
  • Cache-freundlicher Speicherzugriff: Strukturiert Daten, um Cachefehler zu minimieren.
  • Parallelverarbeitung: Verteilt die Arbeitslast effektiv auf mehrere CPU-Kerne.

Hier ist ein Beispiel einer Schlüsselfunktion, die Quantisierung und Inferenz in BitNet implementiert:

 
def bitlinear_forward(input, weight, scale):
    # Quantize the input using absmax quantization
    input_q = quantize(input)
    
    # Perform binary matrix multiplication
    output = binary_matmul(input_q, weight)
    
    # Scale the output to match the original precision
    return output * scale

def quantize(x):
    # Perform absmax quantization
    scale = torch.max(torch.abs(x))
    return torch.clamp(x / scale, -1, 1) * scale

Unterstützte Modelle

Die aktuelle Version von BitNet.cpp unterstützt Folgendes 1-Bit-LLMs auf Hugging Face verfügbar:

  • bitnet_b1_58-groß (0.7 B Parameter)
  • bitnet_b1_58-3B (3.3 B Parameter)
  • Llama3-8B-1.58-100B-Token (8.0 B Parameter)

Diese Modelle sind öffentlich verfügbar, um die Inferenzfähigkeiten des Frameworks zu demonstrieren. Obwohl sie nicht offiziell von Microsoft trainiert oder veröffentlicht wurden, veranschaulichen sie die Vielseitigkeit des Frameworks.

Installationsanleitung

Um mit BitNet.cpp zu beginnen, führen Sie die folgenden Schritte aus:

Voraussetzungen:

  1. Python > = 3.9
  2. CMake > = 3.22
  3. Clang > = 18
  4. Conda (sehr empfehlenswert)

Für Windows Benutzer: Visual Studio sollte mit den folgenden aktivierten Komponenten installiert werden:

  • Desktop-Entwicklung mit C++
  • C++-CMake-Tools für Windows
  • Git für Windows
  • C++-Clang-Compiler für Windows
  • MS-Build-Unterstützung für LLVM-Toolset (Clang)

Für Debian / Ubuntu Benutzern steht ein automatisches Installationsskript zur Verfügung:

bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"

Schrittweise Installation

  1. Klonen Sie das Repository:
    git clone --recursive https://github.com/microsoft/BitNet.git

    cd BitNet
  2. Abhängigkeiten installieren:
    # Create a new Conda environment (recommended)
    conda create -n bitnet-cpp python=3.9
    conda activate bitnet-cpp


    pip install -r requirements.txt
  3. Erstellen und Vorbereiten des Projekts: Sie können ein Modell direkt von Hugging Face herunterladen und in ein quantisiertes Format konvertieren:
    python setup_env.py --hf-repo HF1BitLLM/Llama3-8B-1.58-100B-tokens -q i2_s

    Alternativ können Sie das Modell manuell herunterladen und konvertieren:

    huggingface-cli download HF1BitLLM/Llama3-8B-1.58-100B-tokens --local-dir models/Llama3-8B-1.58-100B-tokens

    python setup_env.py -md models/Llama3-8B-1.58-100B-tokens -q i2_s

Ausführen von Inferenz mit BitNet.cpp

Um Inferenzen mithilfe des Frameworks auszuführen, verwenden Sie den folgenden Befehl:

python run_inference.py -m models/Llama3-8B-1.58-100B-tokens/ggml-model-i2_s.gguf -p "Sandra journeyed to the kitchen. Where is Sandra?" -n 6 -temp 0.7

Erläuterung:

  • -m gibt den Modelldateipfad an.
  • -p definiert den Eingabeaufforderungstext.
  • -n legt die Anzahl der vorherzusagenden Token fest.
  • -temp passt die Zufälligkeit der Stichproben (Temperatur) während der Inferenz an.

Ausgabebeispiel

Sandra journeyed to the kitchen. Where is Sandra?

Answer: Sandra is in the kitchen.

Technische Details von BitNet.cpp

BitLineare Ebene

BitNet.cpp implementiert eine modifizierte Transformer-Architektur und ersetzt Standard-Matrixmultiplikationen durch BitLinear Operationen. Dieser Ansatz zentralisiert die Gewichte vor der Quantisierung auf Null und skaliert sie, um Näherungsfehler zu reduzieren. Die Schlüsseltransformationsfunktion sieht folgendermaßen aus:

# Binarization function for 1-bit weights
def binarize_weights(W):
    alpha = W.mean()
    W_binarized = np.sign(W - alpha)
    return W_binarized

Die Kombination aus zentralisierten Gewichten und Skalierung stellt sicher, dass der Quantisierungsfehler minimal bleibt und somit die Leistung erhalten bleibt.

Auswirkungen auf die Industrie

BitNet.cpp könnte weitreichende Auswirkungen auf den Einsatz von LLMs haben:

  • Barierrefreiheit: Ermöglicht die Ausführung von LLMs auf Standardgeräten und demokratisiert den Zugriff auf leistungsstarke KI.
  • Kosteneffizienz: Reduziert den Bedarf an teuren GPUs und senkt so die Einführungshürde.
  • Energieeffizienz: Spart Energie durch Nutzung standardmäßiger CPU-basierter Inferenz.
  • Innovation : Eröffnet neue Möglichkeiten für geräteinterne KI, wie Echtzeit-Sprachübersetzung, Sprachassistenten und datenschutzorientierte Anwendungen ohne Cloud-Abhängigkeit.

Herausforderungen und zukünftige Richtungen

Obwohl 1-Bit-LLMs vielversprechend sind, bleiben noch einige Herausforderungen bestehen. Dazu gehören die Entwicklung robuster 1-Bit-Modelle für verschiedene Aufgaben, die Optimierung der Hardware für 1-Bit-Berechnungen und die Ermutigung von Entwicklern, dieses neue Paradigma zu übernehmen. Darüber hinaus stellt die Erforschung der 1-Bit-Quantisierung für Computervisions- oder Audioaufgaben eine spannende Zukunftsrichtung dar.

Fazit

Die Einführung von BitNet.cpp durch Microsoft ist ein bedeutender Fortschritt. Indem BitNet.cpp effiziente 1-Bit-Inferenz auf Standard-CPUs ermöglicht, sorgt es für Zugänglichkeit und Nachhaltigkeit von KI. Dieses Framework schafft die Voraussetzungen für portablere und kostengünstigere LLMs und erweitert die Möglichkeiten der geräteinternen KI.

Ich habe die letzten fünf Jahre damit verbracht, in die faszinierende Welt des maschinellen Lernens und des Deep Learning einzutauchen. Meine Leidenschaft und mein Fachwissen haben dazu geführt, dass ich an über 50 verschiedenen Software-Engineering-Projekten mitgewirkt habe, mit besonderem Schwerpunkt auf KI/ML. Meine anhaltende Neugier hat mich auch zur Verarbeitung natürlicher Sprache geführt, einem Bereich, den ich gerne weiter erforschen möchte.