Статьи

Обработайте миллион песен, чтобы найти «экзотических» с помощью Apache Pig

Недавно я нашел интересный набор данных под названием Million Song Dataset (MSD), который содержит подробные акустические и контекстные данные о миллионе песен. Для каждой песни мы можем найти информацию, такую ​​как название, жаркость, темп, продолжительность, танцевальность, громкость, а также имя исполнителя, популярность, локализация (пара широты и долготы) и многое другое. Здесь нет музыкальных файлов, но ссылки на превью песен в формате MP3 на 7digital.com могут быть легко построены на основе данных. 

Набор данных состоит из 339 текстовых файлов, разделенных табуляцией. Каждый файл содержит около 3000 песен, и каждая песня представлена ​​в виде отдельной строки текста. Набор данных общедоступен, и вы можете найти его на Infochimps или Amazon S3 . Поскольку общий размер этих данных составляет около 218 ГБ, обработка их на одной машине может занять очень много времени.

Определенно, гораздо более интересный и эффективный подход состоит в том, чтобы использовать несколько машин и обрабатывать песни параллельно, используя преимущества инструментов с открытым исходным кодом из экосистемы Apache Hadoop (например, Apache Pig ). Если у вас есть несколько собственных машин, вы можете просто использовать CDH (дистрибутив Cloudera, включая Apache Hadoop), который включает в себя полный стек Apache Hadoop. CDH можно установить вручную (быстро и легко, набрав пару простых команд) или даже автоматически с помощью Cloudera Manager Free Edition. CDH и Cloudera Manager можно бесплатно загрузить с веб-сайта Cloudera . Кроме того, вы можете арендовать некоторые машины у Amazon с уже установленным Hadoop и обрабатывать данные с помощью Amazon Elastic MapReduce.(вот классное описание, написанное Полом Лемером, как его использовать и заплатить всего за 1 доллар, и вот моя презентация об Elastic MapReduce, представленная на 2-м собрании варшавской группы пользователей Hadoop ).

Определение проблемы

Мне пришла в голову идея обработать этот набор данных, чтобы найти «экзотические» (но все еще популярные) песни. Под экзотическими песнями я имею в виду песню, записанную артистом, живущим в какой-то зарубежной стране, вдали от других артистов. Общая цель — найти пару необычных и народных песен, связанных с культурой какой-либо страны. Забавным примером может служить песня Jarzębina «Koko Koko Euro Spoko» , которую поляки выбрали в качестве официальной песни сборной Польши по футболу во время UEFA EURO 2012;)

Apache Pig

Я использовал Apache Pig для достижения этой цели. Apache Pig — это удобный инструмент, созданный в Yahoo! анализировать большие наборы данных легко и эффективно. Apache Pig обеспечивает высокий уровень и простой в изучении, понимании и поддержке язык программирования потоков данных, называемый PigLatin. PigLatin очень похож на языки сценариев, он поддерживает множество полезных операций, таких как фильтрация, сортировка, агрегация, объединение, разбиение, и предоставляет несколько сложных типов данных (кортежи, пакеты и карты). Примерный сценарий PigLatin в 20 раз короче, чем его эквивалент в Java MapReduce, и на его реализацию у программиста уходит в 16 раз меньше времени ( подробнее ), в то же время он лишь немного медленнее, чем Java MapReduce (см. Тест PigMix2).). Благодаря этим преимуществам Apache Pig часто используется многими признанными компаниями и организациями, например, Yahoo! (в настоящее время около 90% рабочих мест Hadoop написаны на PigLatin), Twitter, Nokia Ovi Maps, AOL, Mendeley, LinkedIn и ICM UW .

PigLatin скрипт

Чтобы найти такие популярные народные и местные песни, я реализовал простой скрипт PigLatin (около 50 строк кода PigLatin). Этот сценарий использует немного наивную, но довольно эффективную идею и пытается найти «изолированные» песни. Изолированная песня — это просто песня, в которой среднее расстояние между локализацией артиста и любыми другими артистами настолько мало, насколько это возможно (точнее, я должен сказать, что изолированная песня — это песня, записанная изолированным артистом). Такой подход дает нам гораздо большую вероятность обнаружить «привлекательные» песни из Конго, Мали, Польши и Вьетнама, чем из США или Великобритании.

