摘要:IRIS数据是数据科学领域最著名的数据集。该数据集被广泛用于数据分析、挖掘等入门教学,Fisher的论文是该领域的经典之作,至今仍被频繁引用。本文基于Python语言,对IRIS数据集进行聚类分析,分别采用KNN聚类算法和基于TensorFlow的方法进行聚类,并对两种方法的聚类结果进行对比。

关键词:聚类分析,Python,IRIS数据集

引言

无监督学习是机器学习的一种方法,和监督学习相对应。在实际应用中,监督学习要求在训练中为每个样本提供预测量的真实值,即对训练样本进行标记,这在有些应用场合是有困难的。比如在医疗诊断中,如果要通过监督学习来获得诊断模型,就需要请专业的医生对大量的病例及它们的医疗影像资料进行精确标注,这需要耗费大量的人力,且效率很低。在这种情况下通常使用无监督学习方法,即在不提供监督信息(预测量的真实值)的条件下进行学习。

在非监督学习中,所有数据没有标记,但是这些数据会呈现出聚群的结构,相似类型的数据会聚集在一起。把这些没有标记的数据分成一个个组合,就是聚类。聚类法可以应用于商品推荐、景区提取、新闻分类、异常检测等。本文主要利用经典的鸢尾花(IRIS)来实现python的简单的聚类分析。

一、聚类分析简述

聚类就是一种寻找数据之间内在结构的技术。聚类把全体数据实例组织成一些相似组,而这些相似组被称作簇。处于相同簇中的数据实例彼此相同,处于不同簇中的实例彼此不同。

数据之间的相似性是通过定义一个距离或者相似性系数来判别的。图 1 显示了一个按照数据对象之间的距离进行聚类的示例,距离相近的数据对象被划分为一个簇。

图1 聚类分析示意

聚类分析可以应用在数据预处理过程中,对于复杂结构的多维数据可以通过聚类分析的方法对数据进行聚集,使复杂结构数据标准化

二、IRIS数据集

IRIS数据集的中文名是安德森鸢尾花卉数据集,英文全称是Anderson’s IRIS data set。IRIS包含150个样本,对应数据集的每行数据。每行数据包含每个样本的四个特征和样本的类别信息,所以IRIS数据集是一个150行5列的二维表,如表1所示。

表1 IRIS数据集(部分)

Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species
1 5.1 3.5 1.4 0.2 IRIS-setosa
2 4.9 3 1.4 0.2 IRIS-setosa
3 4.7 3.2 1.3 0.2 IRIS-setosa
4 4.6 3.1 1.5 0.2 IRIS-setosa
5 5 3.6 1.4 0.2 IRIS-setosa
6 5.4 3.9 1.7 0.4 IRIS-setosa

IRIS数据集是用来给花做分类的数据集,每个样本包含了花萼长度、花萼宽度、花瓣长度、花瓣宽度四个特征(前4列),我们需要建立一个分类器,分类器可以通过样本的四个特征来判断样本属于山鸢尾、变色鸢尾还是维吉尼亚鸢尾(这三个名词都是花的品种)。

三、聚类分析过程

从统计学的观点看,聚类分析是通过数据建模简化数据的一种方法。传统的统计聚类分析方法包括系统聚类法、分解法、加入法、动态聚类法、有序样品聚类、有重叠聚类和模糊聚类等。采用k-均值、k-中心点等算法的聚类分析工具已被加入到许多著名的统计分析软件包中,如SPSS、SAS等。当然,随着人工智能技术的发展,越来越多的人更加倾向于使用深度学习网络来进行聚类,以求获得更加满意的聚类效果和预测效果。

3.1 KNN聚类算法

KNN的思路是如果一个样本在特征空间中的k个最邻近的样本中的大多数属于某一个类别,则该样本也划分为这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

KNN里最重要的一个参数就是K值的选择。选的过大或者过小都会导致聚类结果不理想。要求必须事先给出要生成的类的数目k,这个k值的选定非常难以估计,这也是KNN算法的缺点之一。在本文中,通过迭代不同的K,观察最终的聚类效果。

具体的设计思路如下:

  • 从测试集中取测试样本inst

  • 依次计算inst与训练集中样本x_train[i]之间的欧式距离,并记下x_train[i]的类别属性t_train[i],都记入distancd中

  • 对distance从小到大排序,越小代表两者越相似

  • 统计distance前k个值的类型属性,累加到belongs中

  • 取belongs中类别值最大的一个,使用argmax

  • 判断argmax和测试样本实际属性,如果一致,说明预测正确

  • 不同的k值下得准确率如图2所示。

