Jupyter Notebook使用Python做K近邻(KNN)算法实验

2021-9-22 12:00| 发布者: Fuller| 查看: 2515| 评论: 0

摘要: 那么什么是K近邻算法? 能否实际在Python里做相关实验呢?本Notebook将做对这2个问题进行研究和探索。

1,本Notebook背景介绍

最近在看一些研究文章时,看到了2篇研究者使用K近邻(KNN)算法对文本进行分析的研究:

1. 《基于因果机制的用户行为研究及应用》,该研究作者针对营销场景具有海量对照组样本和包含丰富特征描述的数据特性,将基于深度学习的用户建模方法与因果推断思想相结合,提出以用户为单位的细粒度混杂平衡目标;然后,引入K近邻准则为每个营销组用户筛选最为合适的对照组用户,避免大样本引入的干扰误差;最后通过有效综合时序非时序特征进行高效的样本权重学习,实现稳定可靠的因果效应评估。

2. 《改进的K近邻算法及其在文本分析中的应用》,该范文的摘要:随着互联网的发展,实体经济与互联网的结合越来越紧密,人们由线下消费向线上消费转移。互联网上积累了大量人们参与的实体消费的评论信息。这些评论信息是顾客线上消费的重要参考依据,同时也是经营实体经营决策的重要参考,因此合理有效的挖掘评论数据,提取顾客消费评论中的关注点和情感因素对实体经济发展具有重要作用。k近邻算法因其理论简单,易于实施等优点在机器学习和数据挖掘领域得到广泛应用。但是针对文本分析中特征维度高、文本数值化后语意解释等问题,传统k近邻算法无法很好的处理。对传统k近邻算法进行改进优化,使其更加适用于文本分析处理场景是本文的研究重点。

那么什么是K近邻算法? 能否实际在Python里做相关实验呢?本Notebook将做对这2个问题进行研究和探索。

1.1 什么是K Nearest Neighbors(K近邻,简称KNN)

参考文章《K-近邻算法(KNN)》,我们做如下的描述:

K-近邻算法是最简单的分类器,没有显式的学习过程或训练过程,是懒惰学习(Lazy Learning),数据集事先已经有了分类标签和数据特征值,对测试数据可以直接处理,其处理机制就是通过测量不同特征值之间的距离来进行分类。

简单说就是如果测试样本在特征空间中的k个最邻近的样本中,大多数样本属于某个类别,则该测试样本也划分到这个类别,KNN里的K就是最邻近的K个数据样本。

1.1.1 举个例子

怎样确定上图中的绿圆属于哪个类别?

如果k=3,在其最近的3个样本中红色三角形数量最多,绿圆属于红色三角形类别

如果k=5,在其最近的5个样本中蓝色矩形数量最多,绿圆属于蓝色矩形类别

可见k的选择很重要。当k很小时,结果很容易受噪声影响,容易发生过拟合。当k很大时,与测试样本差别很大的样本也会对预测结果产生影响,k通常取奇数,避免产生相等占比的情况。

1.1.2 算法公式

在计算相邻样本的距离时一般采用欧式距离或者曼哈顿距离,公式如下:

详细的算法过程:

1)计算测试数据与各个训练数据之间的距离;

2)按照距离的递增关系进行排序;

3)选取距离最小的K个点;

4)确定前K个点所在类别的出现频率;

5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。

1.1.3 kNN算法的优缺点

优点:

简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;

可用于数值型数据和离散型数据;

训练时间复杂度为O(n);无数据输入假定;

对异常值不敏感。

缺点:

计算复杂性高;空间复杂性高;

样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);

一般数值很大的时候不用这个,计算量太大。但是单个样本又不能太少,否则容易发生误分。

最大的缺点是无法给出数据的内在含义。

1.2 能不能在Notebook中用python中做KNN分析呢?

笔者查了一下,Python的sklearn库官网提供KNN算法的例子。参考sklearn库官网和知乎文章《机器学习框架之sklearn简介

1.2.1 sklearn库简介

sklearn是一个Python第三方提供的非常强力的机器学习库,它包含了从数据预处理到训练模型的各个方面。在实战使用scikit-learn中可以极大的节省我们编写代码的时间以及减少我们的代码量,使我们有更多的精力去分析数据分布,调整模型和修改超参。(sklearn为包名)

sklearn拥有可以用于监督和无监督学习的方法,一般来说监督学习使用的更多。sklearn中的大部分函数可以归为估计器(Estimator)和转化器(Transformer)两类。

