背景:
我正试图使用mapreduce在hadoop上用java制作一个“文档术语”矩阵。文档术语矩阵就像一个巨大的表,其中每行表示一个文档,每列表示一个可能的单词/术语。
问题陈述:
假设我已经有一个术语索引列表(这样我就知道哪个术语与哪个列号相关联),那么在每个文档中查找每个术语的索引的最佳方法是什么,这样我就可以逐行(即逐文档)构建矩阵?
到目前为止,我可以想到两种方法:
进近#1:
将术语索引列表存储在hadoop分布式文件系统上。每次Map程序读取一个新文档进行索引时,都会生成一个新的mapreduce作业—文档中每个唯一的单词对应一个作业,每个作业都会在分布式术语列表中查询其术语。这种方法听起来有点过头了,因为我猜在开始一份新工作时会有一些开销,而且这种方法可能需要数千万个工作。另外,我不确定是否可以在另一个mapreduce作业中调用mapreduce作业。
进近#2:
将术语索引列表附加到每个文档中,以便每个Map器最终得到术语索引列表的本地副本。这种方法非常浪费存储空间(术语索引列表的副本数量与文档数量相同)。另外,我不知道如何将术语索引列表与每个文档合并——我是在Map器中合并它们还是在缩减器中合并它们?
问题更新1
输入文件格式:
输入文件将是一个包含所有文档(产品评论)的csv(逗号分隔值)文件。文件中没有列标题,但每个审阅的值按以下顺序显示:product\u id、review\u id、review、stars。下面是一个假例子:
“产品a”,“1”,“产品a非常非常贵。”,“2”
“产品g”,“2”,“很棒的产品!!”,“5”
术语索引文件格式:
术语索引文件中的每一行都由以下内容组成:索引号、制表符,然后是单词。每个可能的单词在索引文件中只列出一次,因此术语索引文件类似于sql表的主键(单词)列表。对于特定文档中的每个单词,我的初步计划是遍历术语索引文件的每一行,直到找到该单词。然后将该单词的列号定义为与该单词相关联的列/术语索引。下面是一个术语索引文件的示例,它是使用前面提到的两个示例产品评论构建的。
1棒极了
2产品
3安培
4是
5非常
6昂贵
输出文件格式:
我希望输出是“矩阵市场”(mm)格式,这是压缩多个零的矩阵的行业标准。这是一种理想的格式,因为大多数评论只包含一小部分可能的单词,所以对于特定文档,只需要指定非零列。
mm格式的第一行有三个制表符分隔的值:文档总数、word列总数和mm文件中不包括标题的行总数。在标题之后,每一行包含与特定条目相关联的矩阵坐标,以及条目的值,顺序为:reviewid、wordcolumnid、entry(该单词在review中出现的次数)。有关矩阵市场格式的更多详细信息,请参见以下链接:http://math.nist.gov/matrixmarket/formats.html.
每个评审的id将等于文档术语矩阵中的行索引。通过这种方式,我可以在矩阵市场格式中保留评论的id,这样我仍然可以将每个评论与其星级相关联。我的最终目标——超出了这个问题的范围——是建立一个自然语言处理算法,在一篇基于文本的新评论中预测恒星的数量。
使用上面的示例,最终的输出文件如下所示(我无法让stackoverflow显示制表符而不是空格):
2 6 7
1 2 1
1 3 1
1 4 1
1 5 2
1 6 1
2 1 1
2 2 1
3条答案
按热度按时间06odsfpq1#
您可以在hadoop分布式缓存中加载术语索引列表,以便Map器和还原器可以使用它。例如,在hadoop流媒体中,可以按如下方式运行作业:
现在,在mymapper.py中,您可以加载mytermindexlist.txt文件并根据需要使用它。如果你对你的输入和期望的输出有更详细的描述,我可以给你更多的细节。
zpjtge222#
好吧,你可以使用类似于倒排索引的概念。
我建议这样做是因为,我假设两个文件都很大。因此,像一对一那样相互比较将是真正的性能瓶颈。
这里有一种方法-
您可以将输入文件格式csv文件(例如datafile1、datafile2)和术语索引文件(例如term\u index\u file)作为输入提供给作业。
然后在每个Map器中,过滤源文件名,类似这样-
Map器的伪代码-
e、 g.不同Map器的输出
解释(示例):因为它清楚地显示了关键字将是您的单词,值将由两部分组成,两部分之间用分隔符“|”分隔
如果源是一个数据文件,则发出key=product和value=-1 | 1,其中-1是一个伪元素,1是一个review | id。
如果源是一个term|index|文件,则发出key=product和value=2 | 0,其中2是单词“product”的索引,0是一个伪review|id,我们将使用它进行排序-稍后解释。
当然,没有重复的索引将由两个不同的Map器处理,如果我们是提供一个正常的输入文件的作业的长期索引文件。因此,“product,vary”或术语索引文件中的任何其他索引词将仅对一个Map器可用。注意这只对term\索引\文件有效,而不是数据文件。
下一步:
hadoop mapreduce框架,如你所知,将按键分组,所以,你将有这样的东西去不同的还原器,
但是,在上述情况下,我们有一个问题。我们希望在“|”之后的值中进行排序,即
reduce-1 -> 2|0, -1|1, -1|2 and in reduce-2 -> <5|0, -1|1, -1|1>
为了实现这一点,您可以使用使用排序比较器实现的辅助排序。请用谷歌搜索,但这里的链接可能会有所帮助。在这里提到它可能会很长时间。在每个reduce-1中,由于值是按上述方式排序的,因此当我们开始迭代时,我们将在第一次迭代中得到“0”,并用它得到索引\u id=2,然后可以用于后续迭代。在接下来的两次迭代中,我们连续地得到review id 1和2,并使用一个计数器,以便跟踪任何重复的review id。当我们得到重复的评论id时,这意味着一个单词在同一个评论id行中出现了两次。只有在找到不同的审阅id并发出特定索引id的上一个审阅id详细信息时,我们才重置计数器,类似这样-
当循环结束时,我们将只剩下一个先前的\u review \u id,我们最终以相同的方式发出它。
减速机伪代码-
这项工作是用多个reducer来完成的,目的是利用hadoop实现它最著名的性能,结果,最终的输出将分散,类似下面这样,偏离您期望的输出。
但是,如果您想根据review\u id(作为您想要的输出)对所有内容进行排序,您可以使用一个reducer并将previos作业的输出作为输入,为您编写另一个作业。同时计算2 6 7并将其放在输出的前面。
我认为,这只是一种方法(或想法),可能会对你有所帮助。你肯定想修改这个,把一个更好的算法,并使用它的方式,你认为会有利于你。
也可以使用复合键,以比使用分隔符(如“|”)更清晰。
我愿意澄清。如果你认为它可能对你有用,请询问。
谢谢您!
nx7onnlm3#
方法#1并不好,但是如果你没有太多的hadoop经验的话,这种方法非常常见。开始工作是非常昂贵的。你要做的是做2-3份工作,互相补充,以得到想要的结果。类似问题的一个常见解决方案是让Map器对输入和输出对进行标记化,将它们分组到执行某种计算的reducer中,然后将其输入到job 2中。在job 2中的mapper中,以某种方式反转数据,并在reducer中执行其他计算。
我强烈建议通过培训课程学习更多关于hadoop的知识。有趣的是,cloudera的开发课程有一个与您试图解决的问题非常相似的问题。或者除了一门课程之外,我还将学习“使用mapreduce进行数据密集型文本处理”,特别是“计算相对频率”和“文本检索的反向索引”部分
http://lintool.github.io/mapreducealgorithms/mapreduce-book-final.pdf