Масштабируемые или интерактивные многоядерные классификаторы
Я задул свои мозги за последние 2-3 недели по этой проблеме.
У меня проблема с несколькими метками (не многоклассы), где каждый образец может принадлежать нескольким ярлыкам.
У меня около 4,5 миллионов текстовых документов в качестве учебных данных и около 1 миллиона в качестве тестовых данных. Этикетки составляют около 35K.
Я использую scikit-learn. Для извлечения функций я ранее использовал TfidfVectorizer, который вообще не масштабировался, теперь я использую HashVectorizer, который лучше, но не настолько масштабируемый, учитывая количество документов, которые у меня есть.
vect = HashingVectorizer(strip_accents='ascii', analyzer='word', stop_words='english', n_features=(2 ** 10))
SKlearn предоставляет OneVsRestClassifier, в который я могу подать любую оценку. Для multi-label я нашел LinearSVC и SGDClassifier только для правильной работы. В соответствии с моими показателями SGD превосходит LinearSVC как в памяти, так и во времени. Итак, у меня есть что-то вроде этого
clf = OneVsRestClassifier(SGDClassifier(loss='log', penalty='l2', n_jobs=-1), n_jobs=-1)
Но это связано с серьезными проблемами:
- OneVsRest не имеет метода partial_fit, который делает невозможным обучение из-за ядра. Есть ли альтернативы для этого?
- HashingVectorizer/Tfidf работают на одном ядре и не имеют никакого параметра n_jobs. Это требует слишком много времени для хэширования документов. Любые альтернативы/предложения? Правильно ли значение n_features?
- Я опробовал 1 миллион документов. Хешинг занимает 15 минут, и когда дело доходит до clf.fit(X, y), я получаю MemoryError, потому что OvR внутренне использует LabelBinarizer и пытается выделить матрицу измерений (y x classes), которую довольно трудно выделить. Что мне делать?
- Любые другие библиотеки, которые имеют надежные и масштабируемые алгоритмы с несколькими метками? Я знаю о гениальности и махауте, но у обоих из них нет ничего для многоадресных ситуаций?
Ответы
Ответ 1
Я бы сделал многозадачную часть вручную. OneVsRestClassifier рассматривает их как независимые проблемы. Вы можете просто создать n_labels много классификаторов, а затем вызвать partial_fit на них. Вы не можете использовать конвейер, если хотите только хэш-код (который я бы посоветовал).
Не уверен насчет ускорения векторизатора хэширования. Вы должны спросить @Larsmans и @ogrisel для этого;)
Наличие partial_fit
в OneVsRestClassifier было бы хорошим дополнением, и на самом деле я не вижу в нем конкретной проблемы. Вы также можете попытаться реализовать это самостоятельно и отправить PR.
Ответ 2
- Алгоритм, реализуемый
OneVsRestClassifier
, очень прост: он просто подходит для K-бинарных классификаторов при наличии K-классов. Вы можете сделать это в своем собственном коде вместо того, чтобы полагаться на OneVsRestClassifier
. Вы также можете сделать это не более чем на К ядрах параллельно: просто запустите K процессов. Если у вас больше классов, чем процессоров на вашем компьютере, вы можете запланировать обучение с помощью такого инструмента, как GNU parallel.
- Многоядерная поддержка в scikit-learn - это работа; мелкомасштабное параллельное программирование в Python довольно сложно. Существуют потенциальные оптимизации для
HashingVectorizer
, но я (один из авторов хеширующего кода) еще не пришел к нему.
- Если вы следуете советам моего (и Андреаса), чтобы сделать свой собственный однополый отдых, это больше не должно быть проблемой.
- Трюк в (1.) применяется к любому алгоритму классификации.
Что касается количества функций, это зависит от проблемы, но для крупномасштабной классификации текста 2 ^ 10 = 1024 кажется очень маленьким. Я бы попробовал что-то около 2 ^ 18 - 2 ^ 22. Если вы тренируете модель со штрафом L1, вы можете вызвать sparsify
на обученной модели, чтобы преобразовать ее весовую матрицу в более удобный для пространства формат.
Ответ 3
Моим аргументом для масштабируемости является то, что вместо использования OneVsRest, который является просто простейшим базовым уровнем, вы должны использовать более совершенный ансамбль методов преобразования проблем. В моей статье я предлагаю схему разделения пространства меток на подпространства и преобразования подзадач в одноклассовые классификации с несколькими классами с использованием Label Powerset. Чтобы попробовать это, просто используйте следующий код, который использует библиотеку с несколькими метками, созданную поверх scikit-learn - scikit-multilearn:
from skmultilearn.ensemble import LabelSpacePartitioningClassifier
from skmultilearn.cluster import IGraphLabelCooccurenceClusterer
from skmultilearn.problem_transform import LabelPowerset
from sklearn.linear_model import SGDClassifier
# base multi-class classifier SGD
base_classifier = SGDClassifier(loss='log', penalty='l2', n_jobs=-1)
# problem transformation from multi-label to single-label multi-class
transformation_classifier = LabelPowerset(base_classifier)
# clusterer dividing the label space using fast greedy modularity maximizing scheme
clusterer = IGraphLabelCooccurenceClusterer('fastgreedy', weighted=True, include_self_edges=True)
# ensemble
clf = LabelSpacePartitioningClassifier(transformation_classifier, clusterer)
clf.fit(x_train, y_train)
prediction = clf.predict(x_test)
Ответ 4
Метод partial_fit()
был недавно добавлен в sklearn
, поэтому, надеюсь, он должен быть доступен в предстоящей версии (он в главной ветки уже есть).
Размер вашей проблемы делает его привлекательным для решения проблемы с нейронными сетями. Посмотрите magpie, он должен дать гораздо лучшие результаты, чем линейные классификаторы.