【吴恩达】机器学习作业ex7--(k-means聚类)与(PCA降维)Python

一.前言

此次还是分为俩个部分,第一部分是利用k-means算法进行聚类,第一部分分为俩小步骤,第一步为给好的数据集进行分类(ex7data2),第二步是利用k-means算法来对图片进行压缩,然后第二部分也分为俩小步骤,第一步是将一个二维数组利用PCA方法降到1维(ex7data1),第二步,将一堆人脸图片,利用PCA进行降维(ex7faces)

二.k-means聚类

1.算法思想

k-means算法核心就是将一堆数据进行分堆,如下图,比如一堆数据想分成俩堆,那么就可以随机初始化俩个聚类中心(红点和蓝点),这俩个聚类中心可以在图上的任意一点,那么靠近这俩个聚类中心的点就会被染上颜色,也就是被归为其中一类当中,当图中所有的点也就是数据都被划分之后,要继续移动聚类中心的位置,新的聚类中心位置是由已经归属于此聚类中心所有点的平均值决定的

k-means核心就俩步:1.数据分配    2.移动聚类中心的位置

2.代码部分

2.1 导入工具包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

2.2 提取数据

此次数据的维度为(300*2),这里没有预测值y的一列,因为这是无监督学习,只需要特征值即可

data = loadmat('ex7data2.mat')
# print(data.keys())
X = data['X']  # X的维度为:(300*2)

2.3 数据分布图

X1,X2俩个特征值分别作为x,y轴

# 看一下数据集的散点图
def plot_pic(X):
    plt.figure(figsize=(13, 8))
    plt.scatter(X[:, 0], X[:, 1], c='purple', s=150)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.legend()
    plt.show()


# plot_pic(X)

图片如下,可以很明显看出有大致三个聚类 

2.4 给数据进行分配

此步骤是以目前聚类中心的位置,将每个聚类中心附近的数据都染上颜色,也就是数据进行簇分配

说一下步骤:首先形成一个dataFrame框架用来存放俩列特征值,并赋列名,其次用双层for循环来进行数据的簇分配,外层的for循环用来遍历所有组数据,内层的for循环用来遍历每一个聚类,也就是一组数据与每一个聚类都求一下距离(利用勾股定理求俩点之间距离即可),找出其中最短的距离,记录下当前的聚类下标,也就是离第几个聚类最短,将下标存放到color列表中,其余的数据也进行这样反复的操作,就会得到一个聚类的下标集合,以便于后面的聚类移动所使用

简单来说可以把聚类看成颜色球,就这题而言就是三个颜色球放在图片中,其他的数据可以看成时无色球,三个颜色会自动将自己附近的无色球进行染色,收为小弟,这就是簇分配

# 找离聚类中心最近的值
def find_closest(X, initial_centroids):
    count_data = X.shape[0]
    count_centroids = initial_centroids.shape[0]
    title_list = []
    # 下面将数据集放到dataframe框架中,并赋上每一列的名称(X0,X1,X2..)
    for j in range(X.shape[1]):
        str1 = 'X' + str(j)
        title_list.append(str1)
    classify = pd.DataFrame(X, columns=title_list)
    color = []  # 用于存放每一组数据距离某一个最近的聚类下标
    # print(classify['x1'])
    # 下面双层循环,目的是记录每一组数据其最近的聚类下标
    for i in np.arange(count_data):  # 外层循环用来便利所有数据,按行数来定
        closest_distance = 1000000;  # 设置一个大距离,便于在比较距离时能更好的选择
        closest_distance_index = 0;  # 暂时记录每组数据最近的聚类下标,每次都会加到color列表中
        for j in np.arange(count_centroids):  # 内层循环用来遍历所有聚类中心,找出本组数据距离最短的聚类
            cruent_distance = np.power((X[i, :] - initial_centroids[j, :]), 2).sum()  # 勾股定理来求俩点之间的距离
            if (cruent_distance < closest_distance):  # 如果此聚类离本组数据距离更短,那么更新下标与距离
                closest_distance = cruent_distance
                closest_distance_index = j

        color.append(closest_distance_index)
    classify['color'] = color  # 将分配好的数据聚类下标,加到框架的最后一列,一一对应
    # print(color)
    return classify

下面初始化一下聚类中心,这里随机选择三个聚类中心,看看第一次的簇分配 

