Аналитическая подсистема интернет-сервиса подбора мероприятий

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

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 04.07.2018
Размер файла 1,1 M

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

? group.getFinishDate() * MILLIS_IN_SECOND

: group.getStartDate() * MILLIS_IN_SECOND + DEFAULT_DURATION

);

return calendar;

}

@Override

public String getImageUrl() {

return group.getPhoto200();

}

@Override

public String getExtId() {

return group.getId().toString();

}

@Override

public String getSystemId() {

return VK_SYSTEM_ID;

}

@Override

public int getMembersCount() {

return group.getMembersCount();

}

@Override

public List<Category> getCategories() {

return emptyList();

}

@Override

public double getLatitude() {

return group.getPlace() != null ? group.getPlace().getLatitude() : 0;

}

@Override

public double getLongitude() {

return group.getPlace() != null ? group.getPlace().getLatitude() : 0;

}

}

Источник мероприятий представлен классом VkEventSource. Основной метод:

@Override

public Iterable<Event> getNew() {

VkProcedure.Query query = searchFactory.nextRequest();

List<GroupFull> groups = getGroups(query);

return groups.stream()

.filter(groupFull -> groupFull.getIsClosed() != null

&& groupFull.getIsClosed() == GroupIsClosed.OPEN)

.map(VkEvent::new)

.collect(Collectors.toList());

}

“Вконтакте” в запросах на поиск групп не позволяет передать пустую строку в качестве поискового запроса. Для решения этой проблемы создан класс интерфейс VkSearchFactory, отдающий последовательно необходимые поисковые запросы.

Большинство мероприятий в своём названии или описании содержат число, месяц или год своего проведения. Для формирование соответствующих запросов отвечает реализация WithNumbersVkSearchFactory.

За запуск запроса по расписанию и сохранение результатов отвечает класс EventScheduler:

@Scheduled(fixedDelay = 10000)

public void load() {

for (Event event : source.getNew()) {

new CatchedStoredEvent(new JpaStoredEvent(new JpaEvent(event), repository)).save();

}

}

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

Запрос:

SELECT count(*) from events WHERE events.events.system_id = 'VK';

На пустой базе данных возвращает результат 0.

После минуты выполнения результат 221.

3.4 Реализация и тестирование приложения API

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

Поскольку эта задача является типичной, SpringBoot позволяет одним интерфейсом объявить такую функциональность:

@RepositoryRestResource(collectionResourceRel = "events", path = "events")

public interface EventAutoController extends PagingAndSortingRepository<JpaEvent, JpaEvent.EventId>{

}

За аутентификацию отвечает класс AuthController.

@RequestMapping(path = "auth")

public AuthResponse authenticate(@Param("userId")String userName, @Param("password") String password) {

User user = users.get(userName);

if (user == null || !user.getPassword().equals(password)) {

return fail("Неверное имя пользователя или пароль.");

} else {

String token = UUID.randomUUID().toString();

user.setToken(token);

return AuthResponse.success(token);

}

}

Проверить работу приложения можно выполнив http GET запрос:

http://хост:порт/api/v1/events

Результат выполнения представлен на рисунке 3.2.

Рисунок 3.2 - Пример ответа от API

4. Решение задачи классификация мероприятий по категориям

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

4.1 Выгрузка из базы данных

Получить количество мероприятий можно запросом:

select system_id, count(*) from events GROUP BY events.events.system_id;

CULTURE13559

VK8625

Выгрузим данные из базы данных в CSV файл для удобства дальнейшей обработки командой:

select e.name, e.description, ec.categories_name, DATE_FORMAT(e.start_time,'%H:%i') TimeOfStart, DATEDIFF(e.finish_time, e.start_time) days_duration, DAYOFWEEK(e.start_time) dayOfWeek, e.start_time, e.finish_time

from events e LEFT OUTER JOIN events_categories ec

ON e.ext_id = ec.event_ext_id

AND e.system_id = ec.event_system_id

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

4.2 Подготовка данных для обучения

4.2.1 Предварительная подготовка

Загрузим данные из CSV файла и заполним Pandas DataFrame:

name_set = ['Title', 'Description', 'Category', 'StartTime',

'DurationDays', 'WeekDay', 'StartDateTime', 'FinishDateTime'];

frame = pd.read_csv('events.tsv', sep='\t', names = name_set, encoding='utf-8')

Очистим некорректно загруженные данные:

frame = frame.dropna()

Выведем типы данных, с которыми создался DataFrame:

frame.dtypes

Title object
Description object
Category object
StartTime object
DurationDays int64
WeekDay int64
StartDateTime object
FinishDateTime object

Приведём даты к соответствующему типу:

frame.StartDateTime = frame.StartDateTime.apply(pd.to_datetime)

frame.FinishDateTime = frame.FinishDateTime.apply(pd.to_datetime)

Для обучения можем использовать только мероприятия с категориями, получим их:

nn_frame = frame[frame.Category.isnull() == False]

На этом загрузку выборки можно считать завершённой. Выведем результат:

nn_frame.info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 12466 entries, 0 to 20895
Data columns (total 8 columns):
Title 12466 non-null object
Description 12466 non-null object
Category 12466 non-null object
StartTime 12466 non-null object
DurationDays 12466 non-null int64
WeekDay 12466 non-null int64
StartDateTime 12466 non-null datetime64[ns]
FinishDateTime 12466 non-null datetime64[ns]
dtypes: datetime64[ns](2), int64(2), object(4)
memory usage: 876.5+ KB

4.2.2 Выделение признаков

В выборке содержится дата и время начала мероприятия, но такая информация представляется компьютеру в виде числа long, выделим из неё существенную для решения задачи часть.

Выделим часть суток, в которую начинается мероприятие - ночь, утро, день, вечер:

day_parts = ['Ночь', 'Утро', 'День', 'Вечер']

nn_frame['DayPart'] = nn_frame.StartTime.apply(lambda st : time.strptime(st, '%H:%M')[3] / 6)

Выделим номер месяца начала мероприятия:

nn_frame['Month'] = nn_frame.StartDateTime.apply(lambda dt : dt.month)

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

nn_frame['DayPart'] = nn_frame.StartTime.apply(lambda st : time.strptime(st, '%H:%M')[3] / 6)

Приведём категории к числовому виду. Список всех категорий можно получить командой:

categories = DataFrame(nn_frame.Category.unique(), columns={'name'})

categories['index'] = range(len(categories))

categories

В таблице 4.1 представлены все категории.

Таблица 4.1 - DataFrame с категориями

name

index

0

Спектакли

0

1

Встречи

1

2

Прочие

2

3

Обучение

3

4

Праздники

4

5

Концерты

5

6

Выставки

6

4.2.3 Анализ признаков

Проанализируем добавленные и существующие признаки на информативность. Для этого построим гистрограммы распределения признаков. На рисунке 4.1 представлено распределение всех численных признаков на всех объектах.

Рисунок 4.1 - Распределение численных признаков по мероприятиям

Из рисунка 4.1 можно сделать следующие выводы:

1. Мероприятия распределены по категориям неравномерно, мероприятий категории 1 примерно в 10 раз больше мероприятий категории 2. Такая выборка называется несбалансированной.

2. Большинство событий начинается утром.

3. Есть события, которые длятся до 1500 дней, но их немного.

4. Примерно половина мероприятий длятся не больше дня.

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

Длительность мероприятия в днях и однодневность мероприятия линейно зависят. Исключим длительность в днях.

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

Рисунок 4.2 Распределение по части дня.

Заметно, что большинство встреч и выставок начинается с утра, а концертов и спектаклей - вечером. Данный признак полезен.

На рисунке 4.3 представлено распределение признака, является ли событие однодневным.

Рисунок 4.3 - распределение по однодневности мероприятий.

Видно что большинство праздников, встреч и концертов однодневные, а спектакли и выставки нет. Признак также полезен, поскольку разделяет объекты.

Признаки названия и описания анализировать на полезность не будем, поскольку это очевидно.

4.2.4 Подготовка обучающей и тестовой выборок

Разделим имеющуюся выборку случайным образом на обучающую и тестовую в соотношении 70% и 30% от начальной:

train_data, test_data, train_labels, test_labels = cross_validation.train_test_split(nn_frame, nn_frame.CategoryN, test_size=0.3)

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

counts = train_labels.value_counts()

counts.sort_index()

