吴恩达机器学习ex7:K均值和PCA

吴恩达机器学习作业七:K均值和PCA

在这里插入图片描述
K均值知识点回顾:
在这里插入图片描述
在这里插入图片描述

1 K-means Clustering

在本练习中,您将实现K-means算法并将其用于图像压缩。首先从一个示例2D数据集开始,这有助于你对K-均值算法的工作原理有一个直观的了解。然后,您将使用K-means算法进行图像压缩,方法是将图像中出现的颜色数减少到仅该图像中最常见的颜色。

1.1 Implementing K-means

K-means算法是一种自动聚类相似数据的方法。具体地说,给你一个训练集,并希望将数据分组为几个有凝聚力的“集群”。K-means背后的直觉是一个迭代过程,首先猜测初始质心,然后通过反复将示例指定给它们最接近的质心,然后根据指定重新计算质心来细化此猜测。
K-均值算法如下:
该算法的内环重复执行两个步骤:
(i)将每个训练示例x(i)分配给其最近的质心
(ii)使用分配给它的点重新计算每个质心的平均值。
K-means算法总是收敛到质心的最终均值集。注意,收敛解可能并不总是理想的,它取决于质心的初始设置。因此,在实际应用中,K-means算法通常会在不同的随机初始化下运行几次。从不同的随机初始化中选择不同解决方案的一种方法是选择成本函数值(失真)最低的解决方案。在下一节中,您将分别实现K-means算法的两个阶段。

1.1.1 Finding closest centroids

在这里插入图片描述

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

def findClosestCentroids(X,centroids):
    idx  = []
    for i in range(len(X)):
        minus = X[i] - centroids
        distance = minus[:,0] ** 2 + minus[:,1] ** 2
        dis = np.argmin(distance)
        idx.append(dis)
    return np.array(idx)

测试一下

path = 'D:编程/ex7data2.mat'
data1 = loadmat(path)
X = data1['X']
init_centroids = np.array([[3,3],[6,2],[8,5]])
idx = findClosestCentroids(X,init_centroids)
print(idx)

在这里插入图片描述

1.1.2 Computing centroid means

给定每个点到一个质心的分配,算法的第二阶段为每个质心重新计算分配给它的点的平均值。
在这里插入图片描述

def computeCentroids(X,idx):
    centroids = []
    k = np.max(idx)+1
    for j in range(k):
        X_mean = X[idx == j].mean(axis=0)
        centroids.append(X_mean) # 每列求取平均值
    return np.array(centroids)
print(computeCentroids(X,idx))

在这里插入图片描述

1.2 K-means on example dataset

# 获取迭代n次后,各样本的分类情况和簇中心
def runKmeans(X,init_centroids,iters):
    centroids_all = []
    centroids = init_centroids
    for m in range(iters):
        idx = findClosestCentroids(X,centroids)
        centroids = computeCentroids(X,idx)
        centroids_all.append(centroids)
    return idx,centroids,np.array(centroids_all)


# 画图
def plotData(X,idx,centroids):
    k = len(centroids)
    plt.figure(figsize=(6,6))
    colors = ['b', 'g', 'gold', 'darkorange', 'salmon', 'olivedrab',
              'maroon', 'navy', 'sienna', 'tomato', 'lightgray', 'gainsboro'
               'coral', 'aliceblue', 'dimgray', 'mintcream','mintcream']
    for n in range(k):
        cluster = X[idx == n]
        xx = cluster[:,0]
        yy = cluster[:,1]
        plt.scatter(xx,yy,marker='o',c = colors[n],label='Cluster %d'%n)
    plt.legend()
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.title('Plot of X Points')
    # 画出簇中心点的移动轨迹
    xxx, yyy = [], []
    for centroid in centroids_all:
        xxx.append(centroid[:, 0])
        yyy.append(centroid[:, 1])
    plt.plot(xxx, yyy, 'rx--',)
    plt.show()

# 执行入口
if __name__ == "__main__":
    path = 'D:编程/ex7data2.mat'
    data1 = loadmat(path)
    X = data1['X']
    init_centroids = np.array([[3, 3], [6, 2], [8, 5]])
    idx, centroids,centroids_all = runKmeans(X, init_centroids, 10)
    plotData(X,idx,centroids)

