วิธีการเคอร์เนล (核方法, kernel method) หรือนิยมเรียกกันว่า
ลูกเล่นเคอร์เนล (核技巧, kernel trick) เป็นวิธีการที่ใช้อย่างกว้างขวางในเทคนิคการเรียนรู้ของเครื่อง 
 สำหรับบทความนี้จะมาอธิบายหลักการและการใช้วิธีการเคอร์เนลโดยละเอียด    
หลักการ  การวิเคราะห์ปัญหาที่ไม่เป็นเชิงเส้นนั้นมีความซับซ้อน โดยทั่วไปหากสามารถเปลี่ยนปัญหาที่พิจารณาให้เป็นเชิงเส้นได้จะสามารถคิดอะไรๆได้ง่ายขึ้น 
 เช่นเมื่อพิจารณาความสัมพันธ์ระหว่างตัวแปรต้นและตัวแปรตามซึ่งมีความสัมพันธ์ที่ดูแล้วเป็นฟังก์ชันซับซ้อน แต่ถ้าเราใช้วิธีอะไรสักอย่างแปลงข้อมูลเหล่านี้ให้ไปอยู่ในปริภูมิอื่น มันอาจกลายเป็นเชิงเส้นขึ้นมาได้ มิติของปริภูมินั้นอาจสูงกว่า แต่ถ้าเป็นเชิงเส้นก็จะทำให้พิจารณาง่ายขึ้น 
 ภาพตัวอย่างคร่าวๆ ด้านบนคือปริภูมิเดิมของข้อมูลที่เราพิจารณาอยู่ตอนแรก ส่วนภาพล่างคือจุดข้อมูลเหล่านั้นภายในปริภูมิใหม่ สีเดียวกันกับด้านบนแสดงถึงจุดเดียวกันถูกแปลงมาเป็นจุดนั้นในด้านล่าง  
 
  ในการใช้วิธีการเคอร์เนล ปริภูมิใหม่ที่ว่านี้เรียกว่าเป็นปริภูมิของเคอร์เนล ค่าในปริภูมิเคอร์เนลเป็นฟังก์ชันของจุดต่างๆในปริภูมิเดิม 
 ปริภูมิของเคอร์เนลที่ว่านี้ ถ้าจะพูดโดยละเอียดจริงๆก็คือเป็นปริภูมิชนิดที่เรียกว่า 
ปริภูมิฮิลแบร์ทเคอร์เนลเกิดซ้ำ (再生核希尔伯特空间, reproducing kernel Hilbert space)  อย่างไรก็ตามเรื่องนี้เป็นคณิตศาสตร์ระดับสูงเข้าใจยากพอสมควร เราไม่จำเป็นต้องลงลึกในรายละเอียดขนาดนั้นก็ได้ ให้เข้าใจว่าเป็นปริภูมิอะไรบางอย่างที่ต่างจากปริภูมิที่เราพิจารณาอยู่เดิม และการคำนวณในปริภูมินั้นทำให้การวิเคราะห์ข้อมูลสะดวกได้ 
 วิธีการเคอร์เนลมีพื้นฐานมาจากการใช้
ฟังก์ชันฐาน (基函数, basis function) ซึ่งเขียนถึงไปใน 
https://phyblas.hinaboshi.com/20180720  เนื้อหาในนี้จะต่อเนื่องจากพื้นฐานในบทความนั้น สมการต่างๆก็ยกมาจากในนั้นโดยอาจจะไม่ได้อธิบายซ้ำโดยละเอียดนัก ดังนั้นควรอ่านบทความนั้นมาก่อน 
 ปริภูมิเคอร์เนลได้จากการแปลงต่อมาจากปริภูมิของฟังก์ชันฐานลักษณะเฉพาะ ϕ อีกที 
 โดยนิยามฟังก์ชันใหม่ K เรียกว่าเป็นฟังก์ชันเคอร์เนล หรืออาจเรียกว่าเคอร์เนลเฉยๆ
