Логистический, регуляризованный линейный, SVM, ANN, KNN, случайный лес, LGBM и наивный байесовский классификатор, какой из них лучше всего справляется с классификацией газетных статей?

Все эти классификаторы машинного обучения в scikit-learn и light GBM могут эффективно обрабатывать матрицу scipy.sparse, которая является выходом TfidfVectorizer. TfidfVectorizer — это экстрактор функций НЛП, который использует стратегии мешка слов. Сначала он применяет CountVectorizer, а затем применяет TfidfTransformer для нормализации. В этой статье обсуждается TF-IDF.

Давайте проверим набор данных и посмотрим, в чем заключается задача!

data_train = fetch_20newsgroups(subset='train', categories=categories,shuffle=True, random_state=42,remove=remove)

data_test = fetch_20newsgroups(subset='test', categories=categories,
                               shuffle=True, random_state=42,
                               remove=remove)

Набор данных 20 ​​групп новостей включает 11 314 новостных статей в обучающей выборке и 7 532 в тестовой выборке. Разделение между обучающей выборкой и тестовой выборкой не является случайным и основано на дате публикации новостной статьи.

Всего 18 846 новостных статей разбиты на эти 20 групп:

['alt.atheism',
 'comp.graphics',
 'comp.os.ms-windows.misc',
 'comp.sys.ibm.pc.hardware',
 'comp.sys.mac.hardware',
 'comp.windows.x',
 'misc.forsale',
 'rec.autos',
 'rec.motorcycles',
 'rec.sport.baseball',
 'rec.sport.hockey',
 'sci.crypt',
 'sci.electronics',
 'sci.med',
 'sci.space',
 'soc.religion.christian',
 'talk.politics.guns',
 'talk.politics.mideast',
 'talk.politics.misc',
 'talk.religion.misc']

Угадайте, как машина «прочитает» подобную новостную статью, а затем классифицирует ее:

От кого: «[email protected] (Дэвид Най)\nТема: Re: Нужна консультация по проблеме взаимоотношений между врачом и пациентом\nИдентификатор статьи: cnsvax.1993Apr17.012019.6087\nОрганизация: Университет Висконсина О-Клэр\nЛинии: 12\n\n[ответ на [email protected] (Майкл Ковингтон)]\n \n›Похоже, его сердце на правильном месте, но он не умеет\n›выражать это . То, что вы получили, было _задумано_ как глубокое извинение.\n›Извинения, произнесенные переутомленными застенчивыми людьми, часто выглядят так...\n \nМне этот парень не показался слишком застенчивым. Он звучал как придурок. Я говорю: бросьте\nего кого-нибудь более знающего и чуткого.\n\nДэвид Най ([email protected]). Клиника Мидельфорт, О-Клэр, Висконсин.\nЭто откровенный абсурд; но кто хочет стать философом,\nдолжен научиться не пугаться нелепостей. — Бертран Рассел\n”

Какой алгоритм работает лучше всего? Скоро узнаем!

vectorizer = TfidfVectorizer(sublinear_tf=True, max_df=0.5,
stop_words='english')
X_train = vectorizer.fit_transform(data_train.data)
X_test = vectorizer.transform(data_test.data)

TfidfVectorizer извлекает функции из матрицы количества токенов. Он удаляет все английские стоп-слова и просматривает только те токены, которые появляются менее чем в 50% документов (это делается для того, чтобы отфильтровать токены, которые появляются очень часто, что может создавать помехи для алгоритмов и приводить к переобучению).

Всего в обучающем наборе найдено 101 322 токена (токены, которые видны только в тестовом наборе, но не в обучающем наборе, будут игнорироваться). Некоторые жетоны для проверок:

(vectorizer.get_feature_names()[::1000])

вывод:

