美文网首页
KNN算法中关于数据分析和机器学习的应用

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

作者: pikacyxooo | 来源:发表于2018-09-25 18:03 被阅读0次

    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高度依赖距离公式 当数据的维度过大时会面临维数灾难 看似距离相近的两个数的距离会随着维数的增大而变大

    相关文章

      网友评论

          本文标题:KNN算法中关于数据分析和机器学习的应用

          本文链接:https://www.haomeiwen.com/subject/jnbloftx.html