Learning Vector Quantization详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_40793975/article/details/82177189

Learning Vector Quantization详解

第十八次写博客,本人数学基础不是太好,如果有幸能得到读者指正,感激不尽,希望能借此机会向大家学习。这一篇文章是原型聚类中介绍的第二个算法,主要是谈一谈“学习向量量化”(Learning   Vector   Quantization),以及我自己对于这种原型聚类算法的进一步构思。其他有关于原型聚类算法的讨论可以移步到该类算法的导航页《原型聚类算法综述(原型聚类算法开篇)》

  与K-Means算法类似,学习向量量化(LVQ)的目的也是要习得可以表示数据集的一组“原型”,但是,LVQ中用到了数据集的样本标记作为监督信息,并依赖于这些信息来得到原型向量,其中每个向量代表了一个簇。使用通过LVQ得到的原型向量来代表整个簇的过程,称为“向量量化”(Vector   Quantization),这种数据压缩方法属于“有损压缩”(Lossy   Compression)。
  具体的LVQ算法描述如下所示:

算法第1行:根据样本集 D 的原始类别标记,分别从类别标记为 { t 1 , t 2 , . . . , t q } 的样本子集中随机选取一个样本点作为该类样本的原型向量,最终得到原型向量集合 { p 1 , p 2 , . . . , p q }
算法第2-5行:从样本集中随机选取一个样本 x j ,计算该样本与每个原型向量 p i 之间的欧式距离,并找到与该样本距离最近的原型向量 p i 的类别标记 t i
算法第6-11行:根据 t i 与样本 x j 类别标记 y j 的异同,对 p i 进行更新,直到满足停止条件为止。
  关于算法第7、9行的更新公式,作如下解释:
  (1)  p = p i + η ( x j p i )
     p 与样本 x j 之间的距离为

  令学习率 η ( 0 , 1 ) ,则原型向量 p i 在更新为 p 之后将更接近 x j
  (2)  p = p i η ( x j p i )
     p 与样本 x j 之间的距离为

  令学习率 η ( 0 , 1 ) ,则原型向量 p i 在更新为 p 之后将更远离 x j
  LVQ可以被认为是一种特殊的人工神经网络,他遵循了“胜者为王”的竞争策略,是非监督学习中“自组织映射”(SOM)的先驱算法,并且与KNN有关。由LVQ划分出来的聚类区域,也称为“Voronoi   剖分”(Voronoi   Tessellation)。
  下面是自己对该算法的后续构思,在数据集相对较大时,由于该算法采用的是随机初始化且算法第3行采用的是随机抽取(类似于boostrap每个样本有36%的概率不回被抽到),因此LVQ最终可能会区域局部最优。为了解决上述问题,可以采用集成学习的方法,先使用原始数据集 D 和LVQ算法训练多个模型,然后对原始数据集 D 进行随机采样得到测试集,最后根据测试集在每个模型上的表现作为该模型的权值系数,至此得到最终的原型向量。


代码实现

下面是我自己实现的代码,需要自己输入迭代次数和学习率,最后被我注释掉的那一段代码是用来可视化实现的,取消注释运行代码就可以显示出来。

代码细节

"""

@author: Ἥλιος
@CSDN:https://blog.csdn.net/qq_40793975/article/details/82177189

"""
print(__doc__)

import time
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from itertools import cycle, islice


# 加载数据集(从文件中)
def load_Data1(filename):
    data_set = []
    with open(filename) as fi:
        for line in fi.readlines():
            cur_line = line.strip().split('\t')
            flt_line = []
            for i in cur_line:
                flt_line.append(float(i))
            data_set.append(flt_line)
    data_mat = np.mat(data_set)  # 转化为矩阵形式
    return data_mat


