现实应用中属性维度成千上万,在高维度情况下会带来很多麻烦,而且当维度大的时候,数据样本一般分布的非常稀疏,这是所有学习算法要面对的问题,降维技术应运而生。很多时候,人们观测或收集到的数据样本是高维度的,但是和学习任务密切相关的也许就是某个低维分布。
降维是对事物的特征进行压缩和筛选,该项任务相对比较抽象。如果没有特定领域知识,无法预先决定采用哪些数据,比如在人脸识别任务中,如果直接使用图像的原始像素信息,数据的维度会非常高,通常会利用降维技术对图像进行处理,保留下最具有区分度的像素组合。
思想: 寻找表示数据分布的最优子空间(降维可以去掉线性相关性)
数学原理: 取协方差矩阵前 s s s 个最大特征值对应的特征向量构成映射矩阵,对数据进行降维
思想: 寻找可分性判据最大的子空间
数学原理: 用到了 F i s h e r Fisher Fisher 的思想,即寻找一个向量,使得降维后类内散度最小,类间散度最大,其实就是取对应的特征向量构成映射矩阵,对数据进行处理。
局部线性嵌入 ( L o c a l l y L i n e a r E m b e d d i n g Locally \; Linear \; Embedding LocallyLinearEmbedding) 和传统的 P C A PCA PCA , L D A LDA LDA 等关注样本方差的降维方式相比, L L E LLE LLE 关注于降维时保持样本局部的线性特征,由于 L L E LLE LLE 在降维时保持了样本的局部特征,它广泛用于图像识别,高维数据可视化等领域。
将数据集考虑成 矩阵,每行对应一个样本,每列对应一个特征或者维度,在降维方法中,都是把数据集看成矩阵形式,数学上有非常多关于矩阵的处理技巧和定理,降维的方法证明均源于此。
奇异值分解 ( S i n g u l a r V a l u e D e c o m p o s i t i o n Singular \; Value \; Decomposition SingularValueDecomposition) 是在机器学习领域广泛应用的算法,它不光可以用于降维算法中的特征分解,还可以用于 推荐系统,自然语言处理 等领域,是很多机器学习算法的 基础,也是 P C A PCA PCA 降维方法的基础。
奇异值分解: 将矩阵分解成奇异向量和奇异值。可以将矩阵 A = ( a i j ) m × n A = (a_{ij})_{m\times n} A=(aij)m×n 分解成三个矩阵的乘积:
A = U Σ V T A = U\Sigma V^T A=UΣVT
其中 U = ( b i j ) m × m , Σ = ( c i j ) m × n , V T = ( d i j ) n × n U=(b_{ij})m\times m,\;\Sigma=(c_{ij})_{m\times n},\;V^T=(d_{ij})n\times n U=(bij)m×m,Σ=(cij)m×n,VT=(dij)n×n。矩阵 U U U 和 V V V 都为正交矩阵,矩阵 U U U 的列向量称为 左奇异向量,矩阵 V V V 的列向量称为 右奇异向量, Σ \Sigma Σ 为 对角矩阵, Σ \Sigma Σ 对角线上的元素称为矩阵 A A A 的 奇异值,奇异值按从大到小排列。
若 A A A 为实数, U U U 和 $ V$ 都为实正交矩阵,即 U U T = V V T = I UU^T=VV^T=I UUT=VVT=I ,则:
A V = U Σ V T V A V = U Σ A [ v 1 ⃗ v 2 ⃗ … v n ⃗ ] = [ u 1 ⃗ u 2 ⃗ … u m ⃗ ] ( σ 1 … 0 ⋮ ⋱ ⋮ 0 … σ n ) A v i = σ i u i , i=1,2, … ,n \begin{aligned} AV &= U\Sigma V^T V \\ AV &= U\Sigma \\ A[\vec{v_1} \quad \vec{v_2} \quad \dots \quad \vec{v_n}] &= [\vec{u_1} \quad \vec{u_2} \quad \dots \quad \vec{u_m}] \begin{pmatrix} \sigma_1 \quad \dots \quad 0 \\ \vdots \quad \ddots \quad \vdots \\ 0 \quad \dots \quad \sigma_n \end{pmatrix} \\ Av_i &= \sigma_i u_i, \quad \text{i=1,2,$\dots$,n} \end{aligned} AVAVA[v1v2…vn]Avi=UΣVTV=UΣ=[u1u2…um]⎝⎜⎛σ1…0⋮⋱⋮0…σn⎠⎟⎞=σiui,i=1,2,…,n
矩阵 A A A乘 v i v_i vi 相当于对 v i v_i vi 进行旋转、压缩和映射后,变成 σ i u i σ_i u_i σiui。
一般地,奇异值的减少特别的快,在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上的比例。即可以用最大的 k k k 个的奇异值和对应的左右奇异向量来近似描述矩阵 A A A ,这样矩阵 A A A 的信息量就被极大的压缩和去除噪声,也为后续大规模的计算提高了效率。
S V D SVD SVD 是后续 P C A PCA PCA 降维原理的基础。
S V D SVD SVD 分解可以广泛地用来求解最小二乘问题,正则化问题,低秩逼近问题,数据压缩问题,文本处理中的分类问题。
用 S V D SVD SVD 可以很容易得到任意矩阵的满秩分解,用满秩分解可以对数据做压缩。
S V D SVD SVD用于推荐系统思路:
提取数据集,一般数据集的行代表用户 u s e r user user,列代表物品 i t e m item item ,其中的值代表用户对物品的打分。
基于 S V D SVD SVD 的优势在于:用户的评分数据是稀疏矩阵,可以用 S V D SVD SVD 将原始数据映射到低维空间中,然后计算物品 i t e m item item 之间的相似度,可以节省计算资源。
整体思路:先找到用户没有评分的物品,然后再经过 S V D SVD SVD “压缩”后的低维空间中,计算未评分物品与其他物品的相似性,得到一个预测打分,再对这些物品的评分从高到低进行排序,返回前 N N N 个物品推荐给用户。
计算复杂度: S V D SVD SVD是一种强大的降维工具,我们可以利用 S V D SVD SVD来逼近矩阵并从中提取重要特征。但奇异值的计算是一个难题,是一个 O ( N 3 ) O(N^3) O(N3)的算法。 m a t l a b matlab matlab 在一秒钟内就可以算出 1000 ∗ 1000 1000 * 1000 1000∗1000 的矩阵的所有奇异值,但当矩阵的规模增长的时候,计算的复杂度呈 3 3 3 次方增长,需要并行计算。
计算开销: S V D SVD SVD 在解大规模的矩阵的时候,一般使用迭代的方法,当矩阵的规模很大时(比如说维度过亿),迭代的次数也可能上亿次,如果使用 M a p R e d u c e MapReduce MapReduce 框架去解,则每次 M a p R e d u c e MapReduce MapReduce 完成的时候,都会涉及到写、读文件的操作。如果利用 S p a r k Spark Spark 计算框架,会比 M a p R e d u c e MapReduce MapReduce 要快很多倍。
通过 numpy
的 linalg
内的 svd
函数可以实现奇异值分解。
numpy.linalg.svd(a, full_matrices=1, compute_uv=1)
a
: 传入的矩阵,大小为 M × N M \times N M×N
full_matrices
: 为 0 或 1,默认为1,这时U
的大小为 ( M , M ) (M, M) (M,M),V
的大小为 ( N , N ) (N, N) (N,N)。否则U
的大小为 ( M , K ) (M, K) (M,K),V
的大小为 ( K , N ) (K, N) (K,N), K = m i n ( M , N ) K = min(M, N) K=min(M,N)
compute_uv
: 0或1,默认为1,表示计算U
,Σ
、V
,为0时只计算Σ
。
from numpy import linalg as la
import numpy as np
A = np.mat([[1,2,3],[4,5,6]])
U, sigma, VT = la.svd(A, full_matrices=1, compute_uv=1)
U
Out[6]:
matrix([[-0.3863177 , -0.92236578],
[-0.92236578, 0.3863177 ]])
sigma
Out[7]: array([9.508032 , 0.77286964])
VT
Out[8]:
matrix([[-0.42866713, -0.56630692, -0.7039467 ],
[ 0.80596391, 0.11238241, -0.58119908],
[ 0.40824829, -0.81649658, 0.40824829]])
P r i n c i p a l C o m p o n e n t A n a l y s i s Principal \; Component \; Analysis PrincipalComponentAnalysis 主成分分析
在PCA中,数据从原来的坐标系转换到新的坐标系,由数据本身决定。转换坐标系时,以方差最大的方向作为坐标轴方向,因为数据的最大方差给出了数据的最重要的信息。第一个新坐标轴选择的是原始数据中方差最大的方法,第二个新坐标轴选择的是与第一个新坐标轴正交且方差次大的方向(这个要在3维以上空间体会)。重复该过程,重复次数为原始数据的特征维数。
通过这种方式获得的新的坐标系,而且大部分方差都包含在前面几个坐标轴中,后面的坐标轴所含的方差几乎为0,。可以忽略余下的坐标轴,只保留前面的几个含有绝不部分方差的坐标轴。事实上,这样也就相当于只保留包含绝大部分方差的维度特征,而忽略包含方差几乎为0的特征维度,也就实现了对数据特征的降维处理。
A l g o r i t h m Algorithm Algorithm:
I n p u t Input Input: n n n 维特征的样本数据集 D D D
O u t p u t Output Output:降到 p p p 维后的样本集 D ’ D’ D’
优点:
缺点:
通过 sklearn.decomposition.PCA
进行 P C A PCA PCA 降维
PCA(n_components=None, copy=True, whiten=False, svd_solver=‘auto’)
n_components
: 指定主成分的个数,及降维后的数据维度,小于一时表示保留多少百分位的方差
copy
:在运行算法时,是否将原始训练数据复制一份,等于True
表示在原始数据的副本上进行运算,原始训练数据的值不会有任何改变,等于Flase
在原始数据上进行运算,原始训练数据的值会改变
Whiten
:白化,使得每个特征具有相同的方差(在PCA中,保留主要的特征来计算决策,因此难免有误差,为了降低误差,通过白化来降低特征值之间的相关性,使其协方差矩阵变为对角矩阵)
svd_solver
: 可选项为{auto,full,arpack,randomized}
,表示解析器计算 S V D SVD SVD 的策略默认,解析器由基于
X.shape
和n_components
的 默认策略选择:如果输入数据大于 500 × 500 500\times500 500×500 且要提取的组件数低于数据最小维数的 80 % 80% 80% ,那么效率更高随机化方法已启用。 否则,计算精确的完整 S V D SVD SVD并随后截断。运行完全完整的 S V D SVD SVD 通过
scipy.linalg.svd
调用标准LAPACK
解算器并通过后处理选择组件运行 S V D SVD SVD截断为
n_components
通过scipy.sparse.linalg.svds
调用ARPACK
解算器。 它严格要求0
通过 H a l k o Halko Halko 等人的方法进行随机 S V D SVD SVD 。
主要属性:
components_
:降维后各主成分方向,并按照各主成分的方差值大小排序
explained_variance_
:降维后各主成分的方差值,方差值越大,越主要
explained_variance_ratio_
:降维后的各主成分的方差值占总方差值的比例,比例越大,则越主要
singular_values_
:奇异值分解得到的前n_components
个中最大的奇异值
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris
X, y = load_iris(return_X_y=True)
pca = PCA(3)
temp1 = pca.fit_transform(X)
print('data Object Shape: ', X.shape)
print('decomposition shape: ', temp1.shape)
print('components: ', pca.components_)
print('explained_variance_ratio_: ', pca.explained_variance_ratio_)
data Object Shape: (150, 4)
decomposition shape: (150, 3)
components: [[ 0.36138659 -0.08452251 0.85667061 0.3582892 ]
[ 0.65658877 0.73016143 -0.17337266 -0.07548102]
[-0.58202985 0.59791083 0.07623608 0.54583143]]
explained_variance_ratio_: [0.92461872 0.05306648 0.01710261]
# 保留 95% 的方差信息
pca = PCA(n_components=.95)
temp2 = pca.fit_transform(X)
print('data Object Shape: ', X.shape)
print('decomposition shape: ', temp2.shape)
print('components: ', pca.components_)
print('explained_variance_ratio_: ', pca.explained_variance_ratio_)
data Object Shape: (150, 4)
decomposition shape: (150, 2)
components: [[ 0.36138659 -0.08452251 0.85667061 0.3582892 ]
[ 0.65658877 0.73016143 -0.17337266 -0.07548102]]
explained_variance_ratio_: [0.92461872 0.05306648]
线性判别分析( L i n e a r D i s c r i m i n a n t A n a l y s i s Linear \; Discriminant \; Analysis LinearDiscriminantAnalysis) ,也叫 F i s h e r Fisher Fisher 线性判别,是模式识别中的经典算法,一种有监督学习算法,同时经常被用来对数据进行降维。相较而言, P C A PCA PCA 算法没有考虑数据的标签(类别),只是把原数据映射到一些方差比较大的方向上而已。
思想:投影后类内距离最小,类间距离最大。
线性判别:将高维的特征投影到低维的最优向量空间,以达到抽取分类信息和压缩特征空间维数的效果,投影后保证原特征样本在新的子空间有最大的类间距离和最小的类内距离,即该方法能使得投影后模式样本的类间散布矩阵最大,同时类内散布矩阵最小。
A l g o r i t h m : Algorithm: Algorithm:
I n p u t : Input: Input: 数据集 D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x m , y m ) } D = \{(x_1, \; y_1), \; (x_2, \; y_2) \; , \dots , (x_m, \; y_m)\} D={(x1,y1),(x2,y2),…,(xm,ym)} ,其中任意样本 x i x_i xi 为 n n n 维向量, y i ∈ R , i = 1 , 2 , … , k y_i \in R, \; i = 1, 2, \dots, k yi∈R,i=1,2,…,k.
O u t p u t : Output: Output: 降维后的数据集 D ’ D’ D’
L D A LDA LDA 算法既可以用来降维,也可以用来分类,主要用于降维,尤其在进行图像识别相关的数据分析时, L D A LDA LDA 是一个有力的工具。
优点:
缺点:
L D A LDA LDA不适合对非高斯分布样本进行降维, P C A PCA PCA 也有这个问题。
L D A LDA LDA 降维最多降到类别数 k − 1 k-1 k−1 的维数,如果我们降维的维度大于 k − 1 k-1 k−1 ,则不能使用 L D A LDA LDA 。当然目前有一些 $LDA $ 的进化版算法可以绕过这个问题
原始数据是 n n n 维的,有 c c c 个类别,降维后一般是到 c − 1 c-1 c−1 维
由于投影矩阵 W W W 是一个利用了样本的类别得到的投影矩阵 ( n ∗ c , 一 般 c < < n ) (n*c,一般c<(n∗c,一般c<<n) ,而的秩最大为 c − 1 c-1 c−1 ,所以最多有 c − 1 c-1 c−1个 非 $0 $ 的特征值,即最多有 c − 1 c-1 c−1 个特征向量。因此它降维的维度 c c c 最大值为 c − 1 c-1 c−1 。L D A LDA LDA 用于分类
一个常见的 L D A LDA LDA 分类基本思想是假设各个类别的样本数据符合高斯分布,这样利用 L D A LDA LDA 进行投影后,可以利用极大似然估计计算各个类别投影数据的均值和方差,进而得到该类别高斯分布的概率密度函数。当一个新的样本到来后,我们可以将它投影,然后将投影后的样本特征分别带入各个类别的高斯分布概率密度函数,计算它属于这个类别的概率,最大的概率对应的类别即为预测类别。
L D A LDA LDA 在样本分类信息依赖方差而不是均值的时候,降维效果不好。
L D A LDA LDA 可能过度拟合数据。
以红酒分类数据集为例:
from sklearn.datasets import load_wine
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
import pandas as pd
df = load_wine(as_frame=True).frame
df
alcohol malic_acid ash alcalinity_of_ash magnesium total_phenols flavanoids nonflavanoid_phenols proanthocyanins color_intensity hue od280/od315_of_diluted_wines proline target
0 14.23 1.71 2.43 15.6 127.0 2.80 3.06 0.28 2.29 5.64 1.04 3.92 1065.0 0
1 13.20 1.78 2.14 11.2 100.0 2.65 2.76 0.26 1.28 4.38 1.05 3.40 1050.0 0
2 13.16 2.36 2.67 18.6 101.0 2.80 3.24 0.30 2.81 5.68 1.03 3.17 1185.0 0
3 14.37 1.95 2.50 16.8 113.0 3.85 3.49 0.24 2.18 7.80 0.86 3.45 1480.0 0
4 13.24 2.59 2.87 21.0 118.0 2.80 2.69 0.39 1.82 4.32 1.04 2.93 735.0 0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
173 13.71 5.65 2.45 20.5 95.0 1.68 0.61 0.52 1.06 7.70 0.64 1.74 740.0 2
174 13.40 3.91 2.48 23.0 102.0 1.80 0.75 0.43 1.41 7.30 0.70 1.56 750.0 2
175 13.27 4.28 2.26 20.0 120.0 1.59 0.69 0.43 1.35 10.20 0.59 1.56 835.0 2
176 13.17 2.59 2.37 20.0 120.0 1.65 0.68 0.53 1.46 9.30 0.60 1.62 840.0 2
177 14.13 4.10 2.74 24.5 96.0 2.05 0.76 0.56 1.35 9.20 0.61 1.60 560.0 2
该数据集共有178个样本,14个特征,我在其中随意选择三个特征进行测试:
import matplotlib.pyplot as plt
temp = df[['alcohol', 'proline', 'total_phenols']]
target = df['target']
ax = plt.gca(projection='3d')
ax.scatter(temp[target == 0]['alcohol'], temp[target == 0]['proline'], temp[target == 0]['total_phenols'], c='b', label='class_1')
ax.scatter(temp[target == 1]['alcohol'], temp[target == 1]['proline'], temp[target == 1]['total_phenols'], c='r', label='class_2')
ax.scatter(temp[target == 2]['alcohol'], temp[target == 2]['proline'], temp[target == 2]['total_phenols'], c='g', label='class_3')
plt.legend()
方便对比,将用 P C A PCA PCA 降维后的图像和 L D A LDA LDA 降维后的图像进行可视化
LDA = LinearDiscriminantAnalysis(n_components=2)
l_data = LDA.fit_transform(temp, target)
PCA = PCA(n_components=2)
p_data = PCA.fit_transform(temp)
plt.subplot(121)
plt.plot(l_data[target==0][:, 0], l_data[target==0][:, 1], 'bo', label='class_0')
plt.plot(l_data[target==1][:, 0], l_data[target==1][:, 1], 'r*', label='class_1')
plt.plot(l_data[target==2][:, 0], l_data[target==2][:, 1], 'g+', label='class_2')
plt.legend()
plt.subplot(122)
plt.plot(p_data[target==0][:, 0], p_data[target==0][:, 1], 'bo', label='class_0')
plt.plot(p_data[target==1][:, 0], p_data[target==1][:, 1], 'r*', label='class_1')
plt.plot(p_data[target==2][:, 0], p_data[target==2][:, 1], 'g+', label='class_2')
plt.legend()
虽然看起来 L D A LDA LDA 分类效果仍有很多杂质,但对比 P C A PCA PCA 效果明显好了许多。
S V D SVD SVD : S V D SVD SVD 分解可以广泛用来求解最小二乘问题,正则化问题,低秩逼近问题,数据压缩问题,文本处理的分类问题。
P C A PCA PCA: 只适用于连续变量
L D A LDA LDA:可以用于降维,分类和图像识别等场景
P C A PCA PCA 与 L D A LDA LDA :
共同点:
不同点: