实验名称

  • 监督学习之KNN;
  • 无监督学习之K-means聚类、DBSCAN算法实验;

实验目的

  • 掌握KNN的原理,学会利用KNN解决分类问题;
  • 理解K-means聚类算法、DBSCAN算法的基本原理;
  • 学会用python实现KNN、K-means和DBSCAN算法。

实验工具

Python集成开发环境(IDE)

  1. Anaconda: https://www.continuum.io/ (推荐)
  2. IDLE: Python解释器默认工具
  3. PyCharm: https://www.jetbrains.com/pycharm/
  4. 实验数据集:Python的scikit-learn库中自带的鸢尾花数据集,可使用datasets.load_iris()载入。

实验原理

KNN(K-Nearest Neighbor)算法原理

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

说明:KNN没有显示的训练过程,它是“懒惰学习”的代表,它在训练阶段只是把数据保存下来,训练时间开销为0,等收到测试样本后进行处理。

K-means算法原理

k-means算法是一种聚类算法,所谓聚类,即根据相似性原则,将具有较高相似度的数据对象划分至同一类簇,将具有较高相异度的数据对象划分至不同类簇。聚类与分类最大的区别在于,聚类过程为无监督过程,即待处理数据对象没有任何先验知识,而分类过程为有监督过程,即存在有先验知识的训练数据集。

k-means算法中的k代表类簇个数,means代表类簇内数据对象的均值(这种均值是一种对类簇中心的描述),因此,k-means算法又称为k-均值算法。k-means算法是一种基于划分的聚类算法,以距离作为数据对象间相似性度量的标准,即数据对象间的距离越小,则它们的相似性越高,则它们越有可能在同一个类簇。数据对象间距离的计算有很多种,k-means算法通常采用欧氏距离来计算数据对象间的距离。

DBSCAN算法原理

DBSCAN(Density-Based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据库中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。

这类密度聚类算法一般假定类别可以通过样本分布的紧密程度决定。同一类别的样本,他们之间的紧密相连的,也就是说,在该类别任意样本周围不远处一定有同类别的样本存在。通过将紧密相连的样本划为一类,这样就得到了一个聚类类别。通过将所有各组紧密相连的样本划为各个不同的类别,则我们就得到了最终的所有聚类类别结果。

实验步骤

KNN算法实现步骤

  1. 加载数据:使用scikit-learn库中的datasets.load_iris ()函数加载鸢尾花数据集,该数据集包含150个样本,每个样本有4个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度),以及对应的类别标签(3类鸢尾花)。
  2. 数据预处理:将数据集划分为训练集和测试集,采用train_test_split函数,按照7:3的比例划分,即70%的数据用于训练,30%的数据用于测试。同时,对特征数据进行标准化处理(StandardScaler),消除不同特征量纲的影响,使每个特征的均值为0,方差为1。
  3. 创建KNN模型:调用scikit-learn库中的KNeighborsClassifier类,设置k值(通过多次测试后,此处选择k=9),距离度量方式采用默认的欧氏距离。
  4. 模型训练:由于KNN是“懒惰学习”算法,训练过程仅需将训练集数据和标签传入模型的fit()方法,模型会保存训练数据,无需进行复杂的参数学习。
  5. 模型预测:将测试集特征数据传入模型的predict()方法,得到测试集的预测标签。
  6. 模型评估:通过计算准确率(accuracy)、精确率(precision)、召回率(recall)和F1分数(F1-score)来评估模型性能,同时绘制混淆矩阵直观展示分类结果。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.datasets import load_iris
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import StandardScaler
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
    import seaborn as sns

    # 1. 加载数据
    iris = load_iris()
    X = iris.data # 特征数据(4个特征)
    y = iris.target # 类别标签(0,1,2)

    # 2. 数据预处理
    # 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
    # 标准化处理
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)

    # 3. 创建KNN模型
    k = 9
    knn_model = KNeighborsClassifier(n_neighbors=k, metric='euclidean')

    # 4. 模型训练
    knn_model.fit(X_train_scaled, y_train)

    # 5. 模型预测
    y_pred = knn_model.predict(X_test_scaled)

    # 6. 模型评估
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred, average='macro')
    recall = recall_score(y_test, y_pred, average='macro')
    f1 = f1_score(y_test, y_pred, average='macro')
    conf_matrix = confusion_matrix(y_test, y_pred)

    print(f"KNN算法(k={k})评估结果:")
    print(f"准确率(Accuracy):{accuracy:.4f}")
    print(f"精确率(Precision):{precision:.4f}")
    print(f"召回率(Recall):{recall:.4f}")
    print(f"F1分数(F1-score):{f1:.4f}")

    # 绘制混淆矩阵
    plt.figure(figsize=(8, 6))
    sns.heatmap(conf_matrix, annot=True, cmap='Blues', fmt='d', xticklabels=iris.target_names, yticklabels=iris.target_names)
    plt.xlabel('Predicted Label')
    plt.ylabel('True Label')
    plt.title('KNN Confusion Matrix')
    plt.show()

