本筆記為參加阿里云“天池龍珠計劃 機器學習訓練營”所做的學習記錄贼陶,代碼及知識內(nèi)容均來源于訓練營刃泡,本人稍作擴充。
具體活動內(nèi)容請移步阿里云天池龍珠計劃碉怔。
2.4.3 模擬數(shù)據(jù)集--kNN回歸
Step1: 庫函數(shù)導入
#Demo來自sklearn官網(wǎng)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsRegressor
Step2: 數(shù)據(jù)導入&分析
np.random.seed(0)
# 隨機生成40個(0, 1)之前的數(shù)烘贴,乘以5,再進行升序
X = np.sort(5 * np.random.rand(40, 1), axis=0)
# 創(chuàng)建[0, 5]之間的500個數(shù)的等差數(shù)列, 作為測試數(shù)據(jù)
T = np.linspace(0, 5, 500)[:, np.newaxis]
# 使用sin函數(shù)得到y(tǒng)值撮胧,并拉伸到一維
y = np.sin(X).ravel()
# Add noise to targets[y值增加噪聲]
y[::5] += 1 * (0.5 - np.random.rand(8))
Step3: 模型訓練&預測可視化
# Fit regression model
# 設置多個k近鄰進行比較
n_neighbors = [1, 3, 5, 8, 10, 40]
# 設置圖片大小
plt.figure(figsize=(10,20))
for i, k in enumerate(n_neighbors):
# 默認使用加權平均進行計算predictor
clf = KNeighborsRegressor(n_neighbors=k, p=2, metric="minkowski")
# 訓練
clf.fit(X, y)
# 預測
y_ = clf.predict(T)
plt.subplot(6, 1, i + 1)
plt.scatter(X, y, color='red', label='data')
plt.plot(T, y_, color='navy', label='prediction')
plt.axis('tight')
plt.legend()
plt.title("KNeighborsRegressor (k = %i)" % (k))
plt.tight_layout()
plt.show()
Step4:模型分析
當k=1時桨踪,預測的結(jié)果只和最近的一個訓練樣本相關,從預測曲線中可以看出當k很小時候很容易發(fā)生過擬合芹啥。
當k=40時锻离,預測的結(jié)果和最近的40個樣本相關,因為我們只有40個樣本墓怀,此時是所有樣本的平均值汽纠,此時所有預測值都是均值,很容易發(fā)生欠擬合傀履。
一般情況下虱朵,使用knn的時候,根據(jù)數(shù)據(jù)規(guī)模我們會從[3, 20]之間進行嘗試,選擇最好的k碴犬,例如上圖中的[3, 10]相對1和40都是還不錯的選擇絮宁。
2.4.4 馬絞痛數(shù)據(jù)--kNN數(shù)據(jù)預處理+kNN分類pipeline
# 下載需要用到的數(shù)據(jù)集
!wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.csv
# 下載數(shù)據(jù)集介紹
!wget https://tianchi-media.oss-cn-beijing.aliyuncs.com/DSW/3K/horse-colic.names
Step1: 庫函數(shù)導入
import numpy as np
import pandas as pd
# kNN分類器
from sklearn.neighbors import KNeighborsClassifier
# kNN數(shù)據(jù)空值填充
from sklearn.impute import KNNImputer
# 計算帶有空值的歐式距離
from sklearn.metrics.pairwise import nan_euclidean_distances
# 交叉驗證
from sklearn.model_selection import cross_val_score
# KFlod的函數(shù)
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.pipeline import Pipeline
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
Step2: 數(shù)據(jù)導入&分析
2,1,530101,38.50,66,28,3,3,?,2,5,4,4,?,?,?,3,5,45.00,8.40,?,?,2,2,11300,00000,00000,2
1,1,534817,39.2,88,20,?,?,4,1,3,4,2,?,?,?,4,2,50,85,2,2,3,2,02208,00000,00000,2
2,1,530334,38.30,40,24,1,1,3,1,3,3,1,?,?,?,1,1,33.00,6.70,?,?,1,2,00000,00000,00000,1
1,9,5290409,39.10,164,84,4,1,6,2,2,4,4,1,2,5.00,3,?,48.00,7.20,3,5.30,2,1,02208,00000,00000,1
2,1,530255,37.30,104,35,?,?,6,2,?,?,?,?,?,?,?,?,74.00,7.40,?,?,2,2,04300,00000,00000,2
......
數(shù)據(jù)集介紹:horse-colic.names
數(shù)據(jù)中的'?'表示空值,如果我們使用KNN分類器翅敌,'?'不能數(shù)值羞福,不能進行計算惕蹄,因此我們需要進行數(shù)據(jù)預處理對空值進行填充蚯涮。
這里我們使用KNNImputer進行空值填充,KNNImputer填充的原來很簡單卖陵,計算每個樣本最近的k個樣本遭顶,進行空值填充。
我們先來看下KNNImputer的運行原理:
Step3: KNNImputer空值填充--使用和原理介紹
X = [[1, 2, np.nan], [3, 4, 3], [np.nan, 6, 5], [8, 8, 7]]
imputer = KNNImputer(n_neighbors=2, metric='nan_euclidean')
imputer.fit_transform(X)
# Output:
# array([[1. , 2. , 4. ],
# [3. , 4. , 3. ],
# [5.5, 6. , 5. ],
# [8. , 8. , 7. ]])
帶有空值的歐式距離計算公式:
nan_euclidean_distances([[np.nan, 6, 5], [3, 4, 3]], [[3, 4, 3], [1, 2, np.nan], [8, 8, 7]])
# Output:
# array([[3.46410162, 6.92820323, 3.46410162],
# [0. , 3.46410162, 7.54983444]])
Step4: KNNImputer空值填充--歐式距離的計算
樣本[1, 2, np.nan] 最近的2個樣本是: [3, 4, 3] [np.nan, 6, 5], 計算距離的時候使用歐式距離泪蔫,只關注非空樣本棒旗。 [1, 2, np.nan] 填充之后得到 [1, 2, (3 + 5) / 2] = [1, 2, 4]
正常的歐式距離:
帶有空值的歐式聚類:
只計算所有非空的值,對所有空加權到非空值的計算上撩荣,上例中铣揉,我們看到一個有3維,只有第二維全部非空餐曹, 將第一維和第三維的計算加到第二維上逛拱,所有需要乘以3。
表格中距離度量使用的是帶有空值歐式距離計算相似度台猴,使用簡單的加權平均進行填充朽合。
# load dataset, 將?變成空值
input_file = './horse-colic.csv'
df_data = pd.read_csv(input_file, header=None, na_values='?')
# 得到訓練數(shù)據(jù)和label, 第23列表示是否發(fā)生病變, 1: 表示Yes; 2: 表示No.
data = df_data.values
ix = [i for i in range(data.shape[1]) if i != 23]
X, y = data[:, ix], data[:, 23]
# 查看所有特征的缺失值個數(shù)和缺失率
for i in range(df_data.shape[1]):
n_miss = df_data[[i]].isnull().sum()
perc = n_miss / df_data.shape[0] * 100
if n_miss.values[0] > 0:
print('>Feat: %d, Missing: %d, Missing ratio: (%.2f%%)' % (i, n_miss, perc))
# 查看總的空值個數(shù)
print('KNNImputer before Missing: %d' % sum(np.isnan(X).flatten()))
# 定義 knnimputer
imputer = KNNImputer()
# 填充數(shù)據(jù)集中的空值
imputer.fit(X)
# 轉(zhuǎn)換數(shù)據(jù)集
Xtrans = imputer.transform(X)
# 打印轉(zhuǎn)化后的數(shù)據(jù)集的空值
print('KNNImputer after Missing: %d' % sum(np.isnan(Xtrans).flatten()))
# Output:
# >Feat: 0, Missing: 1, Missing ratio: (0.33%)
# >Feat: 3, Missing: 60, Missing ratio: (20.00%)
# >Feat: 4, Missing: 24, Missing ratio: (8.00%)
# >Feat: 5, Missing: 58, Missing ratio: (19.33%)
# >Feat: 6, Missing: 56, Missing ratio: (18.67%)
# >Feat: 7, Missing: 69, Missing ratio: (23.00%)
# >Feat: 8, Missing: 47, Missing ratio: (15.67%)
# >Feat: 9, Missing: 32, Missing ratio: (10.67%)
# >Feat: 10, Missing: 55, Missing ratio: (18.33%)
# >Feat: 11, Missing: 44, Missing ratio: (14.67%)
# >Feat: 12, Missing: 56, Missing ratio: (18.67%)
# >Feat: 13, Missing: 104, Missing ratio: (34.67%)
# >Feat: 14, Missing: 106, Missing ratio: (35.33%)
# >Feat: 15, Missing: 247, Missing ratio: (82.33%)
# >Feat: 16, Missing: 102, Missing ratio: (34.00%)
# >Feat: 17, Missing: 118, Missing ratio: (39.33%)
# >Feat: 18, Missing: 29, Missing ratio: (9.67%)
# >Feat: 19, Missing: 33, Missing ratio: (11.00%)
# >Feat: 20, Missing: 165, Missing ratio: (55.00%)
# >Feat: 21, Missing: 198, Missing ratio: (66.00%)
# >Feat: 22, Missing: 1, Missing ratio: (0.33%)
# KNNImputer before Missing: 1605
# KNNImputer after Missing: 0
Step5: 基于pipeline模型訓練&可視化
什么是Pipeline, 我這里直接翻譯成數(shù)據(jù)管道。任何有序的操作有可以看做pipeline饱狂,例如工廠流水線曹步,對于機器學習模型來說,這就是數(shù)據(jù)流水線休讳。 是指數(shù)據(jù)通過管道中的每一個節(jié)點讲婚,結(jié)果除了之后,繼續(xù)流向下游俊柔。對于我們這個例子筹麸,數(shù)據(jù)是有空值,我們會有一個KNNImputer節(jié)點用來填充空值婆咸, 之后繼續(xù)流向下一個kNN分類節(jié)點竹捉,最后輸出模型。
results = list()
strategies = [str(i) for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 18, 20, 21]]
for s in strategies:
# create the modeling pipeline
pipe = Pipeline(steps=[('imputer', KNNImputer(n_neighbors=int(s))), ('model', KNeighborsClassifier())])
# 數(shù)據(jù)多次隨機劃分取平均得分
scores = []
for k in range(20):
# 得到訓練集合和驗證集合, 8: 2
X_train, X_test, y_train, y_test = train_test_split(Xtrans, y, test_size=0.2)
pipe.fit(X_train, y_train)
# 驗證model
score = pipe.score(X_test, y_test)
scores.append(score)
# 保存results
results.append(np.array(scores))
print('>k: %s, Acc Mean: %.3f, Std: %.3f' % (s, np.mean(scores), np.std(scores)))
# print(results)
# plot model performance for comparison
plt.boxplot(results, labels=strategies, showmeans=True)
plt.show()
# Output:
# >k: 1, Acc Mean: 0.800, Std: 0.031
# >k: 2, Acc Mean: 0.821, Std: 0.041
# >k: 3, Acc Mean: 0.833, Std: 0.053
# >k: 4, Acc Mean: 0.824, Std: 0.037
# >k: 5, Acc Mean: 0.802, Std: 0.038
# >k: 6, Acc Mean: 0.811, Std: 0.030
# >k: 7, Acc Mean: 0.797, Std: 0.056
# >k: 8, Acc Mean: 0.819, Std: 0.044
# >k: 9, Acc Mean: 0.820, Std: 0.032
# >k: 10, Acc Mean: 0.815, Std: 0.046
# >k: 15, Acc Mean: 0.818, Std: 0.037
# >k: 16, Acc Mean: 0.811, Std: 0.048
# >k: 18, Acc Mean: 0.809, Std: 0.043
# >k: 20, Acc Mean: 0.810, Std: 0.038
# >k: 21, Acc Mean: 0.828, Std: 0.038
Step 6: 結(jié)果分析
我們的實驗是每個k值下尚骄,隨機切分20次數(shù)據(jù), 從上述的圖片中, 根據(jù)k值的增加块差,我們的測試準確率會有先上升再下降再上升的過程。 [3, 5]之間是一個很好的取值,上文我們提到憨闰,k很小的時候會發(fā)生過擬合状蜗,k很大時候會發(fā)生欠擬合,當遇到第一下降節(jié)點鹉动,此時我們可以 簡單認為不在發(fā)生過擬合轧坎,取當前的k值即可。
2.5 KNN原理介紹
k近鄰方法是一種惰性學習算法泽示,可以用于回歸和分類缸血,它的主要思想是投票機制,對于一個測試實例x, 我們在有標簽的訓練數(shù)據(jù)集上找到和最相近的k個數(shù)據(jù)械筛,用他們的label進行投票捎泻,分類問題則進行表決投票,回歸問題使用加權平均或者直接平均的方法埋哟。knn算法中我們最需要關注兩個問題:k值的選擇和距離的計算笆豁。 kNN中的k是一個超參數(shù),需要我們進行指定赤赊,一般情況下這個k和數(shù)據(jù)有很大關系闯狱,都是交叉驗證進行選擇,但是建議使用交叉驗證的時候抛计,k∈[2,20]哄孤,使用交叉驗證得到一個很好的k值。
k值還可以表示我們的模型復雜度爷辱,當k值越小意味著模型復雜度變大录豺,更容易過擬合,(用極少數(shù)的樣例來絕對這個預測的結(jié)果饭弓,很容易產(chǎn)生偏見双饥,這就是過擬合)。我們有這樣一句話弟断,k值越多學習的估計誤差越小咏花,但是學習的近似誤差就會增大。
距離/相似度的計算:
樣本之間的距離的計算阀趴,我們一般使用對于一般使用Lp距離進行計算昏翰。當p=1時候,稱為曼哈頓距離(Manhattan distance)刘急,當p=2時候棚菊,稱為歐氏距離(Euclidean distance),當p=∞時候叔汁,稱為極大距離(infty distance), 表示各個坐標的距離最大值,另外也包含夾角余弦等方法统求。
一般采用歐式距離較多检碗,但是文本分類則傾向于使用余弦來計算相似度。
學習感悟:
好難奥肓凇折剃!這部分沒有學明白~~~