机器学习期末复习 K均值和混合高斯的异同点

相同点:

k-means和混合高斯都是聚类算法,他们都是原型聚类,即需要初始化参数,应用于无监督的样本,每次划分后,所有的训练集都会有一个类别(这个区别于密度聚类,密度聚类划分后,有的训练集不一定有类别,这个训练集可能是噪声点)。

不同点:

对于簇数k

k-means算法在初始化时需要初始化簇数,这在某些情况下是不好估计簇数的

混合高斯算法是在运算中自己得出一个一个的簇,不用事先指定

K均值算法的优缺点:

优点:

1.对于大数据集,K均值聚类算法相对是可伸缩和高效的,它的计算复杂度是O(N*K*t)接近于线性,其中N是数据对象的数目,K是聚类的簇数,t是迭代的轮数。
2.尽管算法经常以局部最优结束,但一般情况下达到的局部最优已经可以满足聚类的需求。(因为在选取初始均值向量时,是随机选的,可以多选几次,通过最小平方误差来决定那个最好)
缺点:

1.需要人工预先确定初始K值,且该值和真实的数据分布未必吻合。
2.K均值只能收敛到局部最优,效果受到初始值很大。
3.易受到噪点的影响。
4.无法很好解决数据簇分布类别差别巨大的情况
 

k-means算法:(训练集为Iris数据集)

流程:

1.首先初步确定样本有几类,即确定簇数k;

2.然后根据簇数k,随机从样本中选择出k个样本作为初始的簇中心;

3.遍历样本集,计算出每个样本和每个簇中心的欧氏距离,并将这个样本归类到k个欧式距离中最小的欧氏距离的那一簇;

 

4.根据每个簇中的样本,更新簇中心;

5.重复3-4步,直至簇中心不变为止。

评价标准:最小化平方误差,DB指数

  • 优点:容易实现
  • 缺点:可能收敛到局部最小值,在大规模数据上收敛较慢

import math
import matplotlib.pyplot as plt
import random
import numpy as np


def readFile(filename, len):
    """
    读取iris.txt
    iris.txt为150行,每行有5个数据,前4个数据为特征,第5个为所属的类
    参数
    len:文件的行数
    filename:读取的文件的地址
    返回值
    filesave:保存的数据
    """
    f = open(filename, 'r')
    filesave = [[] for i in range(len)]  # 初始化全集
    for i in range(len):
        s = f.readline()
        s = s.strip('\n')
        s = s.strip(' ')
        x = s.split('\t')

        for j in range(5):
            filesave[i].append(eval(x[j]))

    return filesave

def AriGen():
    fig = plt.figure()
    data = []
    k1 = []
    for i in range(100):
        x = random.randint(0, 50)
        y = random.randint(0, 50)
        temp = [x, y, 1]
        while temp in k1:
            x = random.randint(0, 50)
            y = random.randint(0, 50)
            temp = [x, y, 1]
        k1.append(temp)
    x1 = [k1[i][0] for i in range(len(k1))]
    y1 = [k1[i][1] for i in range(len(k1))]
    plt.scatter(x1, y1, c='y', linewidths=1)

    k2 = []
    for i in range(200):
        x = random.randint(60, 140)
        y = random.randint(60, 140)
        temp = [x, y, 2]
        while temp in k1 or temp in k2:
            x = random.randint(60, 140)
            y = random.randint(60, 140)
            temp = [x, y, 2]
        k2.append(temp)

    x2 = [k2[i][0] for i in range(len(k2))]
    y2 = [k2[i][1] for i in range(len(k2))]
    plt.scatter(x2, y2, c='g', linewidths=1)

    k3 = []
    for i in range(300):
        x = random.randint(150, 250)
        y = random.randint(150, 250)
        temp = [x, y, 3]
        while temp in k1 or temp in k2 or temp in k3:
            x = random.randint(150, 250)
            y = random.randint(150, 250)
            temp = [x, y, 3]
        k3.append(temp)

    x3 = [k3[i][0] for i in range(len(k3))]
    y3 = [k3[i][1] for i in range(len(k3))]
    plt.scatter(x3, y3, c='r', linewidths=1)
    plt.title("init")
    plt.show()

    data.extend(k1)
    data.extend(k2)
    data.extend(k3)

    return data

