在进行具体算法模型剖析之前,首先要清楚MLlib采用的数据结构,spark官方文档也提供了英文的数据结构介绍,地址如下:
http://spark.apache.org/docs/latest/mllib-data-types.html
MLlib支持单机local vectors 和 matrices以及分布式矩阵。其中local vectors 和 matrices是一种用于公共接口的简单数据结构。内部的数学运算是由Breeze 和 jblas提供的。在有监督学习中使用的某个训练样例在MLlib中被称为一个“labeled point”。
1、local vector
MLlib的本地向量主要分为Dense和Sparse两种,前者是用来保存稠密向量,后者是用来保存稀疏向量。例如,对于向量 (1.0, 0.0, 3.0) , [1.0, 0.0, 3.0] 代表稠密向量,(3, [0, 2], [1.0, 3.0])代表稀疏向量,其中3代表的是总长度,0,2代表的是非零元素的位置。
其创建方式主要有三种,以下为其Scala代码:
import org.apache.spark.mllib.linalg.{Vector, Vectors}
// 创建稠密向量 (1.0, 0.0, 3.0).
val dv: Vector = Vectors.dense(1.0, 0.0, 3.0)
// 创建稀疏向量 (1.0, 0.0, 3.0)
val sv1: Vector = Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0))
// 创建稀疏向量 (1.0, 0.0, 3.0)
val sv2: Vector = Vectors.sparse(3, Seq((0, 1.0), (2, 3.0)))
需要注意的是,Scala默认会导入scala.collection.immutable.Vector包,所以必须手动导入org.apache.spark.mllib.linalg.Vector包才能使用MLlib的vector包。
2、Labeled point
一个向量标签是一个带有标签的紧密或稀疏的local vector。在MLlib中,labeled points被用于有监督学习,我们使用double来存储标签,因此在分类和回归中都可以使用,在二值分类中它可以是0或1,在多值分类中则是从0开始的一个序列,如0,1,2,3······
创建向量标签的Scala代码如下:
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.regression.LabeledPoint
// 创建一个正值标签和一个密集向量
val pos = LabeledPoint(1.0, Vectors.dense(1.0, 0.0, 3.0))
// 创建一个负值标签和一个稀疏向量
val neg = LabeledPoint(0.0, Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0)))
在实践中稀疏训练数据是十分常见的,MLlib支持从LIBSVM格式的文件中直接读取训练数据,LIBSVM格式是LIBSVM 和LIBLINEAR的默认数据格式,这种格式每行代表一个稀疏属性的标签向量,格式如下:
label index1:value1 index2:value2 ...
按照这种格式读入标签的方法如下:
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.util.MLUtils
import org.apache.spark.rdd.RDD
val examples: RDD[LabeledPoint] = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt")
3、Local matrix
本地矩阵具有整型的行列下标和double型的值,MLlib支持密集向量
可以被表示为一维的array [1.0, 3.0, 5.0, 2.0, 4.0, 6.0] ,矩阵的大小为 (3, 2)。
Scala创建矩阵的代码如下;
import org.apache.spark.mllib.linalg.{Matrix, Matrices}
// 创建密集矩阵((1.0, 2.0), (3.0, 4.0), (5.0, 6.0))
val dm: Matrix = Matrices.dense(3, 2, Array(1.0, 3.0, 5.0, 2.0, 4.0, 6.0))
4、分布式矩阵
分布式矩阵的行列索引是long类型的,数值是double型,它存储在一个或多个RDD中,选择合适的格式存储大型分布式矩阵非常重要。目前MLlib提供了三种类型的分布式矩阵。
最基本的类型叫做RowMatrix,它没有有意义的行索引,每行是一个本地向量,如果假设列的数量不是非常大,在单个节点上可以被存储和处理。 IndexedRowMatrix 类似于前者,但是可以按照行来索引。CoordinateMatrix是一个coordinate list (COO)格式的分布式矩阵,适用于特别稀疏的矩阵。
①RowMatrix
RowMatrix是一个行索引的分布式矩阵,但是它的行索引并没有实际意义。每行都是一个本地向量,因为每行都被一个本地向量代表,所以列的数量收到整数最大值的限制,但是实际操作中应该远小于上限值。
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.mllib.linalg.distributed.RowMatrix
val rows: RDD[Vector] = ... // an RDD of local vectors
// Create a RowMatrix from an RDD[Vector].
val mat: RowMatrix = new RowMatrix(rows)
// Get its size.
val m = mat.numRows()
val n = mat.numCols()
②IndexedRowMatrix
IndexedRowMatrix和RowMatrix类似,但是行索引有其实际意义。每一行被它的索引和一个本地向量表示。
import org.apache.spark.mllib.linalg.distributed.{IndexedRow, IndexedRowMatrix, RowMatrix}
val rows: RDD[IndexedRow] = ... // an RDD of indexed rows
// Create an IndexedRowMatrix from an RDD[IndexedRow].
val mat: IndexedRowMatrix = new IndexedRowMatrix(rows)
// Get its size.
val m = mat.numRows()
val n = mat.numCols()
// Drop its row indices.
val rowMat: RowMatrix = mat.toRowMatrix()
③CoordinateMatrix
CoordinateMatrix的每一个元素都是一个元组 (i: Long, j: Long, value: Double),i是行,j是列,value是值。只用于特别稀疏的矩阵。
import org.apache.spark.mllib.linalg.distributed.{CoordinateMatrix, MatrixEntry}
val entries: RDD[MatrixEntry] = ... // an RDD of matrix entries
// Create a CoordinateMatrix from an RDD[MatrixEntry].
val mat: CoordinateMatrix = new CoordinateMatrix(entries)
// Get its size.
val m = mat.numRows()
val n = mat.numCols()
// Convert it to an IndexRowMatrix whose rows are sparse vectors.
val indexedRowMatrix = mat.toIndexedRowMatrix()
最后介绍BlockMatrix
BlockMatrix是一个元组((Int, Int), Matrix),其中 (Int, Int)是块的下标,Matrix是给定下标rowsPerBlock x colsPerBlock的子矩阵。BlockMatrix支持add 和 multiply方法和其他矩阵进行运算。还有一个validate方法用于检查BlockMatrix创建的是否合适。
BlockMatrix 一般很容易通过 toBlockMatrix方法从IndexedRowMatrix 或 CoordinateMatrix变换出来,toBlockMatrix方法默认创建一个 1024 x 1024大小的矩阵。用户可以通过toBlockMatrix(rowsPerBlock, colsPerBlock)来自行定义大小。
import org.apache.spark.mllib.linalg.distributed.{BlockMatrix, CoordinateMatrix, MatrixEntry}
val entries: RDD[MatrixEntry] = ... // an RDD of (i, j, v) matrix entries
// Create a CoordinateMatrix from an RDD[MatrixEntry].
val coordMat: CoordinateMatrix = new CoordinateMatrix(entries)
// Transform the CoordinateMatrix to a BlockMatrix
val matA: BlockMatrix = coordMat.toBlockMatrix().cache()
// Validate whether the BlockMatrix is set up properly. Throws an Exception when it is not valid.
// Nothing happens if it is valid.
matA.validate()
// Calculate A^T A.
val ata = matA.transpose.multiply(matA)