机器学习教程二:K近邻算法实现数据分类

scikit-learn中的KNN

scikit-learn是一个用于机器学习的Python库,它提供了丰富的工具和算法,用于数据预处理、特征工程、模型选择和评估等任务。

scikit-learn提供了包括分类、回归、聚类、降维、异常检测和模型选择等多种机器学习算法。其中包括常见的算法,如线性回归、逻辑回归、决策树、支持向量机、随机森林、K最近邻等。

安装方法

推荐安装在conda虚拟环境上,在windows电脑中,win键+r输入cmd打开终端,使用下面指令进行安装:

conda activate youEnvName
pip install scikit-learn

KNeighborsClassifier

sklearn.neighbors模块中的KNeighborsClassifier类实现了KNN算法。

参数说明:

  • n_neighbors :int, default=5。默认情况下用于邻居查询的邻居数。
  • weights:{‘uniform’, ‘distance’}, callable or None, default=’uniform。权重默认是uniform(统一),有三个选项,可以是uniform(统一)、distance(距离)或者用户自定义的函数。统一表示所有临近点的权重都是相同;距离表示距离近的临近点所占的权重更大,距离远的临近点所占的权重更;用户自定义的函数,它接受一个距离数组,并返回一个包含权重的相同形状的数组。
  • algorithm:{‘auto’, ‘ball_tree’, ‘kd_tree’, ‘brute’}, default=’auto’。这个表示计算最近邻所使用的算法,默认是’auto’,表示根据数据选择最合适的算法;ball_tree将使用BallTree;kd_tree将使用KDTree;’brute’将使用蛮力搜索。
  • leaf_size:int, default=30。传递给BallTree或KDTree的叶大小。这会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
  • p:float,default=2。闵可夫斯基距离度量公式,当p=1时,这相当于使用曼哈顿距离;当p=2时,表示使用欧氏距离;对于任意p,使用minkowski_distance(p)。
  • metric:str or callable, default=’minkowski’。用于距离计算的度量。默认值为“minkowski”。
  • metric_params:dict,default=None。距离公式的其他参数。
  • n_jobs:int , default=None。要为邻居搜索运行的并行作业数,默认为1。如果为-1,那么CPU的所有cores都用于并行工作。

函数说明:

  • fit(X, y): 从训练数据集中拟合K最近邻分类器。它接受特征矩阵X和目标向量y作为输入,并使用这些数据训练K最近邻模型。返回值是一个K近邻分类器。
  • get_metadata_routing(): 获取此对象的元数据路由。这个函数返回有关对象元数据的信息,例如对象在训练过程中使用的数据和配置。
  • get_params(deep=True): 获取这个估计器的参数。它返回估计器的当前参数设置,可以选择是否返回深层嵌套的参数。
  • kneighbors([X, n_neighbors, return_distance]): 寻找一个点的K个最近邻居。它接受一个样本矩阵X、邻居数量n_neighbors和是否返回距离的参数,并返回与给定点最近的邻居的索引和(可选地)距离。
  • kneighbors_graph([X, n_neighbors, mode]): 计算X中点的K个(加权)邻居的图形。它接受一个样本矩阵X、邻居数量n_neighbors和模式参数,并返回一个表示点之间邻居关系的图形(稀疏矩阵)。
  • predict(X): 预测提供数据的类标签。它接受一个数据集X,并使用已训练的K最近邻分类器对数据进行预测,并返回预测的类标签。
  • predict_proba(X): 返回测试数据X的概率估计值。它接受一个数据集X,并使用已训练的模型对每个类别的概率进行估计,并返回这些概率。
  • score(X, y ,sample_weight): 返回给定测试数据和标签的平均准确率。它接受测试数据X、标签y和(可选的)样本权重,并计算模型在给定数据上的平均准确率。
  • set_params(**params): 设置这个估计器的参数。它接受参数字典params,并将估计器的参数设置为给定值。
  • set_score_request(*[, sample_weight]): 请求传递给score方法的元数据。这个方法允许在评分过程中传递额外的元数据信息,例如样本权重。

其中fit()、predict()、predict_proda()和score()函数使用频繁,需要铭记。

正态分布数据分类

我们分为准备数据集,处理数据集,构建分类器,结果打分四个步骤来完成正态分布数据的分类。

准备数据集

使用numpy库来完成正态分布数据的创建,使用matplotlib库来完成数据的可视化。写出下面的代码。

import numpy as np
import matplotlib.pyplot as plt

# 创建数据
np.random.seed(0)  # 设置随机种子,以便结果可复现

def createDatabase():
    # 第一组数据,loc表示横坐标均值为5,纵坐标均值为2,scale表示标准差为0.5,szie表示生成100个样本,每个样本里面有两个维度,分别是横坐标和纵坐标。
    data1 = np.random.normal(loc=[5, 2], scale=1, size=(100, 2))
    data2 = np.random.normal(loc=[5, -2], scale=1, size=(100, 2))
    data3 = np.random.normal(loc=[2, 0], scale=0.8, size=(100, 2))
    data=np.concatenate((data1,data2,data3),axis=0)
    labels = [1] * 100 + [2] * 100 + [3] * 100
    return data,labels
