03.PCA主成分分析

参考:
讲一下numpy的矩阵特征值分解与奇异值分解
主成分分析(PCA)原理详解
源代码: https://github.com/wucng/MLAndDL


1.特征值分解

(1) 特征值与特征向量

如果一个向量 v v v是矩阵 A A A的特征向量,将一定可以表示成下面的形式:

A v = λ v Av=\lambda v Av=λv

其中, λ λ λ是特征向量 v v v对应的特征值,一个矩阵的一组特征向量是一组正交向量。

(2) 特征值分解矩阵

从上面我们知道了特征矩阵与特征向量的定义,即满足: A v = λ v Av=\lambda v Av=λv

如果存在矩阵 A ∈ R n x n A\in R^{n\mathrm{x}n} ARnxn 有n个特征值分别为 λ 1 , λ 2 , . . . λ n \lambda_1,\lambda_2,...\lambda_n λ1,λ2,...λn,对应的特征向量分别为: v 1 , v 2 , . . . v n v_1,v_2,...v_n v1,v2,...vn

把所有的特征向量写成矩阵形式: V ∈ R n x n V \in R^{n \mathrm{x} n} VRnxn,特征值写成对角阵 D ∈ R n x n D \in R^{n \mathrm{x} n} DRnxn,则有: A V = V D AV=VD AV=VD

如果特征矩阵 V V V 是可逆的 则有 A = V D V − 1 = V D V T A=VDV^{-1}=VDV^T A=VDV1=VDVT ,因为特征矩阵其实是正交矩阵 即: V V T = E VV^T=E VVT=E,因此 V − 1 = V T V^{-1}=V^T V1=VT

如果矩阵 A A A是正定的(可逆) 则有 A = V D V T A=VDV^T A=VDVT

import numpy as np
from numpy.linalg import eig,svd

A = np.random.random_sample([8,8])
C = np.dot(A.T, A)
# 特征值分解
vals, vecs = eig(C)

# 重构
Lambda = np.diag(vals) # 特征值对角阵
new_C = np.dot(np.dot(vecs, Lambda), vecs.T) # 与C=A.T*A相等

print(np.allclose(new_C,C)) # True

2.SVD(奇异值)分解

如果矩阵 A A A可逆,则可以进行特征值分解;如果矩阵 A A A不可逆,则可以是SVD分解(并且 A A A可逆也能使用SVD分解)

奇异值分解是一个能适用于任意矩阵的一种分解的方法,对于任意矩阵A总是存在一个奇异值分解:

A = U Σ V T A = U\Sigma V^T A=UΣVT

假设 A A A是一个 m ∗ n m*n mn的矩阵,那么得到的 U U U是一个 m ∗ m m*m mm的方阵, U U U里面的正交向量被称为左奇异向量。 Σ Σ Σ是一个 m ∗ n m*n mn的矩阵, Σ Σ Σ除了对角线其它元素都为0,对角线上的元素称为奇异值。是 V V V的转置矩阵,是一个 n ∗ n n*n nn的矩阵,它里面的正交向量被称为右奇异值向量。而且一般来讲,我们会将 Σ Σ Σ上的值按从大到小的顺序排列。

SVD分解矩阵A的步骤:

  • (1) 求 A A T AA^T AAT的特征值和特征向量,用单位化的特征向量构成 U U U
  • A T A A^TA ATA的特征值和特征向量,用单位化的特征向量构成 V V V
  • A A T AA^T AAT或者 A T A A^TA ATA的特征值求平方根,然后构成 Σ Σ Σ
import numpy as np
from numpy.linalg import eig,svd

A = np.random.random_sample([8,6])
u, s, vh = np.linalg.svd(A)    # 这里vh为V的转置
print(u.shape,s.shape,vh.shape)
print(np.allclose(A, np.dot(u[:, :len(s)] * s, vh))) # True

3.PCA算法

  • (1) 基于特征值分解协方差矩阵实现PCA算法

输入:数据集 X = { x 1 , x 2 , . . . , x n } , X ∈ R n ∗ m X=\{x_1,x_2,...,x_n\},X \in R^ {n*m} X={x1,x2,...,xn}XRnm,需要降到 k k k X ′ ∈ R n ∗ k , k < m X' \in R^ {n*k} , k<m XRnk,k<m

  1. 去平均值(即去中心化),即每一位特征减去各自的平均值。

  2. 计算协方差矩阵 X T X X^TX XTX,注:这里除或不除样本数量 n n n n − 1 n-1 n1,其实对求出的特征向量没有影响。

  3. 用特征值分解方法求协方差矩阵 X T X X^TX XTX的特征值与特征向量。

  4. 对特征值从大到小排序,选择其中最大的 k k k个。然后将其对应的 k k k个特征向量(对应的列向量)分别作为列向量组成特征向量矩阵 P P P

  5. 将数据转换到 k k k个特征向量构建的新空间中,即 Y = X P Y=XP Y=XP


  • (2) 基于SVD分解协方差矩阵实现PCA算法

输入:数据集 X = { x 1 , x 2 , . . . , x n } , X ∈ R n ∗ m X=\{x_1,x_2,...,x_n\},X \in R^ {n*m} X={x1,x2,...,xn}XRnm,需要降到 k k k X ′ ∈ R n ∗ k , k < m X' \in R^ {n*k} , k<m XRnk,k<m

  1. 去平均值,即每一位特征减去各自的平均值。

  2. 计算协方差矩阵 X T X X^TX XTX

  3. 通过SVD计算协方差矩阵的特征值与特征向量。

  4. 对特征值从大到小排序,选择其中最大的k个。然后将其对应的k个特征向量(右奇异矩阵)分别作为列向量组成特征向量矩阵 P P P。(左奇异矩阵可以用于对行数的压缩;右奇异矩阵可以用于对列(即特征维度)的压缩。)

  5. 将数据转换到k个特征向量构建的新空间中,即 Y = X P Y=XP Y=XP

"""
Author:wucng
Time:  20200109
Summary: PCA 基于特征值分解
源代码: https://github.com/wucng/MLAndDL
"""
import scipy
# from scipy.misc import imread,imshow
from imageio import imread,imsave
import numpy as np
from numpy.linalg import eig,svd
import matplotlib.pyplot as plt
from sklearn.decomposition  import PCA

class PCASelf(object):
    def __init__(self,n_components=1,mode="svd"):
        """
        :param n_components:  主成分数(压缩后的特征数)
        :param mode: "eig" 特征矩阵分解,"svd" 奇异值分解
        """
        self.n_components = n_components
        self.mode = mode

    def fit_transform(self,X:np.array):
        # 去平均值
        X = X-np.mean(X,0)
        # 协方差矩阵
        A = np.matmul(X.T,X)#/len(X)

        if self.mode == "eig":
            # 计算协方差矩阵的特征值与特征向量
            vals, vecs = eig(A)
        else:
            # u, s, vh = np.linalg.svd(A)    # 这里vh为V的转置
            _, vals, vecs = svd(A)

        # 对特征值从大到小排序,选择其中最大的k个
        index = np.argsort(vals*(-1))[:self.n_components] # 默认是从小到大排序,乘上-1 后就变成从大到小排序

        # 根据选择的K个特征值组成新的特征向量矩阵(列对应特征向量,而不是行)
        P = vecs[:,index]

        # 特征压缩后的矩阵
        return np.matmul(X,P)

if __name__=="__main__":
    # 自定义方法
    X = np.array([[-1, 1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
    pca = PCASelf(1)
    print(pca.fit_transform(X))

    # sklearn 方法
    print(PCA(1).fit_transform(X))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值