2 điểm bởi GN⁺ 2024-12-09 | 1 bình luận | Chia sẻ qua WhatsApp
  • Có thể giới hạn phản hồi của mô hình cục bộ theo JSON Schema, giúp giảm gánh nặng phân tích cú pháp hậu xử lý và làm cho kết quả trích xuất dữ liệu nhất quán hơn
  • Người dùng truyền schema vào tham số format trong cURL, Python, JavaScript để yêu cầu đầu ra có cấu trúc theo cùng một cách
  • Với Python có thể dùng cùng Pydantic, còn JavaScript dùng cùng Zod để dễ liên kết định nghĩa schema và xác thực phản hồi ở cấp mã
  • Các ví dụ trả về và xác thực thông tin quốc gia, văn bản về thú cưng, kết quả phân tích hình ảnh bằng llama3.2-vision dưới dạng các trường JSON đã định
  • Nếu cần kết quả ổn định, khuyến nghị thêm “return as JSON” vào prompt và đặt temperature về 0

Giới hạn phản hồi của mô hình bằng JSON Schema

  • Ollama hỗ trợ đầu ra có cấu trúc, giới hạn đầu ra của mô hình tuân theo một định dạng JSON Schema cụ thể
  • Các thư viện PythonJavaScript của Ollama cũng đã được cập nhật để hỗ trợ đầu ra có cấu trúc
  • Tính năng này có thể được dùng cho các tác vụ sau
    • Phân tích cú pháp dữ liệu từ tài liệu
    • Trích xuất dữ liệu từ hình ảnh
    • Cấu trúc hóa phản hồi của mô hình ngôn ngữ
    • Đạt độ tin cậy và tính nhất quán cao hơn JSON mode

Cài đặt và cách gọi

  • Cần tải phiên bản mới nhất của Ollama
  • Cập nhật thư viện Python lên phiên bản mới nhất bằng lệnh sau
pip install -U ollama
  • Cài đặt thư viện JavaScript bằng lệnh sau
npm i ollama
  • Khi yêu cầu đầu ra có cấu trúc, truyền schema vào tham số format trong yêu cầu cURL hoặc trong thư viện Python·JavaScript

Truyền JSON Schema bằng cURL

  • Ví dụ cURL gửi yêu cầu tới http://localhost:11434/api/chat, đặt modelllama3.1streamfalse
  • Trong format, đưa vào schema đối tượng có name, capital, languages
    • name: chuỗi
    • capital: chuỗi
    • languages: mảng chuỗi
    • Cả ba trường đều được chỉ định là required
  • Phản hồi được trả về theo định dạng JSON Schema có trong yêu cầu
{
  "capital": "Ottawa",
  "languages": [
    "English",
    "French"
  ],
  "name": "Canada"
}

Sử dụng thư viện Python và JavaScript

  • Python

    • Ollama Python library truyền schema dưới dạng đối tượng JSON vào tham số format
    • Schema có thể được truyền dưới dạng dict; cách khuyến nghị là tuần tự hóa bằng model_json_schema() của Pydantic
    • Ví dụ định nghĩa các trường name, capital, languages trong mô hình Country, rồi xác thực phản hồi bằng Country.model_validate_json()
country = Country.model_validate_json(response.message.content)
  • Kết quả ví dụ trả về Canada, Ottawa, English, French khớp với các trường đã định nghĩa
  • JavaScript

    • Ollama JavaScript library truyền schema dưới dạng đối tượng JSON vào tham số format
    • Schema có thể được truyền dưới dạng object; cách khuyến nghị là dùng ZodzodToJsonSchema()
    • Ví dụ định nghĩa schema Country bằng đối tượng Zod, sau đó JSON.parse() phần thân phản hồi và xác thực bằng Country.parse()
const country = Country.parse(JSON.parse(response.message.content));

