機器學習入門(2)KNN原理與KNN迴歸(附程式碼)
1。KNN原理
K-Nearest Neighbors
很簡單,看圖就一目瞭然了。
綠色和藍色是已知資料,根據已知資料,我們想要知道,紅色的點屬於哪一類。
這是,我們選擇的方法是:看距離紅色最近的一個點(K=1)是屬於哪一類,或者看距離紅色最近的兩個個點(K=1)或三個點(K=3)是屬於哪一類,此時,我們需要在這些點裡做投票,看看這個區域內,哪個顏色的點數量多。
我們以K=3為例來看:
在選擇距離紅色點最近的三個點作為參考時,綠色佔兩個,藍色佔一個,這是,我們就將紅色歸為綠色一類。
這也告訴我了我們,K在通常情況下要選擇奇數,否則很容易出現兩種情況數量相等的情況(如K為4,可能會出現兩藍兩綠的情況)
2。KNN演算法
KNN具體的實現方法
1。特徵工程,Feature Engineering,把物體用向量、矩陣、張量的形式表示出來
2。標記號:每個物體的標籤
3。計算兩個物體之間的距離,最常用的就是歐式距離:
4。選擇合適的K
首先,要理解什麼是決策邊界(非常重要):
決策邊界決定線性分類器和非線性分類器。
決策邊界不可以過於陡峭(過擬合)
看一個小例子
根據距離橙/綠點的距離,填充背景。那麼很明顯,在右圖我們可以看到清晰的決策邊界。
用交叉驗證方法(Cross Validation)選擇K:
將所有資料分為訓練資料(綠色)和測試資料(橙色),預設為75%和25%,可以自行調整。
交叉驗證是將訓練資料再次分配,我們以5折為例,就是說將交叉資料分成五份,每次都選取不同的資料作為驗證資料(藍色)。
首先驗證K=1
每一組不同的驗證資料都會得出一個準確度,求得五組準確度的平均值,就是K=1情況下的準確度。
同理,當K=3時,方法同上,求出平均準確度
求出兩個準確度之後,看哪一個準確度更高,說明哪一個K值取得好。當然在實際中,交叉驗證的不會只有K=1K=3兩個值,經常都是交叉驗證很多個K值,取其中準確度最高的為我們需要的K值。
當資料量少的時候,可以適當增加折數。
切記:不能用測試資料(橙色)來調參(K)!!!!!!!!
5。其他需要注意的地方
特徵的縮放:特徵中量綱差距過大,導致其中一個或幾個特徵在計算中基本起不到作用,這是我們需要特徵的縮放。方法如下:
線性歸一化(Min-Max-Normalization):一般情況Min=0,Max=1
公式為:
標準差標準化(Z-Score-Normalization):
公式為:
,其中,mean(x)為平均值,std(x)為標準差
3。KNN程式碼
詳細請看註釋
1。 呼叫KNN函式來實現分類
資料採用的是經典的iris資料,是三分類問題
# 讀取相應的庫
from
sklearn
import
datasets
#sklearn是自帶的資料
from
sklearn。model_selection
import
train_test_split
#訓練測試分類
from
sklearn。neighbors
import
KNeighborsClassifier
#KNN模型
import
numpy
as
np
# 讀取資料 X, y
iris
=
datasets
。
load_iris
()
#讀取資料
X
=
iris
。
data
#四維向量,特徵
y
=
iris
。
target
#分類:三分類,結果為0,1,2。要拖到結果的最下面才能看到
(
X
,
y
)
#列印資料
[[
5。1
3。5
1。4
0。2
]
[
4。9
3。
1。4
0。2
]
[
4。7
3。2
1。3
0。2
]
[
4。6
3。1
1。5
0。2
]
[
5。
3。6
1。4
0。2
]
[
5。4
3。9
1。7
0。4
]
[
4。6
3。4
1。4
0。3
]
[
5。
3。4
1。5
0。2
]
[
4。4
2。9
1。4
0。2
]
[
4。9
3。1
1。5
0。1
]
[
5。4
3。7
1。5
0。2
]
[
4。8
3。4
1。6
0。2
]
[
4。8
3。
1。4
0。1
]
[
4。3
3。
1。1
0。1
]
[
5。8
4。
1。2
0。2
]
[
5。7
4。4
1。5
0。4
]
[
5。4
3。9
1。3
0。4
]
[
5。1
3。5
1。4
0。3
]
[
5。7
3。8
1。7
0。3
]
[
5。1
3。8
1。5
0。3
]
[
5。4
3。4
1。7
0。2
]
[
5。1
3。7
1。5
0。4
]
[
4。6
3。6
1。
0。2
]
[
5。1
3。3
1。7
0。5
]
[
4。8
3。4
1。9
0。2
]
[
5。
3。
1。6
0。2
]
[
5。
3。4
1。6
0。4
]
[
5。2
3。5
1。5
0。2
]
[
5。2
3。4
1。4
0。2
]
[
4。7
3。2
1。6
0。2
]
[
4。8
3。1
1。6
0。2
]
[
5。4
3。4
1。5
0。4
]
[
5。2
4。1
1。5
0。1
]
[
5。5
4。2
1。4
0。2
]
[
4。9
3。1
1。5
0。2
]
[
5。
3。2
1。2
0。2
]
[
5。5
3。5
1。3
0。2
]
[
4。9
3。6
1。4
0。1
]
[
4。4
3。
1。3
0。2
]
[
5。1
3。4
1。5
0。2
]
[
5。
3。5
1。3
0。3
]
[
4。5
2。3
1。3
0。3
]
[
4。4
3。2
1。3
0。2
]
[
5。
3。5
1。6
0。6
]
[
5。1
3。8
1。9
0。4
]
[
4。8
3。
1。4
0。3
]
[
5。1
3。8
1。6
0。2
]
[
4。6
3。2
1。4
0。2
]
[
5。3
3。7
1。5
0。2
]
[
5。
3。3
1。4
0。2
]
[
7。
3。2
4。7
1。4
]
[
6。4
3。2
4。5
1。5
]
[
6。9
3。1
4。9
1。5
]
[
5。5
2。3
4。
1。3
]
[
6。5
2。8
4。6
1。5
]
[
5。7
2。8
4。5
1。3
]
[
6。3
3。3
4。7
1。6
]
[
4。9
2。4
3。3
1。
]
[
6。6
2。9
4。6
1。3
]
[
5。2
2。7
3。9
1。4
]
[
5。
2。
3。5
1。
]
[
5。9
3。
4。2
1。5
]
[
6。
2。2
4。
1。
]
[
6。1
2。9
4。7
1。4
]
[
5。6
2。9
3。6
1。3
]
[
6。7
3。1
4。4
1。4
]
[
5。6
3。
4。5
1。5
]
[
5。8
2。7
4。1
1。
]
[
6。2
2。2
4。5
1。5
]
[
5。6
2。5
3。9
1。1
]
[
5。9
3。2
4。8
1。8
]
[
6。1
2。8
4。
1。3
]
[
6。3
2。5
4。9
1。5
]
[
6。1
2。8
4。7
1。2
]
[
6。4
2。9
4。3
1。3
]
[
6。6
3。
4。4
1。4
]
[
6。8
2。8
4。8
1。4
]
[
6。7
3。
5。
1。7
]
[
6。
2。9
4。5
1。5
]
[
5。7
2。6
3。5
1。
]
[
5。5
2。4
3。8
1。1
]
[
5。5
2。4
3。7
1。
]
[
5。8
2。7
3。9
1。2
]
[
6。
2。7
5。1
1。6
]
[
5。4
3。
4。5
1。5
]
[
6。
3。4
4。5
1。6
]
[
6。7
3。1
4。7
1。5
]
[
6。3
2。3
4。4
1。3
]
[
5。6
3。
4。1
1。3
]
[
5。5
2。5
4。
1。3
]
[
5。5
2。6
4。4
1。2
]
[
6。1
3。
4。6
1。4
]
[
5。8
2。6
4。
1。2
]
[
5。
2。3
3。3
1。
]
[
5。6
2。7
4。2
1。3
]
[
5。7
3。
4。2
1。2
]
[
5。7
2。9
4。2
1。3
]
[
6。2
2。9
4。3
1。3
]
[
5。1
2。5
3。
1。1
]
[
5。7
2。8
4。1
1。3
]
[
6。3
3。3
6。
2。5
]
[
5。8
2。7
5。1
1。9
]
[
7。1
3。
5。9
2。1
]
[
6。3
2。9
5。6
1。8
]
[
6。5
3。
5。8
2。2
]
[
7。6
3。
6。6
2。1
]
[
4。9
2。5
4。5
1。7
]
[
7。3
2。9
6。3
1。8
]
[
6。7
2。5
5。8
1。8
]
[
7。2
3。6
6。1
2。5
]
[
6。5
3。2
5。1
2。
]
[
6。4
2。7
5。3
1。9
]
[
6。8
3。
5。5
2。1
]
[
5。7
2。5
5。
2。
]
[
5。8
2。8
5。1
2。4
]
[
6。4
3。2
5。3
2。3
]
[
6。5
3。
5。5
1。8
]
[
7。7
3。8
6。7
2。2
]
[
7。7
2。6
6。9
2。3
]
[
6。
2。2
5。
1。5
]
[
6。9
3。2
5。7
2。3
]
[
5。6
2。8
4。9
2。
]
[
7。7
2。8
6。7
2。
]
[
6。3
2。7
4。9
1。8
]
[
6。7
3。3
5。7
2。1
]
[
7。2
3。2
6。
1。8
]
[
6。2
2。8
4。8
1。8
]
[
6。1
3。
4。9
1。8
]
[
6。4
2。8
5。6
2。1
]
[
7。2
3。
5。8
1。6
]
[
7。4
2。8
6。1
1。9
]
[
7。9
3。8
6。4
2。
]
[
6。4
2。8
5。6
2。2
]
[
6。3
2。8
5。1
1。5
]
[
6。1
2。6
5。6
1。4
]
[
7。7
3。
6。1
2。3
]
[
6。3
3。4
5。6
2。4
]
[
6。4
3。1
5。5
1。8
]
[
6。
3。
4。8
1。8
]
[
6。9
3。1
5。4
2。1
]
[
6。7
3。1
5。6
2。4
]
[
6。9
3。1
5。1
2。3
]
[
5。8
2。7
5。1
1。9
]
[
6。8
3。2
5。9
2。3
]
[
6。7
3。3
5。7
2。5
]
[
6。7
3。
5。2
2。3
]
[
6。3
2。5
5。
1。9
]
[
6。5
3。
5。2
2。
]
[
6。2
3。4
5。4
2。3
]
[
5。9
3。
5。1
1。8
]]
[
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
]
# 把資料分成訓練資料和測試資料
X_train
,
X_test
,
y_train
,
y_test
=
train_test_split
(
X
,
y
,
random_state
=
2003
)
#2003是種子點,確保每次的訓練資料和測試資料是一樣的,如果不選種子點,每次生成的資料不一樣,無法判斷我們的訓練結果是否準確
# 構建KNN模型, K值為3、 並做訓練
clf
=
KNeighborsClassifier
(
n_neighbors
=
3
)
clf
。
fit
(
X_train
,
y_train
)
#fit函式,相當於把資料儲存下來,記憶體分配,不對資料進行訓練
KNeighborsClassifier
(
algorithm
=
‘auto’
,
leaf_size
=
30
,
metric
=
‘minkowski’
,
metric_params
=
None
,
n_jobs
=
None
,
n_neighbors
=
3
,
p
=
2
,
weights
=
‘uniform’
)
# 計算準確率
from
sklearn。metrics
import
accuracy_score
correct
=
np
。
count_nonzero
((
clf
。
predict
(
X_test
)
==
y_test
)
==
True
)
#accuracy_score(y_test, clf。predict(X_test))
(
“Accuracy is:
%。3f
”
%
(
correct
/
len
(
X_test
)))
Accuracy
is
:
0。921
2。從零開始自己寫一個KNN演算法
from
sklearn
import
datasets
from
collections
import
Counter
# 為了做投票
from
sklearn。model_selection
import
train_test_split
import
numpy
as
np
# 匯入iris資料
iris
=
datasets
。
load_iris
()
X
=
iris
。
data
y
=
iris
。
target
X_train
,
X_test
,
y_train
,
y_test
=
train_test_split
(
X
,
y
,
random_state
=
2003
)
def
euc_dis
(
instance1
,
instance2
):
“”“
計算兩個樣本instance1和instance2之間的歐式距離
instance1: 第一個樣本, array型
instance2: 第二個樣本, array型
”“”
# TODO
dist
=
np
。
sqrt
(
sum
((
instance1
-
instance2
)
**
2
))
return
dist
def
knn_classify
(
X
,
y
,
testInstance
,
k
):
“”“
給定一個測試資料testInstance, 透過KNN演算法來預測它的標籤。
X: 訓練資料的特徵
y: 訓練資料的標籤
testInstance: 測試資料,這裡假定一個測試資料 array型
k: 選擇多少個neighbors?
”“”
# TODO 返回testInstance的預測標籤 = {0,1,2}
#時間複雜度 O(N): N:number of samples
distances
=
[
euc_dis
(
x
,
testInstance
)
for
x
in
X
]
#若N很大,計算歐式距離消耗的測試時間非常大的時間,take forever~ (KNN的缺點)
#argsort: O(NlogN)
#如何最佳化? priority queue O(NlogK)
kneighbors
=
np
。
argsort
(
distances
)[:
k
]
count
=
Counter
(
y
[
kneighbors
])
#print(count)可以顯示出來每個樣本出現了多少次
return
count
。
most_common
()[
0
][
0
]
#取出出現最多的值
# 預測結果。
predictions
=
[
knn_classify
(
X_train
,
y_train
,
data
,
3
)
for
data
in
X_test
]
correct
=
np
。
count_nonzero
((
predictions
==
y_test
)
==
True
)
#accuracy_score(y_test, clf。predict(X_test))
(
“Accuracy is:
%。3f
”
%
(
correct
/
len
(
X_test
)))
Accuracy
is
:
0。921
3。KNN的決策邊界
import
matplotlib。pyplot
as
plt
import
numpy
as
np
from
itertools
import
product
from
sklearn。neighbors
import
KNeighborsClassifier
# 生成一些隨機樣本
n_points
=
100
X1
=
np
。
random
。
multivariate_normal
([
1
,
50
],
[[
1
,
0
],[
0
,
10
]],
n_points
)
X2
=
np
。
random
。
multivariate_normal
([
2
,
50
],
[[
1
,
0
],[
0
,
10
]],
n_points
)
X
=
np
。
concatenate
([
X1
,
X2
])
y
=
np
。
array
([
0
]
*
n_points
+
[
1
]
*
n_points
)
(
X
。
shape
,
y
。
shape
)
(
200
,
2
)
(
200
,)
# KNN模型的訓練過程
clfs
=
[]
neighbors
=
[
1
,
3
,
5
,
9
,
11
,
13
,
15
,
17
,
19
]
for
i
in
range
(
len
(
neighbors
)):
clfs
。
append
(
KNeighborsClassifier
(
n_neighbors
=
neighbors
[
i
])
。
fit
(
X
,
y
))
# 視覺化結果
x_min
,
x_max
=
X
[:,
0
]
。
min
()
-
1
,
X
[:,
0
]
。
max
()
+
1
y_min
,
y_max
=
X
[:,
1
]
。
min
()
-
1
,
X
[:,
1
]
。
max
()
+
1
xx
,
yy
=
np
。
meshgrid
(
np
。
arange
(
x_min
,
x_max
,
0。1
),
np
。
arange
(
y_min
,
y_max
,
0。1
))
#print(xx,yy),瞭解meshgrid函式
f
,
axarr
=
plt
。
subplots
(
3
,
3
,
sharex
=
‘col’
,
sharey
=
‘row’
,
figsize
=
(
15
,
12
))
for
idx
,
clf
,
tt
in
zip
(
product
([
0
,
1
,
2
],
[
0
,
1
,
2
]),
clfs
,
[
‘KNN (k=
%d
)’
%
k
for
k
in
neighbors
]):
Z
=
clf
。
predict
(
np
。
c_
[
xx
。
ravel
(),
yy
。
ravel
()])
Z
=
Z
。
reshape
(
xx
。
shape
)
axarr
[
idx
[
0
],
idx
[
1
]]
。
contourf
(
xx
,
yy
,
Z
,
alpha
=
0。4
)
axarr
[
idx
[
0
],
idx
[
1
]]
。
scatter
(
X
[:,
0
],
X
[:,
1
],
c
=
y
,
s
=
20
,
edgecolor
=
‘k’
)
axarr
[
idx
[
0
],
idx
[
1
]]
。
set_title
(
tt
)
plt
。
show
()
#K=1 不平緩,陡峭,不穩定
#K越大,決策邊界越平滑smooth
4。KNN迴歸
import
pandas
as
pd
import
matplotlib
import
matplotlib。pyplot
as
plt
import
numpy
as
np
import
seaborn
as
sns
#預測二手車價格
#讀取資料
df
=
pd
。
read_csv
(
‘data。csv’
)
df
# data frame
#清洗資料
# 把顏色獨熱編碼
df_colors
=
df
[
‘Color’
]
。
str
。
get_dummies
()
。
add_prefix
(
‘Color: ’
)
# 把型別獨熱編碼
df_type
=
df
[
‘Type’
]
。
apply
(
str
)
。
str
。
get_dummies
()
。
add_prefix
(
‘Type: ’
)
# 新增獨熱編碼資料列
df
=
pd
。
concat
([
df
,
df_colors
,
df_type
],
axis
=
1
)
# 去除獨熱編碼對應的原始列
df
=
df
。
drop
([
‘Brand’
,
‘Type’
,
‘Color’
],
axis
=
1
)
df
# 資料轉換
matrix
=
df
。
corr
()
f
,
ax
=
plt
。
subplots
(
figsize
=
(
8
,
6
))
sns
。
heatmap
(
matrix
,
square
=
True
)
plt
。
title
(
‘Car Price Variables’
)
Text
(
0。5
,
1。0
,
‘Car Price Variables’
)
#sns。pairplot(df[[‘Construction Year’, ‘Days Until MOT’, ‘Odometer’, ‘Ask Price’]], size=2)
#plt。show()
from
sklearn。neighbors
import
KNeighborsRegressor
from
sklearn。model_selection
import
train_test_split
from
sklearn
import
preprocessing
from
sklearn。preprocessing
import
StandardScaler
import
numpy
as
np
X
=
df
[[
‘Construction Year’
,
‘Days Until MOT’
,
‘Odometer’
]]
y
=
df
[
‘Ask Price’
]
。
values
。
reshape
(
-
1
,
1
)
X_train
,
X_test
,
y_train
,
y_test
=
train_test_split
(
X
,
y
,
test_size
=
0。3
,
random_state
=
41
)
X_normalizer
=
StandardScaler
()
# N(0,1)
X_train
=
X_normalizer
。
fit_transform
(
X_train
)
X_test
=
X_normalizer
。
transform
(
X_test
)
y_normalizer
=
StandardScaler
()
y_train
=
y_normalizer
。
fit_transform
(
y_train
)
y_test
=
y_normalizer
。
transform
(
y_test
)
knn
=
KNeighborsRegressor
(
n_neighbors
=
2
)
#K=2
knn
。
fit
(
X_train
,
y_train
。
ravel
())
#Now we can predict prices:
y_pred
=
knn
。
predict
(
X_test
)
y_pred_inv
=
y_normalizer
。
inverse_transform
(
y_pred
)
y_test_inv
=
y_normalizer
。
inverse_transform
(
y_test
)
# Build a plot
plt
。
scatter
(
y_pred_inv
,
y_test_inv
)
plt
。
xlabel
(
‘Prediction’
)
plt
。
ylabel
(
‘Real value’
)
# Now add the perfect prediction line
diagonal
=
np
。
linspace
(
500
,
1500
,
100
)
plt
。
plot
(
diagonal
,
diagonal
,
‘-r’
)
plt
。
xlabel
(
‘Predicted ask price’
)
plt
。
ylabel
(
‘Ask price’
)
plt
。
show
()
(
y_pred_inv
)
[1199。 1199。 700。 899。]
knn
KNeighborsRegressor(algorithm=‘auto’, leaf_size=30, metric=‘minkowski’,
metric_params=None, n_jobs=None, n_neighbors=2, p=2,
weights=‘uniform’)
pred = knn。predict(X_test)
pred
array([ 1。36676513, 1。36676513, -0。68269804, 0。13462294])
from sklearn。metrics import mean_absolute_error
mean_absolute_error(y_pred_inv, y_test_inv)
175。5
from sklearn。metrics import mean_squared_error
mean_squared_error(y_pred_inv, y_test_inv)
56525。5
y_pred_inv
array([1199。, 1199。, 700。, 899。])
y_test_inv
array([[1300。],
[1650。],
[ 650。],
[ 799。]])
如果有興趣,可以關注我的公眾號(圈圈小姐),程式設計小白不定期分享自己的學習成果(以及生活旅行和德語小知識點),大家一起進步~~