1.2.2 估计器(Estimator)

其实就是模型,它用于对数据的预测或回归。基本上估计器都会有以下几个方法:

fit(x,y):传入数据以及标签即可训练模型,训练的时间和参数设置、数据集大小以及数据本身的特点有关

score(x,y):用于对模型的正确率进行评分(范围0-1)。但在不同的问题下,评判模型优劣的的标准不限于简单的正确率,可能还包括召回率或者是查准率等其他的指标,特别是对于类别失衡的样本,准确率并不能很好的评估模型的优劣,因此在对模型进行评估时,不要轻易的被score的得分蒙蔽。

predict(x):用于对数据的预测,它接受输入,并输出预测标签,输出的格式为numpy数组。我们通常使用这个方法返回测试的结果,再将这个结果用于评估模型。

1.2.3 转化器(Transformer)

用于对数据的处理,例如标准化、降维以及特征选择等等。同估计器的使用方法类似:

fit(x,y):该方法接受输入和标签,计算出数据变换的方式。

transform(x):根据已经计算出的变换方式,返回对输入数据x变换后的结果(不改变x)

fit_transform(x,y):该方法在计算出数据变换方式之后对输入x就地转换。

1.2.4 测试数据

sklearn的datasets中提供一些训练数据,我们可以使用这些数据来进行分类或者回归等等,以便熟悉sklearn的使用。

2,第三方库

本notebook使用了sklearn库做KNN算法实验。

如果未安装,请先使用下面的命令安装sklearnm库,再运行实验本notebook:

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple sklearn #国内安装使用清华的源,速度快

3,本notebook所做的测试

基于测试数据和sklearn官网的例子,在Jupyter Notebook中使用Python做KNN算法实验。

4,准备编程环境

4.1 把matplotlib输出的图形内嵌到Jupyter Notebook中

下面这行执行后,使用matplotlib画图,会直接显示在Jupyter Notebook中

%matplotlib inline

4.2 开启日志输出

把实验过程中的日志信息直接在Jupyter Notebook中输出

import logging

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

4.3 引入sklearn库

引入sklearn库下的neighbors

from sklearn.neighbors import NearestNeighbors

4.4 引入numpy库

使用Numpy的array来构造测试数据

import numpy as np

5,进行KNN计算

5.1 构造测试数据

构造一个测试数据数组:[[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]] 。内部数组是一个行向量,表示一个点,那么这个例子含有6个点,每个点有两维。

X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])

5.2 测试一:k=2的情况下求近邻

nbrs = NearestNeighbors(n_neighbors=2, algorithm='ball_tree').fit(X)

distances, indices = nbrs.kneighbors(X)

5.2.1 第一种返回值是近邻点坐标

indices的返回值是由行向量组成的矩阵,行号n表示第n的点的最近邻, 以[0,1]为例, 表述测试数据的第0个点的最近邻的2个点, 一个是X[0], 就是[-1,-1],也就是自己。 另一个是X[1],就是[-2, -1]。其它点的解释类推

indices

输出结果:

array([[0, 1],

       [1, 0],

       [2, 1],

       [3, 4],

       [4, 3],

       [5, 4]], dtype=int64)


5.2.2 第二种返回值是近邻点距离

distances的返回值, 以[0. , 1. ]为例, 表述测试数据的第0个点的最近邻的2个点, 一个距离是0,也就是自己。 另一个距离是1。其它点的解释类推

distances

输出结果:

array([[0.        , 1.        ],

       [0.        , 1.        ],

       [0.        , 1.41421356],

       [0.        , 1.        ],

       [0.        , 1.        ],

       [0.        , 1.41421356]])


5.3 测试二:k=3的情况下求近邻

nbrs = NearestNeighbors(n_neighbors=3, algorithm='ball_tree').fit(X)

distances, indices = nbrs.kneighbors(X)


5.3.1 第一种返回值是近邻点坐标

下面的返回值, 以[0, 1, 2]为例, 表述测试数据的第0个点的最近邻的3个点, 一个是X[0], 就是[-1,-1],也就是自己。 另一个是X[1],就是[-2, -1].第3个点是X[2],就是[-3, -2]。其它点的解释类推

indices

输出结果:

array([[0, 1, 2],

       [1, 0, 2],

       [2, 1, 0],

       [3, 4, 5],

       [4, 3, 5],

       [5, 4, 3]], dtype=int64)


5.3.2 第二种返回值是近邻点距离

