如何在Apache Spark中计算两个分布式RowMatrix的点积?

时间:2017-09-04 13:48:07

标签: apache-spark linear-algebra distributed-computing apache-spark-mllib matrix-multiplication

Q 成为Spark中的分布式行矩阵,我想计算 Q 的叉积及其转置 Q'

但是,虽然行矩阵确实有 multiply() 方法,但它只能接受本地矩阵作为参数。

代码插图(Scala):

val phi = new RowMatrix(phiRDD)            // phiRDD is an instance of RDD[Vector]
val phiTranspose = transposeRowMatrix(phi) // transposeRowMatrix()
                                           // returns the transpose of a RowMatrix
val crossMat = ?                           // phi * phiTranspose

请注意,我想执行 2 分布式RowMatrix 的点积,而不是具有本地点的分布式产品。

一种解决方案是使用IndexedRowMatrix,如下所示:

val phi = new IndexedRowMatrix(phiRDD)  // phiRDD is an instance of RDD[IndexedRow]
val phiTranspose = transposeMatrix(phi) // transposeMatrix()
                                        // returns the transpose of a Matrix
val crossMat = phi.toBlockMatrix().multiply( phiTranspose.toBlockMatrix()
                                             ).toIndexedRowMatrix()

但是,我想使用行矩阵方法,例如 tallSkinnyQR() ,这意味着我使用crossMat.toRowMatrix()变换为RowMatrix方法:

val crossRowMat = crossMat.toRowMatrix()

最后我可以申请

crossRowMat.tallSkinnyQR()

但是这个过程包括分布式矩阵类型之间的许多转换,根据我从MLlib Programming Guide的理解,这很昂贵:

  

选择正确的格式来存储大型和分布式矩阵非常重要。将分布式矩阵转换为不同的格式可能需要全局混洗,这非常昂贵。

请有人详细说明。

2 个答案:

答案 0 :(得分:2)

只有支持矩阵 - 矩阵乘法的分布式矩阵才是BlockMatrices。你必须相应地转换你的数据 - 人工指数足够好了:

new IndexedRowMatrix(
  rowMatrix.rows.zipWithIndex.map(x => IndexedRow(x._2,  x._1))
).toBlockMatrix match { case m => m.multiply(m.transpose) }

答案 1 :(得分:1)

我使用了这个page上列出的算法,它通过使用向量外积来将乘法问题从点积转移到分布式标量积问题:

  

两个载体之间的外部产物是该标记的标量积   第二个向量与第一个向量中的所有元素,导致   矩阵

我自己创建的行矩阵的乘法函数(可以更优化)就像那样。

def multiplyRowMatrices(m1: RowMatrix, m2: RowMatrix)(implicit ctx: SparkSession): RowMatrix = {

 // Zip m1 columns with m2 rows
val m1Cm2R = transposeRowMatrix(m1).rows.zip(m2.rows)

// Apply scalar product between each entry in m1 vector with m2 row
val scalar = m1Cm2R.map{
case(column:DenseVector,row:DenseVector) => column.toArray.map{
  columnValue => row.toArray.map{
    rowValue => columnValue*rowValue
  }
 }
}

// Add all the resulting matrices point wisely
val sum = scalar.reduce{
case(matrix1,matrix2) => matrix1.zip(matrix2).map{
  case(array1,array2)=> array1.zip(array2).map{
    case(value1,value2)=> value1+value2
  }
 }
}

new RowMatrix(ctx.sparkContext.parallelize(sum.map(array=> Vectors.dense(array))))
}

之后我测试了两种方法 - 我自己的功能和使用块矩阵 - 在一台机器上使用300 * 10矩阵

使用我自己的功能:

val PhiMat = new RowMatrix(phi)
val TphiMat = transposeRowMatrix(PhiMat)
val product = multiplyRowMatrices(PhiMat,TphiMat)

使用矩阵变换:

val MatRow = new RowMatrix(phi)
val MatBlock = new IndexedRowMatrix(MatRow.rows.zipWithIndex.map(x => IndexedRow(x._2,  x._1))).toBlockMatrix()
val TMatBlock = MatBlock.transpose
val productMatBlock = MatBlock.multiply(TMatBlock)
val productMatRow = productMatBlock.toIndexedRowMatrix().toRowMatrix()

第一种方法跨越 1个工作 5个阶段,并且 2s 总计完成。虽然第二种方法跨越<强> 4个工作,三个一个阶段一个阶段两个阶段,并且 0.323s 总共。另外,第二种方法在Shuffle读/写大小方面优于第一种方法。

然而,我仍然对MLlib Programming指南声明感到困惑:

  

选择正确的格式存储大型和非常重要   分布式矩阵。将分布式矩阵转换为不同的矩阵   格式可能需要全局洗牌,这非常昂贵。