目录

系列文章目录

一、问题

二、实验思路综述

(1)实验工具

(2)实验数据

(3)实验目标

(4)实验步骤

三、K-means聚类算法的原理与算法过程

(1)K-means算法原理

(2)K-means算法流程

(3)K-means算法分析

四、手写数字图像聚类

(1)导入并处理MNIST数据集

(2)K-means聚类

(3)结果可视化

3.1 PCA

3.2 LDA

五、存在问题与优化及创新聚类算法设计

六、结果与分析

(1)复杂度分析

(2)样本数对聚类效果的影响

(3)可视化结果展示

(4)编程语言对于聚类效果的影响

(5)算法优化前后聚类效果对比

(6)实验小结

七、其他 

1. 数据集及资源

2. 参考资料


系列文章目录

本系列博客重点在最优化方法的概念原理与代码实践,不包含繁琐的数学推导(有问题欢迎在评论区讨论指出,或直接私信联系我)。

代码可以全抄    大家搞懂原理与流程去复现才是有意义的!!!

第一章 最优化方法——K-means实现手写数字图像聚类


梗概

本篇博客主要介绍K-means算法的原理与流程,降维算法的优化问题解决与使用,并分别使用Matlab、Pycharm分别实现了使用K-means算法在实际问题中(对MNIST手写数字数据集)的聚类并进行分析,并分别使用了PCA与LDA对其进行了降维可视化(内附数据集和python及matlab代码)。


一、问题

手写数字图像数据分类问题文件train_images.mat包含大小为28*28的手写数字图像60000文件train_labels.mat其对应的数字标签。文件数据具体读写数据格式,请参考附件DataRead.m文件实验要求对手写数字图像进行聚类,并讨论其性能

