KNN算法中关于数据分析和机器学习的应用

KNN算法中关于数据分析和机器学习的应用

K-近邻法

KNN做为機器学习中最为简单的算法,其实用性还是很强的.

KNN就是所谓的物以类聚的方法(近朱者赤,近墨者黑) KNN就是在一个数据模型中,当新进来一个样本,通过比较与数据集中其他样本的距离来对该新样本做一个分类

看一个简单的示例:

以下是一组模拟数据 raw_data_X 为肿瘤病人的模拟数据 并且对于肿瘤有两个特征值

raw_data_y是肿瘤病人的病况 0为良性肿瘤 1为恶性肿瘤

raw_data_X = [[3.393533211,2.331273381],
              [3.110073483,1.781539638],
              [1.343808831,3.369360594],
              [3.582294042,4.679179110],
              [2.280362439,2.866990263],
              [7.423436942,4.696522875],
              [5.745023312,3.533312331],
              [9.172163123,2.511103121],
              [7.792731231,3.424088123],
              [7.939820817,0.791637231]
             ]
raw_data_y = [0,0,0,0,0,1,1,1,1,1]

使用matplotlib绘制散点图

import numpy as np
from matplotlib import pyplot as plt
X_train = np.array(raw_data_X)
y_train = np.array(raw_data_y)
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color="g")
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color="r")
plt.show()
图1

现在进来一个新的肿瘤样本

x = np.array([8.093607318,3.365731514])
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color="g")
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color="r")
plt.scatter(x[0],x[1],color="b")
plt.show()
图2

上图中的蓝点为新的肿瘤样本

我们回到KNN算法 很简单 就是取最近的k个样本 并让那k个样本来投票来决定该新的样本的结果

通俗一点说呢就是 比如k取3 那就取离这个样本最近的三个样本 在上图中 离该新样本的三个样本都是恶性肿瘤

那么很不幸 经过投票 该样本是恶性肿瘤

再来看另外一个样本

x2 = np.array([5.093607323,3.365731514])
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color="g")
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color="r")
plt.scatter(x2[0],x2[1],color="b")
plt.show()
图3

这个时候就不好判断样本与样本之间的距离了
一般可以使用欧拉距离来计算出样本之间的距离

\sqrt{\sum_{i=0}^{n}{(X{new}[i]-X[i])^{2}}}

向量各个元素的差的平方求和然后求平方根
from math import sqrt
distances = [sqrt(np.sum((x2-x_train)**2)) for x_train in X_train]
distances
[1.9900642238939008,
 2.538517361455215,
 3.749800248123507,
 2.0023017806427585,
 2.8571121106462054,
 2.6831160429772547,
 0.6726262862475981,
 4.167134159567629,
 2.6997546859948547,
 3.837563371620197]

使用argsort排序distances

nearest = np.argsort(distances)
nearest
array([6, 0, 3, 1, 5, 8, 4, 2, 9, 7], dtype=int64)

我们假设k=3

top_k为最近的三个结果值

k=3
top_y = [y_train[i] for i in nearest[:k]]
top_y
[1, 0, 0]
from collections import Counter

votes = Counter(top_y)
votes.most_common(1)[0][0]
0

投票结果是0 所有大概估计这个肿瘤样本是良性肿瘤

但是只是大概 为什么呢? 从上图不难看出 该样本离恶性肿瘤的那个样本很近 而离另外两个良性肿瘤样本很远 甚至恶性肿瘤的权重比那两个良性肿瘤样本相加还要多

这个时候就牵扯出另外一个概念

超参数

我们可以考虑最近k个样本与新样本的距离,也可以不考虑
在scikit-learn中封装了关于weights的设置
我们首先来看一下scikit-learn中对KNN的封装

from sklearn.neighbors import KNeighborsClassifier

knn_cls = KNeighborsClassifier(n_neighbors=3,weights="distance")
knn_cls.fit(X_train,y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=3, p=2,
           weights='distance')
x2.shape
(2,)
x2 = x2.reshape(1,-1)
knn_cls.predict(x2)
array([1])

结果是1,是恶性肿瘤,所有在一个算法中使用不同的超参数,往往预测出来的结果大为不同

这是在机器学习领域很重要的一个环节

调参!!!

再想想,在考虑距离的方式上,我们有很多种方式
在这里说三种,其实前两种都属于第三种的一部分
曼哈顿距离 欧拉距离 明可夫斯基距离

曼哈顿距离

在上图红蓝黄均为曼哈顿距离 而绿色为欧拉距离

曼哈顿距离

{\sum_{i=0}^{n}{|Xnew-X[i]|}}

明可夫斯基距离