Trích xuất dữ liệu từ văn bản và hình ảnh

  • Trích xuất văn bản về thú cưng

    • Đầu ra có cấu trúc có thể được dùng để rút ra thông tin cần thiết từ văn bản
    • Ví dụ định nghĩa các mô hình Pydantic PetPetList để trả về thông tin thú cưng theo cấu trúc JSON
      • Pet: name, animal, age, color, favorite_toy
      • PetList: mảng pets
    • Văn bản đầu vào chứa thông tin về hai con mèo
      • Luna: 5 tuổi, lông màu xám, thích yarn
      • Loki: 2 tuổi, màu đen, thích tennis balls
    • Đầu ra được xác thực dưới dạng danh sách đối tượng Pet theo schema đã định nghĩa
  • Mô tả hình ảnh bằng mô hình thị giác

    • Đầu ra có cấu trúc có thể được dùng cùng mô hình thị giác
    • Ví dụ phân tích hình ảnh bằng llama3.2-vision và trả về kết quả theo schema ImageDescription
    • Schema bao gồm các trường sau
      • summary
      • objects
      • scene
      • colors
      • time_of_day
      • setting
      • text_content
    • Yêu cầu ví dụ chỉ thị phân tích đối tượng, cảnh, màu sắc và văn bản có thể phát hiện trong hình ảnh
    • Trong tùy chọn, đặt temperature về 0 để dùng đầu ra mang tính quyết định hơn
    • Kết quả ví dụ tóm tắt cảnh cây cọ trên bãi biển, đồng thời trả về các đối tượng treebeach, màu sắc, thời điểm trong ngày và bối cảnh ngoài trời dưới dạng các trường có cấu trúc

Sử dụng API tương thích OpenAI

  • Ví dụ tương thích OpenAI đặt client OpenAI với base_url="http://localhost:11434/v1";api_key="ollama"
  • Dùng client.beta.chat.completions.parse() để truyền mô hình Pydantic PetList vào response_format
  • Kiểm tra completion.choices[0].message trong phản hồi; nếu có parsed thì in kết quả đã phân tích cú pháp
  • Nếu có refusal thì in phản hồi từ chối, còn openai.LengthFinishReasonError được xử lý là trường hợp có quá nhiều token

Thiết lập đầu ra ổn định và kế hoạch tương lai

  • Khuyến nghị dùng Pydantic của Python hoặc Zod của JavaScript để định nghĩa schema phản hồi
  • Nên thêm “return as JSON” vào prompt để mô hình hiểu yêu cầu
  • Nếu muốn đầu ra mang tính quyết định hơn, đặt temperature về 0
  • Các kế hoạch trong tương lai gồm
    • Phơi bày logits cho sinh có kiểm soát
    • Cải thiện hiệu năng và độ chính xác của đầu ra có cấu trúc
    • Tăng tốc GPU cho lấy mẫu
    • Hỗ trợ thêm các định dạng vượt ngoài JSON Schema