[‘00’, ‘0h’, ‘11100001’, ‘16339’, ‘1qi83b’, ‘25x30’, ‘2x4s’, ‘3h’, ‘491’, ‘5685’, ‘639’, ‘6us’, ‘7l4e’, ‘8ffzu’, ‘9a8’, ‘_does’, ‘a89’, ‘adv21’, ‘allotment’, ‘apml’, ‘assyria’, ‘b3r’, ‘beast’, ‘bkdd’, ‘breakdown’, ‘c1i’, ‘cated’, ‘chipkeyk1’, ‘cmcl2’, ‘concurrence’, ‘cots’, ‘cursing’, ‘dcc’, ‘deselected’, ‘disinformation’, ‘drawed’, ‘ebrandt’, ‘emrick’, ‘ethiopians’, ‘f3fg’, ‘ff1zv’, ‘foolproof’, ‘fwmr’, ‘geode06’, ‘gq30tb’, ‘h378’, ‘heaters’, ‘holograph’, ‘hzrcj’, ‘imbecile’, ‘innacurate’, ‘irx’, ‘jerseys’, ‘jz75vzneut’, ‘khojaly’, ‘kxp2124’, ‘legatus’, ‘llat’, ‘m13sm’, ‘magnanamous’, ‘mbes’, ‘meteosat’, ‘mitcc7_b_’, ‘morpl’, ‘musil’, ‘nbc’, ‘nko’, ‘o5hp1en’, ‘ooops’, ‘ovk9lg’, ‘patl7a’, ‘phono’, ‘poley’, ‘princess’, ‘purporting’, ‘quandaries’, ‘razmella’, ‘relations’, ‘rfi’, ‘rotate’, ‘sadist’, ‘scx_sy’, ‘shickley’, ‘slaughter’, ‘spearhead’, ‘stavropol’, ‘suggestionas’, ‘t3dlib’, ‘tenour’, ‘tl1mm’, ‘triple’, ‘ucssun1’, ‘unloved’, ‘v108’, ‘vices’, ‘w1t45w’, ‘westboro’, ‘worn’, ‘xds’, ‘xuo’, ‘ysjp0r’, ‘zp9w’]

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

Как мы видим из вышеизложенного, многие токены не имеют смысла сами по себе. Мне не терпится увидеть, как машины интерпретируют их.

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

После всех подготовительных работ, описанных выше, теперь дело доходит до сути этой статьи! Давайте попробуем множество классификаторов, чтобы увидеть, какой из них работает лучше всего!

Во-первых, попробуйте некоторые базовые классификаторы:

(RidgeClassifier(tol=1e-2, solver="sag"), "Ridge Classifier"),
(Perceptron(max_iter=50), "Perceptron"),
(MLPClassifier(random_state=1,max_iter=300,early_stopping=True,hidden_layer_sizes=(200,100)),"MLP"),
(LogisticRegression(random_state=0,n_jobs=-1),"Logistic"),
(PassiveAggressiveClassifier(max_iter=50),"Passive-Aggressive"),
(KNeighborsClassifier(n_neighbors=5), "kNN"),
(RandomForestClassifier(), "Random forest"))

Линейные модели с различными регуляризациями:

for penalty in ["l2", "l1"]:
# Train Liblinear model
    (LinearSVC(penalty=penalty, dual=False,tol=1e-3))
# Train SGD model
    benchmark(SGDClassifier(alpha=.0001, max_iter=50, penalty=penalty))

Линейная модель с регуляризацией эластичной сети:

SGDClassifier(alpha=.0001, max_iter=50, penalty="elasticnet"))

Легкий ГБМ:

LGBMClassifier()

Ближайший центроид:

NearestCentroid()

Когда ближайший центроид используется для классификации текста с векторами tf-idf, этот классификатор также известен как классификатор Роккио.

Различные версии наивных байесовских классификаторов

MultinomialNB(alpha=.01)
BernoulliNB(alpha=.01)
ComplementNB(alpha=.1)

LinearSVC с выбором функций на основе L1:

Pipeline(
[('feature_selection',SelectFromModel(LinearSVC(penalty="l1", dual=False,tol=1e-3))),
('classification', LinearSVC(penalty="l2"))])

Напоминаем, что функции разрежены, и обучающий набор, и тестовый набор имеют 101 322 измерения, так что это явно непростая задача.

