K-近邻算法(KNN)

K-近邻算法(KNN)

K nearest neighbour

0、导引

如何进行电影分类

众所周知,电影可以按照题材分类,然而题材本身是如何定义的?由谁来判定某部电影属于哪
个题材?也就是说同一题材的电影具有哪些公共特征?这些都是在进行电影分类时必须要考虑的问
题。没有哪个电影人会说自己制作的电影和以前的某部电影类似,但我们确实知道每部电影在风格
上的确有可能会和同题材的电影相近。那么动作片具有哪些共有特征,使得动作片之间非常类似,
而与爱情片存在着明显的差别呢?动作片中也会存在接吻镜头,爱情片中也会存在打斗场景,我们
不能单纯依靠是否存在打斗或者亲吻来判断影片的类型。但是爱情片中的亲吻镜头更多,动作片中
的打斗场景也更频繁,基于此类场景在某部电影中出现的次数可以用来进行电影分类。

本章介绍第一个机器学习算法:K-近邻算法,它非常有效而且易于掌握。

1、k-近邻算法原理

简单地说,K-近邻算法采用测量不同特征值之间的距离方法进行分类。

  • 优点:精度高、对异常值不敏感、无数据输入假定。
  • 缺点:时间复杂度高、空间复杂度高。
  • 适用数据范围:数值型和标称型。

工作原理

存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据
与所属分类的对应关系。输人没有标签的新数据后,将新数据的每个特征与样本集中数据对应的
特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们
只选择样本数据集中前K个最相似的数据,这就是K-近邻算法中K的出处,通常K是不大于20的整数。
最后 ,选择K个最相似数据中出现次数最多的分类,作为新数据的分类

回到前面电影分类的例子,使用K-近邻算法分类爱情片和动作片。有人曾经统计过很多电影的打斗镜头和接吻镜头,下图显示了6部电影的打斗和接吻次数。假如有一部未看过的电影,如何确定它是爱情片还是动作片呢?我们可以使用K-近邻算法来解决这个问题。

1

首先我们需要知道这个未知电影存在多少个打斗镜头和接吻镜头,上图中问号位置是该未知电影出现的镜头数图形化展示,具体数字参见下表。

2

即使不知道未知电影属于哪种类型,我们也可以通过某种方法计算出来。首先计算未知电影与样本集中其他电影的距离,如图所示。

3

现在我们得到了样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到K个距
离最近的电影。假定k=3,则三个最靠近的电影依次是California Man、He’s Not Really into Dudes、Beautiful Woman。K-近邻算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。

欧几里得距离(Euclidean Distance)

欧氏距离是最常见的距离度量,衡量的是多维空间中各个点之间的绝对距离。公式如下:

4

import numpy as np
import pandas as pd

from sklearn.neighbors import KNeighborsClassifier
movie =pd.read_excel('../data/tests.xlsx',sheet_name=1)
movie
电影名称武打镜头接吻镜头分类情况
0大话西游361动作片
1杀破狼432动作片
2前任3010爱情片
3战狼2591动作片
4泰坦尼克号115爱情片
5星语心愿219爱情片
# X 是数据
X = movie[['武打镜头','接吻镜头']]
X
武打镜头接吻镜头
0361
1432
2010
3591
4115
5219
# Y 是目标值
Y =movie['分类情况']
Y
0    动作片
1    动作片
2    爱情片
3    动作片
4    爱情片
5    爱情片
Name: 分类情况, dtype: object
knn=KNeighborsClassifier(n_neighbors=5)

#该方法就是训练数据
knn.fit(X,Y)
# 预测新电影
#碟中谍6 100 ,3
# 战狼2  200 ,1
#山楂树  0   ,10
X_test=pd.DataFrame({'武打镜头':[100,200,0],"接吻镜头":[3,1,10]})
X_test
武打镜头接吻镜头
01003
12001
2010
knn.predict(X_test)
#array(['动作片', '动作片', '爱情片'], dtype=object)
# 算法推理

