Використовуйте scikit-learn для класифікації за кількома категоріями


80

Я намагаюся використовувати один із контрольованих методів навчання scikit-learn для класифікації фрагментів тексту в одну або кілька категорій. Функція передбачення всіх алгоритмів, які я пробував, повертає лише одне збіг.

Наприклад, у мене є шматок тексту:

"Theaters in New York compared to those in London"

І я навчив алгоритм вибирати місце для кожного текстового фрагмента, яким я його подаю.

У наведеному вище прикладі я хотів би, щоб він повернувся New Yorkта London, але він повертається лише New York.

Чи можна за допомогою scikit-learn повернути кілька результатів? Або навіть повернути ярлик із наступною найбільшою ймовірністю?

Спасибі за вашу допомогу.

--- Оновлення

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

y_train = ('New York','London')


train_set = ("new york nyc big apple", "london uk great britain")
vocab = {'new york' :0,'nyc':1,'big apple':2,'london' : 3, 'uk': 4, 'great britain' : 5}
count = CountVectorizer(analyzer=WordNGramAnalyzer(min_n=1, max_n=2),vocabulary=vocab)
test_set = ('nice day in nyc','london town','hello welcome to the big apple. enjoy it here and london too')

X_vectorized = count.transform(train_set).todense()
smatrix2  = count.transform(test_set).todense()


base_clf = MultinomialNB(alpha=1)

clf = OneVsRestClassifier(base_clf).fit(X_vectorized, y_train)
Y_pred = clf.predict(smatrix2)
print Y_pred

Результат: ['Нью-Йорк' 'Лондон' 'Лондон']

Відповіді:


111

Те, що ви хочете, називається класифікацією з декількома мітками. Scikits-learn можуть це зробити. Дивіться тут: http://scikit-learn.org/dev/modules/multiclass.html .

Я не впевнений, що йде не так у вашому прикладі, у моїй версії sklearn, мабуть, немає WordNGramAnalyzer. Можливо, мова йде про використання більшої кількості навчальних прикладів чи пробування іншого класифікатора? Хоча зауважте, що класифікатор із декількома мітками очікує, що ціллю буде список кортежів / списків міток.

Для мене працює наступне:

import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.multiclass import OneVsRestClassifier

X_train = np.array(["new york is a hell of a town",
                    "new york was originally dutch",
                    "the big apple is great",
                    "new york is also called the big apple",
                    "nyc is nice",
                    "people abbreviate new york city as nyc",
                    "the capital of great britain is london",
                    "london is in the uk",
                    "london is in england",
                    "london is in great britain",
                    "it rains a lot in london",
                    "london hosts the british museum",
                    "new york is great and so is london",
                    "i like london better than new york"])
y_train = [[0],[0],[0],[0],[0],[0],[1],[1],[1],[1],[1],[1],[0,1],[0,1]]
X_test = np.array(['nice day in nyc',
                   'welcome to london',
                   'hello welcome to new york. enjoy it here and london too'])   
target_names = ['New York', 'London']

classifier = Pipeline([
    ('vectorizer', CountVectorizer(min_n=1,max_n=2)),
    ('tfidf', TfidfTransformer()),
    ('clf', OneVsRestClassifier(LinearSVC()))])
classifier.fit(X_train, y_train)
predicted = classifier.predict(X_test)
for item, labels in zip(X_test, predicted):
    print '%s => %s' % (item, ', '.join(target_names[x] for x in labels))

Для мене це дає результат:

nice day in nyc => New York
welcome to london => London
hello welcome to new york. enjoy it here and london too => New York, London

Сподіваюся, це допомагає.


1
Я спробував вилучити два останні приклади навчання, які поєднують назви міст, і я отримую: привіт, ласкаво просимо до Нью-Йорка. насолоджуйся тут і Лондон теж => Нью-Йорк Він більше не повертає двох ярликів. Для мене це лише повернення двох лейблів, якщо я треную комбінації двох міст. Мені чогось не вистачає? Ще раз спасибі за вашу допомогу
CodeMonkeyB

1
Це просто набір іграшок, я б не робив із цього занадто багато висновків. Ви пробували цю процедуру на своїх реальних даних?
mwv

3
@CodeMonkeyB: ви дійсно повинні прийняти цю відповідь, це правильно з точки зору програмування. Чи буде це працювати на практиці, залежить від ваших даних, а не від коду.
Фред Фу

2
Хтось ще отримує проблему з min_nта max_n. Мені потрібно , щоб змінити їх ngram_range=(1,2)до роботи
emmagras

1
Це видає таку помилку: ValueError: Ви, схоже, використовуєте застаріле подання даних із декількома мітками. Послідовність послідовностей більше не підтримується; використовуйте замість цього двійковий масив або розріджену матрицю.
MANU

61

EDIT: оновлено для Python 3, scikit-learn 0.18.1 за допомогою MultiLabelBinarizer, як пропонується.

