python KNN算法實現(xiàn)鳶尾花數(shù)據(jù)集分類
一、knn算法描述
1.基本概述
knn算法,又叫k-近鄰算法。屬于一個分類算法,主要思想如下:
一個樣本在特征空間中的k個最近鄰的樣本中的大多數(shù)都屬于某一個類別,則該樣本也屬于這個類別。其中k表示最近鄰居的個數(shù)。
用二維的圖例,說明knn算法,如下:
二維空間下數(shù)據(jù)之間的距離計算:
在n維空間兩個數(shù)據(jù)之間:
2.具體步驟:
(1)計算待測試數(shù)據(jù)與各訓練數(shù)據(jù)的距離
(2)將計算的距離進行由小到大排序
(3)找出距離最小的k個值
(4)計算找出的值中每個類別的頻次
(5)返回頻次最高的類別
二、鳶尾花數(shù)據(jù)集
Iris 鳶尾花數(shù)據(jù)集內(nèi)包含 3 類分別為山鳶尾(Iris-setosa)、變色鳶尾(Iris-versicolor)和維吉尼亞鳶尾(Iris-virginica),共 150 條記錄,每類各 50 個數(shù)據(jù),每條記錄都有 4 項特征:花萼長度、花萼寬度、花瓣長度、花瓣寬度,可以通過這4個特征預(yù)測鳶尾花卉屬于哪一品種。
iris數(shù)據(jù)集包含在sklearn庫當中,具體在sklearn\datasets\data文件夾下,文件名為iris.csv。以本機為例。其路徑如下:
D:\python\lib\site-packages\sklearn\datasets\data\iris.csv
其中數(shù)據(jù)如下格式:
第一行數(shù)據(jù)意義如下:
150:數(shù)據(jù)集中數(shù)據(jù)的總條數(shù)
4:特征值的類別數(shù),即花萼長度、花萼寬度、花瓣長度、花瓣寬度。
setosa、versicolor、virginica:三種鳶尾花名
從第二行開始:
第一列為花萼長度值
第二列為花萼寬度值
第三列為花瓣長度值
第四列為花瓣寬度值
第五列對應(yīng)是種類(三類鳶尾花分別用0,1,2表示)
三、算法實現(xiàn)
1.算法流程圖:
從以上流程圖可以看出,knn算法包含后四步操作,所以將整個程序分為三個模塊。
2.具體實現(xiàn)
(1)方法一
①利用slearn庫中的load_iris()導入iris數(shù)據(jù)集
②使用train_test_split()對數(shù)據(jù)集進行劃分
③KNeighborsClassifier()設(shè)置鄰居數(shù)
④利用fit()構(gòu)建基于訓練集的模型
⑤使用predict()進行預(yù)測
⑥使用score()進行模型評估
說明:本代碼來源于《Python機器學習基礎(chǔ)教程》在此僅供學習使用。
from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier import numpy as np # 載入數(shù)據(jù)集 iris_dataset = load_iris() # 數(shù)據(jù)劃分 X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], random_state=0) # 設(shè)置鄰居數(shù) knn = KNeighborsClassifier(n_neighbors=1) # 構(gòu)建基于訓練集的模型 knn.fit(X_train, y_train) # 一條測試數(shù)據(jù) X_new = np.array([[5, 2.9, 1, 0.2]]) # 對X_new預(yù)測結(jié)果 prediction = knn.predict(X_new) print("預(yù)測值%d" % prediction) # 得出測試集X_test測試集的分數(shù) print("score:{:.2f}".format(knn.score(X_test,y_test)))
(2)方法二
①使用讀取文件的方式,使用open、以及csv中的相關(guān)方法載入數(shù)據(jù)
②輸入測試集和訓練集的比率,對載入的數(shù)據(jù)使用shuffle()打亂后,計算訓練集及測試集個數(shù)對特征值數(shù)據(jù)和對應(yīng)的標簽數(shù)據(jù)進行分割。
③將分割后的數(shù)據(jù),計算測試集數(shù)據(jù)與每一個訓練集的距離,使用norm()函數(shù)直接求二范數(shù),或者載入數(shù)據(jù)使用np.sqrt(sum((test - train) ** 2))求得距離,使用argsort()將距離進行排序,并返回索引值,
④取出值最小的k個,獲得其標簽值,存進一個字典,標簽值為鍵,出現(xiàn)次數(shù)為值,對字典進行按值的大小遞減排序,將字典第一個鍵的值存入預(yù)測結(jié)果的列表中,計算完所有測試集數(shù)據(jù)后,返回一個列表。
⑤將預(yù)測結(jié)果與測試集本身的標簽進行對比,得出分數(shù)。
import csv import random import numpy as np import operator def openfile(filename): """ 打開數(shù)據(jù)集,進行數(shù)據(jù)處理 :param filename: 數(shù)據(jù)集的路徑 :return: 返回數(shù)據(jù)集的數(shù)據(jù),標簽,以及標簽名 """ with open(filename) as csv_file: data_file = csv.reader(csv_file) temp = next(data_file) # 數(shù)據(jù)集中數(shù)據(jù)的總數(shù)量 n_samples = int(temp[0]) # 數(shù)據(jù)集中特征值的種類個數(shù) n_features = int(temp[1]) # 標簽名 target_names = np.array(temp[2:]) # empty()函數(shù)構(gòu)造一個未初始化的矩陣,行數(shù)為數(shù)據(jù)集數(shù)量,列數(shù)為特征值的種類個數(shù) data = np.empty((n_samples, n_features)) # empty()函數(shù)構(gòu)造一個未初始化的矩陣,行數(shù)為數(shù)據(jù)集數(shù)量,1列,數(shù)據(jù)格式為int target = np.empty((n_samples,), dtype=np.int) for i, j in enumerate(data_file): # 將數(shù)據(jù)集中的將數(shù)據(jù)轉(zhuǎn)化為矩陣,數(shù)據(jù)格式為float # 將數(shù)據(jù)中從第一列到倒數(shù)第二列中的數(shù)據(jù)保存在data中 data[i] = np.asarray(j[:-1], dtype=np.float64) # 將數(shù)據(jù)集中的將數(shù)據(jù)轉(zhuǎn)化為矩陣,數(shù)據(jù)格式為int # 將數(shù)據(jù)集中倒數(shù)第一列中的數(shù)據(jù)保存在target中 target[i] = np.asarray(j[-1], dtype=np.int) # 返回 數(shù)據(jù),標簽 和標簽名 return data, target, target_names def random_number(data_size): """ 該函數(shù)使用shuffle()打亂一個包含從0到數(shù)據(jù)集大小的整數(shù)列表。因此每次運行程序劃分不同,導致結(jié)果不同 改進: 可使用random設(shè)置隨機種子,隨機一個包含從0到數(shù)據(jù)集大小的整數(shù)列表,保證每次的劃分結(jié)果相同。 :param data_size: 數(shù)據(jù)集大小 :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): """ 說明:分割數(shù)據(jù)集,默認數(shù)據(jù)集的25%是測試集 :param data_set: 數(shù)據(jù)集 :param target_data: 標簽數(shù)據(jù) :param rate: 測試集所占的比率 :return: 返回訓練集數(shù)據(jù)、訓練集標簽、訓練集數(shù)據(jù)、訓練集標簽 """ # 計算訓練集的數(shù)據(jù)個數(shù) train_size = int((1-rate) * len(data_set)) # 獲得數(shù)據(jù) data_index = random_number(len(data_set)) # 分割數(shù)據(jù)集(X表示數(shù)據(jù),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_train, y_test def data_diatance(x_test, x_train): """ :param x_test: 測試集 :param x_train: 訓練集 :return: 返回計算的距離 """ # sqrt_x = np.linalg.norm(test-train) # 使用norm求二范數(shù)(距離) distances = np.sqrt(sum((x_test - x_train) ** 2)) return distances def knn(x_test, x_train, y_train, k): """ :param x_test: 測試集數(shù)據(jù) :param x_train: 訓練集數(shù)據(jù) :param y_train: 測試集標簽 :param k: 鄰居數(shù) :return: 返回一個列表包含預(yù)測結(jié)果 """ # 預(yù)測結(jié)果列表,用于存儲測試集預(yù)測出來的結(jié)果 predict_result_set=[] # 訓練集的長度 train_set_size = len(x_train) # 創(chuàng)建一個全零的矩陣,長度為訓練集的長度 distances = np.array(np.zeros(train_set_size)) # 計算每一個測試集與每一個訓練集的距離 for i in x_test: for indx in range(train_set_size): # 計算數(shù)據(jù)之間的距離 distances[indx] = data_diatance(i, x_train[indx]) # 排序后的距離的下標 sorted_dist = np.argsort(distances) class_count = {} # 取出k個最短距離 for i in range(k): # 獲得下標所對應(yīng)的標簽值 sort_label = y_train[sorted_dist[i]] # 將標簽存入字典之中并存入個數(shù) class_count[sort_label]=class_count.get(sort_label, 0) + 1 # 對標簽進行排序 sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True) # 將出現(xiàn)頻次最高的放入預(yù)測結(jié)果列表 predict_result_set.append(sorted_class_count[0][0]) # 返回預(yù)測結(jié)果列表 return predict_result_set def score(predict_result_set, y_test): """ :param predict_result_set: 預(yù)測結(jié)果列表 :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, 2.9, 1, 0.2]]) x_train, x_test, y_train, y_test = split_data_set(iris_dataset[0], iris_dataset[1]) result = knn(x_test,x_train, y_train, 6) print("原有標簽:", y_test) # 為了方便對比查看,此處將預(yù)測結(jié)果轉(zhuǎn)化為array,可直接打印結(jié)果 print("預(yù)測結(jié)果:", np.array(result)) score = score(result, y_test) print("測試集的精度:%.2f" % score)
四、運行結(jié)果
結(jié)果不同,因為每次劃分的訓練集和測試集不同,具體見random_number()方法。
五、總結(jié)
在本次使用python實現(xiàn)knn算法時,遇到了很多困難,如數(shù)據(jù)集的加載,數(shù)據(jù)的格式不能滿足后續(xù)需要,因此閱讀了sklearn庫中的一部分代碼,有選擇性的進行了復(fù)用。數(shù)據(jù)與標簽無法分離,或是數(shù)據(jù)與標簽排序后后無法對應(yīng)的情況,查詢許多資料后使用argsort()完美解決該問題。出現(xiàn)了n多錯誤,通過多次調(diào)試之后最終完成。
附:本次實驗參考 :
①*鄭捷《機器學習算法原理與編程實踐》
②《Python機器學習基礎(chǔ)教程》
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python openpyxl方法 zip函數(shù)用法及說明
這篇文章主要介紹了python openpyxl方法 zip函數(shù)用法及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05Python英文詞頻統(tǒng)計(哈姆雷特)程序示例代碼
在文本處理方面,Python也有著得天獨厚的優(yōu)勢,不僅提供了多種字符串操作函數(shù),而且還可以使用各種開源庫來處理文本,下面這篇文章主要給大家介紹了關(guān)于Python英文詞頻統(tǒng)計(哈姆雷特)程序示例的相關(guān)資料,需要的朋友可以參考下2023-06-06pandas中DataFrame的merge操作的實現(xiàn)
本文主要介紹了pandas中DataFrame的merge操作的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-07-07python繪制y關(guān)于x的線性回歸線性方程圖像實例
這篇文章主要為大家介紹了python繪制y關(guān)于x的線性回歸線性方程圖像實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10