1. 程式人生 > >用R語言DIY機器學習演算法--KNN

用R語言DIY機器學習演算法--KNN

1. KNN演算法

KNN演算法又稱為k最鄰近演算法(k-Nearest Neighbour),是一種出現較早且原理比較簡單的機器學習演算法。其基本思想就是根據距離待分類資料A最近的k個樣本資料的分類來預測A可能屬於的類別。基本的計算步驟如下:

  • 計算待分類資料與樣本集中每一個樣本之間的距離(歐式距離、馬氏距離等);
  • 找出距離最近的k個樣本;
  • 觀測這k個樣本的分類情況;
  • 將出現次數最多的類別作為待分類資料的類別。

KNN演算法的優點是原理簡單,易於實現,在多分類問題中能取得較好的結果;缺點是計算量較大,需要計算待測資料與每一個樣本資料的距離,而且容易受到樣本分佈的影響,當樣本不平衡時很容易產生錯誤分類。

2. R中的KNN

class包中提供了KNN的相關演算法,包括knn,knn1(k取1時的knn),還有改進的演算法knn.cv,kknn等函式,呼叫起來非常方便。本文主要介紹一下如何手工實現最基本的KNN演算法。自己實現演算法的過程能夠加深對演算法的理解,同時也能鍛鍊一下編寫程式碼的能力,是快速提升個人能力的途徑之一。下面是詳細程式碼:

library(dplyr)
knnDIY <- function(train,test,labels,k){
  if(ncol(train)!=ncol(test)) stop("dims of 'test' and 'train' differ")
  if(nrow(train)!=length(labels)) stop("'train' and 'class' have different lengths")
  labels <- as.character(labels)              
  classify0 <- function(vec){                 
    diffMat <- matrix(vec,nrow(train),ncol(train),byrow = T) - train
    distances <- diffMat^2  %>% apply(.,1,sum) %>% sqrt
    sortedDistIndexes <- order(distances)
    kMaxDistLabels <- vector(length = k)
    for(i in 1:k){
      kMaxDistLabels[i] <- labels[sortedDistIndexes[i]]
    }
    predictLabel <- table(kMaxDistLabels) %>% which.max %>% names
    return(predictLabel)
  }
  allPredict <- apply(test,1,classify0)
  return(allPredict)
}
  • 自定義了一個knnDIY函式,需要傳入四個引數,train是訓練集,即帶分類標籤的資料;test為待測試的資料;labels為train的分類標籤,k為鄰居個數。在函式中又定義了一個classify0的內部函式,該函式的作用是對每一條待分類資料做分類,最後通過apply函式對整個test資料集做分類。這樣做的好處是能夠簡化邏輯思維,更專注於演算法過程的實現,同時apply函式的效率也比for迴圈要高一些。因此主要的演算法過程定義在了classify0中。
  • 首先將傳入到classify0中的單條資料vec擴充成與train訓練集維度相同的矩陣,用byrow引數控制擴充的方向,再與train做減法,得到diffMat。diffMat中的每一行都相當於vec與train訓練集中每一條資料做減法的結果(
    )。
    這樣再對diffMat的每一個元素平方(),按行相加(),再做開方就求出了vec與每一個樣本點之間的歐式距離()。
  • 然後對distances從小到大做排序並返回排序後元素在distances中的序號向量sortedDistIndexes(詳見order函式),取sortedDistIndexes的前k個值去labels裡查詢相應的標籤,就得到了k個最近鄰居的分類標籤。通過table函式對k個鄰居的標籤做頻數統計,求出頻數最大的資料,利用names得到分類標籤。
  • 將最終得到的標籤作為vec資料的分類。利用apply函式對整個test執行classify0函式即可得到所有test的分類標籤。

3. 效果測試

利用iris資料集測試knnDIY與class包中的knn函式的效果差異
train1 <- iris[,c(1:4)]  ##訓練樣本集
test1 <- iris[,c(1:4)]   ##測試集
labels1 <- iris[,5]      ##樣本集標籤
mean(knnDIY(train1,test1,labels1,3) == knn(train1,test1,labels1,3))
[1] 1
經測試分類結果和knn函式一致。 這裡只是實現了最簡單的knn演算法,程式碼邏輯參考了《機器學習實戰》第十九頁中的python程式碼。由於python與R在許多方面的相似性,且python社群更活躍、程式碼可讀性更高,依照python程式碼來編寫R相應的程式碼也是一種很便捷的途徑。