Python - 聚类(KMeans 和 LVQ)

K-means:无监督算法,具有不确定性,因为刚开始输入的聚类点不同,可能会导致最终聚类的结果不同,因此建议多做几次聚类,看看那种分类靠谱点。

  • 簇的位置:簇中心的坐标。K-means初始化的时候随机选择一个点作为中心点,然后每个步骤迭代找到一个新的中心,在这个新的中心附近的点都相似,并被划分到同一个组;
  • 簇的半径:簇内每个点到簇中心的距离的平方差;
  • 簇的规模:簇内点的总数:
  • 簇的密度:簇的规模和簇的半径的比值:
  • 轮廓系数:用以评估聚类结果的好坏,它的值介于-1到1,负值说明簇的半径大于簇之间的距离,也就是说簇之间有重叠,说明聚类结果很差,1最好。
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 31 21:16:20 2018

@author: Alvin AI
"""

import numpy as np
import matplotlib.pyplot as plt

def get_random_data():
    x_1 = np.random.normal(loc=0.2,scale=0.2,size=(100,100))
    x_2 = np.random.normal(loc=0.9,scale=0.1,size=(100,100))
    x = np.r_[x_1,x_2]
    return x

x = get_random_data()

plt.cla()
plt.figure(1)
plt.title('generated data')
plt.scatter(x[:,0],x[:,1])
plt.show()

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score#求轮廓系数

def form_clusters(x,k):
    '''
    Built Cluster
    '''
    no_clusters = k
    model = KMeans(n_clusters=no_clusters,init='random')
    model.fit(x)
    labels = model.labels_#得到数据点的聚类标签
    print labels
    sh_score = silhouette_score(x,labels)
    return sh_score

#k=2,3,4,5都挨个试下,然后得到其轮廓系数,用以评分
#轮廓系数的值介于-1到1之间,越接近1表明聚类效果好
sh_scores= []
for i in range(1,5):
    sh_score = form_clusters(x,i+1)
    sh_scores.append(sh_score)
    
no_clusters = [i+1 for i in range(1,5)]

plt.figure(2)
plt.plot(no_clusters,sh_scores)
plt.title('cluster quality')
plt.xlabel('no of clusters k')
plt.ylabel('sh_scores')
plt.show()
2. LVQ:Learning Vector Quantization, 学习向量量化。是黑箱方法,对于生成的原型变量很难分辨好坏,没有任何优化条件。
  • 为数据集里的每个类别选择K个初始的原始向量,如果是个两分类问题,并且每个分类中有两个原型变量,那我们就需要设置4个初始的原型变量,他们是从输入的数据集中随机选取的。
  • 接着进行循环,知道epsilon值变为0或者预先设定的阈值。我们得确定一个epsilon值并在每次循环中都使之减小。
  • 每次循环中,我们都要采样一个输入点(带替换),采用欧式距离找出离它最近的原型向量,然后按下面的操作更新最近邻点的原型向量。
  • 如果它的原型向量的类别标签和输入数据点相同,则在原型向量上增加原型向量和数据点的差异。

    # -*- coding: utf-8 -*-
    """
    Created on Sun Apr 01 11:02:16 2018
    
    @author: Alvin AI
    """
    
    from sklearn.datasets import load_iris
    import numpy as np
    from sklearn.metrics import euclidean_distances
    
    data = load_iris()
    x = data['data']
    y = data['target']
    
    #最大最小值缩放到[0,1],因为要进行欧式距离的计算
    from sklearn.preprocessing import MinMaxScaler
    minmax = MinMaxScaler()
    x= minmax.fit_transform(x)
    
    #LVQ参数声明
    R = 2 #每个类别标签有两种原型向量y
    n_classes = 3 #iris是有3个类别
    epsilon = 0.9 #初始epsilon --> 0
    epsilon_dec_factor = 0.001 #缩放因子
    
    class prototype(object):
    
        #保存原型向量的各个细节
        def __init__(self,class_id,p_vector,eplsilon):
            self.class_id = class_id
            self.p_vector = p_vector
            self.eplsilon = eplsilon
            
        def update(self,u_vector,increment=True):
            if increment:
                #将原型向量向输入向量靠近
                self.p_vector = self.p_vector + self.eplsilon(u_vector - self.p_vector)
            else:
                #将原型向量原远离输入向量
                self.p_vector = self.p_vector - self.eplsilon(u_vector - self.p_vector)
    
    #此函数用于找出离给定向量最近的原型向量
    #这块代码不知道为什么有问题,好像是欧式距离那块运用的不恰当,导致distance那边逻辑判断模糊
    #如果有朋友解决了这个问题请告知我一声,谢谢哈
    def find_closest(in_vector,proto_vectors):
        closest = None
        closest_distance = 9999
        for p_v in proto_vectors:
            distance = euclidean_distances(in_vector.reshape(-1,1),p_v.p_vector.reshape(-1,1))
            #distance =np.linalg.norm(in_vector - p_v.p_vector) 
            if distance < closest_distance:
                closest_distance = distance
                closest = p_v
        return closest
    
    #此函数用于找到最近原型向量的类别ID
    def find_class_id(test_vector,p_vectors):
        return find_closest(test_vector,p_vectors).class_id
    
    #选择初始化的k*原型向量类别数
    #为每个类选择R个原型
    p_vectors = []
    for i in range(n_classes):
        y_subset = np.where(y==i)#选择一个类
        x_subset= x[y_subset]
        samples = np.random.randint(0,len(x_subset),R)#介于0—50中间随机选择R个下标
        for sample in samples:
            s = x_subset[sample]
            p = prototype(i,s,epsilon)
            p_vectors.append(p)
            
    while epsilon >= 0.01:
        #随机采样一个训练实例
        rnd_i = np.random.randint(0,149)
        rnd_s = x[rnd_i]
        target_y = y[rnd_i]
        
        #为下一次循环减少epsilon
        epsilon = epsilon - epsilon_dec_factor
        #查找与给定点最相近的原型向量
        closest_pvector = find_closest(rnd_s,p_vectors)
        
        #更新最相近的原型向量
        if target_y == closest_pvector.class_id:
            closest_pvector.update(rnd_s)#靠近
        else:
            closest_pvector.update(rnd_s,False)#远离
        closest_pvector.epsilon = epsilon
            
    print 'class id \t Initial prototype vector\n'
    for p_v in p_vectors:
        print p_v.class_id,'\t',p_v.p_vector
      
    #下面为测试代码来检查方法是否正确
    predicted_y = [find_class_id(instance,p_vectors) for instance in x]
    
    from sklearn.metrics import classification_report
    
    print classification_report(y,predicted_y,terget_names=['Iris-Setosa','Iris-Versicolour','Iris-Virginica'])
    
    



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值