
作者 | Japson
來源 | 木東居士
0x00 前言
天下苦數(shù)學(xué)久矣!
對于很多想要入門機器學(xué)習(xí)的工程師來說,數(shù)學(xué)是通往AI道路上的第一支攔路虎。一些已經(jīng)工作的同學(xué)不得不撿起早已還給老師的數(shù)學(xué)知識,勉強拿起《統(tǒng)計學(xué)習(xí)方法》、《西瓜書》等入門書籍鉆研?;虮灰粋€個復(fù)雜的機公式勸退,或記下一堆公式定理之后卻不知道和代碼有什么關(guān)系,茫然不知所措。
其實對于工程師來說,最直接的入門方法就是coding。
本系列從最簡單的機器學(xué)習(xí)算法“K-近鄰算法”開始,通過代碼走進機器學(xué)習(xí)的大門,搞定傳統(tǒng)機器學(xué)習(xí)算法。
首先會介紹算法的基本原理,然后依據(jù)原理手動實現(xiàn)算法,最后使用sklearn中提供的機器學(xué)習(xí)庫完成一些小demo。不用擔心,相關(guān)的機器學(xué)習(xí)概念以及算法原理也會穿插其中,幫助你以“代碼->原理->代碼”這種迭代的方式完成學(xué)習(xí)。
需要:
掌握Python語言,能夠使用Numpy、Pandas等工具庫。
安裝Anaconda
不要求對機器學(xué)習(xí)算法以及相關(guān)概念有很深刻的了解,因為在文章中會對首次出現(xiàn)的概念進行介紹。
子曰:“先行其言而后從之”。行動永遠是引發(fā)改變的第一步,話不多說,先讓我們碼起來吧!
0x01 初探kNN算法
為什么選擇kNN
為什么說KNN算法是機器學(xué)習(xí)的敲門磚?
首先KNN算法思想簡單樸素,容易理解,幾乎不需要任何數(shù)學(xué)知識。這一點使得KNN算法非常適合入門。
其次,KNN算法也很好用,理論成熟,簡單粗暴,既可以用來做分類(天然支持多分類),也可以用來做回歸。并且與樸素貝葉斯之類的算法相比,由于其對數(shù)據(jù)沒有假設(shè),因此準確度高,對異常點不敏感。
最后,kNN算法簡單,但是可以解釋機器學(xué)習(xí)算法過程中的很多細節(jié)問題,能夠完整的刻畫機器學(xué)習(xí)應(yīng)用的流程。
當然KNN算法也有缺點,我們會在最后進行總結(jié)。
kNN思想簡介
魯迅曾經(jīng)說過:“想要了解一個人,就去看看他的朋友”。因此,KNN算法是魯迅發(fā)明的。
kNN(k-NearestNeighbor),也就是k最近鄰算法。顧名思義,所謂K最近鄰,就是k個最近的鄰居的意思。也就是在數(shù)據(jù)集中,認為每個樣本可以用離他最距離近的k個鄰居來代表。
貼出一張從百度百科上找的一張圖,我們可以直觀地感受到這樸素的思想:我們要判斷Xu 是什么顏色的,找到與其距離最近的5個點,有4個是紅色的,有1個是綠色的。因此我們認為Xu是屬于紅色的集合
因此我們說:
在一個給定的類別已知的訓(xùn)練樣本集中,已知樣本集中每一個數(shù)據(jù)與所屬分類的對應(yīng)關(guān)系(標簽)。在輸入不含有標簽的新樣本后,將新的數(shù)據(jù)的每個特征與樣本集中數(shù)據(jù)對應(yīng)的特征進行比較,然后算法提取樣本最相似的k個數(shù)據(jù)(最近鄰)的分類標簽。通過多數(shù)表決等方式進行預(yù)測。即選擇k個最相似數(shù)據(jù)中出現(xiàn)次數(shù)最多的分類,作為新數(shù)據(jù)的分類。
K近鄰法不具有顯式的學(xué)習(xí)過程,而是利用訓(xùn)練數(shù)據(jù)集對特征向量空間進行劃分,并作為其分類的“模型”。
kNN算法流程
通過理解算法思想,可以將其簡化為“找鄰居+投票”。K近鄰法使用的模型,實際上是特征空間的劃分。模型由三個基本要素決定:
其中兩個實例點之間的距離反映了相似程度。一般來說使用歐氏距離來計算。
梳理kNN算法流程如下:
0x02 算法實現(xiàn)
kNN算法自實現(xiàn)
打開Jupyter Notebook,創(chuàng)建Python3文件。
準備數(shù)據(jù)
首先我們準備一組數(shù)據(jù):
import numpy as npimport matplotlib.pyplot as plt# raw_data_x是特征,raw_data_y是標簽,0為良性,1為惡性raw_data_X = [[3.393533211, 2.331273381], [3.110073483, 1.781539638], [1.343853454, 3.368312451], [3.582294121, 4.679917921], [2.280362211, 2.866990212], [7.423436752, 4.685324231], [5.745231231, 3.532131321], [9.172112222, 2.511113104], [7.927841231, 3.421455345], [7.939831414, 0.791631213] ] raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]# 設(shè)置訓(xùn)練組X_train = np.array(raw_data_X) y_train = np.array(raw_data_y)# 將數(shù)據(jù)可視化plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1], color='g', label = 'Tumor Size') plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1], color='r', label = 'Time') plt.xlabel('Tumor Size') plt.ylabel('Time') plt.axis([0,10,0,5]) plt.show()
數(shù)據(jù)可視化后生成的圖片如下圖所示。其中橫軸是腫塊大小,縱軸是發(fā)現(xiàn)時間。每個病人的腫塊大小和發(fā)病時間構(gòu)成了二維平面特征中的一個點。對于每個點,我們通過label明確是惡性腫瘤(綠色)、良性腫瘤(紅色)。
那么現(xiàn)在給出一個腫瘤患者的數(shù)據(jù)(樣本點)x:[8.90933607318, 3.365731514],是良性腫瘤還是惡性腫瘤
求距離
我們要做的是:求點x到數(shù)據(jù)集中每個點的距離,首先計算距離,使用歐氏距離
下面寫代碼:
from math import sqrt distances = [] # 用來記錄x到樣本數(shù)據(jù)集中每個點的距離for x_train in X_train: d = sqrt(np.sum((x_train - x) ** 2)) distances.append(d)# 使用列表生成器,一行就能搞定,對于X_train中的每一個元素x_train都進行前面的運算,把結(jié)果生成一個列表distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train] distances 輸出:[5.611968000921151, 6.011747706769277, 7.565483059418645, 5.486753308891268, 6.647709180746875, 1.9872648870854204, 3.168477291709152, 0.8941051007010301, 0.9830754144862234, 2.7506238644678445]
在求出距離列表之后,我們要找到最小的距離,需要進行一次排序操作。其實不是簡單的排序,因為我們把只將距離排大小是沒有意義的,我們要知道距離最小的k個點是在樣本集中的位置。
這里我們使用函數(shù):np.argsort(array) 對一個數(shù)組進行排序,返回的是相應(yīng)的排序后結(jié)果的索引
nearest = np.argsort(distances) nearest 輸出:array([7, 8, 5, 9, 6, 3, 0, 1, 4, 2]) 結(jié)果的含義是:距離最小的點在distances數(shù)組中的索引是7,第二小的點索引是8... 近到遠是哪些點
選k值
然后我們選擇k值,這里暫定為6,那就找出最近的6個點(top 6),并記錄他們的標簽值(y)
k = 6topK_y = [y_train[i] for i in nearest[:k]] topK_y 輸出:[1, 1, 1, 1, 1, 0]
決策規(guī)則
下面進入投票環(huán)節(jié)。找到與測試樣本點最近的6個訓(xùn)練樣本點的標簽y是什么。可以查不同類別的點有多少個。
將數(shù)組中的元素和元素出現(xiàn)的頻次進行統(tǒng)計
from collections import Counter votes = Counter(topK_y) votes 輸出:一個字典,原數(shù)組中值為0的個數(shù)為1,值為1的個數(shù)有為5Counter({0:1, 1:5}) # Counter.most_common(n) 找出票數(shù)最多的n個元素,返回的是一個列表,列表中的每個元素是一個元組,元組中第一個元素是對應(yīng)的元素是誰,第二個元素是頻次votes.most_common(1) 輸出:[(1,5)] predict_y = votes.most_common(1)[0][0] predict_y 輸出:1
得到預(yù)測的y值是1
自實現(xiàn)完整工程代碼
我們已經(jīng)在jupyter notebook中寫好了kNN算法,下面我們在外部進行封裝。
相關(guān)代碼可以在 https://github.com/japsonzbz/ML_Algorithms 中看到
import numpy as npimport math as sqrtfrom collections import Counterclass kNNClassifier: def __init__(self, k): """初始化分類器""" assert k >= 1, "k must be valid" self.k = k self._X_train = None self._y_train = None def fit(self, X_train, y_train): """根據(jù)訓(xùn)練數(shù)據(jù)集X_train和y_train訓(xùn)練kNN分類器""" assert X_train.shape[0] == y_train.shape[0], \ "the size of X_train must be equal to the size of y_train" assert self.k <= X_train.shape[0], \ "the size of X_train must be at least k" self._X_train = X_train self._y_train = y_train return self def predict(self,X_predict): """給定待預(yù)測數(shù)據(jù)集X_predict,返回表示X_predict結(jié)果的向量""" assert self._X_train is not None and self._y_train is not None, \ "must fit before predict!" assert X_predict.shape[1] == self._X_train.shape[1], \ "the feature number of X_predict must be equal to X_train" y_predict = [self._predict(x) for x in X_predict] return np.array(y_predict) def _predict(self, x): distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in self._X_train] nearest = np.argsort(distances) topK_y = [self._y_train[i] for i in nearest] votes = Counter(topK_y) return votes.most_common(1)[0][0] def __repr__(self): return "kNN(k=%d)" % self.k
當我們寫完定義好自己的kNN代碼之后,可以在jupyter notebook中使用魔法命令進行調(diào)用:
%run myAlgorithm/kNN.py knn_clf = kNNClassifier(k=6) knn_clf.fit(X_train, y_train) X_predict = x.reshape(1,-1) y_predict = knn_clf.predict(X_predict) y_predict 輸出:array([1])
現(xiàn)在我們就完成了一個sklearn風格的kNN算法,但是實際上,sklearn封裝的算法比我們實現(xiàn)的要復(fù)雜得多。
sklearn中的kNN
代碼
對于機器學(xué)習(xí)來說,其流程是:訓(xùn)練數(shù)據(jù)集 -> 機器學(xué)習(xí)算法 -fit-> 模型 輸入樣例 -> 模型 -predict-> 輸出結(jié)果
我們之前說過,kNN算法沒有模型,模型其實就是訓(xùn)練數(shù)據(jù)集,predict的過程就是求k近鄰的過程。
我們使用sklearn中已經(jīng)封裝好的kNN庫。你可以看到使用有多么簡單。
from sklearn.neighbors import KNeighborsClassifier# 創(chuàng)建kNN_classifier實例kNN_classifier = KNeighborsClassifier(n_neighbors=6)# kNN_classifier做一遍fit(擬合)的過程,沒有返回值,模型就存儲在kNN_classifier實例中kNN_classifier.fit(X_train, y_train)# kNN進行預(yù)測predict,需要傳入一個矩陣,而不能是一個數(shù)組。reshape()成一個二維數(shù)組,第一個參數(shù)是1表示只有一個數(shù)據(jù),第二個參數(shù)-1,numpy自動決定第二維度有多少y_predict = kNN_classifier.predict(x.reshape(1,-1)) y_predict 輸出:array([1])
在kNN_classifier.fit(X_train, y_train)這行代碼后其實會有一個輸出:
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski', metric_params=None, n_jobs=1, n_neighbors=6, p=2, weights='uniform')
參數(shù)
class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2, metric=’minkowski’, metric_params=None, n_jobs=None, **kwargs)
我們研究一下參數(shù):
方法
對于KNeighborsClassifier的方法:
方法名含義fit(X, y)使用X作為訓(xùn)練數(shù)據(jù),y作為目標值(類似于標簽)來擬合模型。get_params([deep])獲取估值器的參數(shù)。neighbors([X, n_neighbors, return_distance])查找一個或幾個點的K個鄰居。kneighbors_graph([X, n_neighbors, mode])計算在X數(shù)組中每個點的k鄰居的(權(quán)重)圖。predict(X)給提供的數(shù)據(jù)預(yù)測對應(yīng)的標簽。predict_proba(X)返回測試數(shù)據(jù)X的概率估值。score(X, y[, sample_weight])返回給定測試數(shù)據(jù)和標簽的平均準確值。set_params(**params)設(shè)置估值器的參數(shù)。
0xFF 總結(jié)
在本文中我們了解了第一個ML算法kNN,kNN憑借著自己樸素成熟的特點成為機器學(xué)習(xí)的敲門磚。
然后我們學(xué)習(xí)了kNN算法的流程,并且在jupyter notebook上手動實現(xiàn)了代碼,并且在外部也進行了封裝。最后我們學(xué)習(xí)了sklearn中的kNN算法。
雖然我們自己實現(xiàn)了一個機器學(xué)習(xí)算法,但是它的效果怎樣樣?預(yù)測準確率高不高?我們在機器學(xué)習(xí)過程中還有哪些需要注意的問題呢?
且聽下回分解。
數(shù)據(jù)分析咨詢請掃描二維碼
若不方便掃碼,搜微信號:CDAshujufenxi
LSTM 模型輸入長度選擇技巧:提升序列建模效能的關(guān)鍵? 在循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)家族中,長短期記憶網(wǎng)絡(luò)(LSTM)憑借其解決長序列 ...
2025-07-11CDA 數(shù)據(jù)分析師報考條件詳解與準備指南? ? 在數(shù)據(jù)驅(qū)動決策的時代浪潮下,CDA 數(shù)據(jù)分析師認證愈發(fā)受到矚目,成為眾多有志投身數(shù) ...
2025-07-11數(shù)據(jù)透視表中兩列相乘合計的實用指南? 在數(shù)據(jù)分析的日常工作中,數(shù)據(jù)透視表憑借其強大的數(shù)據(jù)匯總和分析功能,成為了 Excel 用戶 ...
2025-07-11尊敬的考生: 您好! 我們誠摯通知您,CDA Level I和 Level II考試大綱將于 2025年7月25日 實施重大更新。 此次更新旨在確保認 ...
2025-07-10BI 大數(shù)據(jù)分析師:連接數(shù)據(jù)與業(yè)務(wù)的價值轉(zhuǎn)化者? ? 在大數(shù)據(jù)與商業(yè)智能(Business Intelligence,簡稱 BI)深度融合的時代,BI ...
2025-07-10SQL 在預(yù)測分析中的應(yīng)用:從數(shù)據(jù)查詢到趨勢預(yù)判? ? 在數(shù)據(jù)驅(qū)動決策的時代,預(yù)測分析作為挖掘數(shù)據(jù)潛在價值的核心手段,正被廣泛 ...
2025-07-10數(shù)據(jù)查詢結(jié)束后:分析師的收尾工作與價值深化? ? 在數(shù)據(jù)分析的全流程中,“query end”(查詢結(jié)束)并非工作的終點,而是將數(shù) ...
2025-07-10CDA 數(shù)據(jù)分析師考試:從報考到取證的全攻略? 在數(shù)字經(jīng)濟蓬勃發(fā)展的今天,數(shù)據(jù)分析師已成為各行業(yè)爭搶的核心人才,而 CDA(Certi ...
2025-07-09【CDA干貨】單樣本趨勢性檢驗:捕捉數(shù)據(jù)背后的時間軌跡? 在數(shù)據(jù)分析的版圖中,單樣本趨勢性檢驗如同一位耐心的偵探,專注于從單 ...
2025-07-09year_month數(shù)據(jù)類型:時間維度的精準切片? ? 在數(shù)據(jù)的世界里,時間是最不可或缺的維度之一,而year_month數(shù)據(jù)類型就像一把精準 ...
2025-07-09CDA 備考干貨:Python 在數(shù)據(jù)分析中的核心應(yīng)用與實戰(zhàn)技巧? ? 在 CDA 數(shù)據(jù)分析師認證考試中,Python 作為數(shù)據(jù)處理與分析的核心 ...
2025-07-08SPSS 中的 Mann-Kendall 檢驗:數(shù)據(jù)趨勢與突變分析的有力工具? ? ? 在數(shù)據(jù)分析的廣袤領(lǐng)域中,準確捕捉數(shù)據(jù)的趨勢變化以及識別 ...
2025-07-08備戰(zhàn) CDA 數(shù)據(jù)分析師考試:需要多久?如何規(guī)劃? CDA(Certified Data Analyst)數(shù)據(jù)分析師認證作為國內(nèi)權(quán)威的數(shù)據(jù)分析能力認證 ...
2025-07-08LSTM 輸出不確定的成因、影響與應(yīng)對策略? 長短期記憶網(wǎng)絡(luò)(LSTM)作為循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)的一種變體,憑借獨特的門控機制,在 ...
2025-07-07統(tǒng)計學(xué)方法在市場調(diào)研數(shù)據(jù)中的深度應(yīng)用? 市場調(diào)研是企業(yè)洞察市場動態(tài)、了解消費者需求的重要途徑,而統(tǒng)計學(xué)方法則是市場調(diào)研數(shù) ...
2025-07-07CDA數(shù)據(jù)分析師證書考試全攻略? 在數(shù)字化浪潮席卷全球的當下,數(shù)據(jù)已成為企業(yè)決策、行業(yè)發(fā)展的核心驅(qū)動力,數(shù)據(jù)分析師也因此成為 ...
2025-07-07剖析 CDA 數(shù)據(jù)分析師考試題型:解鎖高效備考與答題策略? CDA(Certified Data Analyst)數(shù)據(jù)分析師考試作為衡量數(shù)據(jù)專業(yè)能力的 ...
2025-07-04SQL Server 字符串截取轉(zhuǎn)日期:解鎖數(shù)據(jù)處理的關(guān)鍵技能? 在數(shù)據(jù)處理與分析工作中,數(shù)據(jù)格式的規(guī)范性是保證后續(xù)分析準確性的基礎(chǔ) ...
2025-07-04CDA 數(shù)據(jù)分析師視角:從數(shù)據(jù)迷霧中探尋商業(yè)真相? 在數(shù)字化浪潮席卷全球的今天,數(shù)據(jù)已成為企業(yè)決策的核心驅(qū)動力,CDA(Certifie ...
2025-07-04CDA 數(shù)據(jù)分析師:開啟數(shù)據(jù)職業(yè)發(fā)展新征程? ? 在數(shù)據(jù)成為核心生產(chǎn)要素的今天,數(shù)據(jù)分析師的職業(yè)價值愈發(fā)凸顯。CDA(Certified D ...
2025-07-03