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

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

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

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

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

39. Pickle - объект Python сериализации // Python Software Foundation [Электронный ресурс] URL: https: // docs. python.org/3/library/pickle.html/ (дата обращения: 09.04.2017).

Приложения

Приложение B. Диаграмма прецедентов для бизнес-процесса "Анализ данных"

Рисунок B.1. Диаграмма прецедентов

Приложение С. Диаграмма активности "Загрузка данных"

Рисунок С.1. Диаграмма активности для прецедента "Загрузка данных"

Приложение D. Диаграмма активности "Группировка данных"

Рисунок D.1. Диаграмма активности для прецедента "Группировка данных"

Приложение E. Диаграмма активности "Поиск ассоциаций"

Рисунок E.1. Диаграмма активности для прецедента "Поиск ассоциаций в данных"

Приложение F. Диаграмма активности "Поиск дубликатов"

Рисунок F.1. Диаграмма активности для прецедента "Поиск дубликатов"

Приложение G. Диаграмма последовательности "Группировка"

Рисунок G.1. Диаграмма последовательности "Группировка данных"

Приложение H. Диаграмма последовательности "Поиск ассоциативных правил"

Рисунок H.1. Диаграмма последовательности "Поиск ассоциативных правил"

Приложение J. Диаграмма последовательности "Поиск дубликатов"

Рисунок J.1. Диаграмма последовательности "Поиск дубликатов"

Рисунок J.2. Диаграмма последовательности "Поиск дубликатов" (продолжение)

Рисунок J.3. Диаграмма последовательности "Поиск дубликатов" (продолжение)

Рисунок J.4. Диаграмма последовательности "Разметка данных для поиска дубликатов"

Приложение K. HTTP методы для микросервисов

Таблица K.1. Проектирование API для микросервисов

Тип метода

Название

Строка запроса

Входные параметры (задает клиент)

Выходные (возвращает сервер)

Микросервис группировки данных

POST

Запуск группировки данных

/data/{имя}/group/

Название данных,

параметры

Статус проведения группировки (ошибка или success)

GET

Получение списка сгруппированных данных (названий таблиц)

/data/names

Список имен таблиц

GET

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

/data/{имя}/clusters/ {смещение - r}/{кол-во кластеров - n}

Название данных, смещение (номер кластера, с которого начинать вывод), количество возвращаемых кластеров

Список кластеров (n штук начиная с r) с их объектами

GET

Получение объектов кластера по объекту

/data/{имя}/ cluster_by_object/{id объекта }/

Название данных, id объекта

Список объектов кластера, к которому принадлежит объект с указанным id

GET

Получение списка объектов кластера

/data/{имя}/clusters/ {кластер}

Название данных, номер кластера

Список объектов указанного кластера

GET

Получение сsv файла с результатами

/data/{имя}/get_file

Микросервис поиска ассоциативных правил

POST

Запуск поиска ассоциативных правил

data/{имя}/ search_rules/

Название данных, параметры

Статус проведения операции (ошибка или success)

GET

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

/data/names

Список имен таблиц

GET

Получение часто встречающихся наборов

/data/{имя}/ frequent_itemsets

Название данных

Список наборов

GET

Получение объектов, часто встречающихся с данными

/data/{имя}/ frequent_itemsets/ {items}

Название данных, список объектов

Список дополняющих объектов

GET

Получение списка правил

/data/{имя}/rules/ {смещение - r}/{кол-во правил}

Название данных, смещение (номер правила, с которого начинать вывод), количество возвращаемых правил

Список правил (n штук, начиная с r)

GET

Получение сsv файла с результатами

/data/{имя}/get_file

Микросервис поиска дубликатов

GET

Получение списка начатых процессов поиска дубликатов

/processes/

Список имен процессов

POST

Создание нового процесса (инициализация данных)

/processes/init_process

Название процесса, параметры

Сообщение о статусе операции

POST

Получение новой пары объектов для разметки и сохранение указанной

/processes/{имя}/ marking

Название процесса, пара объектов, результат

Новая пара объектов, идущая после указанной

GET

Получение списка обучающих выборок

/processes/{имя}/train_datasets

Название процесса

Список имен обучающих выборок процесса

POST

Обучение модели

/processes/{имя}/train/ {имя выборки}

Название процесса, название обучающей выборки

Сообщение о статусе операции

GET

Получение списка готовых моделей

/processes/{имя}/ models

Название процесса

Список имен моделей

POST

Создание матрицы сходства

/processes/{имя}/new_matrix/{имя модели}

Название процесса, название модели

Сообщение о статусе операции

GET

Получение списка матриц сходства

