第三章-KNN(分類和迴歸算法模型)

上上一章已經學習了感知機模型、策略和算法,感知機對於分類任務有着其優點,但是該模型是在具有強假設的條件下——訓練數據集必須是線性可分的,但是如果數據集是呈現無規則的分佈,那麼此時如果要做分類任務,還可以考慮k近鄰(KNN),這是一種基本的分類和迴歸方法,既可以做簡單的二分類也可以做複雜的多分類任務,還可以做迴歸任務。

KNN模型

KNN模型實際上對應於對特徵空間的劃分,雖然沒有具體的數學抽象語言描述,但是仍然存在其三要素:距離度量、K值的選擇、分類決策規則

距離度量

χnRn,xi,xjχ,xi=(xi(1),xi(2),xi(3)...,xi(n))T,xj=(xj(1),xj(2),xj(3)...,xj(n))T,xi,xjLP(xi,xj)=(l=1nxi(l)xj(l)p)1pp=1L1(xi,xj)=(l=1nxi(l)xj(l)),;p=2L2(xi,xj)=(l=1nxi(l)xj(l)2)12,L2使;p=,:L(xi,xj)=maxxi(l)xj(l) 設特徵空間\chi是n維實數向量空間R^n,x_i,x_j \in \chi,x_i=(x_i^{(1)},x_i^{(2)},x_i^{(3)}...,x_i^{(n)})^T,\\ x_j=(x_j^{(1)},x_j^{(2)},x_j^{(3)}...,x_j^{(n)})^T,x_i,x_j的距離可定義爲:\\ L_P(x_i,x_j)=(\sum^n_{l=1}|x_i^{(l)}-x_j^{(l)}|^p)^{\frac{1}{p}}\\ 一般地,當p=1時,L_1(x_i,x_j)=(\sum^n_{l=1}|x_i^{(l)}-x_j^{(l)}|),稱爲曼哈頓距離;\\ 當p=2時,L_2(x_i,x_j)=(\sum^n_{l=1}|x_i^{(l)}-x_j^{(l)}|^2)^{\frac{1}{2}},其實形式上也是L2範數,稱爲歐式距離,平常使用的比較多;\\ 當p=\infty,它是各個座標距離的最大值,即爲:L_{\infty}(x_i,x_j)=max|x_i^{(l)}-x_j^{(l)}|

K值的選擇

除了距離度量外,還有K值的選擇對KNN算法的結果也會產生重大影響。

  • 如果選擇較小的k值,就相當於用較小的領域中的訓練實例進行預測,“學習”的近似誤差會減小,只有與輸入實例較近的實例纔會對預測結果起到作用,但缺點就是學習的估計誤差就會增大,預測結果就會近鄰的實例點非常敏感;
  • 如果選擇較大的值,學習的誤差估計會減小,但是與此同時,近似誤差就會增大,這時會出現對於距離比較遠的實例點起不到預測作用,使得預測結果錯誤。

分類決策規則

KNN中的決策規則通常就是“投票選舉”——少數服從多數的方式。

如果損失函數是0-1損失函數,那麼分類函數就是:
f:Rn{c1,c2,...,ck} f:R^n \rightarrow {\{c_1,c_2,...,c_k}\}
對於相鄰k個訓練實例點構成集合N,誤分類率是:
1kxiNk(x)I(yicj)=11kxiNk(x)I(yi=cj) \frac{1}{k}\sum_{x_i\in N_k(x)}I(y_i \neq c_j )=1-\frac{1}{k}\sum_{x_i\in N_k(x)}I(y_i = c_j )
要使誤分類率最小,那麼就是要求正確的概率最大,所以少數服從多數的規則正好可以滿足經驗風險最小化。

KNN算法

算法描述

T={(x1,y1),(x2,y2),(x3,y3)...,(xN,yN)},xiχRnyiy={c1,c2,...,cK}i=12...,x;xy(1)TxkkxNk(x);(2)Nk(x)xyy=argmaxxiNk(x)I(yi=cj),i,j=1,2,..,N 輸入:訓練數據集:T=\{(x_1,y_1),(x_2,y_2),(x_3,y_3)...,(x_N,y_N)\},其中x_i \in \chi \subseteq R^n爲實例的特徵向量,\\ y_i \in y=\{c_1,c_2,...,c_K\}爲實例得類別,i=1,2,...,;實例特徵向量x;\\ 輸出:實例x所屬的類別y。\\ (1)根據給定的距離向量,在訓練集T中找出與x最近鄰的k個點,包含k個點的x的領域記作N_k(x);\\ (2)在N_k(x)中根據分類決策規則決定x的類別y:y=argmax\sum_{x_i\in N_k(x)}I(y_i = c_j ),i,j=1,2,..,N。

實現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值較大容易造成欠擬合。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章