广州大学机器学习与数据挖掘实验三

实验三 聚类分析

一、 实验目的
本实验课程是计算机、人工智能、软件工程等专业学生的一门专业课程,通过实验,帮助学生更好地掌握数据挖掘与机器学习相关概念、技术、原理、应用等;通过实验提高学生编写实验报告、总结实验结果的能力;使学生对机器学习模型、算法等有比较深入的认识。要掌握的知识点如下:

  1. 掌握机器学习中涉及的相关概念、模型、算法;
  2. 熟悉机器学习模型训练、验证、测试的流程;
  3. 熟悉常用的数据预处理方法;
  4. 掌握聚类分析问题的表示、求解及编程。

二、基本要求

  1. 实验前,复习《数据挖掘与机器学习》课程中的有关内容。
  2. 准备好实验数据,编程完成实验内容,收集实验结果。
  3. 独立完成实验报告。

三、实验软件
推荐使用Python编程语言(允许使用numpy库,需实现详细实验步骤,不允许直接调用scikit-learn中回归、分类、聚类等高层API)。

四、实验内容:
基于IRIS鸢尾花数据集,完成关于鸢尾花的聚类分析。

1 准备数据集并认识数据
下载IRIS数据集
https://archive.ics.uci.edu/ml/datasets/iris
了解数据集各个维度特征的含义在这里插入图片描述

2 探索数据并预处理数据
观察数据集各个维度特征的数值类型与分布
挑选sepal length、petal length两维特征作为聚类依据

3 求解聚类中心
编程实现k-means聚类、混合高斯聚类

4 测试和评估模型
在数据集上计算聚类的性能指标

五、学生实验报告
(1)简要介绍k-means、混合高斯聚类的原理
k-means原理:
k-means算法是常用的一种聚类算法。算法的输入为一个样本集(点集),通过该算法可以将样本进行聚类,具有相似特征的样本聚为一类。
算法思想:
假设我们要把数据分成K个类,则可分为以下步骤:
1、随机选k个点,作为聚类中心
2计算每个点分别到k个聚类中心的距离,然后将该点分给距离最近的那个中心点,这样就形成了k个簇
3、再重新计算每个簇的质心(均值)
4、重复2-3步,直到质心的位置不在发生变化或者达到设定的迭代次数

混合高斯聚类原理:
①假设观测数据y1,y2,…,yN由高斯混合模型生成,即
在这里插入图片描述

其中
在这里插入图片描述

我们用EM算法估计高斯混合模型的参数θ

②初始化模型参数:
在这里插入图片描述

③EM算法的E步:
计算
在这里插入图片描述

(有点相当于朴素贝叶斯中计算后验概率,用先验概率乘以条件概率)
这是当前模型参数下第j个观测数据来自第k个分模型的概率,称为分模型k对观测数据yj的影响度

④EM算法的M步:更新模型参数
在这里插入图片描述

重复E步和M步直到模型收敛

(2)程序清单(包含详细求解步骤)
k-means聚类算法:
①要引进的库
在这里插入图片描述

②导入数据集,观察数据特点
在这里插入图片描述

③挑选sepal length、petal length两维特征作为聚类依据,将类别’class’列的值赋给labels,并进行标签编码
在这里插入图片描述

④初始化聚类中心
在这里插入图片描述

⑤开始训练:计算每个点分别到k个聚类中心的距离,然后将该点分给距离最近的那个中心点
在这里插入图片描述

⑥重新计算每个簇的质心(均值)
在这里插入图片描述

⑦ 迭代第⑤、⑥步max_iter=1000次

⑧显示k-means算法的聚类情况,同时也显示实际数据的归类情况
在这里插入图片描述

⑨计算准确率(由于聚类算法只会将原始数据样本划分为K个簇,但是并不会告诉我们每个簇分别对应哪个类别,因此我们用排列组合的方法,分别计算每种情况的准确率,选最高的为最终的准确率值)
在这里插入图片描述
在这里插入图片描述

混合高斯算法
①要引入的库
在这里插入图片描述

②导入数据集,观察数据特点
在这里插入图片描述

③挑选sepal length、petal length两维特征作为聚类依据,用data存储这两列数据。labels存储类别‘class’列的值,并进行标签编码
在这里插入图片描述

