ml/ library Learn by doing
ALGORITHM 24 · PYTHON / JAVASCRIPT

Radius Neighbors

ถามเฉพาะเพื่อนบ้านที่อยู่ในรัศมีที่กำหนด

LEARNING LEVEL2
ต่อยอดโดยตรงควรรู้มาก่อน · k-Nearest Neighbors
ลองใน Playground

01Overall Concept

OVERALL CONCEPT

ไอเดียหลัก — Radius Neighbors หาเพื่อนบ้านด้วยระยะที่ยอมรับได้ แทนการบังคับจำนวน K ถ้าอยู่กลางกลุ่มหนาแน่นก็มีคนช่วยตอบมาก ถ้าอยู่ในบริเวณห่างไกลอาจไม่มีใครตอบเลย จึงเหมาะเมื่อความหมายของคำว่าใกล้สำคัญกว่าจำนวนตัวอย่าง

ลองนึกภาพว่า — ถ้าต้องการรู้ว่าแถวบ้านฝนตกไหม เราอาจถามทุกคนในรัศมีหนึ่งกิโลเมตร แทนที่จะถามให้ครบห้าคนจนต้องลากคนที่อยู่อีกจังหวัดมารวมด้วย

ตัวอย่างการนำไปใช้ — จำแนกจากตัวอย่างในละแวกที่คล้ายกัน ประมาณค่าจากเซนเซอร์ใกล้เคียง หรือใช้ระบบที่ยอมเว้นคำตอบเมื่อข้อมูลใหม่ไกลจากสิ่งที่เคยเห็น

02อธิบายภาษาคนเข้าใจง่ายๆ

เล่าแบบไม่ใช้ศัพท์ยาก

KNN ถามว่าใครใกล้ที่สุด K คน ส่วน Radius Neighbors ถามว่าใครบ้างที่ใกล้ไม่เกินระยะที่เรายอมรับ จำนวนผู้ตอบจึงไม่ตายตัว

นึกภาพวงกลมรอบบ้านเรา ทุกบ้านที่อยู่ในวงมีสิทธิ์โหวตว่าละแวกนี้เป็นย่านแบบไหน วงเล็กเก็บเฉพาะบ้านใกล้จริง ๆ วงใหญ่เริ่มดึงย่านอื่นเข้ามาด้วย

ถ้าในวงมี A สามจุดและ B หนึ่งจุด การโหวตแบบเท่ากันเลือก A แต่ถ้าไม่มีจุดในวงเลย โมเดลไม่มีหลักฐานเฉพาะบริเวณนั้น เราต้องกำหนดว่าจะงดตอบหรือใช้กฎสำรอง

ในตัวอย่างนี้ สีเทาหมายถึงไม่มีเพื่อนบ้าน ไม่ใช่คลาสที่สี่ คะแนน accuracy นับจุดที่งดตอบว่าไม่ถูกด้วย จึงควรดูจำนวนที่ตอบได้ประกอบกัน

ระยะ 0.15 ใน Playground วัดหลังหารพิกัดด้วย 100 ให้เป็นช่วง 0–1 ถ้าเปลี่ยนหน่วยหรือปรับสเกลอีกแบบ ก็ต้องเลือกรัศมีใหม่ ตัวเลขเดิมไม่จำเป็นต้องใช้ได้กับทุกชุดข้อมูล

ภาพจำ: ขีดวงว่าคำแนะนำจากที่ไหนยังใกล้พอ และยอมรับเมื่อไม่มีใครอยู่ในวง

03How it works

ค่อยๆ ทำไปด้วยกัน ตั้งแต่ข้อมูลแถวแรกจนถึงการตรวจคำตอบ

  1. แบ่งข้อมูลก่อนเลือกวง

    เตรียม X และ y แล้วกันชุดทดสอบไว้ ใช้เฉพาะข้อมูลฝึกเป็นเพื่อนบ้าน ถ้าเป็นงานพื้นที่จริง ระยะทางควรมีความหมายกับพิกัดและไม่ให้ข้อมูลที่อยู่ติดกันมากรั่วข้ามชุดจนคะแนนสูงเกินจริง

  2. กำหนดหน่วยของ radius

    ในโค้ดใช้ MinMaxScaler ภายใน pipeline เพื่อแปลง feature จากชุดฝึก แล้วตั้ง RadiusNeighborsClassifier(radius=0.15, outlier_label=-1) โดยคลาสจริงเป็น 0, 1, 2 และ -1 สงวนไว้แทนการงดตอบ

  3. ค้นหาทุกคนในวง

    คำนวณระยะจากคำถามใหม่ถึงข้อมูลฝึก เก็บเฉพาะ d ≤ radius จำนวนเพื่อนบ้านแต่ละคำถามจึงต่างกันได้ ไม่ต้องเติมจุดไกลเพื่อให้ครบ K

  4. รวมคำตอบและจัดการวงว่าง

    โหวตคลาสด้วยจำนวนเพื่อนบ้านเมื่อ weights="uniform" ถ้าวงว่างให้ -1; ค่าเริ่มต้น outlier_label=None ของ classifier จะเกิดข้อผิดพลาดเมื่อทำนายจุดที่ไม่มีเพื่อนบ้าน ส่วน regression ต้องกำหนดการรับมือค่าที่ทายไม่ได้แยกต่างหาก

  5. ตรวจทั้งความถูกต้องและการครอบคลุม

    เปรียบเทียบ accuracy บนชุดทดสอบทั้งหมดกับ coverage ซึ่งเป็นสัดส่วนที่มีเพื่อนบ้าน เลือกรัศมีด้วย validation แล้วค่อยประเมิน test อีกครั้ง รัศมีที่แม่นเฉพาะจุดจำนวนน้อยอาจยังไม่เหมาะใช้งานจริง