图2 准确率与K的关系

可以看到,不同k值会影响分类准确结果,k从1到50,准确率是先升再降,由于我们使用的IRIS数据集规模较小,这个趋势不是特别明显,不过也还是能看出这个规律的。其实对于特定分类任务,使用KNN算法,存在最优k值。

这里,设置K=3进行分类,查看聚类情况。并将原始的数据集的分类情况和通过KNN算法分类之后的分类情况进行可视化比对,图3所示。

K=3时聚类可视化结果

3.2 基于TensorFlow的聚类分析

神经网络主要是指一种仿造人脑设计的简化的计算模型,这种模型中包含了大量的用于计算的神经元,这些神经元之间会通过一些带有权重的连边以一种层次化的方式组织在一起。每一层的神经元之间可以进行大规模的并行计算,层与层之间进行消息的传递。

这里基于TnesorFlow框架,对IRIS数据集进行分析。设置了小型网络,将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行。因为有4个输入特征,所以输入层为4个输入节点;因为3分类,故输出层为3个神经元 。最终通过绘制Loss曲线以及准确率曲线,来更加直观的查看训练效果分别如图4和图5所示。

图4 训练的loss曲线

图5 训练的准确率曲线

四、总结

对于k-means聚类算法,必须要确定好生成簇的数目。当k取值过小时,簇内误方差会很大,训练出来的模型就会很差,但是当k过大时候又会导致分类过度,得不到想要的效果,因此k值的选取对于k-means算法来说至关重要。通常我们采用肘部法则选取K值,再依据轮廓系数,及各个数据集中数据的数量综合去评估哪个K值为最佳。然而,在实际应用中,肘部法则并没有那么实用,因为往往通过肘部法则得到的曲线,是十分模糊的,无法很好地选取一个合适的拐点。这里,我选择通过迭代不同的K值,绘制不同K值下聚类的准确率来判断聚类效果更加显著和合理。此外,通过深度学习来进行聚类,也有着良好的效果。

附录

(a)KNN-使用不同的 k值计算准确率

import numpy as np  
import sklearn  
import sklearn.datasets as db  
import matplotlib.pyplot as plt  
def load_IRIS(train_sample=130, normalize=True, one_hot_label=True):  
    IRIS_set = db.load_IRIS()  
    if normalize:  
        mean = np.average(IRIS_set.data, axis=0)  # 求得均值  
        variance = np.std(IRIS_set.data, axis=0)  # 求得方差  
        IRIS_set.data = IRIS_set.data - mean  
        IRIS_set.data = IRIS_set.data / variance  

    train_mask = np.random.choice(150, train_sample, replace=False)  # 150个数据中随机选择20个数,不放加抽样,即replace=False  
    base_array = np.array([i for i in range(0, 150)])  # 构造从0到100的数据  
    test_mask = np.delete(base_array, train_mask, axis=0)  # 取训练集数据  

    x_train = IRIS_set.data[train_mask]  # 训练集数据  
    t_train = IRIS_set.target[train_mask]  # 训练集标签  
    x_test = IRIS_set.data[test_mask]  # 测试集数据  
    t_test = IRIS_set.target[test_mask]  # 测试集标签  

    return (x_train, t_train), (x_test, t_test)  


(x_train, t_train), (x_test, t_test) = load_IRIS(train_sample=100, normalize=False, one_hot_label=False)  

K = []  
Acc = []  
for k in range(1, 50):  
    acc = 0  
    # 没有训练过程,直接进行测试,依次拿测试集数据去和训练集数据比距离,  
    for i in range(0, len(x_test)):  
        # for i in range(0, 1):  
        inst = x_test[i]  
        distance = np.zeros((len(x_train), 2))  # 二维的,第一维记录距离,第二维记录类别  
        for j in range(0, len(x_train)):  
            # 计算样本点到训练数据集中所有点的欧式距离  
            distance[j, 0] = np.sqrt(np.sum((inst - x_train[j]) ** 2))  
            distance[j, 1] = t_train[j]  # 第j个数据属于什么类型的  

        # 对第0维距离数据从小到大排序  
        dist = distance[:, 0]  
        # sort_mapping = np.argsort(-dist)  
        sort_mapping = np.argsort(dist)  # 这里我们希望数据从小到到排列,即升序  
        distance = distance[sort_mapping]  

        # 统计前k个最小距离中所属的类别多少  
        belongs = np.zeros(3)  # 一共就三个类别  
        for u in range(0, k):  
            belongs[int(distance[u, 1])] = belongs[int(distance[u, 1])] + 1  

        belong = np.argmax(belongs)  # 找出类最多的一个  
        if belong == t_test[i]:  
            acc = acc + 1  

    print(acc, len(t_test), acc / len(t_test))  
    acc = acc / len(t_test)  

    K.append(k)  
    Acc.append(acc)  

