Архив за месяц: Январь 2018

Определение языка текста на python с помощью langdetect

В данном посте покажем, как с помощью библиотеки langdetect определить язык текстового фрагмента. Для начала необходимо поставить библиотеку

$ pip install langdetect

Заметим, что код портирован на питон из гугловской библиотеки language-detection, поэтому качество распознавания языка находится на уровне, во всяком случае у меня пока особых претензий не было.
Далее, всё весьма стандартно, импортируем библиотеку и вызываем функцию «детектирования языка», пример:

from langdetect import detect
detect("здесь произвольный фрагмент текста на вход")
# результат: ru

По умолчанию поддерживается 55 языков, двухбуквенные коды взяты из стандарта кодов языков, и если быть подробнее, то они такие: af, ar, bg, bn, ca, cs, cy, da, de, el, en, es, et, fa, fi, fr, gu, he, hi, hr, hu, id, it, ja, kn, ko, lt, lv, mk, ml, mr, ne, nl, no, pa, pl, pt, ro, ru, sk, sl, so, sq, sv, sw, ta, te, th, tl, tr, uk, ur, vi, zh-cn, zh-tw

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

from langdetect import detect, detect_langs
from langdetect import DetectorFactory
DetectorFactory.seed = 0

detect_langs("Otec matka syn.") 
# результат [sk:0.572770823327, pl:0.292872522702, cs:0.134356653968]

Как видим, detect_langs определил не только языки, использованные в тексте, но ещё и их вероятности. Также возможно у Вас возник вопрос, зачем я использовал DetectorFactory? Дело в том, что алгоритм не является детерминированным, т.е. при разных запусках он может выдавать разные результаты. Если Вы хотите избежать разнозначности при разных запусках, то советую использовать DetectorFactory.seed = 0, если же такой необходимости нет, то лучше сэкономить пару строк.

Далее подметим, что detect_langs вернул не просто языки, а лист объектов Language, и возникает вопрос, как же получить языки и вероятности из этого листа? Смотрите пример:

from langdetect import detect_langs

list_of_languages = detect_langs("Здесь некий текст") 
for l in list_of_languages:
    # используем атрибуты lang и prod
    print(l.lang, l.prob)
    # результат: ru , 0.99

Определение языка может быть полезно при реализации поиска (для выдачи более релевантных результатов), а также для запуска языко-зависимых программ. Как видите с помощью langdetect определить язык можно всего в 2 строки!

Пример использования CountVectorizer в sklearn

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

Модуль CountVectorizer в sklearn как раз подзволяет сконвертировать набор текстов в матрицу токенов, находящихся в тексте. Также имеется много полезных настроек, например можно задать минимальное количество необходимое для появления токена в матрице и даже получить статистику по n-граммам. Следует учитывать, что CountVectorizer по умолчанию сам производит токенизацию и выкидывает слова с длиной меньшей чем два.

Пример:

from sklearn.feature_extraction.text import CountVectorizer
import numpy as np

# инициализируем
vectorizer = CountVectorizer()
# составляем корпус документов
corpus = [
  'слово1 слово2 слово3',
  'слово2 слово3',
  'слово1 слово2 слово1',
  'слово4'
]
# подсчитываем
X = vectorizer.fit_transform(corpus)

# таким образом будет подсчитана следующая структура:
#        | слово1 | слово2 | слово3 | слово4
# текст1 |   1    |    1   |   1    |   0
# текст2 |   0    |    1   |   1    |   0
# текст3 |   2    |    1   |   0    |   0
# текст4 |   0    |    0   |   0    |   1

# чтобы получить сгенерированный словарь, из приведенной структуры CountVectorizer, стоит отметить что порядок совпадает с матрицей
vectorizer.get_feature_names()  # ['слово1', 'слово2', 'слово3', 'слово4']

# чтобы узнать индекс токена в словаре
vectorizer.vocabulary_.get('слово3') # вернет 2

# показать матрицу
X.toarray()

# теперь можно быстро подсчитать вектор для нового документа
vectorizer.transform(["слово1 слово4 слово4"])  # результат [[1 0 0 2]]

# чтобы узнать количественное вхождение каждого слова:
matrix_freq = np.asarray(X.sum(axis=0)).ravel()
final_matrix = np.array([np.array(vectorizer.get_feature_names()), matrix_freq])

Один из минусов, что добавлять новые слова в словарь не получится, необходимо сразу указать все документы.
Второй из минусов, отчасти идущий от первого — плохая масштабируемость на большой набор текстов. Есть вероятность натолкнуться на MemoryError уже при десятке тысяч документов. Поэтому либо надо ограничивать количество фич/токенов для попадания в словарь с помощью настройки max_features или же использовать другую библиотеку. Однако для нескольких тысяч можно весьма быстро получить результат и далее использовать фичи например для классификации или кластеризации документов.