Архив метки: классификация текстов

Пример классификации текстов на python с помощью fasttext

Fasttext это библиотека от фейсбука для создания векторного представления слов а также для классификации небольших текстов, а точнее — предложений. Основное отличие от самого известного векторного представления word2vec заключается в том, что каждое слово дополнительно дробится на символьные n-граммы, что позволяет решить проблему редких слов. Достоинством данного эмбеддинга также является высокая скорость работы и оптимизация под небольшие ресурсы (специально была оптимизирована под мобильные устройства).

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

Для установки воспользуемся командой:

pip3 install fasttext

Непосредственно пример:

import pandas as pd
import fasttext


# зададим набор текстов и меток классов
some_texts = [
   'текст номер один',
   'текст следующий под номером два',
   'третий набор слов',
   'что-то ещё для полноценного дата-сета',
   'пример текстового документа',
]
some_labels = [1, 1, 0, 0, 1]

# А теперь одно из разачарований имплементации именно этой библиотеки:
# Для обучения придется сделать файл, где целевой класс должен начинаться с __label__
df_train = pd.DataFrame({'txt': some_texts, 'target': some_labels})
df_train['target'] = df_train['target'].apply(lambda x: '__label__' + str(x))
df_train[['target', 'txt']].to_csv('train_data.txt', header=False, index=False, sep="\t")

# обучаем на 20 эпохах, полный набор гиперпараметров можно взглянуть на официальном сайте fasttext.cc
model = fasttext.train_supervised(input='train_data.txt', epoch=20)

# указываем чтобы отдавало вероятности обоих классов, по умолчанию k=1
p = model.predict('текст номер четыре', k=2)

print(p)
# (('__label__1', '__label__0'), array([0.5010367 , 0.49898329]))

Второе разачарование заключается в том, что в итоговом предсказании выдаётся сначала самый вероятный класс, и если требуется получить вероятность по конкретному классу, то нужно сначала узнать индекс нужного класса и только после этого по индексу взять вероятность. Для 2ух классов можно сделать примерно так:

    if p[0][0] == '__label__0':
        real_probability = p[1][1]
    else:
        real_probability = p[1][0]

В целом данный пример предлагается использовать для быстрого получения результата в задаче классификации текстов. Я попробовал код выше на соревновании mlbootcamp . ru по классификации экспертных вопросов и он показал результат из коробки лучше чем tf-idf: 0.73 fasttext против 0.68 tf-idf + lgbm

Классификация текстов методом Doc2vec в python

Используя любой метод классификации текстов, первоночально необходимо придумать способ перевода текстового документа в набор чисел. Ранее уже был рассмотрен метод на основе TF-IDF, в этот раз рассмотрим метод DOC2VEC. Из названия сразу понятно, что метод переводит документ в вектор, причём делается это просто. Сразу приведу код:

# определим датасет
some_texts = [
   'текст номер один',
   'Текст следуюйщих под номером два',
   'третий набор слов',
   'что-то ещё для полноценного дата-сета',
   'пример текстового документа'
]

from gensim.models.doc2vec import Doc2Vec, TaggedDocument
documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(some_texts)]

model = Doc2Vec(documents, vector_size=5, workers=4, epochs=3)

Обучение, в данном примере (взятом из документации), происходит сразу при создании объекта. (Мне это показалось немного странным, после sklearn с методами fit и transform).
Некоторые основные параметры:
vector_size — какой конечный размер будет у вектора. Тонкий параметр, который придётся подбирать. Чем больше выборка и длиннее каждый документ — тем имеет смысл ставить параметр больше, порядка 100-300. Для небольших текстов и датасетов может хватить размерности 5-10.
epoch — кол-во эпох на обучении. Тоже потребует настройки, обычно 10-20

workers — сколько ядер хотите задействовать на обучении. (Узнать кол-во: from multiprocessing import cpu_count)
Стоит также отметить, что начальные веса для документов берутся случайно, и в дальнейшем происходит утряска-оптимизация весов. Поэтому после каждого обучения результирующий вектор для одного и того же документа будет разным.

