2 Punkte von GN⁺ 2024-12-09 | 1 Kommentare | Auf WhatsApp teilen
  • Antworten lokaler Modelle können an ein JSON Schema gebunden werden, wodurch sich der Aufwand für nachträgliches Parsing verringert und die Ergebnisse bei der Datenextraktion konsistenter werden
  • Nutzer können in cURL, Python und JavaScript über den Parameter format ein Schema übergeben und so auf dieselbe Weise strukturierte Ausgaben anfordern
  • In Python mit Pydantic und in JavaScript mit Zod lässt sich die Schemadefinition leicht mit der Antwortvalidierung auf Code-Ebene verknüpfen
  • Die Beispiele geben Länderinformationen, Texte zu Haustieren und Bildanalyse-Ergebnisse von llama3.2-vision in festgelegten JSON-Feldern zurück und validieren sie
  • Wenn stabile Ergebnisse wichtig sind, wird empfohlen, im Prompt „return as JSON“ zu ergänzen und die temperature auf 0 zu setzen

Modellantworten mit JSON Schema einschränken

  • Ollama unterstützt strukturierte Ausgaben, mit denen sich die Modellausgabe auf ein bestimmtes JSON-Schema beschränken lässt
  • Auch die Python- und JavaScript-Bibliotheken von Ollama wurden aktualisiert, um strukturierte Ausgaben zu unterstützen
  • Diese Funktion kann für folgende Aufgaben genutzt werden
    • Daten aus Dokumenten parsen
    • Daten aus Bildern extrahieren
    • Antworten von Sprachmodellen strukturieren
    • Höhere Zuverlässigkeit und Konsistenz als im JSON-Modus erreichen

Installation und Aufruf

  • Es muss die neueste Version von Ollama heruntergeladen werden
  • Die Python-Bibliothek wird mit folgendem Befehl auf die neueste Version aktualisiert
pip install -U ollama
  • Die JavaScript-Bibliothek wird mit folgendem Befehl installiert
npm i ollama
  • Um strukturierte Ausgaben anzufordern, wird das Schema in cURL-Anfragen oder in den Python- bzw. JavaScript-Bibliotheken über den Parameter format übergeben

JSON Schema per cURL übergeben

  • Das cURL-Beispiel sendet eine Anfrage an http://localhost:11434/api/chat, setzt model auf llama3.1 und stream auf false
  • In format wird ein Objektschema mit name, capital und languages übergeben
    • name: Zeichenkette
    • capital: Zeichenkette
    • languages: Array von Zeichenketten
    • Alle drei Felder sind als required markiert
  • Die Antwort wird im Format des in der Anfrage enthaltenen JSON-Schemas zurückgegeben
{
  "capital": "Ottawa",
  "languages": [
    "English",
    "French"
  ],
  "name": "Canada"
}

Verwendung der Python- und JavaScript-Bibliotheken

  • Python

    • Die Ollama Python library übergibt das Schema als JSON-Objekt an den Parameter format
    • Das Schema kann als dict übergeben werden; empfohlen wird die Serialisierung mit model_json_schema() aus Pydantic
    • Im Beispiel definiert das Modell Country die Felder name, capital und languages, und die Antwort wird mit Country.model_validate_json() validiert
country = Country.model_validate_json(response.message.content)
  • Die Beispielausgabe gibt Canada, Ottawa, English und French passend zu den definierten Feldern zurück
  • JavaScript

    • Die Ollama JavaScript library übergibt das Schema als JSON-Objekt an den Parameter format
    • Das Schema kann als object übergeben werden; empfohlen wird die Verwendung von Zod zusammen mit zodToJsonSchema()
    • Im Beispiel wird das Schema Country als Zod-Objekt definiert, der Antworttext mit JSON.parse() verarbeitet und anschließend mit Country.parse() validiert
const country = Country.parse(JSON.parse(response.message.content));