..(1) 
 K เกิดจากผลคูณเวกเตอร์ ϕ ในปริภูมิของ ϕ หรือโดยทั่วไปมักเรียกว่าเป็น
ผลคูณภายใน (内积, inner product) ของปริภูมิ ϕ บางครั้งก็นิยมเขียนแทนด้วยกรอบลักษณะแบบนี้
..(2) 
 โดยทั่วไปถ้าปริภูมิ ϕ มีจำนวนมิติจำกัด เช่นมีจำนวนมิติ m มิติ ผลคูณภายในจะได้ว่า
..(3) 
 แต่ในวิธีการเคอร์เนลเรามักจะให้ ϕ มีจำนวนมิติเป็นอนันต์ ซึ่งผลคูณภายในจะเป็นการหาปริพันธ์แบบนี้
..(4) 
 มีมิติเป็นจำนวนอนันต์นี่เป็นยังไง คิดว่าอาจจะเป็นเรื่องที่จินตนาการได้ยาก ขอยกตัวอย่างฟังก์ชัน เช่นกรณีที่ฐาน ϕ เป็นฟังก์ชันเกาส์
..(5) 
 ในที่นี้ ξ จะหมายถึงจุดที่ใช้เป็นศูนย์กลาง ซึ่งตำแหน่งนี้อาจเป็นจุดใดๆบนปริภูมิของตัวแปรต้น x ดังนั้นจึงมีจำนวนเป็นอนันต์ จึงเรียกได้ว่าปริภูมิของ ϕ มีจำนวนมิติเป็นอนันต์ 
 แบบนี้ผลคูณภายในจะกลายเป็น
..(6) 
 จะเห็นว่าเมื่อใช้ฟังก์ชันฐานเป็นฟังก์ชันเกาส์ ก็จะได้เคอร์เนลออกมาเป็นฟังก์ชันเกาส์ในลักษณะเดิม 
 เขียนเคอร์เนลเกาส์ใหม่ในรูปทั่วไปได้เป็น
..(7) 
 โดย
..(8) 
 โดย σ แสดงถึงขนาดความกว้าง ปกติฟังก์ชันโดยพื้นฐานแล้วจะถูกเขียนในรูป σ เพราะเห็นภาพชัดกว่า แต่เพื่อให้เขียนง่ายในที่นี้ใช้รูป γ 
 โดยทั่วไปเคอร์เนลเกาส์แบบนี้มักถูกเรียกว่า 
เคอร์เนลฟังก์ชันฐานแนวรัศมี (径向基函数核, Radial basis function kernel) และนิยมเรียกย่อๆว่า RBF 
 เพียงแต่ว่ากันในรายละเอียดจริงๆแล้ว RBF หมายถึงฟังก์ชันอะไรก็ได้ที่ใช้ฐานเป็นฟังก์ชันที่มีจุดศูนย์กลางและมีค่าเปลี่ยนแปลงตามระยะห่างจากใจกลาง ดังนั้นอาจไม่ใช่ฟังก์ชันเกาส์ เพียงแต่ปกติฟังก์ชันเกาส์ใช้กันทั่วไปมากที่สุด ดังนั้นพอพูดถึง RBF ก็จะหมายถึงฟังก์ชันเกาส์ 
 นอกจากเคอร์เนลเกาส์แล้ว ยังมีเคอร์เนลชนิดอื่นที่อาจใช้ได้ เช่น เคอร์เนลลาปลาส เคอร์เนลพหุนาม เป็นต้น แต่ในที่นี้จะอธิบายโดยเน้นเคอร์เนลเกาส์เป็นหลัก 
 ในปัญหาการวิเคราะห์การถดถอยจะคำนวณเคอร์เนล K ระหว่างค่าใหม่ x ที่ต้องการหาค่ากับ x เก่าที่เป็นข้อมูลที่มี แล้วนำมาคำนวณ
