机器学习笔记(2)---K-means

机器学习笔记(2)—K-means

K-means作为一种聚类方法,是一种比较典型的无监督学习(unsupervised learning),即没有给定事先标记过的训练示例(期望),按照给定的训练模式,自动对输入的数据进行分类或分群。而监督式学习(supervised learning)不同之处在于预先给出了训练示例(期望),并通过训练示例(期望)来推测一个训练模式,再对训练数据进行操作。
两者并不难区分,以我的理解,简单的来说,无监督学习没有预先给出训练期望,而监督学习预先给出了训练期望。

K-平均聚类的目的是:把n个点划分到k个聚类中,其中每个聚类有一个聚类中心,使得每个点都属于离他最近聚类中心对应的聚类(比方说n1点距离聚类中心k1比它距离其他所有聚类中心近,那么它就归属于聚类k1),以之作为聚类的标准。
大致流程如下:
1.随机选取k个点作为聚类中心,每个点代表一个类的中心点;
2.计算每个数据点距离所有聚类中心的距离,再把每个数据点归到距离它最近的类中,划分出k个类;
3.将同一个类的所有数据点计算均值,得到一个此类的新中心点;
4.迭代2-3,直到新中心点变换的距离越来越小,趋紧不变;
方法简单易懂,但很遗憾的是,K-means是属于NP-hard的,这里并不多概述,可以理解为,算起来非常慢,同时验证其正确性也很难

这篇文章简单写一下K-means方法的对数据集的算法实现

(1)

首先生成数据集,4*100个二维向量,分别给出四个均值点与协方差矩阵来生成数据集
分别给出四个均值点与协方差矩阵来生成数据集

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D #This is for 3d scatter plots.
import math
np.random.seed(1)

def generate_data():
    X = np.zeros((400, 2))
    cov=1/30*np.eye(2)
    mean_1=np.array([0.5,0.5])
    mean_2=np.array([-0.5,0.5])
    mean_3=np.array([0.75,-0.5])
    mean_4=np.array([-0.25,-0.5])
    x1=np.random.multivariate_normal(mean_1,cov,100)
    x2=np.random.multivariate_normal(mean_2,cov,100)
    x3=np.random.multivariate_normal(mean_3,cov,100)
    x4=np.random.multivariate_normal(mean_4,cov,100)
    X=np.vstack((x1,x2,x3,x4))
    return X

X = generate_data()
plt.scatter(X[:,0], X[:,1])
plt.show()

数据集生成结果如下:
在这里插入图片描述

(2)

K-means的两个难点:
1.
我们从图中可以明显看出数据应划分为四个聚类,这也是由我们生成数据的方式决定的,但在更广泛的实际的机器学习中,这样一开始就决定好准确聚类数量的好运气非常少见
举个例子,如果数据集是在多维空间中分布,而不像我们现在简单的在二维空间分布的话,观察出聚类数量十分困难
再举个例子,如果数据集的聚类高达数百万个,那我们又如何找到这个k呢?
这也是造成K-means属于NP困难的原因之一
2.
K-means算法需要给出初始聚类的中心点,那么中心点如何选取呢?
有这样两个思路(当然也不止两个):
随机取数据集中k个数据,将它们定为初始的k个聚类中心;
将数据集中的所有数据随机划分到k个聚类中,再计算出每个聚类中数据的均值作为聚类中心
从大量的训练结果来看,后者的效果明显好于前者,但博主为了偷懒,用了第一种方法,第一种方法写起来简单,同时对于我们这个不大的数据集来说,也不怎么会影响最终效果

更为详细的划分思路:https://en.wikipedia.org/wiki/K-means_clustering#Initialization_methods

def initialise_parameters(m, n, X):
    C = np.zeros((m, n))
    for i in range(C.shape[0]):
        C[i]=X[np.random.randint(X.shape[0])]
    return C

C = initialise_parameters(4, 2, X)
print(C)

运行结果产生了四个初始聚类中心如下:
在这里插入图片描述

(3)

然后进行文章开头所述的step2,将数据集划分到距离最近的聚类中

def E_step(C, X):
    L = np.zeros(X.shape)
    def nearest(x,C):
        i=0
        m=np.linalg.norm(x - C[0], axis=0)
        for j in range(C.shape[0]):
            d=np.linalg.norm(x - C[j], axis=0)
            if d<m:
                m=d
                i=j
        return C[i]
    for i in range(X.shape[0]):
        L[i]=nearest(X[i],C)
    return L

