KNN及其改進算法的python實現


一、 馬氏距離

我們熟悉的歐氏距離雖然很有用,但也有明顯的缺點。它將樣品的不同屬性(即各指標或各變量)之間的差別等同看待,這一點有時不能滿足實際要求。例如,在教育研究中,經常遇到對人的分析和判別,個體的不同屬性對於區分個體有着不同的重要性。因此,有時需要采用不同的距離函數。
  如果用dij表示第i個樣品和第j個樣品之間的距離,那么對一切ijkdij應該滿足如下四個條件:
   
當且僅當i=j時,dij=0
  dij0
  dijdji(對稱性)
  dijdikdkj(三角不等式)
顯然,歐氏距離滿足以上四個條件。滿足以上條件的函數有多種,本節將要用到的馬氏距離也是其中的一種。

  第i個樣品與第j個樣品的馬氏距離dij用下式計算:
    dij =(x i x j)'S-1(x ixj)
  其中,x i x j分別為第i個和第j個樣品的m個指標所組成的向量,S為樣本協方差矩陣。

  馬氏距離有很多優點。它不受量綱的影響,兩點之間的馬氏距離與原始數據的測量單位無關;由標准化數據和中心化數據(
即原始數據與均值之差)計算出的二點之間的馬氏距離相同。馬氏距離還可以排除變量之間的相關性的干擾。它的缺點是誇大了變化微小的變量的作用。舉例說明:

兩個樣本:
His1 = {3,4,5,6}
His2 = {2,2,8,4}

它們的均值為:

U = {2.5, 3, 6.5, 5}

協方差矩陣為:

S =
| 0.25  0.50  -0.75  0.50  |
| 0.50  1.00  -1.50  1.00  | 
|-0.75  -1.50    2.25  -1.50  |
| 0.50  1.00  -1.50  1.00  |

其中
S(i,j)={[His1(i)-u(i)]*[His1(j)-u(j)]+[His2(i)-u(i)]*[His2(j)-u(j)]}/2
下一步就是求出逆矩陣
S^(-1)
馬氏距離 D=sqrt{[His1-His2] * S^(-1) * [(His1-His2)的轉置列向量
]}
1
)馬氏距離的計算是建立在總體樣本的基礎上的,這一點可以從上述協方差矩陣的解釋中可以得出,也就是說,如果拿同樣的兩個樣本,放入兩個不同的總體中,最后計算得出的兩個樣本間的馬氏距離通常是不相同的,除非這兩個總體的協方差矩陣碰巧相同;

2
)在計算馬氏距離過程中,要求總體樣本數大於樣本的維數,否則得到的總體樣本協方差矩陣逆矩陣不存在,這種情況下,用歐式距離來代替馬氏距離,也可以理解為,如果樣本數小於樣本的維數,這種情況下求其中兩個樣本的距離,采用歐式距離計算即可。

3
)還有一種情況,滿足了條件總體樣本數大於樣本的維數,但是協方差矩陣的逆矩陣仍然不存在,比如A34),B56);C78),這種情況是因為這三個樣本在其所處的二維空間平面內共線(如果是大於二維的話,比較復雜)。這種情況下,也采用歐式距離計算。

4
)在實際應用中總體樣本數大於樣本的維數這個條件是很容易滿足的,而所有樣本點出現3)中所描述的情況是很少出現的,所以在絕大多數情況下,馬氏距離是可以順利計算的,但是馬氏距離的計算是不穩定的,不穩定的來源是協方差矩陣,這也是馬氏距離與歐式距離的最大差異之處。

綜上,我們用python
編寫了馬氏距離,如下:

distances=[]
for i in range
(dataSetSize):
    x = numpy.array(dataSet)
    xt=x.T
    D=numpy.cov(xt)
    invD=numpy.linalg.inv(D)
    tp=inX-dataSet[i]
    distances.append(numpy.sqrt(dot(dot(tp
,invD),tp.T)))

最后得到的distances就是測試樣本和每個訓練樣本的馬氏距離。

二、 wk_NNC算法

wk-NNC算法是對經典knn算法的改進,這種方法是對k個近鄰的樣本按照他們距離待分類樣本的遠近給一個權值w

clip_image002

clip_image004是第i個近鄰的權值,其中1<i<k,clip_image006是待測樣本距離第i個近鄰的距離。

python實現這個算法比較簡單:

def wk_knn(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[
0
]
    diffMat = tile(inX
, (dataSetSize,1)) - dataSet
  

    sqDiffMat = diffMat**2
   
sqDistances = sqDiffMat.sum(axis=1) 

   
distances = sqDistances**0.5
   
sortedDistIndicies = distances.argsort()        
    classCount={}   
    w=[]     
   
for i in range
(k):
        w.append((distances[sortedDistIndicies[k-
1
]]-distances[sortedDistIndicies[i]]\
        )/(distances[sortedDistIndicies[k-
1]]-distances[sortedDistIndicies[0
]]))
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel
,0
) + w[i]
    sortedClassCount =
sorted(classCount.items(), key=operator.itemgetter(1), reverse=True
)
   
return sortedClassCount[0][0]

 

三、 knnm算法

knnm算法運用了訓練樣本中的每一個模式,對訓練樣本的每個類clip_image008

1 i c,在每一個類中找出距離測試樣本距離最近的k個近鄰clip_image010,假設這k個近鄰的均值為clip_image012,同樣的,i1c變化,我們得到clip_image014,如果clip_image016M當中距離測試樣本最近的,則測試樣本屬於clip_image018類。

       如下圖所示,對於一個兩類的問題,每個類選三個近鄰,類clip_image020*表示,類clip_image022o表示,“Y”是測試樣本,則Y屬於clip_image022[1]類。

clip_image025

python實現如下:

def knnm(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[
0
]
    diffMat = tile(inX
, (dataSetSize, 1)) - dataSet  
#tile repeat inX to (dataSetSize,1)
   
sqDiffMat = diffMat **
2
   
sqDistances = sqDiffMat.sum(axis=1) 
#sum per row
   
distances = sqDistances **
0.5
   
sortedDistIndicies = distances.argsort()
    classCount={}
    classNum={}
    i=
0
   
while
i<dataSetSize:
        voteIlabel = labels[sortedDistIndicies[i]]
       
if sum(classNum)==10
*k:
           
break
        elif
classNum.get(voteIlabel,0
)==k:
            i +=
1
       
else
:
            classCount[voteIlabel] = classCount.get(voteIlabel
,0
) \
                                     + distances[sortedDistIndicies[i]]
            classNum[voteIlabel]=classNum.get(voteIlabel
,0)+
1
           
i +=
1
   
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1
))
   
return sortedClassCount[0][0]

四、 實驗過程

我在手寫字符和約會數集分別作了實驗,結果如下(k=7):

約會數集錯誤率

KNN

WK_KNN

KNNM

馬氏距離

6%

6.6%

6.2%

歐氏距離

5.8%

6.2%

6.2%

由於手寫字符訓練樣本協方差矩陣不可逆,因此只能求歐氏距離

手寫字符錯誤率

KNN

WK_KNN

KNNM

歐式距離

1.1628%(k=3最小)

0.9514%(k=5最小)

1.2685%(k=3最小)

五、實驗小結

歐式距離比馬氏距離計算量小得多,速度快,而且可以看出分類的效果甚至比馬氏距離要好,,可以看到,在約會數集中,knn的表現要優於其他兩種算法,歐式距離的knn錯誤率最低,而wk_knn在手寫字符識別中有較為出色的表現,相對於其他兩種算法,knnm並沒有想象中的效果


免責聲明!

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



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