在这里插入图片描述
在上面的基础上进行改进,增加随机初始化过程。每次会产生不一样的结果。

1.3 Random initialization

在实际应用中,一个很好的质心初始化策略是从训练集中随机选取样本。

# 随机初始化聚类中心
def init_centroids(X,k):
    init_centroids = []
    for a in range(k):
        centre = []
        x = np.random.randint(np.max(X[:,0]))
        y = np.random.randint(np.max(X[:,1]))
        centre.append(x)
        centre.append(y)
        init_centroids.append(centre)
    print(init_centroids)
    return init_centroids



# 执行入口
if __name__ == "__main__":
    path = 'D:编程/ex7data2.mat'
    data1 = loadmat(path)
    X = data1['X']
    init_centroids = init_centroids(X,3)
    idx, centroids,centroids_all = runKmeans(X, init_centroids, 10)
    plotData(X,idx,centroids)
# 另一种随机初始化
def init_centroids(X, k):
    """随机初始化"""
    m, n = X.shape
    centre = np.random.choice(m, k)
    centroids = X[centre]
    return centroids

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

1.4 Image compression with K-means

一个简单的24位彩色图像表示法,每个像素表示为三个8位无符号整数(范围从0到255),指定红、绿和蓝的强度值。这种编码通常被称为RGB编码。我们的图像包含数千种颜色,在这部分练习中,您将把颜色的数量减少到16种颜色。通过这种简化,可以有效地表示(压缩)照片。具体来说,您只需要存储16种选定颜色的RGB值,并且对于图像中的每个像素,您现在只需要存储该位置处的颜色索引(其中仅需要4位来表示16种可能性)。在本练习中,您将使用K-means算法选择16种颜色来表示压缩图像。具体地说,您将把原始图像中的每个像素作为一个数据示例,并使用K-means算法来找到在三维RGB空间中对像素进行最佳分组(聚类)的16种颜色。一旦计算了图像上的簇质心,就可以使用16种颜色替换原始图像中的像素。

1.4.1 K-means on pixels

这将创建一个三维矩阵A,其前两个索引标识像素位置,最后一个索引表示红色、绿色或蓝色。例如,A(50,33,3)给出第50行和第33列的像素的蓝色强度。首先加载图像,然后对其进行整形以创建一个m× 3像素颜色矩阵(其中m=16384=128× 128),并对其调用K-means函数。
在找到最上面的K=16个颜色来表示图像之后,你可以使用findClosestCentroids函数将每个像素位置指定给其最近的质心。这允许您使用每个像素的质心指定来表示原始图像。请注意,您已经大大减少了描述图像所需的位数。原始图像需要24位,每一个128位×128像素位置,总大小为128×128×24=393216位。新的表示法需要一些开销存储,比如16种颜色的字典,每种颜色需要24位,但是图像本身只需要每像素4位。因此,最终使用的位数是16× 24 + 128 × 128× 4=65920位,相当于将原始图像压缩约6倍。
最后,您可以通过仅基于质心指定重建图像来查看压缩效果。具体来说,您可以用指定给每个像素的质心的平均值来替换每个像素位置。

from skimage import io

path2 = 'D:编程/bird_small.png'
A = io.imread(path2)
print(A.shape)
plt.imshow(A)
plt.show()
# (128, 128, 3)

在这里插入图片描述

#encoding=utf-8
import  numpy as np
from matplotlib import pyplot as plt
from skimage import io

def findClosestCentroids(X,centroids):
    idx  = []
    for i in range(len(X)):
        minus = X[i] - centroids
        distance = minus[:,0] ** 2 + minus[:,1] ** 2 + minus[:,2] ** 2
        dis = np.argmin(distance)
        idx.append(dis)
    return np.array(idx)


def computeCentroids(X,idx):
    centroids = []
    k = np.max(idx)+1
    for j in range(k):
        if any(idx==j):
            X_mean = X[idx == j].mean(axis=0)
            centroids.append(X_mean) # 每列求取平均值
    return np.array(centroids)