0 1342

1 3062

2 413

3 234

4 734

5 1021

6 1920

Выборка является сильно несбалансированной. Можно применить подход undersampling для доведения количества объектов каждого класса до необходимого путём случайного дублирования имеющихся объектов. Доведём количество объектов для каждого класса в обучающей выборке до 3000.

resampled_train_data = []

resampled_train_labels = []

for i in categories.index:

resampled_train_data.append(resample(train_data[train_data.CategoryN == i], n_samples = 3000, random_state=0))

resampled_train_labels.append(resample(train_labels[train_labels == i], n_samples = 3000, random_state=0))

train_data = pd.concat(resampled_train_data)

train_labels = pd.concat(resampled_train_labels)

Удалим неиспользуемые признаки:

train_data = train_data.drop(['Category', 'StartTime', 'StartDateTime', 'FinishDateTime', 'CategoryN'], axis=1)

test_data = test_data.drop(['Category', 'StartTime', 'StartDateTime', 'FinishDateTime', 'CategoryN',], axis=1)

4.2.5 Векторизация признаков

В выборке имеются следующие типы признаков:

1. бинарные - OneDay;

2. категориальные - WeekDay, DayPart;

3. текстовые - Title, Description

Бинарные типы не нуждаются в дополнительной обработке.

Категориальные признаки записаны близкими числами. Если оставить их в таком виде, то алгоритм увидеть несуществующие зависимости, поскольку близость номеров категорий не отражает их похожесть в бизнес-области. Для векторизации категориальных признаков может быть использован подход one-hot. В этом случае 1 категориальный признак с N возможными вариантами заменяется N бинарными признаками, отвечающими на вопрос, относится ли объект к текущей категории.

Текстовые признаки также должны быть отражены в векторное пространство. Для этого может быть использован наиболее интуитивный алгоритм “Мешок слов”:

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

для каждого документа #i посчитать количество употреблений каждого слова w и сохранить его (количество) в X[i, j]. Это будет значение признака #j, где j -- это индекс слова w в словаре.

Большинство значений в X являются нулями, поэтому «мешок слов» чаще всего является высокоразмерным разреженным набором данных.

Матрицы scipy.sparse -- это структуры данных, оптимизирующие хранение высокоразреженных структур.

Подсчёт словоупотреблений не учитывает длину текста, что может быть неправильно интерпретировано алгоритмом. Для учёта длины текста достаточно разделить количество употреблений каждого слова в документе на общее количество слов в документе. Этот признак называется tf -- Частота термина.

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

Это снижение называется tf-idf, что значит “Term Frequency times Inverse Document Frequency” (обратная частота термина).

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

transformer = FeatureUnion(transformer_list = [

('week_day_processing', Pipeline(steps = [

('selecting', ItemSelector(key='WeekDay')),

('hot_encoding', preprocessing.OneHotEncoder(handle_unknown = 'ignore'))

])),

('day_part_processing', Pipeline(steps = [

('selecting', ItemSelector(key='DayPart')),

('hot_encoding', preprocessing.OneHotEncoder(handle_unknown = 'ignore'))

])),

('one_day_processing', ItemSelector(key='OneDay') ),

('title_processing', Pipeline(steps = [

('selecting', ItemSelector2(key='Title')),

('vect', CountVectorizer()),

('tfidf', TfidfTransformer())

])),

('description_processing', Pipeline(steps = [

('selecting', ItemSelector2(key='Description')),

('vect', CountVectorizer()),

('tfidf', TfidfTransformer())

]))

])

4.3 Обучение линейного классификатора

Воспользуемся реализацией линейного стохастического спуска пакета sklearn и обучим модель с помощью пайплайна.

%%time

sgd_pipe = Pipeline(steps = [

('feature_processing', transformer),

('model_fitting', SGDClassifier())

]

)

sgd_pipe.fit(train_data, train_labels)

CPU times: user 3.23 s, sys: 63.2 ms, total: 3.29 s
Wall time: 2.87 s

Оценим качество обучения на тестовой выборке с помощью метки точности, то есть отношения правильно определённых классов к общему количеству объектов.

metrics.accuracy_score(test_labels, sgd_pipe.predict(test_data))

Точность на тестовой выборке составила 0.886

