- สามารถจำกัดการตอบกลับของโมเดลภายในเครื่องให้ตรงตาม JSON Schema ได้ ช่วยลดภาระการพาร์สหลังประมวลผลและทำให้ผลลัพธ์การดึงข้อมูลสม่ำเสมอยิ่งขึ้น
- ผู้ใช้สามารถส่งสคีมาผ่าน พารามิเตอร์
format ใน cURL, Python และ JavaScript เพื่อขอผลลัพธ์แบบมีโครงสร้างด้วยวิธีเดียวกัน
- ฝั่ง Python หากใช้ Pydantic และฝั่ง JavaScript หากใช้ Zod จะเชื่อมการนิยามสคีมาและการตรวจสอบผลลัพธ์ได้ง่ายในระดับโค้ด
- ตัวอย่างแสดงการคืนค่าและตรวจสอบข้อมูลประเทศ ข้อความเกี่ยวกับสัตว์เลี้ยง และผลการวิเคราะห์ภาพจาก
llama3.2-vision ให้อยู่ในฟิลด์ JSON ที่กำหนดไว้
- หากต้องการผลลัพธ์ที่เสถียร แนะนำให้ใส่ “return as JSON” ในพรอมป์ต์และตั้งค่า temperature เป็น 0
จำกัดการตอบกลับของโมเดลด้วย JSON Schema
- Ollama รองรับ structured outputs ที่จำกัดให้เอาต์พุตของโมเดลเป็นไปตามรูปแบบ JSON Schema ที่กำหนด
- ไลบรารี Python และ JavaScript ของ Ollama ก็ได้รับการอัปเดตให้รองรับ structured outputs แล้ว
- ฟีเจอร์นี้สามารถนำไปใช้กับงานต่อไปนี้
- พาร์สข้อมูลจากเอกสาร
- ดึงข้อมูลจากภาพ
- จัดโครงสร้างการตอบกลับของโมเดลภาษา
- ให้ความน่าเชื่อถือและความสม่ำเสมอสูงกว่า JSON mode
การติดตั้งและวิธีเรียกใช้งาน
- ต้องดาวน์โหลด Ollama เวอร์ชันล่าสุด
- ไลบรารี Python อัปเดตเป็นเวอร์ชันล่าสุดด้วยคำสั่งต่อไปนี้
pip install -U ollama
- ไลบรารี JavaScript ติดตั้งด้วยคำสั่งต่อไปนี้
npm i ollama
- เมื่อต้องการขอ structured outputs ให้ส่งสคีมาผ่าน พารามิเตอร์
format ในคำขอ cURL หรือผ่านไลบรารี Python และ JavaScript
ส่ง JSON Schema ด้วย cURL
- ตัวอย่าง cURL จะส่งคำขอไปที่
http://localhost:11434/api/chat โดยตั้ง model เป็น llama3.1 และ stream เป็น false
- ใน
format จะใส่อ็อบเจ็กต์สคีมาที่มี name, capital, languages
name: สตริง
capital: สตริง
languages: อาร์เรย์ของสตริง
- ทั้งสามฟิลด์ถูกกำหนดเป็น
required
- การตอบกลับจะถูกส่งคืนในรูปแบบ JSON Schema ที่รวมอยู่ในคำขอ
{
"capital": "Ottawa",
"languages": [
"English",
"French"
],
"name": "Canada"
}
การใช้ไลบรารี Python และ JavaScript
-
Python
- Ollama Python library ส่งสคีมาเป็นอ็อบเจ็กต์ JSON ผ่านพารามิเตอร์
format
- สามารถส่งสคีมาเป็น
dict ได้ และวิธีที่แนะนำคือซีเรียลไลซ์ด้วย model_json_schema() ของ Pydantic
- ตัวอย่างกำหนดฟิลด์
name, capital, languages ในโมเดล Country และตรวจสอบผลลัพธ์ด้วย Country.model_validate_json()
country = Country.model_validate_json(response.message.content)
- เอาต์พุตตัวอย่างจะคืนค่า
Canada, Ottawa, English, French ให้ตรงกับฟิลด์ที่กำหนด
-
JavaScript
- Ollama JavaScript library ส่งสคีมาเป็นอ็อบเจ็กต์ JSON ผ่านพารามิเตอร์
format
- สามารถส่งสคีมาเป็น
object ได้ และวิธีที่แนะนำคือใช้ Zod ร่วมกับ zodToJsonSchema()
- ตัวอย่างนิยามสคีมา
Country เป็นอ็อบเจ็กต์ของ Zod แล้วตรวจสอบผลลัพธ์โดยใช้ JSON.parse() กับเนื้อหาการตอบกลับก่อน จากนั้นใช้ Country.parse()
const country = Country.parse(JSON.parse(response.message.content));
การดึงข้อมูลจากข้อความและภาพ
-
การดึงข้อมูลข้อความเกี่ยวกับสัตว์เลี้ยง
- structured outputs สามารถใช้ดึงข้อมูลที่ต้องการจากข้อความได้
- ตัวอย่างนิยามโมเดล Pydantic คือ
Pet และ PetList เพื่อคืนค่าข้อมูลสัตว์เลี้ยงในโครงสร้าง JSON
Pet: name, animal, age, color, favorite_toy
PetList: อาร์เรย์ pets
- ข้อความอินพุตมีข้อมูลของแมวสองตัว
- Luna: อายุ 5 ปี ขนสีเทา ชอบ yarn
- Loki: อายุ 2 ปี สีดำ ชอบ tennis balls
- เอาต์พุตจะถูกตรวจสอบเป็นรายการของอ็อบเจ็กต์
Pet ตามสคีมาที่กำหนด
-
คำอธิบายภาพด้วยวิชันโมเดล
- structured outputs สามารถใช้ร่วมกับ vision model ได้
- ตัวอย่างใช้
llama3.2-vision วิเคราะห์ภาพและคืนค่าผลลัพธ์ตามสคีมา ImageDescription
- สคีมามีฟิลด์ต่อไปนี้
summary
objects
scene
colors
time_of_day
setting
text_content
- คำขอตัวอย่างสั่งให้วิเคราะห์วัตถุ ฉาก สี และข้อความที่ตรวจจับได้ในภาพ
- ใน options มีการตั้ง
temperature เป็น 0 เพื่อให้ใช้เอาต์พุตที่ เป็นแบบกำหนดแน่นอนมากขึ้น
- เอาต์พุตตัวอย่างจะสรุปฉากต้นปาล์มริมชายหาด และคืนค่าวัตถุ
tree กับ beach รวมถึงสี ช่วงเวลา และการตั้งค่าแบบกลางแจ้งในฟิลด์ที่มีโครงสร้าง
การใช้ OpenAI-compatible API
- ตัวอย่างแบบ OpenAI-compatible ตั้งค่าไคลเอนต์
OpenAI ด้วย base_url="http://localhost:11434/v1" และ api_key="ollama"
- ใช้
client.beta.chat.completions.parse() โดยส่งโมเดล Pydantic PetList ผ่าน response_format
- ตรวจสอบ
completion.choices[0].message ในการตอบกลับ และหากมี parsed ก็จะแสดงผลลัพธ์ที่พาร์สแล้ว
- หากมี
refusal ก็จะแสดงผลการปฏิเสธ และจัดการ openai.LengthFinishReasonError เป็นกรณีที่มีโทเคนมากเกินไป
การตั้งค่าเพื่อผลลัพธ์ที่เสถียรและแผนในอนาคต
- แนะนำให้ใช้ Pydantic ใน Python หรือ Zod ใน JavaScript สำหรับนิยาม response schema
- ควรเพิ่ม “return as JSON” ในพรอมป์ต์เพื่อช่วยให้โมเดลเข้าใจคำขอ
- หากต้องการเอาต์พุตที่กำหนดแน่นอนมากขึ้น ให้ตั้ง temperature เป็น 0
- แผนในอนาคตมีดังนี้
- เปิดเผย logits เพื่อการสร้างแบบควบคุม
- ปรับปรุงประสิทธิภาพและความแม่นยำของ structured outputs
- เร่งความเร็วด้วย GPU สำหรับการสุ่มตัวอย่าง
- รองรับรูปแบบเพิ่มเติมนอกเหนือจาก JSON Schema
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
หากต้องการข้อจำกัดของเอาต์พุตที่เข้มงวดกว่า llama.cpp รองรับ GBNF
https://github.com/ggerganov/llama.cpp/blob/master/grammars/...
ถ้ามองแบบง่าย ๆ เพราะโมเดลเห็น JSON มามากกว่ามาก จึงน่าจะสร้าง JSON ได้ดีกว่ารูปแบบอื่น ๆ
เป็นข่าวดี
ตอนสร้างข้อมูล CSV เคยกังวลว่าจะเขียนพรอมป์ทั่วไปอย่างไรไม่ให้มีข้อความไม่จำเป็นอย่าง "Here is your data" หรือ "Please note blah blah" ติดมาข้างหน้าข้างหลัง ตอนนี้ดีใจที่สามารถกำหนดรูปแบบการคืนค่าที่ต้องการได้อย่างแม่นยำ แล้วส่ง เอาต์พุตแบบมีโครงสร้าง ไปเป็น CSV ได้เลย
ไม่เช่นนั้นอาจได้เอาต์พุตที่ในเชิงเทคนิคเป็นรูปแบบ CSV แต่ไม่มีความหมาย อาจเป็นกรณีที่จริง ๆ โมเดลตั้งใจจะเขียนคำตอบเป็นย่อหน้า แต่ token sampler เลือกโทเค็นความน่าจะเป็นต่ำที่โมเดลไม่ได้อยากพูดนัก
\n แล้วให้หยุดที่ก็ช่วยป้องกันปัญหานี้ได้ใช้งานได้ ผมใส่ประโยคด้านล่างลงใน gemma2:2b แล้วได้ JSON ตามที่ต้องการ
You have spent 190 at Fresh Mart. Current balance: 5098ผลลัพธ์คือ
{"amount": 190, "balance": 5098, "category": "Shopping", "place": "Fresh Mart"}น่าทึ่งจริง ๆ นี่เป็นหนึ่งในฟีเจอร์ที่อยากได้พอดี
เหตุผลที่ ollama ดีคือมันให้ความรู้สึกเหมือนใช้ LLM เป็นโปรแกรม UNIX อีกตัวหนึ่ง และทำให้รู้สึกว่า LLM อยู่ใน UNIX ได้อย่างเป็นธรรมชาติ
แต่สงสัยว่ามีใครรันบน AMD GPU ได้ดีบ้างหรือเปล่า ได้ยินว่ายากกว่า แต่ปีหน้าเวลาซื้อการ์ดอยากสนับสนุนคู่แข่ง
ผมจัดสรรหน่วยความจำแชร์ 16GB ผ่าน BIOS บนมินิพีซีที่มี 780M แล้วก็รันได้ค่อนข้างดี
สงสัยว่าข้อจำกัดแบบนี้ส่งผลต่อ คุณภาพเอาต์พุตของ LLM อย่างไร
ในบางกรณี ถ้าคุณภาพเอาต์พุตดีกว่า ก็อยากเลือกพาร์ส Markdown หรือข้อความธรรมดามากกว่า
รอบแรกใช้โมเดลหนักกับภาษาธรรมชาติ ให้จัดการการให้เหตุผลเป็นส่วน ๆ ใน Markdown และให้คำตอบสุดท้ายเป็นภาษาธรรมชาติ ถ้าเป็นไปได้ให้ติดป้ายชัดเจนด้วยหัวข้อ Markdown
รอบที่สองใช้โมเดลที่ถูกกว่าและเร็วกว่า แปลงคำตอบนั้นเป็นรูปแบบเอาต์พุตแบบมีโครงสร้าง เพื่อให้ส่วนที่ไม่ใช่ LLM ของไปป์ไลน์นำไปใช้
โดยพื้นฐานคือใช้โหมด JSON schema เพื่อสร้างขอบเขตที่สะอาดรอบ ๆ ส่วนภาษาธรรมชาติที่กำกวม และให้ LLM ทำหน้าที่เป็นพรีโปรเซสเซอร์ที่จับเอาต์พุตของตัวเองให้อยู่ในรูปแบบที่ใช้งานได้
และต้องบอกสคีมาให้โมเดลรู้ด้วย ถ้าไม่บอกจะเกิดปัญหา tokenization แปลก ๆ มากขึ้น
ตัวอย่างเช่น หากสคีมาคาดหวังคีย์ JSON
"foobarbaz"และการทำ BPE tokenization มาตรฐานได้เป็น["foobar", "baz"]token mask ที่ไลบรารี constrained output ปัจจุบันสร้างขึ้น อาจอนุญาตให้โมเดลเลือกได้ระหว่าง"f","foo","foobar"ถ้าโมเดลเลือก"foo"ข้อจำกัดจะบังคับโทเค็นถัดไป เช่น"bar"และ"baz"จากนั้นโมเดลจะเห็น["foo", "bar", "baz"]แทนที่จะเป็น["foobar", "baz"]ทำให้สับสน [0]ถ้าโมเดลรู้จากพรอมป์ว่า
"foobarbaz"เป็นหนึ่งในคีย์ของสคีมา โดยทั่วไปมันจะชอบ"foobar"มากกว่า"foo"[0] ในโมเดลรุ่นใหม่ ๆ โทเค็นเหล่านี้เกี่ยวข้องกันเพราะ normalization แต่ไม่ใช่สิ่งเดียวกัน
ขึ้นอยู่มากว่า LLM และพรอมป์เดิมมีแนวโน้มจะสร้างคำตอบ JSON อยู่แล้วหรือไม่ ยิ่งบังคับ LLM มากเท่าไร โอกาสที่จะสร้างอินพุตปกติก็ยิ่งลดลง
ในโมเดลขนาดเล็ก จะไปถึงขอบของพื้นที่ที่มีพลังทำนายที่มีความหมายได้เร็วกว่า และเอาต์พุตจะเริ่มใกล้เคียงสัญญาณรบกวนแบบสุ่ม
นี่ไม่ใช่การวัดอย่างเข้มงวด เป็นการประเมินจากความรู้สึกหลังใช้เวลากับหลายโปรเจกต์ LLM มานาน ผมยังไม่ได้ลองเครื่องมือเฉพาะเหล่านี้ แต่ก่อนหน้านี้ ollama ก็สามารถรับประกันเอาต์พุต JSON ด้วยวิธีที่ดูคล้ายเทคนิคแบบนี้ได้ และผมเคยทำงานร่วมกับพาร์ตเนอร์เกี่ยวกับสิ่งคล้าย jsonformer สำหรับ oobabooga ซึ่งเป็นเครื่องมือรันไทม์ LLM อีกตัวหนึ่งด้วย
หวังว่าหากมีการเปลี่ยนแปลงแบบนั้นเข้ามา จะทำให้สร้างโครงสร้างทั่วไปที่ไม่จำกัดแค่ JSON ได้ด้วย
มันเผาโทเค็นได้ง่าย แต่ถ้างานที่ทำคุ้มกับต้นทุนนั้น ก็สามารถดันไปได้ค่อนข้างแรง แม้จะไม่ใช่คุณภาพสูงสุดแบบเด็ดขาด แต่เครื่องมือที่ให้ผลระดับ 95% ได้โดยแทบไม่ต้องพยายาม ก็คุ้มพอที่จะมีไว้ในกล่องเครื่องมือ
สงสัยว่าใช้กับโมเดลใดก็ได้ที่รองรับหรือเปล่า
ฮาร์ดแวร์ของผมรันได้เสถียรแค่ โมเดล 1B~3B เลยถามดู
ในโมเดลขนาดเล็ก ผลลัพธ์อาจแกว่งไปมา แต่การขอให้ “คืนค่า x เป็น JSON” มักช่วยเพิ่มความแม่นยำได้
PR ที่เกี่ยวกับฟีเจอร์นี้เปิดค้างมาเกือบ 1 ปี
น่าเสียดายนิดหน่อยที่ maintainer เงียบเกินไป
หวังว่าปีหน้าจะดูแลและ merge PR จากชุมชนได้ดีกว่านี้
ผมยังใช้ oobabooga อยู่ ด้วยการรองรับ exlv2 ทำให้รัน inference บนการ์ด 3090 คู่ได้มีประสิทธิภาพกว่ามาก
เท่าที่จำได้ แม้ exl2 จะเร็วกว่า แต่โดยเฉพาะที่ bit depth ต่ำ ๆ ดูเหมือนว่า quantization แบบ gptq จะดีกว่าในแง่ความแม่นยำ
สงสัยว่ามีมูลค่าเพิ่มอะไรเมื่อเทียบกับ
outlineshttps://www.souzatharsis.com/tamingLLMs/notebooks/structured...
หวังว่าจะอัปเดตการสุ่มตัวอย่างให้รองรับรูปแบบได้มากขึ้น เพิ่มความแม่นยำ และปรับปรุงประสิทธิภาพ โดยอิงจากงานวิจัยล่าสุดเกี่ยวกับ outlines/xgrammar
สงสัยว่ามีวิธีที่ดีที่สุดในการให้ อินพุตแบบมีโครงสร้าง แก่ LLM หรือไม่
เช่น กรณีใส่ประโยค 100 ประโยคเข้าไป แล้วให้จัดหมวดหมู่แต่ละประโยคได้หลายวิธี การรับข้อมูลแบบมีโครงสร้างนั้นง่าย แต่แนวทางของผมที่ใส่เลขบรรทัดไว้ข้างหน้ารู้สึกเทอะทะ
แต่ในกรณีนี้ วิธีที่ดีที่สุดน่าจะเป็นการส่งประโยคทีละประโยค เพื่อไม่ให้โมเดลสับสน
ถ้าจัด prompt เป็นรูปแบบ
"จงจัดหมวดหมู่ประโยคต่อไปนี้ กฎคือ ..."+ ประโยค ก็จะใช้ประโยชน์จาก prefix cache ได้ และประสิทธิภาพอาจดีกว่าการถามทีเดียวทั้งหมดด้วยแน่นอนว่าวิธีนี้เป็นไปได้เมื่อมี prefix cache และไม่ได้คิดเงินตามจำนวนโทเคนอินพุต ทุกวันนี้ผู้ให้บริการส่วนใหญ่จะให้ใช้ในราคาที่ต่ำลงหากแสดงเจตนาว่าต้องการใช้ prefix cache