Время прочтения: 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
В целом, довольно неплохой результат, учитывая, что взята легкая модель, и не было сложных манипуляций с данными и параметрами модели.