Расширяем существующий scikit-learn классификатор

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

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

В этой статье я покажу один из возможных вариантов решения этой задачи на примере классического набора данных 20 newsgroup dataset:

  1. Создадам новый классификатор новостей из категорий «rec.sport.baseball», «talk.politics.guns» и «comp.sys.ibm.pc.hardware» (далее, модель_1)
  2. Расширю этот классификатор таким образом, чтобы он мог определять новости по теме «sci.electronics» (далее, модель_2).

Считаем, что модель_1 поставляется «в коробке», а клиенту требуется помимо новостей про бейсбол, оружие и PC иметь возможность классифицировать новости про электронику.

Есть несколько вариантов решения этой проблемы.

Клиент передаёт нам статьи про электронику

Сейчас это работает именно так, но это не масштабируется – отвлекаться на каждого клиента нет ни времени, ни желания.

Мы передаём клиенту статьи про бейсбол, оружие и PC

Считаем, что эти статьи конфицециальные и передавать их кому-либо в открытом виде нам запрещает NDA (с реальными документами так и есть).

Шифровать статьи про бейсбол, оружие и PC и передавать их клиенту

Считаем, что у нас есть быстрый алгоритм шифрования (хэширования) текстов. Здесь возможны два варианта.

Расшифровать тексты в процессе работы приложения и обучить на расшифрованных данных и данных клиента классификатор

Здесь основная пробема в том, что в памяти эти статьи будут в незашифрованном виде и в теории их можно получить в виде простого текста – не годится.

Зашифровать статьи клиента тем же алгоритмом, как наш набор данных и обучать классификатор на зашифрованных данных

Этот подход выглядит неплохо, но имея большой набор данных и некоторое количество времени можно сгенерировать достаточно много хешей и расшифровать датасет.

Обучать классификатор на сгенерированных на существующем классификаторе текстах

Здесь так же возможны два варианта с использованием векторизатора (в моём случае TfidfVectorizer) и классификатора (SGDClassifier).

Генерация текстов для каждого класса в существующей модели и добавление к ним новых текстов для нового класса

Упрощённый алгоритм выглядит следующим образом:

train_samples = []

for document_class in classifier:
words := get_specific_words_from_vectorizer_and_classifier()
samples := generate_N_samples_for_class()
train_samples.append(samples)
train_samples.append(new_samples)

model.fit(train_samples)


Проверил этот вариант и получил F1-меру ниже, чем при обучении на «реальных» данных. Но в этом случае нет необходимости передавать чужие данные клиентам. В принципе, этот вариант вполне неплох.

Генерация текстов из данных модели и обучить бинарный классификатор: существующие тексты vs новые тексты

В этом случае потребуется создать копию существующего классификатора и переписать некоторые свойства:

train_samples := []
words := get_all_words_from_vectorizer_and_model
samples := generate_samples_for_existing_classes_and_save_them_as_an_other_class()
train_samples.append(samples)
train_samples.append(new_samples_with_new_class_name)
new_model.fit(train_samples)
clone_model := copy(model)
clone_model.update_params_from_model(new_model)


Реализацию этого подхода можно посмотреть здесь.

Ротация логов в Python

Недавно возникла задача сделать ротацию логов в веб-сервисе на Tornado. До начала работы казалось, что вопрос должен быть давно изучен и решение будет очевидным.

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

Продолжить чтение «Ротация логов в Python»