1 điểm bởi GN⁺ 2023-08-15 | 1 bình luận | Chia sẻ qua WhatsApp
  • Outlines là công cụ đầu ra có cấu trúc dành cho LLM, cho phép chỉ định kiểu đầu ra mong muốn trong quá trình sinh để đảm bảo dữ liệu khớp chính xác với cấu trúc đó
  • Thay vì sửa các đầu ra sai sau khi sinh bằng parsing, regex hoặc mã dễ vỡ, công cụ trực tiếp cưỡng chế cấu trúc hợp lệ ngay ở giai đoạn sinh
  • Cách sử dụng có dạng model(prompt, output_type), và có thể chỉ định đầu ra khớp với hệ thống kiểu của Python như Literal["Yes", "No"], int, Pydantic model
  • Các kiểu đầu ra được hỗ trợ gồm lựa chọn được định nghĩa sẵn, Function Calls dựa trên chữ ký hàm, schema JSON/Pydantic, mẫu biểu thức chính quy, và cưỡng chế cấu trúc dựa trên ngữ pháp (grammars)
  • Tích hợp mô hình được chia thành vLLM·Ollama có hỗ trợ máy chủ, transformers·llama.cpp có hỗ trợ mô hình cục bộ, và OpenAI·Gemini·Dottxt có hỗ trợ API
  • Các workflow ví dụ gồm chuyển email khách hàng thành ticket dịch vụ, chuyển mô tả sản phẩm thành dữ liệu danh mục, trả về thông tin có cấu trúc hoặc "I don't know" từ mô tả sự kiện không đầy đủ, phân loại tài liệu theo các danh mục định nghĩa sẵn, và chuyển yêu cầu họp bằng ngôn ngữ tự nhiên thành tham số hàm
  • Mẫu prompt hỗ trợ outlines.Template.from_string dựa trên Jinja và tải từ file, dùng để tách các prompt phức tạp khỏi mã và tái sử dụng
  • Cài đặt bằng pip install outlines, và .txt API hiện được hiển thị là đang ở trạng thái early access

