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'])