奇异值分解(SVD)
赖永炫   Tue Dec 27 2016 22:24:36 GMT+0800 (中国标准时间) [ 技术 ]     浏览次数:16928
版权声明: 本文发自http://mocom.xmu.edu.cn,为 赖永炫 老师的个人博文,文章仅代表个人观点。无需授权即可转载,转载时请务必注明作者。

返回 [Spark MLlib入门教程](http://mocom.xmu.edu.cn/article/show/5858ab782b2730e00d70fa08/0/1) ## MLlib中的特征降维方法 --- **降维(Dimensionality Reduction)** 是机器学习中的一种重要的特征处理手段,它可以减少计算过程中考虑到的随机变量(即特征)的个数,其被广泛应用于各种机器学习问题中,用于消除噪声、对抗数据稀疏问题。它在尽可能维持原始数据的内在结构的前提下,得到一组描述原数据的,低维度的隐式特征(或称主要特征)。 MLlib机器学习库提供了两个常用的降维方法:**奇异值分解(Singular Value Decomposition,SVD)** 和 **主成分分析(Principal Component Analysis,PCA)**,下面我们将通过实例介绍其具体的使用方法。 ## 一、奇异值分解(SVD) ## 1、概念介绍 奇异值分解(SVD)** 来源于代数学中的矩阵分解问题,对于一个方阵来说,我们可以利用矩阵特征值和特征向量的特殊性质(矩阵点乘特征向量等于特征值数乘特征向量),通过求特征值与特征向量来达到矩阵分解的效果: $$A = Q\Sigma Q^{-1} $$ 这里,$Q$是由特征向量组成的矩阵,而 $\Sigma$是特征值降序排列构成的一个对角矩阵(对角线上每个值是一个特征值,按降序排列,其他值为0),特征值的数值表示对应的特征的重要性。 在很多情况下,最大的一小部分特征值的和即可以约等于所有特征值的和,而通过矩阵分解的降维就是通过在$Q$、$\Sigma$中删去那些比较小的特征值及其对应的特征向量,使用一小部分的特征值和特征向量来描述整个矩阵,从而达到降维的效果。 但是,实际问题中大多数矩阵是以奇异矩阵形式,而不是方阵的形式出现的,奇异值分解是特征值分解在奇异矩阵上的推广形式,它将一个维度为$m\times n$奇异矩阵$A$分解成三个部分 : $$A = U\Sigma V^{T}$$ 其中$U$、$V$是两个正交矩阵,其中的每一行(每一列)分别被称为 **左奇异向量** 和 **右奇异向量**,他们和 $\Sigma$中对角线上的奇异值相对应,通常情况下我们只需要取一个较小的值$k$,保留前$k$个奇异向量和奇异值即可,其中$U$的维度是$m\times k$、$V$的维度是$n \times k$、$\Sigma$是一个$k \times k$的方阵,从而达到降维效果。 ## 2、SVD变换的例子 Mllib内置的奇异值分解功能位于`org.apache.spark.mllib.linalg`包下的`RowMatrix`和`IndexedRowMatrix`类中,所以,我们必须先通过已有数据创建出相应矩阵类型的对象,然后调用该类的成员方法来进行SVD分解,这里以`RowMatrix`为例: 首先,引入需要的类: ```scala import org.apache.spark.mllib.linalg.distributed.RowMatrix ``` 准备好一个矩阵,这里我们采用一个简单的文件`a.mat`来存储一个尺寸为(4,9)的矩阵,其内容如下: ``` 1 2 3 4 5 6 7 8 9 5 6 7 8 9 0 8 6 7 9 0 8 7 1 4 3 2 1 6 4 2 1 3 4 2 1 5 ``` 随后,将该文本文件读入成`RDD[Vector]`,并转换成`RowMatrix`,即可调用`RowMatrix`自带的`computeSVD`方法计算分解结果,这一结果保存在类型为`SingularValueDecomposition`的`svd`对象中: ```scala scala> val data = sc.textFile("a.mat").map(_.split(" ").map(_.toDouble)).map(line => Vectors.dense(line)) data: org.apache.spark.rdd.RDD[org.apache.spark.mllib.linalg.Vector] = MapPartitionsRDD[3] at map at :31 //通过RDD[Vectors]创建行矩阵 scala> val rm = new RowMatrix(data) rm: org.apache.spark.mllib.linalg.distributed.RowMatrix = org.apache.spark.mllib.linalg.distributed.RowMatrix@4397952a //保留前3个奇异值 scala> val svd = rm.computeSVD(3) svd: org.apache.spark.mllib.linalg.SingularValueDecomposition[o.... ``` 通过访问`svd`对象的`V`、`s`、`U`成员分别拿到进行SVD分解后的右奇异矩阵、奇异值向量和左奇异矩阵: ```scala scala> svd.s res7: org.apache.spark.mllib.linalg.Vector = [28.741265581939565,10.847941223452608,7.089519467626695] scala> svd.V res8: org.apache.spark.mllib.linalg.Matrix = -0.32908987300830383 0.6309429972945555 0.16077051991193514 -0.2208243332000108 -0.1315794105679425 -0.2368641953308101 -0.35540818799208057 0.39958899365222394 -0.147099615168733 -0.37221718676772064 0.2541945113699779 -0.25918656625268804 -0.3499773046239524 -0.24670052066546988 -0.34607608172732196 -0.21080978995485605 0.036424486072344636 0.7867152486535043 -0.38111806017302313 -0.1925222521055529 -0.09403561250768909 -0.32751631238613577 -0.3056795887065441 0.09922623079118417 -0.3982876638452927 -0.40941282445850646 0.26805622896042314 scala> svd.U res9: org.apache.spark.mllib.linalg.distributed.RowMatrix = null ``` 这里可以看到,由于限定了取前三个奇异值,所以奇异值向量`s`包含有三个从大到小排列的奇异值,而右奇异矩阵`V`中的每一列都代表了对应的右奇异向量。`U`成员得到的是一个`null`值,这是因为在实际运用中,只需要`V`和`S`两个成员,即可通过矩阵计算达到降维的效果,其具体原理可以参看这篇博文:[机器学习中的数学(5)-强大的矩阵奇异值分解(SVD)及其应用](http://www.cnblogs.com/LeftNotEasy/archive/2011/01/19/svd-and-applications.html),这里不再赘述。 如果需要获得`U`成员,可以在进行SVD分解时,指定`computeU`参数,令其等于`True`,即可在分解后的`svd`对象中拿到`U`成员,如下文所示: ```scala scala> val svd = rm.computeSVD(3, computeU = true) svd: org.apache.spark.mllib.linalg.SingularValueDecomposition[o ```


自动标签  : 分解   SVD   奇异   矩阵   奇异值   特征值   特征向量   奇异值分解   降维   可以   特征   机器学习   方法   降维方法    

更多 [ 技术 ] 文章

请先 登录, 查看相关评论.