Skip to main content

Qwen3.6-35B-A3B บน DGX Spark: เรื่อง sampling ที่ผมตั้งผิดมาตลอด

· 9 min read

ผมตั้งค่า vLLM มาหลายเดือนด้วยสูตรเดียวตลอด — temperature: 0.6, top_p: 0.95, top_k: 20 แล้วก็ปล่อยให้ client ไป override เอาเอง ไม่ว่าจะเป็น trading bot, Hermes agent, code review — ใช้ค่าเดียวกันหมด

จนเมื่อวานนี้ผมเปิด Hugging Face model card ของ Qwen3.6-35B-A3B อ่านเล่น ๆ ในส่วน Sampling Parameters ถึงได้รู้ว่า — Qwen team แนะนำ sampling ตาม mode และ task type ไม่ใช่ตาม benchmark หรือ use case แบบที่ผมเข้าใจ

อ้าว ผมเลยต้องกลับมานั่งคิดใหม่

TL;DR

Qwen3.6-35B-A3B แนะนำ sampling parameters ตาม mode (thinking vs instruct/non-thinking) และ task type (general vs coding vs reasoning) ไม่ใช่ตาม benchmark หรือ use case ภายนอก จุดที่ผมเข้าใจผิดมาตลอดคือ — thinking mode สำหรับ general tasks ใช้ temperature=1.0 สูงกว่า coding ที่ใช้ temperature=0.6 ซึ่งกลับกับ intuition เดิมที่ว่า "coding ต้อง explore หลายทางเลือก จึงต้อง temp สูง" จริง ๆ แล้ว precise coding ต้องความแม่นยำสูง จึงใช้ temp ต่ำ ส่วน general tasks ใน thinking mode ต้องการความหลากหลายในการคิด จึงใช้ temp สูง

เริ่มจากความสับสนเรื่อง maxOutputTokens

ก่อนจะเข้าเรื่อง sampling ขอเคลียร์ confusion ที่ผมเพิ่งรู้ตัวเมื่อวานก่อน — maxOutputTokens ที่หลายคนถามหา "ค่า default บน server เป็นเท่าไหร่" จริง ๆ แล้ว มันเป็น client-side parameter เท่านั้น

# ที่ client ส่ง — ไม่มี server default
curl http://10.0.0.246:8001/v1/chat/completions -d '{
"model": "qwen3.6-35b-base",
"messages": [...],
"max_tokens": 3000 # <-- ตรงนี้
}'

ส่วนที่ vLLM server มีให้ตั้งจริง ๆ คือ max_model_len ซึ่งเป็น total context (prompt + output รวมกัน) ไม่ใช่ output อย่างเดียว

Note: ค่าที่อยู่บน DGX Spark ตอนนี้ — recipe qwen3.6-35b-a3b-base-128k-v23.yaml:

  • max_model_len: 131072 (128K = prompt + output รวมกัน)
  • temperature: 0.6 จาก --override-generation-config
  • top_p: 0.95, top_k: 20, min_p: 0.0
  • enable_thinking: true จาก --default-chat-template-kwargs
  • max_num_seqs: 12 (ลดจาก 40 เพราะ KV cache ใหญ่ขึ้นตาม context)

ผมตั้งมาตลอดแบบ "เซ็ตครั้งเดียว ใช้ทุกอย่าง" โดยไม่เคยตั้งคำถามว่า use case ต่างกันควรตั้งต่างกันไหม

สิ่งที่ผมเจอใน Sampling Parameters ของ Qwen

ใน HF model card มีตารางแนะนำ sampling ตาม mode และ task type — ผมเลย extract ออกมา:

ModeTask TypeTemperatureTop PTop KMin PPresence PenaltyRepetition Penalty
ThinkingGeneral tasks1.00.95200.01.51.0
ThinkingPrecise coding (e.g. WebDev)0.60.95200.00.01.0
Instruct (non-thinking)General tasks0.70.8200.01.51.0
Instruct (non-thinking)Reasoning tasks1.01.0400.02.01.0

Note: สังเกตว่า — Thinking mode สำหรับ general tasks ใช้ temp=1.0 สูงกว่า precise coding ที่ใช้ temp=0.6 ทั้งที่เป็น model ตัวเดียวกัน นี่แหละคือ insight ที่ผมพลาดไป

ทำไม thinking general ต้อง temp สูงกว่า coding? ผมเดาว่า:

  • General tasks ใน thinking mode ต้องการ ความหลากหลายในการคิด (diverse reasoning paths) — temp สูงช่วย explore หลายทางเลือกในการวิเคราะห์
  • Precise coding ต้องการ ความแม่นยำสูง (deterministic code structure) — temp ต่ำช่วยลด hallucination ใน syntax และ logic
  • ที่สำคัญ — presence penalty ของ general tasks สูง (1.5) เพื่อลด repetition ในขณะที่ coding ไม่มี presence penalty (0.0) เพราะ code อาจต้องใช้ตัวแปรหรือ pattern เดิมซ้ำ ๆ

นอกจากนี้ยังมีข้อแนะนำเรื่อง output length:

  • 32,768 tokens สำหรับ queries ทั่วไป
  • 81,920 tokens สำหรับ complex problems (math, programming competitions)

ทบทวน use case ของตัวเอง

หลังอ่านแล้วผมเลยนั่ง list use case ที่ใช้จริง แล้วลองเทียบกับ sampling ที่ผมตั้ง:

1. Trading bot (bot4k)

// AiTradeService.ts — เดิม
maxOutputTokens: 3000,
temperature: BACKTEST_MODE ? 0 : 0.2, // ตรึงต่ำเพราะ logic-sensitive
providerOptions: { openai: { reasoningEffort: 'medium' } }

ความเห็นตอนนี้: น่าจะถูกแล้ว — trading signal เป็น logic-sensitive task ที่ต้อง reproducible ใน backtest ใช้ temp 0/0.2 OK แต่ควรพิจารณาว่าใช้ thinking mode หรือ instruct mode — ถ้าเป็น instruct mode อาจตั้ง presence_penalty: 1.5 เพิ่มได้

2. Hermes coding agent

// Hermes workflow
maxTokens: 8000, // thinking + code blocks
temperature: 0.6, // <-- ตรงนี้อาจถูกแล้ว ถ้าเป็น precise coding
top_p: 0.95

Insight: Qwen แนะนำ temp=0.6 สำหรับ precise coding ใน thinking mode — ผมอาจต้องเพิ่ม presence_penalty: 0.0 และลองเทียบกับ temp=1.0 สำหรับ multi-step coding task ที่ต้องการ explore หลายทางเลือก แต่ยังไม่ได้ทดสอบ

3. Long document analysis

// 128K context
maxTokens: 4000,
temperature: 0.6,
enable_thinking: true

Insight: ถ้าเป็น general analysis ใน thinking mode ควรใช้ temp=1.0 ตามที่ Qwen แนะนำ — ค่า 0.6 ที่ตั้งไว้อาจต่ำเกินไปสำหรับ general tasks ใน thinking mode

Decision tree ที่ผมลองร่าง

จาก sampling parameters + use case ของตัวเอง ผมลองร่าง decision tree ไว้ใช้ตอนตั้งค่า:

ตารางเปรียบเทียบ: 32K → 128K บน vLLM v0.23.0

ข้อมูล benchmark จริงที่ผมวัดได้ — เทียบทั้ง 3 configuration ที่ผ่านมา เพื่อให้เห็นว่า context ขยายขึ้นมีผลยังไง:

Workloadv0.22.1rc1
+ 32K
MTP k=1
v0.23.0
+ 32K
+ FlashInfer=1
v0.23.0
+ 128K
+ MTP k=2
+ flags
Short (~200 tok)43.1 tok/s56.8 tok/s (+32%)63.6 tok/s
Medium (~1K tok)63.7 tok/s60.9 tok/s58.1 tok/s
Long (~16K tok)64.2 tok/s65.0 tok/s52.7 tok/s
Huge (~60K tok)N/AN/A25.7 tok/s
Max (~110K tok)N/AN/A16.4 tok/s

Note: ตัวเลขเหล่านี้คือ total tok/s รวม prefill — สำหรับ huge prompt, prefill กินเวลา 5-25s ขึ้นกับความยาว ถ้าดูแค่ pure generation speed จะสูงกว่านี้

ถ้าแยก pure generation speed (หัก prefill ออก) ของ 128K config:

WorkloadTotal timePrefill est.Gen timePure gen tok/s
0K (short)4.0s~0.1s~3.9s65
1K (medium)2.2s~0.3s~1.9s67
16K (long)14.5s~0.5s~14s54
64K (huge)19.5s~5s~14.5s34
120K (max)30.6s~25s~5.6s89

ที่น่าสนใจคือ — short prompt 0-1K tokens ได้ความเร็วเท่าเดิมหรือดีกว่า แม้จะขยาย context เป็น 128K แสดงว่า context length ไม่กระทบ generation speed ตรง ๆ ส่วนที่ช้าคือ prefill เท่านั้น

ข้อสำคัญที่ผมเพิ่งเห็นใน model card

"Context Length: Default 262,144 tokens. Maintain at least 128K tokens to preserve thinking capabilities."

แปลว่า — ถ้า context สั้นกว่า 128K model อาจไม่ preserve thinking ผมตั้ง 128K recipe ไว้พอดี (max_model_len: 131072) ตรงนี้โชคดี

นอกจากนี้ยังมี Thinking Preservation ด้วย preserve_thinking option — ช่วยให้ model เก็บ reasoning context จาก historical messages ไว้ใช้ต่อ ซึ่ง beneficial สำหรับ agent scenarios ที่ต้อง decision consistency

สรุปสิ่งที่ผมจะลอง

  1. ทดสอบ Hermes coding agent ด้วย temp=0.6, presence_penalty=0.0 — ตามที่ Qwen แนะนำสำหรับ precise coding ใน thinking mode
  2. เปรียบเทียบ temp=0.6 vs temp=1.0 บน general analysis tasks ใน thinking mode — ดูว่า quality ดีขึ้นไหม
  3. เพิ่ม LiteLLM alias แยกqwen3-thinking-general (temp=1.0, presence=1.5) vs qwen3-thinking-coding (temp=0.6, presence=0.0) vs qwen3-instruct (temp=0.7) เพื่อให้ client เลือกได้ตาม mode และ task
  4. อัปเดต bot4k comment — note ว่าทำไม trading ใช้ temp 0/0.2 แทนที่จะใช้ Qwen's default และว่าเป็น instruct mode หรือ thinking mode

สิ่งที่ผมยังไม่รู้

  • ผมยังไม่ได้ทดสอบว่า temp=1.0 บน general thinking tasks ใน Spark จริง ๆ ให้ผลดีกว่า temp=0.6 แค่ไหน — เป็นแค่ hypothesis จาก Qwen's sampling recommendations
  • LiteLLM alias routing ตาม mode และ task อาจทำให้ config ซับซ้อนขึ้น — ต้องดูว่า worth หรือไม่
  • presence_penalty และ repetition_penalty ใน vLLM อาจมี interaction กับ thinking mode ที่ผมยังไม่เข้าใจ
  • preserve_thinking ใน long-running agent อาจกิน context เร็วขึ้น — ต้อง balance ระหว่าง context retention กับ truncation

What's Next

  • ถ้าพี่ ๆ ที่อ่านอยู่ใช้ Qwen3.6-35B-A3B ด้วย ลองเล่าใน comments ได้ว่า sampling ที่ใช้เป็นยังไง
  • ถ้ามีคน test temp=1.0 กับ general thinking tasks บน DGX Spark แล้วเห็นผลชัด ผมจะกลับมาเขียน follow-up
  • บทความนี้เป็น "เรียนรู้จากการอ่าน model card" ไม่ใช่ guide สำเร็จรูป — ใช้วิจารณญาณด้วยนะครับ

Conclusion

การอ่าน HF model card ของ Qwen3.6-35B-A3B ทำให้ผมรู้ว่า sampling parameters ไม่ควรตั้ง "สูตรเดียวใช้ทุกอย่าง" โดยเฉพาะกับ model ที่มี hybrid reasoning แบบนี้ จุดเปลี่ยนสำคัญคือการแยก mode (thinking vs instruct) และ task type (general vs coding vs reasoning) แล้วตั้ง sampling ตามนั้น โดยเฉพาะอย่าสับสนว่า "coding ต้อง temp สูง" — จริง ๆ แล้ว precise coding ใน thinking mode ต้อง temp=0.6 ต่ำกว่า general tasks ที่ใช้ temp=1.0

References

แชร์บทความ

เนื้อหานี้มีประโยชน์ไหม? ช่วยสนับสนุนค่ากาแฟให้ผู้เขียนสักแก้ว

Buy Me a Coffee
Loading...