④实例化类GMM_EM的对象gmm

执行类的__init__函数,确定进行聚类的类数为3 即n_components=3
在这里插入图片描述
在这里插入图片描述

⑤调用对象gmm中的fit_predict函数,得到用混合高斯模型进行聚类的结果
在这里插入图片描述
在这里插入图片描述

分析fit_predict(data)函数内进行的步骤:
1’进行数据预处理,调用类内函数preprocess()
在这里插入图片描述

data数据集中大小为150,特征数为2

2’调用类内函数_init()初始化高斯模型的参数
在这里插入图片描述

3’在max_iter迭代次数为1000的情况下执行EM算法中的E、M步骤,当前后两次迭代概率的变化<1e-6时,即可跳出迭代

E步骤的函数对应该算法
在这里插入图片描述
在这里插入图片描述

_e_step()函数内调用的guass()函数定义如下:
在这里插入图片描述

M步骤的函数对应算法:
在这里插入图片描述
在这里插入图片描述

⑥求解混合高斯模型聚类中心
在这里插入图片描述

⑦画图显示k-means算法的聚类情况及中心点,同时也显示实际数据的归类情况
在这里插入图片描述

⑧计算准确率,方法与K-means中的相同

(3)展示实验结果,可视化聚类结果
k-means算法:
k-means算法的聚类情况:
在这里插入图片描述

数据的实际归类情况:
在这里插入图片描述

该聚类算法得出准确率为:
在这里插入图片描述

混合高斯算法:( EM算法是对初始值敏感的,修改初始化的值会发现模型性能变化很大)
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

固定一个分类较好的随机情况:
在这里插入图片描述

此时混合高斯算法聚类情况:
在这里插入图片描述

数据实际分类情况:
在这里插入图片描述

此时该聚类算法得出准确率为:
在这里插入图片描述

(4)讨论实验结果,分析k-means聚类数量与聚类指标的关系

由于在根据鸢尾花数据集进行编码时,我就先默认聚类数量为3,代码中的许多运算都是固定死了针对3这个聚类数量,代码很不灵活,所以该小节的关系分析没法做出。
在网上查找了一下针对k-means聚类数量与聚类指标的关系问题,但找到的信息无法理解分析。针对k-means聚类数目的确定,得到的相关信息是:k-means分类数目k值很难估计,不确定分成多少类才最合适。

(5)源代码
k-means

import numpy as np
import pandas as pd
import random
import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder

iris_data=pd.read_csv("Iris/iris.data",header=None,names=['sepal length','sepal width','petal length',
                                                          'petal width','class'])


print(iris_data.info())

#发现有iris-setosa   iris-versicolor   iris-virginica三种
print(iris_data['class'].value_counts())


#对labels进行标签编码
labels=iris_data['class'].values
label_encoder=LabelEncoder()
labels=label_encoder.fit_transform(labels)
# print(labels)

#挑选sepal length、petal length两维特征作为聚类依据
x_axis=iris_data['sepal length']  #series(150,)
y_axis=iris_data['petal length']

print(x_axis.shape)
print(y_axis.shape)


#随机三个index值,在150条数据集中随机选中三个种类的开始点的标号
indexList=random.sample(range(0,150),3)
print(indexList)

#随机初始的中心点
x_center1=x_axis[indexList[0]]
y_center1=y_axis[indexList[0]]
x_center2=x_axis[indexList[1]]
y_center2=y_axis[indexList[1]]
x_center3=x_axis[indexList[2]]
y_center3=y_axis[indexList[2]]

print(x_center1)
print(x_axis[0])



#---------------------开始训练  训练100次-------------------------