K-means算法实现步骤

  1. 加载数据:同样使用鸢尾花数据集,仅提取特征数据(无需标签,因为聚类是无监督学习)。
  2. 数据预处理:对特征数据进行标准化处理,确保各特征对聚类结果的影响权重一致。
  3. 确定k值:由于鸢尾花数据集真实类别数为3,此处设置k=3(也可通过肘部法则等方法确定最优k值)。
  4. 创建K-means模型:调用scikit-learn库中的KMeans类,设置n_clusters=3random_state=42以保证实验可重复。
  5. 模型训练与聚类:将标准化后的特征数据传入模型的fit()方法,模型会自动完成聚类,得到每个样本的聚类标签。
  6. 结果可视化与评估:由于数据集有4个特征,采用主成分分析(PCA)将特征降维至2维,绘制聚类散点图。同时,使用调整兰德指数(ARI)和轮廓系数(Silhouette Score)评估聚类效果(ARI越接近1表示聚类结果与真实标签越一致,轮廓系数越接近1表示聚类效果越好)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import adjusted_rand_score, silhouette_score
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import load_iris

# 设置中文字体支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

# 1. 加载数据(仅使用特征数据)
iris = load_iris()
X = iris.data
y_true = iris.target # 真实标签,用于评估聚类效果

# 2. 数据预处理
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 3. 确定k值(此处k=3,与真实类别数一致)
k = 3

# 4. 创建K-means模型
kmeans_model = KMeans(n_clusters=k, random_state=42)

# 5. 模型训练与聚类
y_pred_cluster = kmeans_model.fit_predict(X_scaled)

# 6. 结果可视化(PCA降维至2维)
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

plt.figure(figsize=(8, 6))
plt.scatter(X_pca[:, 0], X_pca[:, 1], c=y_pred_cluster, cmap='viridis', s=50, alpha=0.8)
# 绘制聚类中心
centers_pca = pca.transform(kmeans_model.cluster_centers_)
plt.scatter(centers_pca[:, 0], centers_pca[:, 1], c='red', marker='x', s=200, linewidths=3, label='聚类中心')
plt.xlabel('主成分1')
plt.ylabel('主成分2')
plt.title(f'K-means聚类结果可视化(k={k})')
plt.legend()
plt.show()

# 模型评估
ari = adjusted_rand_score(y_true, y_pred_cluster)
silhouette = silhouette_score(X_scaled, y_pred_cluster)

print(f"K-means算法(k={k})评估结果:")
print(f"调整兰德指数(ARI):{ari:.4f}")
print(f"轮廓系数(Silhouette Score):{silhouette:.4f}")

