1 poin oleh GN⁺ 2023-08-15 | 1 komentar | Bagikan ke WhatsApp
  • Outlines adalah alat output terstruktur untuk LLM, yang memungkinkan penentuan tipe output yang diinginkan saat proses generasi sehingga data dijamin sesuai tepat dengan struktur tersebut
  • Alih-alih memperbaiki output yang salah setelah generasi dengan parsing, regex, atau kode rapuh, alat ini langsung memaksakan struktur yang valid pada tahap generasi
  • Cara penggunaannya adalah dalam bentuk model(prompt, output_type), dan memungkinkan penentuan output yang selaras dengan sistem tipe Python seperti Literal["Yes", "No"], int, atau model Pydantic
  • Jenis output yang didukung mencakup pilihan yang telah ditentukan sebelumnya, Function Calls berbasis signature fungsi, skema JSON/Pydantic, pola regex, dan pemaksaan struktur berbasis grammar
  • Integrasi model dibagi menjadi dukungan server vLLM·Ollama, dukungan model lokal transformers·llama.cpp, serta dukungan API OpenAI·Gemini·Dottxt
  • Contoh alur kerja meliputi mengubah email pelanggan menjadi tiket layanan, mengubah deskripsi produk menjadi data kategori, mengembalikan informasi terstruktur atau "I don't know" dari deskripsi acara yang tidak lengkap, mengklasifikasikan dokumen ke kategori yang telah ditentukan, dan mengubah permintaan rapat dalam bahasa alami menjadi parameter fungsi
  • Template prompt mendukung outlines.Template.from_string berbasis Jinja serta pemuatan dari file, untuk memisahkan prompt yang kompleks dari kode dan menggunakannya kembali
  • Instalasi tersedia melalui pip install outlines, dan .txt API saat ini ditandai sebagai dalam tahap early access