..(9) 
 ในที่นี้เขียนห้อย B หมายถึงข้อมูลกลุ่มใหม่ที่ต้องการหาค่า ห้อย A หมายถึงกลุ่มเก่าซึ่งรู้ค่า z อยู่แล้วและใช้ในการเรียนรู้ 
 ที่ใช้สัญลักษณ์เป็นเวกเตอร์ในที่นี้หมายถึงว่ามีข้อมูล x หลายตัว 
 อนึ่ง ในบทความนี้จะใช้อักษรตัวหนาแทนปริมาณที่เป็นเมทริกซ์สองมิติ ส่วนที่มีลูกศรด้านบนแทนปริมาณเวกเตอร์ ซึ่งถือเป็นเมทริกซ์หนึ่งมิติด้วย ส่วนอักษรตัวเอียงธรรมดาคือค่าเลขตัวเดียว 
 ถ้าแจกเป็นเมทริกซ์ให้เห็นภาพชัดจะได้แบบนี้
..(10) 
 โดย n เป็นจำนวนข้อมูลเก่า n' เป็นจำนวนข้อมูลกลุ่มใหม่ 
 ค่าของแต่ละตัวจะได้เป็น
..(11) 
 โดย a คือค่าน้ำหนักของเคอร์เนลแต่ละตัว อาจมองว่าคล้ายกับค่าน้ำหนัก w ตอนใช้ฟังก์ชันฐานลักษณะเฉพาะก็ได้ แค่เปลี่ยนจากน้ำหนักของฟังก์ชันฐานมาเป็นน้ำหนักของเคอร์เนล 
 ค่า a คำนวณจากเคอร์เนลภายในตัวข้อมูลเก่าบวกกับเรกูลาไรซ์
..(12) 
 โดย λ คือขนาดของการเรกูลาไรซ์ 
 ส่วนที่มาของค่า a ถ้าจะเข้าใจอาจต้องอธิบายย้อนกลับไปนิด 
 เดิมทีในการใช้ฟังก์ชันฐานลักษณะเฉพาะในการคำนวณนั้น เราจะอธิบายค่าของฟังก์ชัน z ที่เราต้องการหาในรูปของฟังก์ชันฐาน ϕ(x) คูณกับน้ำหนัก w
..(13) 
 เมื่อมีจุดข้อมูลอยู่หลายค่า ค่า x และ z ก็มีอยู่หลายตัว อาจแสดงได้ในรูปเมทริกซ์ดังนี้
..(14) 
 โดยในที่นี้ m เป็นจำนวนฐาน และ n เป็นจำนวนจุดข้อมูลที่ใช้ 
 ค่าน้ำหนัก w นี้เป็นค่าที่ต้องคำนวณหาเอาจากข้อมูลค่า x และ z ที่ป้อนเข้าไปเป็นตัวอย่างข้อมูลสำหรับการเรียนรู้ 
 จากเมทริกซ์ข้างต้นนี้จะได้ระบบสมการทั้งหมด n สมการ และมีค่าที่ต้องการหาคือ w ทั้งหมด m ตัว 
 โดยทั่วไปแล้วเวลาใช้ฟังก์ชันฐานในการคำนวณโดยทั่วไปจำนวนฐาน (m) จะน้อยกว่าจำนวนจุดข้อมูล (n) แสดงว่าจำนวนเงื่อนไขมีมากกว่าตัวแปรที่ต้องการหา กรณีแบบนี้ปกติจะไม่สามารถหา w ที่สอดคล้องกับจุดข้อมูลทั้งหมดได้ 
 ดังนั้นเวลาแก้หาค่า w จะเป็นการหาโดยพิจารณาจากการทำให้ค่าความคลาดเคลื่อนต่ำสุด นั่นคือ
..(15) 
 แล้วก็จะได้ว่า
..(16) 
 แต่ในทางกลับกัน คราวนี้เราต้องการพิจารณากรณีที่ใช้ฐานจำนวนมากมาย มากกว่าจำนวนจุดข้อมูลที่มี (มากจนเป็นอนันต์) แบบนี้จะกลายเป็นว่าจำนวนสมการเงื่อนไขมีน้อยกว่าตัวแปรที่ต้องการหา กรณีนี้จะกลายเป็นว่าหาค่า w ได้ไม่จำกัด 
 ดังนั้นตรงนี้จะกลายมาเป็นปัญหาที่ว่าให้หาค่า w ที่มีขนาดต่ำสุด หรือก็คือผลรวมกำลังสองต่ำที่สุด