def showDatabase(data1,data2,data3):
    # 可视化数据
    plt.scatter(data1[:, 0], data1[:, 1], color='red', label='Group 1')
    plt.scatter(data2[:, 0], data2[:, 1], color='blue', label='Group 2')
    plt.scatter(data3[:, 0], data3[:, 1], color='green', label='Group 3')
    plt.xlabel('X')
    plt.ylabel('Y')
    plt.title('Data Visualization')
    # 显示数据的标签
    plt.legend()
    plt.show()
if __name__=='__main__':
    #创建训练集
    data,labels=createDatabase()
    #展示训练集
    showDatabase(data[:100,:],data[100:200,:],data[200:300,:])

得到下面的结果。

处理数据集

处理数据集主要是最数据进行归一化处理,对数据进行归一化可以帮助K最近邻(KNN)算法更准确地计算样本之间的距离。这是因为KNN算法的核心思想是根据样本之间的距离来确定最近的邻居,从而进行分类或回归预测。对于上面的例子可能不是很明显,我们来举一个其他的例子。

假设有一个数据集包含两个特征:收入(范围从1万到100万)和年龄(范围从20到80)。现在我们要使用KNN算法对这个数据集进行分类,其中目标是根据收入和年龄预测一个人是否会购买某个产品(二分类问题)。

如果不对数据进行归一化,收入的范围远远超过年龄的范围。这意味着在计算样本之间的距离时,收入这个特征将对距离计算产生更大的影响。这可能会导致年龄这个特征在KNN算法中的权重较小,对分类结果的影响较小。

通过对数据进行归一化,将收入和年龄的特征范围缩放到相同的尺度,例如[0, 1]范围内。这样,收入和年龄将具有相似的权重,而不会被数据范围的差异所主导。这将有助于更准确地计算样本之间的距离,提高KNN算法的性能和准确度。

通常情况下,我们会把数据归一化为[0,1]区间内,使用下面的公式对数据进行归一化。
xnormalized=xmin(x)max(x)min(x)

x表示的是一个维度的特征,类似于上面数据的横坐标或者纵坐标。使用下面的代码对数据做归一化处理。

def normalDatabase(data):
    # 计算数据的最小值和最大值
    data_min = np.min(data, axis=0)
    data_max = np.max(data, axis=0)
    print(data_max)
    print(data_min)
    # 对 x 维度进行归一化
    xNormalized = (data[:, 0] - data_min[0]) / (data_max[0] - data_min[0])
    # 对 y 维度进行归一化
    yNormalized = (data[:, 1] - data_min[1]) / (data_max[1] - data_min[1])
    # 将 x 和 y 归一化后的数据合并为一个数组
    xyNormalized = np.array(list(zip(xNormalized, yNormalized)))
    return xyNormalized,data_min,data_max

使用showDatabase函数展示归一化后的数据。

构建分类器

使用sklearn.neighbors.KNeighborsClassifier作为分类器,给出下面代码:

def knnClassify(data,labels):
    knnModel=KNN(n_neighbors=10)
    knnModel.fit(data,labels)
    return knnModel

然后创建测试点(10,0),(2,2)进行分类,代码如下:

if __name__=='__main__':
    #创建分类器
    knnModel=knnClassify(data=noramlData,labels=labels)
    #创建测试集(10,0),(2,2)并进行归一化
    test=[(10,0),(2,2)]
    normalTest=[((10-minData[0])/(maxData[0]-minData[0]),(0-minData[1])/(maxData[1]-minData[1])),((1-minData[0])/(maxData[0]-minData[0]),(1-minData[1])/(maxData[1]-minData[1]))]
    #对测试集数据进行预测
    prediction=knnModel.predict(test)
    print(f"{test[0]}属于类别{prediction[0]};{test[1]}属于类别{prediction[1]}")
    #展示预测结果所属类别
    preProba=knnModel.predict_proba(test)
    print(f"{test[0]}属于类别1的概率为{[prediction[0][0]]},属于类别2的概率为{[prediction[0][1]]},属于类别3的概率为{[prediction[0][2]]}")
    print(f"{test[1]}属于类别1的概率为{[prediction[1][0]]},属于类别2的概率为{[prediction[1][1]]},属于类别3的概率为{[prediction[2][2]]}")

得到下面的结果

结果打分

首先创建测试集,为训练集的一半,然后对测试集做归一化,对测试集做预测,得到准确率。

    #创建测试集,为训练集数据量的一半
    testData,testLabels=createDatabase()
    testCutData = np.concatenate((testData[:50, :], testData[100:150, :], testData[200:250, :]), axis=0)
    testCutLabels=np.concatenate((testLabels[:50],testLabels[100:150],testLabels[200:250]),axis=0)
    print(testCutData.shape)
    #对测试集数据进行归一化处理
    xNormal=(testCutData[:,0]-minData[0])/(maxData[0]-minData[0])
    yNormal=(testCutData[:,1]-minData[1])/(maxData[1]-minData[1])
    xyNormal=list(zip(xNormal,yNormal))
    #对测试集数据进行预测
    scores=knnModel.score(xyNormal,testCutLabels)
    print(f"测试集的准确率为{scores}")

得到下面的结果还是相当不错的。

参考链接

1.sklearn中的KNN参考官网

2.正态分布数据集原创

Share