На рисунке 4.4 представлена диаграмма ожидания/предсказания для обучающей выборки(слева) и тестовой(справа). Чем ближе точки к диагонали, тем выше точность предсказания.

Видно, что классификатор сильнее всего ошибается на категории 1.

Рисунок 4.4 - Диаграмма ожидание/предсказание для линейного классификатора

4.4 Обучение решающего дерева

Воспользуемся реализацией случайного решающего дерева. Обучим дерево:

%%time

tree_pipe = Pipeline(steps = [

('feature_processing', transformer),

('model_fitting', RandomForestClassifier())

]

)

tree_pipe.fit(train_data, train_labels)

CPU times: user 6.62 s, sys: 53.8 ms, total: 6.68 s

Wall time: 6.25 s

Оценим точность предсказания на тестовой выборке:

metrics.accuracy_score(test_labels, tree_pipe.predict(test_data))

0.792

На рисунке 4.5 представлена диаграмма ожидание/предсказание для случайного дерева. Заметно, что классификатор снова сильнее всего ошибается на категории 1. Отсутствие ошибок на предсказании по обучающей выборке говорит о том, что дерево переобучено.

Рисунок 4.5 - Диаграмма ожидание/предсказание для случайного дерева

4.5 Обучение Байесовского классификатора

Воспользуемся реализацией наивного Байесовского классификатора MultinomialNB. Обучим классификатор на параметрах по умолчанию:

%%time

nb_pipe = Pipeline(steps = [

('feature_processing', transformer),

('model_fitting', MultinomialNB())

]

)

nb_pipe.fit(train_data, train_labels)

CPU times: user 2.69 s, sys: 50.8 ms, total: 2.74 s
Wall time: 2.51 s

Оценим точность на тестовой выборке:

metrics.accuracy_score(test_labels, nb_pipe.predict(test_data))

0.819

На рисунке 4.6 представлена диаграмма ожидание/предсказание для наивного Байесовского классификатора. Заметно, что классификатор также сильнее всего ошибается на категории 1.

Рисунок 4.6 - диаграмма ожидание/предсказание для наивного Байесовского классификатора

4.6 Выбор классификатора

Наиболее точным из рассмотренных классификаторов оказался линейный классификатор стохастического градиентного спуска. Предскажем с помощью него категорию для мероприятий из другой внешней системы. В таблице 4.3 представлен результат для случайных 10 мероприятий.

Таблица 4.3 - Результат предсказания для новых данных

Title

Description

prediction

1

Пейнтбол (Paintball) 10.06.18 KAVA Днепр

10 Июня 2018 ПЕЙНТБОЛ будет проходить на ...

Обучение Name: name, dtype: object

2

Барабанный Фестиваль Drummers United 2018!

Drummers United 2018 это:\n- отдельный павильо...

5 Концерты Name: name, dtype: object

3

Сальса-бачата пати! 12 июня, вторник, "ПЧЕЛА"

Сальса, бачата, ча-ча-ча и другие латиноамерик...

1 Встречи Name: name, dtype: object

5

Фестиваль КАМПУС ФЕСТ 15/09/17

«Кампус фест 2017» - фестиваль под открытым не...

4 Праздники Name: name, dtype: object

6

Ретрит с Empty_Mirror на Алтае 1-10 августа 2018

То, чем Вы являетесь, не требует осмысления. Р...

3 Обучение Name: name, dtype: object

8

Фестиваль РОК ОТ РОКА 2018 * 14 июля * 16:00

Четвертый по счёту фестиваль "РОК ОТ РОКА" от ...

5 Концерты Name: name, dtype: object

10

21 июля. Пикник Long Hair Meetings в Киеве

Собираемся с целью отдохнуть на природе после ...

1 Встречи Name: name, dtype: object

11

Встречи с Empty_Mirror в Украине 20.02-3.03.2019

То, чем Вы являетесь, не требует осмысления. Р...

2 Прочие Name: name, dtype: object

12

[10.06.18] Городской ЛАН-турнир по DOTA 2 MIX 53

"ViewOfGames Mix Open" - это открытый городско...

1 Встречи Name: name, dtype: object

13

16 июня Спонтанное жестовое рисование СПб

Нарисуй свои переживания, ощути форму эмоций и...