..(17) 
 การแก้หาค่าอาจทำโดยใช้ตัวคูณลากรองจ์ (拉格朗日乘数, Lagrange multipliers) สำหรับวิธีการจะขอละไว้ แต่ผลที่ได้จะออกมาเป็น
..(18) 
 จะเห็นว่าสมการ (16) กับ (18) อาจดูคล้ายกันแต่ก็ต่างกัน ที่สำคัญคือ ΦΦ
T จะได้เมทริกซ์ขนาด n×n ในขณะที่ Φ
TΦ จะเป็นเมทริกซ์ขนาด m×m 
 ถ้านำ w ที่ได้จากตรงนี้ไปใช้เพื่อคำนวณหาค่า z ใหม่สำหรับค่า x ใหม่โดยใช้สมการ (14) จะได้ว่า
..(19) 
 และจะเห็นว่าฟังก์ชันฐานที่ปรากฏในนี้อยู่ในรูปของเวกเตอร์คูณกัน ซึ่งก็คือผลคูณภายใน ผลคูณภายในของฟังก์ชันฐานก็คือเคอร์เนล ดังนิยามในสมการ (1) ดังนั้นจะได้
..(20) 
 ซึ่งก็กลับไปสู่สมการ (11) แล้วก็จะได้ค่า a ตามสมการ (12) 
 จะเห็นว่า ϕ ทั้งหมดถูกเขียนในรูปของ K ได้หมด นั่นหมายความว่าเราไม่จำเป็นต้องคำนวณฟังก์ชัน ϕ โดยตรงแต่คำนวณ K ซึ่งเป็นผลคูณภายในของ ϕ 
 แนวคิดตรงนี้มีประโยชน์มาก เพราะเมื่อมีจำนวนฟังก์ชันฐานเยอะมาก แบบนั้นถ้าเราต้องมาคำนวณ ϕ ออกมาจำนวนขนาดนั้น ปริมาณการคำนวณจะหนักมาก 
 แต่พอกลายเป็นว่าแค่คำนวณเคอร์เนล K ก็พอแล้ว ไม่ต้องคำนวณ ϕ โดยตรงแล้ว ต่อให้จำนวนฐานมีมากแค่ไหน จำนวนฐานอาจมีมากถึงเป็นอนันต์ ก็ไม่ทำให้ต้องคำนวณหนัก 
 แค่คำนวณพารามิเตอร์เป็นจำนวนแค่เท่ากับจุดข้อมูลก็เสมือนกับว่าพิจารณาฐานที่มีจำนวนมิติเป็นอนันต์ได้แล้ว แบบนี้ดูแล้วเป็นกลยุทธ์ลูกเช่นที่ทำให้การคำนวณง่ายสบายมาก ก็เลยมักถูกเรียกว่าเป็นลูกเล่นเคอร์เนล 
 ลองเขียนสรุปลำดับการคำนวณแล้วก็จะออกมาแบบนี้
.*. 
 แต่ขั้นตอน Φ นั้นถูกละไปไม่ต้องคำนวณโดยตรง จาก x คำนวณ K เลย นี่คือวิธีการของลูกเล่นเคอร์เนล    
โปรแกรม  เมื่อเข้าใจหลักการแล้วต่อมาลองเขียนโปรแกรมขึ้นมาดู 
 การคำนวณก็เป็นไปตามขั้นตอนนี้
 1. คำนวณเคอร์เนล (K) ภายในตัวแปรต้นของข้อมูลที่จะเรียนรู้ (x)
 2. ใช้ K และตัวแปรตามของข้อมูลที่จะเรียนรู้ (z) คำนวณค่าน้ำหนัก (a)
 3. คำนวณเคอร์เนล (K_) ของข้อมูลที่ต้องการหาค่า (x_) และข้อมูลที่เรียนรู้ (x)
 3. ใช้ K_ และ a คำนวณตัวแปรตามที่ต้องการหาค่า (z_) ของข้อมูลที่ต้องการหาค่า 
 ลองเขียนโค้ดได้แบบนี้
