ชุดข้อมูลและการประเมิน
UAT 315 ปัญญาประดิษฐ์ การวิเคราะห์ข้อมูล และการมองเห็นด้วยคอมพิวเตอร์สำหรับระบบอากาศยานไร้คนขับ
บทเรียน
เมื่อเรียนจบโมดูลนี้ ผู้เรียนจะสามารถ
- อธิบายการรั่วของข้อมูลเมื่อภาพจากเที่ยวบินเดียวกันอยู่ทั้งชุดฝึกและชุดทดสอบ และแบ่งชุดตามกลุ่มด้วย GroupShuffleSplit
- คำนวณ confusion matrix precision recall F1 และ IoU และอ่านความหมายให้ตรงงาน
- อธิบายความต่างระหว่าง mAP50 กับ mAP50–95 ของ COCO
- เลือกจุดตัดความมั่นใจตามผลเสียของความผิดพลาด และจัดทำ data card
ทำไมต้องรู้
ทีมหนึ่งรายงานว่าแบบจำลองตรวจจับผู้ประสบภัยได้ความแม่นยำ 99% แต่เมื่อบินในป่าที่ไม่เคยบิน ระบบพลาดเกือบหมด สาเหตุไม่ได้อยู่ที่แบบจำลอง แต่อยู่ที่ วิธีแบ่งข้อมูลและวิธีวัดผล การประเมินที่ผิดทำให้ทั้งทีมเชื่อผิด และอาจทำให้ผู้บังคับบัญชาตัดสินใจใช้ระบบที่ยังไม่พร้อม หน่วยความรู้ “ตรวจชุดข้อมูลก่อนเทรน AI” และ “ตรวจคุณภาพภาพและอ่านตัวชี้วัด AI” ของคลังความรู้โดรนเป็นพื้นฐานของโมดูลนี้
ข้อมูลรั่วจากเที่ยวบินเดียวกัน
ภาพหลายเฟรมจากเที่ยวบินเดียวกันคล้ายกันมาก ถ้าสุ่มแบ่ง รายภาพ เฟรมที่เกือบเหมือนกันจะอยู่ทั้งในชุดฝึกและชุดทดสอบ แบบจำลองจึงได้คะแนนสูงเพราะจำฉากได้ ไม่ใช่เพราะเรียนรู้สิ่งที่ต้องการ เรียกว่า การรั่วของข้อมูล (data leakage) ทางแก้คือแบ่ง ตามกลุ่ม เช่น ทั้งเที่ยวบินอยู่ชุดเดียว ถ้าต้องใช้กับพื้นที่ใหม่ ก็ต้องแบ่งตามพื้นที่
ตัวอย่างที่ 1 คะแนนที่ดีเกินจริงจากการแบ่งแบบสุ่ม
ข้อมูลสังเคราะห์ 40 เที่ยวบิน เที่ยวละ 30 เฟรม แต่ละเที่ยวมี “ลายเซ็น” ของฉากเฉพาะตัว และป้ายของทั้งเที่ยวเป็นแบบเดียวกัน สัญญาณที่เกี่ยวกับป้ายจริงมีอยู่น้อยมาก ใช้แบบจำลองเพื่อนบ้านใกล้สุด (1-NN) ซึ่งทำนายตามตัวอย่างฝึกที่คล้ายที่สุด
import numpy as np
from sklearn.model_selection import GroupShuffleSplit, train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
rng = np.random.default_rng(345)
flights, frames = 40, 30
signature = rng.normal(0, 1, (flights, 5))
flight_label = rng.integers(0, 2, flights)
X = np.repeat(signature, frames, axis=0) + rng.normal(0, 0.1, (flights * frames, 5))
X[:, 0] += 0.3 * np.repeat(flight_label, frames)
y = np.repeat(flight_label, frames)
groups = np.repeat(np.arange(flights), frames)
knn = KNeighborsClassifier(n_neighbors=1)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.25, random_state=0)
print(f"random split by frame: accuracy {accuracy_score(y_te, knn.fit(X_tr, y_tr).predict(X_te)):.3f}")
train_idx, test_idx = next(GroupShuffleSplit(test_size=0.25, random_state=0).split(X, y, groups))
print(f"split by flight: accuracy {accuracy_score(y[test_idx], knn.fit(X[train_idx], y[train_idx]).predict(X[test_idx])):.3f}")
print("flights shared between train and test:", len(set(groups[train_idx]) & set(groups[test_idx])))
random split by frame: accuracy 1.000
split by flight: accuracy 0.300
flights shared between train and test: 0
แบ่งแบบสุ่มได้ 100% เพราะเฟรมทดสอบทุกเฟรมมีฝาแฝดจากเที่ยวเดียวกันอยู่ในชุดฝึก แต่เมื่อแบ่งตามเที่ยวบิน คะแนนเหลือเพียง 30% ซึ่งไม่ดีกว่าการเดาสุ่มของปัญหาสองคลาส (ชุดทดสอบมีเพียง 10 เที่ยวบิน ตัวเลขนี้จึงแกว่งได้มาก) ตัวเลขหลังคือความสามารถจริงกับเที่ยวบินใหม่ ซึ่งในข้อมูลนี้แทบไม่มีเลย
Confusion matrix และตัวชี้วัด
Confusion matrix นับผลสี่แบบ ได้แก่ TP (มีจริงและระบบบอกว่ามี) FP (ไม่มีจริงแต่บอกว่ามี หรือเตือนผิด) FN (มีจริงแต่พลาด) และ TN (ไม่มีและบอกว่าไม่มี) จากนั้นคำนวณ
- precision สิ่งที่ระบบแจ้งว่าพบ ถูกกี่ส่วน
- recall สิ่งที่มีจริง ระบบพบกี่ส่วน
- F1 ค่าเฉลี่ยฮาร์มอนิกของทั้งสอง
ตัวอย่างนี้ใช้ยอดนับจากหน่วยความรู้ “Computer Vision และการต่อยอดสู่ Edge AI” คือ TP = 8, FP = 2, FN = 4
from sklearn.metrics import confusion_matrix, precision_recall_fscore_support
y_true = np.array([1] * 12 + [0] * 8)
y_pred = np.array([1] * 8 + [0] * 4 + [1] * 2 + [0] * 6)
tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel()
p, r, f1, _ = precision_recall_fscore_support(y_true, y_pred, average="binary")
print(f"TP {tp} FP {fp} FN {fn} TN {tn}")
print(f"precision {p:.4f} recall {r:.4f} F1 {f1:.4f}")
TP 8 FP 2 FN 4 TN 6
precision 0.8000 recall 0.6667 F1 0.7273
ถ้าเป็นงานค้นหาผู้ประสบภัย recall 0.67 หมายถึงพลาดผู้ประสบภัยหนึ่งในสามคน ซึ่งร้ายแรงกว่าการเตือนผิดที่ทำให้ต้องส่งคนไปตรวจเพิ่ม คะแนนความมั่นใจ (confidence) ของแบบจำลองก็ไม่ใช่ความแม่นยำ คะแนน 0.9 ของภาพหนึ่งไม่ได้แปลว่าแบบจำลองถูก 90% ของทุกภาพ
IoU และ mAP
สำหรับการตรวจจับวัตถุ กรอบทำนายต้องจับคู่กับกรอบจริงแบบหนึ่งต่อหนึ่งก่อน โดยต้องมี IoU ถึงเกณฑ์จึงนับเป็น TP กรอบสองกรอบที่ทับวัตถุเดียวนับเป็น TP ได้เพียงกรอบเดียว
AP (average precision) สรุปความสัมพันธ์ระหว่าง precision กับ recall เมื่อเปลี่ยนจุดตัดความมั่นใจ ส่วน mAP เฉลี่ย AP ของทุกคลาส การประเมินของ COCO ใช้ AP ที่เฉลี่ยจาก IoU 0.50 ถึง 0.95 ทีละ 0.05 เป็นตัวชี้วัดหลัก ส่วน AP50 ใช้ IoU 0.50 ค่าเดียว ซึ่งผ่อนปรนกว่ามาก ตัวเลขสองแบบนี้จึงเทียบกันตรง ๆ ไม่ได้
iou_thresholds = np.round(np.arange(0.50, 0.96, 0.05), 2)
print("COCO IoU thresholds:", iou_thresholds.tolist(), "count", len(iou_thresholds))
ap_per_class = {"person": 0.6, "vehicle": 0.8}
print("mAP =", round(sum(ap_per_class.values()) / len(ap_per_class), 2))
COCO IoU thresholds: [0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95] count 10
mAP = 0.7
ค่า AP ของแต่ละคลาสในตัวอย่างเป็นค่าสมมติจากคลังความรู้ AP คำนวณจากยอดนับที่จุดตัดเดียวไม่ได้ ต้องใช้โปรแกรมประเมินที่เรียงคะแนนความมั่นใจทั้งหมด และเมื่อรายงานต้องระบุชื่อตัวชี้วัดให้ชัด เช่น “mAP50–95 บนชุดทดสอบที่แบ่งตามพื้นที่”
เลือกจุดตัดตามผลเสีย
scores = np.array([0.95, 0.91, 0.88, 0.84, 0.80, 0.74, 0.69, 0.62, 0.55, 0.48,
0.44, 0.39, 0.33, 0.28, 0.22, 0.18, 0.12, 0.09, 0.05, 0.03])
labels = np.array([1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0])
for threshold in (0.7, 0.5, 0.3):
pred = (scores >= threshold).astype(int)
p, r, f1, _ = precision_recall_fscore_support(labels, pred, average="binary")
print(f"threshold {threshold}: alerts {pred.sum():>2} precision {p:.2f} recall {r:.2f} F1 {f1:.2f}")
threshold 0.7: alerts 6 precision 0.83 recall 0.50 F1 0.62
threshold 0.5: alerts 9 precision 0.78 recall 0.70 F1 0.74
threshold 0.3: alerts 13 precision 0.69 recall 0.90 F1 0.78
ลดจุดตัดทำให้พบของจริงมากขึ้น แต่เตือนผิดมากขึ้นด้วย ไม่มีจุดตัดที่ดีที่สุดสำหรับทุกงาน ต้องเลือกจากผลเสียของความผิดพลาดแต่ละแบบและกำลังคนที่ตรวจยืนยันได้ และต้องเลือกด้วย ชุดตรวจสอบ ไม่ใช่ชุดทดสอบ
Data card
Data card คือเอกสารประจำชุดข้อมูล บอกที่มาและสิทธิ์การใช้ภาพ วิธีเก็บและติดป้าย นิยามของแต่ละคลาส การแบ่งชุดและหน่วยที่ใช้แบ่ง จำนวนตัวอย่างต่อคลาส สภาพที่ครอบคลุมและไม่ครอบคลุม (เช่น ไม่มีภาพกลางคืน) และข้อจำกัดที่ทราบ ผู้ใช้แบบจำลองต้องอ่านได้ว่าผลใช้ได้กับสถานการณ์ใดบ้าง
กิจกรรมในชั้นเรียน
กิจกรรม: ตรวจรายงานผลของแบบจำลอง
- อาจารย์แจกรายงานสมมติที่อ้าง “แม่นยำ 98%” ให้แต่ละกลุ่มหาอย่างน้อยห้าคำถามที่ต้องถามก่อนเชื่อ เช่น แบ่งชุดอย่างไร ตัวชี้วัดชื่ออะไร ชุดทดสอบมาจากพื้นที่ใด
- ทำแบบฝึก “ตรวจชุดข้อมูลก่อนเทรน AI” ในคลังความรู้โดรน หาข้อผิดพลาดห้าประเภทใน manifest ก่อนรันโปรแกรมตรวจ
- สำหรับงานค้นหาผู้ประสบภัยและงานนับต้นไม้ เลือกจุดตัดจากตารางในบทพร้อมเหตุผล
- เขียน data card หนึ่งหน้าสำหรับชุดภาพที่กลุ่มจะเก็บในอนาคต
ข้อผิดพลาดที่พบบ่อย
ระวัง
- สุ่มแบ่งรายภาพ ทั้งที่ภาพมาจากเที่ยวบินหรือพื้นที่เดียวกัน
- รายงานความแม่นยำอย่างเดียว ในงานตรวจจับที่ของจริงมีน้อย
- เทียบ mAP50 กับ mAP50–95 ราวกับเป็นตัวเลขเดียวกัน
- ใช้ชุดทดสอบเลือกจุดตัดความมั่นใจ
- ไม่มี data card ผู้ใช้จึงไม่รู้ว่าแบบจำลองไม่เคยเห็นภาพกลางคืนหรือภาพจากพื้นที่อื่น
สรุป
- ภาพจากเที่ยวบินเดียวกันต้องอยู่ชุดเดียวกัน มิฉะนั้นคะแนนจะดีเกินจริง
- precision recall และ F1 ตอบคนละคำถาม เลือกตัวชี้วัดตามผลเสียของความผิดพลาด
- การตรวจจับต้องจับคู่กรอบด้วย IoU และ mAP50 กับ mAP50–95 เป็นตัวชี้วัดคนละตัว
- เลือกจุดตัดด้วยชุดตรวจสอบ และบันทึกข้อมูลด้วย data card
แบบฝึกตรวจความเข้าใจ
- TP = 30, FP = 10, FN = 20 มี precision และ recall เท่าใด
- ถ้า FP ลดเป็น 0 แต่ TP และ FN เท่าเดิม F1 เป็นเท่าใด เมื่อ TP = 8 และ FN = 4
- ทำไมการแบ่งแบบสุ่มรายภาพจึงให้คะแนนสูงเกินจริง
- mAP50–95 ของ COCO เฉลี่ย AP จาก IoU กี่ระดับ
- งานใดควรเน้น recall มากกว่า precision ระหว่าง “ค้นหาผู้ประสบภัย” กับ “เลือกภาพสวยไปทำสื่อประชาสัมพันธ์”
เฉลย
- precision และ recall
- เฟรมที่เกือบเหมือนกันจากเที่ยวบินเดียวกันอยู่ทั้งชุดฝึกและชุดทดสอบ แบบจำลองจึงจำฉากได้
- 10 ระดับ คือ 0.50, 0.55, …, 0.95
- ค้นหาผู้ประสบภัย เพราะการพลาดคนที่ต้องการความช่วยเหลือร้ายแรงกว่าการเตือนผิด
สรุปสูตรสำคัญ
| precision | |
| recall | |
| F1 | |
| mAP |
แหล่งอ้างอิงหลัก
- scikit-learn developers. Cross-validation: Evaluating estimator performance (scikit-learn 1.9). link
- scikit-learn developers. Metrics and scoring: Quantifying the quality of predictions (scikit-learn 1.9). link
- COCO Consortium. Detection evaluation. Common Objects in Context. link
- Ultralytics. Models supported by Ultralytics (YOLO11, YOLO26). Ultralytics documentation. link
- Géron, A. (2025). Hands-on machine learning with Scikit-Learn and PyTorch. O'Reilly. link
- National Institute of Standards and Technology. (2023). Artificial intelligence risk management framework (AI RMF 1.0) (NIST AI 100-1). link
อ่านเพิ่มเติม
ศึกษาหน่วยความรู้ที่กำหนดล่วงหน้า ดูสื่อประกอบ และทำ quiz ประจำโมดูล
ตรวจชุดข้อมูลก่อนเทรน AI
ตรวจคุณภาพภาพและอ่านตัวชี้วัด AI
เตรียมและแบ่งชุดข้อมูล
ฝึกและประเมิน object detection
ในชั้นเรียน / ภาคสนาม
บรรยาย อภิปรายกรณีศึกษา และแก้โจทย์ในชั้นเรียน
หลักฐานการเรียนรู้: ผล quiz และแบบฝึกหัดที่ส่ง