13、K-means算法,无监督学习

"""
在这个练习中,您将实现K-means算法并将其用于图像压缩。
通过减少图像中出现的颜色的数量,只剩下那些在图像中最常见的颜色。
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

在这里插入图片描述
numpy的广播机制:
在这里插入图片描述

def findClosestCentroids(X, centroids):
    """
        分配簇阶段,算法将每一个训练样本分配给最接近的簇中心。
        Args:
            X: 训练集
            centroids: 簇中心

        Returns:
            输出一个一维数组idx,该数组保存距离每一个训练样本最近的簇中心的索引。
    """
    idx = []
    max_dist = 1000000  # 限制最大距离
    # 遍历训练集
    for i in range(len(X)):
        # 计算当前样本与各个聚簇中心的距离(这里的减法运用了numpy的广播机制)
        minus = X[i] - centroids
        dist = minus[:, 0] ** 2 + minus[:, 1] ** 2
        # 选择距离当前样本最近的聚簇中心
        if dist.min() < max_dist:
            ci = np.argmin(dist)  # 返回距离最近的聚簇中心的索引值
            idx.append(ci)  # 放入返回值列表
    return np.array(idx)  # 返回一维数组


# 读取数据
mat = loadmat('data/ex7data2.mat')
# print(mat.keys())  # dict_keys(['__header__', '__version__', '__globals__', 'X'])
# 训练集
X = mat['X']
# 初始化聚簇中心的位置
init_centroids = np.array([[3, 3], [6, 2], [8, 5]])
# 为每一个训练样本分配簇中心
idx = findClosestCentroids(X, init_centroids)
print(idx)
'''
[0,2,1,0,...,1,0]
'''

重新计算每个簇中心,求这个簇里面所有点位置的平均值。
在这里插入图片描述

def computeCentroids(X, idx):
    '''
    重新计算聚簇中心
    Args:
        X: 训练集
        idx: 当前给训练集中的每个样本分配的聚簇索引
    Returns:
        新的聚簇中心位置
    '''
    centroids = []
    # print(np.unique(idx))  # [0 1 2]
    for i in range(len(np.unique(idx))):  # 一共3个聚簇中心,共循环3次
        u_k = X[idx == i].mean(axis=0)  # 按列求平均值(新聚簇中心的位置)
        centroids.append(u_k)
    return np.array(centroids)

print(computeCentroids(X, idx))
'''
[[2.42830111 3.15792418]
 [5.81350331 2.63365645]
 [7.11938687 3.6166844 ]]
'''

可视化训练集和聚簇中心
在这里插入图片描述

def plotData(X, centroids, idx=None):
    '''
    可视化训练集和聚簇中心,并自动分开着色。
    Args:
        X: 训练集
        centroids: 每次聚簇中心点的所有历史记录,type:list (注意:是每次)
        idx: 最后一次迭代生成的idx向量,存储每个样本分配的簇中心点的索引值

    Returns:
        None
    '''
    # 颜色列表
    colors = ['b', 'g', 'gold', 'darkorange', 'salmon', 'olivedrab',
              'maroon', 'navy', 'sienna', 'tomato', 'lightgray', 'gainsboro',
              'coral', 'aliceblue', 'dimgray', 'mintcream', 'mintcream']
    # 断言:聚簇中心的数量是否大于颜色的数量
    assert len(centroids[0]) <= len(colors), 'colors not enough'
    subX = []  # 存储每个聚簇中心的样本集,subX的结构:[X[idx==1]列表, X[idx==2]列表,..., X[idx==K]列表]
    if idx is not None:
        for i in range(centroids[0].shape[0]):  # i:(1,2,3,..,K)
            x_i = X[idx == i]
            subX.append(x_i)
    else:
        subX = [X]

    # 分别画出每个簇的点,并着不同的颜色
    plt.figure(figsize=(8, 5))
    for i in range(len(subX)):  # i:(0,1,2,...,k)
        xx = subX[i]  # xx是一个list,包含所有属于第i聚类的样本元素列表
        plt.scatter(xx[:, 0], xx[:, 1], c=colors[i], label='Cluster %d' % i)
    plt.legend()
    plt.grid(True)
    plt.xlabel('x1', fontsize=14)
    plt.ylabel('x2', fontsize=14)
    plt.title('Plot of X Points', fontsize=16)

    # 画聚簇中心的移动轨迹
    xx, yy = [], []  # xx存聚簇中心的横坐标值,yy存纵坐标值
    for centroid in centroids:
        xx.append(centroid[:, 0])
        yy.append(centroid[:, 1])
    plt.plot(xx, yy, 'rx--', markersize=8)  # 'rx--':红色带小叉的虚线


# 可视化未分类前的数据情况
plotData(X, [init_centroids])


# plt.show()

在这里插入图片描述
无监督分类,画出聚簇中心移动轨迹:

def runKmeans(X, centroids, max_iters):
    '''
    无监督分类,画出聚簇中心移动轨迹
    Args:
        X: 训练集
        centroids: 初始聚簇中心位置
        max_iters: 迭代次数

    Returns:
        每个样本最终所属的聚簇中心索引号 ids
        每次聚簇中心点的所有历史记录 centroids_all 。 type:[二维数组,二维数组,...]
    '''
    K = len(centroids)  # 聚簇中心数量
    centroids_all = []
    centroids_all.append(centroids)
    centroid_i = centroids  # 当前聚簇中心位置
    for i in range(max_iters):
        idx = findClosestCentroids(X, centroid_i)  # 分配簇,idx存放最后一次迭代生成的idx向量
        centroid_i = computeCentroids(X, idx)  # 更新聚簇中心位置
        centroids_all.append(centroid_i)
    return idx, centroids_all


idx, centroids_all = runKmeans(X, init_centroids, 20)
plotData(X, centroids_all, idx)
#plt.show()

在这里插入图片描述
聚簇中心位置的随机初始化

def initCentroids(X, K):
    '''
    聚簇中心位置的随机初始化:随机选择样本点作为聚簇中心
    Args:
        X:训练集
        K:聚簇中心的数量

    Returns:
        聚簇中心的位置
    '''
    m = X.shape[0]  # 样本总数
    idx = np.random.choice(m, K)  # 在训练集样本中随机选择3个样本的索引号  print(type(idx))  # 一维数组
    centroids = X[idx]  # print(type(centroids))  # 二维数组
    return centroids


for i in range(3):
    centroids = initCentroids(X, 3)  # 随机初始化后的聚簇中心
    idx, centroids_all = runKmeans(X, centroids, 10)
    plotData(X, centroids_all, idx)
#plt.show()

有的结果会出现局部最优情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实现图片压缩
读入图片:

'''
用Kmeans来进行图片压缩。
在一个简单的24位颜色表示图像。每个像素被表示为三个8位无符号整数(从0到255),指定了红、绿和蓝色的强度值。
这种编码通常被称为RGB编码。我们的图像包含数千种颜色,在这一部分的练习中,你将把颜色的数量减少到16种颜色。
这可以有效地压缩照片。
具体地说,您只需要存储16个选中颜色的RGB值,
而对于图中的每个像素,现在只需要将该颜色的索引存储在该位置(只需要4 bits就能表示16种可能性)。因为2的4次幂是16。
接下来我们要用K-means算法选16种颜色,用于图片压缩。
你将把原始图片的每个像素看作一个数据样本,然后利用K-means算法去找分组最好的16种颜色。
'''
from skimage import io

A = io.imread('data/bird_small.png')
# print(A.shape)  # (128, 128, 3) 图片大小 :行,列,颜色通道
plt.imshow(A)  # 显示图片
# plt.show()
A = A / 255  # 令图片像素值都处于0~1之间

在这里插入图片描述
压缩图片:

'''
将图像重塑为(N,3)矩阵,其中N=像素个数。
每行将包含红色、绿色和蓝色的像素值
这就相当于给了我们数据集矩阵X,我们将使用K-Means来进行图片压缩。
'''
X = A.reshape(-1, 3)  # 将图像重塑为(N,3)矩阵
K = 16  # 聚簇中心个数设置为16(压缩至只有16种颜色)
centroids = initCentroids(X, K)  # 随机初始化聚簇中心,即随机选择16个颜色块
idx, centroids_all = runKmeans(X, centroids, 10)  # 执行K-Means算法
img = np.zeros(X.shape)  # 存储压缩后的图片
centroids = centroids_all[-1]  # 获取最终的聚簇中心位置
for i in range(K):
    img[idx == i] = centroids[i]  # 压缩后的图片,进行像素块赋值
img = img.reshape((128, 128, 3))  # 像图片矩阵重塑回原来的结构
# 画压缩后的图片
fig, axes = plt.subplots(1, 2, figsize=(12, 6))  # 画一个1行2列的子图
axes[0].imshow(A)
axes[1].imshow(img)
plt.show()

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值