Давайте посмотрим, как работают наши классификаторы:

====================================================================
Ridge Classifier
____________________________________________________________________
Training: 
RidgeClassifier(solver='sag', tol=0.01)
train time: 5.077s
test time:  0.028s
accuracy:   0.703
density: 0.999998
====================================================================
Perceptron
____________________________________________________________________
Training: 
Perceptron(max_iter=50)
train time: 0.769s
test time:  0.039s
accuracy:   0.634
density: 0.118555
====================================================================
MLP
____________________________________________________________________
Training: 
MLPClassifier(early_stopping=True, hidden_layer_sizes=(200, 100), max_iter=300,
              random_state=1)
train time: 610.054s
test time:  0.201s
accuracy:   0.696
====================================================================
Logistic
____________________________________________________________________
Training: 
LogisticRegression(n_jobs=-1, random_state=0)
train time: 32.092s
test time:  0.022s
accuracy:   0.695
====================================================================
Passive-Aggressive
____________________________________________________________________
Training: 
PassiveAggressiveClassifier(max_iter=50)
train time: 1.259s
test time:  0.033s
accuracy:   0.680
====================================================================
kNN
____________________________________________________________________
Training: 
KNeighborsClassifier()
train time: 0.006s
test time:  3.676s
accuracy:   0.071
====================================================================
Random forest
____________________________________________________________________
Training: 
RandomForestClassifier()
train time: 69.612s
test time:  1.232s
accuracy:   0.627
====================================================================
L2 penalty
____________________________________________________________________
Training: 
LinearSVC(dual=False, tol=0.001)
train time: 3.832s
test time:  0.025s
accuracy:   0.697
____________________________________________________________________
Training: 
SGDClassifier(max_iter=50)
train time: 1.019s
test time:  0.031s
accuracy:   0.699
====================================================================
L1 penalty
____________________________________________________________________
Training: 
LinearSVC(dual=False, penalty='l1', tol=0.001)
train time: 4.014s
test time:  0.017s
accuracy:   0.666
____________________________________________________________________
Training: 
SGDClassifier(max_iter=50, penalty='l1')
train time: 1.641s
test time:  0.032s
accuracy:   0.620
====================================================================
Elastic-Net penalty
____________________________________________________________________
Training: 
SGDClassifier(max_iter=50, penalty='elasticnet')
train time: 2.741s
test time:  0.022s
accuracy:   0.693
====================================================================
NearestCentroid (aka Rocchio classifier)
____________________________________________________________________
Training: 
NearestCentroid()
train time: 0.037s
test time:  0.027s
accuracy:   0.643
====================================================================
Naive Bayes
____________________________________________________________________
Training: 
MultinomialNB(alpha=0.01)
train time: 0.098s
test time:  0.024s
accuracy:   0.696
____________________________________________________________________
Training: 
BernoulliNB(alpha=0.01)
train time: 0.101s
test time:  0.098s
accuracy:   0.567
____________________________________________________________________
Training: 
ComplementNB(alpha=0.1)
train time: 0.120s
test time:  0.020s
accuracy:   0.708
====================================================================
LinearSVC with L1-based feature selection
____________________________________________________________________
Training: 
Pipeline(steps=[('feature_selection',
                 SelectFromModel(estimator=LinearSVC(dual=False, penalty='l1',
                                                     tol=0.001))),
                ('classification', LinearSVC())])
train time: 4.601s
test time:  0.024s
accuracy:   0.681
==================================================================== lgbm ____________________________________________________________________ 
Training:  LGBMClassifier() 
train time: 426.488s 
test time:  7.752s 
accuracy:   0.610

Барабанная дробь…… и победитель……

Комплементарный наивный байесовский метод (CNB) является победителем благодаря высочайшей точности и короткому времени обучения.

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

RidgeClassifier на втором месте, а один из SGDClassifiers на третьем:

Давайте рассмотрим время, необходимое классификаторам для обработки стольких измерений.

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

Для справки, эта статья является расширением этой. Надеюсь, вам понравилось. И все коды сохраняются здесь.