吴恩达机器学习作业7(上)---K-means Clustering 的实现和应用(图像压缩)

K-means 实现

在这里插入图片描述
导入类库

import numpy as np
import matplotlib.pyplot as plt
import scipy.io #导入.mat文件
from random import sample #随机初始化
import matplotlib.cm as cm #用于在特定颜色映射中显示图像
from scipy import linalg #Used for the "SVD" function
import imageio #用于导入图像文件
%matplotlib inline

导入数据

datafile = 'data/ex7data2.mat'
mat = scipy.io.loadmat( datafile )
X = mat['X']
#X.shape (300, 2)

编写可视化数据的函数

#可视化数据
#传入样本X,clusters,c^(i)(即历史分类)
def plotData(myX,mycentroids,myidxs = None):
    colors = ['b','g','gold','darkorange','salmon','olivedrab']
    
    assert myX[0].shape == mycentroids[0][0].shape
    assert mycentroids[-1].shape[0] <= len(colors)#cluster点的维度

    #如果提供了idxs(样本点所属clusters向量),就按idxs对X进行颜色分类到subX
    if myidxs is not None:
        assert myidxs.shape[0] == myX.shape[0]
        subX = []
        #mycentroids为装载着centroids的所有历史点的list
        #遍历clusters
        for x in range(mycentroids[0].shape[0]):
            #对myX进行颜色分类
            subX.append(np.array([myX[i] for i in range(myX.shape[0]) if myidxs[i] == x]))
    else:
        #否则都分为一个颜色
        subX = [myX]
        
    fig = plt.figure(figsize=(7,5))
    #遍历clusters
    for x in range(len(subX)):
        newX = subX[x]
        plt.plot(newX[:,0],newX[:,1],'o',color=colors[x],alpha=0.75, label='Data Points: Cluster %d'%x)
    plt.xlabel('x1',fontsize=14)
    plt.ylabel('x2',fontsize=14)
    plt.title('Plot of X Points',fontsize=16)
    plt.grid(True)

    #绘制cluster的移动轨迹
    tempx, tempy = [], []
    for mycentroid in mycentroids:
        tempx.append(mycentroid[:,0])
        tempy.append(mycentroid[:,1])
    
    for x in range(len(tempx[0])):
        plt.plot(tempx, tempy, 'rx--', markersize=8)

    leg = plt.legend(loc=4, framealpha=0.5)

可视化数据

#先初始化几个centroids
initial_centroids = np.array([[3,3],[6,2],[8,5]])

plotData(X,[initial_centroids])

在这里插入图片描述
好了,下面开始编写K-means算法
J ( c ( i ) , ⋯   , c ( m ) , u 1 , ⋯   , u K ) = 1 m ∑ i = 1 m ∣ ∣ x ( i ) − u c ( i ) ∣ ∣ 2 J(c^{(i)},\cdots,c^{(m)},u_1,\cdots,u_K)=\frac{1}{m}\sum_{i=1}^{m}||x^{(i)}-u_{c^{(i)}}||^2 J(c(i),,c(m),u1,,uK)=m1i=1mx(i)uc(i)2

计算两点的距离的平方的函数

def distSquared(point1, point2):
    assert point1.shape == point2.shape
    return np.sum(np.square(point2-point1))

