zoukankan      html  css  js  c++  java
  • 数据降维算法——主成分分析

    数据降维

    数据降维在机器学习中非常有用,可以用来舍弃数据中一些区分度较小的特征,转化数据的观察视角,使其在更少量的特征维度上也有较好的表现。数据降维也可以用在将高维数据可视化的操作中,这都是不可或缺的重要算法,

    PCA

    PCA(Principal Components Analysis)主成分分析法,是一种常用的数据降维算法。

    PCA的主要思路,是选取数据特征中一些较低维度的空间,让数据在这些空间上的方差比较大,这个时候我们可以认为数据是比较分散的,我们在这个低维度空间依然还能表现出数据的离散性。

    数学

    在正式介绍PCA的步骤之前,有一些重要的数学知识能够极大的帮助我们计算。如果你读完了本节还是不清楚,下面两篇文章或许可以帮到你。

    参考一

    参考二,数学原理

    方差表示

    假设矩阵的每一行表示一组数据,每一列表示一维特征,即(A_{n imes m})

    协方差矩阵可以表示为:

    [Cov = frac{1}{n} A^T A ag{1} ]

    其中(n)是数据组数。

    反之,若用行表示每组特征,则

    [Cov = frac{1}{n} A A^T ]

    显然,协方差矩阵(Cov)是实对称矩阵。

    特征值与特征向量

    对于矩阵(A),如果存在(lambda)和向量(v)满足

    [A v = lambda v ]

    那么称(lambda)(A)的特征值,(v)(A)的特征向量。

    特征值特征向量的求解

    [A v = lambda v \ (A - lambda) v = 0\ det (A - lambda I) = 0 ag{2} ]

    求解(2)式就可以算出所有的特征值与特征向量。

    特征值与特征向量的性质

    将矩阵按照特征值可以分解为

    [A = Q Sigma Q ^{-1} ag{3} ]

    其中(Q)是特征向量按列排列成的矩阵,(Sigma)是特征值构成的对角阵。

    证明

    [Q Sigma Q ^{-1} = egin{pmatrix} v_1 & v_2 & .. & v_n end{pmatrix} Sigma Q^{-1}\ = egin{pmatrix} v_1 & v_2 & .. v_n end{pmatrix} egin{pmatrix} lambda_1 & & & &\ & lambda_2\ & & ..\ & & & lambda_n end{pmatrix} Q^{-1}\ = AQQ^{-1}\ = A ]

    由于不同的特征值对应的两个特征向量(任意两个不同的特征向量)是正交的,所以对特征向量做归一化后我们得到

    [Q Q^T = I \ Q^T = Q^{-1} ]

    所以也可以像下面这样分解

    [A = Q Sigma Q^{T} ag{4} ]

    矩阵对角化

    将上面(3)式变形

    [Q^{-1} A Q = Q^T A Q = Sigma ag{5} ]

    初等变换

    从矩阵的层面上讲,左乘一个矩阵相当于作初等行变换,右乘矩阵相当于初等列变换。如果把矩阵乘法理解为对矩阵的行和列作一些操作,下面理解PCA对数据特征的操作或许会更方便一些。

    下面所讲的都是按照行为一组数据,每一列是一个特征。

    PCA步骤

    下面详细的阐述主成分分析算法的每一个步骤,涉及到许多线性代数的知识,需要对上面的内容有一定了解。

    去均值

    首先拿到数据之后统计平均值(mu),然后每一列特征都减去均值,使得数据以(0)为中心。

    协方差矩阵

    利用式(1),计算协方差矩阵,我们这时候得到的协方差矩阵是一个实对称矩阵,对角线上分布的是(m)个特征的方差,其他位置是协方差。我们希望得到的是尽量少的、正交的、能表现全面的特征平面。那么这个矩阵应该长下面这个样子。

    [egin{pmatrix} x_1 & & &\ & x_2\ & & ..\ & & & x_n end{pmatrix} ]

    这意味着,这些特征的协方差都是零,即他们两两正交;每个特征自己有一个方差,我们选择前(k)大的方差,就得到了我们要形成怎样的特征,从而完成降维。下面我们向上面这个矩阵前进,也就是矩阵对角化

    对角化

    在数学上,矩阵对角化已经不是什么难事了。

    想象一下PCA最后剩下的那些特征,可以理解为现有的那些特征做了一些线性变换得到的,在这些维度中方差表现得更大,对特征做线性变换对应于基础操作中的列变换,于是我们可以为数据矩阵(A)右乘一个矩阵(P)之后得到我们要的正交的特征。

    变换之后协方差可以写成

    [Cov(D) = (AP)^T(AP)\= P^T (A^T A) P ag{6} ]

    现在有没有觉得(5)和(6)有些神似了呢?

    数学家告诉我们,特征向量矩阵有很好的性质,刚好可以满足我们对(P)的要求,所以我们计算出(A^T A)的特征向量和特征值就可以实现对角化了。

    排序

    我们选择特征值的时候要选取前(k)大,因为方差越大,我们能保留下来的信息越多,选取的越是主要成分。

    对这几个特征值排序然后选出前(k)大,这个时候由对应的矩阵(P)我们就可以知道选择的那些维度是怎么线性变换来的了。

    应用

    我用fastNLP中的预训练glove embedding构建除了几个单词的词嵌入向量,是50维的,为了比较我们的 embedding 是否成功,nlp中经常用可视化来估计一下。

    我在下面的代码中使用sklearn.decomposition.PCA分别尝试了将50维降到2维和3维。一起来看一下效果吧。

    2-dimension

    from sklearn.decomposition import PCA
    import matplotlib.pyplot as plt
    import numpy as np
    from fastNLP.embeddings import StaticEmbedding
    from fastNLP import Vocabulary
    import torch
    
    
    word_list = ['he', 'she', 'right', 'black', 'work', 'evening', 'his', 'do', 'left', 'white', 'green', 'job', 'it', 'her', 'morning', 'afternoon']
    
    vocab = Vocabulary()
    vocab.add_word_lst(
        # msg.split(' ')
        word_list
    )
    
    embeded = StaticEmbedding(vocab=vocab, model_dir_or_name="en-glove-6b-50d")
    words_idx = torch.LongTensor(list(vocab.idx2word.keys()))
    word_embedding = embeded(words_idx)
    word_embedding = word_embedding.detach().numpy()
    
    
    pca = PCA(n_components=3)
    pca.fit(word_embedding)
    data = pca.transform(word_embedding)
    
    plt.scatter(data[:, 0], data[:, 1], alpha=0.6)
    
    for i in range(len(data)):
        plt.text(x=data[i][0], y=data[i][1], s=vocab.to_word(words_idx[i].item()))
        # plt.text()
    
    plt.show()
    

    3-dimension

    from sklearn.decomposition import PCA
    import matplotlib.pyplot as plt
    import numpy as np
    from fastNLP.embeddings import StaticEmbedding
    from fastNLP import Vocabulary
    import torch
    
    word_list = ['he', 'she', 'right', 'black', 'work', 'evening', 'his', 'do', 'left', 'white', 'green', 'job', 'it', 'her', 'morning', 'afternoon']
    
    vocab = Vocabulary()
    vocab.add_word_lst(
        # msg.split(' ')
        word_list
    )
    
    embeded = StaticEmbedding(vocab=vocab, model_dir_or_name="en-glove-6b-50d")
    words_idx = torch.LongTensor(list(vocab.idx2word.keys()))
    word_embedding = embeded(words_idx)
    word_embedding = word_embedding.detach().numpy()
    
    
    pca = PCA(n_components=3)
    pca.fit(word_embedding)
    data = pca.transform(word_embedding)
    from mpl_toolkits.mplot3d import Axes3D
    
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.scatter(data[:, 0], data[:, 1], data[:, 2], alpha=0.6)
    for i, datai in enumerate(data):
        ax.text3D(x=datai[0], y=datai[1], z=datai[2]
            , s=vocab.to_word(words_idx[i].item()), fontsize=5, horizontalalignment="center")
    
    plt.show()
    

    可以看到,相似的词汇确实还能在降维后的图中表现出关联。

    一个人没有梦想,和咸鱼有什么区别!
  • 相关阅读:
    java 抽象类
    ClassNotFoundException: dao.impl.ActionImpl
    侦听状态一直为T的处理
    Duplicate entry '1' for key 'PRIMARY'(报错)
    ibatis学习笔记
    java中的堆、栈和常量池
    servlet学习
    三大排序
    第一次面试??交流
    毕业季,学长,学姐们的践行
  • 原文地址:https://www.cnblogs.com/TABball/p/12727342.html
Copyright © 2011-2022 走看看