機器學習實戰筆記2——KNN及其實現

任務安排

1、機器學習導論       8、核方法
2、KNN及其實現      9、稀疏表示
3、K-means聚類      10、高斯混合模型
4、主成分分析          11、嵌入學習
5、線性判別分析      12、強化學習
6、貝葉斯方法          13、PageRank
7、邏輯迴歸              14、深度學習

KNN及其實現

Ⅰ 分類與聚類

      首先,我們先來區分下兩個重要概念,分類與聚類
分類:
      事先已知道數據集中包含多少種類,從而對數據集中每一樣本進行分類,且所分配的標籤必須包含在已知的標籤集中,屬於監督學習

聚類:
      將數據對象的集合分成相似的對象類(簇)的過程。使得同一類(簇)中的對象之間具有較高的相似性,而不同類(簇)中的對象具有較高的相異性,並且事先不知道數據集本身有多少類別,屬於無監督學習

      正所謂,物以類聚,人以羣分,對於分類和聚類的應用也大相徑庭
      分類應用:對一個學校的在校大學生進行性別分類,我們知道有且僅可爲“男”,“女”兩類
      聚類應用:對於在校大學生,同學們根據自己的興趣愛好等因素nn 個小團體
由此我們知道,分類與聚類:
不同點:
      分類已知種類個數與類型進行區分;
      聚類的種類個數與類型均未知;

共同點:
      分類與聚類的類中有很高相似性類間有很高相異性

Ⅱ 定義

      KNN 算法,是數據挖掘分類技術中最簡單的一種
      KNN(K-NearestNeighbor),K 最近鄰分類算法,K 是指最近的 K 個鄰居,而測試樣本可以根據這 K 個鄰居的特徵值來代表,KNN 算法,就是通過測量不同特徵值之間的距離來實現的
在這裏插入圖片描述

★Ⅲ 算法剖析

輸入:訓練樣本 {xiyixi,yi}i=1n^{n}_{i=1},測試樣本 xx,近鄰個數 KK,距離函數 distdist
輸出:測試樣本的預測類別 yy
      1.計算測試數據與各個訓練數據之間的距離;
      2.按照距離的遞增關係進行排序;
      3.選取距離最小的 K 個點;
      4.分別確定前 K 個點所在類別的出現頻率;
      5.返回前 K 個點中出現頻率最高的類別作爲測試數據的預測分類;
(老師還給了個看着挺複雜的公式)