def data3():
    x = []  # 数据集x属性
    y = []  # 数据集y属性
    x.extend(np.random.normal(loc=40.0, scale=10, size=100))  # 向x中放100个均值为30,方差为10,正态分布的随机数
    x.extend(np.random.normal(loc=80.0, scale=10, size=200))  # 向x中放100个均值为80,方差为10,正态分布的随机数
    x.extend(np.random.normal(loc=120.0, scale=10, size=300))
    y.extend(np.random.normal(loc=80.0, scale=10, size=100))  # 向y中放100个均值为80,方差为10,正态分布的随机数
    y.extend(np.random.normal(loc=40.0, scale=10, size=200))  # 向y中放100个均值为30,方差为10,正态分布的随机数
    y.extend(np.random.normal(loc=120.0, scale=10, size=300))

    c = [1] * 100  # c标签第一类为 1
    c.extend([2] * 200)  # # c标签第二类为 2
    c.extend([3] * 300)  # # c标签第二类为 3

    # 生成训练集与测试集=======================================
    lt = list(range(600))  # 得到一个顺序序列
    random.shuffle(lt)  # 打乱序列
    data3 = []
    for i in lt[0:600]:  # 截取部分做训练集
        temp = []
        temp.append(x[i])  # 加上数据集x属性
        temp.append(y[i])  # 加上数据集y属性
        temp.append(c[i])  # 加上数据集c标签
        data3.append(temp)

    print(data3)
    x = [data3[i][0] for i in range(len(data3))]
    y = [data3[i][1] for i in range(len(data3))]

    clor=['r','g','b']
    for i in range(len(x)):
        plt.scatter(x[i], y[i], c=clor[data3[i][-1]-1], linewidths=1)

    return data3


def showData(data):
    fig = plt.figure()
    m = plt.axes(projection='3d')
    color = ['r', 'g', 'b']
    ls = [[] for j in range(len(data[0]))]
    cindex = 0
    for i in range(len(data)):
        for j in range(len(data[0])):
            ls[j].append(data[i][j])
        if i % 49 == 0 and i != 0:
            m.scatter3D(ls[0], ls[1], ls[2], c=color[cindex], alpha=0.8)  # alpha透明度
            cindex += 1
            ls = [[] for j in range(len(data[0]))]
    plt.title("init")
    # plt.show()

