MLlib学习——基本数据类型
数据类型——基于RDD的API
- 本地矢量
- 标记点
- 本地矩阵
- 分布式矩阵
- RowMatrix(行矩阵)
- IndexedRowMatrix(索引行矩阵)
- CoordinateMatrix(坐标矩阵)
- BlockMatrix(块矩阵)
MLlib 支持存储在单台机器上的本地向量和矩阵,以及由一个或多个 RDD 支持的分布式矩阵。本地向量和本地矩阵是用作公共接口的简单数据模型。有监督学习中使用的训练示例在MLlib中称为"标记点"。
一、本地矢量(Local Vector)
MLlib 支持两种类型的局部向量:密集和稀疏。密集向量由表示其条目值的双精度数组支持,而稀疏向量由两个并行数组支持:索引和值。例如:向量(1.0, 0.0, 3.0)
- 稠密向量的表现形式:[1.0, 0.0, 3.0];
- 稀疏向量的表现形式:(3,[0,2],[1.0,3.0]),其中3表示向量长度,[0,2]是数组非0向量的索引,而[1.0,3.0]是索引对应的向量值;
本地矢量都以 org.apache.spark.mllib.linalg.Vector 为
基类,提供了两个实现:DenseVector
和SparseVector
,分别对应稠密向量和稀疏向量。
%spark
//使用Vectors
工具类下定义的工厂方法来创建本地向量
import org.apache.spark.mllib.linalg.{Vector, Vectors}
//创建一个本地稠密向量
val dv:Vector = Vectors.dense(2.0,0.0,8.0)
//创建一个本地稀疏向量
val sv1:Vector = Vectors.sparse(3,Array(0,2),Array(2.0,8.0))
//另一种创建本地稀疏向量的方法,参数由长度和一个键值对组成(index,elem),每个键值对都是一个非0的向量
val sv2:Vector = Vectors.sparse(3,Seq((0,2.0),(2,8.0)))
注意:Scala会默认引入 scala.collection.immutable.Vector 这个包,所以必须要显示引org.apache.spark.mllib.linalg.Vector
来使用MLlib提供的向量类型。
二、标记点(Labeled Point)
标记点是(Label/Response)的本地向量,可以是稠密或稀疏的。在 MLlib 中,标记点用于监督学习算法。由于标签是使用双精度浮点型来存储的,因此可以在回归和分类问题中使用标记点。对于二元分类问题,标签应为(负/0)或(正/1);对于多类分类,标签应是从零开始的类索引:0,1,2 。。。
标记点的实现类是:org.apache.spark.mllib.regression.LabeledPoint
%spark
//创建一个被标记为1.0的稠密向量标记点,创建一个被标记为0的稀疏向量
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
val temp1 = LabeledPoint(1.0,Vectors.dense(2.0,0.0,8.0))
val temp2 = LabeledPoint(0.0,Vectors.sparse(3,Seq((0,2.0),(2,8.0))))
在实践中,稀疏的训练数据非常普遍。MLlib 支持读取以格式存储的训练示例,这是LIBSVM 和LIBLINEAR使用的默认格式。它是一种文本格式,其中每行使用以下格式表示标记的稀疏特征向量:LIBSVM
label index1:value1 index2:value2 ...
其中 label
是该样本点的标签值,一系列 index:value
对则代表了该样本向量中所有非零元素的索引和元素值。这里需要特别注意的是,index是以1开始并递增的。
MLlib在org.apache.spark.mllib.util.MLUtils
工具类中提供了读取LIBSVM格式的方法loadLibSVMFile,示例如下:
%spark import org.apache.spark.mllib.regression.LabeledPoint import org.apache.spark.mllib.util.MLUtils import org.apache.spark.rdd.RDD //使用MLUtils类中的loadLibSVMFile方法读取LIBSVM格式的数据 //sample_libsvm_data.txt为spark自带的一个示例 val examples: RDD[LabeledPoint] = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
三、本地矩阵(Local Matrix)
本地矩阵具有整数类型的行和列索引以及存储在单台计算机上的双精度浮点型值。MLlib 支持稠密矩阵(其条目值按列主序存储在单个双精度数组中)和稀疏矩阵(其非零条目值以压缩稀疏列 (CSC) 格式按列主序存储)。本地矩阵的基类是org.apache.spark.mllib.linalg.Matrix
,DenseMatrix
和SparseMatrix
均是它的实现类,和本地向量类似,MLlib也为本地矩阵提供了相应的工具类Matrices
,调用工厂方法即可创建实例:
%spark import org.apache.spark.mllib.linalg.{Matrix,Matrices} //创建一个3行2列的稠密矩阵,注意是列先序的 val dm:Matrix = Matrices.dense(3,2,Array(1.0,3.0,5.0,2.0,4.0,6.0)) 输出: dm: org.apache.spark.mllib.linalg.Matrix = 1.0 2.0 3.0 4.0 5.0 6.0
同样创建稀疏矩阵:
// 创建一个3行2列的稀疏矩阵[ [9.0,0.0], [0.0,8.0], [0.0,6.0]] // 第一个数组参数表示列指针,即每一列元素的开始索引值 // 第二个数组参数表示行索引,即对应的元素是属于哪一行 // 第三个数组即是按列先序排列的所有非零元素,通过列指针和行索引即可判断每个元素所在的位置 val sm:Matrix = Matrices.sparse(3,2,Array(0,1,3),Array(0,2,1),Array(9,6,8)) 输出: sm: org.apache.spark.mllib.linalg.Matrix = 3 x 2 CSCMatrix (0,0) 9.0 (2,1) 6.0 (1,1) 8.0
四、分布式矩阵(Distributed Matrix)
分布式矩阵由长整型的行列索引值和双精度浮点型的元素值组成。它可以分布式地存储在一个或多个RDD上,MLlib提供了三种分布式矩阵的存储方案:行矩阵RowMatrix
,索引行矩阵IndexedRowMatrix
、坐标矩阵CoordinateMatrix
和分块矩阵BlockMatrix
。它们都属于org.apache.spark.mllib.linalg.distributed
包。
1、行矩阵(RowMatrix)
行矩阵RowMatrix
是最基础的分布式矩阵类型。每行是一个本地向量,行索引无实际意义(即无法直接使用)。数据存储在一个由行组成的RDD中,其中每一行都使用一个本地向量来进行存储。由于行是通过本地向量来实现的,故列数(即行的维度)被限制在普通整型(integer
)的范围内。在实际使用时,由于单机处理本地向量的存储和通信代价,行维度更是需要被控制在一个更小的范围之内。RowMatrix
可通过一个RDD[Vector]
的实例来创建,如下代码所示:
%spark import org.apache.spark.rdd.RDD import org.apache.spark.mllib.linalg.{Vector,Vectors} import org.apache.spark.mllib.linalg.distributed.RowMatrix //首先创建两个本地稠密向量 val dv1:Vector = Vectors.dense(1.0,2.0,3.0) val dv2:Vector = Vectors.dense(2.0,3.0,4.0) val rows:RDD[Vector] = sc.parallelize(Array(dv1,dv2)) //通过RDD[Vector]创建一个行矩阵 val mat:RowMatrix = new RowMatrix(rows) //使用numRows和numCols方法分别输出行矩阵的行、列 mat.numRows() mat.numCols() //打印行矩阵 mat.rows.foreach(println)
在获得RowMatrix
的实例后,我们可以通过其自带的computeColumnSummaryStatistics()
方法获取该矩阵的一些统计摘要信息,并可以对其进行QR分解,SVD分解和PCA分解。 统计摘要信息的获取如下代码段所示(接上代码段):
//通过computeColumnSummaryStatistics()方法获取统计摘要 val summary = mat.computeColumnSummaryStatistics() //根据统计信息获取行数 summary.count //获取最大向量 summary.max //获取方差向量 summary.variance //平均向量 summary.mean //L1范数向量 summary.normL1
2、索引行矩阵(IndexedRowMatrix)
索引行矩阵IndexedRowMatrix
与RowMatrix
相似,但它的每一行都带有一个有意义的行索引值,这个索引值可以被用来识别不同行,或是进行诸如join之类的操作。其数据存储在一个由IndexedRow
组成的RDD里,即每一行都是一个带长整型索引的本地向量。IndexedRowMatrix
的实例可以通过RDD[IndexedRow]
实例来创建。
%spark import org.apache.spark.rdd.RDD import org.apache.spark.mllib.linalg.{Vector,Vectors} import org.apache.spark.mllib.linalg.distributed.{IndexedRow, IndexedRowMatrix} //1、首先创建两个本地稠密向量 val dv1:Vector = Vectors.dense(1.0,2.0,3.0) val dv2:Vector = Vectors.dense(2.0,3.0,4.0) // 2、通过本地向量dv1 dv2来创建对应的IndexedRow // 在创建时可以给定行的索引值,如这里给dv1的向量赋索引值1,dv2赋索引值2 val idxr1 = IndexedRow(1,dv1) val idxr2 = IndexedRow(2,dv2) //3、 通过IndexedRow创建RDD[IndexedRow] val idxrows = sc.parallelize(Array(idxr1,idxr2)) //4、 通过RDD[IndexedRow]创建一个索引行矩阵 val idxmat: IndexedRowMatrix = new IndexedRowMatrix(idxrows) //5、 打印输出索引行矩阵 idxmat.rows.foreach(println)
3、坐标矩阵(CoordinateMatrix)
坐标矩阵CoordinateMatrix
是一个基于矩阵项构成的RDD的分布式矩阵。每一个矩阵项MatrixEntry
都是一个三元组:(i: Long, j: Long, value: Double)
,其中i
是行索引,j
是列索引,value
是该位置的值。坐标矩阵一般在矩阵的两个维度都很大,且矩阵非常稀疏的时候使用。
CoordinateMatrix
实例可通过RDD[MatrixEntry]
实例来创建,其中每一个矩阵项都是一个(rowIndex, colIndex, elem)的三元组:
%spark import org.apache.spark.mllib.linalg.distributed.{CoordinateMatrix, MatrixEntry} //1、创建两个矩阵项,每个矩阵项由索引和值构成三元组 val ent1 = new MatrixEntry(0,1,0.5) val ent2 = new MatrixEntry(2,2,1.8) //2、创建RDD[MatrixEntry] val entries : RDD[MatrixEntry] = sc.parallelize(Array(ent1,ent2)) //3、通过RDD[MatrixEntry]创建一个坐标矩阵 val coordMat: CoordinateMatrix = new CoordinateMatrix(entries) //4、打印 coordMat.entries.foreach(println)
坐标矩阵可以通过transpose()
方法对矩阵进行转置操作,并可以通过自带的toIndexedRowMatrix()
方法转换成索引行矩阵IndexedRowMatrix
。
//5、将coordMat进行转置 val transMat: CoordinateMatrix = coordMat.transpose() //打印输出 transMat.entries.foreach(println) //6、将坐标矩阵转换成一个索引行矩阵 val indexedRowMatrix = transMat.toIndexedRowMatrix() indexedRowMatrix.rows.foreach(println)
4、分块矩阵(BlockMatrix)
分块矩阵是基于矩阵块MatrixBlock
构成的RDD的分布式矩阵,其中每一个矩阵块MatrixBlock
都是一个元组((Int, Int), Matrix)
,其中(Int, Int)
是块的索引,而Matrix
则是在对应位置的子矩阵(sub-matrix),其尺寸由rowsPerBlock
和colsPerBlock
决定,默认值均为1024。分块矩阵支持和另一个分块矩阵进行加法操作和乘法操作,并提供了一个支持方法validate()
来确认分块矩阵是否创建成功。
分块矩阵可由索引行矩阵IndexedRowMatrix
或坐标矩阵CoordinateMatrix
调用toBlockMatrix()
方法来进行转换,该方法将矩阵划分成尺寸默认为1024x1024的分块,可以在调用toBlockMatrix(rowsPerBlock, colsPerBlock)
方法时传入参数来调整分块的尺寸。
%spark import org.apache.spark.rdd.RDD import org.apache.spark.mllib.linalg.distributed.{CoordinateMatrix, MatrixEntry} import org.apache.spark.mllib.linalg.distributed.BlockMatrix //1、 创建8个矩阵项,每一个矩阵项都是由索引和值构成的三元组 val ent1 = new MatrixEntry(0,0,1) val ent2 = new MatrixEntry(1,1,1) val ent3 = new MatrixEntry(2,0,-1) val ent4 = new MatrixEntry(2,1,2) val ent5 = new MatrixEntry(2,2,1) val ent6 = new MatrixEntry(3,0,1) val ent7 = new MatrixEntry(3,1,1) val ent8 = new MatrixEntry(3,3,1) //2、 创建RDD[MatrixEntry] val entries : RDD[MatrixEntry] = sc.parallelize(Array(ent1,ent2,ent3,ent4,ent5,ent6,ent7,ent8)) //3、 通过RDD[MatrixEntry]创建一个坐标矩阵 val coordMat: CoordinateMatrix = new CoordinateMatrix(entries) //4、 将坐标矩阵转换成2x2的分块矩阵并存储,尺寸通过参数传入 val matA: BlockMatrix = coordMat.toBlockMatrix(2,2).cache() //5、 可以用validate()方法判断是否分块成功 matA.validate() //6、构建成功后,可通过toLocalMatrix转换成本地矩阵,并查看其分块情况 matA.toLocalMatrix 输出: res6: org.apache.spark.mllib.linalg.Matrix = 1.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 -1.0 2.0 1.0 0.0 1.0 1.0 0.0 1.0
//查看其分块情况 matA.numColBlocks matA.numRowBlocks // 计算矩阵A和其转置矩阵的积矩阵 val ata = matA.transpose.multiply(matA) //转换为本地矩阵查看 ata.toLocalMatrix
输出: ata: org.apache.spark.mllib.linalg.distributed.BlockMatrix = org.apache.spark.mllib.linalg.distributed.BlockMatrix@37b6f828 res7: org.apache.spark.mllib.linalg.Matrix = 3.0 -1.0 -1.0 1.0 -1.0 6.0 2.0 1.0 -1.0 2.0 1.0 0.0 1.0 1.0 0.0 1.0
分块矩阵BlockMatrix
将矩阵分成一系列矩阵块,底层由矩阵块构成的RDD来进行数据存储。值得指出的是,用于生成分布式矩阵的底层RDD必须是已经确定(Deterministic)的,因为矩阵的尺寸将被存储下来,所以使用未确定的RDD将会导致错误。而且,不同类型的分布式矩阵之间的转换需要进行一个全局的shuffle操作,非常耗费资源。所以,根据数据本身的性质和应用需求来选取恰当的分布式矩阵存储类型是非常重要的。
2、标记点