吴恩达|机器学习作业7.1.主成分分析(PCA)

7.1.主成分分析对人脸图像降维

1)题目:

在这个练习中,你将运用主成分分析(PCA)来实现降维。首先,你将用一个二维的样本集来实验,从而对PCA如何运行的有一个直观的感受,然后再在一个更大的由5000个人脸图像组成的数据集上实现PCA。
数据集链接: https://pan.baidu.com/s/1cEgQIvehUcLxZ0WVhxcPuQ 提取码: xejn

2)知识点概括:

  • 降维(dimensionality reduction):
    压缩数据(减少内存和硬盘的需求/加速算法)
    可视化数据

  • 主成分分析(principal components analysis,简称PCA)
    先将数据进行均值归一化(mean normalization),然后找到一个低维平面,将数据投影到上面,使得投影误差(projection error)最小(即找到一组基向量,然后将数据投影到这个向量空间里,使得投影误差最小)

  • 具体步骤(将n维数据降到k维):
    1、数据预处理(均值归一化 / 特征缩放feature scaling)
    2、计算协方差矩阵 Σ ( n × n ) = 1 m i = 1 n ( x ( i ) ) ( x ( i ) ) T = 1 m X T X \Sigma_{(n\times n)}={1\over m}\sum_{i=1}^n(x^{(i)})(x^{(i)})^T={1\over m}X^TX 以及它的特征向量
    可以用SVD奇异值分解(Singular Value Decomposition)或eig求特征向量
    3、取U矩阵(特征向量矩阵)中前k个向量得到一个n乘k维的矩阵 U r e d u c e U_{reduce} ,转置后乘以数据x就可得到降维后的数据 z = X U r e d u c e z=XU_{reduce}

  • k(主成分的数量)的选择
    投影误差的平方和 1 m i = 1 m x ( i ) x a p p r o x ( i ) 2 {1\over m}\sum_{i=1}^m\|x^{(i)}-x_{approx}^{(i)}\|^2 ,PCA的目标就是最小化x和其在低维表面上的映射点之间的距离的平方。
    数据的总方差 1 m i = 1 m x ( i ) 2 {1\over m}\sum_{i=1}^m\|x^{(i)}\|^2 ,训练样本长度的平均值
    一般来说,就是选择k使得下式小于0.01,即 1 m i = 1 m x ( i ) x a p p r o x ( i ) 2 1 m i = 1 m x ( i ) 2 0.01 {{1\over m}\sum_{i=1}^m\|x^{(i)}-x_{approx}^{(i)}\|^2\over {1\over m}\sum_{i=1}^m\|x^{(i)}\|^2}\le0.01 ,用PCA的语言说就是,保留了99%的差异性。当采取SVD分解时,上式可以等价于 1 i = 1 k S i i i = 1 n S i i 0.01 1-{\sum_{i=1}^kS_{ii}\over \sum_{i=1}^nS_{ii}}\le0.01 ,其中 S i i S_{ii} 为S矩阵中的主对角元素(S为一个对角阵)。然后尝试k从1到n,选择第一个满足上式的k即可。
    i = 1 k S i i i = 1 n S i i {\sum_{i=1}^kS_{ii}\over \sum_{i=1}^nS_{ii}} 也是一个平方投影误差的测量指标,即使是手动选择k时,也可以阐述百分之多少的差异性被保留了下来。

  • 原始数据的重构(reconstruction)
    利用 z = X U r e d u c e z=XU_{reduce} 得到原始数据的一个近似值

  • 在监督学习中可以用PCA来降低高维数据的维数,得到较低维的训练数据,再使用降维后的数据进行学习,使得算法更快的实现,例如在神经网络中。
    在这里插入图片描述

  • 警告:
    PCA去防止过拟合是一个特别不好的应用,可能会丢失一些有价值的信息!
    此外除非在强有力的证据下说明原始数据直接建模不好(内存太大或者运行太慢),才使用PCA进行降维处理之后再进行建模,一般强烈建议直接用原始数据进行建模!

