Реализация наивного байесовского классификатора текстов на Java

В данной статье мы создадим простую реализацию наивного байесовского классификатора текстов на Java. Классификатор имеет открытый исходный код (под лицензией GPL v3), и вы можете загрузить его с GitHub.

Реализация наивного байесовского классификатора на Java

Мы создадим наш классификатор на базе мультиномиальной модели (multinomial model) с применением алгоритма отбора признаков на основе критерия согласия хи-квадрат (Pearson’s chi-squared test). Исходный код содержит подробные комментарии (в формате Javadoc), касающиеся технических деталей реализации, поэтому в статье мы рассмотрим архитектуру классификатора на высоком уровне абстракции.

1. Класс NaiveBayes

Класс NaiveBayes является основной частью классификатора. Он реализует такие методы, как train() и predict(), используемые для обучения и прогнозирования соответственно. Следует отметить, что этот класс также отвечает за вызовы внешних методов для предобработки и лексического анализа документов пред обучением и классификацией.

2. Объект NaiveBayesKnowledgeBase

Результатом обучения является объект NaiveBayesKnowledgeBase, который хранит всю необходимую информацию и вероятности, используемые классификатором.

3. Объект Document

Обучающие и классифицируемые тексты во внутреннем представлении хранятся в виде объектов Document. Объект Document содержит все токены (слова) данного документа, их количества и класс документа.

4. Объект FeatureStats

Объект FeatureStats хранит следующие статистические характеристики, полученные на этапе извлечения признаков: количества совместных появлений признаков и классов (на основе этих значений вычисляются совместные вероятности и правдоподобия), количества появлений каждого класса (на основе этих значений вычисляются априорные вероятности, если они не заданы на входе) и общее количество текстов в обучающем наборе.

5. Класс FeatureExtraction

Этот класс отвечает за извлечение признаков. Также он вычисляет ряд статистических характеристик, необходимых в дальнейшем для алгоритма классификации. Все эти характеристики сохраняются и возвращаются в виде объекта FeatureStats, чтобы избежать их повторного вычисления.

6. Класс TextTokenizer

Это простой класс для лексического анализа. Он выполняет предобработку, очистку и лексический анализ исходных документов, а также их преобразование в объекты Document.

Использование класса NaiveBayes

Примером использования класса NaiveBayes является класс NaiveBayesExample. Этот демонстрационный класс обучает наивный байесовский классификатор для определения языка текста. Вначале мы задаем пути к обучающим наборам данных в объекте HashMap, а затем загружаем их в память.

//map of dataset files
Map<String, URL> trainingFiles = new HashMap<>();
trainingFiles.put("English", NaiveBayesExample.class.getResource("/datasets/training.language.en.txt"));
trainingFiles.put("French", NaiveBayesExample.class.getResource("/datasets/training.language.fr.txt"));
trainingFiles.put("German", NaiveBayesExample.class.getResource("/datasets/training.language.de.txt"));
 
//loading examples in memory
Map<String, String[]> trainingExamples = new HashMap<>();
for(Map.Entry<String, URL> entry : trainingFiles.entrySet()) {
   trainingExamples.put(entry.getKey(), readLines(entry.getValue()));
}

Обучаем классификатор, передав ему обучающие данные. После завершения обучения сохраняем объект NaiveBayesKnowledgeBase для дальнейшего использования.

//train classifier
NaiveBayes nb = new NaiveBayes();
nb.setChisquareCriticalValue(6.63); //0.01 pvalue
nb.train(trainingExamples);
    
//get trained classifier
NaiveBayesKnowledgeBase knowledgeBase = nb.getKnowledgeBase();

Чтобы выполнить классификацию, необходимо инициализировать новый классификатор, передав ему объект NaiveBayesKnowledgeBase, полученный на этапе обучения. Затем с помощью метода predict() определяем класс целевого текста.

//Test classifier
nb = new NaiveBayes(knowledgeBase);
String exampleEn = "I am English";
String outputEn = nb.predict(exampleEn);
System.out.format("The sentense \"%s\" was classified as \"%s\".%n", exampleEn, outputEn);

Необходимые усовершенствования

Не следует воспринимать рассмотренную нами реализацию, как готовое решение для сложных задач классификации текстов. Ниже представлен ряд важных усовершенствований, повышающих эффективность.

1. Извлечение слов

Несмотря на то, что для решения простых задач, таких как определение языка, обычно достаточно использовать одиночные слова, для работы с более сложными задачами необходимо извлекать N-граммы (комбинации слов). Для извлечения N-грамм можно создать более сложный алгоритм метода TextTokenizer.extractKeywords() или воспользоваться функцией KeywordExtraction, реализованной в API компании Datumbox.

2. Предобработка текста

Перед обучением и классификацией, как правило, необходимо выполнить предобработку документов, чтобы удалить ненужные символы. В нашей реализации ограниченная предобработка выполняется с помощью метода TextTokenizer.preprocess(), но когда речь идет об анализе HTML-страниц, этот процесс становится намного более сложным. В этом случае можно просто удалить HTML-теги и оставить только текст документа. Также можно применить более сложные алгоритмы машинного обучения, которые определяют основной текст страницы и удаляют контент, принадлежащий футеру, хэдеру, меню и т.д. В качестве еще одного варианта можно воспользоваться функцией TextExtraction, реализованной в API компании Datumbox.

3. Другие модели наивного байесовского алгоритма

Рассмотренная нами реализация выполнена на основе мультиномиальной модели, однако для решения различных задач классификации требуются различные модели. Например, в некоторых случаях модель на основе распределения Бернулли обеспечит более высокие результаты. Используйте данную реализацию в качестве отправной точки и модифицируйте ее в соответствии с решаемой задачей.

4. Другие методы отбора признаков

Чтобы извлечь наиболее полезные признаки, в нашей реализации применяется алгоритм на основе критерия согласия хи-квадрат. Это достаточно эффективный алгоритм, однако он имеет тенденцию давать более высокие оценки редким признакам, которые встречаются только в одной категории. Для повышения эффективности перед отбором признаков можно удалить «шумные» и редкие признаки. Также можно реализовать другие подходы, например, отбор признаков на основе взаимной информации (mutual information).

5. Повышение производительности

В данной реализации приоритетом была хорошая читаемость кода, а не его оптимизация. Несмотря на то, что в результате оптимизации код становится мене читабельным, и затрудняется его обслуживание, подобные меры, как правило, необходимы, поскольку многие циклы алгоритма выполняют миллионы итераций в процессе обучения и тестирования. Эта реализация может стать отличным первым шагом в процессе создания собственной оптимизированной версии.

Заключение

Следует отметить, что хотя наивный байесовский алгоритм является простым, быстрым и в большинстве случаев достаточно точным, он все же является «наивным», потому что в его основе лежит допущение о независимости признаков.

Поскольку это допущение практически никогда не выполняется в задачах классификации текстов, наивный байесовский алгоритм практически никогда не является наилучшим классификатором. Для решения более сложных задач классификации тестов необходимы более совершенные методы, например, основанные на принципе максимума энтропии (principle of maximum entropy).

По материалам: Datumbox

Добавить комментарий

Ваш e-mail не будет опубликован.

закрыть

Поделиться

Отправить на почту
закрыть

Вход

закрыть

Регистрация

+ =