機器學習實例---1.1、k-近鄰算法(簡單k-nn)


機器學習實例---1.1、k-近鄰算法(簡單k-nn)

一、總結

一句話總結:

【取最鄰近的分類標簽】:算法提取樣本最相似數據(最近鄰)的分類標簽
【k的出處】:一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法中k的出處
【k-近鄰算法實例】:比如,現在我這個k值取3,那么在電影例子中,按距離依次排序的三個點分別是動作片(108,5)、動作片(115,8)、愛情片(5,89)。【在這三個點中,動作片出現的頻率為三分之二,愛情片出現的頻率為三分之一】,所以該紅色圓點標記的電影為動作片。這個判別過程就是k-近鄰算法。

 

1、k-近鄰算法 距離度量?

用歐氏距離就好:$$| A B | = \sqrt { ( x _ { 1 } - x _ { 2 } ) ^ { 2 } + ( y _ { 1 } - y _ { 2 } ) ^ { 2 } }$$
例如:(101,20)->動作片(108,5)的距離約為16.55

 

2、簡單的k-近鄰算法步驟?

1、【計算距離】:計算已知類別數據集中的點與當前點之間的距離;
2、【距離排序】:按照距離遞增次序排序;
3、【選k個點】:選取與當前點【距離最小】的k個點;
4、【確定k個點的類別】:確定前k個點所在類別的出現頻率;返回前k個點所出現頻率最高的類別作為當前點的預測分類。

 

3、k-鄰近算法不具有顯式的學習過程?

【沒進行數據訓練】:k-近鄰算法沒有進行數據的訓練,【直接使用未知的數據與已知的數據進行比較,得到結果】。因此,可以說k-鄰近算法不具有顯式的學習過程。

 

4、完整的k-近鄰算法流程?

1、【收集與准備數據】:可以使用爬蟲進行數據的收集,也可以使用第三方提供的免費或收費的數據。一般來講,數據放在txt文本文件中,按照一定的格式進行存儲,便於解析及處理。准備數據:使用Python解析、預處理數據。
2、【分析數據】:可以使用很多方法對數據進行分析,例如使用Matplotlib將數據【可視化】。
3、【測試與使用算法】:計算【錯誤率】。【使用算法】:錯誤率在可接受范圍內,就可以運行k-近鄰算法進行分類。

 

5、寫k近鄰算法代碼?

直接【分步驟】把代碼寫了就可以了,非常非常簡單
k-近鄰算法步驟

1、【計算距離】:計算已知類別數據集中的點與當前點之間的距離;
2、【距離排序】:按照距離遞增次序排序;
3、【選k個點】:選取與當前點【距離最小】的k個點;
4、【確定k個點的類別】:確定前k個點所在類別的出現頻率;返回前k個點所出現頻率最高的類別作為當前點的預測分類。

 

 

二、1.1、k-近鄰算法(簡單k-nn)

轉自:Python3《機器學習實戰》學習筆記(一):k-近鄰算法(史詩級干貨長文)
https://blog.csdn.net/c406495762/article/details/75172850

 

#一 簡單k-近鄰算法

本文將從k-鄰近算法的思想開始講起,使用python3一步一步編寫代碼進行實戰訓練。並且,我也提供了相應的數據集,對代碼進行了詳細的注釋。除此之外,本文也對sklearn實現k-鄰近算法的方法進行了講解。實戰實例:電影類別分類、約會網站配對效果判定、手寫數字識別。

本文出現的所有代碼和數據集,均可在我的github上下載,歡迎Follow、Star:https://github.com/Jack-Cherish/Machine-Learning/tree/master/kNN

 

1.1 k-近鄰法簡介

k近鄰法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一種基本分類與回歸方法。它的工作原理是:存在一個樣本數據集合,也稱作為訓練樣本集,並且樣本集中每個數據都存在標簽,即我們知道樣本集中每一個數據與所屬分類的對應關系。輸入沒有標簽的新數據后,將新的數據的每個特征與樣本集中數據對應的特征進行比較,然后算法提取樣本最相似數據(最近鄰)的分類標簽。一般來說,我們只選擇樣本數據集中前k個最相似的數據,這就是k-近鄰算法中k的出處,通常k是不大於20的整數。最后,選擇k個最相似數據中出現次數最多的分類,作為新數據的分類。

舉個簡單的例子,我們可以使用k-近鄰算法分類一個電影是愛情片還是動作片。