initial_centroids = np.array([[3.0, 3.0], [6.0, 2.0], [8.0, 5.0]])  # 初始化一下聚类中心,3个聚类中心
classify = find_closest(X,initial_centroids)

下面是下标集合,也可以看成颜色分类,0,1,2三个颜色 

2.5 移动聚类中心的位置

说一下移动聚类中心的公式:比如设置一个聚类中心为Z,靠近他的数据分别为X1,X3,X5,X6,那么新的聚类中心就是  Z = (X1 + X3  + X5  + X6) / 4,其实就是四个数据的平均值即为新的聚类中心

那么知道了聚类中心的公式,就能知道下面俩个for循环的作用了,外层的循环用于遍历所有的聚类中心,在外层将每一个聚类中心附近的数据都提取出来,在第二个循环的内部来进行聚类中心的更新,我这里是按列求和之后加在一起求的平均值,还有很多其他方法,怎么样都可以

# 移动聚类中心的位置
def move_centroids(initial_centroids, classify):
    count_centroids_row, count_centroids_column = initial_centroids.shape  # 获取聚类中心的行和列
    for i in range(count_centroids_row): # 遍历所有的聚类中心
        color0 = classify['color'].isin([i]) # 找出以当前聚类中心为下标的所有数据
        for k in range(count_centroids_column):
            str1 = 'X' + str(k)
            initial_centroids[i][k] = (classify[color0][str1].sum()) * (1 / len(classify[color0][str1]))
    return initial_centroids

看一下第一次聚类中心的移动

classify = find_closest(X,initial_centroids)
centriods = move_centroids(initial_centroids,classify)
print(centriods)

2.6 执行k-means算法

还是像开头说的,一共就俩步,簇分配,更新聚类中心,我们将前面俩个函数放到一起进行迭代即可,update_times是迭代的次数,这里返回的是一个dataframe框架,里面包含了俩列特征值以及对应的聚类下标,还返回一个聚类中心的终点,也就是不会再改变的位置

# 下面执行kmeans算法
def run_kmeans(X, initial_centroids, update_times):
    centroids = initial_centroids
    for i in range(update_times):
        classify = find_closest(X, centroids) # 簇分配
        centroids = move_centroids(centroids, classify) # 更新聚类中心
    return classify, centroids

2.7 k-means执行之后的图片

# 画出分类之后的图片
def plot_devided(classify):
    plt.figure(figsize=(13, 8))
    color0 = classify['color'].isin([0])
    color1 = classify['color'].isin([1])
    color2 = classify['color'].isin([2])
    plt.scatter(classify[color0]['X0'], classify[color0]['X1'], s=100, c='r')
    plt.scatter(classify[color1]['X0'], classify[color1]['X1'], s=100, c='purple')
    plt.scatter(classify[color2]['X0'], classify[color2]['X1'], s=100, c='y')
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.legend()
    plt.show()
classify1,centroids1 = run_kmeans(X,initial_centroids,20)
plot_devided(classify1)

2.8 随机初始化聚类

将聚类进行初始化是为了避免一些极端情况,比如直接随机到聚类中心的终点,避开局部最优的状况,可以通过多次初始化然后观察图片,手动选择最优的聚类中心

# 下面进行随机初始化聚类化
def random_centriods(X, centriods_nums):
    X_rows, X_colums = X.shape
    centriods = np.zeros((centriods_nums, X_colums))
    any_rows = np.random.randint(0, X_rows, centriods_nums)
    for i in range(centriods_nums):
        centriods[i, :] = X[any_rows[i], :]

    return centriods

下面进行三次随机初始化聚类中心

# 随机初始化三次聚类中心,看形成的分类图
for i in range(3):
    cru_centriods = random_centriods(X,3)
    cru_classify,t = run_kmeans(X,cru_centriods,20)
    plot_devided(cru_classify)

可以看出三次当中第一次就发生了局部最优的状况,所以进行多次初始化并观察才是最稳定的 

3.利用k-means进行图片压缩

3.1 大概思路

此部分利用k-means进行图片压缩,其实图片压缩就是减少颜色,这个图片大小是128*128,也就是由128*128个小方块组成,每一个小方块的颜色都是由三原色所组成的,也就是红黄蓝,可以将红黄蓝看成三个特征值,每个特征值的大小都是(0-255)之间,那么可以知道图片中的有许多个颜色,压缩图片实质就是把颜色减少到一定的数量,这里我们把颜色控制在16种,也就是要初始化16个聚类中心,利用k-means算法进行簇分类,将每个小格子的颜色都规划到这16种颜色中,相近的颜色都会被同化,以达到压缩图片的目的

3.2 取数据

这里可以看到取出的数据与255相除,是为了使数据归一化,控制在0-1之间,更方便计算

# 取数据
image = loadmat('bird_small.mat')
# print(image.keys()) # ['__header__', '__version__', '__globals__', 'A'])
original_pic = image['A']
original_pic = original_pic / 255
print(original_pic.shape) # (128, 128, 3)

3.3 看一下原始图片

# 看一下原始图片
plt.imshow(original_pic)
plt.show()

 3.4 进行k-means压缩

第一步,需要降维,因为图片是128*128*3的维度,三维处理起来比较麻烦,所以降到二维,也就是将128*128个小方格都单独拿出来,每个小方格作为一组数据,每个小方格有三个特征值,也就是三列,所以降维为成(16384 * 3)

第二步,就是常规的随机初始化聚类中心(这里初始化的聚类中心都是从图片中的某一个小方格提取的,并不是瞎选),然后进行k-means算法,得到聚类中心的最终位置(16*3)与包含着聚类下标的dataFrame框架

第三步,将框架中的三列特征值都取出,再将框架中的聚类下标集合也取出(array_color)

第四步,用一个for循环去遍历所有的聚类中心,将每一个聚类中心都赋值给以此聚类中心为下标的数据,也就是进行同化,从而减少颜色而达到压缩的效果

# 下面开始利用kmeans进行压缩,这个图片有许多种颜色,现在我们将它减少到16种颜色,其实也就是分16个聚类
# 首先,图片是128*128的维度,可以看成(128*128)个小方块,每个小方块可以看成是一组数据,每组数据
# 三个特征向量,分别是三原色,红黄绿
# 第一步,降维(128*128*3)-》(16384, 3)
compress_pic = np.reshape(original_pic, (original_pic.shape[0] * original_pic.shape[1], original_pic.shape[2]))
centriods_nums = 16
cru_centriods = random_centriods(compress_pic, centriods_nums)  # 进行初始化聚类中心,也就是初始化选择16种颜色
cru_classify, cru_centriods = run_kmeans(compress_pic, cru_centriods, 20) # 执行k-means算法
array_classify = cru_classify[['X0', 'X1', 'X2']].values # 将框架中的俩列特征值取出来
array_color = cru_classify['color'].values # 将框架中已经分类好的颜色,也就是聚类下标取出来
# 得到16个聚类中心的终点位置,下面将同类进行赋值
for i in range(cru_centriods.shape[0]):
    array_classify[array_color == i] = cru_centriods[i]

看一下压缩之后的图片与压缩之前的图片对比,还是挺明显的

三.PCA压缩(主成分分析法)

1.算法思想

1.1 个人理解

说一下我的理解,PCA方法,其实就是将高纬度数据降到低纬度,在降低维度的同时尽量保持原数据的偏差,意思就是代价尽量低,所获取到的新的低数量的特征值可以用来进行模型训练,能够大大节省模型训练的时间,训练结束后,通过重构数据来看效果。

更通俗的理解,把n个特征值看成一个营n个士兵,从这n个士兵挑出k个士兵,这k个士兵首先得能尽量代表这个营的脸面,其次这k个士兵还要与n个士兵关系尽量贴近,沾点亲属啥的,随后将这k个士兵进行魔鬼训练,打造成特种兵,当再次发起战斗的时候,可以将这k个士兵重新幻化成n个士兵来用(这个过程就是近似重构x_approx),虽然不如n个真实的士兵战斗力那样强悍,但是可以以尽量少的人数在最大程度上造成伤害(也就是精度降低了,但是主要特征还是保留了)

1.2 举例

从图片中可以看到,有5个数据在二维空间上,每个数据X是由俩个向量(特征值)组成,也就是X1,X2,对于此例图来说,PCA降维就是将俩个特征向量减少到一个特征向量,就是将二维降成一维,将平面变成一条线,也就是图上的红线,让这些向量都落在这条线上,条件是垂直于这条红线的向量投影之和要最小

三维空间降到二维也一样(3个特征值-》2个特征值),在三位空间中创建俩个向量,建造一个平面,将三维空间中的数据投射到这个二维平面上,总体的投影距离最小即可

1.3 PCA过程总结

PCA的过程大致可以分成三步:

1.进行数据预处理,均值归一化

2.计算协方差矩阵

3.计算协方差矩阵的特征向量并获得新的特征

4.重建压缩表示 

2.代码

2.1 导入数据包 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

2.2 取出数据

这次给的数据维度为(50*2),俩个特征值X1,X2

data = loadmat('ex7data1.mat')
# print(data.keys())
X = data['X']  # 二维度数据,俩个特征值X1,X2,维度为:(50*2)

2.3 数据分布情况

俩个特征值分别当X轴和Y轴

# 看一下每个点的分布情况
def plot_pic(X):
    plt.figure(figsize=(13, 8))
    plt.scatter(X[:, 0], X[:, 1], c='b', s=150)
    pass
plot_pic(X)
plt.show()

2.4 进行数据预处理(归一化)

这里说一下均值归一化

 就是将每一列特征值的数据加在一起求出该列特征值的平均值(x_mean),其次用该列特征值的每一行数据与该列平均值进行相减(xj - x_mean),相减之后除以标准的偏差,这里标准的偏差其实就是该列特征值所有行中最大值减去最小值(max(xj)- min(xj)),就得到了均值归一化

# 数据预处理,归一化操作
def data_normalization(X):
    X_rows, X_colums = X.shape
    for i in range(X_colums):
        X_means = np.sum(X[:, i]) / X_rows  # 先求出第i个特征值的平均值
        X_molecule = X[:, i] - X_means  # 分子(此特征值下的每行与其平均值的差值)
        X_denominator = np.max(X[:, i]) - np.min(X[:, i])  # 分母,标准差化,其实就是此特征值中最大值减去最小值
        X_normalize = X_molecule / X_denominator  # 进行归一化
        X[:, i] = X_normalize
    pass

看一下均值归一化之前和之后的数据

2.5 计算斜方差矩阵与特征向量

1)斜方差矩阵的公式如下图:

可以看到斜方差矩阵就是每行数据的所有特征值(X1,X21,X3....)的列向量,与自己的转质(行向量)进行矩阵乘法,维度可以推算出(n,1)* (1,n)--> (n*n)的矩阵,斜方差矩阵一定是一个方阵(行 * 列 )*(列 * 行)--> (行,行),接下来将每个每行数据所得出的子斜方差矩阵进行求和,最终得到一个最后的协方差矩阵(n,n)

2)运算时的快捷方法:
像上面那样求是正确的,但是用矩阵的性质来求更快一些,我们可以将数据集X进行转质,X.T与X进行矩阵相乘,直接得到的就是最终的斜方差矩阵,不信的小伙伴可以动手推一下,结果是一样的

# 计算协方差矩阵与其特征向量
def convariance_matrix(X):
    sigmoide = (X.T @ X) * (1 / X.shape[0])  # 斜方差的sigmoid函数,这里利用矩阵的性质直接用合并的大矩阵求
    U, S, V = np.linalg.svd(sigmoide)  # 利用奇异值求值分解,U代表所求的特征向量,S是一个对角矩阵,在求偏差率会派上用场
    return U, S
    pass

2.6 获取新的特征向量(主要特征成分)

np.linalg.svd()其实就是利用奇异值分解来求解矩阵的特征向量的,这里U就是所求的斜方差矩阵的特征向量集合,如果想降到K维(也可以说是想获取k个主要特征成分),那么就取U的前K列,前K列被命名为U_reduce(n,k),利用U_reduce.T与X.T矩阵相乘,获得新的特征向量集合Z,下面是维度的变化过程:

        U_reduce.T(k,n) * X.T(n,m) --> Z(k,m)  (k:要降低到的特征数量,n:原本的特征数量,m:数据的数量,也就是行数),所以最后得到的Z进行转质一下更好理解(Z.T),Z(m,k)代表的就是k列新的特征值(k个主要特征成分),m行数据

# 获取新特征Z
def get_new_Character(X, U, k):
    U_reduce = U[:, :k]
    Z = U_reduce.T @ X.T  # 维度为:(1*50)
    return Z
    pass

 Z矩阵如下,维度为(1,50)50行数据,获取到了1个主要特征

2.7 数据恢复

k维重回n维,数据恢复的主要目的是重构出图像,看是否达到了目标

# 将数据进行近似恢复
def return_X(U, Z, k):
    return (U[:, :k] @ Z).T
    pass

2.8 压缩后展示图

