Machine Learning, Text mining

Мультиклассовая классификация текста

Время прочтения: 3 мин.

Задача заключалась в проверке человеческих ошибок. Существуют транзакции и их назначения платежей. По итогу данных назначений, сотрудники уже распределяют по категориям платежей. При этом другие сотрудники уже проверяют вручную, огромную массу данных, чтобы облегчить им работу, давайте обучим модель и посмотрим, как она справится с данной задачей.

Датасет у нас выглядит банально, столбец с данными и столбец с классом. Посмотрим на наши данные:

Для начала посмотрим количество семплов для каждого класса:

train['target'].value_counts()
2     20439
12    19847
11    10411
32     5445
14     3598
17      869
15      351

Видим, что выборка не сбалансированная, но к счастью нет единичных и очень маленьких классов.

Почистим данные от мусора, знаков пунктуаций, спец символов, чисел. Так же от предлогов, имен собственных, которые записаны в отдельный файл «stop_rus_words.txt». В данном файле находятся имена собственные, аббревиатуры (оао, ооо, …), союз, предлоги, месяца, дни недели и многие другие обиходные слова, которые будут захламлять только нам выборку.

def del_stop_words(input, Rus_stop_words):
    try:
        return ' '.join([word for word in input.split() if word not in Rus_stop_words])
    except AttributeError:
	    pass
def delete(input):
    try:
        return ' '.join([word for word in input.split() if len(word)>2])
    except AttributeError:
	    pass
def standardize_text(df, text_field):
    df[text_field] = df[text_field].str.replace(r"http", "")
    df[text_field] = df[text_field].str.replace(r"[0-9(),.!?@\'\`\"\_\n\№\/\-\;\+\<\>]", "")
    df[text_field] = df[text_field].str.lower()
    return df
with open('stop_rus_words.txt', 'r', encoding="utf-8-sig") as fs:
        Rus_stop_words = fs.read().split(',')
fs.close()
train = pd.read_excel("new_train.xlsx")
train = standardize_text(train, "C_Text")
train['C_Text'] = train['C_Text'].map(lambda x: delete(x))
train['C_Text'] = train['C_Text'].map(lambda x: del_stop_words(x, Rus_stop_words))

Теперь нам необходимо преобразовать текст в числа, чтобы подать на обучение. Будем для этого использовать TfidfVectorizer, который преобразует текст в вектора, где каждое слово представляется в виде числа в зависимости от важности слова для документа. Так же используем N-граммы, это комбинации слов в словосочетания, что так же будет дополнительным признаком для модели. Забегу вперед, данная манипуляциия неплохо улучшило качество модели.

v1 = TfidfVectorizer(stop_words=stopwords.words('russian'), max_df=0.3, min_df=10)
vectorized_data1 = v1.fit_transform(train["C_Text"].values.astype('U'))
v2 = TfidfVectorizer(analyzer='char', ngram_range = (2,4))
vectorized_data2 = v2.fit_transform(train["C_Text"].values.astype('U'))

Объединим полученные признаки в одну матрицу:

all_data = hstack((vectorized_data1,vectorized_data2))

Обучим модель. Для примера был взят алгоритм опорных векторов.

logic = LinearSVC(random_state=42)
logic.fit(all_data, y)

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

pikle.dump(v1, open("Vectorizer1", "wb"))
pikle.dump(v2, open("Vectorizer2", "wb"))
pikle.dump(logic, open("Model", "wb"))

Теперь проверим качество модели:

Повторив все манипуляции с данными, при обучении.

vectorizer1 = pkl.load(open("Vectorizer1", "rb"))
vectorizer2 = pkl.load(open("Vectorizer2", "rb"))
vectorized_data1 = vectorizer1.transform(test_data["C_Text"].values.astype('U'))
vectorized_data2 = vectorizer2.transform(test_data["C_Text"].values.astype('U'))
all_test = hstack((vectorized_data1, vectorized_data2))
x = test_data['C_Text']
y = test_data['Target']
model = pkl.load(open("Model", "rb"))
predict_test = model.predict(all_test)
print("All Accuracy : ", accuracy_score(y, predict_test))

All Accuracy :  0.9419894963482981

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

from sklearn.metrics import classification_report
print(classification_report(y, predict_test))
    	       precision    recall  f1-score   support

           2       0.99      0.95      0.97     74320
          11       0.95      0.93      0.94     33682
          12       0.92      0.98      0.95     42441
          14       0.93      0.98      0.96     10866
          15       0.93      0.95      0.94      2037
          17       0.99      0.87      0.93      3366
          32       0.83      0.84      0.83     19602

    accuracy                           0.94    187169
   macro avg       0.84      0.83      0.83    187169
weighted avg       0.94      0.94      0.94    187169

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

Советуем почитать