plt.plot(K, Acc)  
plt.show()  

(b)K=3的KNN聚类分析结果

from sklearn import datasets  # 存放鸢尾花数据  
from sklearn.cluster import KMeans  # 机器学习模型  
import matplotlib.pyplot as plt  
import pandas as pd  

IRIS = datasets.load_IRIS()  
IRIS_X = IRIS.data  # 花朵属性  
IRIS_y = IRIS.target  # 花朵类别  

km = KMeans(n_clusters=3)  # 设定簇的定值为3  
km.fit(IRIS_X)  # 对数据进行聚类  

# 各簇中心坐标  
center = km.cluster_centers_  
print(center)  

# 各簇聚类数量  
num = pd.Series(km.labels_).value_counts()  
print(num)  

# 数据聚类结果  
y_train = pd.Series(km.labels_)  
y_train.rename('res', inplace=True)  
print(y_train)  

# 拼接数据与聚类结果  
result = pd.concat([pd.DataFrame(IRIS_X), y_train], axis=1)  
print(result)  

Category_one = result[result['res'].values == 0]  
k1 = result.iloc[Category_one.index]  
# print(k1)  
Category_two = result[result['res'].values == 1]  
k2 = result.iloc[Category_two.index]  
# print(k2)  
Category_three = result[result['res'].values == 2]  
k3 = result.iloc[Category_three.index]  
# print(k3)  


# 原始数据的特征散点图  
plt.scatter(IRIS_X[:50, 2], IRIS_X[:50, 3], label='setosa', marker='o', c='yellow')  
plt.scatter(IRIS_X[50:100, 2], IRIS_X[50:100, 3], label='versicolor', marker='o', c='green')  
plt.scatter(IRIS_X[100:, 2], IRIS_X[100:, 3], label='virginica', marker='o', c='blue')  

# 机器学习后数据的特征散点图  
plt.scatter(k1.iloc[:, 2], k1.iloc[:, 3], label='ML_CLASS_one', marker='+', c='brown')  
plt.scatter(k2.iloc[:, 2], k2.iloc[:, 3], label='ML_CLASS_two', marker='+', c='red')  
plt.scatter(k3.iloc[:, 2], k3.iloc[:, 3], label='ML_CLASS_three', marker='+', c='black')  
plt.xlabel('petal length')  # 花瓣长  
plt.ylabel('petal width')  # 花瓣宽  
plt.title("result of KMeans")  
plt.legend()  
plt.show()

(C)TensorFlow分析IRIS代码

# -*- coding: UTF-8 -*-  
# 利用鸢尾花数据集,实现前向传播、反向传播,可视化loss曲线  
# 导入所需模块  
import tensorflow as tf  
from sklearn import datasets  
from matplotlib import pyplot as plt  
import numpy as np  

# 导入数据,分别为输入特征和标签  
x_data = datasets.load_IRIS().data  
y_data = datasets.load_IRIS().target  

# 随机打乱数据(因为原始数据是顺序的,顺序不打乱会影响准确率)  
# seed: 随机数种子,是一个整数,当设置之后,每次生成的随机数都一样(为方便教学,以保每位同学结果一致)  
np.random.seed(116)  # 使用相同的seed,保证输入特征和标签一一对应  
np.random.shuffle(x_data)  
np.random.seed(116)  
np.random.shuffle(y_data)  
tf.random.set_seed(116)  

# 将打乱后的数据集分割为训练集和测试集,训练集为前120行,测试集为后30行  
x_train = x_data[:-30]  
y_train = y_data[:-30]  
x_test = x_data[-30:]  
y_test = y_data[-30:]  

# 转换x的数据类型,否则后面矩阵相乘时会因数据类型不一致报错  
x_train = tf.cast(x_train, tf.float32)  
x_test = tf.cast(x_test, tf.float32)  

# from_tensor_slices函数使输入特征和标签值一一对应。(把数据集分批次,每个批次batch组数据)  
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)  
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)  