({\sum_{i=0}^{n}{|Xnew-X[i]|^{p}}})^{\frac{1}{p}}

不难看出 当p=1时为曼哈顿距离,当p=2时为欧拉距离

来看一下scikit-learn中对于p这个超参数的封装

knn_cls = KNeighborsClassifier(n_neighbors=3,weights="distance",p=1)  # 使用曼哈顿距离
knn_cls.fit(X_train,y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=3, p=1,
           weights='distance')
knn_cls.predict(x2)
array([1])

当使用曼哈顿距离时,结果为1

但由于有的超参数之间是有关联的,所以我们一般可使用sklearn中的model_selection中的网格搜索,而网格搜索使用的验证数据准确性的方法是交叉验证,我们不深入探讨交叉验证,我们后面再一起来看如何验证数据的分类准确性

from sklearn.model_selection import GridSearchCV

param_grid = [
    {
        "weights":["uniform"],
        "n_neighbors":[i for i in range(1,6)] # 看从一到10之间的k
    },
    {
        "weights":["distance"],
        "n_neighbors":[i for i in range(1,6)],
        "p":[i for i in range(1,3)]
    }
]

knn_cls = KNeighborsClassifier()
gridSearch = GridSearchCV(knn_cls,param_grid,n_jobs=2,verbose=3)  # n_jobs 为调用的核数 verbose为执行过程中信息的详细度,数字越高越详细
gridSearch.fit(X_train,y_train)
Fitting 3 folds for each of 15 candidates, totalling 45 fits


[Parallel(n_jobs=2)]: Done  45 out of  45 | elapsed:    0.7s finished





GridSearchCV(cv=None, error_score='raise',
       estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=5, p=2,
           weights='uniform'),
       fit_params=None, iid=True, n_jobs=2,
       param_grid=[{'weights': ['uniform'], 'n_neighbors': [1, 2, 3, 4, 5]}, {'weights': ['distance'], 'n_neighbors': [1, 2, 3, 4, 5], 'p': [1, 2]}],
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=3)
gridSearch.best_score_
1.0
gridSearch.best_params_
{'n_neighbors': 1, 'weights': 'uniform'}
knn_cls = gridSearch.best_estimator_
knn_cls.predict(x2)
array([1])

在上面我们可以看到有一个best_score是1.0还有其他best_params的参数 说的是当n_neighbors=1,weights=uniform的时候分类准确度最高

而什么是分类准确度呢???

回过头看 当我们拿到一个X_train和y_train的时候,我们并不知道这个训练集是否准确,这个训练集时候符合真实环境,换句话说就是这个训练集能不能用来预测数据 所以我们可以把拿到的训练集分为一个训练集和一个测试数据集

先自己来实现一下 我们使用另外一个更大的sklearn内置的数据集

鸢尾花数据集

先加载一个鸢尾花数据集

from sklearn import datasets
iris = datasets.load_iris()
iris.data.shape
iris.target.shape
(150,)

看一下鸢尾花数据集的四个特征分别是

萼片长度 萼片宽度 花瓣长度 花瓣宽度
iris.feature_names
['sepal length (cm)',
 'sepal width (cm)',
 'petal length (cm)',
 'petal width (cm)']
datas = iris.data
targets = iris.target

我们先把iris的特征赋值为X_train iris的结果赋值为y_train

X_train = iris.data
X_train.shape
(150, 4)
y_train = iris.target
y_train.shape
(150,)

我们把X_train和y_train合并并且打乱,以确保后面的测试训练集有不同的特征和结果

merge_datas = np.hstack([X_train,y_train.reshape(-1,1)])
new_datas = np.random.permutation(merge_datas)
new_X_train,new_y_train = np.hsplit(new_datas,[-1]) 

我们把训练集和测试集的比例分为8:2

test_size = int(len(new_X_train)*0.2)
new_y_train = new_y_train.reshape(len(new_X_train))
X_train = new_X_train[test_size:]
X_test = new_X_train[:test_size]
y_train = new_y_train[test_size:]
y_test = new_y_train[:test_size]
X_test.shape
(30, 4)
X_train.shape
(120, 4)
y_train.shape
(120,)
y_test.shape
(30,)

我们使用sklearn中的Knn,把X_train和y_train fit到knn中,再predict出X_test的结果

knn_cls = KNeighborsClassifier(6)   #k=6
knn_cls.fit(X_train,y_train)
y_predict = knn_cls.predict(X_test)
y_predict
array([0., 0., 0., 1., 2., 0., 0., 2., 0., 0., 1., 1., 0., 0., 1., 0., 1.,
       1., 2., 0., 2., 0., 2., 2., 1., 0., 2., 2., 0., 1.])
score = sum(y_predict==y_test)/len(y_test)
score
0.9666666666666667