L = E_step(C, X)
plt.scatter(L[:, 0], L[:, 1])
plt.show()

在这里插入图片描述

(4)

接下来是step3,重新计算聚类中心

def M_step(C, X, L):
    new_C = np.zeros(C.shape)
    for i in range(C.shape[0]):
        acc=np.zeros(C.shape[1])
        n=0
        for j in range(L.shape[0]):
            if np.array_equal(L[j, :], C[i,:]):
                acc=acc+X[j]
                n=n+1
        if n!= 0: new_C[i]=acc/n
        else: new_C[i]=X[np.random.randint(low=0, high=X.shape[0])]            
    return new_C

print('Before:')
print(C)
print('\nAfter:')
print(M_step(C, X, L))

在这里插入图片描述

(5)

接下来是step4,迭代step2与step3,不断重新划分聚类,得到更理想的训练结果,这里我们设置迭代次数为10次

def kmeans(X, m, i):
    L = np.zeros(X.shape)
    C=initialise_parameters(m, X.shape[1], X)
    oldC=None
    it=0
    while not (oldC == C).all() and it<i:
        oldC=C
        L=E_step(C, X)
        C=M_step(C, X, L)
        it=it+1
    return C, L

C_final, L_final = kmeans(X, 4, 10)
print('Initial Parameters:')
print(C)
print('\nFinal Parameters:')
print(C_final)

def allocator(X, L, c):
    cluster = []
    for i in range(L.shape[0]):
        if np.array_equal(L[i, :], c):
            cluster.append(X[i, :])
    return np.asarray(cluster)

colours = ['r', 'g', 'b', 'y']
for i in range(4):
    cluster = allocator(X, L_final, C_final[i, :])
    plt.scatter(cluster[:,0], cluster[:,1], c=colours[i])
plt.show()

运行结果如下,可以看出,原数据集被我们划分成了四个清晰可见的类:
在这里插入图片描述

(5)

拓展一下,假设我们生成的数据集如下

def gen_z():
    Z = np.random.randn(800, 2)
    for r in range(Z.shape[0]):
        s = np.linalg.norm(Z[r, :])
        Z[r, :] = Z[r, :] * (s < 0.3 or s > 1.45)
    return Z
Z = gen_z()
plt.scatter(Z[:, 0], Z[:, 1])
plt.show()

在这里插入图片描述
对于这样的数据集,像刚刚那样在二维空间内划分聚类是不太行得通的
因此,我们观察数据集的特性,其挖空了参数s属于(0.3,1.45)区间内的点,我们不妨将其扩增到三维空间,其三维空间的坐标z,采用<x,y>取内积的方式
在这里,我们运用到的方法是kernel method,对于低维空间难以划分的数据集,我们常常将其扩增至更高维的空间中去,利用函数f将n维空间数据映射至m维空间,而kernel method可以帮我们省去高维空间中数据难以计算的麻烦,甚至解决无限维空间无法计算的问题
更多链接:https://en.wikipedia.org/wiki/Kernel_method

Z = gen_z()
Z = np.array(list(map(lambda x : [x[0],x[1],10*math.sqrt(x[0]**2+x[1]**2)],Z)))
#YOUR CODE HERE.
C,L=kmeans(Z,2,10)

cluster0 = allocator(Z, L, C[0, :])
cluster1 = allocator(Z, L, C[1, :])
if cluster0.shape[0]>cluster1.shape[0]:
    clump=cluster0
    ring=cluster1
else:
    ring=cluster0
    clump=cluster1
    
colours = ['r', 'b']
if Z.shape[1] == 2:
    Z = np.hstack((Z, np.zeros((800, 1))))
cluster = Z
fig = plt.figure(figsize=(16, 10))
ax = fig.add_subplot(111, projection='3d')

ax.scatter(ring[:, 0], ring[:, 1], ring[:, 2], alpha=0.45)
ax.scatter(clump[:, 0], clump[:, 1], clump[:, 2], alpha=0.45)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()

fig = plt.figure(figsize=(16, 10))
plt.scatter(ring[:,0], ring[:,1])
plt.scatter(clump[:,0], clump[:,1])
plt.show()

对于新增广三维数据集,我们再使用K-means划分,得到了很好的解决
在这里插入图片描述
再将聚类转化至原二维空间中,结果如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值