В этом посте объясняются методы обработки текста и анализа, используемые при запуске Yieldbot. Их технология использует инструменты с открытым исходным кодом, включая Cascalog, Lucene, Hadoop и Clojure Java Interop. Следующая статья была написана Сореном Макбетом , специалистом по данным в Yieldbot.
Здесь, в Yieldbot, мы выполняем много текстовой обработки аналитических данных. Чтобы добиться этого за разумное время, мы используем Cascalog , библиотеку обработки и запросов данных для Hadoop; написано в Clojure. Поскольку Cascalog — это Clojure, вы можете разрабатывать и тестировать запросы прямо в Clojure REPL. Это позволяет итеративно разрабатывать рабочие процессы обработки с предельной скоростью. Поскольку запросы Cascalog — это просто код Clojure, вы можете получить доступ ко всему, что может предложить Clojure, без необходимости реализации каких-либо специфичных для домена API или интерфейсов для пользовательских функций обработки. В сочетании с удивительным Java Interop от Clojure вы можете делать довольно сложные вещи очень просто и лаконично.
Многие большие библиотеки Java уже существуют для обработки текста, например, Lucene , OpenNLP , LingPipe , Stanford NLP . Использование Cascalog позволяет использовать эти существующие библиотеки с минимальными усилиями, что приводит к гораздо более коротким циклам разработки.
В качестве примера я покажу, как легко комбинировать Lucene и Cascalog для некоторой (простой) обработки текста. Вы можете найти весь код, использованный в примерах, на Github .
Наша цель состоит в том, чтобы токенизировать строку текста. Это почти всегда первый шаг в любом виде обработки текста, так что это хорошее место для начала. Для наших целей мы будем широко определять токен как базовую единицу языка, которую мы хотели бы проанализировать; обычно токен — это слово. Есть много разных способов сделать токенизацию. Lucene содержит много различных подпрограмм токенизации, которые я не буду здесь подробно описывать, но вы можете прочитать документы, чтобы узнать больше. Мы будем использовать стандартный анализатор Lucene, который является хорошим базовым токенизатором. Он будет вводить все входные данные в нижнем регистре, удалять основной список стоп-слов и довольно умно обрабатывать знаки препинания и тому подобное.
Во-первых, давайте смоделируем наш запрос Cascalog. Наши входные данные будут состоять из 1 набора строк, которые мы хотели бы разбить на токены.
(defn tokenize-strings [in-path out-path] (let [src (hfs-textline in-path)] (?<- (hfs-textline out-path :sinkmode :replace) [!line ?token] (src !line) (tokenize-string !line :> ?token) (:distinct false))))
Я не буду тратить кучу времени на объяснение синтаксиса Cascalog, так как вики и документы уже очень хороши в этом. Здесь мы читаем в текстовом файле, который содержит строки, которые мы хотим маркировать, по одной строке на строку. Каждая из этих строк будет передана в функцию tokenize-string, которая выдаст 1 или более 1-кортежей; по одному на каждый сгенерированный токен.
Далее давайте напишем нашу функцию tokenize-string. Мы будем использовать удобную функцию Cascalog, которая называется операция с состоянием. Если выглядит так:
(defmapcatop tokenize-string {:stateful true} ([] (load-analyzer StandardAnalyzer/STOP_WORDS_SET)) ([analyzer text] (emit-tokens (tokenize-text analyzer text))) ([analyzer] nil))
В начале 0-арная версия вызывается один раз для каждой задачи. Мы будем использовать это для создания экземпляра нашего анализатора Lucene, который будет выполнять токенизацию. 1 + n-арность передает результат функции 0-арности в качестве первого параметра плюс любые другие параметры, которые мы определяем. Это где фактическая работа произойдет. Последняя функция из 1 арности используется для очистки.
Далее мы создадим остальные служебные функции, которые нам нужны для загрузки анализатора Lucene, получения токенов и их выдачи обратно.
(defn tokenizer-seq "Build a lazy-seq out of a tokenizer with TermAttribute" [^TokenStream tokenizer ^TermAttribute term-att] (lazy-seq (when (.incrementToken tokenizer) (cons (.term term-att) (tokenizer-seq tokenizer term-att))))) (defn load-analyzer [^java.util.Set stopwords] (StandardAnalyzer. Version/LUCENE_CURRENT stopwords)) (defn tokenize-text "Apply a lucene tokenizer to cleaned text content as a lazy-seq" [^StandardAnalyzer analyzer page-text] (let [reader (java.io.StringReader. page-text) tokenizer (.tokenStream analyzer nil reader) term-att (.addAttribute tokenizer TermAttribute)] (tokenizer-seq tokenizer term-att))) (defn emit-tokens [tokens-seq] "Compute n-grams of a seq of tokens" (partition 1 1 tokens-seq))
Мы интенсивно используем удивительное Java Interop от Clojure, чтобы использовать Java API Lucene для выполнения тяжелой работы. Хотя этот пример очень прост, вы можете взять эту платформу и добавить любое количество различных доступных анализаторов Lucene, чтобы выполнить гораздо более сложную работу с небольшим изменением кода Cascalog.
Опираясь на Lucene, мы получаем закаленную в бою, быструю обработку без необходимости писать тонну клеевого кода благодаря Clojure. Так как код Cascalog — это код Clojure, нам не нужно тратить кучу времени на переключение между различными средами сборки и тестирования, а производственное развертывание — это просто «идеальный вариант».
Источник: http://blog.yieldbot.com/using-lucene-and-cascalog-for-fast-text-proce