В реалиях современного мира, когда ведется повсеместная цифровизация и накопление данных обо всем и о каждом, возникает резонный вопрос, а как этими данными воспользоваться? Многие, наверняка, уже слышали о рекомендательных системах в сферах развлечения и продаж. Инвестиционные компании не стоят в стороне от современных тенденций в области Data Science и рекомендательных систем в частности. Так давайте рассмотрим, в чем особенности и какие этапы пришлось пройти одной крупной инвестиционной компании для того, чтобы разработать собственную рекомендательную систему для повышения эффективности кросс-продаж и что в итоге получилось.  Чем же отличается процесс продаж в инвестиционной компании от аналогичного процесса в других компаниях? Давайте посмотрим, что есть общего.  А теперь начинаются отличия и сложности.  Исходя из вышеописанного была сформулирована цель: разработать систему, которая могла бы брать необходимую информацию о клиентах из распределенных баз и агрегировать ее в одном месте. После чего, на основе поведенческой модели клиента, выводить в карточку звонка список услуг, которые менеджер может предложить. Тем самым система должна быть нацелена на помощь менеджерам в повышении эффективности кросс-продаж и уменьшение вероятности недовольства клиента (ведь мало кто любит, когда ему часто звонят и предлагают ненужные или неактуальные услуги). Так как система нацелена на кросс-продажи, то в нее попадают клиенты, у которых есть хотя бы одна приобретенная услуга. Следовательно, к этому моменту о клиенте известен широкий пул информации: пол, возраст, город проживания, история его коммуникаций с менеджерами брокера, риск-профиль, приобретенные услуги, а также многие другие данные (в том числе знак зодиака уровень лояльности). В совокупности только общей информации более 100 различных атрибутов, что явно избыточно. Если говорить об атрибутах, связанных с услугами и инвестиционной деятельностью, то их наберется еще больше. С целью сокращения атрибутов для каждого продукта была составлена матрица размером более 70х70 признаков, после чего для каждой вычислен ранг (максимальное количество линейно независимых строк) и корреляция между строками. Для этих целей использовались модули Pandas и NumPy Python. В результате матрицы были проверены на пересечение атрибутов и в итоге число уникальных данных было сокращено до 31, в том числе коррелируемых, например, риск-профиль клиента и связанные с ним величины доходности и просадки. Для каждого атрибута средствами Python проверена гипотеза о статистическом распределении, сезонности и тренде. На основании исследования атрибутов разработаны алгоритмы для очистки данных от аномалий и артефактов. После того, как была проведена очистка и подготовка данных, мы развернули базу данных (БД) с четырьмя таблицами для агрегации и трансфера данных: После подготовки БД следовало решить, а какой тип рекомендательной системы использовать? Для повышения скорости интеграции и разработки было решено остановиться на базовых подходах – контентной или коллаборативной фильтрациях, тем более их легко модифицировать в другие системы фильтрации. Как писалось ранее, система разрабатывается для широкого круга финансовых продуктов, которые могут не иметь общих атрибутов. Таким образом, от контентной фильтрации пришлось отказаться, так как для ее реализации требуется знать для каждого продукта набор определенных характеристик и его балльную оценку. Дальнейшая разработка велась на основе коллаборативной фильтрации, которая формирует матрицы схожести для каждого клиента и в соответствии с заданным порогом определяет вероятность выбора того или иного продукта на основе поведения всей группы. Также коллаборативная фильтрация позволяет заполнять «пробелы» в данных. Такой вид фильтрации имеет несколько недостатков: После того как был выбран тип рекомендательной системы, для более гибкой настройки матриц похожести и преодоления эффекта «серой овцы» было решено использовать многоуровневые планы, т.е. факторы принимают значения от нуля до плюс бесконечности. Так как многие из выбранных атрибутов передаются в виде строковых данных, было выполнено шифрование в числовой эквивалент, например, значения атрибута «Пол» были закодированы следующим образом:  Это требовалось для того, чтобы можно было применять математические функции ко всему объему данных. Также для правильности выдачи рекомендаций и снижения влияния ряда атрибутов было выполнено нормирование с гипотезой о повышении. Теперь, собственно, почему торг?   Необходимо было определиться с математическим аппаратом распределения клиентов по группам «похожести». Так как у нас получилось многомерное векторное пространство, то требовалось выбрать функцию, способную определять расстояние в многомерных векторных пространствах. К таким функциям относятся: косинусное расстояние, Евклидова метрика, Манхэттеновская метрика, коэффициент корреляции Пирсона и др. Для простоты формирования матрицы и порога схожести была выбрана функция косинусного расстояния. Данная функция может принимать значения в диапазоне от 0 до 1, где 0 – это полное совпадение. Также для косинусного расстояния проще настраивать порог схожести, так как диапазон изначально четко определен и не требуется калибровка... Однако, задав эту функцию в Python «в лоб», мы получили следующее: Из-за того, что эта функция в Python относится к классу «медленных», необходимое для формирования матрицы схожести время для 30000 клиентов в 31-мерном пространстве составит около двух суток, и это только тестовая выборка. Для нас это было недопустимо.  Первым, очевидным, решением было вычислить сходство одним из самых простых способов, через «l1-норму» (она же — Манхэттенское расстояние), либо через «l2-норму» (Евклидова метрика). или Данные варианты позволяют в разы повысить скорость вычислений. Казалось бы, следует выбрать один из вариантов и продолжить разработку системы. Однако, любой из этих вариантов приведет к тому, что необходимо проводить дополнительную калибровку порога попадания клиента в ту или иную группу. Так как это связано с тем, что «l1-норма» имеет значения в диапазоне от плюс-минус бесконечности, а «l2-норма» — от нуля до плюс бесконечности. На первый взгляд кажется, что берем Евклидову метрику, калибруем и точно вычисляем порог попадания в ту или группу и радуемся жизни…  И опять проблемы. Любая новая услуга или продукт, неминуемо «собьют» порог, аналогична ситуация и с закрытием продукта. Притом что такие действия происходят один-два раза в месяц. И что же в этом случае делать? Руководствуясь этим правилом, а что нам мешает? Пойдем по порядку... Вернемся к косинусному расстоянию. И почему оно столь популярно: А вот и формула:  Фактически, косинусное расстояние равно разности между единицей и косинусной мерой (косинусом угла между вектором А и B). Как вариант, можно записать это следующим образом: Как итог, скорость вычислений стала близка полученной для «l-норм», но все еще была недостаточно быстро. GIL (Global Interpreter Lock) позволяет избегать конфликтов при обращении разных потоков к одному и тому же участку памяти. Фактически же, когда один из потоков «захватывает» GIL, остальные «замораживаются». Как итог, нет параллельных потоков — нет проблемы. А что же делать, когда хочется параллельных вычислений? Для этого можно воспользоваться модулем Numba. Чтобы оптимизировать код Python, Numba берет байт-код из предоставленной функции и запускает на ней набор анализаторов. Байт-код Python содержит последовательность небольших и простых инструкций, что позволяет реконструировать логику функции из байт-кода без использования исходного кода. В результате Numba переводит байт-код Python в промежуточное представление LLVM (по синтексу похож на ассемблер и не имеет ничего общего с Python). Таким образом, код принимает вид:  Таким образом, импорт модуля Numba позволил «отвлечь» на себя GIL и оптимизировать выполнение циклов. В итоге удалось сократить время вычислений в 60 раз.  После всех вычислений и окончательной настройки порога "похожести" клиентов (в общей сложности было получено 13 основных групп клиентов). Внутри этих групп для каждого клиента вычислена вероятность выбора того или иного продукта или услуги.  Для случаев, когда клиент использовал продукт два и более раза — появилась возможность определить примерную периодичность совершения сделок этого типа. Для этого использовался алгоритм на основе статистической медианы. На базе данных о коммуникациях с клиентом были определены дни и даже конкретные часы, в которые тот или иной клиент «любит» звонить. Для этого использовался алгоритм на основе статистической моды с корректировкой с помощью статистической медианы. На основе данных о некоторых продуктах, используя алгоритм коллаборативной фильтрации, были получены скорректированные данные о приемлемом уровне риска для клиента и рекомендуемых продуктах, соответствующих этим уровням. В текущей версии разработки поставленная цель была полностью реализована. Результаты тестов показали повышение эффективности кросс-продаж. Как итог, система была интегрирована в CRM. Также информация о периодичности сделок клиентов и «любимый день и время» для звонков позволили оптимизировать время работы менеджеров с клиентами. Дальнейшая работа над системой будет связана с полным преодолением недостатков коллаборативной фильтрации:  Для этого уже начаты работы по модификации системы в один из видов гибридной рекомендательной системы. Также ведутся работы по углублению работы рекомендательной системы со сложными продуктами. аналитик данных