MLlib学习——降维
降维(Dimensionality Reduction) 是机器学习中的一种重要的特征处理手段,它可以减少计算过程中考虑到的随机变量(即特征)的个数,其被广泛应用于各种机器学习问题中,用于消除噪声、对抗数据稀疏问题。它在尽可能维持原始数据的内在结构的前提下,从原始和噪声特征中提取潜在特征或在保持结构的同时压缩数据得到一组描述原数据的,低维度的隐式特征(或称主要特征)。
Spark MLlib为org.apache.spark.mllib.linalg.distributed.RowMatrix
类上的降维提供支持。
MLlib机器学习库提供了两个常用的降维方法:
- 奇异值分解(Singular Value Decomposition,SVD)
- 主成分分析(Principal Component Analysis,PCA)
一、奇异值分解(SVD)
MLlib内置的奇异值分解功能位于org.apache.spark.mllib.linalg
包下的RowMatrix
和IndexedRowMatrix
类中,所以,我们必须先通过已有数据创建出相应矩阵类型的对象,然后调用该类的成员方法来进行SVD分解:
%spark import org.apache.spark.mllib.linalg.Matrix import org.apache.spark.mllib.linalg.SingularValueDecomposition import org.apache.spark.mllib.linalg.Vector import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.linalg.distributed.RowMatrix // 创建样本矩阵,由一个稀疏向量两个稠密向量组成 val data = Array( Vectors.sparse(5, Seq((1, 1.0), (3, 7.0))), Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0), Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)) // 将样本矩阵生成RDD val rows = sc.parallelize(data) // 创建行矩阵 val mat: RowMatrix = new RowMatrix(rows) // 计算前5个奇异值和相应的奇异向量。 val svd: SingularValueDecomposition[RowMatrix, Matrix] = mat.computeSVD(5, computeU = true) // val U: RowMatrix = svd.U // U因子是一个行矩阵。 val s: Vector = svd.s // s奇异值存储在局部稠密向量中。 val V: Matrix = svd.V // V因子是一个局部稠密矩阵。
其中,V是右奇异向量,U是左奇异向量,s是奇异值。在实际运用中,只需要V
和S
两个成员,即可通过矩阵计算达到降维的效果。
如果需要获得U
成员,可以在进行SVD分解时,指定computeU
参数,令其等于True
,即可在分解后的svd
对象中拿到U
成员。
二、主成分分析(PCA)
主成分分析(PCA) 是一种对数据进行旋转变换的统计学方法,其本质是在线性空间中进行一个基变换,使得变换后的数据投影在一组新的“坐标轴”上的方差最大化,随后,裁剪掉变换后方差很小的“坐标轴”,剩下的新“坐标轴”即被称为 主成分(Principal Component) ,它们可以在一个较低维度的子空间中尽可能地表示原有数据的性质。主成分分析被广泛应用在各种统计学、机器学习问题中,是最常见的降维方法之一。PCA有许多具体的实现方法,可以通过计算协方差矩阵,甚至是通过上文提到的SVD分解来进行PCA变换。
MLlib提供了两种进行PCA变换的方法,第一种与上文提到的SVD分解类似,位于org.apache.spark.mllib.linalg
包下的RowMatrix
中,PCA变换示例:
%spark import org.apache.spark.mllib.linalg.Matrix import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.linalg.distributed.RowMatrix val data = Array( Vectors.sparse(5, Seq((1, 1.0), (3, 7.0))), Vectors.dense(2.0, 0.0, 3.0, 4.0, 5.0), Vectors.dense(4.0, 0.0, 0.0, 6.0, 7.0)) // 生成RDD val rows = sc.parallelize(data) // 生成行矩阵 val mat: RowMatrix = new RowMatrix(rows) // 计算前4个主成分 // 将主成分存储到本地矩阵pc中 val pc: Matrix = mat.computePrincipalComponents(4) // 将行矩阵投影到由前4个主成分构成的线性空间。 val projected: RowMatrix = mat.multiply(pc)
输出:
pc: org.apache.spark.mllib.linalg.Matrix = -0.44859172075072673 -0.28423808214073987 0.08344545257592471 0.8364102009456849
0.13301985745398526 -0.05621155904253121 0.044239792581370035 0.17224337841622106
-0.1252315635978212 0.7636264774662965 -0.578071228563837 0.2554154886635869
0.21650756651919933 -0.5652958773533949 -0.7955405062786798 4.858121429822393E-5
-0.8476512931126826 -0.11560340501314653 -0.1550117891430013 -0.4533355491646027
注意:其中每一列代表一个主成分(新坐标轴),每一行代表原有的一个特征。
除了矩阵类内置的PCA变换外,MLlib还提供了一种“模型式”的PCA变换实现,它位于org.apache.spark.mllib.feature
包下的PCA
类,它可以接受RDD[Vectors]
作为参数,进行PCA变换。
该方法特别适用于原始数据是LabeledPoint
类型的情况,只需取出LabeledPoint
的feature
成员(它是RDD[Vector]
类型),对其做PCA操作后再放回,即可在不影响原有标签情况下进行PCA变换。
%spark import org.apache.spark.mllib.feature.PCA import org.apache.spark.mllib.linalg.Vectors import org.apache.spark.mllib.regression.LabeledPoint import org.apache.spark.rdd.RDD val data: RDD[LabeledPoint] = sc.parallelize(Seq( new LabeledPoint(0, Vectors.dense(1, 0, 0, 0, 1)), new LabeledPoint(1, Vectors.dense(1, 1, 0, 1, 0)), new LabeledPoint(1, Vectors.dense(1, 1, 0, 0, 0)), new LabeledPoint(0, Vectors.dense(1, 0, 0, 0, 0)), new LabeledPoint(1, Vectors.dense(1, 1, 0, 0, 0)))) // 计算前5个主成分。创建一个PCA
类的对象,在构造器中给定主成分个数为3,并调用其fit
方法来生成一个PCAModel
类的对象pca
,该对象保存了对应的主成分矩阵. val pca = new PCA(3).fit(data.map(_.features)) // 将向量投影到由前3个主成分构成的线性空间,保留标签.对于LabeledPoint
型的数据来说,可使用map
算子对每一条数据进行处理,将features
成员替换成PCA变换后的特征即可 val projected = data.map(p => p.copy(features = pca.transform(p.features)))