data_normalization(X)
U,S = convariance_matrix(X)
Z = get_new_Character(X,U,1)
# print(Z,Z.shape)
X_approx = return_X(U,Z,1)
plot_pic(X)
plt.plot(X_approx[:,0],X_approx[:,1],c='r')
plt.show()

下面的红线即为蓝点压缩的位置,从二维平面压缩在了一维直线上 

3.人脸图片压缩

3.1 任务与思想

这一部分是利用PCA对许多张人脸图片进行压缩操作,数据集为ex7faces.mat,里面存放了5000张人脸图片,每张人脸图片都是32*32的像素格,X的行代表人脸的个数,列代表每个小像素格颜色的深浅,一共有32*32=1024个像素格,也就是说每张人脸有1024列特征值来构成,下面进入代码部分。

3.2 导入包

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

3.3 取数据

这里取出的X_all的维度为:(5000,1024),我们只取前100张人脸就可

data = loadmat('ex7faces.mat')
# print(data.keys()) # ['__header__', '__version__', '__globals__', 'X']
X_all = data['X']
# print(X_all.shape) # (5000*1024) 可以理解为有5000张人脸照片,每个图片是由1024个小方块组成的(32*32),因为这里只有深色,所以每个小方块的值也就是特征值代表的颜色的深度,从白到黑0-1
X = X_all[:100, :]  # 取前100张人脸
# print(X.shape)

3.4 画出100张人脸

我大致说一下图像画法,首先建立一个大的画布(8*8),里面存放10*10=100个小的画布,俩层for循环遍历到每一个子画布,用imshow方法展示前100行数据,这里注意需要reshape重塑一下,变成方阵,转质是为了图片是正的,要不然是左旋转90度的图片,歪头杀 👀

# 下面画出前100张人脸
def plot_hundred_faces(X):
    figs, axes = plt.subplots(nrows=10, ncols=10, figsize=(10, 10))  # 建造一个10*10的大画布,里面有一个100个子画布
    for i in range(10):
        for j in range(10):
            axes[i][j].imshow(X[(i * 10) + j, :].reshape(32, 32).T)
    pass
plot_hundred_faces(X)
plt.show()

3.5 其余操作都和PCA第一部分代码相同,4步走,这里我只展示最后一步了

这次我打算获取前100个主要特征,k设置为100,得到Z(100,100),再进行数据结构的恢复,求出近似值矩阵x_approx,看看到底损失了哪些细节

data_normalization(X)
U,S = convariance_matrix(X)
Z = get_new_Character(X,U,100)
X_approx = return_X(U,Z,100)
plot_hundred_faces(X_approx)
plt.show()

下面俩张图片分别是原图,和近似值x_approx数据集的图片 

 四.全部代码

1.ex7data2.mat代码(k-means第一部分)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

data = loadmat('ex7data2.mat')
# print(data.keys())
X = data['X']  # X的维度为:(300*2)


# print(X.shape)

# 看一下数据集的散点图
def plot_pic(X):
    plt.figure(figsize=(13, 8))
    plt.scatter(X[:, 0], X[:, 1], c='purple', s=150)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.legend()
    plt.show()


# plot_pic(X)

initial_centroids = np.array([[3.0, 3.0], [6.0, 2.0], [8.0, 5.0]])  # 初始化一下聚类中心,3个聚类中心


# 找离3个聚类中心最近的值
def find_closest(X, initial_centroids):
    count_data = X.shape[0]
    count_centroids = initial_centroids.shape[0]
    classify = pd.DataFrame(X, columns=['X1', 'X2'])
    color = []
    # print(classify['x1'])
    for i in np.arange(count_data):
        closest_distance = 1000000;
        closest_distance_index = 0;
        for j in np.arange(count_centroids):
            cruent_distance = np.power((X[i, :] - initial_centroids[j, :]), 2).sum()
            if (cruent_distance < closest_distance):
                closest_distance = cruent_distance
                closest_distance_index = j

        color.append(closest_distance_index)
    classify['color'] = color
    # print(color)
    return classify


# classify = find_closest(X,initial_centroids)
# zero = classify['color'].isin([0])
# print(classify[zero]['X1'])