Как уже упоминалось, набор данных содержит информацию о локализации художников в виде пар lat / long, и, к счастью, есть библиотека с открытым исходным кодом DataFu (созданная LinkedIn), которая предоставляет, например, PigLatin UDF для вычисления расстояния между двумя парами lat / long с использованием формулы Хаверсайна. ,

Просто в нескольких словах. Я читаю входные данные и отфильтровываю непопулярные песни или песни без лат / долгой локализации (отношение

отфильтрованный
). Затем я создаю все пары разных песен (отношение

Другой
) и рассчитать расстояние между художниками локализацией (отношение

дистанцировался
). Затем для каждой песни я вычисляю среднее расстояние между локализацией исполнителя песни и локализацией исполнителя всех других песен (отношение

AvgDistanced
). Теперь я могу ограничить свои записи, чтобы получать только самые интересные результаты. Во-первых, я хочу иметь только самую горячую песню для данного местоположения (отношение

Популярный
). Тогда у меня останется только небольшое количество песен с самым низким средним расстоянием (отношение

Ограниченное
) и, наконец, сохранить результаты вывода в формате, который будет полезен для дальнейшей визуализации с помощью GoogleMaps (отношение

Displable
).

Вот исходный код:

SET default_parallel $parallel
SET pig.tmpfilecompression true
SET pig.tmpfilecompression.codec lzo
SET pig.maxCombinedSplitSize 134217728
 
REGISTER '/usr/lib/pig-0.10.0/contrib/datafu-0.0.4/dist/datafu-0.0.4.jar';
DEFINE haversineMiles datafu.pig.geo.HaversineDistInMiles();
 
Songs = LOAD '$input';
--"Project early and often"
Projected = FOREACH Songs GENERATE
        (double) $6 AS artistLat, (double) $8 AS artistLong, $12 AS artistName,
        (double) $43 AS songHotness, $51 AS songTitle, (int) $52 AS songPreview;
 
--"Filter early and often"
Filtered = FILTER Projected BY (songHotness >= $hotness) AND (artistLat IS NOT NULL) AND (artistLong IS NOT NULL);
--"Copy useful fields from Popluar relation"
Filtered2 = FOREACH Filtered GENERATE songPreview as song2Preview, artistLat AS artist2Lat, artistLong AS artist2Long;
 
--"Produce all pairs of different songs and calculate distance between localizations of their artists"
Crossed = CROSS Filtered, Filtered2;
Different = FILTER Crossed BY songPreview != song2Preview;
Distanced = FOREACH Different GENERATE artistLat..songPreview,
        haversineMiles(artistLat, artistLong, artist2Lat, artist2Long) as distance;
 
--"For each song, calculate average distance between its artists and all other artists"
Grouped = GROUP Distanced BY artistLat..songPreview;
AvgDistanced = FOREACH Grouped {
        Distances = Distanced.distance;
        GENERATE FLATTEN(group), AVG(Distances) AS distanceAvg;
}
 
--"Find the most popular song for a given location"
Locationed = GROUP AvgDistanced BY (artistLat, artistLong);
Popular = FOREACH Locationed {
        OrderedSongs = ORDER AvgDistanced BY songHotness DESC;
        TopSong = LIMIT OrderedSongs 1;
        GENERATE FLATTEN(TopSong);
}
--"Find the most isolated songs which were recored by artists who live far away from other artists"
Ordered = ORDER Popular BY distanceAvg DESC;
Limited = LIMIT Ordered $topCount;
 
