《機器學習實戰》學習筆記——kNN演算法
《機器學習實戰》(MLiA)是一本介紹機器學習的書(的確是廢話),和其他書不同的地方在於它給出了python的實現程式碼,而其他的書籍重在解釋理論。我作為一名渣渣,理論就先放一放了。
MLiA的第一章主要介紹了一些概念、常識性的東西,所以不做介紹,這篇文章主要介紹k-近鄰演算法(kNN)。
kNN演算法的優點是精度高,對異常值(離群點)不敏感且不需要訓練;缺點是計算複雜度、空間複雜度高。
kNN演算法的適用範圍是數值型和標稱型資料。
kNN工作原理是:
存在一個樣本資料集合,也稱作訓練樣本集,並且樣本集中每個資料都存在標籤,即我們知道樣本集中每一資料與所屬分類的對應關係。輸人沒有標籤的新資料後,將新資料的每個特徵與樣本集中資料對應的特徵進行比較,然後演算法提取樣本集中特徵最相似資料(最近鄰)的分類標籤。一般來說,我們只選擇樣本資料集中前k個最相似的資料,這就是k-近鄰演算法中k的出處,通常k是不大於20的整數。最後,選擇k個最相似資料中出現次數最多的分類,作為新資料的分類。
kNN演算法的演算法流程為:
(1).計算已知類別資料集中的點與當前點之間的距離;
(2).按照距離遞增次序排序;
(3).選取與當前點距離最小的k個點;
(4).確定前k個點所在類別的出現頻率;
(5).返回前k個點出現頻率最高的類別作為當前點的預測分類。
既然演算法流程已經明確,現在開始構造自己的演算法。《MLiA》中採用的是python來構造的演算法,原因是機器學習的演算法大都需要處理複雜的矩陣運算。鑑於自己的演算法處理的資料特徵維數並不多,矩陣運算較少,故寫了一個簡單的c++版本
/* kNN.cpp
* @author: Toroto
*/
#include <iostream>
#include <math.h>
using namespace std;
#define DSSIZE 8 //訓練集大小
/*
* 資料結構:
* 包含兩個特徵:x, y
* 及標記:label
*/
struct data{
float x, y;
char label;
};
data D[DSSIZE]; //訓練集
float dis[DSSIZE]; //距離陣列
//設定訓練集:
//{(0,0),(0,0.1),(0,0.2)(0,0.3),(1,1.4),(1,1.5),(1,1.6),(1,1.7)}
void init(int x){
for (int i = 0; i < x; i++){
if (i < 4){
D[i].x = 0;
D[i].label = 'A';
}
else {
D[i].x = 1;
D[i].label = 'B';
}
D[i].y = D[i].x + 0.1 * i;
}
};
/*
* kNN演算法實現:
* 引數:
* x, y: 目標向量
k:最近鄰居的數目
* 返回值:待分類向量的標籤
*/
char kNN(float x, float y, int k){
float temp;
data tem;
for (int i = 0; i < DSSIZE; i++){
//計算目標向量到各訓練資料的距離
dis[i] = pow(pow(x - D[i].x, 2) + pow(y - D[i].y, 2), 0.5);
// cout << "i = " << i << "; distance = " << dis[i] << endl;
}
//排序
for (int i = 0; i < DSSIZE; i++){
for (int j = i; j < DSSIZE; j++){
if (dis[i] > dis[j]){
temp = dis[i];
dis[i] = dis[j];
dis[j] = temp;
tem = D[i];
D[i] = D[j];
D[j] = tem;
}
}
}
/*
for (int i = 0; i < DSSIZE; i++){
cout << D[i].x << " " << D[i].y << " " << D[i].label << " " << dis[i] << endl;
}
//*/
int label_A = 0, label_B = 0;
//在給定數目的最近鄰居中選擇出現次數最多的鄰居
for (int i = 0; i < k; i++){
if (D[i].label == 'A') label_A++;
else label_B++;
}
return label_A > label_B ? 'A' : 'B';
}
int main(){
init(DSSIZE);
int k;
float x, y;
char label;
cout << "輸入x, y: ";
cin >> x >> y;
cout << "輸入k :";
cin >> k;
label = kNN(x * 1.0, y * 1.0, k);
cout << label << endl;
return 0;
};
這樣就完成了一個簡單版的kNN。
測試:
輸入0.1 ,0.2, k = 5,結果如下:
輸入0.8, 1.2, k = 5, 結果如下: