Spark上矩阵运算库(四)基本API搭建完毕

来源:互联网 发布:个人软件开发合同书 编辑:程序博客网 时间:2024/05/18 09:09

 

重写MLlib矩阵相关API

上周工作时所用到的矩阵相关API,均是extends MLlib中相关分布式矩阵API,但是很快便遇到了问题,由于我需要重写原先IndexedRow的toString方法,所以写了一个类IndexRow extends IndexedRow,由于MLlib中IndexedRowMatrix是对IndexedRow的RDD封装,即RDD[IndexedRow],我们自己的矩阵类IndexMatrix 原先也是extends  IndexedRowMatrix,所以其成员rows也是RDD[IndexedRow] 。但是由于RDD的不可变特性,当我试图用RDD[IndexRow]来初始化IndexMatrix时,会出现编译不通过的错误,见下图:

image

关于这个问题,Spark的jira上有过讨论Make RDDs Covariant,结论是不会fix这个问题。另外,mllib调用了breeze去进行矩阵运算,但是相关toBreeze等函数被写成了private [mllib] 私有的,无法从其他类访问到,综上两点我的替代方案就是直接将mllib中的相关源码拷贝过来,对其进行重写。

调用Breeze进行矩阵运算

受限制于JVM的先天不足,基于Java的矩阵运算速度远远比不上本地fortran和C代码实现的矩阵库,比如BLAS,Lapack这种,而MLlib则采用了下面的结构调用关系

image

注意右边,用scala实现的Breeze去调用Netlib-java,而Netlb-java再去调用本地的BLAS和Lapack,以获得接近本地Native code运算矩阵的效果。所以我们这里也与MLlib一样使用Breeze。

不过可惜的是,由于集群的环境有点旧,而Netlib-java其Jar包编译时用的gfortran版本是1.4,系统的gfortran版本比较低,导致Netlib-java无法调用本地的BLAS,所以实际上目前我们的本地乘法仍然是用作为备用策略的java实现。

矩阵库提供的API

目前我已经完成并提供矩阵操作的基本API,其列表如下:

  • 两个矩阵相乘 multiply(B: IndexMatrix, blkNum: Int)
  • 两个矩阵相加 add(B: IndexMatrix)
  • 两个矩阵相减 minus(B: IndexMatrix)
  • 矩阵A与标量的逐个元素相加 elemWiseAdd(b: Double)
  • 矩阵A与标量的逐个元素相减 elemWiseMinus(b: Double)
  • 矩阵A与标量的逐个元素相乘 elemWiseMult(b: Double)
  • 矩阵A与标量的逐个元素相除 elemWiseDivide(b: Double)
  • 获取矩阵给定范围行 sliceByRow(startRow: Long, endRow: Long)
  • 获取矩阵给定范围列 sliceByColumn(startCol: Int, endCol: Int)
  • 获取矩阵给定范围子矩阵 getSubMatrix(startRow: Long, endRow: Long ,startCol: Int, endCol: Int)

注意 获取给定范围行和列时,上下界参数都是包含关系

简单实验结果分析

在保证程序运行正确的基础上,我对比了单机breeze和我的程序运行时间,在对两个分别是860MB的文本矩阵文件(每个均是1万乘1万的float型)进行乘法时,前者从读文件到计算写文件需要4个小时左右,而我们的程序在11分钟30秒左右,下面是一次运行的stage监控情况

image

其中耗时9分钟的stage是执行乘法的阶段,下一步当解决调用本地库进行矩阵运算后,其速度会大大加快。

下一步工作

  1. 完成Netlib-java调用本地BLAS库
  2. 完成高级矩阵算法,例如计算SVD等
  3. 优化中间输出,减少中间结果
  4. 可能考虑与HBase衔接,用来保存和读取数据
0 0