本文來自同步博客。html
P.S. 不知道怎麼顯示數學公式以及排版文章。因此若是以爲文章下面格式亂的話請自行跳轉到上述連接。後續我將再也不對數學公式進行截圖,畢竟行內公式截圖的話排版會很亂。看原博客地址會有更好的體驗。python
本文內容介紹機器學習的K近鄰算法,用它處理分類問題。分類問題的目標是利用採集到的已經通過分類處理的數據來預測新數據屬於何種類別。git
K近鄰算法對給定的某個新數據,讓它與採集到的樣本數據點分別進行比較,從中選擇最類似的K個點,而後統計這K個點中出現的各個類別的頻數,並斷定頻數最高的類別做爲新數據所屬的類別。github
這裏有個問題是如何斷定樣本數據與新數據是否類似。經常使用的一種計算方法叫歐幾里得距離。算法
假設有兩個數據分別是:$- X = (x_1,x_2,...,x_n) -$和$- Y = (y_1,y_2,...,y_n) -$。則$-X-$與$-Y-$的距離爲:數據庫
$$ D = \sqrt{\sum_{i=1}{n}(x_i-y_i)2} $$數組
用Python
實現這個式子代碼以下:app
def euclidean_distance(x, y): if len(x) != len(y): warnings.warn('Input error') return sqrt( sum( [(x[i] - y[i])**2 for i in range(0, len(x))] ) ) print(euclidean_distance([1,2,3], [2,4,5]))
NumPy
提供則能夠用下面方法計算兩個ndarray
的距離:less
np.linalg.norm(np.array([1,2,3]) - np.array([2,4,5]))
接下來依照上面描述的算法原理實現K近鄰算法。先定義一下輸入數據的格式:dom
#二維測試數據的格式 dataset = {'k': [[1,2],[2,3],[3,1]], 'r':[[6,5],[7,7],[8,6]]} new_features = [5,7]
咱們假定樣本數據的結構如dataset
爲一個字典類型的數據,字典的元素的關鍵字爲類型名稱,元素值爲一個包含該類型全部樣本點的列表。新數據爲一個數據點。
因此K近鄰算法的實現以下:
# KNN實現 def k_nearest_neighbors(data, predict, k=3): if len(data) >= k: warnings.warn('K less than total voting groups') # 計算距離 distances = [] for group in data: for features in data[group]: #distance = euclidean_distance(features, predict) distance = np.linalg.norm(np.array(features)-np.array(predict)) distances.append([distance, group]) # 排序後取前k項數據類別構成新數組 votes = [i[1] for i in sorted(distances)[:k]] # 統計數組中頻數最高的類別 vote_result = Counter(votes).most_common(1)[0][0] return vote_result # 調用KNN result = k_nearest_neighbors(dataset, new_features, k=3) print(result)
在UCI網站的機器學習數據庫中能夠找到Breast Cancer Wisconsin(Original)
的真實統計數據。數據能夠從這個連接下載,數據的描述能夠看這個連接。
看到數據描述中提到了數據每一列的定義以下:
這份數據的預測目標是判斷給定特徵數據對應的乳癌狀況,2表示良性、4表示惡性。
根據描述咱們先處理一下下載到的數據,給它的每一列加上個列名。這樣在Python
裏面就能夠把它當成一個CSV
文件處理。我這裏把它保存到了一個名爲breast-cancer-wisconsin.data
的文件裏。形狀以下:
數據裏面還有一些統計不到的數據,用英文問號?
表示。還有一個很奇怪的現象是數據讀取進來後有些數字會被處理成字符串類型,如'1'
這樣的數據。這些都須要咱們提早處理一下。
df = pd.read_csv('./dataset/breast-cancer-wisconsin.data') # 處理問號 df.replace('?', -99999, inplace=True) # id字段不該該當成一個統計特徵字段,所以去除該列的內容 df.drop(['id'], 1, inplace=True) # 源數據有部分數據是字符串,如'1',這對咱們的模型有影響,因此整理一下類型 # 用列表存放數據 full_data = df.astype(float).values.tolist() random.shuffle(full_data) # 洗亂數據
接下來生成訓練數據集和統計數據集,代碼以下:
# 生成訓練數據集和統計數據集 test_size = 0.2 train_set = {2:[], 4:[]} # 訓練集,佔80% test_set = {2:[], 4:[]} # 統計集,佔20% train_data = full_data[:-int(test_size*len(full_data))] test_data = full_data[-int(test_size*len(full_data)):] for i in train_data: train_set[i[-1]].append(i[:-1]) for i in test_data: test_set[i[-1]].append(i[:-1])
最後,利用上述KNN函數統計測試數據的準確性。
correct = 0 total = 0 for group in test_set: for data in test_set[group]: vote = k_nearest_neighbors(train_set, data, k=5) if group == vote: correct += 1 total += 1 # 打印結果 print('correct: ', correct) print('total: ', total) print('Accuracy: ', correct/total)
完整代碼請查看github連接。
一樣須要先處理一下數據並生成符合sklearn
的輸入格式的數據集。
from sklearn import model_selection # 讀取乳癌統計數據 df = pd.read_csv('./dataset/breast-cancer-wisconsin.data') # 處理問號 df.replace('?', -99999, inplace=True) # 由於ID字段與分類無關,因此去除他先,稍後咱們看一下它的影響 df.drop(['id'], 1, inplace=True) df = df.astype(float) # 生成數據集 X = np.array(df.drop(['class'], 1)) y = np.array(df['class']) X_train, X_test, y_train, y_test = model_selection.train_test_split(X, y, test_size=0.2)
而後生成KNN模型對象,用數據訓練模型,評估模型準確性。
from sklearn import neighbors # 構建模型與訓練 clf = neighbors.KNeighborsClassifier() clf.fit(X_train, y_train) # 計算精確度 accuracy = clf.score(X_test, y_test) print('Accuracy: ', accuracy) # 預測咱們本身構造的數據屬於哪一個類型 example_measures = np.array([[4,2,1,1,1,2,3,2,1],[2,3,4,4,1,2,3,4,1]]) prediction = clf.predict(example_measures) print('Predict resuct: ', prediction)
完整代碼請查看github連接。