3)大致步骤:

  • 导入数据以及数据可视化。

  • 主成分分析。首先进行数据标准化,注意这里的sigma如果取无偏的标准差,则自由度为n-1;然后计算协方差矩阵,再进行SVD奇异值分解得到特征向量和特征值;然后对比两个不同的特征向量(即第1列和第2列)哪个的投影误差更小,画出以数据均值为中心的特征向量,可以看出取U矩阵中前1列得到矩阵(这里为[-0.70710678, -0.70710678]),转置后乘以数据x就可得到降维后的数据 z = X U r e d u c e z=XU_{reduce}

  • 将数据投影到主成分上,可以得到z的第一个值为1.48127391。然后再重建数据得到数据的近似值,因为U是特征向量矩阵,它由标准正交基组成,因此U.T@U为单位矩阵,所以这里要重建数据只需 x r e c = z U r e d u c e x_{rec}=zU_{reduce}' ,可以得到重建后的第一行数据为[-1.04741883, -1.04741883]。然后再可视化投影,画出标准化的数据以及投影之后又重建的数据,把对应点用虚线连接起来。然后看看降维后的数据保留了原始数据多少的差异性,这里只有不到87%

    扫描二维码关注公众号,回复: 8692924 查看本文章
  • 导入人脸数据以及数据可视化。x是一个(5000, 1024)的数据,每列对应一个人脸图像,需要reshape成32*32的再画出。选出前100个人脸图像进行可视化。

  • 在人脸数据上实施主成分分析。先将人脸数据标准化,再计算特征向量,然后可视化前36个特征向量,注意这里每个特征向量都是1024维的。

  • 人脸数据降维以及可视化。和前面一样对数据进行降维,这里设置k=100,再进行重建数据,画出重建后的前100个人脸图,和降维前的对比。这里93.19%的差异性保持了。

  • 最后是对上次作业的一个提升,先重复上次作业中对图片进行的处理,即先运行k-means算法,得到聚类中心和索引,然后再随机选取1000个样本像素画出三维图。之后再用PCA进行降维,画出降维后的二维图。99.34%的差异性保持了。

4)关于Python:

  • plt.plot([1,2], [1,2])可以画出点[1,1]到点[2,2]的连线。
  • np.random.randint( )函数可以随机选择整数,没有np.的话是一个闭区间中选取,这里是一个开区间。
  • plt.cm.get_cmap(‘Accent’) plt.cm是matplotlib库中内置的色彩映射函数,Accent是get_cmap函数可选的一个颜色参数。
  • import mpl_toolkits.mplot3d as Axes3D模块可以用来绘制三维图。

5)代码与结果:

import numpy as np
import matplotlib.pyplot as plt
import scipy.io as scio
import matplotlib.image as mpimg #读入图片
import mpl_toolkits.mplot3d as Axes3D #用来画三维图

'''============================part1 数据导入及可视化========================='''

data = scio.loadmat('ex7data1.mat')
x = data['X']
plt.scatter(x[:,0], x[:,1], marker='o', facecolors='none', edgecolors='b')

'''============================part2 PCA===================================='''
'''数据标准化'''
def featureNormalize(x):
    mu = x.mean(axis=0) #求每列的
    sigma = x.std(axis=0, ddof=1) #无偏的标准差,自由度为n-1
    x_norm = (x-mu)/sigma
    return x_norm, mu, sigma

x_norm, mu, sigma = featureNormalize(x)

'''得到特征向量和特征值'''
def pca(x):
    m, n = x.shape
    Sigma = (x.T @ x)/m #协方差矩阵
    U, S, V = np.linalg.svd(Sigma) #SVD奇异值分解
    return U, S
    
U, S = pca(x_norm)
    
#画出特征向量
plt.figure(figsize=(6,6))
plt.scatter(x[:,0], x[:,1], marker='o', facecolors='none', edgecolors='b')
plt.plot([mu[0], mu[0]+1.5*S[0]*U[0,0]], [mu[1], mu[1]+1.5*S[0]*U[1,0]], 'k') #两个点的连线
plt.plot([mu[0], mu[0]+1.5*S[1]*U[0,1]], [mu[1], mu[1]+1.5*S[1]*U[1,1]], 'k') #这里的1.5和S表示特征向量的长度
plt.title('Computed eigenvectors of the dataset')


'''============================part3 降维========================='''
'''降维,数据投影到特征向量上'''
def projectData(x, U, K):
    U_reduce = U[:, 0:K]
    return x @ U_reduce
    
#降维后的数据,(50, 1)
z = projectData(x_norm, U, 1)
z[0] #1.48127391

'''重建数据'''
def recoverData(z, U, K):
    U_reduce = U[:, 0:K]
    return z @ U_reduce.T #因为U是特征向量矩阵,由标准正交基组成,U.T@U为单位矩阵

#重建后的数据
x_rec = recoverData(z, U, 1)
x_rec[0,:] #[-1.04741883, -1.04741883]

