我想尝试一下Mincemeat map/reduce Python应用程序的矩阵乘法。我正在使用Python 2.7。我发现了几个描述如何在Java中使用Hadoop做矩阵乘法的网页,我一直在参考这个http://importantfish.com/one-step-matrix-multiplication-with-hadoop/,因为它很简单,因为它显示的伪代码已经非常接近Python代码了。
我注意到,在Java代码中,矩阵维度是通过Context类型的附加参数提供给map和reduce函数的。mincemate不提供这样的东西,但我得到了一个建议,我可以提供这些值给我的map和reduce函数使用闭包。我写的map和reduce函数是这样的:
def make_map_fn(num_rows_result, num_cols_result):
m = num_rows_result
p = num_cols_result
def map_fn(key, value):
# value is ('A', i, j, a_ij) or ('B', j, k, b_jk)
if value[0] == 'A':
i = value[1]
j = value[2]
a_ij = value[3]
for k in xrange(1, p):
yield ((i, k), ('A', j, a_ij))
else:
j = value[1]
k = value[2]
b_jk = value[3]
for i in xrange(1, m):
yield ((i, k), ('B', j, b_jk))
return map_fn
def make_reduce_fn(inner_dim):
n = inner_dim
def reduce_fn(key, values):
# key is (i, k)
# values is a list of ('A', j, a_ij) and ('B', j, b_jk)
hash_A = {j: a_ij for (x, j, a_ij) in values if x == 'A'}
hash_B = {j: b_jk for (x, j, b_jk) in values if x == 'B'}
result = 0
for j in xrange(1, n):
result += hash_A[j] * hash_B[j]
return (key, result)
return reduce_fn
然后我将它们分配给Mincemeat,像这样:
s = mincemeat.Server()
s.mapfn = make_map_fn(num_rows_A, num_cols_B)
s.reducefn = make_reduce_fn(num_cols_A)
当我在Mincemeat中运行这个时,我得到这个错误消息:
error: uncaptured python exception, closing channel <__main__.Client connected at 0x2ada4d0>
(<type 'exceptions.TypeError'>:arg 5 (closure) must be tuple
[/usr/lib/python2.7/asyncore.py|read|83]
[/usr/lib/python2.7/asyncore.py|handle_read_event|444]
[/usr/lib/python2.7/asynchat.py|handle_read|140]
[/usr/local/lib/python2.7/dist-packages/mincemeat.py|found_terminator|96]
[/usr/local/lib/python2.7/dist-packages/mincemeat.py|process_command|194]
[/usr/local/lib/python2.7/dist-packages/mincemeat.py|set_mapfn|159])
我在网上搜索了搜索词,比如|python闭包必须是元组|,我发现的东西似乎是在处理有人试图使用lambda或function()构造函数的情况下,需要确保他们在定义闭包时没有遗漏某些东西。在我的例子中,make_map_fn和make_reduce_fn返回的map_fn和reduce_fn值看起来像有效的函数对象,它们的func_closure值是包含我想要提供的数组维度的单元格元组,但是仍然缺少一些东西。我需要把这些函数传递到什么形式才能被Mincemeat使用?
我不想告诉你坏消息,但这只是你的代码中有几个错误,加上你链接的网站提供的输入文件中有两个错误。这与闭包的使用无关,尽管有误导性的错误消息。
"从一开始"错误
注意,伪代码中最内层的循环是这样的:for k = 1 to p:
for i = 1 to m:
for j = 1 to n:
在伪代码中,这通常表示包含端点,即for k = 1 to p
表示k = 1, 2, ..., p-1, p
。另一方面,代码中相应的循环如下所示:
for k in xrange(1, p):
for i in xrange(1, m):
for j in xrange(1, n):
当然,xrange(1, p)
得到1,2,…p-2 p-1。假设您从0开始索引矩阵(就像它们在您链接的站点上所做的那样),那么所有的xrange都应该从0开始(例如xrange(0, p)
),就像它们在Java代码中的对等物一样(for (int k = 0; k < p; k++)
)。这解决了你的一个问题。
输入文件错误
如果您没有注意到这一点,网站提供的A和B的输入文件是不正确的-他们忘记了两个矩阵的(0,0)项。特别是,您应该在表单A,0,0,0.0
的开头添加一行,并在表单B,0,0,0.0
的9和10之间添加一行。(我猜你把它放在哪里并不重要,但为了一致性,你不妨把它们放在它们自然适合的地方。)
一旦我纠正了这两个错误,mincemeat给出了我们期望的结果(格式化):
{(0, 1): ((0, 1), 100.0),
(1, 2): ((1, 2), 310.0),
(0, 0): ((0, 0), 90.0),
(0, 2): ((0, 2), 110.0),
(1, 0): ((1, 0), 240.0),
(1, 1): ((1, 1), 275.0)}
我还没有弄清楚到底发生了什么与错误消息,但我认为它归结为一个事实,不正确的循环索引在映射函数导致垃圾数据被传递到减少节点,这就是为什么错误提到减少函数。
基本上,发生的事情是,hash_A
和hash_B
在reduce函数中有时没有相同的键,所以当你试图乘以hash_A[j] * hash_B[j]
时,你会得到一个KeyError
,因为j
不是其中一个或另一个的键,这被捕获在上游的某个地方并作为TypeError
重新抛出。