python KNN演算法實現鳶尾花資料集分類
一、knn演算法描述
1.基本概述
knn演算法,又叫k-近鄰演算法。屬於一個分類演算法,主要思想如下:
一個樣本在特徵空間中的k個最近鄰的樣本中的大多數都屬於某一個類別,則該樣本也屬於這個類別。其中k表示最近鄰居的個數。
用二維的圖例,說明knn演算法,如下:
二維空間下資料之間的距離計算:
在n維空間兩個資料之間:
2.具體步驟:
(1)計算待測試資料與各訓練資料的距離
(2)將計算的距離進行由小到大排序
(3)找出距離最小的k個值
(4)計算找出的值中每個類別的頻次
(5)返回頻次最高的類別
二、鳶尾花資料集
Iris 鳶尾花資料集內包含 3 類分別為山鳶尾(Iris-setosa)、變色鳶尾(Iris-versicolor)和維吉尼亞鳶尾(Iris-virginica),共 150 條記錄,每類各 50 個數據,每條記錄都有 4 項特徵:花萼長度、花萼寬度、花瓣長度、花瓣寬度,可以通過這4個特徵預測鳶尾花卉屬於哪一品種。
iris資料集包含在sklearn庫當中,具體在sklearn\datasets\data資料夾下,檔名為iris.csv。以本機為例。其路徑如下:
D:\python\lib\site-packages\sklearn\datasets\data\iris.csv
其中資料如下格式:
第一行資料意義如下:
150:資料集中資料的總條數
4:特徵值的類別數,即花萼長度、花萼寬度、花瓣長度、花瓣寬度。
setosa、versicolor、virginica:三種鳶尾花名
從第二行開始:
第一列為花萼長度值
第二列為花萼寬度值
第三列為花瓣長度值
第四列為花瓣寬度值
第五列對應是種類(三類鳶尾花分別用0,1,2表示)
三、演算法實現
1.演算法流程圖:
從以上流程圖可以看出,knn演算法包含後四步操作,所以將整個程式分為三個模組。
2.具體實現
(1)方法一
①利用slearn庫中的load_iris()匯入iris資料集
②使用train_test_split()對資料集進行劃分
③KNeighborsClassifier()設定鄰居數
④利用fit()構建基於訓練集的模型
⑤使用predict()進行預測
⑥使用score()進行模型評估
說明:本程式碼來源於《Python機器學習基礎教程》在此僅供學習使用。
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier import numpy as np # 載入資料集 iris_dataset = load_iris() # 資料劃分 X_train,X_test,y_train,y_test = train_test_split(iris_dataset['data'],iris_dataset['target'],random_state=0) # 設定鄰居數 knn = KNeighborsClassifier(n_neighbors=1) # 構建基於訓練集的模型 knn.fit(X_train,y_train) # 一條測試資料 X_new = np.array([[5,2.9,1,0.2]]) # 對X_new預測結果 prediction = knn.predict(X_new) print("預測值%d" % prediction) # 得出測試集X_test測試集的分數 print("score:{:.2f}".format(knn.score(X_test,y_test)))
(2)方法二
①使用讀取檔案的方式,使用open、以及csv中的相關方法載入資料
②輸入測試集和訓練集的比率,對載入的資料使用shuffle()打亂後,計算訓練集及測試集個數對特徵值資料和對應的標籤資料進行分割。
③將分割後的資料,計算測試集資料與每一個訓練集的距離,使用norm()函式直接求二範數,或者載入資料使用np.sqrt(sum((test - train) ** 2))求得距離,使用argsort()將距離進行排序,並返回索引值,
④取出值最小的k個,獲得其標籤值,存進一個字典,標籤值為鍵,出現次數為值,對字典進行按值的大小遞減排序,將字典第一個鍵的值存入預測結果的列表中,計算完所有測試集資料後,返回一個列表。
⑤將預測結果與測試集本身的標籤進行對比,得出分數。
import csv import random import numpy as np import operator def openfile(filename): """ 開啟資料集,進行資料處理 :param filename: 資料集的路徑 :return: 返回資料集的資料,標籤,以及標籤名 """ with open(filename) as csv_file: data_file = csv.reader(csv_file) temp = next(data_file) # 資料集中資料的總數量 n_samples = int(temp[0]) # 資料集中特徵值的種類個數 n_features = int(temp[1]) # 標籤名 target_names = np.array(temp[2:]) # empty()函式構造一個未初始化的矩陣,行數為資料集數量,列數為特徵值的種類個數 data = np.empty((n_samples,n_features)) # empty()函式構造一個未初始化的矩陣,行數為資料集數量,1列,資料格式為int target = np.empty((n_samples,),dtype=np.int) for i,j in enumerate(data_file): # 將資料集中的將資料轉化為矩陣,資料格式為float # 將資料中從第一列到倒數第二列中的資料儲存在data中 data[i] = np.asarray(j[:-1],dtype=np.float64) # 將資料集中的將資料轉化為矩陣,資料格式為int # 將資料集中倒數第一列中的資料儲存在target中 target[i] = np.asarray(j[-1],dtype=np.int) # 返回 資料,標籤 和標籤名 return data,target,target_names def random_number(data_size): """ 該函式使用shuffle()打亂一個包含從0到資料集大小的整數列表。因此每次執行程式劃分不同,導致結果不同 改進: 可使用random設定隨機種子,隨機一個包含從0到資料集大小的整數列表,保證每次的劃分結果相同。 :param data_size: 資料集大小 :return: 返回一個列表 """ number_set = [] for i in range(data_size): number_set.append(i) random.shuffle(number_set) return number_set def split_data_set(data_set,target_data,rate=0.25): """ 說明:分割資料集,預設資料集的25%是測試集 :param data_set: 資料集 :param target_data: 標籤資料 :param rate: 測試集所佔的比率 :return: 返回訓練集資料、訓練集標籤、訓練集資料、訓練集標籤 """ # 計算訓練集的資料個數 train_size = int((1-rate) * len(data_set)) # 獲得資料 data_index = random_number(len(data_set)) # 分割資料集(X表示資料,y表示標籤),以返回的index為下標 x_train = data_set[data_index[:train_size]] x_test = data_set[data_index[train_size:]] y_train = target_data[data_index[:train_size]] y_test = target_data[data_index[train_size:]] return x_train,x_test,y_test def data_diatance(x_test,x_train): """ :param x_test: 測試集 :param x_train: 訓練集 :return: 返回計算的距離 """ # sqrt_x = np.linalg.norm(test-train) # 使用norm求二範數(距離) distances = np.sqrt(sum((x_test - x_train) ** 2)) return distances def knn(x_test,x_train,k): """ :param x_test: 測試集資料 :param x_train: 訓練集資料 :param y_train: 測試集標籤 :param k: 鄰居數 :return: 返回一個列表包含預測結果 """ # 預測結果列表,用於儲存測試集預測出來的結果 predict_result_set=[] # 訓練集的長度 train_set_size = len(x_train) # 建立一個全零的矩陣,長度為訓練集的長度 distances = np.array(np.zeros(train_set_size)) # 計算每一個測試集與每一個訓練集的距離 for i in x_test: for indx in range(train_set_size): # 計算資料之間的距離 distances[indx] = data_diatance(i,x_train[indx]) # 排序後的距離的下標 sorted_dist = np.argsort(distances) class_count = {} # 取出k個最短距離 for i in range(k): # 獲得下標所對應的標籤值 sort_label = y_train[sorted_dist[i]] # 將標籤存入字典之中並存入個數 class_count[sort_label]=class_count.get(sort_label,0) + 1 # 對標籤進行排序 sorted_class_count = sorted(class_count.items(),key=operator.itemgetter(1),reverse=True) # 將出現頻次最高的放入預測結果列表 predict_result_set.append(sorted_class_count[0][0]) # 返回預測結果列表 return predict_result_set def score(predict_result_set,y_test): """ :param predict_result_set: 預測結果列表 :param y_test: 測試集標籤 :return: 返回測試集精度 """ count = 0 for i in range(0,len(predict_result_set)): if predict_result_set[i] == y_test[i]: count += 1 score = count / len(predict_result_set) return score if __name__ == "__main__": iris_dataset = openfile('iris.csv') # x_new = np.array([[5,0.2]]) x_train,y_test = split_data_set(iris_dataset[0],iris_dataset[1]) result = knn(x_test,6) print("原有標籤:",y_test) # 為了方便對比檢視,此處將預測結果轉化為array,可直接列印結果 print("預測結果:",np.array(result)) score = score(result,y_test) print("測試集的精度:%.2f" % score)
四、執行結果
結果不同,因為每次劃分的訓練集和測試集不同,具體見random_number()方法。
五、總結
在本次使用python實現knn演算法時,遇到了很多困難,如資料集的載入,資料的格式不能滿足後續需要,因此閱讀了sklearn庫中的一部分程式碼,有選擇性的進行了複用。資料與標籤無法分離,或是資料與標籤排序後後無法對應的情況,查詢許多資料後使用argsort()完美解決該問題。出現了n多錯誤,通過多次除錯之後最終完成。
附:本次實驗參考 :
①*鄭捷《機器學習演算法原理與程式設計實踐》
②《Python機器學習基礎教程》
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。