Задача анализа тональности отзывов о банках

Карасиков Михаил, CS, 2 курс

Постановка задачи

Будем определять полярность документа по бинарной в шкале, используя два класса оценок: позитивная или негативная.

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

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

Таким образом сводим исходную задачу к задаче классификации на три класса. Для обучения классификатора будем использовать стандартный подход обучения с учителем.

Поставим задачу формально.

Дано:

  • $X$ -- отзывы
  • $Y = \{-1, 0, 1\}$ -- оценки. $0$ -- отказ от классификации (отзыв нейтральный, написан некорректно -- не русский язык или сленг или классификатор недостаточно уверен в ответе).

Построить классификатор $a:\;X\to Y$, приближающий целевую зависимость на обучающей выборке $(X^\text{train},y^\text{train})$.

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

  • positive -- позитивный,
  • negative -- негативный,
  • refused -- отказ от классификации.

Для оценки качества будем использовать метрику accuracy для двух классов распознанных объектов и долю объектов с отказом от классификации. Эти метрики были бы понятны любому заказчику.

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

  • язык русский без сленга
  • отзыв состоит минимум из трех слов

Загрузка данных

Воспользуемся открытой базой отзывов сайта www.banki.ru. Для этого предварительно соберем данные с сайта краулером scrapy.

С сайта www.banki.ru за ночь удалось загрузить лишь около 5к примеров (прямым переборов ссылок только ~1% оказываются корректными). На следующий день докачать данные уже не получилось, потому что доступ к сайту заблокировали, видимо, по IP.

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

Загружаем собранные данные

In [1]:
import json

data = []
with open('banki.json', 'r') as f:
    for line in f:
        if len(line):
            try:
                data.append(json.loads(line))
            except:
                pass

Удалим отзывы без оценок.

In [2]:
data = [response for response in data if response['score']]
In [3]:
import pandas as pd