1 komentar

 
GN⁺ 2023-08-15
Komentar Hacker News
  • Pustaka ini tampaknya memakai ide sederhana yang sangat bagus: secara mekanisme, ia memask sebagian ruang kosakata dan melangkah secara efisien pada tiap tahap waktu.
    Namun, sebagai orang yang pernah memakai pustaka yang menambahkan struktur keluaran di atas LLM dasar, saya penasaran apakah model dasar seperti Llama2 benar-benar bekerja dengan baik.
    Dari pengalaman saya, jawabannya lebih dekat ke “sama sekali tidak”, dan agar benar-benar berfungsi, diperlukan cukup banyak instruction tuning yang disesuaikan untuk penggunaan tertentu.
    Selain itu, melakukan masking ruang keadaan secara post-hoc selama generasi pada model yang sudah di-instruction-tune pada akhirnya mengubah distribusi generasi, sehingga secara intuitif terasa bertentangan karena bisa saja merugikan instruction tuning.

    • Saya tidak begitu paham alasan harus memakai llama-2 asli. Di HF Hub ada sangat banyak versi llama-2 kuat yang sudah di-fine-tune untuk instruksi, dan model-model seperti itu akan bekerja jauh lebih baik. Contohnya Beluga-2 dari Stability-AI: https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderb...
      Untuk poin kedua, jika tujuannya misalnya membuat model hanya menghasilkan JSON, itu 100% bisa dilakukan dengan membatasi token keluaran mana yang boleh dan tidak boleh dipakai.
    • Saya cukup terkesan dengan Llama 2 13B, dan makin lama memakainya, makin terasa bahwa mungkin ia benar-benar berguna, bukan sekadar mainan LLM lokal.
      Karena bisa memakai GPU di M2 Mac, saya menggunakan versi MLC melalui plugin https://github.com/simonw/llm-mlc.
    • Setidaknya dalam generasi kode, mengarahkan generasi pada level token bisa meningkatkan model dasar secara signifikan.
      Makalah “Guiding Language Models of Code with Global Context using Monitors”(https://arxiv.org/abs/2306.10763) mengusulkan Monitor Guided Decoding, yang menghubungkan LLM dengan analisis statis untuk mengarahkan model agar menghasilkan kode yang konsisten secara tipe.
      Tanpa fine-tuning apa pun, mengarahkan generasi level token dengan analisis statis pada titik-titik tertentu meningkatkan kualitas kode yang dihasilkan secara besar, baik dari sisi kemampuan dikompilasi maupun kesesuaian dengan jawaban benar. Bahkan model yang sangat kecil (1.1B) menghasilkan lebih banyak kode yang bisa dikompilasi dibanding model yang jauh lebih besar (175B), sekaligus meningkatkan kesesuaian jawaban.
    • Kalau “masking post-hoc selama generasi pada model yang sudah di-instruction-tune mengubah distribusi generasi”, rasanya itu bukankah yang dilakukan dalam test-driven development?
      Perbedaan utamanya hanya fungsi generasinya adalah manusia, bukan LLM. Saya tidak melihat alasan mengapa perantara manusia itu tidak boleh dihilangkan.
    • Instruction tuning itu sendiri tergolong “sepele”; yang benar-benar sulit adalah menangani kondisi batas.
      Dalam kode tradisional, kondisi batas secara harfiah lebih mirip kasus khusus kecil, tetapi pada LLM kita tidak tahu apa yang akan membuatnya melenceng ke arah aneh, dan kode parsing harus menanggung kekacauan itu.
      Dengan kata lain, rasanya proporsi kasus yang harus diperlakukan sebagai kondisi batas meningkat drastis.
  • GPT-4 bisa dibuat mengembalikan JSON valid hanya dengan memasukkan contoh di pesan sistem, dan berhasil sembilan dari sepuluh kali.
    Namun tetap saja probabilistik, dan sembilan dari sepuluh belum cukup.
    Kadang ia juga membuat respons halusinasi seperti {"key1": "value1", "key2": "value2" for i in range(n)}.
    Jika diprompt ulang bersama pesan error parsing, biasanya percobaan kedua akan beres.
    Namun escaping tanda kutip ganda dan karakter baris baru kurang stabil. Meski diberi beberapa contoh, hanya sekitar separuh yang di-escape dengan benar, dan walaupun error escaping diprompt ulang, tingkat keberhasilannya tetap sekitar 50%.

    • Pendekatan memprompt ulang saat terjadi error juga digunakan pustaka baru Microsoft, TypeChat: https://github.com/microsoft/TypeChat
      Prompt terkait ada di sini: https://github.com/microsoft/TypeChat/blob/c45460f4030938da3...
      Saya melihat pendekatan berbasis tata bahasa seperti yang tampak di sini, atau cara seperti https://github.com/ggerganov/llama.cpp/pull/1773, sebagai solusi yang jauh lebih elegan.
    • Meminta keluaran XML ternyata bekerja lebih baik daripada JSON. (1) XML bisa memuat lebih banyak bahasa nyata dan makna, dan LLM menyukai hal semacam itu, serta (2) parser bisa dibuat lebih toleran.
      Saya paham orang-orang menginginkan JSON, tetapi bagi saya itu mirip mencoba membuat kucing berenang. Pada akhirnya bisa saja berhasil, tetapi itu bukan kecenderungan alaminya.
    • Dengan function calling ChatGPT, selama tidak ada kesalahan dalam prompt, saya mendapatkan JSON valid dari GPT-4 100% setiap kali.
      Kesalahan utamanya adalah tidak menyediakan jalan keluar. Karena LLM berusaha menemukan jawaban yang benar, jika kita memasukkan teks-teks dan memintanya mengembalikan data terstruktur tetapi ada teks yang kosong, ia jadi sulit menentukan jawaban yang benar dan halusinasi pun muncul.
      Solusinya adalah membuat jalan keluar, misalnya menambahkan boolean seperti textIsMissing sebagai salah satu argumen. Jika pola kegagalan seperti ini diperhitungkan, semuanya berjalan mulus.
    • Saya pernah meminta GPT-4 mengembalikan kode PHP contoh di dalam JSON arbitrer, dan sejak percobaan pertama hasilnya tidak lolos JSON linter.
      Saya mencoba ulang berkali-kali dan bahkan meminta perbaikan lanjutan, tetapi tetap tidak lolos validasi; tidak sekali pun ia menghasilkan JSON yang 100% valid, jadi akhirnya saya menyerah.
    • Generasi dengan batasan tata bahasa punya dua keunggulan besar.
      Pertama, tidak perlu memasukkan terlalu banyak contoh ke prompt, sehingga memakai lebih sedikit token.
      Kedua, lebih sedikit terdampak masalah lupa.
      Keunggulan kecilnya, kita bisa mengontrol secara tepat dari mana keluaran yang diinginkan harus dimulai. Namun secara keseluruhan, saya menganggapnya fitur tambahan yang bagus, bukan sesuatu yang sangat esensial.
  • Sebagian besar kekuatan LLM ada pada distribusi probabilitas yang terkalibrasi dari responsnya, dan teknik ini tampaknya mungkin membuang kemampuan itu. Saya bertanya-tanya mengapa ini dianggap cukup
    Sebagai contoh sederhana, anggap keluaran LLM yang mungkin hanya “hello world”, “food”, “hello”, dan “good day”, dan semuanya berprobabilitas sama saat tidak ada prompt. Anggap tata bahasanya hanya memberi batasan bahwa harus ada spasi di suatu tempat dalam keluaran
    Jika kita mengambil sampel keluaran LLM sampai lolos tata bahasa, kita akan mendapatkan “hello world” dan “good day” dengan probabilitas yang sama. Namun jika menerapkan teknik dari situs web itu, “hello world” akan muncul dua kali lebih sering daripada “good day”
    Masalah intinya adalah, meskipun sebuah prefiks jawaban mungkin memiliki peluang yang amat rendah untuk berujung pada respons yang valid, teknik ini—dengan asumsi berhasil—akan membentuk respons valid dari prefiks tersebut. Jika cukup independen di tempat yang tepat, mungkin tidak masalah, tetapi pada model autoregresif, kesalahan yang berkorelasi cepat menumpuk
    Kalau dibatasi pada JSON: saat LLM membuat respons yang tidak memenuhi skema, apakah ia menghasilkan lebih banyak atau lebih sedikit kesalahan faktual, halusinasi, string terpotong, penghilangan tokoh utama, dan sebagainya? Jika tingkat kesalahan faktual terkait dengan tingkat kesalahan skema secara tidak sepele, jalur ini lebih berbahaya daripada kelihatannya. Melihat bagaimana kata tertentu atau frasa yang menempel bisa sangat memengaruhi keluaran LLM, detail seperti kepatuhan skema juga tampaknya sangat mungkin merembet ke karakteristik keluaran lainnya

    • Jika ini pembuatan pilihan ganda seperti kasus ini, saat salah satu keluaran yang mungkin tidak cocok dengan regex, cukup keluarkan saja dari proses generasi
      Saya mencoba memikirkan contoh “prefiks jawaban punya peluang sangat rendah untuk berujung pada respons valid, tetapi tekniknya entah bagaimana menyusun respons valid sehingga menimbulkan masalah”, tetapi belum terpikir. Kalau ada contoh yang bagus, itu bisa menjadi pertanyaan riset yang menarik
  • Terkait hal ini, LLama.cpp bulan lalu mengimplementasikan sampling berbasis tata bahasa
    https://news.ycombinator.com/item?id=36819906
    https://github.com/ggerganov/llama.cpp/pull/1773

    • Seperti yang dijelaskan dalam makalah di atas, pendekatan kami juga bisa diperluas menjadi sampling berbasis tata bahasa. PR terkait: https://github.com/normal-computing/outlines/pull/178
      Cara kami jauh lebih efisien. llama.cpp menelusuri seluruh kosakata, sekitar 50 ribu token, untuk membuat mask di setiap langkah
      Kami membuat indeks saat inisialisasi, lalu pada setiap langkah cukup melakukan lookup dictionary untuk menyusun mask. Ini menukar kecepatan dengan memori, dan sampling-nya secepat sampling standar
    • Di periode yang mirip, kami juga punya implementasi panduan berbasis tata bahasa: https://github.com/normal-computing/outlines/pull/131
      Melihat banyaknya makalah terkait, kemungkinan ada beberapa tempat lain juga. Poin dari ini dan pekerjaan saat ini adalah menyediakan panduan dengan biaya sangat rendah; untuk kasus regex sudah diimplementasikan cukup lama, lalu kemudian diperluas ke JSON
  • Terima kasih sudah membuat hal seperti ini; mekanisme kerjanya terasa seperti ide yang sangat jelas, jadi mengejutkan bahwa platform pihak pertama belum melakukannya
    Selain JSON, saya penasaran bagaimana ini bisa dipakai untuk pekerjaan lain yang membutuhkan input terstruktur

    • Saya memahami bahwa teknologi LLM sekarang berada dalam perlombaan senjata yang sangat cepat dan berubah drastis setiap beberapa bulan
      Bisa jadi ini akibat sumber daya developer yang terbatas. Kalau fitur dasar seperti ini tidak ada pada teknologi berusia 10 tahun, itu mengejutkan; tetapi untuk teknologi AI yang sedang dalam perlombaan senjata, masih kurangnya fitur kenyamanan terasa bisa dimaklumi
    • Kami telah memperluas pendekatan ini ke sampling berbasis tata bahasa, dan itu dijelaskan dalam makalah yang ditautkan di atas. PR terkait adalah https://github.com/normal-computing/outlines/pull/178
    • “Guiding Language Models of Code with Global Context using Monitors” di https://arxiv.org/abs/2306.10763 menunjukkan cara membuat model bahasa menghasilkan kode tanpa dereferensi yang dihalusinasikan
  • Saya kurang paham apa bedanya ini dengan proyek-proyek berikut
    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
    Secara umum, sistem panduan berbasis logit seperti ini sangat banyak, dan alasan utamanya tidak mendapat traction besar adalah karena model-model dengan performa terbaik berada di balik REST API yang tidak memungkinkan pendekatan sedetail ini
    Karena model-model itu jauh lebih baik, orang biasanya cukup puas dengan meminta ulang sampai format yang benar keluar. Dalam pengalaman saya, kegagalan seperti itu juga cukup jarang pada GPT-4

    • Terima kasih sudah memberi tahu tentang clownfish dan relm. Setahu saya, library lain menelusuri seluruh kosakata pada setiap langkah generasi
      Kami menelusuri kosakata satu kali saat inisialisasi untuk membuat indeks, lalu setelah itu generasi secepat generasi standar
  • Dengan kata lain, apakah setiap kali LLM menghasilkan satu token, ia memperbarui “mask” bias logit agar token berikutnya hanya bisa berupa token JSON yang valid? Sangat keren

    • Ia harus terus menghasilkan sampai seluruh string menjadi valid, tapi saya tidak tahu apa yang terjadi jika terjebak dalam loop
      Saya tidak yakin bagaimana metode ini benar-benar bisa menjamin 100%
    • Benar. Dan mask bisa diperbarui lewat lookup dictionary tanpa menelusuri seluruh kosakata. Penelusuran penuh itu lambat
    • Karena token JSON tidak persis berkorespondensi dengan logit, tetap diperlukan semacam beam search atau rejection sampling
      Edit: makalahnya menjelaskan bagian ini dengan lebih hati-hati
    • Sebenarnya ini teknik yang sangat lama, dan banyak library melakukan ini. Saya tidak begitu paham apa yang begitu hebat dari library ini
  • Apakah Brandon Willard ini Brandon Willard breakdancer dari Detroit?
    Edit: benar! https://brandonwillard.github.io/

    • Benar. Itu sudah cukup lama, tapi benar-benar menyenangkan
  • remilouf, karena Anda mengatakan latar belakang Anda adalah “pemrograman probabilistik, relasional, dan simbolik”, saya rasa Anda memahami bahwa menghasilkan teks dari tata bahasa reguler, tata bahasa bebas konteks, atau pada dasarnya tata bahasa di level apa pun bukanlah masalah
    Misalnya, dalam Prolog, sebuah bahasa relasional, ini sangat mudah dilakukan dengan notasi Definite Clause Grammars selama tata bahasanya diberikan
    Menurut saya pendekatan ini mengharuskan pengguna menyediakan tata bahasa. Kalau begitu, saya penasaran apa keunggulan memakai LLM untuk menghasilkan teks
    Mengapa tidak cukup menjalankan tata bahasa sebagai generator untuk membuat teks yang diinginkan? Dengan begitu, usaha dan biaya besar untuk melatih LLM sejak awal bisa dikurangi. Jika tujuannya hanya menghasilkan teks terstruktur, saya juga mempertanyakan mengapa LLM, yang merupakan model bahasa alami, diperlukan

    • Bukankah itu akan menghasilkan keluaran yang valid tetapi sepenuhnya acak? Di sini yang dibutuhkan adalah keluaran valid yang relevan dengan permintaan
      Alasan LLM diperlukan meskipun yang diinginkan hanya teks terstruktur adalah untuk mem-parsing teks tak terstruktur yang ditulis manusia dan mengembalikan data terstruktur yang bisa dipakai mesin
    • Tujuannya bukan menghasilkan sembarang string acak yang sesuai tata bahasa
      Jika permintaannya adalah “apa 10 digit pertama pi?” dan respons dibatasi dengan regex "[0-9]+\.[0-9]+", tujuannya adalah mendapatkan jawaban sebenarnya, 3.1415926535, bukan string acak seperti “1.2346789” yang sekadar cocok dengan pola
    • IanCal sudah menjelaskan semuanya. Namun, pendekatan alternatif untuk memakai LLM bersama miniKanren bisa dilihat di https://arxiv.org/abs/1809.02840
  • Menarik, dan baru-baru ini kami juga membuat tool serupa yang membatasi keluaran llama agar cocok dengan interface TypeScript[1]
    Saya sangat yakin jaminan format keluaran akan menjadi penting selama beberapa dekade ke depan saat LLM dipakai untuk kasus nyata yang bukan sekadar main-main
    [1] https://github.com/ggerganov/llama.cpp/discussions/2494