# 移动聚类中心的位置
def move_centroids(initial_centroids, classify):
    color0 = classify['color'].isin([0])
    color1 = classify['color'].isin([1])
    color2 = classify['color'].isin([2])
    color_list = [color0, color1, color2]
    count_centroids_row, count_centroids_column = initial_centroids.shape
    for i in range(count_centroids_row):
        initial_centroids[i][0] = (classify[color_list[i]]['X1'].sum()) * (1 / len(classify[color_list[i]]['X1']))
        initial_centroids[i][1] = (classify[color_list[i]]['X2'].sum()) * (1 / len(classify[color_list[i]]['X1']))
    return initial_centroids


# classify = find_closest(X,initial_centroids)
# centriods = move_centroids(initial_centroids,classify)
# print(centriods)

# 下面执行kmeans算法
def run_kmeans(X, initial_centroids, update_times):
    centroids = initial_centroids
    for i in range(update_times):
        classify = find_closest(X, centroids)
        centroids = move_centroids(centroids, classify)
    return classify, centroids


# print(f"centroids:{centroids1}")
# 画出分类之后的图片
def plot_devided(classify):
    plt.figure(figsize=(13, 8))
    color0 = classify['color'].isin([0])
    color1 = classify['color'].isin([1])
    color2 = classify['color'].isin([2])
    plt.scatter(classify[color0]['X1'], classify[color0]['X2'], s=100, c='r')
    plt.scatter(classify[color1]['X1'], classify[color1]['X2'], s=100, c='purple')
    plt.scatter(classify[color2]['X1'], classify[color2]['X2'], s=100, c='y')
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.legend()
    plt.show()


# classify1,centroids1 = run_kmeans(X,initial_centroids,20)
# # print(centroids1)
# plot_devided(classify1)

# 下面进行随机初始化聚类化
def random_centriods(X, centriods_nums):
    X_rows, X_colums = X.shape
    centriods = np.zeros((centriods_nums, X_colums))
    any_rows = np.random.randint(0, X_rows, centriods_nums)
    for i in range(centriods_nums):
        centriods[i, :] = X[any_rows[i], :]

    return centriods

# 随机初始化三次聚类中心,看形成的分类图
for i in range(3):
    cru_centriods = random_centriods(X,3)
    cru_classify,t = run_kmeans(X,cru_centriods,20)
    plot_devided(cru_classify)

2.bird_small.mat(k-means第二部分,对小鸟图片进行压缩)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

data = loadmat('ex7data2.mat')
# print(data.keys())
X = data['X']  # X的维度为:(300*2)


# print(X.shape)

# 看一下数据集的散点图
def plot_pic(X):
    plt.figure(figsize=(13, 8))
    plt.scatter(X[:, 0], X[:, 1], c='purple', s=150)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.legend()
    plt.show()


# plot_pic(X)


# 找离聚类中心最近的值
def find_closest(X, initial_centroids):
    count_data = X.shape[0]
    count_centroids = initial_centroids.shape[0]
    title_list = []
    # 下面将数据集放到dataframe框架中,并赋上每一列的名称(X0,X1,X2..)
    for j in range(X.shape[1]):
        str1 = 'X' + str(j)
        title_list.append(str1)
    classify = pd.DataFrame(X, columns=title_list)
    color = []  # 用于存放每一组数据距离某一个最近的聚类下标
    # print(classify['x1'])
    # 下面双层循环,目的是记录每一组数据其最近的聚类下标
    for i in np.arange(count_data):  # 外层循环用来便利所有数据,按行数来定
        closest_distance = 1000000;  # 设置一个大距离,便于在比较距离时能更好的选择
        closest_distance_index = 0;  # 暂时记录每组数据最近的聚类下标,每次都会加到color列表中
        for j in np.arange(count_centroids):  # 内层循环用来遍历所有聚类中心,找出本组数据距离最短的聚类
            cruent_distance = np.power((X[i, :] - initial_centroids[j, :]), 2).sum()  # 勾股定理来求俩点之间的距离
            if (cruent_distance < closest_distance):  # 如果此聚类离本组数据距离更短,那么更新下标与距离
                closest_distance = cruent_distance
                closest_distance_index = j

        color.append(closest_distance_index)
    classify['color'] = color  # 将分配好的数据聚类下标,加到框架的最后一列,一一对应
    # print(f"数据对应的聚类下标:{color}")
    return classify


initial_centroids = np.array([[3.0, 3.0], [6.0, 2.0], [8.0, 5.0]])  # 初始化一下聚类中心,3个聚类中心
# classify = find_closest(X,initial_centroids)


