1. 程式人生 > >K--最鄰近(K-NN)演算法

K--最鄰近(K-NN)演算法

程式碼整理:

# -*- coding: utf-8 -*

import numpy as np
import matplotlib.pyplot as plt

from collections import Counter

def dist(A,B):
    a = np.asarray(A)
    b = np.asarray(B)
    a = a.ravel()
    b = b.ravel()
    d = a-b
    return np.linalg.norm(d)

def cosSim(A,B):
    a = np.asarray(A)
    b = np.asarray(B)
    a = a.ravel()
    b = b.ravel()
    d1 = np.dot(a,b)
    d2 = np.linalg.norm(a)*np.linalg.norm(b)
    return d1/d2
    
    
#K_NN函式定義:
#data表示測試資料
#predict表示待測試樣本
#k表示k_nn中的k值
#distfun表示選擇的距離
def k_nn_test(data,predict,k=3,distfun=dist):
    if len(data) >= k:
        print('K is set to a value less than total voting groups!')
    
    distances = []
    for group in data:
        for features in data[group]:
            #計算每個樣本與測試樣本之間的距離,採用distfun距離演算法
            test_distance = distfun(features,predict)
            #將距離以及類別組成列表存入distances中
            distances.append([test_distance,group])
#     print(distances)
            
    #i[0]為距離,i[1]為類別,我們需要的是類別
    #取按照distances列表進行排序後的0到k-1個值
    votes = [i[1] for i in sorted(distances)[:k] ]
    #使用collections.Counter類來統計跟蹤的值出現的次數
    #most_common():取元素次數最多的前1個也就是那個多數派
    vote_result = Counter(votes).most_common(1)[0][0]
    return vote_result

if __name__ == '__main__':
    dataset = {'k':[[1,3],[2,4],[2,1]],'r':[[6,3],[7,7],[5,6]]}
    new_features = [4,4]
    #dataset(資料集)只是一個python字典,其中的鍵看作類,後面的值看作這個類相關的資料點
    #new_features是將要預測其所屬類的點
    #我們可以做一個快速圖表
            
    for i in dataset:
        for i2 in dataset[i]:
            plt.scatter(i2[0],i2[1],s=100,color=i)
            
    plt.scatter(new_features[0],new_features[1],s=100)
    plt.show()
    
    result = k_nn_test(dataset,new_features)
    plt.scatter(new_features[0],new_features[1],s=100,color=result)
    plt.show()
def loadImage(filename,fsize=40):
    from PIL import Image
    
    image = Image.open(filename)
    img1 = image.resize((fsize,fsize))
    image.close()
    img = np.asarray(img1)
    img1.close()
    x = img.ravel()  #將陣列降為一維
    return x          #x是一張圖片生成的一個向量


def loadDataset():   
    import os
    Ylab = [chr(i+ord('A')) for i in range(26)]  # Y是A到Z,26個字母構成的列表
    fsize = 40
    X = []
    Y = []
    for ypath in Ylab:
        #下面,最後需要[0]的作用是去掉了一層列表符號
        pngfiles = [ dirs[2]  for dirs in os.walk('/Users/he-jia/English_hand_writing/Img/daxie/'+ypath)][0]    # pngfiles是資料夾A下面所有訓練圖片的檔名構成的列表
        for file in pngfiles:    #針對每一個A的訓練圖片
            if not (file.endswith('.png') or file.endswith('.PNG') ):  #如果不是png檔案可以跳過
                continue
            x = loadImage('/Users/he-jia/English_hand_writing/Img/daxie/'+ypath+'/'+file,fsize)    # loadImage函式用於將每一個測試圖片生成一個行向量
            X.append(x)   # X是一個列表
            Y.append(ypath)
    return np.mat(X),np.asarray(Y) 
#這兩個函式都是將列表矩陣化,
#當A資料夾裡的訓練圖片被遍歷後,X1=np.mat(X)最終是個55行4800列的矩陣,X1 是個二維矩陣,Y1 =np.asarry(Y)是由一個列向量構成的矩陣,26行1列
# 55*26 =1430,當A到Z資料夾裡的訓練圖片都被遍歷之後,X1=np.mat(X)最終是個1430行4800列的矩陣,Y1 =np.asarry(Y)是由一個列向量構成的矩陣,1430行1列


import sklearn.neighbors as knnlib
import datetime
begin = datetime.datetime.now()
print(begin)
print('------------------------')

#訓練分類器
testx =  loadImage('/Users/he-jia/English_hand_writing/test.png')
charX,charY = loadDataset()   #charX,charY 是兩個矩陣,一個1430行4800列,一個26行1列
k = int(np.sqrt(len(charY)))  #k為樣本數量開方
knn = knnlib.KNeighborsClassifier(algorithm = 'ball_tree',n_neighbors=k,weights='distance',p=1) 
#建立knn分類器
#四個引數含義:量度距離,以曼哈頓距離演算法,k個近鄰,約等按球樹
print(charX.shape)
print(charY.shape)
knn = knn.fit(charX,charY) #訓練knn模型
testx = np.mat(testx)
y = knn.predict(testx)    #測試預測樣本,先要轉為矩陣
print('測試圖片結果為:',y)


print('------------------------')
end = datetime.datetime.now()
print(end)