Daten aus Texten und Bildern extrahieren

  • Extraktion aus Haustiertexten

    • Strukturierte Ausgaben können verwendet werden, um benötigte Informationen aus Texten zu extrahieren
    • Das Beispiel definiert die Pydantic-Modelle Pet und PetList und gibt Informationen zu Haustieren in einer JSON-Struktur zurück
      • Pet: name, animal, age, color, favorite_toy
      • PetList: Array pets
    • Der Eingabetext enthält Informationen zu zwei Katzen
      • Luna: 5 Jahre alt, graues Fell, mag yarn
      • Loki: 2 Jahre alt, schwarz, mag tennis balls
    • Die Ausgabe wird gemäß dem definierten Schema als Liste von Pet-Objekten validiert
  • Bildbeschreibung mit Vision-Modell

    • Strukturierte Ausgaben können zusammen mit Vision-Modellen verwendet werden
    • Das Beispiel analysiert ein Bild mit llama3.2-vision und gibt ein Ergebnis zurück, das dem Schema ImageDescription entspricht
    • Das Schema enthält die folgenden Felder
      • summary
      • objects
      • scene
      • colors
      • time_of_day
      • setting
      • text_content
    • Die Beispielanfrage weist an, Objekte, Szene, Farben und erkennbaren Text im Bild zu analysieren
    • In den Optionen wird temperature auf 0 gesetzt, um eine deterministischere Ausgabe zu erhalten
    • Die Beispielausgabe fasst eine Szene mit Palmen am Strand zusammen und gibt strukturierte Felder für tree und beach, Farben, Tageszeit und Outdoor-Umgebung zurück

Verwendung der OpenAI-kompatiblen API

  • Das OpenAI-kompatible Beispiel konfiguriert den OpenAI-Client mit base_url="http://localhost:11434/v1"; und api_key="ollama"
  • Mit client.beta.chat.completions.parse() wird im response_format das Pydantic-Modell PetList übergeben
  • In der Antwort wird completion.choices[0].message geprüft; falls parsed vorhanden ist, wird das geparste Ergebnis ausgegeben
  • Falls refusal vorhanden ist, wird eine Ablehnungsantwort ausgegeben; openai.LengthFinishReasonError wird als Fall behandelt, in dem zu viele Tokens erzeugt wurden

Einstellungen für stabile Ausgaben und künftige Pläne

  • Für die Definition des Antwortschemas wird die Verwendung von Pydantic in Python oder Zod in JavaScript empfohlen
  • Es ist sinnvoll, dem Prompt „return as JSON“ hinzuzufügen, damit das Modell die Anforderung versteht
  • Für deterministischere Ausgaben sollte temperature auf 0 gesetzt werden
  • Für die Zukunft sind folgende Punkte geplant
    • Logits-Freigabe für kontrollierte Generierung
    • Verbesserungen bei Leistung und Genauigkeit strukturierter Ausgaben
    • GPU-Beschleunigung für Sampling
    • Unterstützung weiterer Formate über JSON Schema hinaus