Я також працював над цим, і зробив невелике вдосконалення чудової відповіді mwv, яка може бути корисною. Він бере текстові мітки як вхідні, а не двійкові мітки, і кодує їх за допомогою MultiLabelBinarizer.

import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.svm import LinearSVC
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.multiclass import OneVsRestClassifier
from sklearn.preprocessing import MultiLabelBinarizer

X_train = np.array(["new york is a hell of a town",
                    "new york was originally dutch",
                    "the big apple is great",
                    "new york is also called the big apple",
                    "nyc is nice",
                    "people abbreviate new york city as nyc",
                    "the capital of great britain is london",
                    "london is in the uk",
                    "london is in england",
                    "london is in great britain",
                    "it rains a lot in london",
                    "london hosts the british museum",
                    "new york is great and so is london",
                    "i like london better than new york"])
y_train_text = [["new york"],["new york"],["new york"],["new york"],["new york"],
                ["new york"],["london"],["london"],["london"],["london"],
                ["london"],["london"],["new york","london"],["new york","london"]]

X_test = np.array(['nice day in nyc',
                   'welcome to london',
                   'london is rainy',
                   'it is raining in britian',
                   'it is raining in britian and the big apple',
                   'it is raining in britian and nyc',
                   'hello welcome to new york. enjoy it here and london too'])
target_names = ['New York', 'London']

mlb = MultiLabelBinarizer()
Y = mlb.fit_transform(y_train_text)

classifier = Pipeline([
    ('vectorizer', CountVectorizer()),
    ('tfidf', TfidfTransformer()),
    ('clf', OneVsRestClassifier(LinearSVC()))])

classifier.fit(X_train, Y)
predicted = classifier.predict(X_test)
all_labels = mlb.inverse_transform(predicted)

for item, labels in zip(X_test, all_labels):
    print('{0} => {1}'.format(item, ', '.join(labels)))

Це дає мені такий результат:

nice day in nyc => new york
welcome to london => london
london is rainy => london
it is raining in britian => london
it is raining in britian and the big apple => new york
it is raining in britian and nyc => london, new york
hello welcome to new york. enjoy it here and london too => london, new york

13
labelBinarizerзастарілий. Використовуйте lb = preprocessing.MultiLabelBinarizer()замість цього
Роман

1
Це не дає Великобританії, оскільки єдиними мітками виводу є New Yorkі London.
umop aplsdn

2
Відповідно до scikit-learn One-Vs-All підтримується усіма лінійними моделями, за винятком sklearn.svm.SVC, а також багатомітка підтримується: Деревами прийняття рішень, Випадковими лісами, Найближчими сусідами, тому я б не використовував LinearSVC () для цього типу завдання (він же багатозначний класифікатор, який, я гадаю, ви хочете використовувати)
PeterB

2
Fyi One-Vs-All, про яку згадує @mindstorm, відповідає класу scikit-learn "OneVsRestClassifier" (зверніть увагу на "Відпочинок", а не на "всі"). Ця довідкова сторінка scikit-learn роз’яснює це.
lucid_dreamer

1
Як зазначає @mindstorm, правда, на цій сторінці в документації згадується: "One-Vs-All: усі лінійні моделі, крім sklearn.svm.SVC". Однак інший приклад багатозначок з документації scikit-learn показує приклад багатозначок з цим рядком classif = OneVsRestClassifier(SVC(kernel='linear')). Спантеличений.
lucid_dreamer

8

Я щойно натрапив на це, і проблема для мене полягала в тому, що мій y_Train був послідовністю рядків, а не послідовністю послідовностей рядка. Очевидно, OneVsRestClassifier на основі формату вхідної мітки вирішить, чи використовувати мультиклас проти багатоміток. Тож змініть:

y_train = ('New York','London')

до

y_train = (['New York'],['London'])

Очевидно, це зникне в майбутньому, оскільки розриви всіх міток однакові: https://github.com/scikit-learn/scikit-learn/pull/1987


8

Змініть цей рядок, щоб він працював у нових версіях python

# lb = preprocessing.LabelBinarizer()
lb = preprocessing.MultiLabelBinarizer()

2

Кілька прикладів мульти класифікації подано нижче: -

Приклад 1: -

import numpy as np
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()

arr2d = np.array([1, 2, 3,4,5,6,7,8,9,10,11,12,13,14,1])
transfomed_label = encoder.fit_transform(arr2d)
print(transfomed_label)

Вихід є

[[1 0 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 1 0 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 1 0 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 1 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 1 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 1 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 1 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 1 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 1 0 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 1 0]
 [0 0 0 0 0 0 0 0 0 0 0 0 0 1]
 [1 0 0 0 0 0 0 0 0 0 0 0 0 0]]

Приклад 2: -

import numpy as np
from sklearn.preprocessing import LabelBinarizer
encoder = LabelBinarizer()

arr2d = np.array(['Leopard','Lion','Tiger', 'Lion'])
transfomed_label = encoder.fit_transform(arr2d)
print(transfomed_label)

Вихід є

[[1 0 0]
 [0 1 0]
 [0 0 1]
 [0 1 0]]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.