吴恩达|机器学习作业7.0.k-means聚类

7.0.k-means聚类以及图像压缩(颜色压缩)

1)题目:

在本练习中,您将使用k-means聚类算法并且将其应用于压缩图像。在第二部分中你将使用主成分分析来找到脸部图像的一个低维表示。
第一部分为k-means聚类,你首先从一个2维的样本集开始,他可以帮助你对k-means算法有一个直观的感受。然后你将使用k-means算法对图像进行压缩,通过减少颜色数量,直到只出现在该图像中最常见的那些颜色。
数据集链接: https://pan.baidu.com/s/1cEgQIvehUcLxZ0WVhxcPuQ 提取码: xejn

2)知识点概括:

  • 无监督学习的数据是不带标签的

  • k-means算法:
    原理
    1、随机初始化生成k个点,即聚类中心(cluster centroids)
    2、重复迭代进行簇分配(cluster assignment)和移动聚类中心(move centroid),直到聚类中心不再改变

  • 输入:k和不带标签的数据集
    在这里插入图片描述
    其中
    c ( i ) c^{(i)} 表示当前样本 x ( i ) x^{(i)} 所属的那个簇的索引号或者序号
    μ k \mu_k 表示第k个聚类中心的位置
    μ c ( i ) \mu_{c^{(i)}} 表示 x ( i ) x^{(i)} 所属簇的聚类中心

  • 代价函数(优化目标)为 J ( c ( 1 ) ,   , c ( m ) , μ 1 ,   , μ K ) = 1 m i = 1 m x ( i ) μ c ( i ) 2 J(c^{(1)},\cdots,c^{(m)},\mu_1,\cdots,\mu_K)={1\over m}\sum_{i=1}^m\|x^{(i)}-\mu_{c^{(i)}}\|^2
    有时也称这个代价函数为失真代价函数(distortion cost function)

  • 随机初始化
    随机挑选k个样本作为初始的聚类中心

  • 防止陷入局部最优解的方法:尝试多次(这里举100次迭代)初始化k-means算法并实现,然后选择代价函数最小的那个作为最优解。这个方法一般对于聚类中心在10个以下比较有效。

  • 关于选择聚类的数量
    1、肘部法则(Elbow method)画出代价函数随聚类数量变化的曲线,选择肘部点对应的聚类数量即为较好的(不常用,效果不是很好)
    2、看哪个聚类数量能更好的应用于后续目的(downstream purpose)

3)大致步骤:

  • 簇分配,寻找最近的聚类中心。历遍所有数据,返回一个取值为1到K的索引向量。用初始化的聚类中心[3, 3], [6, 2], [8, 5]测试下,应该得到x的前三个数据分别归属第1 3 2个聚类中心。
  • 计算聚类中心。
  • 运行k-means,设置一个布尔值,若为真则画出每次迭代的图像,以及最后的聚类结果和聚类中心移动的路径;若为假则只输出聚类中心和样本索引向量。
  • 随机初始化聚类中心。尝试以上步骤观察图像。
  • 图像压缩。原图像存储在一个三维数组(height,width,RGB)中,以0~255的整数表示红/绿/蓝的强度,这种编码通常被称为RGB编码。这里只需要把颜色的数量减少到16种颜色,即设置初始聚类数量为16,把原始图片的每个像素看作一个数据样本,然后利用k-means算法去找分组最好的16种颜色。这里需要把像素数据先展开成一个三列的矩阵,然后每行相当于一个数据样本,对这个样本进行k-means聚类,最后得到一个16个的聚类中心,然后每个数据用它的索引来得到距离最近的聚类中心的RGB编码,重组成之前shape的一个矩阵,即可得到压缩后的图像。

4)关于Python:

  • np.argmin函数返回最小值的索引。
  • plt.plot(mu[:, 4], mu[:, 5], ‘kx–’, markersize=8)中kx—可以直接规定连线的颜色,标记点形状和连线形状等。
  • matplotlib.image程序包中mpimg.imread函数可以用于读取图片,imsave函数用来保存图片。
  • .reshape(-1, 3)表示行数由Numpy自动计算,3列。
  • .set_title可以给子图加标题。
  • 用sklearn中的MiniBatchKMeans可以直接进行k-means回归,Mini Batch KMeans算法是一种能尽量保持聚类准确性下但能大幅度降低计算时间的聚类模型,采用小批量的数据子集减少计算时间,同时仍试图优化目标函数,这里所谓的Mini Batch是指每次训练算法时随机抽取的数据子集,采用这些随机选取的数据进行训练,大大的减少了计算的时间,减少的KMeans算法的收敛时间,但要比标准算法略差一点,建议当样本量大于一万做聚类时,就需要考虑选用Mini Batch KMeans算法。

5)代码与结果:


import numpy as np
import matplotlib.pyplot as plt
import scipy.io as scio
import matplotlib.image as mpimg #用于读取、保存图片
from sklearn.cluster import MiniBatchKMeans #Mini Batch K-Means聚类

data = scio.loadmat('ex7data2.mat')
x = data['X']

'''============================part1 簇分配========================='''
'''可视化数据集'''
plt.scatter(x[:,0], x[:,1], marker='o', c='w', edgecolors='k')


'''初始化'''
K = 3 #设置聚类数量
initial_centroids = np.array([[3, 3], [6, 2], [8, 5]]) #初始聚类中心


'''簇分配函数,找出距离最近的那个聚类中心'''
def findClosestCentroids(x, centroids):
    idx = np.zeros(len(x))
    for i in range(len(x)):
        c = np.sqrt(np.sum(np.square((x[i,:]-centroids)), axis=1)) #行求和
        idx[i] = np.argmin(c)+1
    return idx

