Mapreduce 火花减少操作花费的时间太长

Mapreduce 火花减少操作花费的时间太长,mapreduce,apache-spark,text-mining,tf-idf,Mapreduce,Apache Spark,Text Mining,Tf Idf,我正在用Spark制作一个应用程序,它将运行一些主题提取算法。为此,首先我需要进行一些预处理,最后提取文档术语矩阵。我可以做到这一点,但对于一个(没有那么多)庞大的文档集合(只有两千五百万字节),这个过程需要花费很多时间 所以,在调试中,我发现程序有点死机,它处于reduce操作中。我在代码的这一部分中所做的是计算每个术语在集合中出现的次数,因此首先我做了一个“映射”,为每个rdd计算它,然后我“减少”它,将结果保存在hashmap中。map操作非常快,但是在reduce中,它将操作分为40个块

我正在用Spark制作一个应用程序,它将运行一些主题提取算法。为此,首先我需要进行一些预处理,最后提取文档术语矩阵。我可以做到这一点,但对于一个(没有那么多)庞大的文档集合(只有两千五百万字节),这个过程需要花费很多时间

所以,在调试中,我发现程序有点死机,它处于reduce操作中。我在代码的这一部分中所做的是计算每个术语在集合中出现的次数,因此首先我做了一个“映射”,为每个rdd计算它,然后我“减少”它,将结果保存在hashmap中。map操作非常快,但是在reduce中,它将操作分为40个块,每个块需要5~10分钟来处理

所以我试图找出我做错了什么,或者减少操作的成本有多高

SparkConf:独立模式,使用本地[2]。我试着用它来做“spark://master:7077“,它成功了,但仍然是一样的缓慢

代码:

“filesIn”是一个javapairdd,其中键是文件路径,值是文件内容。 因此,首先是地图,我把这个“文件”放在地图上,拆分单词,并计算它们的频率(在这种情况下,不管是什么文档) 然后是reduce,在这里我创建了一个HashMap(term,freq)

JavaRDD termDF_979;=filesIn.map(新函数(){
@凌驾
公共HashMap调用(tuple2t)引发异常{
字符串[]allWords=t.。_2.拆分(“”);
HashMap hashTermFreq=新HashMap();
ArrayList words=新的ArrayList();
ArrayList terms=新的ArrayList();
HashMap termDF=新HashMap();
for(字符串术语:allWords){
if(hashTermFreq.containsKey(术语)){
Double freq=hashTermFreq.get(术语);
hashTermFreq.put(术语,频率+1);
}否则{
if(term.length()>1){
hashTermFreq.put(术语,1.0);
如果(!terms.contains(term)){
条款。添加(条款);
}
如果(!words.contains(术语)){
添加(术语);
if(术语集装箱(术语)){
int值=termDF.get(术语);
值++;
termDF.put(期限、价值);
}否则{
期限(期限1);
}
}
}
}
}
返回项df;
}
});
HashMap termDF=termDF_2;.reduce(新函数2(){
@凌驾
公共HashMap调用(HashMap t1,HashMap t2)引发异常{
HashMap结果=新建HashMap();
迭代器迭代器=t1.keySet().Iterator();
while(iterator.hasNext()){
字符串键=(字符串)迭代器。下一步();
if(result.containsKey(key)==false){
result.put(key,t1.get(key));
}否则{
result.put(key,result.get(key)+1);
}
}
迭代器=t2.keySet().iterator();
while(iterator.hasNext()){
字符串键=(字符串)迭代器。下一步();
if(result.containsKey(key)==false){
结果.put(key,t2.get(key));
}否则{
result.put(key,result.get(key)+1);
}
}
返回结果;
}
});

谢谢

好的,就在我的头顶上:

  • Spark转换是懒惰的。这意味着在调用后续的
    reduce
    操作之前,
    map
    不会执行,因此您所描述的慢速
    reduce
    很可能是慢速
    map
    +
    reduce
  • ArrayList.contains
    是O(N),所以所有这些
    单词.contains
    术语.contains
    效率极低
  • map
    逻辑有点可疑。特别地:
    • 若已经看到术语,则永远不会进入
      else
      分支
    • 乍一看,
      words
      terms
      应该具有完全相同的内容,并且应该与
      hashTermFreq
      键或
      termDF
      键等效
    • 看起来
      termDF
      中的值只能取值1。如果这是您想要的,而您忽略了频率,那么创建
      hashTermFreq
      的意义何在
  • 这里实现的
    reduce
    阶段意味着在数据上进行低效的线性扫描,对象不断增长,而您真正想要的是
    reduceByKey
使用Scala作为伪代码,您的整个代码可以高效地表示为:

val termDF=filesIn.flatMap{
大小写(_,文本)=>
text.split(“”//split
.toSet//采用唯一术语
.filter(u.size>1)//删除单个字符
.map(term=>(term,1))}//映射到对
.reduceByKey(+)//按键还原
termDF.collectAsMap//可选

最后,看起来你正在重新发明轮子。至少您需要的一些工具已经在或

中实现了,我将您的代码转换为java,并且工作正常。只有“collectasmap”部分仍然有点慢,但我想我不需要收集它,所以没关系。。有两个原因让我自己来建造它:1。预处理更改很多,所以我认为如果我从头开始构建自己的应用程序,将更容易进行更改/改进。2.我想这是主要原因:当我使用mllib时,我可以很容易地创建TFIDF,但是我不知道如何将向量中的TF与术语和他的文档联系起来。谢谢你的帮助。
JavaRDD<HashMap<String, Integer>> termDF_ = filesIn.map(new Function<Tuple2<String, String>, HashMap<String, Integer>>() {

        @Override
        public HashMap<String, Integer> call(Tuple2<String, String> t) throws Exception {
            String[] allWords = t._2.split(" ");

            HashMap<String, Double> hashTermFreq = new HashMap<String, Double>();
            ArrayList<String> words = new ArrayList<String>();
            ArrayList<String> terms = new ArrayList<String>();
            HashMap<String, Integer> termDF = new HashMap<String, Integer>();

            for (String term : allWords) {

                if (hashTermFreq.containsKey(term)) {
                    Double freq = hashTermFreq.get(term);
                    hashTermFreq.put(term, freq + 1);
                } else {
                    if (term.length() > 1) {
                        hashTermFreq.put(term, 1.0);
                        if (!terms.contains(term)) {
                            terms.add(term);
                        }
                        if (!words.contains(term)) {
                            words.add(term);
                            if (termDF.containsKey(term)) {
                                int value = termDF.get(term);
                                value++;
                                termDF.put(term, value);
                            } else {
                                termDF.put(term, 1);
                            }
                        }
                    }
                }
            }
            return termDF;
        }
    });

 HashMap<String, Integer> termDF = termDF_.reduce(new Function2<HashMap<String, Integer>, HashMap<String, Integer>, HashMap<String, Integer>>() {

        @Override
        public HashMap<String, Integer> call(HashMap<String, Integer> t1, HashMap<String, Integer> t2) throws Exception {
            HashMap<String, Integer> result = new HashMap<String, Integer>();

            Iterator iterator = t1.keySet().iterator();

            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                if (result.containsKey(key) == false) {
                    result.put(key, t1.get(key));
                } else {
                    result.put(key, result.get(key) + 1);
                }

            }

            iterator = t2.keySet().iterator();

            while (iterator.hasNext()) {
                String key = (String) iterator.next();
                if (result.containsKey(key) == false) {
                    result.put(key, t2.get(key));
                } else {
                    result.put(key, result.get(key) + 1);
                }

            }

            return result;
        }
    });