下面的返回值, 以[0,1.,2.23606798]为例, 表述测试数据的第0个点的最近邻的3个点, 一个距离是0,也就是自己。 另一个距离是1。第3个距离是2.23606798。其它点的解释类推

distances

输出结果:

array([[0.        , 1.        , 2.23606798],

       [0.        , 1.        , 1.41421356],

       [0.        , 1.41421356, 2.23606798],

       [0.        , 1.        , 2.23606798],

       [0.        , 1.        , 1.41421356],

       [0.        , 1.41421356, 2.23606798]])


6,在sklearn官网有一个基于KNN做分类的例子

使用的测试数据是iris数据集,为了易于展示,只用两维数据进行分析。有兴趣的同学可以参考

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sns

from matplotlib.colors import ListedColormap

from sklearn import neighbors, datasets


# 设置k=15

n_neighbors = 15


# 加载iris测试数据集

iris = datasets.load_iris()


# we only take the first two features. We could avoid this ugly

# slicing by using a two-dim dataset

X = iris.data[:, :2]

y = iris.target


# 在平面上,用一条折线将多个类别区分开,下面这个变量决定了折线的精细程度

h = .2  # step size in the mesh


# 对应于分类器和数据分类颜色

cmap_light = ListedColormap(['orange', 'cyan', 'cornflowerblue'])

cmap_bold = ['darkorange', 'c', 'darkblue']


# uniform是等权重,distance是按距离的倒数给权重

for weights in ['uniform', 'distance']:

    # 初始化分类器

    clf = neighbors.KNeighborsClassifier(n_neighbors, weights=weights)

    clf.fit(X, y)


    # Plot the decision boundary. For that, we will assign a color to each

    # point in the mesh [x_min, x_max]x[y_min, y_max].

    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, h),

                         np.arange(y_min, y_max, h))

    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])


    # Put the result into a color plot

    Z = Z.reshape(xx.shape)

    plt.figure(figsize=(8, 6))

    plt.contourf(xx, yy, Z, cmap=cmap_light)


    # Plot also the training points

    sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=iris.target_names[y],

                    palette=cmap_bold, alpha=1.0, edgecolor="black")

    # 可视化数据样例

    plt.xlim(xx.min(), xx.max())

    plt.ylim(yy.min(), yy.max())

    plt.title("3-Class classification (k = %i, weights = '%s')"

              % (n_neighbors, weights))

    plt.xlabel(iris.feature_names[0])

    plt.ylabel(iris.feature_names[1])


plt.show()

import numpy as np

import matplotlib.pyplot as plt

import seaborn as sns

from matplotlib.colors import ListedColormap

from sklearn import neighbors, datasets

# 设置k=15

n_neighbors = 15

# 加载iris测试数据集

iris = datasets.load_iris()

# we only take the first two features. We could avoid this ugly

# slicing by using a two-dim dataset

X = iris.data[:, :2]

y = iris.target

# 在平面上,用一条折线将多个类别区分开,下面这个变量决定了折线的精细程度

h = .2  # step size in the mesh

# 对应于分类器和数据分类颜色

cmap_light = ListedColormap(['orange', 'cyan', 'cornflowerblue'])

cmap_bold = ['darkorange', 'c', 'darkblue']

# uniform是等权重,distance是按距离的倒数给权重

for weights in ['uniform', 'distance']:

    # 初始化分类器

    clf = neighbors.KNeighborsClassifier(n_neighbors, weights=weights)

    clf.fit(X, y)

    # Plot the decision boundary. For that, we will assign a color to each

    # point in the mesh [x_min, x_max]x[y_min, y_max].

    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, h),

                         np.arange(y_min, y_max, h))

    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])

    # Put the result into a color plot

    Z = Z.reshape(xx.shape)

    plt.figure(figsize=(8, 6))

    plt.contourf(xx, yy, Z, cmap=cmap_light)

    # Plot also the training points

    sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=iris.target_names[y],

                    palette=cmap_bold, alpha=1.0, edgecolor="black")

    # 可视化数据样例

    plt.xlim(xx.min(), xx.max())

    plt.ylim(yy.min(), yy.max())

    plt.title("3-Class classification (k = %i, weights = '%s')"

              % (n_neighbors, weights))

    plt.xlabel(iris.feature_names[0])

    plt.ylabel(iris.feature_names[1])

plt.show()


鲜花

握手

雷人

路过

鸡蛋

最新评论

GMT+8, 2024-3-28 20:06