/processes/{имя}/ matrices

Название процесса

Список имен матриц сходства процесса

POST

Поиск групп дубликатов

/processes/{имя}/ dupl_search/{имя матрицы}

Название процесса, название матрицы сходства

Сообщение о статусе операции

GET

Получение сsv файла с результатами

/processes/{имя}/ get_file

Название процесса

GET

Получение группы дубликатов по объекту

/proсesses/{имя}/ group_by_object/{id объекта }/

Название процесса, id объекта

Список объектов группы, к которому принадлежит объект с указанным id

GET

Получение групп дубликатов

/processes/{имя}/dupl_groups/{смещение - r}/{кол-во групп - n}

Название процесса, смещение, количество групп

Список групп (n штук начиная с r) с их объектами

POST

Корректировка групп

/processes/{имя}/correct_groups/

Название прооцесса, список групп с их объектами

Сообщение о статусе операции

POST

Обобщение дубликатов

/processes/{имя}/unite _duplicates/

Название прооцесса, список групп с объектом-представителем

Сообщение о статусе операции

Микросервис работы с данными предоставляет лишь POST запросы для загрузки данных: /data/from_file, /data/add, /data/from_db, входными данными которых являются файл, объект и название коллекции из базы данных соответственно, а также параметры.

Приложение L. Технико-экономическое обоснование разработки

Расчет трудоемкости и срока разработки

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

Данная методика позволяет рассчитать трудоемкость разработки системы и ее стоимость. CETIN основывается на измерении функционального размера разрабатываемой системы. В ней определены пять функциональных единиц имерения: "C - Case", "E - Entity", "T - Tool", "I - Interaction", "N - Node".

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

Оценка функционального размера модуля анализа данных

На данном этапе производится подсчет каждой из функциональных единиц измерения: количество вариантов использования - C (диаграмма прецедентов), количество типов объектов - E (классы на диаграмме классов), количество свойст типов объектов - T (свойства классов), количество взаимодействий между типами объектов - I (отношения между классами), количество типов использования - N (типы узлов на диаграмме развертывания). На основе посторенной диаграммы прецедентов (рис. L.1) найдем величину C. Количество варинтов использования для модуля анализа равно 25. Следовательно, C равно 25. На рис. L.2 изображены типы и свойства объектов. На основе данной диаграммы получим следующие значения единиц измерения: E = 15 (количество классов), T = 33 (свойства всех классов, за исключением ссылок), I = 13 (количество связей).

На рис. L.3 изображена диаграмма с узлами системы. Получается, что N = 8 (2 БД, 4 микросервиса, диспетчер, клиент).

Рисунок L.1. Диаграмма прецедентов

Рисунок L.2. Типы и свойства объектов

Рисунок L.3. Узлы системы

Таблица L.1. Функциональный размер

Количество вариантов использования

Количество типов объектов

Количество свойств типов объектов

Количество взаимодействий между типами объектов

Количество узлов

SIZE

25

15

33

13

8

Расчет базовой трудоемкости разработки системы

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

Базовая трудоемкость Sj стадии с номером j рассчитывается по следующей формуле:

Sj=1/165· [C*Sj (C) +E*Sj (E) +T*Sj (T) +I*Sj (I) +N*Sj (N)].

Sj (X) - значение нормативного коэффициента (приведены в приложении A к методике CETIN).

Для каждой стадии находим базовую трудоемкость:

Таблица L.2. Расчет базовой трудоемкости по стадиям

Стадия

Расчет

Результат, чел. - мес.

S1

Бизнес-моделирование

1/165* (25*28+15*32+33*0+13*11+8*0) = 1/165* (700+480+143)

9

S2

Управление требованиями

1/165* (25*16+15*26+33*0+13*8+8*0) = 1/165* (400+390+104)

6

S3

Проектирование

1/165* (25*15+15*67+33*15+13*54+8*24) = 1/165* (375+1005+495+702+192)

17

S4

Реализация

1/165* (25*18+15*60+33*17+13*36+8*0) = 1/165* (450+900+561+468+0)

15

S5

Тестирование

1/165* (25*89+15*0+33*0+13*0+8*0) = 1/165* (2225)

14

S6

Развертывание

1/165* (25*5+15*0+33*0+13*0+8*30) = 1/165* (125+240)

3

Расчет трудоемкости с учетом поправочных коэффициентов

Расчет поправочных коэффициентов основывается на частных поправочных коэффициентах (указаны в разделе A.2 прил. А).

Таблица L.3. Расчет поправочных коэффициентов

Номер процесса

Обозначение поправочного коэффициента

Формула для определения поправочного коэффициента