df = pd.DataFrame(data)
df.score = df.score.astype(int)
df
Out[3]:
bank comments datetime score text title views
0 Уральский Банк Реконструкции и Развития 1 2015-05-18 10:35:11 3 \n\t\t\t\t\tВыбрал этот банк, т.к. он является... \n\t\t\t\t\t\t\t\t\t\tНе соответствие обслужив... 894
1 Промсвязьбанк 2 2015-05-18 10:39:00 5 \n\t\t\t\t\tЯ недавно стала клиентом Промсвязь... \n\t\t\t\t\t\t\t\t\t\tБлагодарность\n\t\t\t\t 258
2 ВТБ 24 1 2015-05-18 10:45:15 1 \n\t\t\t\t\tОрганизация отправляет валютные пл... \n\t\t\t\t\t\t\t\t\t\tНе устраивает работа бан... 407
3 Альфа-Банк 0 2015-05-18 11:23:32 1 \n\t\t\t\t\tПридя в офис, расположенный около ... \n\t\t\t\t\t\t\t\t\t\tОткрытие карты\n\t\t\t\t 244
4 Банк РСБ 24 (Русславбанк) 2 2015-05-18 11:33:37 1 \n\t\t\t\t\tОткрыла вклад в конце декабря, в  ... \n\t\t\t\t\t\t\t\t\t\tПолучение вклада\n\t\t\t\t 908
5 Московский Кредитный Банк 0 2015-05-18 11:35:00 1 \n\t\t\t\t\tТерминал МКБ расположенный по адре... \n\t\t\t\t\t\t\t\t\t\tКак я стал клиентом МКБ ... 370
6 Ситибанк 2 2015-05-18 11:36:00 5 \n\t\t\t\t\tВыражаю Благодарность Мальгину Арт... \n\t\t\t\t\t\t\t\t\t\tБлагодарность за содейст... 402
7 Плюс Банк 2 2015-05-18 11:45:25 1 \n\t\t\t\t\tЗдравствуйте, почитала отзывы и уж... \n\t\t\t\t\t\t\t\t\t\tВозмущена обслуживанием ... 577
8 ВТБ 24 1 2015-05-18 11:46:34 1 \n\t\t\t\t\tПополнение счета. Придя в офис, ра... \n\t\t\t\t\t\t\t\t\t\tПеревод средств\n\t\t\t\t 245
9 Банк РСБ 24 (Русславбанк) 0 2015-05-18 11:51:24 2 \n\t\t\t\t\t10 мая 2015 г. на мой  счет, привя... \n\t\t\t\t\t\t\t\t\t\tСредства не зачисляются ... 360
10 Национальный Банк «Траст» 1 2015-05-18 12:00:00 1 \n\t\t\t\t\t4 Февраля мною было написано заявл... \n\t\t\t\t\t\t\t\t\t\tНе прошло досрочное пога... 227
11 Метробанк 0 2015-05-18 12:07:04 1 \n\t\t\t\t\tУ меня закончился срок вклада, но ... \n\t\t\t\t\t\t\t\t\t\tОтказ в выдаче денег по ... 454
12 БМ-Банк (бывш. Банк Москвы) 0 2015-05-18 12:05:00 5 \n\t\t\t\t\tДавно сотрудничаю с «Банком Москвы... \n\t\t\t\t\t\t\t\t\t\tБлагодарность\n\t\t\t\t 103
13 Сбербанк России 1 2015-05-18 12:13:51 1 \n\t\t\t\t\tПохоже, что Сбербанк не тестирует ... \n\t\t\t\t\t\t\t\t\t\tНа сайте Сбербанка не от... 235
14 ВТБ 24 0 2015-05-18 12:10:33 1 \n\t\t\t\t\tПисьмо ВТБ 24 от 02.02.2015 г. № Ф... \n\t\t\t\t\t\t\t\t\t\tЖалоба на Центр автокред... 411
15 Новопокровский 1 2015-05-18 12:07:54 5 \n\t\t\t\t\tОткрыл сегодня вклад в этом Банке.... \n\t\t\t\t\t\t\t\t\t\tХороший банк\n\t\t\t\t 374
16 Сбербанк России 11 2015-05-18 12:38:47 2 \n\t\t\t\t\t15 мая 2014г. пришел пополнить счё... \n\t\t\t\t\t\t\t\t\t\tСотрудники Сбербанка отк... 808
17 Хоум Кредит Банк 1 2015-05-18 12:08:00 3 \n\t\t\t\t\tПо-моему, единственный банк, в кот... \n\t\t\t\t\t\t\t\t\t\tОтсутствует выписка по к... 236
18 Сетелем Банк 0 2015-05-18 12:52:05 1 \n\t\t\t\t\tНа выходных с мужем решили присмот... \n\t\t\t\t\t\t\t\t\t\tДля банка мы не граждане... 439
19 Сетелем Банк 3 2015-05-18 12:56:30 1 \n\t\t\t\t\tДобрый день! \nХочу обратить внима... \n\t\t\t\t\t\t\t\t\t\tУжасное отношение к клие... 355
20 ОТП Банк 0 2015-05-18 13:02:00 1 \n\t\t\t\t\tДобрый день!\nЯвляюсь клиентом "ОТ... \n\t\t\t\t\t\t\t\t\t\tНеобоснованное списание ... 275
21 МТС Банк 3 2015-05-18 13:15:00 2 \n\t\t\t\t\t17 мая пытались оформить дебетовую... \n\t\t\t\t\t\t\t\t\t\tВранье в офисах МТС при ... 612
22 Сбербанк России 0 2015-05-18 13:17:57 1 \n\t\t\t\t\tДобрый день! Наш ТСЖ (ИНН 50450492... \n\t\t\t\t\t\t\t\t\t\tОтвратительное обслужива... 247
23 Сбербанк России 0 2015-05-18 13:20:49 1 \n\t\t\t\t\tПриехали с мужем за 500 км, специа... \n\t\t\t\t\t\t\t\t\t\tДобрый день!\n\t\t\t\t 269
24 Почта Банк 0 2015-05-18 13:31:35 1 \n\t\t\t\t\tОформляли кредитную карту по интер... \n\t\t\t\t\t\t\t\t\t\tПри одобренной и выпущен... 307
25 Сбербанк России 0 2015-05-18 13:49:49 1 \n\t\t\t\t\t28 апреля мне потребовалось срочно... \n\t\t\t\t\t\t\t\t\t\tСотрудник намеренно обма... 442
26 Российский Капитал 0 2015-05-18 13:51:22 5 \n\t\t\t\t\tЗдравствуйте, решил оставить отзыв... \n\t\t\t\t\t\t\t\t\t\tПонравилось обслуживание... 291
27 Почта Банк 0 2015-05-18 13:53:10 1 \n\t\t\t\t\t15.05.2015 брал кредит наличными в... \n\t\t\t\t\t\t\t\t\t\tСкрыли про страховку\n\t... 355
28 Россельхозбанк 1 2015-05-18 13:58:25 1 \n\t\t\t\t\tДобрый день! В нашем городе Курско... \n\t\t\t\t\t\t\t\t\t\tОбслуживание в банке\n\t... 606
29 Альфа-Банк 0 2015-05-18 14:02:06 5 \n\t\t\t\t\tРаботаем с Альфа-Банком уже больше... \n\t\t\t\t\t\t\t\t\t\tОтзыв о работе с Альфа-Б... 206
... ... ... ... ... ... ... ...
5884 БМ-Банк (бывш. Банк Москвы) 0 2015-12-09 00:11:16 1 \n\t\t\t\t\tДобрый день, я являюсь зарплатным ... \n\t\t\t\t\t\t\t\t\t\tКредит со страховкой\n\t... 609
5885 ОТП Банк 0 2015-12-09 03:07:41 1 \n\t\t\t\t\tБанк отвратительный, обслуживание ... \n\t\t\t\t\t\t\t\t\t\tМой отзыв\n\t\t\t\t 994
5886 ВТБ 24 0 2015-12-09 02:33:00 3 \n\t\t\t\t\tПользуюсь услугами ВТБ 24 довольно... \n\t\t\t\t\t\t\t\t\t\tНазойливые агенты\n\t\t\t\t 581
5887 Восточный Экспресс Банк 0 2015-12-09 00:10:59 1 \n\t\t\t\t\tДень добрый! \nЗовут меня Б-нов Се... \n\t\t\t\t\t\t\t\t\t\tБанк ведет себя крайне н... 1646
5888 Совкомбанк 1 2015-12-09 06:23:27 5 \n\t\t\t\t\tЯ пользовалась пару раз кредитами ... \n\t\t\t\t\t\t\t\t\t\tВыгодно по финансам и пр... 428
5889 Сетелем Банк 1 2015-12-09 07:22:56 3 \n\t\t\t\t\tДорогие друзья!\nНе думала, что пр... \n\t\t\t\t\t\t\t\t\t\tПросьба решить вопрос\n\... 369
5890 Тинькофф Банк 0 2015-12-09 07:06:13 5 \n\t\t\t\t\tНе знаю почему у многих проблемы. ... \n\t\t\t\t\t\t\t\t\t\tБез нареканий\n\t\t\t\t 646
5891 Альфа-Банк 0 2015-12-09 07:44:21 2 \n\t\t\t\t\tЯ являюсь зарплатным клиентом этог... \n\t\t\t\t\t\t\t\t\t\tОбман или специально нед... 551
5892 Сбербанк России 2 2015-12-09 07:44:46 5 \n\t\t\t\t\tХочу выразить огромную благодарнос... \n\t\t\t\t\t\t\t\t\t\tПодключение услуги Сберб... 316
5893 Глобэкс 2 2015-12-09 02:10:09 1 \n\t\t\t\t\tДобрый день!\n04.12.2015 г. я обра... \n\t\t\t\t\t\t\t\t\t\tХамское отношение к клие... 734
5894 Ренессанс Кредит 1 2015-12-09 01:45:12 1 \n\t\t\t\t\t21 февраля 2013 года я со своей су... \n\t\t\t\t\t\t\t\t\t\tПолное отсутствие клиент... 605
5895 Альфа-Банк 42 2015-12-09 01:39:06 1 \n\t\t\t\t\t20 июня 2013 г. мной в отделении А... \n\t\t\t\t\t\t\t\t\t\tОбман от банка\n\t\t\t\t 4692
5896 ВТБ 24 7 2015-12-09 08:34:32 1 \n\t\t\t\t\t07.12.2015 решил я досрочно погаси... \n\t\t\t\t\t\t\t\t\t\tПро обиду\n\t\t\t\t 661
5897 Сбербанк России 0 2015-12-09 08:15:59 5 \n\t\t\t\t\tЗдравствуйте!\nХочу выразить благо... \n\t\t\t\t\t\t\t\t\t\tБлагодарность сотруднику... 277
5898 Сбербанк России 3 2015-12-09 09:26:04 1 \n\t\t\t\t\tПользуюсь дебетовыми картами Сберб... \n\t\t\t\t\t\t\t\t\t\tКогда кредиты не нужны -... 281
5899 Платежный Центр 3 2015-12-09 09:35:16 1 \n\t\t\t\t\t11.11.2015 года оплатил заказ на А... \n\t\t\t\t\t\t\t\t\t\tУкрали деньги. Взяли кре... 410
5900 Тойота Банк 0 2015-12-09 10:13:43 1 \n\t\t\t\t\tДобрый день, хотел бы оставить отз... \n\t\t\t\t\t\t\t\t\t\tРабота банка\n\t\t\t\t 392
5901 Российский Национальный Коммерческий Банк 1 2015-12-09 10:14:47 1 \n\t\t\t\t\tРНКБ. Ко времени получения зарплат... \n\t\t\t\t\t\t\t\t\t\tНе возможно получить ден... 428
5902 ВТБ 24 2 2015-12-09 10:15:03 1 \n\t\t\t\t\tОткрыл ИП, расчетный счет в ВТБ! С... \n\t\t\t\t\t\t\t\t\t\tМалому бизнесу в ВТБ 24 ... 471
5903 Сбербанк России 1 2015-12-09 10:21:59 1 \n\t\t\t\t\tЗдравствуйте! \nМеня зовут С-ва Ел... \n\t\t\t\t\t\t\t\t\t\tСписание денежных средст... 245
5904 Модульбанк 0 2015-12-09 10:31:32 5 \n\t\t\t\t\tПользуюсь Модульбанком с самого на... \n\t\t\t\t\t\t\t\t\t\tОтличный банк для предпр... 231
5905 Сбербанк России 1 2015-12-09 10:42:54 1 \n\t\t\t\t\tБезуспешно пытался открыть счет и ... \n\t\t\t\t\t\t\t\t\t\tНевозможно дождаться обс... 239
5906 Генбанк 0 2015-12-09 10:36:17 1 \n\t\t\t\t\tУже второй день пытаюсь заплатить ... \n\t\t\t\t\t\t\t\t\t\tОтделение отказывается п... 405
5907 ОТП Банк 0 2015-12-09 10:45:50 1 \n\t\t\t\t\t15.01.2015 я обратилась в магазин ... \n\t\t\t\t\t\t\t\t\t\tНекомпетентность сотрудн... 961
5908 Альфа-Банк 4 2015-12-09 11:12:44 1 \n\t\t\t\t\tЕсли позвонить в службу поддержки,... \n\t\t\t\t\t\t\t\t\t\tКак мне кажется, меня фа... 546
5909 Сбербанк России 1 2015-12-09 11:11:32 1 \n\t\t\t\t\tУже второй день, не могу зайти в с... \n\t\t\t\t\t\t\t\t\t\tУже второй день не могу ... 238
5910 Русский Стандарт 1 2015-12-09 11:45:56 5 \n\t\t\t\t\tДавно пользуюсь услугами банка Рус... \n\t\t\t\t\t\t\t\t\t\tУже давно с РС\n\t\t\t\t 590
5911 Сбербанк России 2 2015-12-09 11:43:21 1 \n\t\t\t\t\t21 ноября В отделении Сбербанка по... \n\t\t\t\t\t\t\t\t\t\tНезачисление денег на ка... 265
5912 Русский Стандарт 3 2015-12-09 11:50:31 5 \n\t\t\t\t\tОбразовалась задолженность по кред... \n\t\t\t\t\t\t\t\t\t\tСпасибо!!!\n\t\t\t\t 651
5913 Смартбанк 1 2015-12-09 11:59:28 1 \n\t\t\t\t\tИмею карту банка (для % по вкладу)... \n\t\t\t\t\t\t\t\t\t\tНе перекладывайте свои п... 294

5914 rows × 7 columns

Посмотрим на распределение оценок.

In [4]:
from matplotlib import pyplot as plt
import numpy as np
%matplotlib inline

df.score = df.score.apply(int)
df.score.hist(bins=5, range=[0.5, 5.5])
plt.show()

Почитав отзывы на сайте, можно заметить, что отзывы с оценками 1 и 2 являются отрицательными, а с оценкой 5 --- положительными. Остальные отзывы будем считать нейтральными и отказываться от их классификации.

Самый короткий отзыв:

In [5]:
df.iloc[np.argmin([len(x) for x in df.text])]
Out[5]:
bank                                                Ярсоцбанк
comments                                                    0
datetime                                  2005-04-28 15:01:00
score                                                       3
text        \n\t\t\t\t\tОхранники у банка слишком злые.\n\...
title               \n\t\t\t\t\t\t\t\t\t\tОхранники\n\t\t\t\t
views                                                     119
Name: 842, dtype: object

Составим обучающую выборку.

In [6]:
y = np.zeros(df.shape[0])

y[np.array(df.score == 5)] = 1
y[np.array(df.score < 3)] = -1

X = df.text
In [7]:
plt.hist(y)
plt.show()

Обучение

In [8]:
from sklearn.feature_extraction.text import TfidfVectorizer
import stop_words
from sklearn.decomposition import TruncatedSVD

stop_words = stop_words.get_stop_words('russian')
vectorizer = TfidfVectorizer(min_df=10, stop_words=stop_words, ngram_range=(1, 1))
dataset = vectorizer.fit_transform(X)
svd = TruncatedSVD(n_components=40, random_state=17)
dataset = svd.fit_transform(dataset)
In [9]:
print len(vectorizer.vocabulary_)
print dataset.shape
7412
(5914, 40)
In [10]:
def score(estimator, X, y):
    y_hat = estimator.predict(X)
    ind = y_hat != 0
    print (len(y) - sum(ind)) * 1. / len(y), np.mean(y_hat[ind] == y[ind])
    return np.mean(y_hat[ind] == y[ind])
In [11]:
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.cross_validation import cross_val_score

model = GradientBoostingClassifier(n_estimators=80, max_depth=4)
score_value = cross_val_score(model, dataset, y, cv=5, scoring=lambda f, X, y: score(f, X, y)).mean()
print '\nScore:', score_value
0.0320945945946 0.767015706806
0.0684121621622 0.756119673617
0.032967032967 0.763986013986
0.0169204737733 0.760757314974
0.00846740050804 0.769427839453

Score: 0.763461309767

Итак, точность на валидации 76%. Отказ от классификации в среднем происходит в 5% случаев.

In [12]:
from sklearn.pipeline import Pipeline

classifier = Pipeline([('vect', vectorizer),
                       ('svd', svd),
                       ('clf', model)])
classifier.fit(X, y)
Out[12]:
Pipeline(steps=[('vect', TfidfVectorizer(analyzer=u'word', binary=False, decode_error=u'strict',
        dtype=<type 'numpy.int64'>, encoding=u'utf-8', input=u'content',
        lowercase=True, max_df=1.0, max_features=None, min_df=10,
        ngram_range=(1, 1), norm=u'l2', preprocessor=None, smooth_idf=Tru...      presort='auto', random_state=None, subsample=1.0, verbose=0,
              warm_start=False))])
In [13]:
import pickle

with open('classifier.pkl', 'wb') as f:
    pickle.dump(classifier, f)
In [14]:
import pickle

with open('classifier.pkl', 'rb') as f:
    classifier = pickle.load(f)
In [15]:
classifier.predict
Out[15]:
<function sklearn.pipeline.predict>

Демонстрация работы

Для демонстрации работы алгоритма использовался micro web framework Flask. Скрипт загружает обученную модель и при помощи Flask формирует html страницу. После ввода пользователем отзыва в поле, ответ классификатора передается на страницу. Таким образом результат показывается пользователю.

Для публичного доступа к приложению была развернута связка Flask и Apache через WSGI (Web Server Gateway Interface).

Демонстрация работы алгоритма доступна по адресу link.