import numpy as np
import matplotlib.pyplot as plt
def gauss(x1,x2):
    return np.exp(-gamma*(x1-x2)**2)
np.random.seed(3)
n = 30
x = np.random.uniform(0,1,n) # จุดข้อมูลที่ใช้เรียนรู้
z = np.sin(x*9)*np.exp(-2*x)+np.random.normal(0,0.04,n)
gamma = 10 # γ
l = 0.02 # λ
K = gauss(x[:,None],x)
a = np.linalg.solve(K+np.eye(n)*l,z)
x_ = np.linspace(x.min(),x.max(),401) # จุดที่ต้องการหาค่า
K_ = gauss(x_[:,None],x)
z_ = K_.dot(a)
plt.scatter(x,z,c='y',edgecolor='g')
plt.plot(x_,z_,'m')
plt.show()
  
  ต่อมาลองเขียนให้อยู่ในรูปของคลาส
class ThotthoiKernel:
    def __init__(self,gamma=1,l=0.1):
        self.gamma = gamma
        self.l = l
    def rianru(self,x,z):
        self.x = x
        self.K = self.kernel(x[:,None],x)
        self.a = np.linalg.solve(self.K+np.eye(len(z))*self.l,z)
    def thamnai(self,x_):
        K_ = self.kernel(x_[:,None],self.x)
        return K_.dot(self.a)
    def kernel(self,x1,x2):
        return np.exp(-self.gamma*((x1-x2)**2))
 ขอทดสอบการใช้ด้วยการลองเปรียบเทียบผลของพารามิเตอร์ γ และ λ ที่มีผลต่อค่าที่ได้
n = 60
x = np.random.uniform(0,10,n)
z = np.sin(x*2)*np.exp(-0.2*x)*np.random.normal(1,0.1,n)
x_ = np.linspace(x.min(),x.max(),501)
plt.figure(figsize=[6.5,7])
for i,l in enumerate([0.01,0.1,1]):
    for j,g in enumerate([0.1,1,10,100]):
        tt = ThotthoiKernel(gamma=g,l=l)
        tt.rianru(x,z)
        z_ = tt.thamnai(x_)
        plt.subplot2grid((4,3),(j,i),xticks=[],yticks=[])
        plt.plot(x_,z_,'m')
        plt.scatter(x,z,s=10,c='y',edgecolor='g')
        plt.title('$\\lambda$=%.1f,$\\gamma$=%.1f'%(l,g),size=8)
plt.tight_layout()
plt.show()
 
  จะเห็นว่าผลที่ได้ต่างกันมาก ดังนั้นการเลือกค่าพารามิเตอร์ให้เหมาะสมจึงมีความสำคัญมาก    
กรณีหลายตัวแปร  ในตัวอย่างที่อธิบายผ่านมาเป็นปัญหาที่มีตัวแปรต้นเพียงตัวเดียว (มิติเดียว) คือ x 
 แต่วิธีการเคอร์เนลสามารถประยุกต์ใช้กับข้อมูลที่มีตัวแปรต้นกี่ตัวก็ได้ ดังนั้นคราวนี้จะลองเปลี่ยนมาพิจารณากรณีหลายตัวแปร 
 ให้ข้อมูลตัวแปรต้นแทนด้วย
..(21) 
 ในที่นี้ตัวเลขห้อยมี ๒ ตัว ตัวแรกแทนว่าเป็นจุดที่เท่าไหร่ ตัวหลังแทนว่าเป็นมิติที่เท่าไหร่ แต่ถ้าห้อยตัวเดียวจะแค่แทนว่าเป็นจุดเท่าไหร่โดยเป็นเวกเตอร์แสดงค่าในทุกมิติของจุดนั้น 
 การคำนวณ Φ ก็เปลี่ยนเป็นใช้ค่าในแต่ละมิติมาร่วมคำนวณ แล้วเคอร์เนลก็คำนวณจาก Φ เหมือนเดิม