for i in range(100):
    # 用来装分属于三类的数据的index值
    belong1 = []
    belong2 = []
    belong3 = []

    #计算每条分数据别到3个聚类中心的距离
    for j in range(150):
        belong=0          #belong用来记录该条数据属于的类别
        dis_1=pow((x_axis[j]-x_center1),2)+pow((y_axis[j]-y_center1),2)
        dis_2=pow((x_axis[j]-x_center2),2)+pow((y_axis[j]-y_center2),2)
        dis_3=pow((x_axis[j]-x_center3),2)+pow((y_axis[j]-y_center3),2)

        #比较离三类中心点哪个更近,将该条数据归于距离更近的中心点所属类
        if dis_2<dis_1:
            belong=2
            if dis_3<dis_2:
                belong=3
        else:
            belong=1
            if dis_3<dis_1:
                belong=3
        # print(belong)
        if belong==1:
            belong1.append(j)
        elif belong==2:
            belong2.append(j)
        else:
            belong3.append(j)


    #进行center点的位置更新
    for k in range(len(belong1)):
        x_center1+=x_axis[belong1[k]]
        y_center1+=y_axis[belong1[k]]
    for k in range(len(belong2)):
        x_center2 += x_axis[belong2[k]]
        y_center2 += y_axis[belong2[k]]
    for k in range(len(belong3)):
        x_center3 += x_axis[belong3[k]]
        y_center3 += y_axis[belong3[k]]

    x_center1=x_center1/(1+len(belong1))
    x_center2=x_center2/(1+len(belong2))
    x_center3=x_center3/(1+len(belong3))
    y_center1 = y_center1 / (1 + len(belong1))
    y_center2 = y_center2 / (1 + len(belong2))
    y_center3 = y_center3 / (1 + len(belong3))


#y_pred用来装k-means聚类算法对每条数据所归到的类
#注意这里的类值1、2、3没有实际意义,跟实际数据labels中用标签编码所得的值0、1、2值没有对应关系
#仅仅是为了区分类别
y_pred=np.array(np.zeros(150))

for i in range(len(belong1)):
    y_pred[belong1[i]]=1

for i in range(len(belong2)):
    y_pred[belong2[i]]=2

for i in range(len(belong3)):
    y_pred[belong3[i]]=3

#k-means求出的聚类中心
x_center=[x_center1,x_center2,x_center3]
y_center=[y_center1,y_center2,y_center3]
#数据集的实际中心
x_ac_center=[x_axis[0:50].mean(),x_axis[50:100].mean(),x_axis[100:150].mean()]
y_ac_center=[y_axis[0:50].mean(),y_axis[50:100].mean(),y_axis[100:150].mean()]
#画图
#聚类算法的归类情况
plt.scatter(x_axis,y_axis,c=y_pred)
plt.scatter(x_center,y_center,c='r',marker='x')
plt.show()

#实际数据的归类情况
plt.scatter(x_axis,y_axis,c=labels)
plt.scatter(x_ac_center,y_ac_center,c='r',marker='x')
plt.show()

#计算准确率
#分别计算三种类别组合  0 1 2    1 0 2   0 2 1    1 2 0    2 1 0   2 0 1
y_pred_1=np.array(np.zeros(150))
y_pred_2=np.array(np.zeros(150))
y_pred_3=np.array(np.zeros(150))
y_pred_4=np.array(np.zeros(150))
y_pred_5=np.array(np.zeros(150))
y_pred_6=np.array(np.zeros(150))
for i in range(150):
    if y_pred[i]==1:
        y_pred_1[i]=0
        y_pred_2[i] = 1
        y_pred_3[i] = 0
        y_pred_4[i] = 1
        y_pred_5[i] = 2
        y_pred_6[i] = 2
    if y_pred[i]==2:
        y_pred_1[i] = 1
        y_pred_2[i] = 0
        y_pred_3[i] = 2
        y_pred_4[i] = 2
        y_pred_5[i] = 1
        y_pred_6[i] = 0
    if y_pred[i]==3:
        y_pred_1[i] = 2
        y_pred_2[i] = 2
        y_pred_3[i] = 1
        y_pred_4[i] = 0
        y_pred_5[i] = 0
        y_pred_6[i] = 1

def correct_rate(lei_list):
    correct_num = 0
    for i in range(150):
        if (lei_list[i] == labels[i]):
            correct_num += 1
    rate = correct_num / 150
    return rate

rate1=correct_rate(y_pred_1)
rate2=correct_rate(y_pred_2)
rate3=correct_rate(y_pred_3)
rate4=correct_rate(y_pred_4)
rate5=correct_rate(y_pred_5)
rate6=correct_rate(y_pred_6)

#比较
rate=[rate1,rate2,rate3,rate4,rate5,rate6]
max_rate=0
for i in range(6):
    if rate[i]>max_rate:
        max_rate=rate[i]