DBSCAN算法实现步骤

  1. 加载数据:使用鸢尾花数据集的特征数据。
  2. 数据预处理:对特征数据进行标准化处理。
  3. 设置DBSCAN参数:DBSCAN的核心参数为ε(邻域半径)和min_samples(邻域内最小样本数),此处设置ε=0.5min_samples=10(通过参数调优确定)。
  4. 创建DBSCAN模型:调用scikit-learn库中的DBSCAN类,设置eps=0.5min_samples=10,距离度量采用欧氏距离。
  5. 模型训练与聚类:将标准化后的特征数据传入模型的fit()方法,模型会自动识别密度相连的样本形成簇,将噪声点标记为-1。
  6. 结果可视化与评估:同样使用PCA降维至2维,绘制聚类散点图(不同颜色表示不同簇,黑色表示噪声点)。使用调整兰德指数(ARI)和轮廓系数评估聚类效果(忽略噪声点对轮廓系数的影响)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    from sklearn.cluster import DBSCAN
    import matplotlib.pyplot as plt
    from sklearn.preprocessing import StandardScaler
    from sklearn.datasets import load_iris
    from sklearn.decomposition import PCA
    from sklearn.metrics import adjusted_rand_score, silhouette_score
    import numpy as np

    # 设置中文字体支持
    plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
    plt.rcParams['axes.unicode_minus'] = False

    # 1. 加载数据
    iris = load_iris()
    X = iris.data
    y_true = iris.target

    # 2. 数据预处理
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(X)

    # 3. 设置DBSCAN参数
    eps = 0.5
    min_samples = 10

    # 4. 创建DBSCAN模型
    dbscan_model = DBSCAN(eps=eps, min_samples=min_samples, metric='euclidean')

    # 5. 模型训练与聚类
    y_pred_dbscan = dbscan_model.fit_predict(X_scaled)

    # 统计簇的数量和噪声点数量
    n_clusters = len(set(y_pred_dbscan)) - (1 if -1 in y_pred_dbscan else 0)
    n_noise = list(y_pred_dbscan).count(-1)

    print(f"DBSCAN算法聚类结果:")
    print(f"簇的数量:{n_clusters}")
    print(f"噪声点数量:{n_noise}")

    # 6. 结果可视化(PCA降维至2维)
    pca = PCA(n_components=2)
    X_pca = pca.fit_transform(X_scaled)

    plt.figure(figsize=(8, 6))
    # 绘制聚类簇
    colors = plt.cm.viridis(np.linspace(0, 1, len(set(y_pred_dbscan))))
    for i, cluster in enumerate(set(y_pred_dbscan)):
    if cluster == -1:
    # 噪声点
    plt.scatter(X_pca[y_pred_dbscan == cluster, 0], X_pca[y_pred_dbscan == cluster, 1],
    c='black', marker='x', s=100, label='噪声点', zorder=3)
    else:
    plt.scatter(X_pca[y_pred_dbscan == cluster, 0], X_pca[y_pred_dbscan == cluster, 1],
    c=[colors[i]], s=50, alpha=0.8, label=f'簇{cluster+1}', zorder=2)

    plt.xlabel('主成分1')
    plt.ylabel('主成分2')
    plt.title(f'DBSCAN聚类结果可视化(ε={eps}, min_samples={min_samples})')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.show()

    # 模型评估(忽略噪声点计算ARI和轮廓系数)
    if n_clusters > 1:
    # 筛选非噪声点
    mask = y_pred_dbscan != -1
    ari_dbscan = adjusted_rand_score(y_true[mask], y_pred_dbscan[mask])
    silhouette_dbscan = silhouette_score(X_scaled[mask], y_pred_dbscan[mask])
    print(f"调整兰德指数(ARI):{ari_dbscan:.4f}")
    print(f"轮廓系数(Silhouette Score):{silhouette_dbscan:.4f}")
    else:
    print("聚类结果仅形成一个簇或全为噪声点,无法计算有效的评估指标")

实验数据及处理结果

KNN算法结果

  1. 评估指标:当k=9时,KNN算法在测试集上的评估结果如下:

    • 准确率(Accuracy):0.9556
    • 精确率(Precision):0.9688
    • 召回率(Recall):0.9556
    • F1分数(F1-score):0.9554
  2. 结果分析:准确率接近100%,说明KNN算法在鸢尾花数据集的分类任务中表现优异。混淆矩阵显示,仅2个测试样本被错误分类,其余样本均正确匹配真实标签。这是因为鸢尾花数据集的特征区分度较高,KNN通过计算特征相似度能够准确识别样本类别,且标准化处理消除了量纲影响,进一步提升了模型性能。

  3. 结果展示