# 加载数据集(自建数据集)
def load_Data2(n_samples=1500):
    # 带噪声的圆形数据
    noisy_circles = datasets.make_circles(n_samples=n_samples, factor=.5, noise=.05)

    # 带噪声的月牙形数据
    noisy_moons = datasets.make_moons(n_samples=n_samples, noise=.05)

    # 随机分布数据
    no_structure = np.random.rand(n_samples, 2), np.ones((1, n_samples), dtype=np.int32).tolist()[0]

    # 各向异性分布数据(Anisotropicly distributed data)
    random_state = 170
    X, y = datasets.make_blobs(n_samples=n_samples, random_state=random_state)
    transformation = [[0.6, -0.6], [-0.4, 0.8]]
    X_aniso = np.dot(X, transformation)
    aniso = (X_aniso, y)

    # 不同方差的气泡形数据(blobs with varied variances)
    varied = datasets.make_blobs(n_samples=n_samples, cluster_std=[1.0, 2.5, 0.5], random_state=random_state)

    # 相同方差的气泡形数据
    blobs = datasets.make_blobs(n_samples=n_samples, random_state=8)

    # 合并数据集
    data_sets = [noisy_circles, noisy_moons, no_structure, aniso, varied, blobs]
    cluster_nums = [2, 2, 3, 3, 3, 3]
    data_mats = []
    for i in range(data_sets.__len__()):
        X, y = data_sets[i]
        X = StandardScaler().fit_transform(X)  # 对数据集进行标准化处理
        X_mat = np.mat(X)
        y_mat = np.mat(y)
        data_mats.append((X_mat, y_mat))

    # 展示数据集
    plt.figure(figsize=(2.5, 14))
    plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05, hspace=.01)
    for i in range(data_sets.__len__()):
        X, y = data_sets[i]
        X = StandardScaler().fit_transform(X)  # 对数据集进行标准化处理
        colors = np.array(list(islice(cycle(['#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', '#984ea3', '#999999',
                                             '#e41a1c', '#dede00']), int(max(y) + 1))))
        plt.subplot(len(data_sets), 1, i+1)
        if i == 0:
            plt.title("Self-built Data Set", size=18)
        plt.scatter(X[:, 0], X[:, 1], c=colors[y], s=10)
        plt.xlim(-2.5, 2.5)
        plt.ylim(-2.5, 2.5)

    plt.show()

    return data_mats, cluster_nums


# 计算样本点A与B间距离(欧氏距离)
def dist_Euclid(VecA, VecB):
    return np.sqrt(np.sum(np.power(VecA-VecB, 2)))


# 对k个聚类中心随机初始化(随机选取数据集中的k个样本点)
def rand_initial_center1(data_mat, k):
    (m, n) = np.shape(data_mat)
    centroids = np.mat(np.zeros((k, n), dtype=np.float32))
    for i in range(k):
        index = int(np.random.rand()*m)
        centroids[i, :] = data_mat[index, :]
    return centroids


# 对k个聚类中心随机初始化(在样本空间范围内随机选取)
def rand_initial_center2(data_mat, k):
    n = np.shape(data_mat)[1]
    centroids = np.mat(np.zeros((k, n), dtype=np.float32))
    for i in range(n):
        minJ = np.min(data_mat[:, i])
        maxJ = np.max(data_mat[:, i])
        centroids[:, i] = np.mat(np.random.rand(k, 1)*(maxJ - minJ)) + minJ
    return centroids