Полезные методы Doc2Vec

# сохранение модели для дальнейшего использования
model.save("my_doc2vec_model")
# загрузка модели
model = Doc2Vec.load("my_doc2vec_model")
# нахождение наиболее похожего документа
vector_to_search = model.infer_vector(["ищем", "похожий", "текст"])
# три наиболее похожих
similar_documents = model.docvecs.most_similar([vector_to_search], topn=3)
for s in similar_documents:
    print(some_texts[s[0]])

И так, модель Doc2vec обучена, теперь можно любой документ перевести в вектор и обучить вашим любимым классификатором. Давайте для пример возьмем RandomForest

from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier()
clf.fit([model.infer_vector([x.words]) for x in documents], [1, 1, 1, 0, 0])
 
res = clf.predict([model.infer_vector(['текст', 'номер', 'три'])])
print(res)  # [1]

По своему опыту могу сказать, что doc2vec несколько капризная штука и по настоящему хорошо работать этот метод мне не удалось заставить. Особенно неприятно удивили результаты с находением наиболее похожих документов в датасете. Однако, т.к. это другой подход, отличный от классического bag of words, tf-idf а также от LDA и подходов на нейронных сетях, то можно его смело рекомендовать использовать в дополнение с другими алгоритмами для достижения максимальной точности классификации.

Простой пример классификации текста на python sklearn

Классификация текста — классическая задача в области обработки естественного языка. Лет 10-15 назад тема классификации бурлила в научных журналах, однако со временем бум утих. Это связоно с тем, что подход на основе TF-IDF показал точность близкую к 95%-99.9%. При такой точности на качество классификации больше уже влияют методы предобработки и особенности текста, чем непосредственно выбор самого алгоритма. Появление ембеддингов в 2013 году сильно повлияло на методы в обработке текстов, открыв новую эпоху. Сейчас практически все «production» решения основываются на ембеддингах, но! Тут надо сделать оговорку — ембеддинги чрезвычайно хороши для анализа коротких текстов, которыми сейчас полон интернет, а вот для средних и больших текстов — TF-IDF по прежнему на высоте!

Ниже я приведу базовый подход классификации на основе библиотеки sklearn в python. Код максимально короткий и понятный

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline


texts = ['текст номер один', 'текст номер два', 'комьютеры в лингвистике', 'компьютеры и обработка текстов']
texts_labels = [1, 1, 0, 0]

text_clf = Pipeline([
                     ('tfidf', TfidfVectorizer()),
                     ('clf', RandomForestClassifier())
                     ])

text_clf.fit(texts, texts_labels)

res = text_clf.predict(['текст номер три'])
print(res)  # [1]

На входе у нас список из текстов и список размеченных классов, далее делаем Pipeline, который включает в себя векторизацию слов на основе TfIdf и классификатор RandomForest . Далее обучаем «пайплайн-классификатор» и пытаемся предсказать новый текст. Как видим классификацию тексто на python сделать очень просто.

Если у Вас получилось запустить первый пример, то наверно вы зададитесь вопросом, как поднять качество существующей модели. Для этого я дам список улучшений, которые следует попробовать.

1. предобработка текста. Попробуйте нормализовать слова, тогда одно смысловое слово в разных склонениях/спряжениях будет интерпретироваться программой одинаково и возможно поднимет качество.
2. У TfIdf есть много разных параметров, наиболее существенные это
2.1 добавить список стоп слов: параметр stop_words
2.2 добавить n-gramm ы слов : параметр ngram_range, например ngram_range=(1,2)
2.3 ограничить список фич, взяв только самые важные и отрезав менее важные: параметр max_features
3. попробовать другой классификатор, например from sklearn.linear_model import SGDClassifier или SVM , XGB итд. соответсвенно подбирая в каждом из них свои гиперпараметры

С помощью перечисленных шагов, Вы достаточно быстро подберёте оптимальный вариант для классификации и получите точность более 90% (основываясь на моём опыте). Если не так, то возможно надо посмотреть внимательнее корректность входных данных.