เพื่อที่จะ Optimize Accuracy หรือเพิ่มความแม่นยำของ Agent/LLM เราจำเป็นจะต้องเข้าใจว่า Test-time Scaling คืออะไร ในบทความนี้เรามาเร่ิมจากทำความเข้าใจพื้นฐานและเทคนิคจากเปเปอร์ที่ชื่อว่า Self-Consistency พร้อมดู Code ตัวอย่างกัน
คือเทคนิคนึงในการเพิ่มความแม่นยำหรือความฉลาดโดยการเพิ่ม “เวลา” หรือ “Resource”ในการตอบสนองของ LLM ผ่านการใช้งาน เรียกง่ายๆคือ ใช้เวลาตอบนานขึ้นเพื่อความแม่นยำที่มากขึ้น(นานขึ้น คือใช้ resource มากขึ้น)
Test-time = ตอนเรียกใช้ ตอน Inference จริง
Scaling = การ scale ความฉลาด
เทคนิคที่ถูกพูดถึงคู่กับ Test-time scaling คือ Train-time scaling หมายถึงการเพิ่มความฉลาดตอนที่ Train model นั่นเอง
ตัวอย่างที่เห็นได้ชัดของ Test-time scaling คือการใช้ Chain of Thought เพื่อให้ LLM มีการให้เหตุผลก่อนคำตอบสุดท้ายทำให้ความแม่นยำมากขึ้น
ส่วนตัวอย่างของ Train-time scaling คือยิ่งโมเดลใช้เวลาเทรนนานมากขึ้น ด้วย data ที่ใหญ่ขึ้น ทำให้โมเดลมีความฉลาดมากขึ้น
ก่อนจะถลำลึกไปกว่านี้ อยากอธิบายก่อนว่า Chain of Thought หรือ CoT คือเทคนิคการเขียน Prompt ที่ “บังคับ” หรือ “โน้มน้าว” ให้ LLM คิดเป็นขั้นเป็นตอน โดยการทำสิ่งต่อไปนี้
โดยเทคนิค Chain of Thought เป็นพื้นฐานสำคัญมากๆ เนื่องจากเทคนิคที่ใช้เพิ่มความแม่นยำต่างๆ ส่วนใหญ่เกิดมาจากงานวิจัยตัวนี้ รวมไปถึง การ Train พวก Reasoning model หรือ Non-Reasoning model ในยุคหลังๆ ที่ LLM เริ่มมีการทำ Chain of Thought โดยไม่ต้องพิมพ์ Prompt บอกกันแล้ว
มาต่อที่ Self-Consistency กันครับ
ต้องบอกก่อนว่า Paper ตัวนี้ถือได้ว่า “เก่า” ออกมาในช่วงปี 2023 ช่วงก่อน ChatGPT บูมซะอีก แต่ว่าเป็นเปเปอร์ตัวหนึ่งที่ถูก reference ในช่วงหลังๆ ระหว่างปี 2024–2025 บ่อยมากๆ โดยเฉพาะ paper ประเภท Test-time scaling จนผมต้องกลับมาอ่านและทบทวนดูว่า เจ้า Self-Consistency นี่คืออะไรกันแน่ แล้วมันใช้งานยังไง
สุดท้ายตัว method เป็นที่น่าสนใจกว่าผลลัพธ์ซะอีก เราไปดูกันครับ
ผู้เขียนกล่าวว่า Self-Consistency ตัวนี้ ช่วยเพิ่มความสามารถในการให้เหตุผลได้ดีขึ้นมากใน Benchmark ต่างๆ เช่น
ในตอนแรกก็ไม่ได้ว้าวอะไรมากเพราะ Method/Paper ต่างๆ ก็มี base เป็น Chain-of-Thought (CoT) ต้องเอาชนะอยู่แล้ว
มาสะดุดตรง…
แรงบันดาลใจของ paper ตัวนี้เกิดจากว่า
วิธีการแก้ปัญหาสามารถมีได้หลายวิธี เพื่อแก้ปัญหาเดียว
Self-Consistency เป็นเทคนิคที่ใช้ร่วมกับ Chain-of-Thought (CoT) prompting เพื่อเพิ่มความแม่นยำของคำตอบ โดยมีหลักการดังนี้:
จากรูปด้านบน(Figure 1) มี Generate 3 ครั้ง ได้คำตอบ:
คำตอบสุดท้ายจึงเป็น 18USD
เพื่อนๆสามารถลอง Self-Consistency ผ่าน Google Colab ตามขั้นตอนด้านล่างได้เลย
Task หลักของ Agent เราในตัวอย่างนี้คือการแก้ Puzzle ที่เลือกมาจาก ZebraLogic Benchmark ซึ่งจะเห็นได้ว่า Prompt และ Extraction Code แต่ส่วนถูกเขียนมาเพื่อแก้โจทย์นี้โดยเฉพาะ
หลักการทำงานของโค้ดชุดนี้คือ
การหาโจทย์มาเป็นตัวอย่างที่ชัดเจนสำหรับ paper ตัวนี้ค่อนข้างยากมาก เนื่องจาก benchmark ใน paper อย่าง GSM8Kโดน LLM ตั้งแต่ 3B เกมหมดแล้ว เลยต้องออกตามหาโจทย์แปลกๆ แต่ไม่ยากจนเกินไป และเลือกโมเดลที่เก่งพอดีๆ ไม่มากไป เพื่อหลีกเลี่ยงการตอบผิดหมดทุก Sample กับตอบถูกหมดทุก Sample
เลยมาลงที่ ZebraLogic Benchmark นี่แหละ
https://en.wikipedia.org/wiki/Zebra_Puzzle
เรามาดูโค้ดส่วนที่สำคัญๆ กันต่อได้เลย
# Prompt
# CoT Prompt
system_prompt="""
Each example/input/question is a Logic Grid Puzzle, also known as a Zebra Puzzle. In each puzzle, we are given N houses (numbered 1 to N from left to right) and M features for each house. There are N distinct values for each feature, and each house must have a unique value for each feature. Given a list of clues, one should be able to deduce a unique correct assignment of values. The logic grid puzzle is a typical Constraint Satisfaction Problem (CSP) and is often used to test humans' logical reasoning abilities in exams such as the Law School Admission Test (LSAT).
You always solve problem step by step
start answering with `## Reasoning steps:` and end with
## Final answer:
```json
````
"""
# Example Input Prompt
user_prompt="""
There are 2 houses, numbered 1 to 2 from left to right.
Each house is occupied by a different person.
Each house has a unique attribute for each of the following characteristics:
- Each person has a unique name: **Arnold, Eric**
- People own unique car models: **ford f150, tesla model 3**
- The people keep unique animals: **cat, horse**
**Clues**:
1. Eric is directly left of the person who owns a Tesla Model 3.
2. The person who keeps horses is in the first house.
"""
# Example Output Prompt
assistant_message="""
## Reasoning steps:
From Clue 1, we know that Eric is to the left of someone, so he must be the owner of House 1 because House 2 is the rightmost house.
Additionally, we know that the person in House 2 must be Arnold, and he owns a Tesla Model 3. Thus, Eric owns a Ford F150.
From Clue 2, we know that Eric keeps horses in House 1, which means the other house must keep cats. Finally, we arrive at the unique solution to this puzzle.
The solution is presented in table format:
## Final answer:
```json
{
"header":[
"Houses",
"Name",
"CarModel",
"Animal"
],
"rows":[
[
"1",
"Eric",
"ford f150",
"horse"
],
[
"2",
"Arnold",
"tesla model 3",
"cat"
]
]
}
```
"""
# lgp-test-2x2-33
# lgp-test-6x5-2
# lgp-test-2x4-33
# lgp-test-6x6-5
# lgp-test-3x3-24
question="""
Question:
There are 3 houses, numbered 1 to 3 from left to right, as seen from across the street. Each house is occupied by a different person. Each house has a unique attribute for each of the following characteristics:
- Each person has a unique name: `Peter`, `Eric`, `Arnold`
- Each person has a favorite color: `red`, `white`, `yellow`
- Each mother is accompanied by their child: `Fred`, `Meredith`, `Bella`
## Clues:
1. Arnold is the person whose favorite color is red.
2. The person's child is named Fred is somewhere to the left of Eric.
3. The person whose favorite color is red is in the second house.
4. The person's child is named Bella is in the first house.
5. The person who loves white is the person's child is named Meredith.
## Headers
"header": [
"House",
"Name",
"Color",
"Children"
]
"""
correct_answer="""
```json
{
"header": [
"House",
"Name",
"Color",
"Children"
],
"rows": [
["1", "Peter", "yellow", "Bella"],
["2", "Arnold", "red", "Fred"],
["3", "Eric", "white", "Meredith"]
]
}
```
"""
คือโค้ดส่วนที่เขียน Prompt
system_prompt คือส่วนที่เป็น Instruction หลัก
user_prompt โจทย์ตัวอย่าง
assistant_prompt คำตอบของโจทย์ตัวอย่าง เพื่อแสดงให้เห็นวิธีการคิด(CoT) และรูปแบบ output
question คือโจทย์ที่เราต้องการให้ LLM แก้
correct_answer คือเฉลยของโจทย์
model_name = "openai/gpt-4.1-nano" # Choose not too smart model to see the diversity (phi-4,openai/gpt-4.1,deepseek/DeepSeek-R1-0528,xai/grok-3,openai/gpt-4.1-mini,deepseek/DeepSeek-V3-0324)
temperature=1.0 # Choose 1.0 for the most diverse answers
top_p=1.0 # Choose 1.0 for the most diverse answers
คือโค้ดส่วนที่เรา set up parameter และ configuration ต่างๆ
ส่วนที่อยากเน้นคือ
number_samples = 20 คือ จำนวน Sample ที่เราจะใช้ หรือจำนวนครั้งที่เราจะเรียก LLM เพื่อเอาคำตอบทั้งหมดมาให้ Majority Vote
model_name = “openai/gpt-4.1-nano” คือโมเดลที่เราจะใช้ เราเลือกใช้ openai/gpt-4.1-nano เพราะว่า จำเป็นต้องใช้ Model ที่ไม่ฉลาดเกินไป เพื่อจะ demo ประโยชน์ ของ Self-Consistency ถ้าเลือกโมเดลฉลาดเกินที่ตอบถูกโดยไม่ต้องใช้ Self-Consistency อาจจะไม่เห็นประโยชน์ของมันเท่าที่ควร
temperature=1.0 และ top_p=1.0 คือค่าที่ควบคุมความ random ของคำตอบ เราตั้งไว้สูงสุดเพื่อให้ LLM มีคำตอบที่หลากหลายมากที่สุด
responses = []
final_answers = [] # Collect all final answers
try:
for i in range(num_samples):
response = client.complete(
messages=messages,
temperature=temperature,
top_p=top_p,
model=model_name,
)
responses.append(response.choices[0])
answer = response.choices[0].message.content
final_ans = extract_final_answer_regex(answer)
if final_ans is None:
print(f"Warning: Could not extract answer from sample {i+1}")
clean_ans = final_ans.lower().replace(" ", "")
json_object = extract_json_from_string(clean_ans)
final_answers.append(json_object) # Store the final answer
print(f"\nSample {i+1}: {json_object}")
print(f"{'='*50}")
except Exception as e:
print(f"Error processing sample {i+1}: {e}")
โค้ดส่วนนี้เป็นส่วนที่วน loop เพื่อเรียก LLM เพื่อเก็บ Sample จากการเรียกหลายๆ ครั้ง พร้อมกับ Extract คำตอบสุดท้ายของแต่ละ Call
if final_answers:
# Convert answers to normalized format for counting
normalized_answers = [normalize_answer(ans, for_comparison=False) for ans in final_answers]
answer_counts = Counter(normalized_answers)
most_common_string, count = answer_counts.most_common(1)[0]
# Convert back to original type for display
try:
most_common_normalized = json.loads(most_common_string)
# Find the original answer that matches this normalized version
for ans in final_answers:
if normalize_answer(ans, for_comparison=False) == most_common_string:
most_common_answer = ans
break
else:
most_common_answer = most_common_normalized
except (json.JSONDecodeError, TypeError):
most_common_answer = most_common_string
print(f"\n{'='*50}")
print(f"MOST COMMON ANSWER (appeared {count}/{num_samples} times):")
print(f"{most_common_answer}")
print(f"{'='*50}\n")
# Optional: print all answer frequencies
print("All answer frequencies:")
for ans_str, freq in answer_counts.most_common():
# Find the original answer that matches this normalized version
original_ans = None
for ans in final_answers:
if normalize_answer(ans, for_comparison=False) == ans_str:
original_ans = ans
break
if original_ans:
print(f"{original_ans} (appeared {freq} times)")
else:
try:
ans_display = json.loads(ans_str)
print(f"{ans_display} (appeared {freq} times)")
except (json.JSONDecodeError, TypeError):
print(f"{ans_str} (appeared {freq} times)")
นำคำตอบทุกอันจาก Sample ที่เรามีมาหา Common Answer หรือ Majority Vote
จากการทดสอบพบว่า ด้วย Dataset 20 row(grid_size 3*3 ล้วน) กับ Model gpt-4.1-nano
แบบ Sample = 1 หรือไม่ได้ใช้ Self-Consistency เลย ได้ Accuracy ที่ 35%
แบบ Sample = 5 หรือใช้ Self-Consistenccy ขนาดเท่ากับ 5 ได้ Accuracy ที่ 60%
ทั้งนี้อยากให้ลองรันการทดลองนี้ด้วยตัวเอง แล้วลองปรับ model และ parameter และ grid_size เพื่อความเข้าใจที่ลึกซึ้งยิ่งขึ้น
Self-Consistency เหมาะกับงานที่ต้องการความแม่นยำสูง และ End User ยินดีรอผลลัพธ์สักพัก ยกตัวอย่างเช่น งานวิเคราะห์ทางการเงิน หรือการตรวจสอบเอกสารสำคัญ ที่ความถูกต้องสำคัญกว่าความเร็ว หรือ Data Pipeline สามารถ manage ความคาดหวังของ user ได้ว่าต้องรอ
ถ้าเป็น Agentic System ที่มีการ Execute หลาย step อย่าง Coding Agent ที่ต้องทำหลายขั้นตอนต่อเนื่อง การใช้ Sampling N ที่สูง (เช่น 10–20) ในทุก step จะทำให้ใช้เวลาและ Token เยอะมาก ไม่ค่อยเหมาะ กับ LLM Inference latency ณ ปัจจุบัน
จากตัวอย่างก่อนหน้านี้ เราใช้ Self-Consistency กับโจทย์คณิตศาสตร์ที่มีคำตอบเป็นตัวเลขชัดเจน การหา Majority Vote ทำได้ง่ายด้วยการเปรียบเทียบค่าตรงๆ
แต่ในความเป็นจริง โจทย์ส่วนใหญ่ไม่ใช่แค่ตัวเลข เช่น:
คำตอบเหล่านี้อาจมีถ้อยคำต่างกัน แต่ให้ความหมายเดียวกัน เช่น:
คำตอบ 1: “ออกกำลังกายตอนเช้าช่วยเพิ่มพลังงานตลอดทั้งวัน”
คำตอบ 2: “การออกกำลังกายในช่วงเช้าทำให้มีแรงมากขึ้นตลอดวัน”
คำตอบ 3: “การวิ่งเช้าๆ ช่วยให้รู้สึกกระฉับกระเฉงตลอดวัน”
ทั้ง 3 คำตอบนี้ต่างกัน แต่ แก่นของเนื้อหาเหมือนกัน ถ้าเราเช็คแบบ exact match จะไม่เจอ Majority Vote เลย
ใช้ LLM อีกตัวเป็น Judge + Semantic Clustering เมื่อคำตอบไม่ใช่ตัวเลข เราต้องใช้ LLM อีกตัว มาช่วยตัดสินว่า(คล้ายๆ กับ LLM-as-a-Judge)คำตอบไหนมีความหมายเหมือนกัน หรือใช้เทคนิค Semantic Similarity เพื่อจัดกลุ่มคำตอบ
สำหรับผม Self-Consistency เป็นเปเปอร์ที่ช่วยให้การก้าวเข้าสู่โลก Test-time scaling ง่ายขึ้น เนื่องจากเห็นชัดเจนว่า Test-time เพิ่มขึ้นจาก Sample N ที่มากขึ้น และเห็นความแม่นยำที่เพิ่มมากขึ้นอีกด้วย
Self-Consistency ด้วยตัวมันเองมีประโยชน์ในการ Optimize Accuracy มาก ถ้าเทียบกับความง่ายในการใช้งาน เพียงแค่เพิ่ม Sample แล้วหา Majority Vote แต่หากยังไม่เพียงพอ หรือใครอยากศึกษาต่อผมแนะนำให้ปรับ parameter ในโค้ด และเลือกโมเดลหลายๆตัวมาลองดูผลลัพธ์เทียบกัน หรือจะลองเปลี่ยนโจทย์เป็น Level ยากสุก 6*6 ก็ได้ครับ จะได้เห็นความแตกต่างว่าในแต่ละโจทย์หรือแต่ละ parameter ได้ผลต่างกันยังไง
สุดท้ายถ้าอยากไปต่อให้ลอง Implement Tree of Thought เพิ่มเติมครับ
หากใครกำลังมองหา Consult มาช่วยสร้าง Agentic AI หรือช่วยปรับปรุง AI Agent ที่มีอยู่แล้วให้มี Accuracy และ Scale ได้
ติดต่อทีมงาน PALO IT Thailand ได้ที่ Facebook: PALO IT Thailand (https://www.facebook.com/PALOITTH)