yargmaxi=1Kδ(y,f(si))(yf(X)y←\arg max{∑^K_{i=1}δ(y,f(si))}(_{y∈f(X)}

      記 xxKK 個最近鄰爲 S1S2SkS1、S2……Sk
      f(X)f(X) 是類別映射函數
      脈衝函數 δ(x,y)δ(x,y) 當且僅當 x=yx=y 時取1,其他爲0
      通俗點理解,右邊的 函數是計數器,符合一類的+1,最終把最多的一類賦值給 yy(即 xx 的預測類別)

Ⅳ 距離的求法

      分析了這麼多,歸根結底,KNN 算法的核心是求距離(即每個訓練樣本點與測試樣本點之間的距離),下面介紹幾種常見距離:

★1、歐幾里得距離(Euclidean Distance):

就是我們最常見的距離求法,衡量多維空間各點之間的絕對距離
dist(X,Y)=i=1n(xiyi)2dist(X,Y)=\sqrt{∑^n_{i=1}(xi-yi)^2}

2、明可夫斯基距離(Minkowski Distance):

明氏距離是歐氏距離的推廣,是對多個距離度量公式的概括性表述,當p=2時即歐幾里得距離
dist(X,Y)=(i=1nxiyip)1/pdist(X,Y)=(∑^n_{i=1}|xi-yi|^p)^{1/p}

3、曼哈頓距離(Manhattan Distance):

曼哈頓距離來源於城市區塊距離,是將多個維度上的距離進行求和後的結果,是明氏距離中p=1的情況
dist(X,Y)=i=1nxiyidist(X,Y)=∑^n_{i=1}|xi-yi|

4、向量空間餘弦相似度(Cosine Similarity):

(點乘公式的變形)餘弦相似度用向量空間中兩個向量夾角的餘弦值作爲衡量兩個個體間差異的大小。相比距離度量,餘弦相似度更加註重兩個向量在方向上的差異,而非距離或長度上
sim(X,Y)=cosθ=xyxysim(X,Y)=cosθ=\frac{\vec{x}·\vec{y}}{||x||·||y||}

Ⅴ 優缺點

      1.如上面方塊三角的例子所示,當樣本不平衡時,取的 K 不同,yy 的結果也不同,準確率也隨之下降
      2.計算量大,尤其是特徵數非常多的時候。
      3.KD 樹,求樹之類的模型建立需要大量的內存。
      4.是慵懶散學習方法,基本上不學習,導致預測時速度比起邏輯迴歸之類的算法慢。
      5.相比決策樹模型,KNN 模型的可解釋性不強。
(3、5點的例子我也不太懂,老師說後續課程會學到)

Ⅵ 分類精度

被正確分類的樣本數佔樣本總數的個數
ACC=ncorrectntotalACC=\frac{n_{correct}}{n_{total}}
例:
有一個用來對貓(cats)、狗(dogs)、兔子(rabbits)進行分類的系統
總共有 27 只動物:8 只貓, 6 條狗, 13 只兔子
混淆矩陣,是爲了進一步分析性能而對該算法測試結果做出的總結
結果的混淆矩陣爲:
在這裏插入圖片描述
=5+3+11/270.704分類精度=(5+3+11)/27≈0.704 (主對角線元素相加/總數)

Ⅶ 算法優化

(作了解即可)

1、距離加權最近鄰算法:

對 KNN 的一個顯而易見的改進是對 K 個近鄰的貢獻加權,根據它對相對查詢點 xqx_q 的距離,將較大的權值賦給較近的近鄰。
f^(x)argmaxyf(X)i=1kωiδ(y,f(xi))\hat{f}(x)←\arg \underset{y∈f(X)}{max}{∑^k_{i=1}ω_iδ(y,f(xi))}
很好理解,生活中肯定也是與你關係越好的朋友,與你相似度越高,越能代表你

2、迴歸(regression)最近鄰算法

f^(x)i=1kf(xi)k\hat{f}(x)←\frac{∑^k_{i=1}f(x_i)}{k}
上節課有提到過,迴歸用於連續型變量的預測,如經濟走向等

3、加權迴歸最近鄰算法

f^(x)i=1kωiδ(y,f(xi))i=1kωi\hat{f}(x)←\frac{∑^k_{i=1}ω_iδ(y,f(xi))}{∑^k_{i=1}ω_i}
———————————————分割線———————————————
(算是明白了,老師就是隻講理論原理,代碼實現讓我們自個兒搞定,雖然挺鍛鍊能力的,但是着實很累啊!!!

今日任務

1、Sklearn 中的 make_circles 方法生成訓練樣本,並隨機生成測試樣本,用KNN分類並可視化
在這裏插入圖片描述
2、Sklearn 中的 datasets 方法導入訓練樣本,並用留一法產生測試樣本,用 KNN 分類並輸出分類精度。[留一法:留一個樣本進行測試,其它所有的用來訓練,遍歷所有樣本]
            data = sklearn.datasets.iris.data
            label = sklearn.datasets.iris.target

3、下載 CIFAR圖像數據集,並跑一下圖像分類(選做)
4、顏值打分數據和模型下載

任務解決

1、這次沒有完整的代碼 copy 了,有點失落,只能借鑑下前輩寫的類似的KNN 用法依樣葫蘆了
關鍵點:
①隨機生成測試樣本,那麼橫縱座標分別用 random 得到(默認是0~1)
有可能會想到效仿代碼第 8 行生成一個樣本

x2, y2 = make_circles(n_samples=1, factor=0.5, noise=0.1)

實則不可取,這個生成的點是在兩個圈上的隨機,並非實質上的隨機(無法在空白處生成點);看見一個 make_blobs 函數,應該是可以用來生成單個隨機點的,不過畫圖的時候數組取值要稍微注意下
②KNN實現

from sklearn.datasets import make_circles
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
import numpy as np
import random

fig = plt.figure(1)
x1, y1 = make_circles(n_samples=400, factor=0.5, noise=0.1) 
# 模型訓練 求距離、取最小K個、求類別頻率
knn = KNeighborsClassifier(n_neighbors=15)
knn.fit(x1, y1)   # X是訓練集(橫縱座標) y是標籤類別
# 進行預測
x2 = random.random() # 測試樣本橫座標
y2 = random.random() # 測試樣本縱座標

X_sample = np.array([[x2, y2]])   # 給測試點
y_sample = knn.predict(X_sample)   # 得預測類別
neighbors = knn.kneighbors(X_sample, return_distance=False)

plt.subplot(121)
plt.title('make_circles 1')
plt.scatter(x1[:, 0], x1[:, 1], marker='o', c=y1)
plt.scatter(x2, y2, marker='*', c='b')

plt.subplot(122)
plt.title('make_circles 2')
plt.scatter(x1[:, 0], x1[:, 1], marker='o', c=y1)
plt.scatter(x2, y2, marker='*', c='b')
for i in neighbors[0]:
    # plt.plot([x1[i][0], X_sample[0][0]], [x1[i][1], X_sample[0][1]], '-', linewidth=0.6, c='b')   連線
    plt.scatter([x1[i][0], X_sample[0][0]], [x1[i][1], X_sample[0][1]], marker='o', c='b', s=50)

plt.show()

效果圖(如果覺得效果不好可以再多連個線)
在這裏插入圖片描述
本以爲到這就算結束了,沒想到老師讓我們自己敲KNN的代碼,不用Python 的庫,行8,有點腦闊疼
放代碼(效果無異,不過應該時間複雜度偏高)

from sklearn.datasets import make_circles
import matplotlib.pyplot as plt
import numpy as np
import random
import heapq

fig = plt.figure(1)
x1, y1 = make_circles(n_samples=400, factor=0.5, noise=0.1)
distance = []
k = 15
x2 = random.random()
y2 = random.random()

for i in range(0, 400):
    dx = x1[:, 0][i] - x2
    dy = x1[:, 1][i] - y2
    d = (dx**2+dy**2)**1/2
    distance.append(d)

min_index = map(distance.index, heapq.nsmallest(k, distance))
# 求最大的的k個數即用nlargest(此方法僅適用無重複情況,重複情況需改進)

plt.subplot(121)
plt.title('make_circles 1')
plt.scatter(x1[:, 0], x1[:, 1], marker='o', c=y1)
plt.scatter(x2, y2, marker='*', c='b')

plt.subplot(122)
plt.title('make_circles 2')
plt.scatter(x1[:, 0], x1[:, 1], marker='o', c=y1)
plt.scatter(x2, y2, marker='*', c='b')
for q in list(min_index):
    plt.scatter(x1[q, 0], x1[q, 1], marker='o', c='b', s=50)

plt.show()

補充一點numpy的知識

import numpy as np
x1 = np.array([[5, 1], [9, 3], [7, 5]])
print(x1)
print()
print(x1[:, 0], end="\n\n")
print(x1[0, :], "\n")
print(x1[:, 0][1])

輸出結果
在這裏插入圖片描述
2、關鍵點:
①導入經典數據集鳶尾花iris

Iris 鳶尾花數據集是一個經典數據集,在統計學習和機器學習領域都經常被用作示例。數據集內包含 3 類共 150 條記錄,每類各 50 個數據,每條記錄都有 4 項特徵:花萼長度、花萼寬度、花瓣長度、花瓣寬度,可以通過這 4 個特徵預測鳶尾花卉屬於(iris-setosa, iris-versicolour, iris-virginica)中的某一品種。
②留一交叉驗證
用 KNN 庫

from sklearn import datasets
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split, LeaveOneOut
import matplotlib.pyplot as plt

iris = datasets.load_iris()

X = iris.data
y = iris.target
loo = LeaveOneOut()
# knn = KNeighborsClassifier(15)   # 默認k=5
# correct = 0

# for train, test in loo.split(X):
#     # print("留一劃分:%s %s" % (train.shape, test.shape))
#     # X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)
#     knn.fit(X[train], y[train])
#     y_sample = knn.predict(X[test])
#     if y_sample == y[test]:
#         correct += 1
#
# print('Test accuracy:', correct/len(X))
K = []
Accuracy = []
for k in range(1, 16):
    correct = 0
    knn = KNeighborsClassifier(k)
    for train, test in loo.split(X):
        # print("留一劃分:%s %s" % (train.shape, test.shape))
        # X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.4, random_state=0)
        # train_test_split 用於任意劃分訓練集與測試集 test_size是測試集百分比
        knn.fit(X[train], y[train])
        y_sample = knn.predict(X[test])
        if y_sample == y[test]:
            correct += 1
    K.append(k)
    Accuracy.append(correct/len(X))
    plt.plot(K, Accuracy)
    plt.xlabel('Accuracy')
    plt.ylabel('K')
    print('K: ', k)
    print('Test Accuracy: ', correct/len(X))

plt.show()

效果圖
在這裏插入圖片描述
順便可視化了一下
在這裏插入圖片描述
有個實時可視化的畫法,太久沒寫有點忘了,後續補

用自寫 KNN,借鑑 knn算法(純python實現)

from sklearn.model_selection import LeaveOneOut
import numpy as np
from sklearn import neighbors, datasets


def myknn(test, train, labels, k):
    # 返回所屬類別
    m, n = train.shape   # shape(m, n)m列n個特徵
    # 計算測試數據到每個點的歐式距離
    distances = []
    for i in range(m):
        sum = 0
        for j in range(n):
            sum += (test[j] - train[i][j]) ** 2
        distances.append(sum ** 0.5)

    sortDist = sorted(distances)
    # print(sortDist)
    # k 個最近的值所屬的類別
    classCount = {}
    for i in range(k):
        voteLabel = labels[distances.index(sortDist[i])]
        classCount[voteLabel] = classCount.get(voteLabel, 0) + 1   # 0:map default
    sortedClass = sorted(classCount.items(), key=lambda d: d[1], reverse=True)
    return sortedClass[0][0]


k_neighbors = 15
iris = datasets.load_iris()
X = iris.data
y = iris.target
# print(data)
# print(label)
# print(classify(data[137], data, label, n_neighbors))

loo = LeaveOneOut()

correct = 0
for train_index, test_index in loo.split(X, y):
    # print("訓練集:", train_index)
    # print("測試集爲:", test_index[0])
    # print("X_train:", data[train_index])
    # print("X_test:", label[test_index])

    # 放在自己寫的KNN跑結果
    if myknn(X[test_index[0]], X, y, k_neighbors) == y[test_index[0]]:
        correct += 1

    # print("正確數:", correct)
    # print("")

print('K: ', k_neighbors)
print("正確率爲:", correct / len(X))

3、KNN 分類器進行圖像分類——(降維方法)主成分分析PCA線性判別分析LDA

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