idx = findClosestCentroids(x, initial_centroids) #前三个的索引为[1 3 2]


'''============================part2 计算聚类中心========================='''

def computeCentroids(x, idx, K):
    mu = np.zeros((K, x.shape[1]))
    for i in range(1, K+1):
        mu[i-1] = x[idx==i].mean(axis=0) #列求均值
    return mu

computeCentroids(x, idx, K)    #array([[2.42830111, 3.15792418],
                               #       [5.81350331, 2.63365645],
                               #       [7.11938687, 3.6166844 ]])

'''============================part3 k-means聚类========================='''
max_iters = 10 #设置迭代次数

#画出每次迭代的结果
def plot_process(x, mu, idx):
    color = ['r', 'g', 'b'] #颜色序列
    for i in range(1,len(mu)+1):
        plt.scatter(x[idx==i][:,0], x[idx==i][:,1], marker='o', c='w', edgecolors=color[i-1])
        plt.plot(mu[:,0], mu[:,1], 'kx', markersize=8)

#运行k-means
def runKmeans(x, centroids, max_iters, true):
    if true == True:
        mu = np.reshape(centroids, (1, centroids.size))
    for i in range(max_iters):
        idx = findClosestCentroids(x, centroids) #簇分配
        if true == True:
            plt.figure(i)
            plot_process(x, centroids, idx) #画图
            plt.title(i+1)
        centroids = computeCentroids(x, idx, len(centroids)) #移动聚类中心
        if true == True:
            mu = np.r_[mu, np.reshape(centroids, (1, centroids.size))] #用来储存聚类中心的移动坐标
    if true == True:
        plt.figure(max_iters)
        plot_process(x, centroids, idx)
        j = 0
        while j < centroids.size:
            plt.plot(mu[:, j], mu[:, j+1], 'kx--', markersize=8)
            j = j+2
        plt.title('Moving path')
    return centroids, idx

runKmeans(x, initial_centroids, max_iters, True)


'''============================part4 随机初始化聚类中心========================='''
def kMeansInitCentroids(x, K):
    randidx = np.random.permutation(x) #随机排列
    centroids = randidx[:K, :] #选前K个
    return centroids

centroids = kMeansInitCentroids(x, K)
runKmeans(x, centroids, 5, True)


'''============================part5 图像压缩========================='''
'''图像读入'''
A = mpimg.imread('bird_small.png') #读取图片
A.shape #(128, 128, 3)

plt.imshow(img) # 显示图片
plt.axis('off') # 不显示坐标轴
plt.show()

'''图像数据'''
img = scio.loadmat('bird_small.mat')['A']
img.shape #(128, 128, 3)
img = img/255.0 #使得像素数据为0-1之间

#这里随便用A还是用img,都一样的


'''k-means聚类'''
#数据重构
X = A.reshape(-1, 3) #(16384, 3)
K = 16
max_iters = 10
initial_centroids = kMeansInitCentroids(X, K) #随机初始化聚类中心
#聚类
centroids, idx = runKmeans(X, initial_centroids, max_iters, False)

'''还原压缩后的图像'''
X_recovered = np.zeros(X.shape)
for i in range(len(centroids)):
    X_recovered[idx==i] = centroids[i]
#重构
X_recovered = X_recovered.reshape(A.shape)
#画图对比
fig, axes = plt.subplots(1, 2, figsize=(14,6))
plt.title('Original')
axes[0].set_title('Original')
axes[0].imshow(A)
axes[1].set_title('Compressed, with %d colors'%K)
axes[1].imshow(X_recovered)


'''============================part6 sklearn========================='''
#picture = A #优化上面的图片
picture = mpimg.imread('IMG_4994.jpeg') #读取图片
picture #查看下是不是0-1之间
data = picture/255 #如果是0-255的话需要除以255,如果已经是0-1就不需要
data = data.reshape(data.shape[0]*data.shape[1],3) 

'''用sklearn中的MiniBatchKMeans'''
#MiniBatchKMeans聚类
kmeans = MiniBatchKMeans(K) 
kmeans.fit(data) #先fit
picture_recolored = kmeans.cluster_centers_[kmeans.predict(data)] #再predict,然后再分配标记的聚类中心的RGB编码
picture_recolored = picture_recolored.reshape(picture.shape)

#画图
fig, axes = plt.subplots(1, 2, figsize=(14,6))
axes[0].imshow(picture)
axes[0].set_title('Original')
axes[1].imshow(picture_recolored)
axes[1].set_title('Compressed, with %d colors'%K)

#保存,观察文件大小是否变化
mpimg.imsave('IMG_4994_recoverd.jpeg', picture_recolored)

样本集可视化结果
在这里插入图片描述

每次迭代的图像(1-10)
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

最后的聚类结果和聚类中心移动的路径
在这里插入图片描述

原始图像和压缩后的图像对比
在这里插入图片描述

我认为这里聚类效果不好可能是和随机初始化聚类中心有关,陷入了局部最优解,其实这里应该尝试下多次迭代初始化k-means算法并实现,然后选择代价函数最小的那个作为最优解。但考虑到sklearn中有现成的可以调用,就没再继续优化了。

下面是采用mini Batch K-Means算法压缩后的图像
在这里插入图片描述
然后用自己的图片试了下,效果也还不错,在保持了较多信息下压缩了图片,文件大小也变小了
在这里插入图片描述
在这里插入图片描述

发布了32 篇原创文章 · 获赞 33 · 访问量 6634

猜你喜欢

转载自blog.csdn.net/weixin_44750583/article/details/89254821