s=((movie['武打镜头'] - 100 )**2  +(movie['接吻镜头'] - 3)**2) **0.5
index=s.sort_values().index
index
#Int64Index([3, 1, 0, 5, 4, 2], dtype='int64')
movie['分类情况'][index[:5]]
3    动作片
1    动作片
0    动作片
5    爱情片
4    爱情片
Name: 分类情况, dtype: object
#  碟中谍6是动作片的概率是60% ,爱情片概率是40%
knn.predict_proba(X_test)
#array([[0.6, 0.4],
       [0.6, 0.4],
       [0.4, 0.6]])

image-20230221061125969

2、在scikit-learn库中使用k-近邻算法

  • 分类问题:from sklearn.neighbors import KNeighborsClassifier

  • 回归问题:from sklearn.neighbors import KNeighborsRegressor

0)一个最简单的例子

身高、体重、鞋子尺码数据对应性别

X_train = [[180, 180, 43], [170, 150, 39], [173, 128, 43], [170, 140, 39], [163, 90, 39], [164, 85, 36.5], [155, 75, 35], [172, 110, 41], [165, 114, 40], [175, 130, 43], [171, 135, 43], [160, 90, 36],[160, 90, 36], [158, 85, 36]]
y_train = ['男', '男', '男','男', '男', '女', '女', '女', '男', '男', '男', '女', '女', '女']
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
X_test = [[173, 118, 42], [173, 145, 41], [173, 120, 40], [165, 95, 40], [160, 120, 41], [158, 100, 36]]
knn.predict(X_test)
#array(['男', '男', '男', '女', '男', '女'], dtype='<U1')
knn.score(X_train,  y_train)
#0.8571428571428571
knn.score(X_test, ['男', '男', '女', '女', '男', '女'])
#0.8333333333333334

1)用于分类

导包,机器学习的算法KNN、数据鸢尾花、

from sklearn.datasets import load_iris
iris = load_iris()
iris

获取训练样本

data = iris['data']
target = iris['target']
X_train, X_test, y_train, y_test  = train_test_split(data, target, test_size=0.2)
knn = KNeighborsClassifier()
knn.fit(X_train, y_train).score(X_train, y_train)
knn.score(X_test, y_test)
#0.9666666666666667
knn.score(X_test, y_test)
#1.0
from pandas import DataFrame
# 从4个特征中选出两个特征来画散点图
df = DataFrame(data=data)
df.head()
0123
05.13.51.40.2
14.93.01.40.2
24.73.21.30.2
34.63.11.50.2
45.03.61.40.2
df.plot()

image-20230221061437949

# 选择前两个特征
data = data[:,0:2]
data.shape
#(150, 2)
plt.scatter(data[:,0], data[:,1], c=target)

image-20230221061500832

# 先生成线段上的范围
x, y = np.linspace(data[:,0].min(), data[:,0].max(), 1000), np.linspace(data[:,1].min(), data[:,1].max(), 1000)
# 范围拉起来变成2维平面
X,Y = np.meshgrid(x,y)
# 取两个平面相交的点
XY = np.c_[X.ravel(), Y.ravel()]
display(X, Y)
array([[4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       ...,
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ]])

array([[2.       , 2.       , 2.       , ..., 2.       , 2.       ,
        2.       ],
       [2.0024024, 2.0024024, 2.0024024, ..., 2.0024024, 2.0024024,
        2.0024024],
       [2.0048048, 2.0048048, 2.0048048, ..., 2.0048048, 2.0048048,
        2.0048048],
       ...,
       [4.3951952, 4.3951952, 4.3951952, ..., 4.3951952, 4.3951952,
        4.3951952],
       [4.3975976, 4.3975976, 4.3975976, ..., 4.3975976, 4.3975976,
        4.3975976],
       [4.4      , 4.4      , 4.4      , ..., 4.4      , 4.4      ,
        4.4      ]])