1 Kommentare

 
GN⁺ 2024-12-09
Hacker-News-Kommentare
  • Wenn stärkere Ausgabebeschränkungen nötig sind, unterstützt llama.cpp GBNF
    https://github.com/ggerganov/llama.cpp/blob/master/grammars/...

    • Es sieht so aus, als würden sie genau das verwenden
    • Ich frage mich, ob die Ausgabequalität für beliebige Grammatiken zufriedenstellend war
      Rein intuitiv würde ich denken, dass Modelle JSON besser erzeugen als andere Formate, weil sie davon deutlich mehr gesehen haben
    • Ich frage mich, inwiefern das leistungsfähiger ist
  • Gute Nachricht
    Beim Erzeugen von CSV-Daten habe ich mich gefragt, wie man einen normalen Prompt so formulieren soll, dass keine unnötigen Phrasen wie "Here is your data" oder "Please note blah blah" davor oder danach erscheinen; deshalb freut es mich, dass man jetzt das gewünschte Rückgabeformat exakt definieren und die strukturierte Ausgabe direkt als CSV erhalten kann

    • Trotzdem sollte man weiterhin die Anweisung zur CSV-Erzeugung geben, damit der Prompt im richtigen Kontext steht
      Sonst kann zwar technisch gesehen CSV herauskommen, aber ohne sinnvolle Bedeutung. Das Modell wollte möglicherweise eigentlich eine Antwort in Absatzform schreiben, und der Token-Sampler hat dann nur Tokens mit niedriger Wahrscheinlichkeit gewählt, die das Modell gar nicht besonders ausgeben wollte
    • In vielen Fällen lässt sich das verhindern, indem man den Anfang der Ausgabe mit \n vorab füllt und bei stoppen lässt
  • Funktioniert. Ich habe gemma2:2b den folgenden Satz gegeben und das gewünschte JSON erhalten
    You have spent 190 at Fresh Mart. Current balance: 5098
    Das Ergebnis war {"amount": 190, "balance": 5098, "category": "Shopping", "place": "Fresh Mart"}

    • Ziemlich inkonsistentes JSON, aber bei einem so kleinen Modell und dann noch gemma ist das wohl zu erwarten
  • Wirklich erstaunlich. Das war tatsächlich eine der Funktionen, die ich mir gewünscht habe
    Der Reiz von ollama liegt darin, dass es sich anfühlt, als würde man ein LLM wie ein anderes UNIX-Programm verwenden, und als gehöre ein LLM ganz natürlich in UNIX hinein
    Mich würde allerdings interessieren, ob es jemand gut auf einer AMD GPU zum Laufen gebracht hat. Ich habe gehört, dass es schwieriger ist, würde aber beim nächsten Kartenkauf nächstes Jahr gern den Wettbewerber unterstützen

    • Geht. Sogar auf einer integrierten GPU
      Auf einem Mini-PC mit 780M lief es ziemlich gut, nachdem ich im BIOS 16 GB Shared Memory zugewiesen hatte
  • Ich frage mich, welche Auswirkungen solche Beschränkungen auf die Qualität der LLM-Ausgabe haben
    In manchen Fällen würde ich lieber Markdown oder Klartext parsen, wenn die Ausgabequalität dann höher ist

    • Beim Einsatz von OpenAI-Modellen war die Strategie mit zwei Durchläufen sehr gut, wenn man die zusätzlichen Token-Kosten tragen kann
      Im ersten Durchlauf lässt man ein größeres Modell mit natürlicher Sprache die Schlussfolgerung in Markdown-Abschnitten behandeln und die endgültige Antwort in natürlicher Sprache liefern. Wenn möglich, klar mit Markdown-Überschriften beschriften
      Im zweiten Durchlauf wandelt ein günstigeres und schnelleres Modell diese Antwort in ein strukturiertes Ausgabeformat um, das vom nicht-LLM-Teil der Pipeline konsumiert wird
      Im Grunde nutzt man den JSON-Schema-Modus, um saubere Grenzen um mehrdeutige Teile natürlicher Sprache zu ziehen, und lässt das LLM wie einen Vorverarbeiter arbeiten, der seine eigene Ausgabe in ein nützliches Format überführt
    • Es hängt davon ab, wie stark das Modell auf JSON-Ausgabe feinabgestimmt ist
      Außerdem muss man dem Modell das Schema mitteilen. Wenn man das nicht tut, treten eher seltsame Tokenisierungsprobleme auf
      Wenn das Schema zum Beispiel den JSON-Key "foobarbaz" erwartet und die Standard-BPE-Tokenisierung ["foobar", "baz"] ist, dann können die Token-Masken, die heutige Libraries für constrained output erzeugen, dem Modell erlauben, zwischen "f", "foo" und "foobar" zu wählen. Wenn das Modell "foo" wählt, erzwingt die Beschränkung als nächstes Token etwa "bar" und "baz". Dann sieht das Modell ["foo", "bar", "baz"] statt ["foobar", "baz"], was es verwirrt [0]
      Wenn das Modell im Prompt weiß, dass "foobarbaz" einer der Schema-Keys ist, bevorzugt es im Allgemeinen "foobar" gegenüber "foo"
      [0] Bei neueren Modellen sind diese Tokens durch Normalisierung zwar verwandt, aber nicht identisch
    • Es ist situationsabhängig, aber in Bezug auf die Schlussfolgerung gibt es einen negativen Effekt, und in den meisten Fällen war der Unterschied nicht besonders groß
      Es hängt stark davon ab, ob das LLM und der Prompt überhaupt von sich aus wahrscheinlich eine JSON-Antwort erzeugen würden. Je stärker man das LLM in eine Richtung zwingt, desto geringer die Chance, dass es eine normale Eingabe erzeugt
      Bei kleinen Modellen erreicht man schneller den Rand des Bereichs, in dem noch sinnvolle Vorhersagekraft vorhanden ist, und die Ausgabe beginnt eher wie zufälliges Rauschen zu wirken
      Das ist keine strenge Messung, sondern ein Bauchgefühl nach viel Zeit in verschiedenen LLM-Projekten. Diese konkreten Werkzeuge habe ich noch nicht benutzt, aber ollama konnte früher schon auf eine ähnlich wirkende Weise JSON-Ausgabe garantieren, und ich habe mit einem Partner auch an etwas JSONformer-Ähnlichem für oobabooga gearbeitet, ein weiteres LLM-Runtime-Tool
    • Ich verfolge die dazugehörige Forschung weiterhin aufmerksam. Ich schaue mir Sampling allgemein an, um sowohl Geschwindigkeit als auch Genauigkeit zu verbessern
      Ich hoffe, dass sich mit solchen Änderungen irgendwann auch allgemeine strukturierte Generierung ermöglichen lässt, nicht nur JSON
    • Man könnte sagen, dass ich mit meiner Einschätzung zum Nutzen von Tools wie instructor völlig falschlag
      Man kann leicht viele Tokens verbrauchen, aber wenn der Anwendungsfall die Kosten rechtfertigt, lässt sich damit ziemlich viel erzwingen. Auch wenn es nicht die absolut höchste Qualität ist, lohnt sich ein Werkzeug, das ohne großen Aufwand 95 % erreicht, auf jeden Fall für den Werkzeugkasten
  • Ich frage mich, ob man das mit jedem unterstützten Modell verwenden kann
    Auf meiner Hardware kann ich nur 1B- bis 3B-Modelle stabil betreiben, deshalb frage ich

    • Hier der Autor des Blogposts. Es sollte mit jedem Modell funktionieren
      Bei kleinen Modellen können die Ergebnisse schwanken, aber die Bitte, „x als JSON zurückzugeben“, hat tendenziell bei der Genauigkeit geholfen
  • Der PR für diese Funktion war fast ein Jahr lang offen
    Etwas schade, dass die Maintainer so still waren

    • Ich bin der Verfasser des Artikels und einer der Maintainer. Stimme zu. Die Maintainer haben spät reagiert, und insgesamt würde ich gern mehr Beiträge fördern
      Hoffentlich schaffen wir es nächstes Jahr, Community-PRs besser zu begleiten und zusammenzuführen
    • Der Stimmung nach scheint es eher in eine firmenzentrierte Richtung zu gehen, sodass alles durch diese Linse und unter dem Gesichtspunkt der Gewinnmaximierung betrachtet wird
    • Ich wollte auch gern mit daran arbeiten, aber das Gespräch ist plötzlich abgerissen
  • Ich nutze immer noch oobabooga. Dank exlv2 ist auf einer Dual-3090 deutlich effizientere Inferenz möglich.

    • Ich habe ooba schon eine Weile nicht mehr angefasst, aber mich würde interessieren, wie der Stand bei exl2 und heterogenen Quantisierungsmethoden wie q3k_s ist.
      Soweit ich mich erinnere, war exl2 zwar schneller, aber insbesondere bei geringer Bittiefe schien die gptq-Quantisierung bei der Genauigkeit besser zu sein.
  • Ich frage mich, welchen Mehrwert es im Vergleich zu outlines gibt.
    https://www.souzatharsis.com/tamingLLMs/notebooks/structured...

    • Ich bin der Autor des Blogposts. Die aktuelle Implementierung konnte schnell umgesetzt werden, weil sie llama.cpp GBNF verwendet. Der größte Mehrwert im Moment ist, dass die Funktion veröffentlicht wurde.
      Auf Grundlage der aktuellen Forschung zu outlines/xgrammar hoffe ich, das Sampling so aktualisieren zu können, dass mehr Formate unterstützt, die Genauigkeit erhöht und die Performance verbessert werden.
  • Ich frage mich, ob es eine beste Methode gibt, dem LLM strukturierte Eingaben zu geben.
    Zum Beispiel, wenn man 100 Sätze eingibt und jeden davon auf mehrere Arten klassifizieren lassen will. Strukturierte Daten zu empfangen ist einfach, aber meine Methode, Zeilennummern voranzustellen, wirkt unbeholfen.

    • Die Modelle wurden mit Markdown, JSON und verschiedenen Programmiersprachen trainiert, also sollte eines davon funktionieren.
      In diesem Fall ist es aber am besten, die Sätze einzeln zu geben, damit das Modell nicht durcheinanderkommt.
      Wenn du den Prompt als "Klassifiziere den folgenden Satz. Die Regeln sind ..." + Satz aufbaust, kannst du Prefix-Caching nutzen, wodurch die Performance sogar besser sein kann als bei einer Sammelanfrage.
      Das funktioniert natürlich nur, wenn Prefix-Caching verfügbar ist und nicht pro Eingabetoken abgerechnet wird. Die meisten Anbieter ermöglichen inzwischen geringere Kosten, wenn man angibt, dass man Prefix-Caching nutzen möchte.