(MNIST DATABASE下载网址: http://yann.lecun.com/exdb/mnist/)

 (1) train_images.mat的前100张手写数字图像进行类,共10类

图1. 文件100张手写数字图像

 (2) train_images.mat的前1000张手写数字图像进行类,共10类

 (3) 根据实际情况,讨论k-Means对多少张手写图像进行类,性能如何?

二、实验思路综述

(1)实验工具

本次实验分别使用MatlabPycharm分别实现了使用K-means算法对MNIST数据集聚类并进行分析。

(2)实验数据

MNIST数据集是机器学习领域中一个经典数据集,由 60000 个训练样本和 10000 个测试样本组成,每个样本都是一张 28 * 28 像素的灰度手写数字图片。本次实验使用到MNIST的手写体的图像数据(train_images.mat)和图像数据对应的标签(train_labels.mat)。

(3)实验目标

本次实验要求使用K-means算法建立模型,对手写体进行聚类,再对聚类的结果进行性能对比和分析。

(4)实验步骤

本次实验大致步骤如表1所示:

表1 实验1大致步骤

1.读取并处理MNIST数据集

2. 使用K-means算法对手写体进行聚类

3. 使用降维算法对聚类结果进行可视化

4. 对聚类结果进行性能分析

三、K-means聚类算法的原理与算法过程

(1)K-means算法原理

K-means是一种无监督的学习,主要通过不断地取离种子点(质心)最近均值的数据,自动将相似的对象归到同一个簇中(共聚类k个簇),循环往复执行,直到满足聚类的收敛条件为止。常用于聚类分析。K-means中所用最重要方法即求点群中心的算法:即欧氏距离,公式(以n维数据为例)如下:

K-means算法的简单示例(K=2)如下图2:

图2 K-means算法的简单示例

(2)K-means算法流程

经典 K-means 算法的基本工作流程基本与(1)中原理描述相适应,具体的执行步骤如表2所示:

表2 K-means算法流程

3K-means算法分析

K-means优点如下:

① 算法简单,容易理解,聚类效果不错

② 处理大数据集的时候,该算法可以保证较好的伸缩性

③ 当簇近似高斯分布的时候,效果相对较好

K-means缺点如下:

① K值需要人为设定,不同 K 值对实验结果影响较大

② 对初始的簇中心敏感,不同选取方式对实验结果影响较大

③ 对异常值敏感

④ 每个样本只能归为一类,不适合多分类任务

⑤ 不适合太离散的分类、样本类别不平衡的分类、非凸形状的分类

四、手写数字图像聚类

(1)导入并处理MNIST数据集

①导入:实验所给的手写体文件数据集是以mat格式来存储的。如果使用matlab实验,使用拖拽文件导入或使用DataRead.m中的DataRead函数(代码如下)进行导入均可,若使用Python进行实验,需要使用相关的工具库进行导入,例如scipy.io中的函数loadmat。导入成功后,其中一张手写体图片如图3所示。

function DataRead()

load ./train_images.mat    % Read image data

load ./train_labels.mat       %Read lable of images

ImgNum = 1; 
GetOneImg = train_images(:,:,ImgNum);
figure(1);
imshow(GetOneImg,[ ]);   %Show the image


 disp(['The number of Image is :  ',num2str(train_labels(ImgNum))]);
end

图3 手写体图片样例展示

②处理为方便后续操作,应对相关参数(k、right_num、num_sample等)进行设置,并在实验前对数据进行处理。导入后的数据库为28 * 28 * 60000的三维矩阵,使用reshape函数将每张图片拉成一个列向量,前期处理的核心代码如下:

k = 5; % 设置k
right_num = 0; %正确个数
num_sample = 1000; %待聚类个数
train = reshape(train_images,784,60000);
train_data = train(:,1:num_sample); %取前n列做训练

(2)K-means聚类

根据表2中K-means的算法流程与上文提到的算法原理,对实验代码进行编写。

①初始化:处理完数据后,对K-means所需一些参数进行初始化,包括获取数据库的行列与相关信息,对迭代次数times的定义,并使用randperm函数对K个初始聚类中心进行随机初始化,相关代码如下:

data = train_data;
times = 0;
N = k;
%%  初始化工作
[n,m] = size(data); % m = 列,n = 行
center = zeros(n,N);% 初始化聚类中心,生成n行N列的零矩阵
pattern = data;     % 将整个数据拷贝到pattern矩阵中
%% 算法
for x = 1 : N
    % 第一次随机产生聚类中心 randperm随机取数
    center(:,x) = data(:,randperm(num_sample,1)); 
end
while true
distence = zeros(1,N);   % 产生1行N列的零矩阵
num = zeros(1,N);        % 产生1行N列的零矩阵
new_center = zeros(n,N); % 产生n行N列的零矩阵

②聚类与质心更新:对K-means对应参数与初始质心初始化完后,不断重复以下过程:计算出每一张手写体图片与聚类中心的距离,挑选出每张手写体图片距离最近的聚类中心,并把聚类中心的下标按顺序储存到数组C中;接着继续更新质心。重新计算质心后,若质心改变,则更新质心,这时候迭代次数times+1。直到所有质心均不发生移动或迭代次数大于阈值,代码如下:

%% 将所有的点打上标签1 2 3...N
for x = 1 : m
    for y = 1 : N
        distence(y) = norm(data(:,x) - center(:,y)); % norm函数计算到每个类的距离
    end
    [~,temp] = min(distence); %求最小的距离 ~是距离值,temp是第几个
    pattern(n + 1,x) = temp;         
end
times = times+1;
tag = 0;
%% 将所有在同一类里的点坐标全部相加,计算新的中心坐标
for y = 1 : N
    for x = 1 : m
        if pattern(n + 1,x) == y
           new_center(:,y) = new_center(:,y) + pattern(1:n,x);
           num(y) = num(y) + 1;
        end
    end
    new_center(:,y) = new_center(:,y) / num(y);
    if norm(new_center(:,y) - center(:,y)) > 0.0001 %设定最小误差变化(阈值)
        tag = 1;
    end
end
if tag == 0 || times > 10000 % 设置终止条件(加入最大迭代次数限制)
     break;
else
     center = new_center;
end
end

拓展:K-means聚类算法在matlab、Python中均有集成,可直接调用,如在matlab中可使用[idx,C] = kmeans(___)命令实现聚类,idx即聚类后各数据的标签,C为聚类后的质心集合,详细用法见:k 均值聚类 - MATLAB kmeans - MathWorks 中国 

③正确率计算:手写数字数据集MNIST为带标签数据集,本实验标签在train_labels中。但由于K-means为无监督算法,并不能匹配相应的标签,所以需要人为计算聚类正确率。计算方法核心为:利用mode函数统计聚类中每类出现的最多的标签数作为该类预测标签,并根据如下公式计算正确率。

代码如下:

% 聚类正确率
 idx = pattern(785:785,:);
%idx = idx_pc'; % 调库正确率分析
for i = 1:k
    num_i = 0;
    index = zeros(1,num_sample);
    index_label = zeros(1,num_sample);
    for j = 1:num_sample
      if idx(1,j) == i
         num_i = num_i + 1;
         index(num_i) = j;
         index_label(num_i) = train_labels(:,j) + 1;
      end    
    end
    %找出第二多出现的数
    test = mode(index_label);
      index_label(index_label==test) = [];
      [test2,n] = mode(index_label);
        right_num = right_num + n;
end

(3)结果可视化

为使聚类结果更加清晰具体,可以使用降维算法将结果可视化,本实验以PCA与LDA为例。

3.1 PCA

PCA(主成分分析)为主流的一种线性降维算法。以“最小重构误差”为目标导向,通过降维(投影),用数据中相对重要(最主要)的信息表达(代替)原数据,从而达到降维的目的。原理就是其协方差矩阵对应的特征向量,按照对应的特征值大小进行排序,最大的特征值就是第一主成分,其次是第二主成分,依次类推。PCA优化问题如公式1所示,算法流程如表3所示:

3.2 LDA

LDA(线性判别分析)为主流的一种线性降维算法。以“最小化类内方差,最大化类间方差”为目标导向,通过投影,达到降维的目的更好地将样本分类,原理就是通过对目标函数的特征分解达到原理的目的。LDA优化问题如公式2所示,算法流程如表4所示:

Matlab样例使用PCA代码如下:

% %%PCA降维可视化
% % 2.图像求均值,中心化
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% mean_data = mean(data,2);
% centered_data = (data - mean_data);
% % 3.求协方差矩阵、特征值与特征向量并排序
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 
% cov_matrix = centered_data * centered_data';
% [eigen_vectors, dianogol_matrix] = eig(cov_matrix);
% 
% % 从对角矩阵获取特征值
% eigen_values = diag(dianogol_matrix);
% 
% % 对特征值按索引进行从大到小排序
% [sorted_eigen_values, index] = sort(eigen_values, 'descend'); 
% 
% % 获取排序后的征值对应的特征向量
% sorted_eigen_vectors = eigen_vectors(:, index);
% 
% all_eigen_data = sorted_eigen_vectors;
% 
% %可视化
% 
% eigen_data = all_eigen_data(:,1:i);
% i = 3; %降维数
%     % 投影
%     projected_test_data = eigen_data' * (data - mean_data);
% 
%     color = [];
%     for j=1:num_sample
%         color = [color floor((j-1)/4)*5];
%     end
% 
%     if (i == 2)
%         waitfor(scatter(projected_test_data(1, :), projected_test_data(2, :), [], color));
%     else
%         waitfor(scatter3(projected_test_data(1, :), projected_test_data(2, :), projected_test_data(3, :), [], color));
%     end

详细PCA教程与使用(Matlab)可见:机器学习——PCA(主成分分析)与人脸识别_@李忆如的博客-CSDN博客_pca人脸识别

详细LDA教程与使用(Matlab)可见: 

机器学习——LDA(线性判别分析)与人脸识别_@李忆如的博客-CSDN博客_lda

补充:PCA与LDA均可调库与手动实现,本实验中笔者在matlab中手动实现PCA与LDA,在Python中使用对应库函数(参考使用他人代码),例如PCA使用的是Python的sklearn库,调用decomposition.PAC加载PCA进行降维,使用参数n_components指定主成分的个数(降维个数)。 

五、存在问题与优化及创新聚类算法设计

详情见机器学习——LR(线性回归)、LRC(线性回归分类)与人脸识别_@李忆如的博客-CSDN博客_lr线性回归

六、结果与分析

(1)复杂度分析

K-means优化问题如图4所示:

图4 K-means算法的优化问题

(2)样本数对聚类效果的影响

为探究样本数对聚类效果的影响,需要确定k = 10,改变num_sample,探究过程与结果分析如下(以matlab为例):

①num_sample = 100

首先设置参数,num_sample=100(即取data的前100列做训练),k=10,time_while=100,将前100张手写数字图像聚成10类,并且重复运行100次做统计分析,得到如图5所示的可视化图表。可以看到,100次聚类测试中,聚类的平均正确率为58.14%,最高达到68%,最低为45%,方差为0.003,每次聚类的平均时间为0.049s

图5 num_sample=100,k=10,time_while=100时的正确率

补充:为探究聚类后具体参数(聚类中心、迭代次数、时间、正确率等)并分析,笔者在实验时选取5次实验结果进行详细展示,如表6所示:

聚类次数

聚类中心

迭代次数

聚类时间(s)

正确率

1

[3,9,3,0,1,2,8,7,4,6]

6

0.052

67.00%

2

[6,4,3,0,3,8,9,0,1,1]

4

0.037

66.00%

3

[1,4,7,7,7,0,6,8,3,0]

5

0.039

65.00%

4

[7,1,3,3,0,0,8,6,9,4]

5

0.065

69.00%

5

[8,4,0,7,1,0,6,9,3,1]

7

0.066

66.00%

平均

-

5.4

0.052

66.33%

②num_sample = 1000

类似①,首先设置参数,num_sample=1000,k=10,time_while=100,将前1000张手写数字图像聚成10类,并且重复运行100次做统计分析,得到如图6所示的可视化图表。可以看到,100次聚类测试中,聚类的平均正确率为54.42%,最高达到62.81%,最低为46.11%,方差为0.002,每次聚类的平均时间为1.269s

图6 num_sample=1000,k=10,time_while=100时的正确率 

分析:通过对①与②的比较,可以看出随着样本数增大,聚类效果有降低或波动趋势,且时长增加,接下来将会对这一结论进行验证。

③对不同num_sample的情况进行对比分析

为验证上述结论,需要对更多不同num_sample的情况进行对比分析。所以在这里对num_sample=100,1000,3000,5000,7000,9000,11000的情况做了统计分析,结果如表7所示,正确率变化趋势如图7所示,运行时间变化趋势如图8所示。

表7 k=10的前提下,样本数不断增加的对比测试结果

聚类

样本

聚类

中心

平均

正确率

最高

正确率

最低

正确率

方差

平均

时间(s)

100

10

58.14%

68.00%

45.00%

0.0030

0.049

1000

10

54.42%

62.81%

46.11%

0.0020

1.269

3000

10

57.35%

62.57%

50.13%

0.0008

4.783

5000

10

56.72%

62.90%

50.38%

0.0009

8.316

7000

10

56.17%

62.57%

52.60%

0.0008

12.13

9000

10

56.55%

59.04%

54.77%

0.0007

15.981

11000

10

55.88%

58.69%

52.30%

0.0005

20.259

图7 K-means正确率随样本数变化的变化趋势

图8 K-means运行时间随样本数变化的变化趋势

分析:由表7、图7与图8统计分析,可清晰看出,在10个聚类中心的前提下,从正确率的角度来说,随着聚类的样本数的增加,K-means算法的平均正确率都集中在55%-60%,且有波动或下降的趋势。从运行时间的角度来说,随着样本数的增加,平均运行时间也不断增加。 

④K-Means聚类数量上限及性能

为了探究本次设计的K-Means能对多少张手写图像进行聚类及其性能,在k=10的情况,我选取数据集中全部60000张图片进行聚类,聚类的正确率为58.47%,仍处于55%-60%的区间,运行时间为166.92s所以本K-Means算法能完成对全部60000张手写图像进行类,性能正确率可以达到58.47%

3)可视化结果展示