# 获取迭代n次后,各样本的分类情况和簇中心
def runKmeans(X,init_centroids,iters):
    centroids_all = []
    centroids = init_centroids
    for m in range(iters):
        idx = findClosestCentroids(X,centroids)
        centroids = computeCentroids(X,idx)
        centroids_all.append(centroids)
    return idx,centroids,np.array(centroids_all)


#随机初始化聚类中心
def init_centroids(X,k):
    init_centroids = []
    for a in range(k):
        centre = []
        x = np.random.random()
        y = np.random.random()
        z = np.random.random()
        centre.append(x)
        centre.append(y)
        centre.append(z)
        init_centroids.append(centre)
    print(init_centroids)
    return init_centroids

# # 另一种随机初始化
# def init_centroids(X, k):
#     """随机初始化"""
#     m, n = X.shape
#     centre = np.random.choice(m, k)
#     init_centroids = X[centre]
#     return init_centroids


path2 = 'D:编程/bird_small.png'
A = io.imread(path2)
A = A / 255
"""
除以255的目的是做一个数据的归一化,把所有的像素值限定到(0,1),
这对计算距离的模型来说(比如K-means)是很重要的,不然会被离群值影响较大。
 plt.imshow( )函数说明有这样的解释,输入的像素值若是浮点数,
 应该在(0, 1)的范围内,或者是整数,应在(0, 255)的范围内。
"""

#  Reshape the image into an (N,3) matrix where N = number of pixels.
#  Each row will contain the Red, Green and Blue pixel values
#  This gives us our dataset matrix X that we will use K-Means on.
X = A.reshape(-1, 3)
k = 16
init_centroids = init_centroids(X, k)
idx, centroids,centroids_all = runKmeans(X, init_centroids, 10)
img = np.zeros(X.shape)
for  i in range(len(centroids)):
    img[idx==i] = centroids[i]
img = np.reshape(img,A.shape)


fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(20, 8), dpi=100)

axes[0].imshow(A)
axes[1].imshow(img)
plt.show()

# 另一种显示方法
# plt.subplot(121)
# plt.imshow(A)
# plt.subplot(122)
# plt.imshow(img)
# plt.show()

在这里插入图片描述
图中显示了我们获得的重建。即使生成的图像保留了原始图像的大部分特征,我们也会看到一些压缩伪影。
您可以看到我们对图像进行了压缩,但图像的主要特征仍然存在。 这就是K-means。 下面我们来用scikit-learn来实现K-means。

from matplotlib import pyplot as plt
from skimage import io
from sklearn.cluster import KMeans # 导入K均值
path2 = 'D:编程/bird_small.png'
A = io.imread(path2)
A = A / 255
io.imshow(A)
# plt.show()
print(A.shape)
data = A.reshape(128*128,3)
print(data.shape)

model = KMeans(n_clusters=16,n_init=100)
# n_clusters=生成的聚类数, n_init=迭代次数,n_jobs=多线程,
# init:用不同的质心初始化值运行算法的次数,k-means++(默认),random
# 聚类
model.fit(data)
# 簇中心
centroids = model.cluster_centers_
print(centroids.shape)
# (16,3)

# 相当于前面的idx
C = model.predict(data)
print(C.shape)
#(16384,)

print(centroids[C].shape)
# (16384,3)

compressed_A = centroids[C].reshape((128,128,3))

fig, ax = plt.subplots(1, 2)
ax[0].imshow(A)
ax[1].imshow(compressed_A)
plt.show()

1.5 Optional (ungraded) exercise: Use your own image

在这里插入图片描述

2 Principal Component Analysis

知识点回顾:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在本练习中,您将使用主成分分析(PCA)来执行降维。您将首先使用一个示例2D数据集进行实验,以获得PCA工作原理的直观信息,然后将其用于5000个人脸图像数据集的更大数据集。

#PCA主成分分析
#encoding=utf-8
from scipy.io import loadmat
import  numpy as np
from matplotlib import pyplot as plt