# 生成神经网络的参数,4个输入特征故,输入层为4个输入节点;因为3分类,故输出层为3个神经元  
# 用tf.Variable()标记参数可训练  
# 使用seed使每次生成的随机数相同(方便教学,使大家结果都一致,在现实使用时不写seed)  
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))  
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))  
lr = 0.1  # 学习率为0.1  
train_loss_results = []  # 将每轮的loss记录在此列表中,为后续画loss曲线提供数据  
test_acc = []  # 将每轮的acc记录在此列表中,为后续画acc曲线提供数据  
epoch = 500  # 循环500轮  
loss_all = 0  # 每轮分4个step,loss_all记录四个step生成的4个loss的和  

# 训练部分  
for epoch in range(epoch):  # 数据集级别的循环,每个epoch循环一次数据集  
    for step, (x_train, y_train) in enumerate(train_db):  # batch级别的循环 ,每个step循环一个batch  
        with tf.GradientTape() as tape:  # with结构记录梯度信息  
            y = tf.matmul(x_train, w1) + b1  # 神经网络乘加运算  
            y = tf.nn.softmax(y)  # 使输出y符合概率分布(此操作后与独热码同量级,可相减求loss)  
            y_ = tf.one_hot(y_train, depth=3)  # 将标签值转换为独热码格式,方便计算loss和accuracy  
            loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方误差损失函数mse = mean(sum(y-out)^2)  
            loss_all += loss.numpy()  # 将每个step计算出的loss累加,为后续求loss平均值提供数据,这样计算的loss更准确  
        # 计算loss对各个参数的梯度  
        grads = tape.gradient(loss, [w1, b1])  

        # 实现梯度更新 w1 = w1 - lr * w1_grad    b = b - lr * b_grad  
        w1.assign_sub(lr * grads[0])  # 参数w1自更新  
        b1.assign_sub(lr * grads[1])  # 参数b自更新  

    # 每个epoch,打印loss信息  
    print("Epoch {}, loss: {}".format(epoch, loss_all / 4))  
    train_loss_results.append(loss_all / 4)  # 将4个step的loss求平均记录在此变量中  
    loss_all = 0  # loss_all归零,为记录下一个epoch的loss做准备  

    # 测试部分  
    # total_correct为预测对的样本个数, total_number为测试的总样本数,将这两个变量都初始化为0  
    total_correct, total_number = 0, 0  
    for x_test, y_test in test_db:  
        # 使用更新后的参数进行预测  
        y = tf.matmul(x_test, w1) + b1  
        y = tf.nn.softmax(y)  
        pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即预测的分类  
        # 将pred转换为y_test的数据类型  
        pred = tf.cast(pred, dtype=y_test.dtype)  
        # 若分类正确,则correct=1,否则为0,将bool型的结果转换为int型  
        correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)  
        # 将每个batch的correct数加起来  
        correct = tf.reduce_sum(correct)  
        # 将所有batch中的correct数加起来  
        total_correct += int(correct)  
        # total_number为测试的总样本数,也就是x_test的行数,shape[0]返回变量的行数  
        total_number += x_test.shape[0]  
    # 总的准确率等于total_correct/total_number  
    acc = total_correct / total_number  
    test_acc.append(acc)  
    print("Test_acc:", acc)  
    print("--------------------------")  

# 绘制 loss 曲线  
plt.title('Loss Function Curve')  # 图片标题  
plt.xlabel('Epoch')  # x轴变量名称  
plt.ylabel('Loss')  # y轴变量名称  
plt.plot(train_loss_results, label="$Loss$")  # 逐点画出trian_loss_results值并连线,连线图标是Loss  
plt.legend()  # 画出曲线图标  
plt.show()  # 画出图像  

# 绘制 Accuracy 曲线  
plt.title('Acc Curve')  # 图片标题  
plt.xlabel('Epoch')  # x轴变量名称  
plt.ylabel('Acc')  # y轴变量名称  
plt.plot(test_acc, label="$Accuracy$")  # 逐点画出test_acc值并连线,连线图标是Accuracy  
plt.legend()  
plt.show()

版权声明 ▶ 本网站名称:陶小桃Blog
▶ 本文链接:https://www.52txr.cn/2022/pythonIRIS.html
▶ 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行核实删除。
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!
▶ 站长邮箱 [email protected][email protected] ,如不方便留言可邮件联系。

最后修改:2022 年 10 月 30 日
如果觉得我的文章对你有用,请随意赞赏