当num_sample=100,k=10时,运行程序,正确率为58.14%,Python使用PCA降维得到如图9的聚类可视化结果,Matlab使用LDA降维得到的可视化如图10所示:

图9 num_sample=100,k=10的聚类前(左边)和聚类后(右边)的PCA可视化结果 

 图10 num_sample=100,k=10的聚类后的LDA可视化结果

当num_sample=1000,k=10时,运行程序,正确率为54.42%%,Python使用PCA降维得到如图11的聚类可视化结果: 

图11 num_sample=1000,k=10的聚类前(左边)和聚类后(右边)的PCA可视化结果 

分析:在经过降维算法的可视化后,可以轻易看出数据集使用K-Means进行聚类并不能有很好的效果。有一部分原因是K-means 是根据28*28来度量距离的,取前两维之后可视化的效果不会特别好。但本质上K-means作为一种无监督的基于距离的聚类算法,仍存在时间复杂度较高并且在实际问题中聚类中心的数量和选取在一开始难以精准确认等问题,这都使得K-Means算法的聚类结果并没有表现的很好。 

(4)编程语言对于聚类效果的影响

为探究编程语言对聚类效果的影响,确定k=10,分别使用Matlab与Python实现手写数字聚类,探究过程与结果分析如下:

①编程语言对正确率的影响

首先设置参数,确定k = 10,令num_sample=100、1000、3000、5000、9000,time_while=100,分别使用Matlab与Python将前num_sample张手写数字图像聚成10类,并且重复运行100次做统计分析,结果如表8与图12所示:

表8 不同编程语言对聚类正确率的影响

图12 不同编程语言对聚类正确率的影响 

分析:由表8与图12分析可知,在不同样本数下Python聚类正确率略高于Matlab但基本一致,且均有波动趋势,平均正确率仍处于5560%区间。

②编程语言对聚类时间的影响

类似①,首先设置参数,确定k = 10,令num_sample=100、1000、3000、5000、9000,60000,time_while=100,分别使用Matlab与Python将前num_sample张手写数字图像聚成10类,并且重复运行100次做统计分析,结果如表9与图13所示:

表9 不同编程语言对聚类时间的影响

图13 不同编程语言对聚类时间的影响(除num_sample = 600000) 

分析:由表9与图13分析可知,在不同样本数下Python聚类时间均高于Matlab,且随着样本数增加,运行效率差距变大。

(5)算法优化前后聚类效果对比

为验证创新聚类算法的优化效果,同时使用优化算法与经典K-means算法完成手写数字聚类任务,探究过程与结果分析如下:

首先设置参数,确定k = 10,令num_sample=100、1000、3000、5000、9000,time_while=100,分别使用K-means与优化算法将前num_sample张手写数字图像聚成10类,并且重复运行100次做统计分析,结果如表10与图14所示:

10 算法优化前后聚类效果对比 

图14 算法优化前后聚类效果对比

分析:由表9与图14分析可得,在不同样本数下,优化算法聚类效果均优于经典K-means算法,以此验证了优化的正确性。

(6)实验小结

(1)本次实验分为实验思路综述、K-means聚类算法的原理与算法过程、手写数字图像聚类、存在问题与优化及创新聚类算法设计、结果与分析五大部分,探究了K-means的原理与流程,并在实际问题中实践并对比分析

(2)随着样本数量(num_sample)的增加,K-means对MNIST的聚类正确率有一定波动,但基本维持在55~60%,运行时间不断增加,在本次实验中聚类样本数对实验没有限制,K-means可以完成MNIST完整数据集(60000张图片)的聚类

(3)经过PCA、LDA等降维算法的可视化后,可以较直观地看出聚类前后效果,并由此分析出数据集使用K-Means进行聚类并不能有很好的效果。

(4)不同编程语言对聚类效果有一定影响,在本次实验中,在不同样本数的情况下,Python的聚类正确率基本均略高于Matlab,但随着样本数增大,Python相对Matlab的聚类时间会大大增加

(5)针对经典K-means的相关问题实验中做了分析与优化,并提出了创新聚类算法,并在不同样本数下与传统K-means进行了对比分析,优化算法的聚类正确率均高于经典算法,验证了优化算法的优越性。

七、其他 

1. 数据集及资源

本实验所用数据集:MNIST

Python代码:深大计软_最优化方法_实验1:K-Means聚类之Python实现手写数字图像MNIST分类_聚类mnist-机器学习文档类资源-CSDN文库

2. 参考资料

1.【最优化方法】K-Means聚类实验:Python实现手写数字图像MNIST分类_Ferry_xie的博客

2.机器学习——K-means(聚类)与人脸识别_@李忆如的博客-CSDN博客_聚类识别

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