ในโลกของ AI production สมัยนี้ การที่โมเดลตอบว่า "มั่นใจ 99%" แต่ความจริงถูกแค่ 60% นี่คือปัญหาที่ทำให้ระบบหลายตัวพังทลาย ในบทความนี้ผมจะสอนวิธี calibrate โมเดล AI ให้ probability ที่ออกมานั้นตรงกับความเป็นจริง พร้อมโค้ดที่ใช้งานได้จริงกับ HolySheep AI
ทำไม Calibration ถึงสำคัญ: กรณีศึกษาจากโปรเจกต์จริง
ผมเคยพัฒนาระบบ RAG สำหรับองค์กรขนาดใหญ่แห่งหนึ่ง ระบบตอบคำถามลูกค้าเกี่ยวกับสินค้าคงคลัง ปัญหาคือตอนแรกโมเดลบอกว่ามั่นใจ 95% แต่พอตรวจสอบ才发现ว่า accuracy จริงๆ แค่ 70% เท่านั้น นี่คือจุดที่ผมเริ่มศึกษาเรื่อง model calibration อย่างจริงจัง
พื้นฐาน: Calibration Curve และ Expected Calibration Error (ECE)
Calibration ที่ดีหมายความว่า เมื่อโมเดลบอกว่ามั่นใจ X% แล้วความถูกต้องควรจะใกล้ X% จริงๆ
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
def calculate_ece(confidences, accuracies, n_bins=10):
"""
คำนวณ Expected Calibration Error (ECE)
- confidences: ความมั่นใจที่โมเดลทำนาย (0-1)
- accuracies: ความถูกต้องจริง (0 หรือ 1)
"""
bin_boundaries = np.linspace(0, 1, n_bins + 1)
ece = 0.0
for i in range(n_bins):
bin_lower = bin_boundaries[i]
bin_upper = bin_boundaries[i + 1]
# หา samples ในแต่ละ bin
in_bin = (confidences > bin_lower) & (confidences <= bin_upper)
if np.sum(in_bin) > 0:
avg_confidence = np.mean(confidences[in_bin])
avg_accuracy = np.mean(accuracies[in_bin])
bin_weight = np.sum(in_bin) / len(confidences)
ece += bin_weight * np.abs(avg_confidence - avg_accuracy)
return ece
def plot_calibration_curve(confidences, accuracies, model_name="Model"):
"""วาด Calibration Curve"""
bin_boundaries = np.linspace(0, 1, 11)
confidences_bin = []
accuracies_bin = []
for i in range(len(bin_boundaries) - 1):
in_bin = (confidences > bin_boundaries[i]) & \
(confidences <= bin_boundaries[i + 1])
if np.sum(in_bin) > 0:
confidences_bin.append(np.mean(confidences[in_bin]))
accuracies_bin.append(np.mean(accuracies[in_bin]))
# Perfect calibration line
plt.figure(figsize=(10, 8))
plt.plot([0, 1], [0, 1], 'k--', label='Perfect Calibration')
plt.plot(confidences_bin, accuracies_bin, 'o-',
linewidth=2, markersize=8, label=model_name)
plt.xlabel('Confidence', fontsize=12)
plt.ylabel('Accuracy', fontsize=12)
plt.title(f'Calibration Curve - {model_name}', fontsize=14)
plt.legend()
plt.grid(True, alpha=0.3)
plt.xlim([0, 1])
plt.ylim([0, 1])
plt.savefig('calibration_curve.png', dpi=150)
plt.show()
ตัวอย่างการใช้งาน
np.random.seed(42)
n_samples = 1000
โมเดลที่ uncalibrated (overconfident)
true_probs = np.random.uniform(0, 1, n_samples)
confidences_uncal = np.minimum(1.0, true_probs + np.random.normal(0.15, 0.1, n_samples))
confidences_uncal = np.clip(confidences_uncal, 0, 1)
accuracies = (np.random.uniform(0, 1, n_samples) < true_probs).astype(int)
คำนวณ ECE
ece_before = calculate_ece(confidences_uncal, accuracies)
print(f"ECE ก่อน Calibration: {ece_before:.4f}")
แสดงผล
plot_calibration_curve(confidences_uncal, accuracies, "Uncalibrated Model")
Temperature Scaling: วิธี Calibration ที่ง่ายแต่ทรงพลัง
Temperature scaling เป็นวิธีที่นิยมมากที่สุดในการ calibrate โมเดล โดยปรับ temperature parameter T ใน softmax function
import torch
import torch.nn.functional as F
from scipy.optimize import minimize_scalar
class TemperatureScaler:
"""
Temperature Scaling for Model Calibration
ปรับ temperature T ให้ probability ตรงกับความเป็นจริง
"""
def __init__(self):
self.temperature = 1.0
def calibrate(self, logits, labels, verbose=True):
"""
Calibrate โมเดลด้วย validation set
- logits: output ก่อน softmax จากโมเดล
- labels: ground truth labels
"""
# Objective: minimize NLL
def nll_loss(T):
scaled_logits = logits / T
probs = F.softmax(scaled_logits, dim=1)
loss = F.cross_entropy(scaled_logits, labels)
return loss.item()
# Optimize temperature
result = minimize_scalar(nll_loss, bounds=(0.1, 10.0), method='bounded')
self.temperature = result.x
if verbose:
print(f"Optimal Temperature: {self.temperature:.4f}")
print(f"NLL Loss: {result.fun:.4f}")
return self.temperature
def forward(self, logits):
"""ใช้ calibrated temperature ในการ predict"""
scaled_logits = logits / self.temperature
return F.softmax(scaled_logits, dim=1)
def get_calibration_metrics(probs, labels):
"""คำนวณ metrics สำหรับวัด calibration quality"""
predictions = np.argmax(probs, axis=1)
accuracies = (predictions == labels).astype(float)
confidences = np.max(probs, axis=1)
ece = calculate_ece(confidences, accuracies)
nll = -np.mean(np.log(probs[np.arange(len(labels)), labels] + 1e-10))
# Brier Score
one_hot = np.zeros_like(probs)
one_hot[np.arange(len(labels)), labels] = 1
brier = np.mean(np.sum((probs - one_hot) ** 2, axis=1))
return {
'ECE': ece,
'NLL': nll,
'Brier Score': brier,
'Accuracy': np.mean(accuracies)
}
ตัวอย่างการใช้งานกับ HolySheep API
import requests
import json
def get_model_calibration_data():
"""
ดึง calibration data จาก HolySheep API
ราคา: DeepSeek V3.2 เพียง $0.42/MTok ประหยัดมาก
"""
base_url = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
}
# ดึง logits จากโมเดล
payload = {
"model": "deepseek-v3.2",
"messages": [
{"role": "user", "content": "Explain what calibration means in ML"}
],
"temperature": 0.7,
"max_tokens": 500
}
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 200:
result = response.json()
return result['choices'][0]['message']['content']
return None
print("Calibration Metrics:")
print(f"- ECE: ยิ่งต่ำยิ่งดี (< 0.01 ดีมาก)")
print(f"- NLL: Negative Log Likelihood")
print(f"- Brier Score: วัดความแม่นยำของ probability")
Ensemble และ Bayesian Methods สำหรับ Uncertainty Estimation
สำหรับงานที่ต้องการความแม่นยำสูงในการประเมิน uncertainty การใช้ ensemble หรือ Bayesian approach จะให้ผลลัพธ์ที่ดีกว่า
import torch
import torch.nn as nn
from typing import List, Tuple
class MC_Dropout_Estimator:
"""
Monte Carlo Dropout - วิธีประมาณค่า uncertainty โดยใช้ dropout ในการ inference
"""
def __init__(self, model, n_dropout_samples=30):
self.model = model
self.n_dropout_samples = n_dropout_samples
self.model.train() # เปิด dropout mode
def predict_with_uncertainty(self, inputs):
"""ทำนายพร้อมคำนวณ uncertainty"""
with torch.no_grad():
dropout_outputs = []
for _ in range(self.n_dropout_samples):
output = self.model(inputs)
dropout_outputs.append(F.softmax(output, dim=1))
# Stack outputs
stacked = torch.stack(dropout_outputs)
# Mean prediction
mean_probs = torch.mean(stacked, dim=0)
# Uncertainty = variance across predictions
uncertainty = torch.var(stacked, dim=0)
return mean_probs, uncertainty
def get_prediction_with_confidence(self, inputs, threshold=0.7):
"""
Return prediction พร้อม confidence level
- ถ้า uncertainty สูง → ต้องมีคนตรวจสอบ
"""
mean_probs, uncertainty = self.predict_with_uncertainty(inputs)
predictions = torch.argmax(mean_probs, dim=1)
confidence = torch.max(mean_probs, dim=1)[0]
max_uncertainty = torch.max(uncertainty, dim=1)[0]
# กำหนดว่าต้อง human review หรือไม่
needs_review = max_uncertainty > (1 - threshold)
return {
'predictions': predictions,
'confidence': confidence,
'uncertainty': max_uncertainty,
'needs_human_review': needs_review
}
class Deep_Ensemble_Calibrator:
"""
Deep Ensemble - รวมผลจากหลายโมเดลเพื่อ uncertainty ที่ดีกว่า
"""
def __init__(self, models: List[nn.Module]):
self.models = models
self.scalers = [TemperatureScaler() for _ in models]
def calibrate_ensemble(self, logits_list, labels):
"""Calibrate ทุกโมเดลใน ensemble"""
for i, (model, scaler, logits) in enumerate(
zip(self.models, self.scalers, logits_list)
):
print(f"Calibrating model {i+1}/{len(self.models)}...")
scaler.calibrate(logits, labels, verbose=False)
def ensemble_predict(self, inputs):
"""Predict ด้วย ensemble พร้อม uncertainty"""
all_probs = []
for model, scaler in zip(self.models, self.scalers):
model.eval()
with torch.no_grad():
logits = model(inputs)
probs = scaler.forward(logits)
all_probs.append(probs)
# รวม probability จากทุกโมเดล
stacked_probs = torch.stack(all_probs)
mean_probs = torch.mean(stacked_probs, dim=0)
std_probs = torch.std(stacked_probs, dim=0)
return mean_probs, std_probs
ตัวอย่างการใช้งาน
print("MC Dropout สำหรับ Uncertainty:")
print("- ทำ inference หลายครั้งโดยเปิด dropout")
print("- ความแปรปรวน (variance) คือ uncertainty")
print("- ยิ่งทำหลายครั้ง ยิ่งแม่นยำ แต่ช้าลง")
การนำ Calibration ไปใช้กับ RAG System จริง
ในระบบ RAG ของผมที่ใช้กับอีคอมเมิร์ซ ผมใช้ calibration เพื่อตัดสินใจว่าควร trust คำตอบจาก retrieval หรือไม่
import requests
from typing import Dict, List, Optional
class RAG_Calibrated_Retriever:
"""
RAG System พร้อม Calibration-based Trust Scoring
"""
def __init__(self, api_key: str):
self.api_key = api_key
self.base_url = "https://api.holysheep.ai/v1"
self.calibration_threshold = 0.75 # confidence threshold
def query_with_confidence(self, question: str, context: str) -> Dict:
"""
Query RAG system พร้อมวัด confidence
ใช้ DeepSeek V3.2 ราคาเพียง $0.42/MTok
"""
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
prompt = f"""Based on the following context, answer the question.
If the context doesn't contain enough information, say so.
Context: {context}
Question: {question}
Answer with your confidence level (0-100%) and explain why."""
payload = {
"model": "deepseek-v3.2",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": prompt}
],
"temperature": 0.3, # ต่ำเพื่อความสม่ำเสมอ
"max_tokens": 300
}
response = requests.post(
f"{self.base_url}/chat/completions",
headers=headers,
json=payload
)
if response.status_code == 200:
result = response.json()
answer = result['choices'][0]['message']['content']
# ประมวลผล confidence จาก response
confidence = self._extract_confidence(answer)
return {
'answer': answer,
'confidence': confidence,
'needs_human_review': confidence < self.calibration_threshold,
'trust_level': self._get_trust_level(confidence)
}
return {'error': 'API request failed'}
def _extract_confidence(self, text: str) -> float:
"""ดึง confidence percentage จาก text"""
import re
match = re.search(r'(\d+)%', text)
if match:
return int(match.group(1)) / 100
# ถ้าไม่มี explicit percentage ใช้ heuristic
high_confidence_words = ['certain', 'definitely', 'clearly', 'obviously']
low_confidence_words = ['might', 'perhaps', 'possibly', 'uncertain']
text_lower = text.lower()
for word in high_confidence_words:
if word in text_lower:
return 0.85
for word in low_confidence_words:
if word in text_lower:
return 0.45
return 0.65 # default
def _get_trust_level(self, confidence: float) -> str:
"""กำหนดระดับความน่าเชื่อถือ"""
if confidence >= 0.9:
return "HIGH - Auto-answer"
elif confidence >= 0.75:
return "MEDIUM - Monitor"
elif confidence >= 0.5:
return "LOW - Flag for review"
else:
return "VERY_LOW - Escalate to human"
ตัวอย่างการใช้งาน
rag_system = RAG_Calibrated_Retriever("YOUR_HOLYSHEEP_API_KEY")
result = rag_system.query_with_confidence(
question="สินค้า SKU-1234 มี stock กี่ชิ้น?",
context="สินค้า SKU-1234 คือ กระเป๋าสตางค์หนังแท้ สีดำ มี stock 45 ชิ้น"
)
print(f"คำตอบ: {result['answer']}")
print(f"ความมั่นใจ: {result['confidence']:.0%}")
print(f"Trust Level: {result['trust_level']}")
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. ใช้ Temperature = 1.0 ตลอด (Underfitting)
ปัญหา: หลายคนไม่ calibrate เลย ใช้ temperature default ที่ 1.0 ซึ่งไม่เหมาะกับทุกโมเดล
# ❌ ผิด: ใช้ temperature แบบ fixed โดยไม่ calibrate
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json={
"model": "gpt-4.1",
"messages": [...],
"temperature": 1.0 # ไม่ได้ calibrate
}
)
✅ ถูกต้อง: Calibrate temperature ก่อนใช้งานจริง
สร้าง validation set ก่อน
val_logits = [] # เก็บ logits จาก validation data
val_labels = [] # เก็บ labels จาก validation data
Optimize temperature
scaler = TemperatureScaler()
optimal_temp = scaler.calibrate(
torch.tensor(val_logits),
torch.tensor(val_labels)
)
print(f"Temperature ที่เหมาะสม: {optimal_temp}")
ใช้ temperature ที่ calibrated แล้ว
response = requests.post(
f"{base_url}/chat/completions",
headers=headers,
json={
"model": "gpt-4.1",
"messages": [...],
"temperature": optimal_temp # ใช้ค่าที่ optimize แล้ว
}
)
2. Confusion ระหว่าง Confidence และ Uncertainty
ปัญหา: ใช้ confidence เป็น uncertainty ซึ่งไม่ถูกต้อง โมเดลอาจมั่นใจแม้ว่าจะไม่รู้
# ❌ ผิด: ใช้ softmax probability เป็น uncertainty
def bad_uncertainty_score(probs):
confidence = np.max(probs, axis=1)
# โมเดลอาจบอก 0.95 แม้ไม่รู้อะไรเลย
return 1 - confidence # นี่ไม่ใช่ uncertainty ที่แท้จริง
✅ ถูกต้อง: ใช้ entropy หรือ variance จาก ensemble
def proper_uncertainty_score(probs, use_entropy=True):
if use_entropy:
# Entropy: สูง = ไม่แน่นอน
eps = 1e-10
entropy = -np.sum(probs * np.log(probs + eps), axis=1)
max_entropy = np.log(probs.shape[1])
return entropy / max_entropy # normalize to [0, 1]
else:
# จาก ensemble: variance สูง = ไม่แน่นอน
return np.std(probs, axis=1)
หรือใช้ MC Dropout
mc_estimator = MC_Dropout_Estimator(model, n_dropout_samples=50)
mean_probs, uncertainty = mc_estimator.predict_with_uncertainty(inputs)
Decision boundary
needs_review = uncertainty > 0.15 # threshold ที่เหมาะสม
3. ลืมว่า Calibration ต้องทำบน Data ที่ Representative
ปัญหา: Calibrate บน training data แล้วนำไปใช้บน production data ที่ต่างกัน
# ❌ ผิด: Calibrate บน training set
train_logits, train_labels = get_model_outputs(model, train_data)
scaler.calibrate(train_logits, train_labels) # ผิด!
✅ ถูกต้อง: แบ่งข้อมูลเป็น train/val/calibration
Step 1: Train โมเดล
model.fit(train_data, train_labels)
Step 2: Validation สำหรับ hyperparameters
val_logits, val_labels = get_model_outputs(model, val_data)
tune_hyperparameters(model, val_logits, val_labels)
Step 3: Calibration set แยกต่างหาก (ไม่เคยเห็นตอน train)
calib_logits, calib_labels = get_model_outputs(model, calibration_data)
scaler.calibrate(calib_logits, calib_labels)
Step 4: Test บน test set (เพื่อวัดผลจริง)
test_logits, test_labels = get_model_outputs(model, test_data)
metrics = get_calibration_metrics(scaler.forward(test_logits), test_labels)
print(f"ECE on Test Set: {metrics['ECE']:.4f}")
สำคัญ: Production data ควรคล้าย calibration data
ถ้า distribution เปลี่ยน → ต้อง recalibrate
4. ใช้ API Endpoint ผิด (Authentication Error)
ปัญหา: ใช้ base_url หรือ API key format ผิด
# ❌ ผิด: ใช้ OpenAI endpoint
WRONG_URL = "https://api.openai.com/v1/chat/completions" # ห้ามใช้!
❌ ผิด: API key format ผิด
headers = {
"Authorization": "sk-xxxxx", # format ผิดสำหรับ HolySheep
}
✅ ถูกต้อง: ใช้ HolySheep endpoint
CORRECT_BASE_URL = "https://api.holysheep.ai/v1"
headers = {
"Authorization": f"Bearer YOUR_HOLYSHEEP_API_KEY", # รูปแบบที่ถูกต้อง
"Content-Type": "application/json"
}
ตรวจสอบ API key ก่อนใช้งาน
def verify_api_connection(api_key: str) -> bool:
"""ตรวจสอบว่า API key ใช้งานได้หรือไม่"""
response = requests.get(
"https://api.holysheep.ai/v1/models",
headers={"Authorization": f"Bearer {api_key}"}
)
return response.status_code == 200
if verify_api_connection("YOUR_HOLYSHEEP_API_KEY"):
print("✅ API Key ถูกต้อง")
else:
print("❌ ตรวจสอบ API Key อีกครั้ง")
สรุป: Best Practices สำหรับ AI Model Calibration
- เริ่มจาก Temperature Scaling: ง่าย ดี คุ้มค่า เหมาะกับงานส่วนใหญ่
- วัดผลด้วย ECE: ตั้งเป้าหมาย ECE < 0.01 สำหรับงาน critical
- ใช้ Ensemble สำหรับ High-Stakes: ถ้าต้องการ uncertainty ที่แม่นยำจริงๆ
- Recalibrate เป็นระยะ: data drift ทำให้ calibration เสื่อมประสิทธิภาพ
- Monitor ใน Production: วัด confidence distribution และ ECE เป็นระยะ
การ calibrate โมเดล AI ไม่ใช่ทางเลือกอีกต่อไป แต่เป็นสิ่งจำเป็นสำหรับระบบที่ต้องการความน่าเชื่อถือ โดยเฉพาะในอุตสาหกรรมที่ต้องการ compliance และ audit trail
👉 สมัคร HolySheep AI — รับเครดิตฟรีเมื่อลงทะเบียน