1 bình luận

 
GN⁺ 2023-08-15
Ý kiến trên Hacker News
  • Thư viện này có vẻ dùng một ý tưởng đơn giản về mặt cơ chế là mask một phần không gian từ vựng và tiến hành hiệu quả theo từng bước thời gian; rất hay
    Tuy nhiên, từ góc nhìn của người đã dùng các thư viện áp cấu trúc đầu ra lên LLM nền, tôi tò mò liệu các mô hình nền như Llama2 có thật sự hoạt động tốt không
    Theo kinh nghiệm của tôi thì gần như là “hoàn toàn không”, và để nó thực sự chạy được thì cần khá nhiều instruction tuning cho mục đích cụ thể
    Hơn nữa, việc mask không gian trạng thái theo kiểu hậu xử lý trong lúc sinh trên một mô hình đã được instruction-tune rốt cuộc cũng là thay đổi phân phối sinh, nên trực giác cho thấy điều đó có thể gây hại cho instruction tuning

    • Tôi không rõ vì sao nhất thiết phải dùng llama-2 nguyên bản. Trên HF Hub có rất nhiều phiên bản mạnh đã được fine-tune theo chỉ dẫn từ llama-2, và các mô hình đó sẽ làm việc tốt hơn nhiều. Ví dụ có Beluga-2 của Stability-AI: https://huggingface.co/spaces/HuggingFaceH4/open_llm_leaderb...
      Về ý thứ hai, nếu mục tiêu là chẳng hạn khiến mô hình chỉ sinh JSON, thì hoàn toàn có thể làm được 100% bằng cách giới hạn token đầu ra nào được phép dùng và token nào không
    • Tôi khá ấn tượng với Llama 2 13B, và càng dùng lâu càng nghĩ rằng nó có thể thực sự hữu ích hơn một món đồ chơi LLM chạy local
      Tôi có thể dùng GPU trên M2 Mac nên đang dùng bản MLC qua plugin https://github.com/simonw/llm-mlc
    • Ít nhất trong sinh mã, việc dẫn dắt quá trình sinh ở cấp token có thể cải thiện đáng kể cả mô hình nền
      Bài báo “Guiding Language Models of Code with Global Context using Monitors”(https://arxiv.org/abs/2306.10763) đề xuất Monitor Guided Decoding, kết nối LLM với phân tích tĩnh để dẫn dắt nó sinh mã có tính nhất quán về kiểu
      Không cần bất kỳ fine-tuning nào, chỉ cần dùng phân tích tĩnh để dẫn dắt sinh ở cấp token tại các điểm cụ thể, chất lượng mã sinh ra đã cải thiện rõ rệt cả về khả năng biên dịch lẫn độ khớp với đáp án đúng. Ngay cả mô hình rất nhỏ (1.1B) cũng tạo ra nhiều mã biên dịch được hơn các mô hình lớn hơn nhiều (175B), đồng thời độ khớp đáp án cũng được cải thiện
    • Nếu “mask hậu kỳ trong lúc sinh trên mô hình đã instruction-tune làm thay đổi phân phối sinh”, thì tôi nghĩ đó chẳng phải là điều vẫn làm trong phát triển hướng kiểm thử sao
      Khác biệt chính chỉ là hàm sinh khi đó là con người chứ không phải LLM. Tôi không thấy có lý do gì để không bỏ người trung gian ra
    • Bản thân instruction tuning là phần tương đối “nhỏ nhặt”, phần thật sự khó là xử lý điều kiện biên
      Trong mã truyền thống, điều kiện biên đúng nghĩa thường gần với các trường hợp đặc biệt nhỏ, nhưng với LLM thì ta không biết điều gì sẽ khiến nó lao sang hướng kỳ quặc, và mã phân tích cú pháp phải gánh sự hỗn loạn đó
      Nói cách khác, cảm giác như tỷ lệ các trường hợp cần được xem là điều kiện biên đã tăng vọt
  • Với GPT-4, chỉ cần đưa ví dụ vào system message là có thể khiến nó trả về JSON hợp lệ, và 9 trên 10 lần sẽ hoạt động
    Nhưng nó vẫn mang tính xác suất, và 9 trên 10 lần là chưa đủ
    Đôi khi nó cũng tạo ra câu trả lời ảo giác kiểu {"key1": "value1", "key2": "value2" for i in range(n)}
    Nếu prompt lại kèm thông báo lỗi parse thì thường lần thử thứ hai sẽ giải quyết được
    Nhưng việc escape dấu ngoặc kép và ký tự xuống dòng thì kém ổn định hơn. Dù đưa nhiều ví dụ, chỉ khoảng một nửa escape đúng, và ngay cả khi prompt lại với lỗi escape thì tỷ lệ thành công vẫn chỉ khoảng 50%

    • Cách prompt lại khi có lỗi cũng được thư viện mới TypeChat của Microsoft sử dụng: https://github.com/microsoft/TypeChat
      Prompt đó nằm ở đây: https://github.com/microsoft/TypeChat/blob/c45460f4030938da3...
      Tôi cho rằng cách tiếp cận dựa trên ngữ pháp như ở đây, hoặc những cách như https://github.com/ggerganov/llama.cpp/pull/1773, là giải pháp thanh lịch hơn nhiều
    • Tôi thấy yêu cầu xuất XML hiệu quả hơn JSON. (1) XML có thể chứa nhiều ngôn ngữ tự nhiên và ngữ nghĩa hơn, và LLM thích những thứ như vậy; (2) có thể làm parser khoan dung hơn
      Tôi hiểu mọi người muốn JSON, nhưng với tôi nó giống như cố bắt mèo đi bơi. Cuối cùng có thể thành công, nhưng đó không phải thiên hướng tự nhiên
    • Nếu dùng function calling của ChatGPT, miễn là không mắc lỗi trong prompt, tôi nhận được JSON hợp lệ 100% từ GPT-4
      Lỗi chủ yếu là không cung cấp lối thoát. LLM cố tìm câu trả lời đúng, nên nếu bạn đưa các văn bản vào và yêu cầu trả về dữ liệu có cấu trúc, nhưng một văn bản nào đó lại trống, nó sẽ khó xác định đáp án đúng và sinh ảo giác
      Cách giải quyết là tạo một lối thoát, chẳng hạn thêm một boolean như textIsMissing vào một trong các tham số. Nếu tính trước kiểu lỗi này thì nó hoạt động hoàn hảo
    • Tôi từng yêu cầu GPT-4 đặt một đoạn mã PHP mẫu vào trong JSON tùy ý rồi trả về, và ngay lần đầu nó đã không qua được JSON linter
      Tôi thử lại nhiều lần và còn yêu cầu sửa tiếp, nhưng vẫn không vượt qua kiểm tra; nó không tạo được JSON hợp lệ 100% dù chỉ một lần, nên cuối cùng tôi bỏ cuộc
    • Sinh có ràng buộc ngữ pháp có hai lợi thế lớn
      Thứ nhất, không cần đưa quá nhiều ví dụ vào prompt nên dùng ít token hơn
      Thứ hai, ít bị ảnh hưởng bởi vấn đề quên
      Một lợi thế nhỏ là có thể kiểm soát chính xác đầu ra mong muốn nên bắt đầu từ đâu. Tuy nhiên nhìn tổng thể, tôi xem đây là một tính năng bổ sung tốt chứ không hẳn là thứ quá cốt lõi
  • Phần lớn sức mạnh của LLM nằm ở phân phối xác suất đã được hiệu chỉnh trong phản hồi, nhưng kỹ thuật này có lẽ đang vứt bỏ khả năng đó. Tôi thắc mắc vì sao mức này lại được xem là đủ
    Lấy ví dụ đơn giản: giả sử các đầu ra khả dĩ của LLM chỉ gồm “hello world”, “food”, “hello”, “good day”, và khi không có prompt thì tất cả có xác suất như nhau. Giả sử ngữ pháp chỉ đặt ràng buộc rằng ở đâu đó trong đầu ra phải có dấu cách
    Nếu lấy mẫu đầu ra của LLM cho đến khi vượt qua ngữ pháp, ta sẽ nhận được “hello world” và “good day” với xác suất bằng nhau. Nhưng nếu áp dụng kỹ thuật trên website, “hello world” sẽ xuất hiện thường xuyên gấp đôi “good day”
    Vấn đề cốt lõi là một tiền tố câu trả lời nào đó có thể vốn có xác suất cực thấp để dẫn tới một phản hồi hợp lệ, nhưng kỹ thuật này, nếu giả định là sẽ thành công, lại tạo ra một phản hồi hợp lệ từ tiền tố đó. Nếu đủ độc lập ở những chỗ thích hợp thì có thể ổn, nhưng trong mô hình tự hồi quy, các lỗi tương quan sẽ tích lũy rất nhanh
    Nếu chỉ nói riêng về JSON: khi LLM tạo ra phản hồi không tuân thủ schema, liệu nó tạo ra nhiều hơn hay ít hơn các lỗi sự kiện, hallucination, chuỗi bị cắt, bỏ sót nhân vật chính, v.v.? Nếu tỷ lệ lỗi sự kiện có liên hệ không tầm thường với tỷ lệ lỗi schema, hướng này nguy hiểm hơn vẻ ngoài. Nhìn vào việc một từ cụ thể hoặc một cụm từ bị dính liền có thể ảnh hưởng lớn đến đầu ra của LLM, có vẻ rất có khả năng các chi tiết như tuân thủ schema cũng lan sang các đặc tính khác của đầu ra

    • Nếu là sinh theo dạng trắc nghiệm như trường hợp này, thì khi một trong các đầu ra khả dĩ không khớp regex, chỉ cần loại nó khỏi quá trình sinh
      Tôi đã cố nghĩ ra một ví dụ “tiền tố câu trả lời có xác suất rất thấp để dẫn tới phản hồi hợp lệ, nhưng kỹ thuật bằng cách nào đó vẫn dựng được phản hồi hợp lệ và gây ra vấn đề”, nhưng chưa nghĩ ra được. Nếu có ví dụ hay thì đây có thể là một câu hỏi nghiên cứu thú vị
  • Liên quan đến việc này, LLama.cpp đã triển khai lấy mẫu dựa trên ngữ pháp vào tháng trước
    https://news.ycombinator.com/item?id=36819906
    https://github.com/ggerganov/llama.cpp/pull/1773

    • Như được mô tả trong bài báo trên, cách tiếp cận của chúng tôi cũng có thể mở rộng thành lấy mẫu dựa trên ngữ pháp. PR liên quan: https://github.com/normal-computing/outlines/pull/178
      Cách của chúng tôi hiệu quả hơn nhiều. llama.cpp duyệt toàn bộ vocabulary khoảng 50 nghìn token ở mỗi bước để tạo mask
      Chúng tôi tạo index khi khởi tạo, và ở mỗi bước khi xây dựng mask thì chỉ cần tra cứu dictionary. Đây là cách đánh đổi bộ nhớ để lấy tốc độ, và việc lấy mẫu nhanh như lấy mẫu tiêu chuẩn
    • Vào khoảng cùng thời điểm, chúng tôi cũng đã có triển khai dẫn hướng theo ngữ pháp: https://github.com/normal-computing/outlines/pull/131
      Nhìn vào việc có nhiều bài báo liên quan, chắc hẳn cũng có nhiều nơi khác làm. Điểm chính ở đây và trong công việc hiện tại là cung cấp khả năng dẫn hướng với chi phí rất thấp; với trường hợp regex thì đã được triển khai một thời gian, rồi sau đó mở rộng sang JSON
  • Cảm ơn vì đã làm ra thứ này; nguyên lý hoạt động là một ý tưởng quá hiển nhiên đến mức thật ngạc nhiên là các nền tảng hạng nhất vẫn chưa làm
    Ngoài JSON, tôi tò mò nó có thể được dùng thế nào cho các tác vụ khác cần đầu vào có cấu trúc

    • Tôi hiểu rằng công nghệ LLM hiện đang ở trong một cuộc chạy đua vũ trang rất nhanh và thay đổi mạnh chỉ sau vài tháng
      Có thể đây là hệ quả của việc tài nguyên phát triển bị giới hạn. Nếu một công nghệ 10 năm tuổi thiếu tính năng cơ bản như vậy thì đáng ngạc nhiên, nhưng với công nghệ AI đang trong cuộc chạy đua vũ trang, việc vẫn thiếu các tính năng tiện ích thì có vẻ chấp nhận được
    • Chúng tôi đã mở rộng cách tiếp cận này thành lấy mẫu dựa trên ngữ pháp, như mô tả trong bài báo được liên kết ở trên. PR liên quan là https://github.com/normal-computing/outlines/pull/178
    • “Guiding Language Models of Code with Global Context using Monitors” tại https://arxiv.org/abs/2306.10763 cho thấy cách để mô hình ngôn ngữ sinh mã mà không có tham chiếu ngược bị hallucinate
  • Tôi không rõ cái này khác các dự án sau như thế nào
    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
    Nhìn chung, các hệ thống dẫn hướng dựa trên logit như vậy rất nhiều, và lý do chúng chưa có traction lớn là vì các mô hình hiệu năng cao nhất nằm sau REST API không cho phép cách tiếp cận tinh vi như thế
    Các mô hình đó tốt hơn nhiều, nên mọi người nói chung hài lòng với cách yêu cầu lại cho đến khi nhận được định dạng đúng. Với GPT-4, theo trải nghiệm của tôi, những thất bại như vậy cũng khá hiếm

    • Cảm ơn đã cho biết về clownfish và relm. Theo tôi biết, các thư viện khác duyệt toàn bộ vocabulary ở mỗi bước của quá trình sinh
      Chúng tôi duyệt vocabulary một lần khi khởi tạo để tạo index, rồi sau đó quá trình sinh nhanh như sinh tiêu chuẩn
  • Nói cách khác, có phải mỗi khi LLM tạo ra một token, nó sẽ cập nhật “mặt nạ” thiên lệch logit sao cho token tiếp theo chỉ có thể là token JSON hợp lệ không? Rất hay

    • Chắc sẽ phải tiếp tục sinh cho đến khi toàn bộ chuỗi trở nên hợp lệ, nhưng nếu bị mắc vào vòng lặp thì không biết sẽ thế nào
      Tôi không chắc cách này thật sự có thể đảm bảo 100% ra sao
    • Đúng vậy. Và có thể cập nhật mặt nạ bằng tra cứu dictionary mà không cần duyệt toàn bộ từ vựng. Duyệt toàn bộ thì chậm
    • Vì token JSON không tương ứng chính xác với logit, nên cũng cần một dạng beam search hoặc rejection sampling nào đó
      Sửa: bài báo giải thích phần này thận trọng hơn
    • Thật ra đây là kỹ thuật rất cũ, và nhiều thư viện đã làm vậy. Tôi không rõ thư viện này có gì ghê gớm đến thế
  • Brandon Willard này có phải là Brandon Willard, vũ công breakdance ở Detroit không?
    Sửa: Đúng rồi! https://brandonwillard.github.io/

    • Đúng vậy. Đã khá lâu rồi, nhưng thật sự rất vui
  • remilouf, vì anh nói nền tảng của mình là “lập trình xác suất, quan hệ và ký hiệu”, tôi nghĩ anh hiểu rằng việc sinh văn bản từ văn pháp chính quy, văn pháp phi ngữ cảnh, hay thực tế là văn pháp ở bất kỳ cấp độ nào, không phải là vấn đề
    Ví dụ, trong một ngôn ngữ quan hệ như Prolog, chỉ cần cung cấp văn pháp bằng ký pháp Definite Clause Grammars là có thể làm rất dễ dàng
    Theo tôi thấy, cách tiếp cận này yêu cầu người dùng cung cấp văn pháp. Vậy tôi thắc mắc lợi thế của việc dùng LLM để sinh văn bản là gì
    Tại sao không chỉ chạy văn pháp như một bộ sinh để tạo ra văn bản mong muốn? Như vậy có thể giảm được công sức và chi phí rất lớn của việc huấn luyện LLM ngay từ đầu. Nếu chỉ muốn sinh văn bản có cấu trúc thì tôi cũng không hiểu vì sao lại cần LLM, vốn là mô hình ngôn ngữ tự nhiên

    • Như vậy chẳng phải sẽ cho ra đầu ra hợp lệ nhưng hoàn toàn ngẫu nhiên sao? Ở đây cần đầu ra hợp lệ có liên quan đến yêu cầu
      Lý do vẫn cần LLM ngay cả khi chỉ muốn văn bản có cấu trúc là để phân tích văn bản phi cấu trúc do con người viết và trả về dữ liệu có cấu trúc mà máy có thể dùng
    • Mục tiêu không phải là tạo ra một chuỗi ngẫu nhiên bất kỳ khớp văn pháp
      Nếu yêu cầu là “10 chữ số đầu tiên của pi là gì?” và giới hạn phản hồi bằng regex "[0-9]+\.[0-9]+", thì mục tiêu là nhận được đáp án đúng thực sự là 3.1415926535, chứ không phải một chuỗi ngẫu nhiên khớp mẫu như “1.2346789”
    • IanCal đã nói hết rồi. Tuy vậy, một cách tiếp cận thay thế dùng LLM cùng với miniKanren có thể xem tại https://arxiv.org/abs/1809.02840
  • Thú vị đấy, gần đây chúng tôi cũng đã làm một công cụ tương tự để ràng buộc đầu ra của llama khớp với interface TypeScript[1]
    Tôi tin chắc rằng việc đảm bảo định dạng đầu ra sẽ trở nên quan trọng trong vài thập kỷ tới, khi LLM được dùng cho các mục đích thực tế chứ không phải đồ chơi
    [1] https://github.com/ggerganov/llama.cpp/discussions/2494