该函数返回包含每个样本点所属的cluster的向量(这是算法的第一步

def findClosestCentroids(myX, mycentroids):
    #初始化向量
    idxs = np.zeros((myX.shape[0],1))
    #遍历训练集
    for x in range(idxs.shape[0]):
        mypoint = myX[x]
        #Compare this point to each centroid,
        #Keep track of shortest distance and index of shortest distance
        mindist, idx = 9999999, 0
        #遍历clusters
        for i in range(mycentroids.shape[0]):
            mycentroid = mycentroids[i]
            distsquared = distSquared(mycentroid,mypoint)
            if distsquared < mindist:
                mindist = distsquared
                idx = i
        #With the best index found, modify the result idx vector
        #修改第x个样本点所属的cluster
        idxs[x] = idx
        
    return idxs

测试一下编写的函数

#为每个样本点寻找所属的cluster
idxs = findClosestCentroids(X,initial_centroids)
#print(idxs[:3].flatten())
#[0. 2. 1.]

plotData(X,[initial_centroids],idxs)

这是一开始样本点的归属情况
在这里插入图片描述
计算cluster的新位置(这是算法的第二步)

def computeCentroids(myX, myidxs):
    subX = []
    #遍历clusters
    for x in range(len(np.unique(myidxs))):
        #将样本点分为几类装到subX中
        subX.append(np.array([myX[i] for i in range(myX.shape[0]) if myidxs[i] == x]))
    #求subX各个平均值并返回numpy数组
    return np.array([np.mean(thisX,axis=0) for thisX in subX])

组装算法

#K-means算法,返回每个点所属的向量和历史clusters(即移动轨迹)
#传入X,初始clusters,K值,迭代次数
def runKMeans(myX, initial_centroids, K, n_iter):
    centroid_history = []
    current_centroids = initial_centroids
    for myiter in range(n_iter):
        centroid_history.append(current_centroids)
        #idxs为每个样本点所属的cluster的向量(第一步)
        idxs = findClosestCentroids(myX,current_centroids)
        #计算cluster的新位置(第二步)
        current_centroids = computeCentroids(myX,idxs)
        
    return idxs, centroid_history

测试

idxs, centroid_history = runKMeans(X,initial_centroids,K=3,n_iter=10)
plotData(X,centroid_history,idxs)

可以看到centroids的移动轨迹
在这里插入图片描述
下面要进行随机初始化

随机初始化函数

#随机初始化cluster的位置
def chooseKRandomCentroids(myX, K):
    #随机下标列表
    rand_indices = sample(range(0,myX.shape[0]),K)
    #根据随机下标抽取样本点作为初始cluster
    return np.array([myX[i] for i in rand_indices])    

测试

#随机初始化三次后cluster的不同解
for x in range(3):
    idxs, centroid_history = runKMeans(X,chooseKRandomCentroids(X,K=3),K=3,n_iter=10)
    plotData(X,centroid_history,idxs)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
最终选择失真值最小的聚类结果

图像压缩

在这里插入图片描述

在这里插入图片描述

前言

一般来说,图片以RGB的形式存储,即一张图片,三个图层(Red,Green,Bule),本次作业的图片为(128x128)像素

已知每个像素的数据结构为(row,column,color),其中color为(Red,Green,Bule), r e d , g r e e n , b u l e , ∈ ( 0 , 255 ) red,green,bule ,\in(0,255) redgreenbule(0,255)

我们打算将一个像素看作一个样本点,这些样本点分布在RGB空间(以red,green,bule为轴),采用K-means聚类算法,将成百上千中颜色压缩为16种

原本一个像素需要8bit+8bit+8bit=24bit(8bit可表示0~255),经过聚类后,只剩16种颜色,故一个像素只需4bit(还需要一些空间来存储每个像素的索引值

代码分析

导入数据

#导入数据
datafile = 'data/bird_small.png'
A = imageio.imread(datafile)
print("A shape is ",A.shape)
dummy = plt.imshow(A)

A shape is (128, 128, 3)
在这里插入图片描述
处理数据并执行K-means聚类算法

#标准化A
A = A / 255.
#将图片 128*128*3 展开为 16384*3
A = A.reshape(-1, 3)

#每个像素代表一个样本点(row,column,color)
#采用随机初始化,运行K-means算法
myK = 16#16个centroids
idxs, centroid_history = runKMeans(A,chooseKRandomCentroids(A,myK),myK,n_iter=10)

得到最终的clusters,并为新图片的每个像素赋上新的颜色

#最终的cluster
final_centroids = centroid_history[-1]
# Now loop through the original image and form a new image
# that only has 16 colors in it
final_image = np.zeros((idxs.shape[0],3))
for x in range(final_image.shape[0]):
    final_image[x] = final_centroids[int(idxs[x])]

打印图片

plt.figure()
dummy = plt.imshow(A.reshape(128,128,3))
plt.figure()
dummy = plt.imshow(final_image.reshape(128,128,3))

在这里插入图片描述
在这里插入图片描述

数据集

ex7data2.mat
birdsmall.png
这里偷个懒,数据可以上kaggle查

猜你喜欢

转载自blog.csdn.net/NP_hard/article/details/113888365