Skip to main content

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

· 12 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) ไม่ใช่ตาม 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

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

Note: ไม่มี reasoning_effort และไม่มี soft switch — Qwen3.6 ไม่มีพารามิเตอร์ reasoning_effort (เป็นของ OpenAI o-series) พารามิเตอร์ที่ควบคุม reasoning/thinking มีแค่ enable_thinking (เปิด/ปิด) และ preserve_thinking (รักษา thinking traces จาก historical messages) ส่วนการควบคุม "ระดับ" ของการคิดให้ปรับ sampling parameters แทน นอกจากนี้ Qwen3.6 ยังไม่รองรับ soft switch แบบ Qwen3 (ไม่มี /think และ /no_think) ต้องควบคุมผ่าน API parameters เท่านั้น

Note: syntax ต่างกันระหว่าง framework — สำหรับ vLLM/SGLang ส่งผ่าน chat_template_kwargs={"enable_thinking": False} แต่สำหรับ Alibaba Cloud DashScope ส่ง "enable_thinking": False โดยตรง (ไม่ต้องห่อใน chat_template_kwargs) ใช้ preserve_thinking ก็เช่นเดียวกัน

Note: reasoning_effort: "none" ใช้ปิด thinking ได้จริงบน vLLM — ทดสอบจริงบน vLLM 0.23.1rc1 พบว่า vLLM แปลง reasoning_effort: "none" เป็นการปิด thinking แต่ค่าอื่น (low/medium/high) ไม่ได้ปรับระดับ thinking จริง แนะนำให้ใช้ chat_template_kwargs.enable_thinking โดยตรงเพื่อความชัดเจน

ทำไม 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 เดิมซ้ำ ๆ

Note: presence_penalty caveat — model card เตือนว่าสามารถปรับ presence_penalty ระหว่าง 0-2 เพื่อลด endless repetitions แต่ค่าที่สูงอาจทำให้เกิด language mixing และลด model performance เล็กน้อย

นอกจากนี้ยังมีข้อแนะนำเรื่อง 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
// Note: Qwen3.6 ไม่มี reasoning_effort (เป็นพารามิเตอร์ของ OpenAI o-series)
// ควบคุม thinking ผ่าน enable_thinking + sampling parameters แทน

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

Note: max_tokens ต้องไม่ต่ำกว่า 2048 เมื่อเปิด thinking — ทดสอบจริงพบว่า reasoning ใช้ ~500 tokens ก่อนตอบ ถ้าตั้งน้อยไปโมเดลจะถูกตัดกลางคัน (finish_reason: length) และไม่ได้ content เลย สำหรับ backtest mode ที่ปิด thinking สามารถตั้ง max_tokens: 500 ได้ แต่ live mode ที่เปิด thinking ต้องตั้งอย่างน้อย 2048-3000

Note: มี profile พร้อมใช้ — ผมสร้าง qwen-trading profile ไว้ใน LiteLLM แล้ว (temp=0.3, seed=42, enable_thinking=true, max_tokens=3000) ดูรายละเอียดได้ที่ Virtual Models บน LiteLLM Proxy

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 หลายทางเลือก แต่ยังไม่ได้ทดสอบ

Note: presence_penalty ไม่กัน action-level loop — ทดสอบจริงกับ agent พบว่า presence_penalty ทำงานที่ token level ไม่ใช่ action level agent ติด loop 23 รอบเพราะโมเดลเรียก tool ซ้ำใน behavior เดิม (search → play → search → play) แม้ token ต่างกันทุกรอบ ต้องตั้ง max iterations ที่ application level หรือใส่ stop condition ใน system prompt ดูรายละเอียดได้ที่ Virtual Models: Profile 3 qwen-agent

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

Note: long context ใช้ temp ต่ำกว่า 1.0 — แต่ถ้า context ใหญ่มาก (50K-120K tokens) โจทย์เปลี่ยน ต้องการ faithfulness ต่อ context มากกว่า creativity ถ้า temp สูงเกินไป บวกกับ context ยาว โมเดลมีแนวโน้มหลุดจากข้อมูลต้นฉบับ ข้ามรายละเอียดสำคัญ หรือสร้างสมมติฐานเพิ่มเอง ดังนั้น long context analysis ทำงานได้ดีกว่าที่ temp 0.6-0.8 ผมตั้ง qwen-longctx profile ไว้ที่ temp=0.7, presence_penalty=0.5 ดูรายละเอียดได้ที่ Virtual Models: Profile 5 qwen-longctx

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 เท่านั้น

Context Length และ Thinking Preservation

Model card ระบุว่า context length นั้น 262,144 tokens natively และขยายได้ถึง 1,010,000 tokens ด้วย YaRN RoPE scaling ผมตั้ง 128K recipe ไว้ (max_model_len: 131072) ซึ่งเพียงพอสำหรับ use case ปัจจุบัน

Note: YaRN scaling — ถ้าต้องการ context เกิน 262K ใช้ YaRN ผ่าน --hf-overrides ใน vLLM โดยตั้ง factor ตามความต้องการ เช่น 524K context ใช้ factor=2.0 แต่ model card เตือนว่า YaRN เป็น static scaling อาจกระทบ performance บน short texts จึงควรเปิดเฉพาะตอนประมวลผล long context

นอกจากนี้ยังมี Thinking Preservation ด้วย preserve_thinking option — ช่วยให้ model เก็บ reasoning context จาก historical messages ไว้ใช้ต่อ model card ระบุว่า beneficial สำหรับ agent scenarios เพราะ:

  • เพิ่ม decision consistency — model มี reasoning context เต็มรูปแบบจากรอบก่อน
  • ลด token consumption — ลด redundant reasoning ในรอบถัดไป
  • ปรับปรุง KV cache utilization — optimize inference efficiency ทั้ง thinking และ non-thinking mode

Note: ผมเปิด preserve_thinking: true ในหลาย profile ที่ต้องการ multi-step reasoning — qwen-reasoning, qwen-coder, qwen-agent, qwen-agent-think, qwen-longctx ดูรายละเอียดแต่ละ profile ได้ที่ Virtual Models บน LiteLLM Proxy

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

  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) แล้วตั้ง sampling ตามนั้น โดยเฉพาะอย่าสับสนว่า "coding ต้อง temp สูง" — จริง ๆ แล้ว precise coding ใน thinking mode ต้อง temp=0.6 ต่ำกว่า general tasks ที่ใช้ temp=1.0

Model Architecture (อ้างอิง)

  • Type: Causal Language Model with Vision Encoder
  • Parameters: 35B total, 3B activated (MoE)
  • Architecture: Gated DeltaNet + Gated Attention hybrid, 40 layers
  • MoE: 256 experts, 8 routed + 1 shared activated
  • Context: 262,144 natively, extensible to 1,010,000 with YaRN
  • MTP: Trained with multi-step (ใช้ใน vLLM เป็น MTP k=2 สำหรับ speculative decoding)

References

แชร์บทความ

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

Buy Me a Coffee
Loading...