原文鏈接:nicholasren.github.com/2013/02/17/knn.html
背景
KNN,全稱K-nearest-neighbour,是機器學習中最簡單的一個分類算法,它的原理是通過對樣本數據的學習,對於給定的新的數據,找出與其距離最近的K個樣本數據,根據這K個最近樣本數據的類別,來確定這個給定數據的類別。
Coolshell上有對這個算法的講解,我的同事邱俊濤也寫了一篇關於KNN算法python實現的文章。本文講解一個KNN算法的ruby實現。
輸入
程序輸入格式如下:
x0,x1,x2,…xn|v0
y0,y1,y2,…yn|v1
z0,z1,z2,…zn|v2
每行爲一個數據樣本,以第一行爲例,x0,x1...xn爲一個向量,v0爲該數據的類別。
學習
從給定文件加載樣本數據:
def train file_path
@samples = from_file(file_path)
end
@sample的格式如下:
[
{:vector => [x0, x1, x2, …xn], :value => v0},
{:vector => [y0, y1, y2, …yn], :value => v1},
…
{:vector => [z0, z1, z2, …zn], :value => vn},
]
分類
對於給定的數據,要判斷其屬於樣本數據中的哪一類,需解決如下幾個問題:
- 計算給定數據和樣本數據之間的距離
- 找出與給定數據距離最小的K個樣本數據
- 從這K個樣本數據中找出樣本多的那個分類,即爲給定數據的分類。
1. 計算距離
給定兩個向量[x0, x1,…xn]
,[y0, y1,...yn]
計算兩個向量之間的距離如下:
(x0 - y0)^2 + (x1 - y1)^2 + … + (xn - yn)^2
因此,對於給定的兩個向量a,b,其距離計算邏輯如下:
#a and b are two vectors
def distance_between a, b
a.zip(b).map {|x| x[0] - x[1]}.inject(0){|sum, x| sum += x*x}
end
2. 找出與給定數據距離最小的K個樣本數據
可以採用計算給定數據與所有樣本數據的距離,然後採用最大堆來找出top k個樣本數據。
def nearest_neighbours candidate, k
heap = MaxHeap.new
@samples.each do |sample|
distance = distance_between(sample[:vector], candidate)
heap.insert Node.new(distance, sample)
end
heap.take_top(k).compact.map(&:sample)
end
3. 從這K個樣本數據中找出樣本多的那個分類,即爲給定數據的分類。
對得到的樣本根據其類別進行分組,組內元素多的那個類別,即爲該給定數據的分類
def value_with_max_vote xs
value_with_votes = xs.group_by{|x| x[:value]}.map{|value, group| {:value => value, :votes => group.length}}
value_with_votes.max_by{|x| x[:votes] }[:value]
end
綜合上面的幾個小任務,我們得到KNN分類算法的實現:
def categorize candidate, k
neighbours = nearest_neighbours_for candidate, k
value_with_max_vote neighbours
end
代碼的完整版本可以在這裏找到。