# 移动聚类中心的位置
def move_centroids(initial_centroids, classify):
    count_centroids_row, count_centroids_column = initial_centroids.shape  # 获取聚类中心的行和列
    for i in range(count_centroids_row): # 遍历所有的聚类中心
        color0 = classify['color'].isin([i]) # 找出以当前聚类中心为下标的所有数据
        for k in range(count_centroids_column):
            str1 = 'X' + str(k)
            initial_centroids[i][k] = (classify[color0][str1].sum()) * (1 / len(classify[color0][str1]))
    return initial_centroids


# classify = find_closest(X,initial_centroids)
# centriods = move_centroids(initial_centroids,classify)
# print(centriods)

# 下面执行kmeans算法
def run_kmeans(X, initial_centroids, update_times):
    centroids = initial_centroids
    for i in range(update_times):
        classify = find_closest(X, centroids) # 簇分配
        centroids = move_centroids(centroids, classify) # 更新聚类中心
    return classify, centroids


# print(f"centroids:{centroids1}")
# 画出分类之后的图片
def plot_devided(classify):
    plt.figure(figsize=(13, 8))
    color0 = classify['color'].isin([0])
    color1 = classify['color'].isin([1])
    color2 = classify['color'].isin([2])
    plt.scatter(classify[color0]['X0'], classify[color0]['X1'], s=100, c='r')
    plt.scatter(classify[color1]['X0'], classify[color1]['X1'], s=100, c='purple')
    plt.scatter(classify[color2]['X0'], classify[color2]['X1'], s=100, c='y')
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.legend()
    plt.show()


# classify1,centroids1 = run_kmeans(X,initial_centroids,20)
# plot_devided(classify1)

# 下面进行随机初始化聚类化
def random_centriods(X, centriods_nums):
    X_rows, X_colums = X.shape
    centriods = np.zeros((centriods_nums, X_colums))
    any_rows = np.random.randint(0, X_rows, centriods_nums)
    for i in range(centriods_nums):
        centriods[i, :] = X[any_rows[i], :]

    return centriods

# # 随机初始化三次聚类中心,看形成的分类图
# for i in range(3):
#     cru_centriods = random_centriods(X,3)
#     cru_classify,t = run_kmeans(X,cru_centriods,20)
#     plot_devided(cru_classify)

# 下面是利用kmeans进行图像压缩
# 取数据
image = loadmat('bird_small.mat')
# print(image.keys()) # ['__header__', '__version__', '__globals__', 'A'])
original_pic = image['A']
original_pic = original_pic / 255
print(original_pic.shape) # (128, 128, 3)
# # 看一下原始图片
plt.imshow(original_pic)
plt.show()

# 下面开始利用kmeans进行压缩,这个图片有许多种颜色,现在我们将它减少到16种颜色,其实也就是分16个聚类
# 首先,图片是128*128的维度,可以看成(128*128)个小方块,每个小方块可以看成是一组数据,每组数据
# 三个特征向量,分别是三原色,红黄绿
# 第一步,降维(128*128*3)-》(16384, 3)
compress_pic = np.reshape(original_pic, (original_pic.shape[0] * original_pic.shape[1], original_pic.shape[2]))
centriods_nums = 16
cru_centriods = random_centriods(compress_pic, centriods_nums)  # 进行初始化聚类中心,也就是初始化选择16种颜色
cru_classify, cru_centriods = run_kmeans(compress_pic, cru_centriods, 20) # 执行k-means算法
array_classify = cru_classify[['X0', 'X1', 'X2']].values # 将框架中的俩列特征值取出来
array_color = cru_classify['color'].values # 将框架中已经分类好的颜色,也就是聚类下标取出来
# 得到16个聚类中心的终点位置,下面将同类进行赋值
for i in range(cru_centriods.shape[0]):
    array_classify[array_color == i] = cru_centriods[i]

new_pic = np.reshape(array_classify, (original_pic.shape[0], original_pic.shape[1], original_pic.shape[2]))
plt.imshow(new_pic)
plt.show()

3.ex7data1.mat(PCA第一部分练习)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

data = loadmat('ex7data1.mat')
# print(data.keys())
X = data['X']  # 二维度数据,俩个特征值X1,X2,维度为:(50*2)


# 看一下每个点的分布情况
def plot_pic(X):
    plt.figure(figsize=(13, 8))
    plt.scatter(X[:, 0], X[:, 1], c='b', s=150)
    pass


# plot_pic(X)
# plt.show()