เปิดตัวอย่างโค้ดเพื่อลองทำตาม →ตัวอย่างตัวเลขในเรื่องใช้ช่วยอธิบาย ส่วนโค้ดและ Playground ใช้ชุดข้อมูลสาธิตของแต่ละหน้าอ่านเอกสารอ้างอิงของเทคนิคนี้ ↗

ข้อมูล พารามิเตอร์ และภาพรวมการคำนวณ

DATA INX · featuresy · targetradius=0.15 · พบคลาส [A, A, A, B]
CALCULATERadius Neighbors
radiusweightsoutlier_label
RESULT0.800.150.05class / probabilityA ได้ 3 จาก 4 เสียง → A · วงว่าง → -1
เส้นทางของข้อมูล: จากค่าที่รับเข้า ผ่านการคำนวณด้วยพารามิเตอร์ ไปเป็นผลลัพธ์
INPUT

ข้อมูลที่รับเข้ามา

X เป็น feature เชิงระยะทางที่มีสเกลเหมาะสม และ y เป็นคำตอบของแต่ละแถว

radius=0.15 · พบคลาส [A, A, A, B]
PARAMETERS

พารามิเตอร์ที่ใช้

radius
ระยะมากที่สุดที่ยังถือเป็นเพื่อนบ้าน
weights
uniform ให้เสียงเท่ากัน หรือ distance ถ่วงตามระยะ
outlier_label
คำตอบสำรองสำหรับ classifier เมื่อวงว่าง
OUTPUT

ผลลัพธ์ที่ได้

คลาสหรือค่าทำนายต่อแถว และสถานะที่ไม่มีเพื่อนบ้าน

A ได้ 3 จาก 4 เสียง → A · วงว่าง → -1
CALCULATION

คำนวณทีละขั้น

  1. 01

    คำนวณ d จากคำถามใหม่ถึงตัวอย่างฝึก

  2. 02

    เก็บทุกจุดที่ d ≤ radius

  3. 03

    โหวตหรือเฉลี่ย; ถ้าวงว่างใช้กฎที่กำหนดไว้

ควรรู้อีกนิด — เมื่อไม่มีเพื่อนบ้าน ตัวอย่างนี้คืน -1 เพื่อแสดงการงดตอบ ไม่ใช่คลาสใหม่ ถ้าใช้ outlier_label="most_frequent" จะเป็นกฎสำรองให้คลาสที่พบมากที่สุดและต้องอธิบายให้ผู้ใช้ทราบ

04Make it yours

Python · scikit-learn
# ติดตั้ง: pip install numpy scikit-learn# ดาวน์โหลด sample.csv จากหน้านี้ แล้ววางไว้ข้างสคริปต์import numpy as np data = np.genfromtxt("radius-neighbors-sample.csv", delimiter=",", skip_header=1)X = data[:, :2] from sklearn.neighbors import RadiusNeighborsClassifierfrom sklearn.pipeline import make_pipelinefrom sklearn.preprocessing import MinMaxScalerfrom sklearn.metrics import accuracy_score, classification_report y = data[:, 2].astype(int)# กันทุกแถวที่ 5 ไว้ทดสอบ ตามข้อมูลสาธิตใน Playground# งานจริงควรเลือก split ให้เหมาะกับเวลา/บุคคล/กลุ่มของข้อมูลis_test = np.arange(len(X)) % 5 == 0X_train, X_test = X[~is_test], X[is_test]y_train, y_test = y[~is_test], y[is_test]model = make_pipeline(MinMaxScaler(), RadiusNeighborsClassifier(radius=0.15, outlier_label=-1))model.fit(X_train, y_train)pred = model.predict(X_test)print("Test accuracy:", accuracy_score(y_test, pred))print(classification_report(y_test, pred, labels=[0, 1, 2], zero_division=0))print("Coverage:", np.mean(pred != -1))# -1 = ไม่มีเพื่อนบ้าน; accuracy รวมถือว่าการงดตอบไม่ถูกต้อง# Python fit scaler จากชุดฝึก; Playground ใช้พิกัดหาร 100# จึงอาจได้คำตอบต่างกันเล็กน้อย 

โค้ดเปลี่ยนตามค่าที่เลือกใน Playground · แต่ละภาษาใช้ข้อมูลตัวอย่างตามที่ระบุในโค้ด

05Learn by playing

ปรับ รัศมีบนพิกัด 0–1 แล้วสังเกตรูปแบบคำทำนายและตัวชี้วัดที่คำนวณใหม่ทันที

Sample dataset / Three classes● คำนวณใหม่เมื่อปรับค่า
รัศมีมากใช้ผู้โหวตมากขึ้น สีเทาแปลว่ายังไม่มีข้อมูลใกล้พอ
พื้นที่สีคือคำทำนาย จุดคือคำตอบจริงFeature x → · Feature y ↑ · ขอบเข้ม = ชุดทดสอบ
Test accuracy86.7%
Coverage · ตอบได้30 / 30
พารามิเตอร์ที่ลอง0.15

Playground เป็น implementation เพื่อการเรียนรู้ที่คำนวณในเบราว์เซอร์ ส่วนโค้ด Python ใช้ scikit-learn จึงอาจให้ค่าต่างกันเล็กน้อยจากรายละเอียดการ optimize และค่าเริ่มต้นของไลบรารี ผลนี้ไม่ใช่ benchmark บนข้อมูลจริง