zoukankan      html  css  js  c++  java
  • 机器学习lab4


    一.实验目标

    实现一个PCA模型,能够对给定数据进行降维(即找到其中的主成分)

    二.实验要求和实验环境

    实验要求

    测试:

    (1)首先人工生成一些数据(如三维数据),让它们主要分布在低维空间中,如首先让某个维度的方差远小于其它唯独,然后对这些数据旋转。生成这些数据后,用你的PCA方法进行主成分提取。

    (2)找一个人脸数据(小点样本量),用你实现PCA方法对该数据降维,找出一些主成分,然后用这些主成分对每一副人脸图像进行重建,比较一些它们与原图像有多大差别(用信噪比衡量)。

    实验环境
    • Microsoft win10 1809

    • python 3.7.0

    • Sublime Text 3

    三.设计思想

    1.算法原理
    1.1 坐标变换
    1. 对于数据集​,其中​。先对每个样本进行中心化, 即:

    其中​称为样本集的​的中心向量。之所以进行中心化,是因为经过中心化之后的常规的线性变换就是绕原点的旋转变化,也就是坐标变换;

    1. 以及​就是样本集的协方差矩阵。经过中心化后的数据,有​。设使用的投影坐标系的标准正交向量基:​,每个样本降维后得到的坐标为:

    2. 因此,样本集与降维后的样本集表示为:

    因此降维的物理意义为:通过线性组合原始特征,从而去掉一些冗余的或者不重要的特征、保留重要的特征。

    1.2 重构后的误差

    在得到​后,需要对其进行重构,重构后的样本设为

    将式​代入,那么对于整个数据集上的所有样本与重构后的样本之间的误差为:

    根据定义,可以有:

    由于​是标量,有​,从而式​变为:

    此外,根据​的定义有:

    结合式​可以化简优化目标:

    从而优化目标为​,约束为​

    对于原始数据样本点​在降维后在新空间的超平面上的投影为​。若使样本点的投影尽可能分开,应该使样本点在投影后的方差最大化,即使下式最大化:

    可以看到式​与​等价。PCA的优化问题就是要求解​的特征值。

    只需将​进行特征值分解,将得到的特征值进行排序:​,提取前​大的特征值对应的单位特征向量即可构成变化矩阵​。

    2.算法的实现

    给定样本集​和低维空间的维数​

    1. 对所有的样本进行中心化操作:

      1. 计算样本均值​

      2. 所有样本减去均值​

    2. 计算样本的协方差矩阵​

    3. 对协方差矩阵​进行特征值分解

    4. 取最大的​个特征值对应的单位特征向量​,构造投影矩阵​

    5. 输出投影矩阵​与样本均值​

    四、实验结果与分析

    3D数据测试

    产生数据: 3维高斯分布随机产生100个样本,参数为:

    降维后,还原的点如下图所示:

    1572951083071

    其中绿色的点是源数据点, 红色的点是降维后有还原到原空间的点.

    1572951102303

    从上图可以看到数据都在一个平面上, 说明我们的却对数据降了一个维度.

    1572951136096

    可以看到降维后的数据与元数据在二维视图中基本重合, 说明我们的降维是成功的.

    人脸数据
    数据源: BioID人脸数据库
    说明

    该数据集由1521张灰度图像组成,分辨率为384x286像素。每个人都展示了23个不同测试人员中一个人的面部正视图。出于比较原因,该组还包含手动设置的眼位。这些图像被标记为“ BioID_xxxx.pgm”,其中字符xxxx被当前图像的索引替换(前导零)。与此类似,文件“ BioID_xxxx.eye”包含相应图像的眼睛位置。

    图像文件格式

    使用便携式灰度图(pgm)数据格式将图像存储在单个文件中。pgm文件包含一个数据头,后跟图像数据。在我们的例子中,标题由四行文本组成。

    • 第一行描述了图像数据的格式(ASCII /二进制)。在我们的文件中,文本“ P5”表示数据以二进制形式写入

    • 第二行包含以文本形式编写的图像宽度

    • 第三行还将图像高度保持为文本形式

    • 第四行包含允许的最大灰度值(在我们的图像中为255)

    标题后跟一个包含图像数据的数据块。数据从上到下每行使用每个像素一个字节存储。

    降维对比:

    说明:1,3列为原图, 2,4列为降维后还原的图片:

    1572964390303

    可以看出, 当维度降为150度时, 还原后的图片基本上只能看到大概轮廓.

    1573010712809

    相比于150维, 300略为清晰.

    1573026794784

    500维时,较清晰.

    1573029270085

    1000维时, 基本和原图差异不大.

    计算信噪比

    计算公式如下:

    取不同维度测试信噪比,令降维后的维度为以下:

    计算每个维度取值得出的信噪比, 画出信噪比变化曲线,如下:

    1573037168252

    可以看到, 维度在0 - 1000之间时, psnr随着维度的增加迅速增加, 这是因为随着维度的增加, 降维后的数据越来越接近源数据.

    当维度超过1500时, psnr的基本不发生变化, 这是因为1500维已经刻画了绝大多数的信息, 再多的维度已经不能提供更多更明显的信息了.

    五、结论

    • 数据降维后会保留主要特征, 即主要成分, 不重要的特征将被忽略

    • 数据降维后的却会丢失一部分信息, 降维降得越多, 丢失的信息越多

    • 从psnr的曲线中可以看出, 15000维左右的数据就几乎可以刻画原图

    六、参考文献

    1. Pattern Recognition and Machine Learning.

    2. 统计学习方法

    3. 人脸数据

    七、附录:源代码(带注释)

     # author: lgq
    # time 2019/11/3
    # 机器学习, 降维, lab4
    import numpy as np
    import os
    from matplotlib import pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    from os import walk, path
    import mahotas as mh


    hight ,width = 286,  384  # 图片尺寸
    k = 4   # 压缩比
    m,n = hight//k, width//k   # 压缩之后的图片


    def generate_data(data_size):
         mean = [1, 1, 1]
         cov = [[0.01, 0, 0], [0, 1, 0], [0, 0, 1]]
         data = np.random.multivariate_normal(mean, cov,data_size)
         return data


    def draw_data(origin_data, pca_data_inverse):
         fig = plt.figure()
         ax = fig.gca(projection='3d')
         ax.scatter(origin_data[:, 0], origin_data[:, 1], origin_data[:, 2],
                    facecolor="none", edgecolor="g", label='Origin')
         ax.scatter(pca_data_inverse[:, 0], pca_data_inverse[:, 1], pca_data_inverse[:, 2], facecolor='r', label='PCA')
         plt.legend()
         plt.show()


    def pca(data, reduced_dimension):
         rows, columns = data.shape
         assert reduced_dimension <= columns
         x_mean = 1.0 / rows * np.sum(data,  axis=0)
         decentralise_x = data - x_mean  # 去中心化
         cov = decentralise_x.T.dot(decentralise_x)  # 计算协方差
         eigenvalues, feature_vectors = np.linalg.eig(cov)  # 特征值分解
         min_d = np.argsort(eigenvalues)
         feature_vectors = np.delete(feature_vectors, min_d[:columns - reduced_dimension], axis=1)
         return feature_vectors, x_mean 

    def psnr(source, target):
         """ 计算信噪比 """
         diff = source - target
         diff = diff ** 2
         rmse = np.sqrt(np.mean(diff))
         return 20 * np.log10(255.0 / rmse)




    def test_3D_data():
         data_size = 100
         data = generate_data(data_size)
         w, mu_data = pca(data,2)
         pca_data = (data - mu_data).dot(w)
         pca_data_inverse = pca_data.dot(w.T) + mu_data
         draw_data(data, pca_data_inverse)


    # 载入脸部数据
    def load_face_data(filepath):
         x = []
         for dir_path, dir_names, file_names in walk(filepath):
            #walk() 函数内存放的是数据的绝对路径,同时注意斜杠的方向。
            for fn in file_names:
                 if fn[-3:] == 'pgm':
                     image_filename = path.join(dir_path, fn)
                     x.append(mh.imread(image_filename, as_grey=True).reshape(109824))
         return np.array(x)


    # 将face矩阵数据压缩并存储在txt中
    def compress_data(x_train,only_part = False):
         data_size = len(x_train)
         k = 4
         c = np.zeros((data_size,m,n))
         for pgm in range(data_size):
             if not pgm %100:
                 print('compress:',100*pgm/data_size,'%') # 输出压缩进度
             img =x_train[pgm].reshape(hight, width)
             for i in range(m):
                 for j in range(n):
                     c[pgm,i,j] = (np.sum(img[k*i:k*i+1,k*j:k*j+1]))/(k*k)
         if only_part:
             np.savetxt(str(k)+'_compress_face_data_part.txt',c[0:200].reshape(200,m*n).astype('int32'))
         else:
             np.savetxt(str(k)+'_compress_face_data.txt',c.reshape(data_size,m*n).astype('int32'))




    def draw_face_data(data,pca_data_inverse,d):
         fig, ax = plt.subplots(nrows=5, ncols=4, sharex='all', sharey='all')
         ax = ax.flatten()
         for i in range(10):
             if i >100:
                 break
             img = data[i*10].reshape(m, n)
             ax[2 * i].imshow(img, cmap='Greys')
             img_compared = pca_data_inverse[i*10].reshape(m, n)
             ax[2 * i + 1].imshow(img_compared, cmap='Greys', interpolation='nearest')
         ax[0].set_xticks([])
         ax[0].set_yticks([])
         plt.tight_layout()
         plt.title('Dimension = ' + str(d))
         plt.show()

    def test_face_data():
         # data = np.loadtxt('4_compress_face_data.txt')
         data = np.loadtxt('4_compress_face_data_part.txt')
         data = data.reshape((len(data),m*n))

         print('load finish; data.shape:',data.shape,type(data[1,1]))
         data = data.astype('float32') # 求过程中会变为小数 

         d = 300 # 压缩后的维度
         w, mu = pca(data, d)
         print('pca finish')

         pca_data_inverse = (data - mu).dot(w).dot(w.T) + mu
         pca_data_inverse = pca_data_inverse.astype(np.int)
       
         draw_face_data(data,pca_data_inverse,d)

    def test_psnr():
         d_list = [50,100,150,200,300,500,1000,2000,3000,4500,6000]
         psnr_list = []
         data = np.loadtxt('4_compress_face_data.txt')
         data = data.astype('float32')
         print('load finish; data.shape:',data.shape,type(data[1,1]))

         rows, columns = data.shape
         x_mean = 1.0 / rows * np.sum(data,  axis=0)
         decentralise_x = data - x_mean  # 去中心化
         cov = decentralise_x.T.dot(decentralise_x)  # 计算协方差
         eigenvalues, feature_vectors = np.linalg.eig(cov)  # 特征值分解
         # np.savetxt('eigenvalues.txt',eigenvalues)
         # np.savetxt('feature_vectors.txt',feature_vectors)
         min_d = np.argsort(eigenvalues)
         print('...')
         for d in d_list:
             assert d <= columns
             feature_vectors_chooose = np.delete(feature_vectors, min_d[:columns - d], axis=1)
             w, mu = feature_vectors_chooose, x_mean
             pca_data_inverse = (data - mu).dot(w).dot(w.T) + mu
             pca_data_inverse = pca_data_inverse.astype(np.int)
            
             psnr_list.append(psnr(data[10],pca_data_inverse[10]))
             print('...')
         plt.plot(d_list,psnr_list)
         plt.xlabel('Dimension')
         plt.ylabel('psnr')
         plt.show()

    def main():
         # 测试随机生成的三维数据
         test_3D_data()

         # 测试脸部数据
         # test_face_data()

         # 测试信噪比
         # test_psnr()
        



    if __name__ == '__main__':
         main()
  • 相关阅读:
    flask强大的第三方组件之falsk-sqlalchemy
    flask 外键关系和多对多查询
    flask sqlalchemy 单表查询
    functools模块
    面试题
    ansible git
    python 操作 表格
    matplotlib
    node,npm,webpack,vue-cli模块化编程安装流程
    Webpack
  • 原文地址:https://www.cnblogs.com/lee3258/p/11994273.html
Copyright © 2011-2022 走看看