機器學習——K近鄰演算法
概述
k近鄰是一種基本分類與迴歸方法.
- 輸入:特徵向量
- 輸出:例項的類別(可取多類)
- 核心思想:如果一個樣本在特徵空間中的k個最相鄰的樣本中的大多數屬於某一個類別,則該樣本也屬於這個類別,並具有這個類別上樣本的特性.
- 優點:計算精度高、對異常值不敏感、無資料輸入假定
- 缺點:計算複雜度高、空間複雜度高
- 適用範圍:數值型和標稱型
演算法流程
- 收集資料
- 準備資料:距離計算所需要的數值,最好是結構化的資料格式
- 分析資料:可以適用任何方法
- 訓練演算法:此步驟不適用於KNN
- 測試演算法:計算錯誤率
- 使用演算法:首先需要輸入樣本資料和結構化的輸出結果,然後執行k-近鄰演算法判定輸入資料分別屬於哪個分類,最後應用對計算出的分類執行後續的處理
kNN演算法
構造資料集
import numpy as np
def create_data_set():
"""構造資料集"""
group = np.array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
label = [`A`, `A`, `B`, `B`]
return group, label
生成資料集
group, label = create_data_set()
實現kNN演算法虛擬碼
對未知類別屬性的資料集種的每個點依次執行以下步驟:
1. 計算已知類別屬性的資料集中的每個點與當前點之間的距離
2. 按照距離遞增次序排序
3. 選取與當前點距離最小的k個點
4. 確定前k個點所在類別的出現頻率
5. 返回前k個點出現頻率最高的類別作為當前點的預測分類
import operator
def classify0(inX, data_set, label, k):
"""
KNN演算法
:param inX: 用於分類的輸入向量
:param data_set: 訓練樣本集
:param label: 訓練標籤向量
:param k: 選取最近鄰居的數量
:return: k個鄰居里頻率最高的分類
"""
"""距離計算"""
# 獲得樣本量
data_set_size = data_set.shape[0]
# tile:在行方向重複inX,dataSetSize次,在列方向上重複inX,1次
diff_mat = np.tile(inX, (data_set_size, 1)) - data_set
# 離原點的距離,相見後平方
sq_diff_mat = diff_mat ** 2
# x軸和y軸差的平方和
sq_distances = sq_diff_mat.sum(axis=1)
# 然後開方
distances = sq_distances ** 0.5
# argsort函式返回的是陣列值從小到大的索引值
sorted_distance_index = distances.argsort()
class_count = {}
"""選擇距離最小的點"""
for i in range(k):
# 返回距離最近的第i個樣本所對應的標籤
vote_label = label[sorted_distance_index[i]]
# print(voteIlabel)
# print(classCount.get(voteIlabel, 0))
# 這裡的0是設定預設值為0,而代替None。而程式碼是給出現情況增加次數,出現一次+1
class_count[vote_label] = class_count.get(vote_label, 0) + 1
# print(classCount)
"""排序"""
# 匯入運算子模組的itemgetter方法,按照第二個元素的次序對元組進行排序,此處的排序為逆序。
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
# 返回頻率最大的Label
return sorted_class_count[0][0]
新增演算法測試程式碼
classify0([0, 0], group, label, k=3)
約會網站示例
載入並解析資料
# 將文字記錄轉換為numpy的解析程式
def file2matrix(filename):
fr = open(filename)
# readlines把檔案所有內容讀取出來,組成一個列表,其中一行為一個元素
array_of_lines = fr.readlines()
number_of_lines = len(array_of_lines)
# 返回一個用1000行每行3個0填充的陣列,形成特徵矩陣
return_mat = np.zeros((number_of_lines, 3))
class_label_vector = []
for index, line in enumerate(array_of_lines):
# 去除每行前後的空格
line = line.strip()
# 根據 把每行分隔成由四個元素組成的列表
list_from_line = line.split(` `)
# 選取前3個元素,將它們按順序儲存到特徵矩陣中
return_mat[index, :] = list_from_line[0: 3]
# 將列表的最後一個元素儲存到class_label_vector中去,儲存的元素值為整型
class_label_vector.append(int(list_from_line[-1]))
return return_mat, class_label_vector
獲得解析資料
dating_data_mat, dating_labels = file2matrix(`datingTestSet2.txt`)
使用Matplotlib建立散點圖
import matplotlib.pyplot as plt
# 建立Figure例項
fig = plt.figure()
# 新增一個子圖,返回Axes例項
ax = fig.add_subplot(111) # 選取最近鄰居的數量
# 生成散點圖,x軸使用dating_data_mat第二列資料,y軸使用dating_data_mat的第三列資料
# ax.scatter(x=dating_data_mat[:, 1], y=dating_data_mat[:, 2])
# 個性化標記散點圖,形狀(s)和顏色(c)
ax.scatter(x=dating_data_mat[:, 1], y=dating_data_mat[:, 2], s=15.0 * np.array(dating_labels), c=np.array(dating_labels))
plt.show()
歸一化特徵值
<html><center>newValue=(oldValue−min)/(max−min)</center></html>
def auto_num(data_set):
"""
歸一化特徵值
:param data_set: 資料集
:return 歸一化後的資料集, 列的差值範圍, 列的最小值
"""
# 列的最小值
min_val = data_set.min()
# 列的最大值
max_val = data_set.max()
# 列的差值範圍
range_s = max_val - min_val
# 構造返回矩陣
norm_data_set = np.zeros(shape=np.shape(data_set))
# m = data_set.shape[0]
# oldValue - min
norm_data_set = data_set - np.tile(min_val, (data_set.shape[0], 1))
# (oldValue - min) / (max - min)
norm_data_set = norm_data_set / np.tile(range_s, (data_set.shape[0], 1))
return norm_data_set, range_s, min_val
歸一化測試
normalize_data_set, ranges, min_val = auto_num(dating_data_mat)
print(normalize_data_set)
測試演算法
def dating_class_test():
# 選擇測試資料量
ho_ratio = 0.10
# 解析資料
dating_data_mat, dating_labels = file2matrix(`datingTestSet2.txt`)
# 歸一化資料
norm_mat, range_s, min_val = auto_num(dating_data_mat)
# 拆分10%資料作為測試資料
m = norm_mat.shape[0] # 總資料量
num_test_vec = int(m * ho_ratio) # 測試資料量
# 錯誤樣本計數
error_count = 0.0
# 對測試資料進行分類,並對比檢驗結果正確率
for i in range(num_test_vec):
classifier_result = classify0( # classifier_result : k個鄰居里頻率最高的分類
norm_mat[i, :], # 用於分類的輸入向量(測試資料, : 表示一行內所有元素)
norm_mat[num_test_vec: m, :], # 訓練樣本集(從測試的資料開始到總資料量結束)
dating_labels[num_test_vec:m], # 訓練標籤向量(從測試的資料開始到總資料量結束)
3 # 選取最近鄰居的數量
)
print(`the classifier came back with: %d, the real answer is: %d` % (classifier_result, dating_labels[i]))
if classifier_result != dating_labels[i]:
error_count += 1.0
print(`the total error rate is: %f` % (error_count / float(num_test_vec)))
執行測試
dating_class_test()
使用演算法
def classify_person():
"""
根據輸入指標,通過分類器進行預測喜歡程度
:return:
"""
result_list = [`not at all`, `in small doses`, `in large doses`]
percent_tats = float(input(`percentage of time spent playing vedio games?`))
ff_miles = float(input(`frequent flier miles earned per year?`))
ice_cream = float(input(`liters of ice cream consumed per year?`))
dating_data, dating_labels = file2matrix(`datingTestSet2.txt`)
normalize_matrix, ranges, min_val = auto_num(dating_data)
# 將輸入指標,歸一化後代入分類器進行預測
in_arr = np.array([ff_miles, percent_tats, ice_cream])
print(in_arr, min_val, ranges, (in_arr-min_val)/ranges)
print(ranges)
classifier_result = classify0((in_arr-min_val)/ranges, normalize_matrix, dating_labels, 3)
print("You will probably like this person: ", result_list[classifier_result - 1])
執行函式
classify_person()
輸出
percentage of time spent playing vedio games?20
frequent flier miles earned per year?299
liters of ice cream consumed per year?1
You will probably like this person: in large doses
sklearn中實現
from sklearn import neighbors
def knn_classify_person():
"""
根據輸入指標,通過分類器進行預測喜歡程度
:return:
"""
result_list = np.array([`not at all`, `in small doses`, `in large doses`])
percent_tats = float(input(`percentage of time spent playing vedio games?`))
ff_miles = float(input(`frequent flier miles earned per year?`))
ice_cream = float(input(`liters of ice cream consumed per year?`))
dating_data, dating_labels = file2matrix(`datingTestSet2.txt`)
normalize_matrix, ranges, min_val = auto_num(dating_data)
# 將輸入指標,歸一化後代入分類器進行預測
in_arr = np.array([ff_miles, percent_tats, ice_cream])
# 宣告k為3的knn演算法,n_neighbors即是鄰居數量,預設值為5
knn = neighbors.KNeighborsClassifier(n_neighbors=3)
# 訓練演算法
knn.fit(normalize_matrix, dating_labels)
# 預測
classifier_result = knn.predict([(in_arr - min_val) / ranges])
print("You will probably like this person: ", result_list[classifier_result - 1][0])
# 執行函式
knn_classify_person()