K-means算法结果

  1. 评估指标k=3时,K-means算法的评估结果如下:
    • 调整兰德指数(ARI):0.4328
    • 轮廓系数(Silhouette Score):0.4799
  2. 结果分析:ARI为0.4328,说明聚类结果与真实标签具有一定的一致性;轮廓系数为0.4799,表明聚类的紧致性和分离度处于较好水平。从可视化散点图可以看出,三个簇的分布较为集中,聚类中心位置合理,但存在少量样本交叉重叠的情况。这是因为K-means算法基于欧氏距离,对球形分布的簇聚类效果较好,而鸢尾花数据集的部分类别边界较为接近,导致少量样本聚类偏差。
  3. 实验结果

DBSCAN算法结果

  1. 聚类结果统计:当ε=0.5min_samples=10时,DBSCAN算法的聚类结果如下:
    • 簇的数量:3
    • 噪声点数量:89
  2. 评估指标
    • 调整兰德指数(ARI):1.0000
    • 轮廓系数(Silhouette Score):0.7101
  3. 结果分析:DBSCAN算法识别出3个主要簇和89个噪声点,ARI为1.0000,高于K-means算法,说明其聚类结果与真实标签的一致性较强。轮廓系数为0.7101,同样高于K-means,表明簇内样本的紧密性较好。从可视化图可以看出,DBSCAN能够识别出非球形的簇结构,但由于鸢尾花数据集的三类样本在特征空间中存在一定的重叠,且部分样本密度较低,导致这些样本被判定为噪声点。若调整εmin_samples参数(如增大ε或减小min_samples),可能会合并簇或减少噪声点数量,但需平衡聚类的准确性和噪声识别能力。
  4. 实验结果

实验总结及心得

实验遇到的问题及解决过程

  1. KNN算法的k值选择问题:初始时随意设置k=10,导致模型准确率较低。通过查阅资料了解到k值过小易过拟合,过大易欠拟合,随后通过尝试,发现k=9时模型性能最优,最终确定k=9作为实验参数。
  2. K-means算法的k值确定问题:一开始不确定k值的选择方法,后来学习了肘部法则(绘制不同k值对应的惯性值曲线,曲线拐点即为最优k值),结合鸢尾花数据集的真实类别数,最终确定k=3
  3. DBSCAN算法的参数调优问题:初始设置ε=0.8min_samples=5时,聚合度较差。通过逐步减小ε、增大min_samples,多次实验后发现ε=0.5min_samples=10时,聚类结果相对合理,既保证了簇的完整性,又未产生过多噪声点。

错误及原因分析

  1. 数据未标准化导致模型性能下降:在KNN和K-means算法的初始实验中,未对特征数据进行标准化处理,导致花萼长度(单位:cm,数值范围4.3-7.9)和花萼宽度(单位:cm,数值范围2.0-4.4)的量纲差异影响距离计算,KNN的准确率降至0.85,K-means的ARI降至0.6。分析原因是未标准化的数据会使数值范围大的特征在距离计算中占据主导地位,导致模型偏向该特征,忽略其他特征的影响。
  2. DBSCAN算法参数设置不当导致聚类失败:当ε设置过大(如ε=1.0)时,DBSCAN 将所有样本聚为1个簇,无噪声点;当ε设置过小(如ε=0.2时,所有样本均被判定为噪声点。原因是ε决定了邻域的大小,过大则所有样本密度都满足条件,过小则无样本满足密度要求,min_samples的设置需与ε匹配,才能得到合理的聚类结果。

实验体会和收获

通过本次实验,我深入理解了KNN、K-means和DBSCAN三种算法的核心原理。KNN的“懒惰学习”特性使其无需训练过程,但预测时计算量较大;K-means基于划分的思想,适合球形分布的簇,计算效率高;DBSCAN基于密度的思想,能够识别任意形状的簇和噪声点,但对参数敏感。将理论知识应用到代码实现中,让我对算法的理解更加深刻。