display(XY)
array([[4.3       , 2.        ],
       [4.3036036 , 2.        ],
       [4.30720721, 2.        ],
       ...,
       [7.89279279, 4.4       ],
       [7.8963964 , 4.4       ],
       [7.9       , 4.4       ]])
display(X)
display(X.ravel())
array([[4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       ...,
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ],
       [4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
        7.9       ]])

array([4.3       , 4.3036036 , 4.30720721, ..., 7.89279279, 7.8963964 ,
       7.9       ])
knn = KNeighborsClassifier()
knn.fit(data, target)
# 预测
y_ = knn.predict(XY)
y_[:100]
array([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, 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])
plt.scatter(XY[:,0], XY[:,1], c=y_)
plt.scatter(data[:,0], data[:,1], c=target, cmap='rainbow')

plt.pcolormesh(X, Y , y_.reshape(1000,1000))
plt.scatter(data[:,0], data[:,1], c=target, cmap='rainbow')

image-20230221061834858

绘制图形

定义KNN分类器

第一步,训练数据

第二步预测数据:,所预测的数据,自己创造,就是上面所显示图片的背景点

生成预测数据

对数据进行预测

显示数据

2)用于回归

回归用于对趋势的预测

x = np.random.rand(100) * 10 
y = np.sin(x)
plt.scatter(x,y)

image-20230221061909302

# 加点噪声
y[::4] += np.random.randn(25) * 0.3 
plt.scatter(x,y)

image-20230221061928498

from sklearn.neighbors import KNeighborsRegressor
knn = KNeighborsRegressor()
knn.fit(x.reshape(-1,1), y)

X_test = np.linspace(0, 10, 1000).reshape(-1,1)
y_ = knn.predict(X_test)
plt.scatter(x,y)
plt.plot(X_test, y_, c='r')

image-20230221061952798

导包

生成样本数据

生成测试数据的结果

第一步:生成模型,并训练数据

第二步:使用模型,预测数据

绘图显示数据

练习

人类动作识别
步行,上楼,下楼,坐着,站立和躺着

数据采集每个人在腰部穿着智能手机,进行了六个活动(步行,上楼,下楼,坐着,站立和躺着)。采用嵌入式加速度计和陀螺仪,以50Hz的恒定速度捕获3轴线性加速度和3轴角速度,来获取数据


导入数据

X_train = np.load('../data/动作分析/x_train.npy')
X_test = np.load('../data/动作分析/x_test.npy')
y_train = np.load('../data/动作分析/y_train.npy')
y_test = np.load('../data/动作分析/y_test.npy')
X_train.shape
#(7352, 561)
X_test.shape
#(2947, 561)
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
knn.score(X_train, y_train)
#0.9843579978237215
y_ = knn.predict(X_test)
knn.score(X_test,y_test)
#0.9015948422124194
y_
#array([5, 5, 5, ..., 2, 2, 1], dtype=int64)
label = {1:'WALKING', 2:'WALKING UPSTAIRS', 3:'WALKING DOWNSTAIRS',4:'SITTING', 5:'STANDING', 6:'LAYING'}
# 可视化预测结果
# 6行4列  24个
plt.figure(figsize=(4*4, 5 * 6))
for i in range(24):
    axes = plt.subplot(6,4,i+1)
    axes.plot(X_test[i*100])
    
    if y_test[i*100] != y_[i*100]:
        axes.set_title('True: %s\nPredict:%s'% (label[y_test[i*100]], label[y_[i*100]]), fontdict=dict(fontsize=20, color='r'))
    axes.set_title('True: %s\nPredict:%s'% (label[y_test[i*100]], label[y_[i*100]]))
    

获取数据

绘制

癌症预测

# 知识点,交叉表
cancer = pd.read_csv('../data/cancer.csv', sep='\t')
cancer.head(20)
# B : benign
# M : malignant
IDDiagnosisradius_meantexture_meanperimeter_meanarea_meansmoothness_meancompactness_meanconcavity_meanconcave_mean...radius_maxtexture_maxperimeter_maxarea_maxsmoothness_maxcompactness_maxconcavity_maxconcave_maxsymmetry_maxfractal_max
0842302M17.9910.38122.801001.00.118400.277600.300100.14710...25.3817.33184.602019.00.16220.66560.71190.265400.46010.11890
1842517M20.5717.77132.901326.00.084740.078640.086900.07017...24.9923.41158.801956.00.12380.18660.24160.186000.27500.08902
284300903M19.6921.25130.001203.00.109600.159900.197400.12790...23.5725.53152.501709.00.14440.42450.45040.243000.36130.08758
384348301M11.4220.3877.58386.10.142500.283900.241400.10520...14.9126.5098.87567.70.20980.86630.68690.257500.66380.17300
484358402M20.2914.34135.101297.00.100300.132800.198000.10430...22.5416.67152.201575.00.13740.20500.40000.162500.23640.07678
5843786M12.4515.7082.57477.10.127800.170000.157800.08089...15.4723.75103.40741.60.17910.52490.53550.174100.39850.12440
6844359M18.2519.98119.601040.00.094630.109000.112700.07400...22.8827.66153.201606.00.14420.25760.37840.193200.30630.08368
784458202M13.7120.8390.20577.90.118900.164500.093660.05985...17.0628.14110.60897.00.16540.36820.26780.155600.31960.11510
8844981M13.0021.8287.50519.80.127300.193200.185900.09353...15.4930.73106.20739.30.17030.54010.53900.206000.43780.10720
984501001M12.4624.0483.97475.90.118600.239600.227300.08543...15.0940.6897.65711.40.18531.05801.10500.221000.43660.20750
10845636M16.0223.24102.70797.80.082060.066690.032990.03323...19.1933.88123.801150.00.11810.15510.14590.099750.29480.08452
1184610002M15.7817.89103.60781.00.097100.129200.099540.06606...20.4227.28136.501299.00.13960.56090.39650.181000.37920.10480
12846226M19.1724.80132.401123.00.097400.245800.206500.11180...20.9629.94151.701332.00.10370.39030.36390.176700.31760.10230
13846381M15.8523.95103.70782.70.084010.100200.099380.05364...16.8427.66112.00876.50.11310.19240.23220.111900.28090.06287
1484667401M13.7322.6193.60578.30.113100.229300.212800.08025...15.0332.01108.80697.70.16510.77250.69430.220800.35960.14310
1584799002M14.5427.5496.73658.80.113900.159500.163900.07364...17.4637.13124.10943.20.16780.65770.70260.171200.42180.13410
16848406M14.6820.1394.74684.50.098670.072000.073950.05259...19.0730.88123.401138.00.14640.18710.29140.160900.30290.08216
1784862001M16.1320.68108.10798.80.117000.202200.172200.10280...20.9631.48136.801315.00.17890.42330.47840.207300.37060.11420
18849014M19.8122.15130.001260.00.098310.102700.147900.09498...27.3230.88186.802398.00.15120.31500.53720.238800.27680.07615
198510426B13.5414.3687.46566.30.097790.081290.066640.04781...15.1119.2699.70711.20.14400.17730.23900.128800.29770.07259

20 rows × 32 columns

data = cancer.iloc[:,  2:]
target = cancer.Diagnosis
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=69)
cancer.shape
#(569, 32)
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
y_ = knn.predict(X_test)
knn.score(X_train, y_train)
#0.94
knn.score(X_test, y_test)
#0.9565217391304348
# 对于这种不太好进行可视化展示的数据,我们可以使用交叉表.
pd.crosstab(index=y_test, columns=y_, rownames=['真实值'], colnames=['预测值'], margins=True)
预测值BMAll
真实值
B44044
M32225
All472269
(44 + 22) / 69
#0.9565217391304348
# 混淆矩阵
from sklearn.metrics import confusion_matrix
confusion_matrix(y_test, y_)

# array([[44,  0],
#        [ 3, 22]], dtype=int64)
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过期的秋刀鱼-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值