電影名稱 打斗鏡頭 接吻鏡頭 電影類型
電影1 1 101 愛情片
電影2 5 89 愛情片
電影3 108 5 動作片
電影4 115 8 動作片

 

表1.1 每部電影的打斗鏡頭數、接吻鏡頭數以及電影類型

 

表1.1就是我們已有的數據集合,也就是訓練樣本集。這個數據集有兩個特征,即打斗鏡頭數和接吻鏡頭數。除此之外,我們也知道每個電影的所屬類型,即分類標簽。用肉眼粗略地觀察,接吻鏡頭多的,是愛情片。打斗鏡頭多的,是動作片。以我們多年的看片經驗,這個分類還算合理。如果現在給我一部電影,你告訴我這個電影打斗鏡頭數和接吻鏡頭數。不告訴我這個電影類型,我可以根據你給我的信息進行判斷,這個電影是屬於愛情片還是動作片。而k-近鄰算法也可以像我們人一樣做到這一點,不同的地方在於,我們的經驗更"牛逼",而k-鄰近算法是靠已有的數據。比如,你告訴我這個電影打斗鏡頭數為2,接吻鏡頭數為102,我的經驗會告訴你這個是愛情片,k-近鄰算法也會告訴你這個是愛情片。你又告訴我另一個電影打斗鏡頭數為49,接吻鏡頭數為51,我"邪惡"的經驗可能會告訴你,這有可能是個"愛情動作片",畫面太美,我不敢想象。 (如果說,你不知道"愛情動作片"是什么?請評論留言與我聯系,我需要你這樣像我一樣純潔的朋友。) 但是k-近鄰算法不會告訴你這些,因為在它的眼里,電影類型只有愛情片和動作片,它會提取樣本集中特征最相似數據(最鄰近)的分類標簽,得到的結果可能是愛情片,也可能是動作片,但絕不會是"愛情動作片"。當然,這些取決於數據集的大小以及最近鄰的判斷標准等因素。

1.2 距離度量

我們已經知道k-近鄰算法根據特征比較,然后提取樣本集中特征最相似數據(最鄰近)的分類標簽。那么,如何進行比較呢?比如,我們還是以表1.1為例,怎么判斷紅色圓點標記的電影所屬的類別呢?如圖1.1所示。

 

 

圖1.1 電影分類

 

我們可以從散點圖大致推斷,這個紅色圓點標記的電影可能屬於動作片,因為距離已知的那兩個動作片的圓點更近。k-近鄰算法用什么方法進行判斷呢?沒錯,就是距離度量。這個電影分類的例子有2個特征,也就是在2維實數向量空間,可以使用我們高中學過的兩點距離公式計算距離,如圖1.2所示。

 

 

圖1.2 兩點距離公式

 

通過計算,我們可以得到如下結果:

  • (101,20)->動作片(108,5)的距離約為16.55
  • (101,20)->動作片(115,8)的距離約為18.44
  • (101,20)->愛情片(5,89)的距離約為118.22
  • (101,20)->愛情片(1,101)的距離約為128.69

通過計算可知,紅色圓點標記的電影到動作片 (108,5)的距離最近,為16.55。如果算法直接根據這個結果,判斷該紅色圓點標記的電影為動作片,這個算法就是最近鄰算法,而非k-近鄰算法。那么k-鄰近算法是什么呢?k-近鄰算法步驟如下:

  1. 計算已知類別數據集中的點與當前點之間的距離;
  2. 按照距離遞增次序排序;
  3. 選取與當前點距離最小的k個點;
  4. 確定前k個點所在類別的出現頻率;
  5. 返回前k個點所出現頻率最高的類別作為當前點的預測分類。

比如,現在我這個k值取3,那么在電影例子中,按距離依次排序的三個點分別是動作片(108,5)、動作片(115,8)、愛情片(5,89)。在這三個點中,動作片出現的頻率為三分之二,愛情片出現的頻率為三分之一,所以該紅色圓點標記的電影為動作片。這個判別過程就是k-近鄰算法。

##1.3 Python3代碼實現

我們已經知道了k-近鄰算法的原理,那么接下來就是使用Python3實現該算法,依然以電影分類為例。

1.3.1 准備數據集

對於表1.1中的數據,我們可以使用numpy直接創建,代碼如下:

# -*- coding: UTF-8 -*- import numpy as np """ 函數說明:創建數據集 Parameters: 無 Returns: group - 數據集 labels - 分類標簽 Modify: 2017-07-13 """ def createDataSet(): #四組二維特征 group = np.array([[1,101],[5,89],[108,5],[115,8]]) #四組特征的標簽 labels = ['愛情片','愛情片','動作片','動作片'] return group, labels if __name__ == '__main__': #創建數據集 group, labels = createDataSet() #打印數據集 print(group) print(labels) 

運行結果,如圖1.3所示:

 

 

圖1.3 運行結果

 

###1.3.2 k-近鄰算法

根據兩點距離公式,計算距離,選擇距離最小的前k個點,並返回分類結果。

# -*- coding: UTF-8 -*- import numpy as np import operator """ 函數說明:kNN算法,分類器 Parameters: inX - 用於分類的數據(測試集) dataSet - 用於訓練的數據(訓練集) labes - 分類標簽 k - kNN算法參數,選擇距離最小的k個點 Returns: sortedClassCount[0][0] - 分類結果 Modify: 2017-07-13 """ def classify0(inX, dataSet, labels, k):  #numpy函數shape[0]返回dataSet的行數 dataSetSize = dataSet.shape[0]  #在列向量方向上重復inX共1次(橫向),行向量方向上重復inX共dataSetSize次(縱向) diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet  #二維特征相減后平方 sqDiffMat = diffMat**2  #sum()所有元素相加,sum(0)列相加,sum(1)行相加 sqDistances = sqDiffMat.sum(axis=1)  #開方,計算出距離 distances = sqDistances**0.5  #返回distances中元素從小到大排序后的索引值 sortedDistIndices = distances.argsort()  #定一個記錄類別次數的字典 classCount = {} for i in range(k):  #取出前k個元素的類別 voteIlabel = labels[sortedDistIndices[i]]  #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回默認值。  #計算類別次數 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1  #python3中用items()替換python2中的iteritems()  #key=operator.itemgetter(1)根據字典的值進行排序  #key=operator.itemgetter(0)根據字典的鍵進行排序  #reverse降序排序字典 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)  #返回次數最多的類別,即所要分類的類別 return sortedClassCount[0][0] 

###1.3.3 整體代碼

這里預測紅色圓點標記的電影(101,20)的類別,K-NN的k值為3。創建kNN_test01.py文件,編寫代碼如下:

# -*- coding: UTF-8 -*- import numpy as np import operator """ 函數說明:創建數據集 Parameters: 無 Returns: group - 數據集 labels - 分類標簽 Modify: 2017-07-13 """ def createDataSet(): #四組二維特征 group = np.array([[1,101],[5,89],[108,5],[115,8]]) #四組特征的標簽 labels = ['愛情片','愛情片','動作片','動作片'] return group, labels """ 函數說明:kNN算法,分類器 Parameters: inX - 用於分類的數據(測試集) dataSet - 用於訓練的數據(訓練集) labes - 分類標簽 k - kNN算法參數,選擇距離最小的k個點 Returns: sortedClassCount[0][0] - 分類結果 Modify: 2017-07-13 """ def classify0(inX, dataSet, labels, k): #numpy函數shape[0]返回dataSet的行數 dataSetSize = dataSet.shape[0] #在列向量方向上重復inX共1次(橫向),行向量方向上重復inX共dataSetSize次(縱向) diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet #二維特征相減后平方 sqDiffMat = diffMat**2 #sum()所有元素相加,sum(0)列相加,sum(1)行相加 sqDistances = sqDiffMat.sum(axis=1) #開方,計算出距離 distances = sqDistances**0.5 #返回distances中元素從小到大排序后的索引值 sortedDistIndices = distances.argsort() #定一個記錄類別次數的字典 classCount = {} for i in range(k): #取出前k個元素的類別 voteIlabel = labels[sortedDistIndices[i]] #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回默認值。 #計算類別次數 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #python3中用items()替換python2中的iteritems() #key=operator.itemgetter(1)根據字典的值進行排序 #key=operator.itemgetter(0)根據字典的鍵進行排序 #reverse降序排序字典 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) #返回次數最多的類別,即所要分類的類別 return sortedClassCount[0][0] if __name__ == '__main__': #創建數據集 group, labels = createDataSet() #測試集 test = [101,20] #kNN分類 test_class = classify0(test, group, labels, 3) #打印分類結果 print(test_class) 

運行結果,如圖1.4所示:

 

 

圖1.4 運行結果

 

可以看到,分類結果根據我們的"經驗",是正確的,盡管這種分類比較耗時,用時1.4s。