print('准确率为:',max_rate)

混合高斯聚类:

from scipy.stats import multivariate_normal
from sklearn import preprocessing
from sklearn.metrics import accuracy_score
import numpy as np
import pandas as pd
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt


class GMM_EM():
    def __init__(self, n_components, max_iter=1000, error=1e-6):
        self.n_components = n_components  # 混合模型由几个gauss模型组成
        self.max_iter = max_iter  # 最大迭代次数
        self.error = error  # 收敛误差
        self.samples = 0   #样本个数
        self.features = 0 #存储特征个数
        self.alpha = []  # 存储模型权重
        self.mu = []  # 存储均值
        self.sigma = []  # 存储标准差

    def _init(self, data):  # 初始化参数
        np.random.seed(4)
        self.mu = np.array(np.random.rand(self.n_components, self.features))
        #sigma中为每个分模型都初始化了一个二维随机变量的协方差矩阵
        self.sigma = np.array([np.eye(self.features) / self.features] * self.n_components)
        self.alpha = np.array([1.0 / self.n_components] * self.n_components)
        print(self.alpha.shape, self.mu.shape, self.sigma.shape)
        print(self.alpha,self.mu,self.sigma)


    def gauss(self, Y, mu, sigma):  # 直接调用多元正态分布的概率密度函数,计算高斯函数值
        return multivariate_normal.pdf(Y,mean=mu, cov=sigma )

    def preprocess(self, data):  # 数据预处理
        self.samples = data.shape[0]   #定义数据集大小
        self.features = data.shape[1]   #定义数据集的特征数
        pre = preprocessing.MinMaxScaler()  #进行了特征归一化
        return pre.fit_transform(data)

    def fit_predict(self, data):  # 拟合数据
        data = self.preprocess(data)   #进行数据预处理
        self._init(data)     #初始化模型参数
        weighted_probs = np.zeros((self.samples, self.n_components))
        print(weighted_probs.shape)  #用来存放进行E步后算出来的当前模型参数下每个观测数据来自第k个分模型的概率 shape(150,3)
        for i in range(self.max_iter):
            prev_weighted_probs = weighted_probs
            #e步
            weighted_probs = self._e_step(data)
            #change 当后验概率没有什么改变了的时候,即收敛的时候,停止迭代
            change = np.linalg.norm(weighted_probs - prev_weighted_probs)
            if change < self.error:
                break
            #m步
            self._m_step(data, weighted_probs)
        #比较每个观测数据来自三个分模型的概率,返回概率最大的分模型所代表列的列号
        return weighted_probs.argmax(axis=1)

    def _e_step(self, data):  # E步
        probs = np.zeros((self.samples, self.n_components))   #shape(150,3)
        for i in range(self.n_components):
            #调用类定义的gauss函数,计算不同高斯模型下数据集对应高斯函数值
            probs[:, i] = self.gauss(data, self.mu[i, :], self.sigma[i, :, :])

        weighted_probs = np.zeros(probs.shape)
        for i in range(self.n_components):
            weighted_probs[:, i] = self.alpha[i] * probs[:, i]
        for i in range(self.samples):
            #后验概率某类概率再除以三类的概率和
            weighted_probs[i, :] /= np.sum(weighted_probs[i, :])

        return weighted_probs

    def _m_step(self, data, weighted_probs):  # M步  进行mu,sigma,alpha的数值更新
        for i in range(self.n_components):
            #求出每一列的概率和 即每行数据属于某一具体类的概率和
            sum_probs_i = np.sum(weighted_probs[:, i])
            #axis=0计算每列的sum
            self.mu[i, :] = np.sum(np.multiply(data, np.mat(weighted_probs[:, i]).T), axis=0) / sum_probs_i

            self.sigma[i, :, :] = (data - self.mu[i, :]).T * np.multiply((data - self.mu[i, :]),
                                                                         np.mat(weighted_probs[:, i]).T) / sum_probs_i

            #行数  shape[0]
            self.alpha[i] = sum_probs_i / data.shape[0]


iris_data=pd.read_csv("Iris/iris.data",header=None,names=['sepal length','sepal width','petal length',
                                                          'petal width','class'])

#发现有iris-setosa   iris-versicolor   iris-virginica三种
print(iris_data['class'].value_counts())
labels=iris_data['class'].values

