当前位置:   article > 正文

机器学习 07:PCA 及其 sklearn 源码解读_sklearn源码分析

sklearn源码分析

概述


Principal Component Analysis 即主成分分析的主要思想是将 n 维特征映射到 k 维上,这 k 维是全新的正交特征也被称为主成分,是在原有 n 维特征的基础上重新构造出来的 k 维特征。PCA 的工作就是从原始的空间中顺序地找一组相互正交的坐标轴,其中,第一个新坐标轴选择是原始数据中方差最大的方向,第二个新坐标轴选取是与第一个坐标轴正交的平面中使得方差最大的,第三个轴是与第 1,2 个轴正交的平面中方差最大的,依次类推,得到 n 个这样的坐标轴。通过这种方式获得的新的坐标轴,大部分方差都包含在前面 k 个坐标轴中,后面的坐标轴所含的方差几乎为 0 。于是,我们可以只保留前 k 维,从而实现对数据特征的降维处理。

PCA 的计算有两种方式:

  1. 分解协方差矩阵:具体实现过程主要包括三步:
    1. 计算数据矩阵的协方差矩阵
    2. 计算协方差矩阵的特征值特征向量
    3. 选择特征值最大(即方差最大)的 k 个特征所对应的特征向量组成的矩阵
  2. 分解内积矩阵

分解协方差矩阵


协方差矩阵

协方差用来刻画两个随机变量之间的相关性。简单比较一下方差与协方差:

均值: x ˉ = 1 n ∑ i = 1 N x ⃗ i \bar x=\frac1n\sum\limits^N_{i=1}\vec x_i xˉ=n1i=1Nx i

样本无偏方差: S 2 = 1 n − 1 ∑ i = 1 N ( x ˉ − x ⃗ i ) 2 S^2=\frac1{n-1}\sum\limits^N_{i=1}(\bar x-\vec x_i)^2 S2=n11i=1N(xˉx i)2

样本 X X X 和样本 Y Y Y 的协方差: C o v ( X , Y ) = 1 n − 1 ∑ i = 1 N ( x ˉ − x ⃗ i ) ( y ˉ − y ⃗ i ) Cov(X,Y)=\frac1{n-1}\sum\limits^N_{i=1}(\bar x-\vec x_i)(\bar y-\vec y_i) Cov(X,Y)=n11i=1N(xˉx i)(yˉy i)

我们可以发现:方差的计算是针对一维特征,而协方差则要求至少二维。方差是协方差的特殊情况。协方差为正时,说明 X , Y X,Y X,Y 是正相关关系,为负时,则说明是负相关关系,为 0 则说明两个样本相互独立。当有多个样本时,两两之间的协方差就构成了协方差矩阵。

协方差矩阵与散度矩阵关系密切,由散度矩阵的定义: S = ∑ i = 1 N ( x ⃗ i − x ˉ ) ( x ⃗ i − x ˉ ) T S=\sum\limits^N_{i=1}(\vec x_i-\bar x)(\vec x_i-\bar x)^T S=i=1N(x ixˉ)(x ixˉ)T我们发现,对于一组数据 X X X S = ( n − 1 ) C o v ( X ) S=(n-1)Cov(X) S=(n1)Cov(X),因此它们有相同的特征值和特征向量。


特征值分解和 SVD

详细过程可以参考我的另一篇文章:特征分解和奇异值分解
计算出 n 个特征向量后,我们对其排序,并取前 k 个即可。


分解内积矩阵


sklearn 库中的 PCA 就是使用此方法计算的。

假设 X X X 矩阵是一个样本集,它已经中心化过(PCA 要求数据中心化),那么协方差矩阵就是: 1 n X X T \frac1nXX^T n1XXT
我们对 X X X 进行 SVD 分解: X = U Σ V T X=U\Sigma V^T X=UΣVT,则 X T = V Σ U T X^T=V\Sigma U^T XT=VΣUT
因此就有: 1 n X X T = U Σ 2 U T \frac1nXX^T=U\Sigma^2U^T n1XXT=UΣ2UT

因此,我们可以对 X X X 进行 SVD 分解,然后求 Σ \Sigma Σ 的内积来计算特征向量。


关于源码


花一晚上阅读了 sklearn官方代码,大概记录一下。源码篇幅过长,所以仅记录重点部分。


计算 PCA

通过 svd_solver 参数,我们可以选择不同方式对数据进行求解,但是基本思路相同,这里仅以 svd_solver == 'full' 也就是使用所有数据进行特征分解这一情况为例。


fit() 方法的实现:
其中,explained_variance_ 为方差值,explained_variance_ratio_ 为每个方程的贡献率,也就是占比,可以通过这个参数来判断 n 具体取值。

注意,n_components 参数可以取好几种值:

  1. “mle”: 自动确定 n 值。
  2. 0 < n_components < 1 0<\text{n\_components}<1 0<n_components<1:表示希望保留的信息量的比例。PCA 会自动选使得 信息量 ≥ n_components \ge\text{n\_components} n_components 的特征数量。
  3. n_components ≥ 1 \text{n\_components} \ge1 n_components1 且为整数:选前 n 个
def _fit_full(self, X, n_components):
	n_samples, n_features = X.shape
	
	... # 对传入的数据进行验证,暂且省略
	
	# Center data
	self.mean_ = np.mean(X, axis=0)
	X -= self.mean_
	
	# 使用分解内积矩阵方法求特征向量
	U, S, Vt = linalg.svd(X, full_matrices=False)
	U, Vt = svd_flip(U, Vt) # 由于有时候可能求得负值,所以翻转特征向量符号以强制执行确定性输出
	components_ = Vt
	
	explained_variance_ = (S**2) / (n_samples - 1)
	total_var = explained_variance_.sum()
	explained_variance_ratio_ = explained_variance_ / total_var
	singular_values_ = S.copy()  # Store the singular values.
	
	# 根据 n_components 确定取几个特征,也就是 n 的值。
	if n_components == "mle":
	    n_components = _infer_dimension(explained_variance_, n_samples)
	elif 0 < n_components < 1.0:
	    ratio_cumsum = stable_cumsum(explained_variance_ratio_)
	    n_components = np.searchsorted(ratio_cumsum, n_components, side="right") + 1
	# Compute noise covariance using Probabilistic PCA model
	# The sigma2 maximum likelihood (cf. eq. 12.46)
	if n_components < min(n_features, n_samples):
	    self.noise_variance_ = explained_variance_[n_components:].mean()
	else:
	    self.noise_variance_ = 0.0
	
	... # 对参数进行赋值,暂且省略
	
	return U, S, Vt
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

transform() 方法的实现:

这里运用了白化操作,即对数据进行去相关,以降低输入数据的冗余性,白化后的数据有两个特性:消除了特征之间的相关性,且所有特征的方差都为 1。

def transform(self, X):
        ... # 对程序和数据进行验证,暂且忽略
        
        if self.mean_ is not None:
            X = X - self.mean_
        X_transformed = np.dot(X, self.components_.T)
        # 白化
        if self.whiten:
            X_transformed /= np.sqrt(self.explained_variance_)
        return X_transformed
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

fit_transform() 方法的实现:

相关的公式都被作者贴心的写在注释里了。

def fit_transform(self, X, y=None):
        U, S, Vt = self._fit(X)
        U = U[:, : self.n_components_]

        if self.whiten:
            # X_new = X * V / S * sqrt(n_samples) = U * sqrt(n_samples)
            U *= sqrt(X.shape[0] - 1)
        else:
            # X_new = X * V = U * S * Vt * V = U * S
            U *= S[: self.n_components_]
        return U
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

自动选择 n 的值

通过计算对数似然的方法来判断 n 具体该取几,也就是保留几个维度。相关论文可见此链接

