MapReduce 矩阵乘法

一、对朴素简单直接方法

把m*n 和n*l的矩阵A和B相乘,这估计是最容易想到的方法了:

把A(m*n)的元素,每个发送l次,把B(n*l)的元素每个发送m次。将发送到一起的数据相乘求和,得到最后的结果。


优点:在知道坐标的情况下,这个过程就一轮mapreduce。
缺点:每个值要被发送多次。m*n 和n*l的矩阵,发送的元素有m*l*2次,比如100万的方正相乘,那么中间文件有100*100百万*百万的记录,这么多元素在网络上传输,结果你懂的。另外,这个并没有有效的避开0元素,也就是说对稀疏矩阵来说很浪费。而且必须要知道矩阵的下标和行列数。因为在map里需要用到。实际情况是,现实中的矩阵,并不一定都是用递增的整数来表示下标的。有可能是人名,帐号等。

上面的方法乍一看还ok,用小数据测试一下也是正确的,不过这个方法问题在于,reduce里收到的数据也许非常非常多,多到内存放不下。然后节点就heapSpace overFlow了。话说回来如果一定要在这个方法上改进,可以把map reduce分成两步。第一步还是和上面一样,但是key中再加一个字段k,表示,这是结果矩阵中坐标为(i,j)的元素的第k个用来求和的元素。k其实是A的列号和B的行号。这样上面的过程就变成 

A: key(1,1,1) value(a,1)  key(1,1,2) value(a,2)  key(1,1,3) value(a,3)
       key(1,2,1) value(a,1)  key(1,2,2) value(a,2) ..........
B:   key(1,1,1) value(b,1)    key(1,1,2) value(b,6) key(1,1,3) value(b,0)
      key(2,1,1) value(b,1)    key(2,1,2) value(b,6) ...........
然后在reduce中对元素求乘积 生成:
      key(1,1)  value= (a,1)*(b,1)   key(1,1)  value= (a,2)*(b,6)    key(1,1)  value= (a,3)*(b,0). 其实连元素来自A还是B都可以省了。
再来第二轮mapreduce,对上一步mapreduce的结果再group by,求和。这样改进之后,没有reduce内存溢出的问题。看起来像是多大的矩阵都可以计算,其实由于没能解决一个A或B的元素要发送很多次的问题,在计算超级大的矩阵的时候,这个方法还是完全不能用。

二、 分解成行和列来控制

观察上面的最细的矩阵乘法,如果矩阵是比较稀疏的,其实有很多东西就白白发送了,发出去在reduce里其实没有用到。也是这个原因导致中间文件急剧膨胀,如果矩阵超级大,即使在mapreduce里,也要跑相当长时间。再来回忆一下矩阵乘法,能发现一些规律。
假设现在有两个矩阵,分别是同现矩阵和用户评分矩阵,现在要把两个矩阵相乘来获得推荐分数。



通过观察,把A矩阵的一列和B矩阵的一行发送到同一个reduce中,然后在reduce中,对来自A的元算和来自B的元素做笛卡尔乘积,对笛卡尔乘积的结果再做一次mapReduce,对相应的坐标求和就可以得到结果矩阵的一个个元素了。
优点:相对与最细的矩阵乘法而言,大大压缩了中间文件的大小,对稀疏矩阵而言,避免了找不到对应元素而造成的浪费。在这个方法里,为0的元素不发送就行了。另外一个明显的优点就是,我用不着一定用数字来表示矩阵的坐标,是字符串也行。只要A矩阵的列坐标和B矩阵的行坐标对的上就行。
缺点:变成了两轮mapreduce才搞得定。另一个明显缺点是,在一个reduce的节点内存里面,未必放的下A的一行+B的一列。因为仅凭key不能区分数据是来自A矩阵还是B矩阵,最起码要往内存里读一次才知道怎么做笛卡尔乘积。当然写成一个文件放到reduce本地,然后再来算也行,不过这样有点低效了,不如直接在内存里来的快。这样其实还是限制了这个方法的范围,我觉得1百万乘以1百万的稀疏矩阵用这个方法来做还是撑的住的,当然得看矩阵稀疏程度。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值