#可视化投影
plt.figure(figsize=(6,6))
plt.xlim((-4, 3))
plt.ylim((-4, 3))
plt.scatter(x_norm[:,0], x_norm[:,1], marker='o', facecolors='none', edgecolors='b')
plt.scatter(x_rec[:,0], x_rec[:,1], marker='o', facecolors='none', edgecolors='r')
for i in range(len(x_norm)):
    plt.plot([x_norm[i,0], x_rec[i,0]], [x_norm[i,1], x_rec[i,1]], 'k--')
plt.title('The normalized and projected data after PCA')

'''保留了多少差异性'''
def retained_variance(S, K):
    rv = np.sum(S[:K])/np.sum(S)
    return print('{:.2f}%'.format(rv*100))

#看看降维后保留了多少差异性
retained_variance(S, 1) #86.78%

'''============================part4 人脸数据导入及可视化========================='''
data = scio.loadmat('ex7faces.mat')
x = data['X']

'''人脸可视化'''
def displayData(x):
    plt.figure()
    n = np.round(np.sqrt(x.shape[0])).astype(int)
    #定义n*n的子画布
    fig, a = plt.subplots(nrows=n, ncols=n, sharex=True, sharey=True, figsize=(6, 6))
    #在每个子画布中画出一个图像
    for row in range(n):
        for column in range(n):
            a[row, column].imshow(x[n * row + column].reshape(32,32).T, cmap='gray')
    plt.xticks([]) #去掉坐标轴
    plt.yticks([])       

#可视化前100个人脸
displayData(x[0:100, :])

'''============================part5 在人脸数据上实施PCA========================='''
#数据标准化
x_norm, mu, sigma = featureNormalize(x)
#特征向量
U, S = pca(x_norm)
#可视化前36个特征向量
displayData(U[:, 0:36].T)

'''============================part6 人脸数据降维以及可视化========================='''
#将原始数据降至100维
K = 100
z = projectData(x_norm, U, K)
z.shape #(5000, 100)
#重建后的数据
x_rec = recoverData(z, U, K) #(5000, 1024)

#画图对比
displayData(x[0:100, :])
displayData(x_rec[0:100, :])

#看看降维后保留了多少差异性
retained_variance(S, K) #93.19%


'''============================part7 PCA用在数据可视化========================='''
#读取图片
A = mpimg.imread('bird_small.png') #读取图片

'''上次作业中k-means的代码'''
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

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

def kMeansInitCentroids(x, K):
    randidx = np.random.permutation(x) #随机排列
    centroids = randidx[:K, :] #选前K个
    return centroids

#运行k-means
def runKmeans(x, centroids, max_iters):
    for i in range(max_iters):
        idx = findClosestCentroids(x, centroids) #簇分配
        centroids = computeCentroids(x, idx, len(centroids)) #移动聚类中心
    return centroids, idx

#k-means部分
X = A.reshape(A.shape[0] * A.shape[1], 3)
K = 16 #聚类数量
max_iters = 10 #最大迭代次数
initial_centroids = kMeansInitCentroids(X, K) #初始化聚类中心
centroids, idx = runKmeans(X, initial_centroids, max_iters) #得到聚类中心和索引
sel = np.random.randint(X.shape[0], size=1000) #随机选择1000个样本
cm = plt.cm.get_cmap('Accent') #设置颜色

#画三维图
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection='3d') #“111”表示“1×1网格,第一子图”
ax.scatter(X[sel, 0], X[sel, 1], X[sel, 2], c=idx[sel], cmap=cm, s=6)
plt.title('Pixel dataset plotted in 3D. Color shows centroid memberships')
 
#PCA部分
X_norm, mu, sigma = featureNormalize(X) #标准化
U, S = pca(X_norm) #特征向量
Z = projectData(X_norm, U, 2) #投影降维

#画二维图
plt.figure(figsize=(8,6))
plt.scatter(Z[sel, 0], Z[sel, 1], c=idx[sel], cmap=cm, s=7)
plt.title('Pixel dataset plotted in 2D, using PCA for dimensionality reduction')

#看看降维后保留了多少差异性
retained_variance(S, 2) #99.34%

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

两个特征向量(相互正交的)
在这里插入图片描述

可视化投影
在这里插入图片描述

前100个人脸可视化
在这里插入图片描述

可视化前36个特征向量
在这里插入图片描述

重建之后的图像(左)与原始的图像(右)
在这里插入图片描述在这里插入图片描述
和之前的对比,可以发现降维后能大致保持人脸的轮廓,但是丢失掉一些面部细节的信息,但是数据从(5000, 1024)降到了(5000, 100),如果用在神经网络等算法中很明显会加快运行的速度。

三维图效果

在这里插入图片描述

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

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

猜你喜欢

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