#对labels进行标签编码
label_encoder=LabelEncoder()
labels=label_encoder.fit_transform(labels)
# print(labels)

#挑选sepal length、petal length两维特征作为聚类依据
x_axis=iris_data['sepal length']  #series(150,)
y_axis=iris_data['petal length']

data=np.array(pd.concat([x_axis,y_axis],axis=1))

gmm = GMM_EM(3)
pre_label = gmm.fit_predict(data)


print(pre_label)
print(labels)

#混合高斯算法求出的聚类中心
num_0,num_1,num_2=[0,0,0]
xsum_0,xsum_1,xsum_2=[0,0,0]
ysum_0,ysum_1,ysum_2=[0,0,0]

for i in range(len(pre_label)):
    if pre_label[i]==0:
        num_0+=1
        xsum_0+=x_axis[i]
        ysum_0 += y_axis[i]
    elif pre_label[i]==1:
        num_1+=1
        xsum_1+=x_axis[i]
        ysum_1 += y_axis[i]
    else:
        num_2+=1
        xsum_2+=x_axis[i]
        ysum_2 += y_axis[i]

x_center_0=xsum_0/num_0
y_center_0=ysum_0/num_0
x_center_1=xsum_1/num_1
y_center_1=ysum_1/num_1
x_center_2=xsum_2/num_2
y_center_2=ysum_2/num_2

x_center=[x_center_0,x_center_1,x_center_2]
y_center=[y_center_0,y_center_1,y_center_2]
#数据集的实际中心
x_ac_center=[x_axis[0:50].mean(),x_axis[50:100].mean(),x_axis[100:150].mean()]
y_ac_center=[y_axis[0:50].mean(),y_axis[50:100].mean(),y_axis[100:150].mean()]


#画图
#画混合高斯聚类图
plt.scatter(x_axis,y_axis,c=pre_label)
plt.scatter(x_center,y_center,c='r',marker='x')
plt.xlabel('sepal length')
plt.ylabel('petal length')
plt.show()

#实际数据图
plt.scatter(x_axis,y_axis,c=labels)
plt.scatter(x_ac_center,y_ac_center,c='r',marker='x')
plt.xlabel('sepal length')
plt.ylabel('petal length')
plt.show()

# EM算法是对初始值敏感的,修改初始化的值会发现模型性能变化很大

#计算准确率
#分别计算三种类别组合  0 1 2    1 0 2   0 2 1    1 2 0    2 1 0   2 0 1
y_pred_1=np.array(np.zeros(150))
y_pred_2=np.array(np.zeros(150))
y_pred_3=np.array(np.zeros(150))
y_pred_4=np.array(np.zeros(150))
y_pred_5=np.array(np.zeros(150))
y_pred_6=np.array(np.zeros(150))
for i in range(150):
    if pre_label[i]==0:
        y_pred_1[i]=0
        y_pred_2[i] = 1
        y_pred_3[i] = 0
        y_pred_4[i] = 1
        y_pred_5[i] = 2
        y_pred_6[i] = 2
    if pre_label[i]==1:
        y_pred_1[i] = 1
        y_pred_2[i] = 0
        y_pred_3[i] = 2
        y_pred_4[i] = 2
        y_pred_5[i] = 1
        y_pred_6[i] = 0
    if pre_label[i]==2:
        y_pred_1[i] = 2
        y_pred_2[i] = 2
        y_pred_3[i] = 1
        y_pred_4[i] = 0
        y_pred_5[i] = 0
        y_pred_6[i] = 1

def correct_rate(lei_list):
    correct_num = 0
    for i in range(150):
        if (lei_list[i] == labels[i]):
            correct_num += 1
    rate = correct_num / 150
    return rate

rate1=correct_rate(y_pred_1)
rate2=correct_rate(y_pred_2)
rate3=correct_rate(y_pred_3)
rate4=correct_rate(y_pred_4)
rate5=correct_rate(y_pred_5)
rate6=correct_rate(y_pred_6)

#比较
rate=[rate1,rate2,rate3,rate4,rate5,rate6]
max_rate=0
for i in range(6):
    if rate[i]>max_rate:
        max_rate=rate[i]

print('准确率为:',max_rate)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值