# 数据预处理,归一化操作
def data_normalization(X):
    X_rows, X_colums = X.shape
    for i in range(X_colums):
        X_means = np.sum(X[:, i]) / X_rows  # 先求出第i个特征值的平均值
        X_molecule = X[:, i] - X_means  # 分子(此特征值下的每行与其平均值的差值)
        X_denominator = np.max(X[:, i]) - np.min(X[:, i])  # 分母,标准差化,其实就是此特征值中最大值减去最小值
        X_normalize = X_molecule / X_denominator  # 进行归一化
        X[:, i] = X_normalize
    pass


# print(X)
# data_normalization(X)
# print(X)


# 计算协方差矩阵与其特征向量
def convariance_matrix(X):
    sigmoide = (X.T @ X) * (1 / X.shape[0])  # 斜方差的sigmoid函数,这里利用矩阵的性质直接用合并的大矩阵求
    U, S, V = np.linalg.svd(sigmoide)  # 利用奇异值求值分解,U代表所求的特征向量,S是一个对角矩阵,在求偏差率会派上用场
    return U, S
    pass


# data_normalization(X)
# U,S = convariance_matrix(X)
# print(U)

# 获取新特征Z
def get_new_Character(X, U, k):
    U_reduce = U[:, :k]
    Z = U_reduce.T @ X.T  # 维度为:(1*50)
    return Z
    pass


# 将数据进行近似恢复
def return_X(U, Z, k):
    return (U[:, :k] @ Z).T
    pass

data_normalization(X)
U,S = convariance_matrix(X)
Z = get_new_Character(X,U,1)
# print(Z,Z.shape)
X_approx = return_X(U,Z,1)
plot_pic(X)
plt.plot(X_approx[:,0],X_approx[:,1],c='r')
plt.show()

4.ex7faces.mat(PCA第二部分人脸图片压缩)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat

data = loadmat('ex7faces.mat')
# print(data.keys()) # ['__header__', '__version__', '__globals__', 'X']
X_all = data['X']
# print(X_all.shape) # (5000*1024) 可以理解为有5000张人脸照片,每个图片是由1024个小方块组成的(32*32),因为这里只有深色,所以每个小方块的值也就是特征值代表的颜色的深度,从白到黑0-1
X = X_all[:100, :]  # 取前100张人脸
print(X.shape)


# 下面画出前100张人脸
def plot_hundred_faces(X):
    figs, axes = plt.subplots(nrows=10, ncols=10, figsize=(10, 10))  # 建造一个10*10的大画布,里面有一个100个子画布
    for i in range(10):
        for j in range(10):
            axes[i][j].imshow(X[(i * 10) + j, :].reshape(32, 32).T)
    pass


plot_hundred_faces(X)
plt.show()

# 数据预处理,归一化操作
def data_normalization(X):
    X_rows, X_colums = X.shape
    for i in range(X_colums):
        X_means = np.sum(X[:, i]) / X_rows  # 先求出第i个特征值的平均值
        X_molecule = X[:, i] - X_means  # 分子(此特征值下的每行与其平均值的差值)
        X_denominator = np.max(X[:, i]) - np.min(X[:, i])  # 分母,标准差化,其实就是此特征值中最大值减去最小值
        X_normalize = X_molecule / X_denominator  # 进行归一化
        X[:, i] = X_normalize
    pass


# data_normalization(X)

# 计算协方差矩阵与其特征向量
def convariance_matrix(X):
    sigmoide = (X.T @ X) * (1 / X.shape[0])  # 斜方差的sigmoid函数,这里利用矩阵的性质直接用合并的大矩阵求
    U, S, V = np.linalg.svd(sigmoide)  # 利用奇异值求值分解,U代表所求的特征向量,S是一个对角矩阵,在求偏差率会派上用场
    return U, S
    pass


# data_normalization(X)
# U,S = convariance_matrix(X)

# 获取新特征Z
def get_new_Character(X, U, k):
    U_reduce = U[:, :k]
    Z = U_reduce.T @ X.T
    return Z
    pass


# 将数据进行近似恢复
def return_X(U, Z, k):
    return (U[:, :k] @ Z).T
    pass

data_normalization(X)
U,S = convariance_matrix(X)
U_T = U.T
Z = get_new_Character(X,U,100)
X_approx = return_X(U,Z,100)
print(X_approx)
plot_hundred_faces(X_approx)
# plot_hundred_faces(U_T)
plt.show()

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值