上上一章已經學習了感知機模型、策略和算法,感知機對於分類任務有着其優點,但是該模型是在具有強假設的條件下——訓練數據集必須是線性可分的,但是如果數據集是呈現無規則的分佈,那麼此時如果要做分類任務,還可以考慮k近鄰(KNN),這是一種基本的分類和迴歸方法,既可以做簡單的二分類也可以做複雜的多分類任務,還可以做迴歸任務。
KNN模型
KNN模型實際上對應於對特徵空間的劃分,雖然沒有具體的數學抽象語言描述,但是仍然存在其三要素:距離度量、K值的選擇、分類決策規則。
距離度量
K值的選擇
除了距離度量外,還有K值的選擇對KNN算法的結果也會產生重大影響。
- 如果選擇較小的k值,就相當於用較小的領域中的訓練實例進行預測,“學習”的近似誤差會減小,只有與輸入實例較近的實例纔會對預測結果起到作用,但缺點就是學習的估計誤差就會增大,預測結果就會近鄰的實例點非常敏感;
- 如果選擇較大的值,學習的誤差估計會減小,但是與此同時,近似誤差就會增大,這時會出現對於距離比較遠的實例點起不到預測作用,使得預測結果錯誤。
分類決策規則
KNN中的決策規則通常就是“投票選舉”——少數服從多數的方式。
如果損失函數是0-1損失函數,那麼分類函數就是:
對於相鄰k個訓練實例點構成集合N,誤分類率是:
要使誤分類率最小,那麼就是要求正確的概率最大,所以少數服從多數的規則正好可以滿足經驗風險最小化。
KNN算法
算法描述
實現KNN時,主要是考慮的問題時如何對訓練數據進行快速K近鄰搜索,如果特徵空間的維數大或者訓練數據容量大時,那麼數據存儲就是一個大問題。KNN最簡單的實現方法是線性掃描,這時當數據集很大,計算就非常地耗時。爲了提高這種搜索效率,使用特殊地結構進行存儲訓練數據——kd樹(kd tree)。kd樹是一種對k維空間中的點進行存儲以便於對其進行快速搜索的樹形數據結構。實質上,kd樹是一種二叉樹,表示對k維空間的一個劃分。
代碼實現
自編程實現
class KNN:
"""
使用自編程實現KNN算法
@author cecilia
"""
def __init__(self,X_train,y_train,k=3):
# 所需參數初始化
self.k=k # 所取k值
self.X_train=X_train
self.y_train=y_train
def predict(self,X_new):
# 計算歐氏距離
dist_list=[(np.linalg.norm(X_new-self.X_train[i],ord=2),self.y_train[i])
for i in range(self.X_train.shape[0])]
#[(d0,-1),(d1,1)...]
# 對所有距離進行排序
dist_list.sort(key=lambda x: x[0])
# 取前k個最小距離對應的類別(也就是y值)
y_list=[dist_list[i][-1] for i in range(self.k)]
# [-1,1,1,-1...]
# 對上述k個點的分類進行統計
y_count=Counter(y_list).most_common()
# [(-1, 3), (1, 2)]
return y_count[0][0]
def main():
# 初始化數據
X_train=np.array([[5,4],
[9,6],
[4,7],
[2,3],
[8,1],
[7,2]])
y_train=np.array([1,1,1,-1,-1,-1])
# 測試數據
X_new = np.array([[5, 3]])
# 不同的k(取奇數)對分類結果的影響
for k in range(1,6,2):
#構建KNN實例
clf=KNN(X_train,y_train,k=k)
#對測試數據進行分類預測
y_predict=clf.predict(X_new)
print("k={},class label is:{}".format(k,y_predict))
Sklearn庫
from sklearn.neighbors import KNeighborsClassifier
def sklearn_knn():
"""
使用sklearn庫實現KNN算法
@author cecilia
"""
X_train=np.array([[5,4],
[9,6],
[4,7],
[2,3],
[8,1],
[7,2]])
y_train=np.array([1,1,1,-1,-1,-1])
# 待預測數據
X_new = np.array([[5, 3]])
# 不同k值對結果的影響
for k in range(1,6,2):
# 構建實例
clf = KNeighborsClassifier(n_neighbors=k,n_jobs=-1)
# 選擇合適算法
clf.fit(X_train, y_train)
# print(clf.kneighbors(X_new))
# 預測
y_predict=clf.predict(X_new)
#print(clf.predict_proba(X_new))
print("accuracy:{:.0%}".format(clf.score([[5,3]],[[1]])))
print("k={},label lcass is:{}".format(k,y_predict))
結果顯示:
思考
KNN算法模型的複雜度主要是體現在哪兒?什麼情況下會造成過擬合?
k臨近算法的模型複雜度體現在k值上;k值較小容易造成過擬合,k值較大容易造成欠擬合。