Результат

1

КП1

К11·К16·К17 = 1.06 *0.8*1.03

0.87

2

КП2

К1·К2·К4·К5·К6·К7· К8·К9·К16·К17·К18 = 1.03*0.9*0.96*1.05*0.97*1.09*0.98*0.97*0.8*1.03*1.09

0.84

3

КП3

К1·К2·К4·К5·К6·К7·К8·К9·К11·К12·К13·К14·К15·К16· К17·К18 = 1.03*0.9*0.96*1.05*0.97*1.09*0.98*0.97*1.06*1.02*1*1* 1.02*0.8*1.03*1.09

0.93

4

КП4

К1·К2·К4·К5·К6·К7·К8·К9·К10·К12·К13·К14·К15·К16· К17·К18 =1.03*0.9*0.96*1.05*0.97* 1.09*0.98*0.97*1.03*1.02*1*1* 1.02*0.8*1.03*1.09

0.90

5

КП5

К1·К2·К4·К5·К6·К7·К8·К9·К10·К11·К12·К13·К14·К15·К16·К17·К18 = 1.03*0.9*0.96*1.05*0.97*1.09*0.98*0.97*1.03*1.06*1.02*1*1* 1.02*0.8*1.03*1.09

0.96

6

КП6

К1·К2·К11· К16· К18 = 1.03*0.9*1.06*0.8*1.09

0.86

Расчет трудоемкости с учетом поправочных коэффициентов:

S=КП1*S1+КП2*S2+КП3*S3+КП4*S4+КП5*S5+КП6*S6

S = 0.87*9+0.84*6+0.93*17+0.9*15+0.96*14+0.86*3 = 7.83+5.04+15.81+13.5+13.44+2.58 = 58.2 человеко-месяцев

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

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

Таблица L.4. Зависимость срока разработки от трудоемкости

Экономическое обоснование

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

Предположим, что в разработке системы будет участвовать команда из 7 человек, выполняющих роли: User Experience, Product Manager, Program Manager, Architector, Programmer, Tester, Release Manager.

В качестве вида регистрации компании выбрано ИП.

Наиболее выгодной схемой налогообложения в данном случае является УСН по статье доходов (так как расходы не являются существенными) со ставкой 6%.

Предположим, что для реализации проекта найден инвестор, который готов предоставить начальную сумму в размере 2 500 000 р. (данное число основано на прогнозируемых расходов на разработку - см. пункт "Расчет расходов") под 15% от полученной прибыли.

Расчет ставки дисконтирования

Расчет ставки дисконтирования выполнен с помощью метода WACC, который был выбран на основе следующих фактов:

1) отсутствие экспертов для метода экспертной оценки;

2) не ведется работа с акциями (поэтому модели Е. Фамы и К. Френча, М. Кархата, Гордона и рентабельность капитала не подходят);

3) отсутсвие необходимого опыта для выбора поправок для рисков (используются в CAMP и MCAMP);

4) достаточно данных для применения именно WACC.

Ставка дисконтирования рассчитывается по следующей формуле:

WACC= Re*E/ (E+D) + Rd* (1-t) *D/ (E+D), где

