Show HN: Outlines - LLM’lerin geçerli JSON gibi yapılandırılmış çıktılar üretmesini garanti eden araç
(github.com/normal-computing)- Outlines, LLM’ler için bir yapılandırılmış çıktı aracıdır; üretim sırasında istenen çıktı türünü belirterek verinin ilgili yapıyla tam olarak eşleşmesini garanti eder
- Üretimden sonra hatalı çıktıları ayrıştırma, regex veya kırılgan kodlarla düzeltmek yerine, üretim aşamasında geçerli yapıyı doğrudan zorunlu kılar
- Kullanım biçimi
model(prompt, output_type)şeklindedir;Literal["Yes", "No"],int, Pydantic model gibi Python tip sistemiyle uyumlu çıktı tanımları yapılabilir - Desteklenen çıktı türleri: önceden tanımlı seçenekler, fonksiyon imzası tabanlı Function Calls, JSON/Pydantic şemaları, regex kalıpları ve grammar tabanlı yapı zorlamadır
- Model entegrasyonları; sunucu destekli vLLM·Ollama, yerel model destekli transformers·llama.cpp ve API destekli OpenAI·Gemini·Dottxt olarak ayrılır
- Örnek iş akışları; müşteri e-postasını servis biletine dönüştürme, ürün açıklamasını kategori verisine dönüştürme, eksik etkinlik açıklamasından yapılandırılmış bilgi veya
"I don't know"döndürme, belgeleri önceden tanımlı kategorilere sınıflandırma ve doğal dille yazılmış toplantı isteğini fonksiyon parametrelerine dönüştürme şeklindedir - Prompt şablonları Jinja tabanlı
outlines.Template.from_stringve dosyadan yüklemeyi destekler; karmaşık prompt’ları koddan ayırıp yeniden kullanmak içindir - Kurulum
pip install outlinesile yapılır;.txt APIşu anda early access durumunda gösterilmektedir
1 yorum
Hacker News görüşleri
Bu kütüphane, mekanizma olarak sözlük uzayının bir kısmını maskeleme ve zaman adımlarında verimli biçimde ilerleme şeklinde basit bir fikir kullanıyor gibi görünüyor; harika.
Ancak temel bir LLM’in çıktısına yapı giydiren kütüphaneleri kullanmış biri olarak, Llama2 gibi temel modellerin gerçekten iyi çalışıp çalışmadığını merak ediyorum.
Benim deneyimime göre yanıt “hiç de değil”e yakın; gerçekten çalışır hâle getirmek için belirli kullanım amacına yönelik epey talimat ayarı gerekiyordu.
Üstelik talimat ayarlı bir modelde üretim sırasında sonradan durum uzayını maskelemek, sonuçta üretim dağılımını değiştirmek anlamına geliyor; bunun talimat ayarına zarar verebileceği sezgisel olarak ters görünüyor.
İkinci noktaya gelince; hedef, örneğin modelin yalnızca JSON üretmesini sağlamaksa, hangi çıktı token’larının kullanılabileceğini ve kullanılamayacağını sınırlayarak bunu %100 yapmak mümkün.
M2 Mac’te GPU kullanabildiğim için MLC sürümünü https://github.com/simonw/llm-mlc eklentisiyle kullanıyorum.
“Guiding Language Models of Code with Global Context using Monitors” makalesinde (https://arxiv.org/abs/2306.10763) Monitor Guided Decoding öneriliyor; LLM’i statik analizle bağlayarak tip açısından tutarlı kod üretmeye yönlendiriyor.
Hiçbir ince ayar yapmadan, belirli noktalarda statik analizle token düzeyinde üretimi yönlendirmek, üretilen kodun kalitesini hem derlenebilirlik hem de doğru cevapla uyum açısından büyük ölçüde artırdı. Çok küçük bir model (1.1B) bile çok daha büyük bir modelden (175B) daha fazla derlenebilir kod üretirken doğru cevapla uyumu da iyileştirdi.
Başlıca fark, üretim fonksiyonunun LLM değil insan olmasıydı. Aradaki insanı çıkarmamak için bir neden göremiyorum.
Geleneksel kodda sınır koşulları kelimenin tam anlamıyla küçük özel durumlara daha yakındı; LLM’lerde ise neyin modeli saçma bir yöne savuracağını bilemiyorsunuz ve ayrıştırma kodunun bu kaosu kaldırması gerekiyor.
Başka bir deyişle, sınır koşulu olarak ele alınması gereken durumların oranı dramatik biçimde artmış gibi.
GPT-4, sistem mesajına yalnızca örnekler koysanız bile geçerli JSON döndürebiliyor ve on denemenin dokuzunda çalışıyor.
Ama hâlâ olasılıksal ve on denemenin dokuzu yeterli değil.
Bazen
{"key1": "value1", "key2": "value2" for i in range(n)}gibi halüsinasyonlu yanıtlar da üretebiliyor.Ayrıştırma hata mesajıyla birlikte tekrar prompt verdiğinizde genelde ikinci denemede çözülüyor.
Ancak çift tırnakları ve satır sonu karakterlerini escape etmek daha az kararlı. Birkaç örnek verseniz bile yaklaşık yarısını doğru escape ediyor; escape hatasını yeniden prompt etseniz de başarı oranı yaklaşık %50’de kalıyor.
İlgili prompt burada: https://github.com/microsoft/TypeChat/blob/c45460f4030938da3...
Burada görülen dilbilgisi tabanlı yaklaşımın veya https://github.com/ggerganov/llama.cpp/pull/1773 benzeri yöntemlerin çok daha zarif çözümler olduğunu düşünüyorum.
İnsanların JSON istemesini anlıyorum ama bana göre bu, bir kediyi yüzdürmeye çalışmaya benziyor. Sonunda başarmak mümkün olsa da doğal eğilim bu değil.
Başlıca hata, bir kaçış yolu sunmamak. LLM doğru yanıtı bulmaya çalıştığı için, metinleri verip yapılandırılmış veri döndürmesini istediğinizde metinlerden biri boşsa doğru cevabı belirlemek zorlaşıyor ve halüsinasyon oluşuyor.
Çözüm, argümanlardan biri olarak
textIsMissinggibi bir boolean koymak şeklinde bir kaçış yolu oluşturmak. Bu tür hata biçimlerini hesaba katarsanız kusursuz çalışıyor.Birkaç kez tekrar denedim ve ardından düzeltme de yaptırdım ama doğrulamadan geçemedi; %100 geçerli JSON’u bir kez bile üretemeyince sonunda vazgeçtim.
Birincisi, prompt’a çok fazla örnek koymanız gerekmediği için daha az token kullanıyor.
İkincisi, unutma probleminden daha az etkileniyor.
Küçük bir avantaj olarak, istediğiniz çıktının nerede başlaması gerektiğini tam olarak kontrol edebiliyorsunuz. Yine de genel olarak bunu güzel bir ek özellik olarak görüyorum; çok temel bir şey olduğunu düşünmüyorum.
LLM’lerin gücünün büyük bir kısmı, yanıtlarındaki kalibre edilmiş olasılık dağılımından geliyor; bu teknik muhtemelen o yeteneği çöpe atıyor gibi. Bunun neden yeterli sayıldığını merak ediyorum.
Basit bir örnek olarak, LLM’in olası çıktılarının yalnızca “hello world”, “food”, “hello”, “good day” olduğunu ve prompt yokken hepsinin eşit olasılıklı olduğunu varsayalım. Gramerin de yalnızca çıktının bir yerinde boşluk olması kısıtını koyduğunu kabul edelim.
Gramerden geçene kadar LLM çıktısını örneklersek “hello world” ve “good day” aynı olasılıkla gelir. Ama web sitesindeki tekniği uygularsak “hello world”, “good day”den iki kat daha sık çıkar.
Temel sorun şu: Belirli bir yanıt önekinin geçerli bir yanıta yol açma olasılığı son derece düşük olabilir; buna rağmen bu teknik, başarılı olacağını varsayarsak, o önekten geçerli bir yanıt üretir. Uygun noktalarda yeterince bağımsızlık varsa sorun olmayabilir; ama otoregresif modellerde ilişkili hatalar hızla birikir.
Yalnızca JSON özelinde sorarsak: LLM şemaya uymayan yanıtlar üretirken olgusal hataları, halüsinasyonları, kesilmiş dizeleri, önemli karakterleri atlamayı vb. daha mı çok yapar, daha mı az? Olgusal hata oranı şema hata oranıyla önemsiz olmayan bir şekilde ilişkiliyse bu yol göründüğünden daha tehlikeli. Belirli sözcüklerin ya da bitişik kalmış sözcük öbeklerinin LLM çıktısı üzerinde büyük etkiler yaratabildiğini görünce, şema uyumluluğu gibi ayrıntıların da çıktının başka özelliklerine yayılma olasılığı yüksek görünüyor.
“Yanıt önekinin geçerli yanıta yol açma olasılığı çok düşüktü ama teknik bir şekilde geçerli yanıt kurup sorun çıkarıyor” türünden bir örnek düşünmeye çalıştım, fakat aklıma pek gelmedi. İyi bir örnek varsa bu ilginç bir araştırma sorusu olabilir.
Bununla ilgili olarak LLama.cpp geçen ay gramer tabanlı örneklemeyi hayata geçirdi.
https://news.ycombinator.com/item?id=36819906
https://github.com/ggerganov/llama.cpp/pull/1773
Bizim yöntemimiz çok daha verimli. llama.cpp her adımda maske oluşturmak için tüm sözlüğü, yaklaşık 50 bin token’ı dolaşıyor.
Biz başlangıçta bir indeks oluşturuyoruz; her adımda maskeyi kurarken yalnızca sözlük araması yapmak yeterli oluyor. Bu, hızı bellekle takas eden bir yaklaşım ve örnekleme standart örnekleme kadar hızlı.
Konuyla ilgili çok sayıda makale olduğu düşünülürse başka yerlerde de birkaç tane daha olmuş olmalı. Buradaki ve mevcut çalışmadaki ana fikir, çok düşük maliyetli yönlendirme sağlamak; regex örneğinde bir süredir uygulanmış durumdaydı ve sonrasında JSON’a genişletildi.
Böyle bir şey yaptığınız için teşekkürler; çalışma prensibi o kadar bariz bir fikir ki birinci sınıf platformların bunu hâlâ yapmıyor olması şaşırtıcı.
JSON dışında yapılandırılmış girdi gerektiren başka işlerde nasıl kullanılabileceğini merak ediyorum.
Bu, geliştirici kaynaklarının sınırlı olmasının sonucu da olabilir. 10 yıllık bir teknolojide böyle temel bir özelliğin eksik olması şaşırtıcı olurdu; ama silahlanma yarışı içindeki yapay zeka teknolojisinde bazı kolaylaştırıcı özelliklerin henüz eksik olması anlaşılabilir görünüyor.
Bunun aşağıdaki projelerden nasıl farklı olduğunu pek anlayamadım.
https://github.com/1rgs/jsonformer
https://github.com/newhouseb/clownfish
https://github.com/mkuchnik/relm
https://github.com/ggerganov/llama.cpp/pull/1773
https://github.com/Shopify/torch-grammar
Genel olarak bu tür logit tabanlı yönlendirme sistemleri çok sayıda var ve fazla traction alamamalarının nedeni, en iyi performanslı modellerin bu kadar ince ayarlı yaklaşımlara izin vermeyen REST API’lerin arkasında olması.
O modeller çok daha iyi performans gösterdiği için insanlar genelde doğru biçim çıkana kadar yeniden istek göndermekle yetiniyor. GPT-4’te benim deneyimime göre bu tür başarısızlıklar da oldukça nadir.
Biz başlangıçta sözlüğü bir kez dolaşıp bir indeks oluşturuyoruz; sonraki üretim standart üretim kadar hızlı.
Başka şekilde açıklarsak, LLM her bir token ürettiğinde bir sonraki token'ın yalnızca geçerli bir JSON token'ı olmasını sağlayacak şekilde logit bias “maskesini” güncelliyor mu? Çok hoş
Bu yöntemin gerçekten %100 garanti verebildiğinden emin değilim
Düzeltme: Makale bu kısmı daha dikkatli açıklıyor
Bu Brandon Willard, Detroit'li break dansçı Brandon Willard mı?
Düzeltme: Evet! https://brandonwillard.github.io/
remilouf, kendi geçmişini “olasılıksal, ilişkisel, sembolik programlama” olarak anlattığına göre, düzenli gramerler ya da bağlamdan bağımsız gramerler, hatta fiilen herhangi bir düzeydeki gramerle metin üretmenin sorun olmadığını anlıyor olmalı
Örneğin ilişkisel bir dil olan Prolog'da, yalnızca gramer verildiğinde Definite Clause Grammars gösterimiyle bunu yapmak çok kolay
Bana göre bu yaklaşım kullanıcının grameri sağlamasını gerektiriyor. Öyleyse metin üretimi için LLM kullanmanın avantajı nedir, merak ediyorum
İstenen metni üretmek için grameri doğrudan bir üreteç olarak çalıştırmamak için ne sebep var? Böylece LLM'i eğitmenin baştaki büyük zahmeti ve maliyeti azaltılabilir. Amaç yalnızca yapılandırılmış metin üretmekse, doğal dil modeli olan LLM'e neden gerek olduğu da soru işareti
Yalnızca yapılandırılmış metin isteseniz bile LLM'e ihtiyaç duyulmasının nedeni, insanların yazdığı yapılandırılmamış metni ayrıştırıp makinenin kullanabileceği yapılandırılmış veriyi döndürmek
İstek “pi sayısının ilk 10 basamağı nedir?” ise ve yanıtı
"[0-9]+\.[0-9]+"düzenli ifadesiyle sınırlandırdıysanız, amaç yalnızca kalıba uyan “1.2346789” gibi rastgele bir dizge değil, gerçek doğru yanıt olan 3.1415926535 değerini almaktırİlginç; yakın zamanda biz de llama çıktısını bir TypeScript interface ile uyumlu olacak şekilde kısıtlayan benzer bir araç yaptık[1]
Çıktı biçimi garantisinin, LLM'lerin gerçek ve oyuncak olmayan kullanım alanlarında yer alacağı önümüzdeki on yıllar boyunca önemli olacağına kesinlikle inanıyorum
[1] https://github.com/ggerganov/llama.cpp/discussions/2494