3 Обучение Name: name, dtype: object

Субъективно, классификатор верно предсказал категории всех событий, кроме 1 (Пейнтбол != обучение). Точность предсказания в 90% на новых данных соответствует точности предсказания на обучающей выборке. На основании проверки на новых данных принято решение о внедрении данного классификатора в эксплуатацию.

Для применения настроенного алгоритма может быть написан исполняемые скрипт обновления мероприятий по расписанию на языке Python или параметры алгоритма могут быть перенесены в полноценный модуль на языке Java.

Заключение

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

Разработанная система представляет собой масштабируемый комплекс программных и инженерных решений. Приложения разработаны на языке Java с использованием фреймворка SpringBoot и реализуют микросервисный подход.

Разработан процесс непрерывной поставки и интеграции с использованием практик DevOps и инструментов Jenkins, Docker, Maven.

Разработаны приложения-адаптеры для интеграции с внешними API социальной сети “Вконтакте” и сервисом данных АИС ЕИПСК. Опубликовано Rest API для получения мероприятий.

Обозначена и решена задача классификации мероприятий по известным категориям. В рамках решения задачи произведён анализ и подготовка данных, выделены и векторизованы признаки, обучены линейный классификатор на основе стохастического градиентного спуска, классификатор на основе случайного дерева, наивный Байесовский классификатор.

Лучший результат на метрике точности предсказания показал линейный классификатор - 89%. Наивный Байесовский классификатор показал точность 82%, классификатор на основе дерева - 79%.

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

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

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

Список использованных источников

1. Методические рекомендации по работе с АИС «Единое информационное пространство в сфере культуры» [Электронный ресурс] // Министерство культуры Российской Федерации: сайт. - Режим доступа: https://www.mkrf.ru/documents/po-rabote-s-ais-edinoe-informatsionnoe-prostranstvo-v-sfere-kultury/.

2. Открытые данные [Электронный ресурс] // Портал открытых данных министерства культуры Российской Федерации: сайт. - Режим доступа: http://opendata.mkrf.ru/.

3. Сервис для информационных партнёнров АИС ЕИПСК [Электронный ресурс] // АИС «Единое информационное пространство в сфере культуры»: сайт. - Режим доступа: https://all.culture.ru/public/.

4. Разработчикам [Электронный ресурс] // Вконтакте. Разработчикам: сайт. - Режим доступа: https://vk.com/dev/.

5. Задача классификации [Электронный ресурс] // Википедия свободная энциклопедия: сайт. - Режим доступа: https://ru.wikipedia.org/wiki/Задача_классификации.

6. Классификация [Электронный ресурс] // MachineLearning.Ru: сайт. - Режим доступа: http://www.machinelearning.ru/wiki/index.php?title=Классификация.

7. Коэльо Л.П., Ричарт В. Построение систем машинного обучения на языке Python. 2016. 302 с.

8. Pandas [Электронный ресурс] // Википедия свободная энциклопедия: сайт. - Режим доступа: https://ru.wikipedia.org/wiki/Pandas.

9. scikit-learn [Электронный ресурс] // The Python Package Index: сайт. - Режим доступа: https://pypi.org/project/scikit-learn/0.19.1.

10. Руководство [Электронный ресурс] // Морфологический анализатор pymorphy2: сайт. - Режим доступа: https://pymorphy2.readthedocs.io/en/0.2/user/index.html

11. SpringFramework [Электронный ресурс] // Spring по-русски: сайт. - Режим доступа: http://spring-projects.ru/projects/spring-framework.

12. Что такое Докер? [Электронный ресурс] // Amazon Web Services: сайт. - Режим доступа: https://aws.amazon.com/ru/docker/.

13. Ржеуцкая, С.Ю. Базы данных. Теоретические и языковые основы: учебное пособие - Вологда: ВоГУ, 2016. -112 с.

14. Каляшов Г. А., Кудряшов А.А. РАЗРАБОТКА РЕКОМЕНДАТЕЛЬНОЙ СИСТЕМЫ ДЛЯ МЕРОПРИЯТИЙ. - «Актуальные проблемы науки и практики в различных отраслях народного хозяйства» (Пенза, 28-29 марта 2018).

Размещено на Allbest.ru


Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.