1 bình luận

 
GN⁺ 2024-12-09
Các ý kiến trên Hacker News
  • Nếu cần ràng buộc đầu ra mạnh hơn, llama.cpp hỗ trợ GBNF
    https://github.com/ggerganov/llama.cpp/blob/master/grammars/...

    • Có vẻ như họ đang dùng đúng cái đó
    • Tôi tò mò liệu chất lượng đầu ra với ngữ pháp tùy ý có thỏa đáng không
      Nhìn đơn giản thì vì các mô hình đã thấy JSON nhiều hơn hẳn, nên tôi nghĩ chúng có lẽ tạo JSON tốt hơn các định dạng khác
    • Tôi tò mò nó mạnh hơn ở điểm nào
  • Tin tốt
    Tôi từng băn khoăn nên cấu trúc prompt thông thường thế nào khi tạo dữ liệu CSV để không bị kèm những câu thừa như "Here is your data" hay "Please note blah blah" ở đầu/cuối; giờ có thể định nghĩa chính xác định dạng trả về mong muốn và chuyển đầu ra có cấu trúc thẳng sang CSV, thật đáng mừng

    • Dù vậy vẫn phải đưa chỉ thị yêu cầu tạo CSV vào để prompt nằm trong đúng ngữ cảnh
      Nếu không, đầu ra có thể đúng định dạng CSV về mặt kỹ thuật nhưng vô nghĩa. Có thể mô hình thực ra định viết câu trả lời dạng đoạn văn, nhưng token sampler lại chọn các token xác suất thấp mà mô hình không thật sự muốn nói
    • Trong nhiều trường hợp, có thể tránh vấn đề này bằng cách điền sẵn phần bắt đầu đầu ra là \n và cho dừng ở
  • Hoạt động được. Tôi đưa câu dưới đây vào gemma2:2b và nhận được JSON mong muốn
    You have spent 190 at Fresh Mart. Current balance: 5098
    Kết quả là {"amount": 190, "balance": 5098, "category": "Shopping", "place": "Fresh Mart"}

    • JSON khá thiếu nhất quán, nhưng với một mô hình rất nhỏ, lại còn là gemma, thì cũng có thể hiểu được
  • Thật sự đáng kinh ngạc. Đây đúng là một trong những tính năng tôi từng muốn
    Điểm hay của ollama là nó tạo cảm giác dùng LLM như một chương trình UNIX khác, khiến LLM có vẻ thuộc về UNIX một cách tự nhiên
    Nhưng tôi tò mò có ai chạy tốt trên GPU AMD chưa. Nghe nói khó hơn, nhưng năm sau khi mua card tôi muốn ủng hộ đối thủ cạnh tranh

    • Có thể. Ngay cả GPU tích hợp cũng được
      Tôi cấp 16GB bộ nhớ chia sẻ qua BIOS trên một mini PC có 780M và nó chạy khá ổn
  • Tôi tò mò các ràng buộc kiểu này ảnh hưởng thế nào đến chất lượng đầu ra của LLM
    Trong một số trường hợp, nếu chất lượng đầu ra cao hơn thì tôi muốn chọn cách phân tích cú pháp Markdown hoặc văn bản thường

    • Khi dùng mô hình OpenAI, nếu chịu được chi phí token bổ sung thì chiến lược chạy hai lượt rất hiệu quả
      Lượt đầu dùng mô hình nặng và ngôn ngữ tự nhiên để xử lý suy luận trong các phần Markdown, rồi cung cấp câu trả lời cuối cùng bằng ngôn ngữ tự nhiên. Nếu có thể thì gắn nhãn rõ bằng header Markdown
      Lượt thứ hai dùng mô hình rẻ hơn và nhanh hơn để chuyển câu trả lời đó sang định dạng đầu ra có cấu trúc, cho phần không phải LLM của pipeline tiêu thụ
      Về cơ bản, dùng chế độ JSON schema để tạo ranh giới gọn gàng quanh các phần ngôn ngữ tự nhiên mơ hồ, và để LLM đóng vai trò bộ tiền xử lý nắm bắt chính đầu ra của nó ở một định dạng hữu ích
    • Còn tùy mô hình được tinh chỉnh cho đầu ra JSON đến mức nào
      Ngoài ra phải cho mô hình biết schema. Nếu không, sẽ phát sinh nhiều vấn đề token hóa kỳ lạ hơn
      Ví dụ, nếu schema kỳ vọng khóa JSON "foobarbaz" và token hóa BPE chuẩn là ["foobar", "baz"], thì token mask do các thư viện đầu ra ràng buộc hiện nay tạo ra có thể cho phép mô hình chọn giữa "f", "foo", "foobar". Nếu mô hình chọn "foo", ràng buộc sẽ ép token tiếp theo, chẳng hạn "bar""baz". Khi đó mô hình sẽ thấy ["foo", "bar", "baz"] thay vì ["foobar", "baz"], nên bị rối [0]
      Nếu trong prompt mô hình biết rằng "foobarbaz" là một trong các khóa schema, thường nó sẽ ưu tiên "foobar" hơn "foo"
      [0] Ở các mô hình mới, do chuẩn hóa nên các token này có liên quan với nhau, nhưng không giống nhau
    • Tùy tình huống, nhưng xét về mặt suy luận thì có tác động tiêu cực, và trong đa số trường hợp khác biệt không quá lớn
      Phụ thuộc nhiều vào việc LLM và prompt vốn có khả năng tạo phản hồi JSON hay không. Càng ép LLM nhiều, khả năng tạo đầu vào bình thường càng thấp
      Với các mô hình nhỏ, bạn sẽ chạm đến rìa của không gian còn có năng lực dự đoán có ý nghĩa nhanh hơn, và đầu ra bắt đầu gần giống nhiễu ngẫu nhiên
      Đây không phải đo lường nghiêm ngặt, chỉ là cảm nhận sau khi dành nhiều thời gian cho nhiều dự án LLM. Tôi chưa dùng các công cụ cụ thể này, nhưng trước đây ollama cũng đã có thể đảm bảo đầu ra JSON bằng một cách có vẻ tương tự, và tôi từng cùng đối tác làm một thứ giống jsonformer cho oobabooga, một công cụ runtime LLM khác
    • Tôi vẫn đang theo dõi các nghiên cứu liên quan. Tôi đang xem xét để cải thiện sampling nói chung cả về tốc độ lẫn độ chính xác
      Hy vọng nếu những thay đổi đó được đưa vào, việc sinh cấu trúc tổng quát không chỉ giới hạn ở JSON cũng có thể khả thi
    • Có thể nói tôi đã hoàn toàn sai về độ hữu ích của các công cụ như instructor
      Chúng dễ đốt nhiều token, nhưng nếu việc cần làm biện minh được cho chi phí đó thì có thể thúc khá mạnh. Dù không phải chất lượng tuyệt đối tốt nhất, một công cụ giúp đạt mức 95% mà không tốn công vẫn đủ đáng để có trong bộ công cụ
  • Tôi tò mò liệu có dùng được với bất kỳ mô hình nào được hỗ trợ không
    Phần cứng của tôi chỉ chạy ổn định được các mô hình 1B~3B, nên tôi hỏi vậy

    • Tôi là tác giả bài blog. Lẽ ra có thể dùng với bất kỳ mô hình nào
      Với mô hình nhỏ, kết quả có thể thất thường, nhưng yêu cầu “trả về x dưới dạng JSON” thường giúp tăng độ chính xác
  • PR liên quan đến tính năng này đã mở gần 1 năm
    Việc các maintainer quá im lặng thì hơi đáng tiếc

    • Tôi là tác giả bài viết và là một trong các maintainer. Tôi đồng ý. Các maintainer đã phản hồi chậm, và nhìn chung tôi muốn khuyến khích nhiều đóng góp hơn
      Hy vọng năm sau chúng tôi có thể chăm sóc và merge PR từ cộng đồng tốt hơn
    • Nhìn bầu không khí thì có vẻ đang đi theo hướng lấy công ty làm trung tâm, nên dường như họ nhìn mọi thứ qua lăng kính đó và góc độ tối đa hóa doanh thu
    • Tôi cũng từng muốn hợp tác xây dựng cái này, nhưng cuộc trao đổi đột ngột bị cắt đứt
  • Hiện vẫn đang dùng oobabooga. Nhờ hỗ trợ exlv2, việc suy luận trên dual 3090 hiệu quả hơn nhiều

    • Lâu rồi tôi chưa động đến ooba, nên tò mò tình hình của exl2 và các phương pháp lượng tử hóa không đồng nhất như q3k_s hiện ra sao
      Theo tôi nhớ thì dù exl2 nhanh hơn, đặc biệt ở độ sâu bit thấp, lượng tử hóa gptq có vẻ tốt hơn về độ chính xác
  • Tò mò không biết so với outlines thì có giá trị gia tăng gì
    https://www.souzatharsis.com/tamingLLMs/notebooks/structured...

    • Tôi là tác giả blog. Triển khai hiện tại dùng llama.cpp GBNF nên có thể làm nhanh được. Giá trị gia tăng lớn nhất ở thời điểm này là tính năng đã được phát hành
      Dựa trên các nghiên cứu gần đây liên quan đến outlines/xgrammar, hy vọng có thể cập nhật phần sampling để hỗ trợ nhiều định dạng hơn, tăng độ chính xác và cải thiện hiệu năng
  • Tò mò liệu có cách tốt nhất nào để cung cấp đầu vào có cấu trúc cho LLM không
    Ví dụ như đưa vào 100 câu và yêu cầu phân loại từng câu theo nhiều cách. Nhận dữ liệu có cấu trúc thì dễ, nhưng cách của tôi là thêm số dòng ở đầu mỗi câu nên cảm thấy khá thô

    • Các mô hình đã được huấn luyện trên Markdown, JSON và nhiều ngôn ngữ lập trình, nên dùng một trong số đó chắc sẽ hoạt động
      Tuy nhiên trong trường hợp này, tốt nhất là đưa từng câu một để mô hình không bị nhầm lẫn
      Nếu cấu trúc prompt theo dạng "Hãy phân loại câu sau. Quy tắc là ..." + câu, bạn có thể tận dụng prefix cache, nên hiệu năng cũng có thể tốt hơn so với hỏi tất cả cùng lúc
      Tất nhiên đây là cách khả thi khi có prefix cache và không bị tính phí theo token đầu vào. Ngày nay hầu hết nhà cung cấp cho phép dùng với chi phí thấp hơn nếu bạn cho biết ý định sử dụng prefix cache