2.1 Example Dataset

为了帮助您了解主成分分析的工作原理,您将首先从一个二维数据集开始,该数据集具有一个大变化方向和一个小变化方向。在这部分练习中,您将看到当您使用PCA将数据从2D减少到1D时会发生什么。实际上,您可能希望将数据从256维减少到50维;但是在这个例子中使用低维数据可以让我们更好地可视化算法。

path = 'D:编程/ex7data1.mat'
data = loadmat(path)
X = data['X']
print(X.shape)
plt.scatter(X[:,0],X[:,1],marker = 'o',edgecolors = 'b',c = '')
plt.show()

在这里插入图片描述

2.2 Implementing PCA

在这部分练习中,您将实现PCA。主成分分析包括两个计算步骤:首先,计算数据的协方差矩阵。然后,使用SVD函数计算特征向量U1,U2,Un。这些将与数据变化的主要成分相对应。
在使用PCA之前,首先从数据集中减去每个特征的平均值,然后缩放每个维度,使它们在相同的范围内,这一点很重要。已经使用featureNormalize函数执行此规范化。
在标准化数据之后,您可以运行PCA来计算主成分。首先,应计算数据的协方差矩阵,其由下式给出:
在这里插入图片描述
式中,X是以行为单位的示例数据矩阵,m是示例数。请注意Σ 是一个n× n矩阵,而不是求和运算符。
在计算协方差矩阵之后,可以对其运行SVD来计算主成分。
其中U将包含主分量,S将包含对角矩阵。
在这里插入图片描述

具体步骤如下:
(1)实现PCA首先要做的就是对数据的处理进行归一化,注意这里的方差的计算,默认ddof为0,通常情况下是使用ddof=1,就是方差计算中最后除以m还是m-1的不同。

def featureNormalize(X):
    mean = X.mean(axis = 0)
    std = X.std(axis = 0,ddof = 1)
    return (X - mean)/std,mean,std
X_norm, means, stds = featureNormalize(X)

(2)奇异值分解

def pca(X):
    sigma = (X.T @ X)/len(X)
    U, S, V = np.linalg.svd(sigma)
    return U,S,V
U, S, V = pca(X_norm)
plt.figure(figsize=(7, 5))
plt.scatter(X[:,0],X[:,1],marker = 'o',facecolors='none',edgecolors = 'b')

plt.plot([means[0], means[0] + 1.5*S[0]*U[0,0]],
         [means[1], means[1] + 1.5*S[0]*U[0,1]],
        c='r', linewidth=3, label='First Principal Component')
plt.plot([means[0], means[0] + 1.5*S[1]*U[1,0]],
         [means[1], means[1] + 1.5*S[1]*U[1,1]],
        c='g', linewidth=3, label='Second Principal Component')
plt.grid()
# changes limits of x or y axis so that equal increments of x and y have the same length
# 不然看着不垂直,不舒服。:)
plt.axis("equal")
plt.legend()
plt.show()

在这里插入图片描述

2.3 Dimensionality Reduction with PCA

在计算主成分之后,您可以使用它们来减少数据集的特征维数,方法是将每个示例投影到低维空间x(i)→ z(i)(例如,将数据从2D投影到1D)。在这部分练习中,您将使用PCA返回的特征向量,并将示例数据集投影到一维空间中。实际上,如果您使用的是线性回归或神经网络之类的学习算法,则现在可以使用投影数据而不是原始数据。通过使用投影数据,可以更快地训练模型,因为输入中的维度较少。

2.3.1 Projecting the data onto the principal components

现在应该完成projectData代码。具体地说,您将得到一个数据集X、主成分U和所需的维数,以减少到K。您应该将X中的每个示例投影到U中的前K个组件上。注意,U中的前K个分量由U的前K列给出,即U reduce=U(:,1:K)。一旦您完成projectData代码,pca将把第一个示例投影到第一个维度上,您将看到一个大约1.481的值(或者可能是-1.481,如果您得到的话是−U1而不是U1)。

def projectData(X, U, K):
    U_reduced = U[:,:K]
    Z = X @ U_reduced
    return Z


