Hadoop / MapReduce-Оптимизация Задания" Top N " Подсчета Слов MapReduce
Я работаю над чем - то похожим на канонический пример MapReduce-количество слов, но с изюминкой в том, что я ищу только Top N результаты.
предположим, у меня очень большой набор текстовых данных в HDFS. Есть много примеров, которые показывают, как построить задание Hadoop MapReduce, которое предоставит вам количество слов для каждого слова в этом тексте. Например, если мой корпус:
"Это проверка тестовых данных и хороший, чтобы проверить это"
результирующий набор из стандартного задания подсчета слов MapReduce будет:
тест: 3, a:2, это: 2, это: 1 и т. д..
но что, если я только хотите получить верхние 3 слова, которые были использованы во всем моем наборе данных?
Я все еще могу запустить то же самое стандартное задание MapReduce word-count, а затем просто взять лучшие результаты 3, Как только он будет готов и выплевывает количество для каждого слова, но это кажется немного неэффективным, потому что много данных необходимо перемещать во время фазы перетасовки.
Я думаю, что если этот образец достаточно велик, и данные хорошо случайным образом и хорошо распределены в HDFS, то каждому картографу не нужно отправлять все свои подсчеты слов в редукторы, а, скорее, только некоторые из лучших данных. Поэтому, если один картограф имеет это:
a: 8234, the: 5422, man: 4352, ...... много слов ... , rareword: 1, weirdword: 1 и т. д.
тогда то, что я хотел бы сделать, это отправить только 100 лучших слов из каждого картографа на фазу редуктора - так как очень мало шансов, что "rareword" внезапно окажется в топ-3, когда все будет сказано и сделано. Это похоже на то, что это сэкономит полосу пропускания, а также время обработки редуктора.
можно ли это сделать на этапе объединения? Является ли такая оптимизация до фазы перетасовки обычно выполняется?
2 ответов
это очень хороший вопрос, потому что вы попали в неэффективность примера подсчета слов Hadoop.
трюки для оптимизации вашей проблемы следующие:
сделать HashMap
на основе группировки на локальном этапе карты, вы также можете использовать объединитель для этого. Это может выглядеть так, я использую HashMultiSet
гуавы, которая облегчает хороший механизм подсчета.
public static class WordFrequencyMapper extends
Mapper<LongWritable, Text, Text, LongWritable> {
private final HashMultiset<String> wordCountSet = HashMultiset.create();
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String[] tokens = value.toString().split("\s+");
for (String token : tokens) {
wordCountSet.add(token);
}
}
и вы излучаете результат на этапе очистки:
@Override
protected void cleanup(Context context) throws IOException,
InterruptedException {
Text key = new Text();
LongWritable value = new LongWritable();
for (Entry<String> entry : wordCountSet.entrySet()) {
key.set(entry.getElement());
value.set(entry.getCount());
context.write(key, value);
}
}
так вы сгруппировали слова в локальном блоке работы, таким образом, уменьшая использование сети с помощью немного ОЗУ. Вы также можете сделать то же самое с Combiner
, но он сортируется в группу-так что это будет медленнее (особенно для строк!) чем использование HashMultiset
.
чтобы просто получить верхний N, вам нужно будет только написать верхний N в этом локальном HashMultiset
к сборнику выхода и агрегату результаты в вашем нормальном путе на стороне уменьшения.
Это экономит пропускную способность сети, только недостатком является то, что вам нужно отсортировать кортежи подсчета слов в вашем методе очистки.
часть кода может выглядеть так:
Set<String> elementSet = wordCountSet.elementSet();
String[] array = elementSet.toArray(new String[elementSet.size()]);
Arrays.sort(array, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// sort descending
return Long.compare(wordCountSet.count(o2), wordCountSet.count(o1));
}
});
Text key = new Text();
LongWritable value = new LongWritable();
// just emit the first n records
for(int i = 0; i < N, i++){
key.set(array[i]);
value.set(wordCountSet.count(array[i]));
context.write(key, value);
}
надеюсь, вы получите суть делать столько же слова локально, а затем просто агрегировать верхние N из верхних N;)
Цитируя Томаса
чтобы просто получить верхний N, вам нужно будет только написать верхний N в этом локальный HashMultiset в выходной коллектор и агрегировать результаты как обычно, на стороне уменьшения. Это экономит вам много сети пропускная способность также, единственный недостаток что вам нужно сортировать кортежи word-count в вашем методе очистки.
Если вы пишете только top N в локальном HashMultiset, то есть вероятность, что вы пропустите количество элементов, которые, если они будут переданы из этого локального HashMultiset, могут стать одним из 10 лучших элементов.
например, рассмотрим следующий формат как три карты как MapName: elementName, elemenntcount:
Карта A: Ele1, 4: Ele2, 5 : Ele3,5: Ele4,2
Карта B: Ele1, 1 : Ele5,7 : Ele6, 3: Ele7,6
Карта C: Ele5, 4: Ele8, 3 : Ele1,1: Ele9,3
теперь, если мы рассмотрим ТОП-3 каждого картографов мы будем Мисс стихия "Ele1" общий счетчик должно было быть 6, но поскольку мы вычисляем вершине каждого картографа 3 мы видим "Ele1"'общее кол ов а 4.
Я надеюсь, что имеет смысл. Пожалуйста, дайте мне знать, что вы думаете об этом.