出现了!!! 该knn算法的分类准确度是0.9666666 约等于 96%

我们使用一下sklearn中的train_test_split 顾名思义就是sklearn中封装的分割训练集测试集的方法

from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(iris.data,iris.target,test_size=0.2,random_state=888)  #random_state为随机种子
print("shape of X_train:{},X_test:{},y_train{},y_test:{}".format(X_train.shape,X_test.shape,y_train.shape,y_test.shape))
shape of X_train:(120, 4),X_test:(30, 4),y_train(120,),y_test:(30,)
knn_cls = KNeighborsClassifier(6)
knn_cls.fit(X_train,y_train)
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=6, p=2,
           weights='uniform')
knn_cls.predict(X_test)
array([1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 2, 2,
       0, 1, 2, 2, 2, 0, 0, 0])
y_test
array([1, 1, 2, 2, 2, 1, 1, 2, 2, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 2, 2,
       0, 1, 2, 2, 1, 0, 0, 0])
knn_cls.score(X_test,y_test)
0.9333333333333333

使用内置封装的score可以预测出score 结果是0.9333333333333333

再对鸢尾花数据集使用一下grid_search

knn_cls2 = KNeighborsClassifier()

param_grid = [
    {
        "weights":["uniform"],
        "n_neighbors":[i for i in range(1,11)] # 看从一到10之间的k
    },
    {
        "weights":["distance"],
        "n_neighbors":[i for i in range(1,11)],
        "p":[i for i in range(1,6)]
    }
]

gridSearch2 = GridSearchCV(knn_cls2,param_grid,n_jobs=2,verbose=2)
gridSearch2.fit(X_train,y_train)
Fitting 3 folds for each of 60 candidates, totalling 180 fits


[Parallel(n_jobs=2)]: Done 180 out of 180 | elapsed:    1.3s finished





GridSearchCV(cv=None, error_score='raise',
       estimator=KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
           metric_params=None, n_jobs=1, n_neighbors=5, p=2,
           weights='uniform'),
       fit_params=None, iid=True, n_jobs=2,
       param_grid=[{'weights': ['uniform'], 'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}, {'weights': ['distance'], 'n_neighbors': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'p': [1, 2, 3, 4, 5]}],
       pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
       scoring=None, verbose=2)
gridSearch2.best_params_
{'n_neighbors': 4, 'weights': 'uniform'}
knn_cls2 = gridSearch2.best_estimator_
knn_cls2.score(X_test,y_test)
0.9333333333333333

数据归一化

解决方案:将所有的数据映射到同一尺度

样本 肿瘤大小(厘米) 发现时间(天)
样本1 1 200
样本2 5 100

可以看出 上面的样本计算出来的欧拉距离其实是被发现的时间决定的

所有我们可以把发现的时间的单位由天化为年

用专业的话来说

数据标准化(归一化)处理是数据挖掘的一项基础工作,不同评价指标往往具有不同的量纲和量纲单位,这样的情况会影响到数据分析的结果,为了消除指标之间的量纲影响,需要进行数据标准化处理,以解决数据指标之间的可比性。原始数据经过数据标准化处理后,各指标处于同一数量级,适合进行综合对比评价。以下是两种常用的归一化方法:

Standardization 均值标准差归一化

{X(scale) = \frac{x-x(mean)}{std}}

数据没有明显边界可使用,比较常用
mean和std分别为对应特征的均值和标准差。量化后的特征将分布在[-1, 1]区间。

normalizaion 最值归一化

{X(scale) = \frac{x-x(min)}{x(max)-x(min)}}

大多数机器学习算法中,会选择Standardization来进行特征缩放,但是,normalizaion也并非会被弃置一地。在数字图像处理中,像素强度通常就会被量化到[0,1]区间,在一般的神经网络算法中,也会要求特征被量化[0,1]区间。

使用sklearn中的scale

from sklearn.preprocessing import StandardScaler
standarScaler = StandardScaler()
standarScaler.fit(X_train)
StandardScaler(copy=True, with_mean=True, with_std=True)
X_train = standarScaler.transform(X_train)
X_test = standarScaler.transform(X_test)
knn_cls = KNeighborsClassifier(6)
knn_cls.fit(X_train,y_train)
knn_cls.score(X_test,y_test)
0.9333333333333333

对knn的总结

knn算法可以天然的解决分类问题 ,并且可以解决多分类问题或者是回归问题 它简单又强大
但是knn的缺点是效率低下 O(m*n) m个样本 n 个特征的时间复杂度
由于knn高度依赖距离公式 当数据的维度过大时会面临维数灾难 看似距离相近的两个数的距离会随着维数的增大而变大
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容