..(22)
..(23) 
 การคำนวณเคอร์เนลเกาส์จะกลายเป็นแบบนี้
..(24) 
 ที่เพิ่มเข้ามาคือการคำนวณระยะห่างจะต้องคิดหลายมิติแล้วมาบวกกัน 
 เขียนโค้ดใหม่ได้ดังนี้
from scipy.spatial.distance import cdist
class ThotthoiKernel:
    def __init__(self,gamma=1,l=0.1):
        self.gamma = gamma
        self.l = l
    def rianru(self,X,z):
        self.X = X
        self.K = self.kernel(X,X)
        self.a = np.linalg.solve(self.K+np.eye(len(z))*self.l,z)
    def thamnai(self,X_):
        K_ = self.kernel(X_,self.X)
        return K_.dot(self.a)
    def kernel(self,X1,X2):
        return np.exp(-self.gamma*(cdist(X1,X2,'sqeuclidean')))
 หลักๆคือเปลี่ยนจากเดิมที่เป็นมิติเดียวมาเป็นหลายมิติ 
 นอกจากนี้ในที่นี้เปลี่ยนมาใช้ cdist ในการคำนวณระยะห่างตอนที่คำนวณเคอร์เนล ฟังก์ชันนี้นอกจากจะเขียนง่ายแล้วก็ยังเร็วด้วย 
 รายละเอียดเกี่ยวกับวิธีการใช้ฟังก์ชันนี้เขียนไว้ใน 
https://phyblas.hinaboshi.com/20180722  ลองนำมาใช้กับข้อมูลสองมิติดู
from mpl_toolkits.mplot3d import Axes3D
n = 200
X = np.random.random([n,2])
x,y = X.T
z = np.sin(x*9)*np.exp(-2*x)+np.random.normal(0,0.1,n)+5*(y-0.5)**2
gamma = 30
l = 0.01
tt = ThotthoiKernel(gamma=gamma,l=l)
tt.rianru(X,z)
mx,my = np.meshgrid(np.linspace(x.min(),x.max(),201),np.linspace(y.min(),y.max(),201))
mX = np.array([mx.ravel(),my.ravel()]).T
mz = tt.thamnai(mX).reshape(201,-1)
ax = plt.figure(figsize=[7,6]).add_axes([0,0,1,1],projection='3d')
c = np.abs(z-tt.thamnai(X))
sc = ax.scatter(x,y,z,c=c,edgecolor='k',cmap='plasma')
ax.plot_surface(mx,my,mz,alpha=0.2,color='g',edgecolor='k')
plt.colorbar(sc,pad=0.01,aspect=50)
plt.show()
 
  พื้นผิวคือค่า z ที่คำนวณได้ที่ x,y ค่าต่างๆ ส่วนจุดคือข้อมูลจริง 
 สีของจุดแสดงถึงความแตกต่างระหว่างค่า z จริงกับค่า z บนระนาบ   
 ทั้งหมดนี้เป็นหลักการพื้นฐานของลูกเล่นเคอร์เนล และการใช้ในการวิเคราะห์การถดถอย 
 แต่จริงๆแล้วแทนที่จะใช้ในการวิเคราะห์การถดถอย คนจำนวนมากทางสายการเรียนรู้ของเครื่องจะรู้จักลูกเล่นเคอร์เนลในฐานะเทคนิคที่ประยุกต์ใช้กับเทคนิคอื่นๆเช่น 
เครื่องเวกเตอร์ค้ำยัน (支持向量机, support vector machine, SVM) หรือ 
การวิเคราะห์องค์ประกอบหลัก (主成分分析, principle component Analysis, PCA) มากกว่า 
 สำหรับ SVM ได้เขียนถึงไปแล้วใน 
https://phyblas.hinaboshi.com/20180709  ส่วน PCA เขียนไว้ใน 
https://phyblas.hinaboshi.com/20180727    อ้างอิง