Re - 7,7% процентная ставка вклада "Новый уровень" на 6 месяцев в Сбербанке (http://www.sberbank.ru/ru/person/contributions/close_deposits/newlevel);

Rd - 15% процент, который необходимо возвращать инвестору;

D - 2 500 000 руб. вклад от инвестора

E - 500 000 р. вклад от команды

t - 6% налог, по УСН

Подставляем данные в формулу:

WACC= 0.077*0.17+ 0.15* (1-0.06) *0.83=0.013+0.117 = 0.13

Ставка дисконтирования равна 13%.

Расчет расходов

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

1. Оформление юр. лица - ИП - 1000 р. - единоразово.

2. Аренда офиса - средняя цена небольших офисов на Авито - 20000 р. /мес.

3. Зарплата программисту - 30000 р. /мес.

4. Зарплата программному менеджеру - 25000 р. /мес.

5. Зарплата менеджеру продукта - 25000 р. /мес.

6. Зарплата релиз менеджеру - 25000 р. /мес.

7. Зарплата тестировщику - 25000 р. /мес.

8. Зарплата архитектору - 25000 р. /мес.

9. Зарплата User Experience - 25000 р. /мес.

10. Аренда 4 Azure серверов с характеристиками:

a. Сервер для микросервиса поиска дубликатов: 10 гб ОЗУ, 3 ядра, диск 100 гб - 7500 р. в месяц

b. Сервер для остальных микросервисов: 10гб ОЗУ, 4 ядра, диск 100 гб - 9500 р. в месяц

c. Сервер БД: 1 ядро, 16гб ОЗУ, 5тб - 10000 р. в месяц

d. Сервер для nginx: 10 гб ОЗУ, 1 ядро, диск 100 гб - 3500 р. в месяц.

11. Среда для программирования - PyCharm - около 10000 р. в год.

12. Налоги УСН - в первый год нет доходов - налоги не платятся. Последующие года - 6% от доходов.

13. Страховые взносы - 2.9% с зарплат сотрудников - 62640 р. в год.

Таким образом, расходы за первый год составят 2 839 640 р.

В последующие года зарплата работников будет увеличваться в среднем на 4%, также будет повышаться аренда офиса примерно на 7% (основано на статистических данных), аренда стоимости серверов в среднем увеличивается на 5%.

Рисунок L.4. Расчет расходов

Расчет доходов

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

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

Рисунок L.5. Расчет доходов

Расчет срока окупаемости проекта

Рисунок L.6. Расчет срока окупаемости проекта

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

Приложение M. Программа и методика испытаний

УТВЕРЖДАЮ

Доцент кафедры информацинных технологий в бизнесе

______________ /Л.Н. Лядова/

"____" ______________ 2017 г.

МОДУЛЬ АВТОМАТИЧЕСКОЙ ОБРАБОТКИ И АНАЛИЗА ДАННЫХ ДЛЯ УНИВЕРСАЛЬНОЙ РЕКОМЕНДАТЕЛЬНОЙ СИСТЕМЫ

На 18 листах

Пермь 2017

Аннотация

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

Программа и методика испытаний разработана в соответствии с требованиями ГОСТ 34.603-92, РД 50-34.698-90.

Приложение N. Программный код

dupl_service. py

# - * - coding: utf-8 - *- from pymongo import MongoClient from flask import Flask, render_template, jsonify, request, Response, abort import configs import redis from app. process import Process from marking. marking import Marking from bson. json_util import dumps, loads from json import encoder from similarity_model. model_creation import ModelCreation from graph_formation. graph_creation import GraphFormation from graph_formation. grouping_graph import GraphGrouping app = Flask (__name__) connections = {} @app. route ('/processes/<name>/marking/<dataset>/<field>',methods= ["GET"]) def get_marking_pair (name, field, dataset):

m = Marking (connections ["redis"],connections ["mongo_db_dupl"],name) try:

o1,o2 = m. get_pair (field, dataset) except Exception as e:

return Response (response=dumps ({"error": {"code": 11,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"obj1": o1, "obj2": o2}), status=200, mimetype="application/json") @app. route ('/processes/<name>/marking/new/<dataset>',methods= ["POST"]) def new_marking (name, dataset):

m = Marking (connections ["redis"],connections ["mongo_db_dupl"],name) try:

m. new_dataset (dataset) except Exception as e:

return Response (response=dumps ({"error": {"code": 12,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"status": "OK"}), status=200, mimetype="application/json") @app. route ('/processes/<name>/marking_save/<dataset>',methods= ["POST"]) def save_marking_pair (name, dataset):

if not request. json:

abort (400) print (request. json) m = Marking (connections ["redis"],connections ["mongo_db_dupl"],name) try:

m. save_pair (request. json ["obj1"],request. json ["obj2"], request. json ["result"], dataset) except Exception as e:

return Response (response=dumps ({"error": {"code": 11,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"status": "OK"}), status=200, mimetype="application/json") @app. route ('/processes/new',methods= ["POST"]) def new_process ():

print (request) if not request. json:

abort (400) name = request. json ["name"] data = request. json ["data"] fields = request. json ["fields"] process = Process (connections ["mongo_db_dupl"],connections ["redis"]) try:

process. new_process (connections ["mongo_db_group"],name, data, fields) except Exception as e:

return Response (response=dumps ({"error": {"code": 10,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"status": "OK"}), status=200, mimetype="application/json") @app. route ('/processes/<name>/train/<dataset>/<model_name>',methods= ["POST"]) def new_model (name, dataset, model_name):

mc = ModelCreation (model_name,connections ["mongo_db_dupl"],name,dataset) try:

mc. new_model () except Exception as e:

return Response (response=dumps ({"error": {"code": 13,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"status": "OK"}), status=200, mimetype="application/json") @app. route ('/processes/<name>/matrices/new',methods= ["POST"]) def new_matrix (name):

gf = GraphFormation (connections ["mongo_db_dupl"],request. json ["model_name"],connections ["redis"],name) try:

gf. new_distance_matrix_c (request. json ["matrix_name"], request. json ["bound"], request. json ["cluster"]) except Exception as e:

return Response (response=dumps ({"error": {"code": 14,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"status": "OK"}), status=200, mimetype="application/json") @app. route ('/processes/<name>/matrices/<matrix>/<cluster>',methods= ["GET"]) def show_matrix (matrix, name, cluster):

gg = GraphGrouping (connections ["mongo_db_dupl"], name) objects = gg. get_duplicates_groups (matrix, cluster) return Response (response=dumps (objects), status=200, mimetype="application/json") @app. route ('/processes/<name>/group_matrix',methods= ["POST"]) def group_matrix (name):

gg = GraphGrouping (connections ["mongo_db_dupl"], name) try:

gg. group_duplicates (request. json ["group_name"],float (request. json ["bound"]),request. json ["matrix_name"]) except Exception as e:

return Response (response=dumps ({"error": {"code": 15,"message": str (e) }}), status=400, mimetype="application/json") return Response (response=dumps ({"status": "OK"}), status=200, mimetype="application/json") if __name__ == '__main__':

mongo_db = MongoClient (host=configs. DB_CONNECTION ["MONGODB_HOST"], port=configs. DB_CONNECTION ["MONGODB_PORT"]) mongo_db_dupl = mongo_db [configs. DB_CONNECTION ["MONGODB_DB_DUPL"]] connections ["mongo_db_group"] = mongo_db [configs. DB_CONNECTION ["MONGODB_DB_GROUP"]] connections ["mongo_db_dupl"] = mongo_db_dupl connections ["redis"] = redis. Redis (host=configs. REDIS_CONNECTION ["REDIS_HOST"], port=configs. REDIS_CONNECTION ["REDIS_PORT"], db=configs. REDIS_CONNECTION ["REDIS_DB"],decode_responses=True) app.run ()

marking. py

from pymongo import MongoClient from src. tokenizator import Tokenizator from src. pair_difference import PairDifference from numpy. random import choice, uniform from bson import ObjectId import random class Marking:

def __init__ (self, redis, mongo, process):

self. redis = redis self. mongo = mongo self. token = Tokenizator () self. pd = PairDifference (redis, process) self. process = process def get_pair (self, field, dataset):

train_dataset = self. mongo [self. process+"_marking_"+dataset] init_data = self. mongo [self. process] text_tagger = Tokenizator () number_of_clusters = self. mongo ["params"]. find_one ({"process": self. process}, no_cursor_timeout=True) ["clusters_count"] fields = self. mongo ["params"]. find_one ({"process": self. process}, no_cursor_timeout=True) ["fields"] elements = [0, 1] weights = [0.2, 0.8] while True:

c = random. randint (0, number_of_clusters) if choice (elements, p=weights) == 0:

p = uniform (0,0.4) else: p = uniform (0.5,1) field_type = "" for f in fields:

if f ["name"] == field:

field_type = f ["type"] break if field_type == "string":

data = init_data. find ({"cluster": c}, no_cursor_timeout=True) for obj in data:

tags1 = text_tagger. extract_tokens (obj [field]) for obj2 in init_data. find ({"cluster": c}, no_cursor_timeout=True):

if obj ["_id"] ==obj2 ["_id"]: continue tags2 = text_tagger. extract_tokens (obj2 [field]) max_tags = max ([len (tags1), len (tags2)]) if (len (set (tags1) & set (tags2)) / max_tags >= p) and (train_dataset. find ({"obj1": obj ["_id"], "obj2": obj2 ["_id"] }). count () ==0):

return obj, obj2 else:

data = init_data. find ({"cluster": c}, no_cursor_timeout=True) for obj in data:

for obj2 in init_data. find ({"cluster": c}, no_cursor_timeout=True):

if obj ["_id"] ==obj2 ["_id"]: continue if obj [field] ==obj2 [field]:

if obj! = None and obj2! = None:

return obj, obj2 def save_pair (self, obj1_id, obj2_id, result, dataset):

train_dataset = self. mongo [self. process + "_" + dataset] init_data = self. mongo [self. process] obj1 = init_data. find_one ({"_id": ObjectId (obj1_id) }) obj2 = init_data. find_one ({"_id": ObjectId (obj2_id) }) fields = self. mongo ["params"]. find_one ({"process": self. process}, no_cursor_timeout=True) ["fields"] sim = self. pd. get_difference (obj1,obj2,fields) sim ["obj1"] = obj1 ["_id"] sim ["obj2"] = obj2 ["_id"] sim ["is_dupl"] = result if train_dataset. find ({"$or": [{"obj1": obj1 ["_id"],"obj2": obj2 ["_id"] },{"obj2": obj1 ["_id"],"obj1": obj2 ["_id"] }] }). count==0:

train_dataset. insert (sim) def new_dataset (self, name):

if self. mongo ["datasets"]. find ({"name": self. process+"_marking_"+name}). count () ==0:

self. mongo ["datasets"]. insert ({"name": self. process+"_marking_"+name,"process": self. process}) else:

raise ValueError ('dataset with such name already exists')

pair_difference. py

import numbers from src. tf_idf import TfIdf from src. tokenizator import Tokenizator class PairDifference:

def __init__ (self, redis, process):

self. tfidf = TfIdf (redis, process) self. token = Tokenizator () self. process = process def get_difference (self, obj1, obj2, fields):

print (obj1) print (obj2) sim = {} for f in fields:

if f ["type"] =="string":

sim [f ["name"]] = self. tfidf. similarity_dq (self. token. extract_tokens (obj1 [f ["name"]], needs_stemming=True), self. token. extract_tokens (obj2 [f ["name"]], needs_stemming=True), f ["name"]) if f ["type"] == "number":

sim [f ["name"]] = abs (obj1 [f ["name"]] - obj2 [f ["name"]]) print (sim) return sim def get_arrays_el_dif (self, array1, array2, fields):

res = [] if len (array1) == len (array2):

for i in range (0, len (array1)):

dif = self. get_difference (array1,array2, fields) res. append (dif) return res

tf_idf. py

__author__ = 'kseniyamalkova' import math import redis import re class TfIdf:

def __init__ (self, redisClient, process_name):

self. redis = redisClient self. process = process_name def freq (self,word, doc):

return list (doc). count (word) def word_count (self,doc):

return len (doc) def tf (self,word, doc):

return (self. freq (word, doc) / float (self. word_count (doc))) def num_docs_containing (self,word, list_of_docs):

count = 0 for document in list_of_docs:

if self. freq (word, document) > 0:

count += 1 return 1 + count def idf (self, num_docs_containing, count_of_docs):

return math. log (count_of_docs / float (num_docs_containing)) def tf_idf (self,tf, idf):

return (tf*idf) def get_vector (self, doc, field):

vector = {} doc_ = list (set (doc)) for t in doc_:

tf = self. tf (t,doc) try:

idf = float (self. redis. get (self. process+": "+field+": tokens: "+t+": idf")) except:

idf = 0 vector [t] = self. tf_idf (tf, idf) return vector def get_document_vector (self,doc, field):

return self. redis. hgetall (self. process+": "+field+": docs: "+ ("". join (doc)) +": tf_idf") def add_document (self,document, field):

key = "". join (document) if self. redis. exists (self. process+": "+field+": docs: "+key) ==0:

document_ = list (set (document)) for token in document_:

self. redis. hset (self. process+": "+field+": docs: "+key+": freq", token, self. freq (token, document)) self. redis. hset (self. process+": "+field+": docs: "+key+": tf", token, self. tf (token, document)) self. redis. lpush (self. process+": "+field+": docs: "+key+": tokens", document) self. redis. incr (self. process+": "+field+": docs: count") self. redis. incr (self. process+": "+field+": tokens: "+token+": docs_count") for doc in self. redis. scan_iter (match=self. process+": "+field+": docs: *: tokens"):

doc = re. match (self. process+": "+field+": docs: (. *): tokens", doc) doc = doc. groups () [0] tokens_tf = self. redis. hgetall (self. process+": "+field+": docs: "+str (doc) +": tf") for token, tf in tokens_tf. items ():

idf = self. idf (int (self. redis. get (self. process+": "+field+": tokens: "+token+": docs_count")), int (self. redis. get (self. process+": "+field+": docs: count"))) self. redis. set (self. process+": "+field+": tokens: "+token+": idf", idf) self. redis. hset (self. process+": "+field+": docs: "+str (doc) +": idf", token, idf) self. redis. hset (self. process+": "+field+": docs: "+doc+": tf_idf", token, self. tf_idf (float (tf), idf)) def add_documents (self,documents, fields):

for doc in documents:

for field,document in doc. items ():

key = "". join (document) if self. redis. exists (self. process+": "+field+": docs: "+key) ==0:

document_ = list (set (document)) for token in document_:

self. redis. hset (self. process+": "+field+": docs: "+key+": freq", token, self. freq (token, document)) self. redis. hset (self. process+": "+field+": docs: "+key+": tf", token, self. tf (token, document)) self. redis. lpush (self. process+": "+field+": docs: "+key+": tokens", document) self. redis. incr (self. process+": "+field+": docs: count") self. redis. incr (self. process+": "+field+": tokens: "+token+": docs_count") for f in fields:

for doc in self. redis. scan_iter (match=self. process+": "+f+": docs: *: tokens"):

doc = re. match (self. process+": "+f+": docs: (. *): tokens", doc) doc = doc. groups () [0] tokens_tf = self. redis. hgetall (self. process+": "+f+": docs: "+str (doc) +": tf") for token, tf in tokens_tf. items ():

idf = self. idf (int (self. redis. get (self. process+": "+f+": tokens: "+token+": docs_count")), int (self. redis. get (self. process+": "+f+": docs: count"))) self. redis. set (self. process+": "+f+": tokens: "+token+": idf", idf) self. redis. hset (self. process+": "+f+": docs: "+str (doc) +": idf", token, idf) self. redis. hset (self. process+": "+f+": docs: "+doc+": tf_idf", token, self. tf_idf (float (tf), idf)) def similarity_dq (self, d1, query, field):

key1 = "". join (d1) tokens1 = self. redis. hgetall (self. process+": "+field+": docs: "+key1+": tf_idf") tokens2 = self. get_vector (query, field) dif1 = list (set (tokens2. keys ()) - set (tokens1. keys ())) for d in dif1:

tokens1 [d] = 0 dif2 = list (set (tokens1. keys ()) - set (tokens2. keys ())) for d in dif2:

tokens2 [d] = 0 sum_p = 0 sum1 = 0 sum2 = 0 for t,v_ in tokens1. items ():

v = float (v_) p = v*tokens2 [t] sum_p = sum_p + p sum1 = sum1 + math. pow (v,2) sum2 = sum2 + math. pow (tokens2 [t],2) try:

sim = sum_p/ (math. sqrt (sum1*sum2)) except:

sim = 0 return sim def similarity_dd (self, d1, d2, field):

key1 = "". join (d1) key2 = "". join (d2) tokens1 = self. redis. hgetall (self. process + ": " + field + ": docs: " + key1 + ": tf_idf") tokens2 = self. redis. hgetall (self. process + ": " + field + ": docs: " + key2 + ": tf_idf") dif1 = list (set (tokens2. keys ()) - set (tokens1. keys ())) for d in dif1:

tokens1 [d] = 0 dif2 = list (set (tokens1. keys ()) - set (tokens2. keys ())) for d in dif2:

tokens2 [d] = 0 sum_p = 0 sum1 = 0 sum2 = 0 for t, v_ in tokens1. items ():

v = float (v_) v2 = float (tokens2 [t]) p = v * v2 sum_p = sum_p + p sum1 = sum1 + math. pow (v,

2) sum2 = sum2 + math. pow (tokens2 [t],

2) try:

sim = sum_p / (math. sqrt (sum1 * sum2)) except:

sim = 0 return sim

tokenizator. py

import re #import snowballstemmer import Stemmer bad_words = {'а-ля', 'без', 'безо', 'в', 'во', 'для', 'для-ради', 'до', 'за', 'из', 'из-за', 'из-под', 'изо', 'к', 'ко','меж', 'на', 'над', 'о', 'об', 'обо', 'от', 'ото', 'по', 'при', 'про', 'с', 'со', 'у', 'а', 'б', 'ан', 'б','бы', 'во', 'вот', 'вот-вот', 'да', 'уж', 'ж', 'же', 'и', 'или', 'как', 'ли', 'ль', 'не', 'нет', 'ни','ну', 'се', 'эк'} def char_range (c1, c2):

chars = [] for c in range (ord (c1), ord (c2) +1):

chars. append (chr (c)) return chars country_codes = { "ru": "russian", "en": "english", } russian_words = set (char_range ("а", "я")) class Tokenizator:

def __init__ (self):

self. stemmers = {} @staticmethod def remove_numbers (text):

return re. sub (r"\d+ (?: [\.,] \d+)?", "", text) @staticmethod def extract_numbers (text):

rx = re.compile (r"\d+ (?: [\. \,] \d+)?", re. VERBOSE) return rx. findall (text) @staticmethod def remove_control_symbols (text):

return re. sub (r" [\{\}]", "", text) @staticmethod def remove_bad_symbols (text):

return re. sub (r" [']", "", text) @staticmethod def get_lang (text):

if len (text) > 0:

if text [0]. lower () in russian_words:

return "ru" else:

return "en" else:

return "en" def stem_word (self, lang, word):

if lang in self. stemmers. keys ():

return self. stemmers [lang]. stemWord (word) elif lang in country_codes. keys ():

self. stemmers [lang] = Stemmer. Stemmer (country_codes [lang]) return self. stemmers [lang]. stemWord (word) else:

return word def extract_tokens (self, text, needs_stemming=False):

text = Tokenizator. remove_bad_symbols (text) text = Tokenizator. remove_control_symbols (text) numbers = Tokenizator. extract_numbers (text) for i in range (0, len (numbers)):

text = text. replace (numbers [i], "{number" + str (i) + "}") text = text. lower () tokens = list (filter (lambda x: (x! = "" and x not in bad_words), re. split (r" [\"""#$%&\ () \*+/<>@\ [\] ^`!,-.:; =? _|~ (?: \s*)]", text))) words_pos = [] for i in range (0, len (tokens)):

res = re. findall (r"\{number ([0-9] +) \}", tokens [i]) if len (res) > 0:

pos = int (res [0]) tokens [i] = str (numbers [pos]) else:

words_pos. append (i) if needs_stemming:

tokens [i] = self. stem_word (Tokenizator. get_lang (tokens [i]), tokens [i]) return tokens

duplicates_predictor. py

import xgboost as xgb from similarity_model. data_transformer import DataTransformer from src. pair_difference import PairDifference class DuplicatesPredictor:

def __init__ (self, model_file, redis, process):

self. model = xgb. Booster (model_file=model_file) self. process = process self. redis = redis self. dt = DataTransformer () self. pd = PairDifference (redis, process) def predict (self, object1, object2, fields):

dif = self. pd. get_difference (object1, object2,fields) data = self. dt. record_to_numpy (dif) prediction = self. model. predict (xgb. DMatrix (data)) return prediction [0] def predict_many (self, objects_array1, objects_array2, fields):

records_array = self. pd. get_arrays_el_dif (objects_array1,objects_array2, fields) data = self. dt. records_to_numpy (records_array) prediction_array = self. model. predict (xgb. DMatrix (data)) return prediction_array

duplicates_trainer. py

import numpy as np import xgboost as xgb from similarity_model. data_transformer import DataTransformer class DuplicatesTrainer:

def __init__ (self, model_file, mongo, process):

self. evals = None self. mongo = mongo self. model_file_name = model_file self. dt = DataTransformer () self. process = process def train (self,dataset, train_part, features_count):

np_dataset = self. dt. records_to_numpy (dataset,"is_dupl") np. random. shuffle (np_dataset) train_count = round (len (np_dataset) * train_part) train_ds = np_dataset [: train_count] test_ds = np_dataset [train_count:] train_X = train_ds [:,: features_count] train_y = train_ds [:, features_count:]. ravel () train = xgb. DMatrix (train_X, train_y) test_X = test_ds [:,: features_count] test_y = test_ds [:, features_count:]. ravel () test = xgb. DMatrix (test_X, test_y) watchlist = [ (test, 'eval'), (train, 'train')] model = xgb. train ({'silent': 1, "objective": "binary: logistic"}, train, 1, evals=watchlist) model. save_model (self. model_file_name) return model

graph_creation. py

from pymongo import MongoClient from similarity_model. duplicates_predictor import DuplicatesPredictor import pandas as pd import pickle from bson. objectid import ObjectId class GraphFormation:

def __init__ (self, mongo, model, redis, process):

self. mongo = mongo self. model = model self. redis = redis self. process = process def new_distance_matrix (self,file_name, bound=0.6):

col = self. mongo [self. process] if self. mongo ["matrices"]. find ({"name": self. process+"_matrix_"+file_name}). count () ==0:

number_of_clusters = self. mongo ["params"]. find_one ({"process": self. process}, no_cursor_timeout=True) [ "clusters_count"] fields = self. mongo ["params"]. find_one ({"process": self. process}) ["fields"] dp = DuplicatesPredictor (self. model,self. redis,self. process) graphs_matrices = {} for c in number_of_clusters:

distance_matrix = pd. DataFrame () for r in col. find ({"cluster": c}, no_cursor_timeout=True):

for r2 in col. find ({"cluster": c}, no_cursor_timeout=True):

if r ["_id"]! =r2 ["_id"]:

prediction = dp. predict (self. select_features (fields,r),self. select_features (fields,r2),fields) print (prediction) columns = distance_matrix. columns. values if prediction>bound:

print (r ["title"], r2 ["title"]) print ("DUPLICATE") print (prediction) for i in columns:

if ObjectId (i)! =r ["_id"]:

pr = col. find_one ({"_id": ObjectId (i) }) prediction2 = dp. predict (self. select_features (fields, pr), self. select_features (fields, r), fields) if prediction2 > bound:

prediction2 = 0.9 else:


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

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