# 学习向量量化
def LVQ(data_mat, label_mat, k, eta=0.05, dist_measure=dist_Euclid, t=15000):  # t-迭代次数
    m, n = np.shape(data_mat)
    centroids = rand_initial_center2(data_mat, k)
    cluster_assment = np.mat(np.zeros((m, 2)), dtype=np.float32)
    n = 0
    while t:
        t = t-1
        index = int(np.random.rand()*m)
        x = data_mat[index, :]
        y = label_mat[index]

        min_distji = np.inf
        min_distji_index = -1
        for i in range(k):  # 找出与样本点x距离最近的质心
            distji = dist_measure(x, centroids[i, :])
            if min_distji > distji:
                min_distji = distji
                min_distji_index = i

        if min_distji_index != y:  # 如果该质心的簇标记与样本点的标记不同
            centroid = centroids[min_distji_index, :]
            # print(centroid)
            centroids[min_distji_index, :] = centroid - eta*(x - centroid)
            # print(centroids[min_distji_index, :])
            n = n + 1
        else:
            centroid = centroids[min_distji_index, :]
            centroids[min_distji_index, :] = centroid + eta*(x - centroid)

    for i in range(m):
        min_distji = np.inf
        min_distji_index = -1

        for j in range(k):
            distji = dist_measure(data_mat[i, :], centroids[j, :])
            # print(distji)
            if min_distji > distji:
                min_distji = distji
                min_distji_index = j
        cluster_assment[i, 0] = min_distji_index
        cluster_assment[i, 1] = min_distji

    # # 静态显示
    # plt.scatter(data_mat[:, 0].T.A[0], data_mat[:, 1].T.A[0], c=cluster_assment[:, 0].T.A[0])
    # plt.scatter(centroids[:, 0].T.tolist()[0], centroids[:, 1].T.tolist()[0], s=100, c='red', marker='x')
    # plt.show()

    return centroids, cluster_assment

# 
# # Learning Vector Quantization
# data_mats, cluster_nums = load_Data2()
# plt.figure(figsize=(2.5, 14))
# plt.subplots_adjust(left=.02, right=.98, bottom=.001, top=.96, wspace=.05, hspace=.01)
# for i in range(len(data_mats)):
#     data_mat = data_mats[i][0]  # 获取自建数据集
#     label_mat = data_mats[i][1].A.tolist()[0]  # 获取自建数据集的标记
#     k = cluster_nums[i]  # 获取自建数据集的簇标记
#     t0 = time.time()  # 计算运行时间
#     centroids, cluster_assment = LVQ(data_mat, label_mat, k)
#     t1 = time.time()
# 
#     y_pred = np.array(cluster_assment[:, 0].T, dtype=np.int32)[0]  # 预测的簇标记,用于画图(使用sklearn的K_Means时可以注释掉)
#     colors = np.array(list(islice(cycle(['#377eb8', '#ff7f00', '#4daf4a', '#f781bf', '#a65628', '#984ea3', '#999999',
#                                          '#e41a1c', '#dede00']), int(max(y_pred) + 1))))
#     plt.subplot(len(data_mats), 1, i + 1)
#     if i == 0:
#         plt.title("LVQ(Self-programming Implementation)", size=10)
#     plt.scatter(data_mat[:, 0].T.A[0], data_mat[:, 1].T.A[0], c=colors[y_pred], s=10)
#     plt.scatter(centroids[:, 0].T.tolist()[0], centroids[:, 1].T.tolist()[0], s=100, c='red', marker='x')
#     plt.xlim(-2.5, 2.5)
#     plt.ylim(-2.5, 2.5)
#     plt.text(.99, .01, ('%.2fs' % (t1 - t0)).lstrip('0'), transform=plt.gca().transAxes, size=15,
#              horizontalalignment='right')
# plt.show()

算法效果

  如上图所示,从左至右依次是:原始数据集、自己实现的LVQ,LVQ作为一种监督式聚类算法,其目的是实现数据的有损压缩,上图中自己实现的LVQ中的红色X可以作为不同簇的代表点,进行数据压缩时可以多选择几个代表点,来降低压缩率,在现实任务中,样本标记一般分布是连续值,因此可以将标记离散化后再执行LVQ。另外,这种算法为了得到更精确的代表点需要调整迭代次数和学习率,时间复杂度比较高,而且聚类效果不是很好,现实应用中多用作神经网络算法。

  

参考资料

【1】《机器学习》周志华
【2】《数据挖掘》

猜你喜欢

转载自blog.csdn.net/qq_40793975/article/details/82177189