--"Generate results in such a format that can be easily displayed by Google Maps (by copy & paste)"
Displayed = FOREACH Limited GENERATE
        CONCAT('[', (chararray) artistLat), artistLong, songPreview,
        CONCAT('"', CONCAT((chararray) songTitle, '"')),
        CONCAT('"', CONCAT((chararray) artistName, '"')),
        songHotness,
        CONCAT((chararray)distanceAvg, '],');
 
STORE Displayed INTO '$output' USING PigStorage(',');

Запуск скрипта PigLatin

Я использую Apache Pig 0.10.0 (новейшая версия Apache Pig на момент написания этой статьи). Вы можете прочитать о новых возможностях в Pig 0.10.0 здесь .

Мой сценарий принимает пять параметров, то есть пути ввода и вывода, порог жаркости песни (число с плавающей запятой между 0,0 и 1,0), количество выходных песен и уровень параллелизма по умолчанию.

 

time /usr/lib/pig-0.10.0/bin/pig -p input=input/million-song -p hotness=0.5 -p topCount=200 -p parallel=100 -p output=output/million-song/all.5.200.100 exotic-songs.pig

Pig запускает этот скрипт как последовательность из семи заданий MapReduce. Общее время выполнения этого скрипта составило 40 млн. 47,758 с. Я оптимизировал этот сценарий, увеличив объем памяти, доступной для дочерних задач, комбинируя небольшие входные файлы, сжимая выходные данные задач карты и выходные промежуточные задания, используя сжатие LZO. Я также отключил умозрительное выполнение как для карты, так и для задач сокращения. Обратите внимание, что в то время в кластере Hadoop не выполнялось никаких других заданий.

Я запустил этот скрипт на кластере Hadoop, который принадлежит ICM UW, Я использовал три «толстых» подчиненных узла и виртуальную машину на отдельной машине в роли HDFS NameNode и Hadoop JobTracker. Каждый рабочий узел имеет четыре процессора AMD Opteron 6174 (всего 48 ядер), 192 ГБ ОЗУ и может хранить 7 ТБ данных. Для этого сценария Pig я настроил на каждом рабочем узле 45 карт и 35 максимально сокращенных задач (итого я могу запустить 135 карт и 105 параллельных задач). Я понимаю, что это не типичная конфигурация Hadoop (определенно, это не стандартное оборудование), но я просто использую то, что у меня есть. В настоящее время здесь установлен CDH3u3, который включает Hadoop 0.20.2, Pig 0.8.1 по умолчанию. Я установил Pig 0.10.0 вручную на своем клиентском компьютере и использовал его для передачи заданий в этот кластер.

Будущие оптимизации

Читаемость и производительность этого скрипта могут быть улучшены за счет реализации собственных пользовательских функций (UDF), например, многотекстовой конкатенации, настройки некоторых параметров MapReduce и Pig (например,

pig.maxCombinedSplitSize
) или настройте лучший уровень параллелизма, используя

ПАРАЛЛЕЛЬНО
ключевое слово.

Поскольку я не хочу делать этот пост долгим, эффекты рефакторизации и оптимизации будут подробно описаны в моих следующих постах. Я хотел бы написать пару постов, посвященных разработке и оптимизации сценариев PigLatin на основе этого примера и набора данных песни milion. Я хотел бы упомянуть, какие инструменты и библиотеки Pig можно использовать, показать некоторые тесты и т. Д.

Результат, чтобы увидеть

Результаты можно визуализировать, используя JavaScipt с GoogleMaps. На приведенной ниже карте показано 200 песен, и для каждой песни я поместил кликабельный маркер в локализацию лота / лонга его исполнителя, который отображает основную информацию об этой песне и ссылку на ее предварительный просмотр.

Результат для прослушивания

Вот несколько песен, которые привлекли мое внимание:

Если вы найдете более интересные примеры, просто дайте мне знать или просто поместите их в комментариях ниже. Я понимаю, что мой музыкальный вкус может быть сомнительным;)

Особая благодарность

Поскольку все мои вычисления были выполнены на кластере Hadoop, который принадлежит ICM UW , я хотел бы поблагодарить персонал ICM за возможность использовать кластер.