Z = projectData(X_norm,U,1)
print(Z[0])
#[1.48127391]
2.3.2 Reconstructing an approximation of the data

将数据投影到低维空间后,可以通过将数据投影回原始高维空间来大致恢复数据。您的任务是完成recoverData.m,将Z中的每个示例投影回原始空间,并返回X_rec中恢复的近似值。
一旦您完成recoverData.m中的代码,ex7 pca.m将恢复第一个示例的近似值,您将看到大约[-1.047-1.047]的值。

def recoverData(Z, U, K):
    X_rec = Z @ U[:, :K].T

    return X_rec


X_rec = recoverData(Z, U, 1)
print(X_rec[0])
#[-1.04741883 -1.04741883]
2.3.3 Visualizing the projections

在完成projectData和recoverData之后,ex7pca.m现在将执行投影和近似重建,以显示投影如何影响数据。在图中,原始数据点用蓝色圆圈表示,而投影数据点用红色圆圈表示。投影仅有效地保留U1所给方向上的信息。

plt.figure(figsize=(7, 5))
plt.scatter(X_norm[:,0],X_norm[:,1],marker = 'o',facecolors='none',edgecolors = 'b',label='Original Data Points')
plt.scatter(X_rec[:,0],X_rec[:,1],marker = 'o',edgecolors = 'r',facecolors = 'none',label='PCA Reduced Data Points')
for i in range(len(X)):
    plt.plot([X_norm[i,0],X_rec[i,0]],[X_norm[i,1],X_rec[i,1]],'k--')
plt.xlim(-4,3)
plt.ylim(-4,3)
plt.title("Example Dataset: Reduced Dimension Points Shown")
plt.xlabel('x1 [Feature Normalized]')
plt.ylabel('x2 [Feature Normalized]')
plt.legend()
plt.show()

在这里插入图片描述

2.4 Face Image Dataset

在本部分练习中,您将对人脸图像运行PCA,以了解如何在实践中使用PCA进行降维。数据集ex7faces.mat包含人脸图像的dataset X,每个图像灰度为32×32。X的每一行对应于一个面部图像(长度为1024的行向量)。

path = 'D:编程/ex7faces.mat'
data2 = loadmat(path)
X = data2['X']
print(X.shape)

def plotImages(X,row,col):
    fig,axs = plt.subplots(row, col, figsize=(8,8))
    for r in range(row):
        for c in range(col):
        # 有5000个样本,每个样本1024维,取出并将其reshape成(32,32)
            axs[r][c].imshow(X[r*row+c].reshape(32,32).T, cmap = 'Greys_r')
            axs[r][c].set_xticks([])
            axs[r][c].set_yticks([])
    plt.show()

plotImages(X,10,10)

在这里插入图片描述

2.4.1 PCA on Faces

为了在人脸数据集上运行PCA,我们首先通过从数据矩阵X中减去每个特征的平均值来规范化数据集。脚本ex7pca.m将为您执行此操作,然后运行您的pca代码。在运行PCA之后,您将获得数据集的主成分。请注意,U(每行)中的每个主分量都是长度为n的向量(其中,对于面数据集,n=1024)。事实证明,我们可以通过将每个主成分重塑为一个32× 32与原始数据集中的像素相对应的矩阵。脚本ex7pca.m显示了描述最大变化的前36个主要组件(图8)。如果需要,还可以更改代码以显示更多的主组件,以查看它们如何捕获越来越多的详细信息。

X_norm, means, stds = featureNormalize(X)
U, S, V = pca(X_norm)

print(U.shape,S.shape)
#(1024, 1024) (1024,)
plotImages(U[:,:36].T, 6, 6)

在这里插入图片描述

2.4.2 Dimensionality Reduction

现在已经计算了人脸数据集的主成分,可以使用它来减少人脸数据集的维数。这允许您使用较小输入大小(例如,100维)的学习算法,而不是原来的1024维。这有助于加快你的学习算法。

Z = projectData(X_norm,U,36)
X_rec = recoverData(Z, U, 36)

plotImages(X_rec,10,10)

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值