class k_means:
    def __init__(self, data, k=3, len=150, num=4):
        """
        :param k:有k个簇
        :param len: 数据集的长度
        :param num: 有num个属性
        :param data: 原始数据集
        """
        self.k = k
        self.len = len
        self.num = num
        self.data = data#[[],[],[]]
        self.ls = []  # 保存初始均值向量

        label=1
        for i in range(k):#对每个簇,找出一个初始均值向量
            temp = random.randint(0, len - 1)#随机整数
            while self.data[temp][-1]!=label:
                temp = random.randint(0, len - 1)  # 随机整数
            self.ls.append(self.data[temp][:-1])
            label+=1

    def method(self):
        print("初始簇中心:",self.ls)
        lst = self.ls
        while 1:
            cu = [[] for i in range(self.k)]  # 初始化空,保存
            flag = 0
            # 根据均值向量进行分类
            for i in range(len(self.data)):  # 遍历数据集,对所有数据进行分类
                value = []  # 用于保存距离
                for k in range(self.k):  # 遍历所有簇,计算每个簇的距离,保存到value中
                    tp = 0
                    for j in range(self.num):  # 遍历这个样本的每个属性
                        tp += (lst[k][j] - self.data[i][j]) * (lst[k][j] - self.data[i][j])
                    tp = math.sqrt(tp)
                    value.append(tp)
                cu[value.index(min(value))].append(self.data[i])

            # 计算新的均值向量
            for i in range(self.k):  # 遍历cu每一个簇
                tl = [0 for i in range(self.num)]  # 用于保存新的均值向量
                for j in range(len(cu[i])):  # 遍历这个簇的所有数据
                    for p in range(self.num):  # 遍历每个数据的每个属性
                        tl[p] += cu[i][j][p]
                for j in range(self.num):
                    if len(cu[i]) != 0:
                        tl[j] /= len(cu[i])

                result = 0  # 用于计算新的均值向量与旧的差距
                for j in range(self.num):
                    result += abs(lst[i][j] - tl[j])
                if result == 0:  # 如果一样,那么flag+=1
                    flag += 1
                else:
                    lst[i] = tl
            if flag == self.k:
                return cu,lst


    def evaluation(self):
        cu,lst= self.method()
        print("最终簇中心:",lst)
        if self.num==2:
            self.show2D(cu)
        else:
            self.show(cu)
        for i in range(self.k):
            ls = [0 for i in range(self.k + 1)]
            for j in range(len(cu[i])):
                if (cu[i][j][-1] == 1):
                    ls[1] += 1
                elif cu[i][j][-1] == 2:
                    ls[2] += 1
                elif cu[i][j][-1] == 3:
                    ls[3] += 1
            print("第{}簇中最多的是{},占比{}%".format(i + 1, ls.index(max(ls)), max(ls) / len(cu[i]) * 100))

        print("DB指数为:",self.DB(cu,lst))
        print("平方误差为:",self.errorRate(cu,lst))



    def show(self, cu):
        fig = plt.figure()
        m = plt.axes(projection='3d')
        color = ['r', 'g', 'b']
        for i in range(self.k):
            ls = [[] for j in range(self.num)]
            for j in range(len(cu[i])):
                for k in range(self.num):
                    ls[k].append(cu[i][j][k])
            m.scatter3D(ls[0], ls[1], ls[2], c=color[i], alpha=0.8)  # alpha透明度
        plt.title("k-means")
        # plt.show()
    def show2D(self,cu):
        fig = plt.figure()
        color = ['r', 'g', 'b']
        for i in range(self.k):
            ls = [[] for j in range(self.num)]
            for j in range(len(cu[i])):
                for k in range(self.num):
                    ls[k].append(cu[i][j][k])
            plt.scatter(ls[0], ls[1], c=color[i], alpha=0.8)  # alpha透明度
        plt.title("k-means")


    def avg(self,ls):
        t=0
        for i in range(len(ls)):
            for j in range(i+1,len(ls)):
                t+=self.dist(ls[i],ls[j])
        t=t*2/(len(ls)*(len(ls)-1))
        return t

    def dist(self,lst1,lst2):
        t=0
        for i in range(len(lst1)):
            t+=(lst1[i]-lst2[i])*(lst1[i]-lst2[i])
        t=math.sqrt(t)
        return t

    def DB(self,cu,lst):
        db=0
        for i in range(len(cu)):
            l=[]
            for j in range(len(cu)):
                if i !=j:
                    l.append((self.avg(cu[i])+self.avg(cu[j]))/self.dist(lst[i],lst[j]))
            db += max(l)
        db=db/self.k
        return db

    def errorRate(self,cu,lst):
        error=0
        for k in range(len(cu)):
            for i in range(len(cu[k])):
                for j in range(len(cu[k][i])-1):
                    error+=(cu[k][i][j]-lst[k][j])*(cu[k][i][j]-lst[k][j])
        return error

if __name__ == "__main__":
    1.
    # data = readFile("D:\\PythonProject_Class\\test_Data\\iris.txt", 150)
    # showData(data)
    # km = k_means(data)
    # km.evaluation()
    # plt.show()

    2.
    # data = data3()
    # print(data)
    # km = k_means(data, len=600, num=2)
    # km.evaluation()
    # plt.show()

    3. 
    data = AriGen()
    print(data)
    km = k_means(data, len=600, num=2)
    km.evaluation()
    plt.show()



六.小结


        1. 聚类是一种无监督的学习方法。聚类区别于分类,即事先不知道要寻找的内容,没有预先设定好的目标变量。

        2. 聚类将数据点归到多个簇中,其中相似的数据点归为同一簇,而不相似的点归为不同的簇。相似度的计算方法有很多,具体的应用选择合适的相似度计算方法

        3. K-means聚类算法,是一种广泛使用的聚类算法,其中k是需要指定的参数,即需要创建的簇的数目,K-means算法中的k个簇的质心可以通过随机的方式获得,但是这些点需要位于数据范围内。在算法中,计算每个点到质心得距离,选择距离最小的质心对应的簇作为该数据点的划分,然后再基于该分配过程后更新簇的质心。重复上述过程,直至各个簇的质心不再变化为止。

        4. K-means算法虽然有效,但是容易受到初始簇质心的情况而影响,有可能陷入局部最优解。为了解决这个问题,可以使用另外一种称为二分K-means的聚类算法。二分K-means算法首先将所有数据点分为一个簇;然后使用K-means(k=2)对其进行划分;下一次迭代时,选择使得SSE下降程度最大的簇进行划分;重复该过程,直至簇的个数达到指定的数目为止。实验表明,二分K-means算法的聚类效果要好于普通的K-means聚类算法。
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值