def _assess_dimension(spectrum, rank, n_samples):
    """Compute the log-likelihood of a rank ``rank`` dataset.
    The dataset is assumed to be embedded in gaussian noise of shape(n,
    dimf) having spectrum ``spectrum``.
    Parameters
    ----------
    spectrum : ndarray of shape (n_features,)
        Data spectrum.
    rank : int
        Tested rank value. It should be strictly lower than n_features,
        otherwise the method isn't specified (division by zero in equation
        (31) from the paper).
    n_samples : int
        Number of samples.
    Returns
    -------
    ll : float
        The log-likelihood.
    """

    n_features = spectrum.shape[0]
    if not 1 <= rank < n_features:
        raise ValueError("the tested rank should be in [1, n_features - 1]")

    eps = 1e-15

    if spectrum[rank - 1] < eps:
        # When the tested rank is associated with a small eigenvalue, there's
        # no point in computing the log-likelihood: it's going to be very
        # small and won't be the max anyway. Also, it can lead to numerical
        # issues below when computing pa, in particular in log((spectrum[i] -
        # spectrum[j]) because this will take the log of something very small.
        return -np.inf

    pu = -rank * log(2.0)
    for i in range(1, rank + 1):
        pu += (
            gammaln((n_features - i + 1) / 2.0)
            - log(np.pi) * (n_features - i + 1) / 2.0
        )

    pl = np.sum(np.log(spectrum[:rank]))
    pl = -pl * n_samples / 2.0

    v = max(eps, np.sum(spectrum[rank:]) / (n_features - rank))
    pv = -np.log(v) * n_samples * (n_features - rank) / 2.0

    m = n_features * rank - rank * (rank + 1.0) / 2.0
    pp = log(2.0 * np.pi) * (m + rank) / 2.0

    pa = 0.0
    spectrum_ = spectrum.copy()
    spectrum_[rank:n_features] = v
    for i in range(rank):
        for j in range(i + 1, len(spectrum)):
            pa += log(
                (spectrum[i] - spectrum[j]) * (1.0 / spectrum_[j] - 1.0 / spectrum_[i])
            ) + log(n_samples)

    ll = pu + pl + pv + pp - pa / 2.0 - rank * log(n_samples) / 2.0
    return ll


def _infer_dimension(spectrum, n_samples):
    """Infers the dimension of a dataset with a given spectrum.
    The returned value will be in [1, n_features - 1].
    """
    ll = np.empty_like(spectrum)
    ll[0] = -np.inf  # we don't want to return n_components = 0
    for rank in range(1, spectrum.shape[0]):
        ll[rank] = _assess_dimension(spectrum, rank, n_samples)
    return ll.argmax()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

样本得分

通过计算每个样本的对数似然来反应性能。相关论文可见此链接

def get_covariance(self):
    """Compute data covariance with the generative model.
    ``cov = components_.T * S**2 * components_ + sigma2 * eye(n_features)``
    where S**2 contains the explained variances, and sigma2 contains the
    noise variances.
    Returns
    -------
    cov : array of shape=(n_features, n_features)
        Estimated covariance of data.
    """
    components_ = self.components_
    exp_var = self.explained_variance_
    if self.whiten:
        components_ = components_ * np.sqrt(exp_var[:, np.newaxis])
    exp_var_diff = np.maximum(exp_var - self.noise_variance_, 0.0)
    cov = np.dot(components_.T * exp_var_diff, components_)
    cov.flat[:: len(cov) + 1] += self.noise_variance_  # modify diag inplace
    return cov

def get_precision(self):
    """Compute data precision matrix with the generative model.
    Equals the inverse of the covariance but computed with
    the matrix inversion lemma for efficiency.
    Returns
    -------
    precision : array, shape=(n_features, n_features)
        Estimated precision of data.
    """
    n_features = self.components_.shape[1]

    # handle corner cases first
    if self.n_components_ == 0:
        return np.eye(n_features) / self.noise_variance_

    if np.isclose(self.noise_variance_, 0.0, atol=0.0):
        return linalg.inv(self.get_covariance())

    # Get precision using matrix inversion lemma
    components_ = self.components_
    exp_var = self.explained_variance_
    if self.whiten:
        components_ = components_ * np.sqrt(exp_var[:, np.newaxis])
    exp_var_diff = np.maximum(exp_var - self.noise_variance_, 0.0)
    precision = np.dot(components_, components_.T) / self.noise_variance_
    precision.flat[:: len(precision) + 1] += 1.0 / exp_var_diff
    precision = np.dot(components_.T, np.dot(linalg.inv(precision), components_))
    precision /= -(self.noise_variance_**2)
    precision.flat[:: len(precision) + 1] += 1.0 / self.noise_variance_
    return precision

def score_samples(self, X):
	"""Return the log-likelihood of each sample.
	Parameters
	----------
	X : array-like of shape (n_samples, n_features)
	    The data.
	"""
	... # 对程序和数据的验证,暂且忽略
	
	Xr = X - self.mean_
	n_features = X.shape[1]
	precision = self.get_precision()
	log_like = -0.5 * (Xr * (np.dot(Xr, precision))).sum(axis=1)
	log_like -= 0.5 * (n_features * log(2.0 * np.pi) - fast_logdet(precision))
	return log_like

def score(self, X, y=None):
    """Return the average log-likelihood of all samples."""
    return np.mean(self.score_samples(X))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

2023-8-2 更新


PCA 公式推导


首先需要两个前置知识:

谱分解定理 Spectral Theorem

Let A ∈ R n A\in R^n ARn be symmetric, and λ i ∈ R , i ∈ [ 1 , n ] \lambda_i\in R, i\in[1,n] λiR,i[1,n] be the eigenvalues of A A A. There exists a set of orthonormal vectors u ⃗ i ∈ R n , i ∈ [ 1 , n ] \vec u_i\in R^n,i\in[1,n] u iRn,i[1,n], such that A u ⃗ i = λ i u ⃗ i A\vec u_i=\lambda_i\vec u_i Au i=λiu i. Equivalently, there exists an orthogonal matrix U = [ u ⃗ 1 , ⋯   , u ⃗ n ] U=[\vec u_1,\cdots,\vec u_n] U=[u 1,,u n] (i.e. U U T = U T U = I n UU^T=U^TU=I_n UUT=UTU=In) such that: A = U Λ U T = ∑ i = 1 n λ i u ⃗ i u ⃗ i T , Λ = [ λ 1 ⋯ 0 ⋮ ⋱ ⋮ 0 ⋯ λ n ] , u ⃗ i = [ u i 1 ⋮ u i n ] A=U\Lambda U^T=\sum^n_{i=1}\lambda_i\vec u_i\vec u_i^T, \Lambda=[λ100λn]

λ100λn
,\vec u_i=[ui1uin]
A=UΛUT=i=1nλiu iu iT,Λ= λ100λn ,u i= ui1uin

瑞利商 Rayleigh Quotients

Given a symmertric matrix A ∈ S n A\in S^n ASn, λ min ⁡ ( A ) ≤ x ⃗ T A x ⃗ x ⃗ T x ⃗ ≤ λ max ⁡ ( A ) , ∀ x ⃗ ≠ 0 λ max ⁡ ( A ) = max ⁡ x ⃗ T A x ⃗ λ max ⁡ ( A ) = max ⁡ x ⃗ T A x ⃗ \lambda_{\min}(A)\le\frac{\vec x^TA\vec x}{\vec x^T\vec x}\le\lambda_{\max}(A),\forall\vec x\ne0\\\lambda_{\max}(A)=\max\vec x^TA\vec x\\\lambda_{\max}(A)=\max\vec x^TA\vec x λmin(A)x Tx x TAx λmax(A),x =0λmax(A)=maxx TAx λmax(A)=maxx TAx The maximum and minimum are attained for x ⃗ = u ⃗ 1 \vec x=\vec u_1 x =u 1 and for x ⃗ = u ⃗ n \vec x=\vec u_n x =u n, where u ⃗ 1 \vec u_1 u 1 and u ⃗ n \vec u_n u n are the largest and smallest eigenvector of A A A. 其中 x ⃗ \vec x x 要做标准化处理,即 ∥ x ⃗ ∥ 2 = 1 \Vert\vec x\Vert_2=1 x 2=1

Proof
x ⃗ T A x ⃗ \vec x^TA\vec x x TAx 使用谱定理: x ⃗ T A x ⃗ = x ⃗ T U Λ U T x ⃗ = x ⃗ ′   T Λ x ⃗ ′ = ∑ i = 1 n λ i x i ′ 2 ,   x ⃗ ′ = U T x ⃗ = [ x 1 ′ ⋮ x n ′ ] \vec x^TA\vec x=\vec x^TU\Lambda U^T\vec x=\vec x^{\prime~T}\Lambda\vec x^\prime=\sum^n_{i=1}\lambda_ix_i^{\prime2},~\vec x^\prime=U^T\vec x=[x1xn]

x TAx =x TUΛUTx =x  TΛx =i=1nλixi′2, x =UTx = x1xn 则有: λ min ⁡ ∑ i = 1 n x i ′ 2 ≤ ∑ i = 1 n λ i x i ′ 2 ≤ λ max ⁡ ∑ i = 1 n x i ′ 2 \lambda_{\min}\sum^n_{i=1}x_i^{\prime2}\le\sum^n_{i=1}\lambda_ix_i^{\prime2}\le\lambda_{\max}\sum^n_{i=1}x_i^{\prime2} λmini=1nxi′2i=1nλixi′2λmaxi=1nxi′2
由于 U U U正交矩阵,所以: ∑ i = 1 n x i ′ 2 = x ⃗ ′   T x ⃗ ′ = ( U T x ⃗ ) T ( U T x ⃗ ) = x ⃗ T U U T x ⃗ = x ⃗ T x ⃗ = ∑ i = 1 n x i 2 \sum^n_{i=1}x_i^{\prime2}=\vec x^{\prime~T}\vec x^\prime=(U^T\vec x)^T(U^T\vec x)=\vec x^TUU^T\vec x=\vec x^T\vec x=\sum^n_{i=1}x_i^2 i=1nxi′2=x  Tx =(UTx )T(UTx )=x TUUTx =x Tx =i=1nxi2
所以: λ min ⁡ x ⃗ T x ⃗ ≤ x ⃗ T A x ⃗ ≤ λ max ⁡ x ⃗ T x ⃗ \lambda_{\min}\vec x^T\vec x\le\vec x^TA\vec x\le\lambda_{\max}\vec x^T\vec x λminx Tx x TAx λmaxx Tx

接下来,我们可以开始进行 PCA 的推导,首先我们要解决如下三个问题:

  1. 如何判断何为最重要的主成分:可以计算样本在该主成分方向上的方差,方差越大说明在该方向上样本分布越离散,特征也就越明显,方差最大的就是最重要的那个主成分。
  2. 如何找次重要的主成分:把最重要的主成分去掉,也就是去掉每个样本在该方向上的分量,然后再选取最重要的主成分。
  3. 如何找到第 n 重要的主成分:如果 n ≠ 1 n\ne1 n=1,重复第二个问题的解决方法。

现在我们分别证明上面两个问题的解决方式是正确的:

  • 对于 “如何判断何为最重要的主成分” 问题:
    • 首先对样本进行中心化: X ~ = [ x ~ 1 , ⋯   , x ~ m ] ,   x ~ i = x ⃗ i − x ˉ \tilde X=[\tilde x_1,\cdots,\tilde x_m],~\tilde x_i=\vec x_i-\bar x X~=[x~1,,x~m], x~i=x ixˉ
    • 然后我们假设最重要的主成分为 z ⃗ 1 , z ⃗ 1 ∈ R n , ∥ z ⃗ ∥ 2 = 1 \vec z_1,\vec z_1\in R^n,\Vert\vec z\Vert_2=1 z 1,z 1Rn,z 2=1,那么样本在 z ⃗ 1 \vec z_1 z 1 上的投影大小为: α i = x ~ i T z ⃗ 1 \alpha_i=\tilde x_i^T\vec z_1 αi=x~iTz 1
    • 然后计算方差: 1 m ∑ i = 1 m α i 2 = 1 m z ⃗ 1 T X ~ X ~ T z ⃗ 1 \frac 1m\sum^m_{i=1}\alpha_i^2=\frac 1m\vec z_1^T\tilde X\tilde X^T\vec z_1 m1i=1mαi2=m1z 1TX~X~Tz 1
    • 我们使方差最大化,以此来计算 z ⃗ 1 \vec z_1 z 1 max ⁡ z ⃗ 1 ∈ R n z 1 T ( X ~ X ~ T ) z ⃗ 1 \max_{\vec z_1\in R^n}z_1^T(\tilde X\tilde X^T)\vec z_1 z 1Rnmaxz1T(X~X~T)z 1
      • X ~ \tilde X X~ 使用 SVD: X ~ = U Σ V T = ∑ i = 1 m σ i u ⃗ i v ⃗ i \tilde X=U\Sigma V^T=\sum^m_{i=1}\sigma_i\vec u_i\vec v_i X~=UΣVT=i=1mσiu iv i
      • 因此 X ~ X ~ T = X ~ = U Σ V T V Σ U T = U Σ 2 U T \tilde X\tilde X^T=\tilde X=U\Sigma V^TV\Sigma U^T=U\Sigma^2U^T X~X~T=X~=UΣVTVΣUT=UΣ2UT Σ \Sigma Σ 是对角矩阵,因此这里直接用平方形式表示),此时问题转化为: max ⁡ z ⃗ 1 ∈ R n z 1 T U Σ 2 U T z ⃗ 1 \max_{\vec z_1\in R^n}z_1^TU\Sigma^2U^T\vec z_1 z 1Rnmaxz1TUΣ2UTz 1
      • 对它使用瑞利商可知,当 z ⃗ 1 = u ⃗ 1 \vec z_1=\vec u_1 z 1=u 1 时取最大值,也就是最重要的主成分就是第一个特征向量
  • 对于 “如何找次重要的主成分” 问题:
    • 我们记 x ~ i ( 1 ) \tilde x_i^{(1)} x~i(1) 为去掉最重要的主成分方向上的分量之后的样本,计算方式如下:
      • 先计算 x ~ i \tilde x_i x~i u 1 u_1 u1 方向上的投影向量,再用 x ~ i \tilde x_i x~i 减去这个向量: x ~ i ( 1 ) = x ~ i − u ⃗ 1 ( u ⃗ 1 T x ~ i ) \tilde x_i^{(1)}=\tilde x_i-\vec u_1(\vec u_1^T\tilde x_i) x~i(1)=x~iu 1(u 1Tx~i) X ~ ( 1 ) = [ x ~ 1 ( 1 ) , ⋯   , x ~ m ( 1 ) ] = ( I n − u ⃗ 1 u ⃗ 1 T ) X ~ \tilde X^{(1)}=[\tilde x_1^{(1)},\cdots,\tilde x^{(1)}_m]=(I_n-\vec u_1\vec u^T_1)\tilde X X~(1)=[x~1(1),,x~m(1)]=(Inu 1u 1T)X~
      • X ~ ( 1 ) \tilde X^{(1)} X~(1) 展开计算,由于 U U U 是正交矩阵,所以 u ⃗ 1 \vec u_1 u 1 点乘除它以外的其它特征向量都为 0: X ~ ( 1 ) = ∑ i = 1 m σ i u ⃗ i v ⃗ i − ( u ⃗ 1 u ⃗ 1 T ) ∑ i = 1 m σ i u ⃗ i v ⃗ i T = ∑ i = 1 m σ i u ⃗ i v ⃗ i − σ 1 u ⃗ 1 v ⃗ 1 T = ∑ i = 2 m σ i u ⃗ i v ⃗ i ˜X(1)=mi=1σiuivi(u1uT1)mi=1σiuivTi=mi=1σiuiviσ1u1vT1=mi=2σiuivi
        X~(1)=i=1mσiu iv i(u 1u 1T)i=1mσiu iv iT=i=1mσiu iv iσ1u 1v 1T=i=2mσiu iv i
    • 因此我们计算下式就可以求出 z ⃗ 2 \vec z_2 z 2 max ⁡ z ⃗ 2 ∈ R n z 2 T ( X ~ ( 1 ) X ~ ( 1 ) T ) z ⃗ 2 \max_{\vec z_2\in R^n}z_2^T(\tilde X^{(1)}\tilde X^{(1)T})\vec z_2 z 2Rnmaxz2T(X~(1)X~(1)T)z 2
    • 我们发现 z ⃗ 2 = u ⃗ 2 \vec z_2=\vec u_2 z 2=u 2,因此次重要的主成分其实就是第二个特征向量
  • 对于 “如何找到第 n 重要的主成分” 问题:
    • 求出 X ( n − 1 ) X^{(n-1)} X(n1),按照上面的步骤计算
    • 显然, z ⃗ n = u ⃗ n \vec z_n=\vec u_n z n=u n,也就是说,特征向量就是主成分,而且越靠前的特征向量越重要

Kernel PCA


kernel PCA 通过使用核函数对数据进行非线性变换使得 PCA 具有了非线性分解的能力。

推导过程如下:

  • 对于输入数据 x i ∈ R n 0 x_i\in R^{n_0} xiRn0,使用一个非线性映射函数 ϕ : R n 0 → R n 1 \phi:R^{n_0}\rightarrow R^{n_1} ϕ:Rn0Rn1
  • 假设 ϕ ( x i ) \phi(x_i) ϕ(xi) 已经中心化,即: 1 N ∑ i = 1 N ϕ ( x i ) = 0 \frac 1N\sum^N_{i=1}\phi(x_i)=0 N1i=1Nϕ(xi)=0它的协方差矩阵为 H ~ = 1 N ∑ i = 1 N ϕ ( x i ) ϕ T ( x i ) \tilde H=\frac 1N\sum^N_{i=1}\phi(x_i)\phi^T(x_i) H~=N1i=1Nϕ(xi)ϕT(xi)我们要对 H ~ \tilde H H~ 进行特征值分解,即求解 H ~ z ~ = λ ~ z ~ \tilde H\tilde z=\tilde\lambda\tilde z H~z~=λ~z~
  • 把特征值分解方程展开得: H ~ z ~ = λ ~ z ~ 1 N ∑ i = 1 N ϕ ( x i ) ϕ T ( x i ) z ~ = λ ~ z ~ ˜H˜z=˜λ˜z1NNi=1ϕ(xi)ϕT(xi)˜z=˜λ˜z
    H~z~N1i=1Nϕ(xi)ϕT(xi)z~=λ~z~=λ~z~
    由于 ϕ T ( x i ) z ~ \phi^T(x_i)\tilde z ϕT(xi)z~ 的结果是一个标量,可以表示为一个数字 α i \alpha_i αi,则有 z ~ = ∑ j = 1 N α j ϕ ( x j ) \tilde z=\sum^N_{j=1}\alpha_j\phi(x_j) z~=j=1Nαjϕ(xj),即特征向量可以表示为所有输入的线性组合。我们将此等式带入方程得: 1 N ∑ i = 1 N ϕ ( x i ) ϕ T ( x i ) ( ∑ j = 1 N α j ϕ ( x j ) ) = λ ~ ∑ j = 1 N α j ϕ ( x j ) 1 N ∑ i = 1 N ϕ ( x i ) ( ∑ j = 1 N α j ϕ T ( x i ) ϕ ( x j ) ) = λ ~ ∑ j = 1 N α j ϕ ( x j ) 1NNi=1ϕ(xi)ϕT(xi)(Nj=1αjϕ(xj))=˜λNj=1αjϕ(xj)1NNi=1ϕ(xi)(Nj=1αjϕT(xi)ϕ(xj))=˜λNj=1αjϕ(xj)
    N1i=1Nϕ(xi)ϕT(xi)(j=1Nαjϕ(xj))N1i=1Nϕ(xi)(j=1NαjϕT(xi)ϕ(xj))=λ~j=1Nαjϕ(xj)=λ~j=1Nαjϕ(xj)
  • 我们定义核函数为 k ( x i , x j ) = ϕ T ( x i ) ϕ ( x j ) k(x_i,x_j)=\phi^T(x_i)\phi(x_j) k(xi,xj)=ϕT(xi)ϕ(xj),该函数可以通过 x i , x j x_i,x_j xi,xj 直接计算出一个标量,作为 ϕ ( x i ) T ϕ ( x j ) \phi(x_i)^T\phi(x_j) ϕ(xi)Tϕ(xj) 的结果,这样可以节省计算 ϕ \phi ϕ 函数的时间,而且,由于 ϕ \phi ϕ 的计算方式以及维度很难确定,使用核函数也可以大大节省工作量。值得注意的是,并不是所有核函数都有对应的 ϕ \phi ϕ 函数。用核函数替换后的方程如下: 1 N ∑ i = 1 N ϕ ( x i ) ( ∑ j = 1 N α j k ( x i , x j ) ) = λ ~ ∑ j = 1 N α j ϕ ( x j ) 1 N ∑ i = 1 N ∑ j = 1 N α j k ( x k , x i ) k ( x i , x j ) = λ ~ ∑ j = 1 N α j k ( x k , x j ) 1NNi=1ϕ(xi)(Nj=1αjk(xi,xj))=˜λNj=1αjϕ(xj)1NNi=1Nj=1αjk(xk,xi)k(xi,xj)=˜λNj=1αjk(xk,xj)
    N1i=1Nϕ(xi)(j=1Nαjk(xi,xj))N1i=1Nj=1Nαjk(xk,xi)k(xi,xj)=λ~j=1Nαjϕ(xj)=λ~j=1Nαjk(xk,xj)
  • 接着可以定义一个 Gram 矩阵 K ∈ R N , K ( i , j ) = k ( x i , x j ) K\in R^N,K(i,j)=k(x_i,x_j) KRN,K(i,j)=k(xi,xj),简化上式: K 2 α ⃗ = N λ ~ K α ⃗ K α ⃗ = N λ ~ α ⃗ K α ⃗ = λ α ⃗ K2α=N˜λKαKα=N˜λαKα=λα
    K2α Kα Kα =Nλ~Kα =Nλ~α =λα
    现在我们可以把 α ⃗ \vec\alpha α 看作一个特征向量并进行求解,但这一步的前提是 1. z ~ \tilde z z~ 必须是单位向量 2. ϕ ( x i ) \phi(x_i) ϕ(xi) 是中心化的
    • z ~ \tilde z z~ 必须是单位向量: 1 = z ~ T z ~ 1 = ∑ i = 1 N ∑ j = 1 N α i α j ϕ T ( x i ) ϕ ( x j ) 1 = α T K α 1=˜zT˜z1=Ni=1Nj=1αiαjϕT(xi)ϕ(xj)1=αTKα
      111=z~Tz~=i=1Nj=1NαiαjϕT(xi)ϕ(xj)=αTKα
      结合 K α = λ α K\alpha=\lambda\alpha Kα=λα,可得 α T λ α = 1 \alpha^T\lambda\alpha=1 αTλα=1,因此我们要对 α \alpha α 进行归一化,使其范数为 1 λ \frac 1\lambda λ1
    • ϕ ( x i ) \phi(x_i) ϕ(xi) 进行中心化: ϕ ~ ( x i ) = ϕ ( x i ) − 1 N ∑ j = 1 N ϕ ( x j ) \tilde\phi(x_i)=\phi(x_i)-\frac 1N\sum^N_{j=1}\phi(x_j) ϕ~(xi)=ϕ(xi)N1j=1Nϕ(xj),则有 k ~ ( x i , x j ) = ( ϕ ( x i ) − 1 N ∑ k = 1 N ϕ ( x k ) ) T ( ϕ ( x j − 1 N ∑ l = 1 N ϕ ( x l ) ) = k ( x i , x j ) − 1 N ∑ l = 1 N k ( x i , x l ) − 1 N ∑ k = 1 N k ( x j , x k ) + 1 N 2 ∑ k = 1 N ∑ l = 1 N k ( x k , x l ) ˜k(xi,xj)=(ϕ(xi)1NNk=1ϕ(xk))T(ϕ(xj1NNl=1ϕ(xl))=k(xi,xj)1NNl=1k(xi,xl)1NNk=1k(xj,xk)+1N2Nk=1Nl=1k(xk,xl)
      k~(xi,xj)=(ϕ(xi)N1k=1Nϕ(xk))T(ϕ(xjN1l=1Nϕ(xl))=k(xi,xj)N1l=1Nk(xi,xl)N1k=1Nk(xj,xk)+N21k=1Nl=1Nk(xk,xl)
      因此有: K ~ = K − 2 I 1 N K + I 1 N K I 1 N \tilde K=K-2I_\frac 1NK+I\frac 1NKI\frac 1N K~=K2IN1K+IN1KIN1
  • 通过以上步骤,我们可以求出 α ⃗ \vec\alpha α ,但是我们仍然不可以直接求解 z ~ \tilde z z~,因为涉及到 ϕ \phi ϕ 函数,而我们的目的是用核函数替换掉所有的 ϕ \phi ϕ 函数来简化计算,所以我们可以通过 ϕ T ( x ) z ~ = ∑ j = 1 N α j k ( x , x j ) \phi^T(x)\tilde z=\sum^N_{j=1}\alpha_jk(x,x_j) ϕT(x)z~=j=1Nαjk(x,xj) 来计算出每个数据在 z ~ \tilde z z~ 方向上的投影,以此间接获取我们所需要的信息,该投影即为 x j x_j xj z ~ \tilde z z~ 方向上的坐标,而 x j x_j xj 去掉该投影方向的分量后,又可以用来计算下一个主成分,所以即使不知道 z ~ \tilde z z~ 具体为多少也没关系。




同步更新于:SP-FA 的博客


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/106867
推荐阅读
  

闽ICP备14008679号