到這里,也許有人早已經發現,電影例子中的特征是2維的,這樣的距離度量可以用兩 點距離公式計算,但是如果是更高維的呢?對,沒錯。我們可以用歐氏距離(也稱歐幾里德度量),如圖1.5所示。我們高中所學的兩點距離公式就是歐氏距離在二維空間上的公式,也就是歐氏距離的n的值為2的情況。

 

 

圖1.5 歐氏距離公式

 

看到這里,有人可能會問:“分類器何種情況下會出錯?”或者“答案是否總是正確的?”答案是否定的,分類器並不會得到百分百正確的結果,我們可以使用多種方法檢測分類器的正確率。此外分類器的性能也會受到多種因素的影響,如分類器設置和數據集等。不同的算法在不同數據集上的表現可能完全不同。為了測試分類器的效果,我們可以使用已知答案的數據,當然答案不能告訴分類器,檢驗分類器給出的結果是否符合預期結果。通過大量的測試數據,我們可以得到分類器的錯誤率-分類器給出錯誤結果的次數除以測試執行的總數。錯誤率是常用的評估方法,主要用於評估分類器在某個數據集上的執行效果。完美分類器的錯誤率為0,最差分類器的錯誤率是1.0。同時,我們也不難發現,k-近鄰算法沒有進行數據的訓練,直接使用未知的數據與已知的數據進行比較,得到結果。因此,可以說k-鄰近算法不具有顯式的學習過程。

 

代碼

k-近鄰算法步驟

1、【計算距離】:計算已知類別數據集中的點與當前點之間的距離;
2、【距離排序】:按照距離遞增次序排序;
3、【選k個點】:選取與當前點【距離最小】的k個點;
4、【確定k個點的類別】:確定前k個點所在類別的出現頻率;返回前k個點所出現頻率最高的類別作為當前點的預測分類。
In [ ]:
代碼可以不用這么寫這么寫復雜了點可以直接將代碼寫的非常非常好理解。比如循環計算點距離 
In [2]:
import numpy as np 
In [6]:
### 數據集相關

#四組二維特征 dataSet = np.array([[1,101],[5,89],[108,5],[115,8]]) #四組特征的標簽 labels = ['愛情片','愛情片','動作片','動作片'] 
In [9]:
#測試集
testPoint = [101,20] 

1、【計算距離】:計算已知類別數據集中的點與當前點之間的距離;

計算測試集中的每個點和和數據集中的點的距離

In [10]:
# 獲取數據集中元素個數
dataSetSize = dataSet.shape[0] # 將測試點重復多份方便做減法,來計算距離 diffMat = np.tile(testPoint, (dataSetSize, 1)) - dataSet # 距離公式中的平方 sqDiffMat = diffMat**2 # 距離公式中的求和 sqDistances = sqDiffMat.sum(axis=1) # 距離公式中的開方計算距離 distances = sqDistances**0.5 print(distances) 
[128.68954892 118.22436297  16.55294536  18.43908891]

2、【距離排序】:按照距離遞增次序排序;

In [11]:
#返回distances中元素從小到大排序后的索引值
sortedDistIndices = distances.argsort() print(sortedDistIndices) 
[2 3 1 0]

3、【選k個點】:選取與當前點【距離最小】的k個點;

In [14]:
# 選三個點
k=3 
In [15]:
#定一個記錄類別次數的字典
classCount = {} for i in range(k): #取出前k個元素的類別 voteIlabel = labels[sortedDistIndices[i]] print(voteIlabel) #dict.get(key,default=None),字典的get()方法,返回指定鍵的值,如果值不在字典中返回默認值。 #計算類別次數 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 print(classCount) print(classCount) 
動作片
{'動作片': 1}
動作片
{'動作片': 2}
愛情片
{'動作片': 2, '愛情片': 1}
{'動作片': 2, '愛情片': 1}

4、【確定k個點的類別】:確定前k個點所在類別的出現頻率;返回前k個點所出現頻率最高的類別作為當前點的預測分類。

In [17]:
import operator 
In [18]:
#python3中用items()替換python2中的iteritems()
#key=operator.itemgetter(1)根據字典的值進行排序 #key=operator.itemgetter(0)根據字典的鍵進行排序 #reverse降序排序字典 sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True) print(sortedClassCount) 
[('動作片', 2), ('愛情片', 1)]
In [19]:
#返回次數最多的類別,即所要分類的類別
print(sortedClassCount[0][0]) 
動作片
In [ ]:
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM