一.概述
PCA是一种统计过程,基于降维的设计思路,使用正交变换将一组可能相关的训练数据列集转换为一组线性不相关的数据列集(称为主成分)。一个PCA算法模型,可以将一个高维训练数据集转换到低维空间。
正交变换把数据变换到一个新的坐标系统中,根据设置主成分的个数(必须小于训练数据中的维度),使第一大方差的数据投影在第一个坐标上,这一坐标维度称为第一主成分,后续主成分与此类似。主成分分析经常用于减少数据集的维度(消除数据类似的维度),同时保持数据集中对方差贡献最大的特征。而这些特征才是数据区分的关键所在。
二.代码实践
PCA算法在Spark中使用是非常方便的,只需要创建PCA算法对象即可:
object PCAnalyse { /** * 设置日志级别 */ Logger.getLogger("org").setLevel(Level.WARN) def main(args: Array[String]) { val spark = SparkSession.builder().appName(s"${this.getClass.getSimpleName}") .master("local[2]") .getOrCreate() val data = Array( Vectors.dense(5.0, 1.0, 0.0, 7.0, 0.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) ) val df = spark.createDataFrame(data.map(Tuple1.apply)).toDF("features") val pca = new PCA() .setInputCol("features") .setOutputCol("pcaFeatures") .setK(3) .fit(df) val result = pca.transform(df) result.show(false) }}
执行结果:
因为设置的主成分为3,所以会把训练数据的维度降为3。通过对转换后的数据观察可知,其和原始数据差别较大,无法一眼看出生成的主成分来自于那几列。而且要注意的是,主成分的大小设置必须小于训练数据的维度,否则就会抛出异常:
因此,需要对源码进行分析,来进一步深入了解其内部是如何进行转换的。
三.源码分析
进入Spark源码目录org.apache.spark.ml.feature下,可以看到PCA算法实现的源码:
从源码中可以看出,主成分的默认值为0,因此使用PCA时,就必须设置主成分的大小。具体的大小要根据训练数据维度的大小确定物理上限,根据后续算法运算和业务要求进行进一步判断确定K的大小。如果只能确定一个范围,无法确定最优值,可以使用试探法,逐步确定K值。
相关配置设置完成后,调用fit函数进行模型训练,进入feature目录下的PCA代码:
具体实现在mllib目录下的PCA对象中:
具体计算实现:
根据代码实践中的代码可知,PCA模型训练之后调用transform()函数:
在feature目录下的PCA中的transform函数调用了mllib目录下的PCA具体代码实现:
整个代码逻辑从feature下的PCA对象开始,先跳转到mllib下的PCA对象,后续使用mllib.linalg.distributed下的RowMatrix对象实现计算,后续在返回到mllib和feature下的PCA对象,最后返回到代码实践中。