Управляем параметрами в процессной аналитике при помощи фреймворка Hydra

Привет, Хабр! Меня зовут Александр Дунаевский, я Data Scientist в Сбере. Сегодня хочу рассказать про управление параметрами в Pricess mining (процессная аналитика) и нюансах этой задачи.

Для работы используется фреймворк Hydra, который здорово облегчает жизнь. В чём вообще проблема? В задачах процессной аналитики требуется постоянно менять входные параметры и сохранять результаты работы. Но при большом количестве запусков возникает потребность в отдельной системе для управления как передаваемыми параметрами, так и логированием. В статье рассмотрим, как фреймворк Hydra может помочь нам с этим. Кому интересно ― просим под кат!

715381674a3af3b4672eea513febbe9f.png

Подробнее о проблеме и работе с фреймворком Hydra

Что такое процессная аналитика? Если коротко, то это технология изучения, мониторинга и оптимизации процессов путём применения специальных алгоритмов к журналам событий.

Анализируя модели бизнес-процессов, можно:

  • находить основные клиентские пути;

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

Для решения подобного рода задач в Сбере разработана собственная библиотека, которая получила название SberPM. В декабре 2020 года её опубликовали, вот ссылочка. К слову, есть и подробная статья о ней на Хабре.

Для лучшего понимания того, как она работает, стоит привести пример ― так нагляднее. Вот, например, как выглядит результат применения самых используемых алгоритмов SimpleMiner и CasualMiner на демонстрационном датасете SberPM:

3b5b08b80f5c9a3acb2b5142c3a42733.png

SimpleMiner визуализирует все найденные рёбра, толщина линии зависит от проходимости ребра. CasualMiner визуализирует только однонаправленные связи. Важная задача, с которой сталкивается программист-исследователь, постоянно работающий с задачами в области процессной аналитики и выполняющий проверку большого числа гипотез, ― передача параметров модели/датасета, логирование запусков и сохранение параметров, при которых получился тот или иной результат.

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

К счастью, изобретать велосипед не придётся, ведь уже существует Hydra — фреймворк на Python с открытым исходным кодом. Он создавался специально для проектов машинного обучения и позволяет гибко работать с конфигурациями моделей, датасетами и другими входными данными.

Вот основные функции фреймворка (источник ― сайт продукта):

  • иерархическая конфигурация по нескольким источникам;

  • конфигурация может быть определена или переопределена из командной строки;

  • автозаполнение командной строки;

  • локальный или удалённый запуск приложения;

  • запуск обучения моделей с разными конфигурациями одной командой.

Установка Hydra довольно простая ― на всякий случай приводим инструкцию из официального источника. По указанной ссылке можно ознакомиться с основными нюансами, в статье о них не будем говорить, чтобы не раздувать объём материала.  Единственное, укажу основные особенности, которые стоит узнать в самом начале работы с Hydra:

  • Hydra меняет текущий рабочий каталог. Например, main.py лежит в src/main.py, но вывод покажет, что текущий рабочий каталог ― src/outputs/2022–02–28/11–32–19;

  • фреймворк будет вести журнал запуска именно в этом каталоге, там же будут и сохраняться все созданные файлы;

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

Ну, а теперь к делу. Используем фреймворк со SberPM

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

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

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

Датасет

Для демонстрации работы фреймворка я взял два датасета (журнала событий):

Конфигурации для них содержатся в папке conf/dataset/, в файлах example.yaml и complains.yaml

Модель

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

  • SimpleMiner ― рисует все найденные рёбра, толщина линии зависит от проходимости ребра;

  • CasualMiner ― рисует только однонаправленные связи;

  • HeuMiner ― рисует только те связи, которые больше определённого порога (threshold) ― чем он больше, тем меньше рёбер;

  • AlphaMiner ― рисует граф в виде сети Петри с учётом прямых, параллельных и независимых связей между активностями;

  • AutoInsights ― модуль автоматического поиска инсайтов, позволяющий выявлять узкие места процесса и визуализировать на графе.

Конфигурационный файл каждой модели будет содержать только имя модели (например, name: simple), за исключением HeuMiner, поскольку он содержит дополнительный параметр threshold = 0.8.

Основной код

На примере написанной программы давайте разберём, как Hydra работает с другой библиотекой (в нашем случае SberPM).

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

На примере написанной программы давайте разберём, как Hydra работает с другой библиотекой (в нашем случае SberPM).
Начало главной функции, в которую будут передаваться параметры запуска:
# Использование шаблона "декоратор" над главной функцией my_app
# Обязательная часть при использовании sberpm    
@hydra.main(config_path='conf', config_name='config')
def my_app(cfg : DictConfig) -> None:

Внутри неё объявлены две функции.

create_dataset () загружает датасет:

    def create_dataset():
        full_path = hydra.utils.get_original_cwd() + '\\' + cfg.dataset.filename
        df = pd.read_csv(full_path, cfg.dataset.separator, encoding='latin-1')
        # Переменная для хранения датасета
        data_holder = DataHolder(data = df, 
                         id_column = cfg.dataset.id_col, 
                         activity_column = cfg.dataset.act_col, 
                         start_timestamp_column = cfg.dataset.time_col, 
                         time_format = cfg.dataset.date_format)
        return data_holder

mine () использует майнер из SberPM. Поскольку майнеров используется много, то и участок кода довольно большой:

    # функция модели - содержит логику выбора майнера по ключу из конфига и реализует каждый вид майнера
    def mine(dh):
        model_name = cfg.model.name
        
        def make_image(miner):
            miner.apply()
            graph = miner.graph

            painter = GraphvizPainter()
            painter.apply(graph)
            painter.write_graph(model_name + '.' + cfg.out_format, format=cfg.out_format)
            
        
        if model_name == 'simple':
            from sberpm.miners import SimpleMiner
            make_image( SimpleMiner(dh) )
            
        elif model_name == 'casual':
            from sberpm.miners import CausalMiner
            make_image( CausalMiner(dh) )
            
        elif model_name == 'heu':
            from sberpm.miners import HeuMiner
            make_image( HeuMiner(data_holder, threshold=cfg.model.threshold) )
            
        elif model_name == 'alpha':
            from sberpm.miners import AlphaMiner
            make_image( AlphaMiner(dh) )
                        
        elif model_name == 'insight':
            from sberpm.miners import SimpleMiner
            from sberpm.autoinsights import AutoInsights
            auto_i = AutoInsights(dh, time_unit='day')
            simple_miner = SimpleMiner(dh)
            
            # Transition duration
            auto_i.apply(miner=simple_miner, mode=cfg.mode)
            graph = auto_i.get_graph()

            painter = GraphvizPainter()
            painter.apply_insights(graph)
            painter.write_graph(model_name + '.' + cfg.out_format, format=cfg.out_format)

В конце нужно только объединить функции:

    # загрузка датасета и использование майнера
    data_holder = create_dataset()
    mine(data_holder)

Содержимое используемого программой конфига:

### src/conf/config.yaml
defaults:
    - _self_
    - dataset: example
    - model: simple
out_format: jpg
mode: time

Запуск

Для запуска с параметрами, установленными в config.yaml: python main.py. 

Запуск датасета по умолчанию, модели casual: python main.py model=casual.

Множественный запуск датасета complains, модели insight во всех трёх режимах: python main.py dataset=complains model=insight mode=time,cycles,overall -m.

Запуск всех комбинаций датасетов example, complains и моделей simple, casual, heu, insight: python main.py dataset=complains model=simple,casual,heu,insight -m.

Полезные трюки при работе с Hydra

Как показать конфигурационный файл

Для этого нужно напечатать конфиг без запуска программы. Использование: --cfg [выбор]. Выбор может быть:

Многократный запуск (в т. ч. комбинации параметров)

Основная идея ― множественный запуск модели с разными параметрами с помощью одной команды. Запуск всех комбинаций датасетов example, complains и моделей simple, casual, heu, insight:  

❯ python main.py dataset=complains model=simple,casual,heu,insight -m
[2022-03-29 16:42:50,289][HYDRA] Launching 4 jobs locally
[2022-03-29 16:42:50,289][HYDRA] 	#0 : dataset=complains model=simple
[2022-03-29 16:42:50,730][HYDRA] 	#1 : dataset=complains model=casual
[2022-03-29 16:42:51,041][HYDRA] 	#2 : dataset=complains model=heu
[2022-03-29 16:42:51,523][HYDRA] 	#3 : dataset=complains model=insight

Фреймворк запустит программу со всеми возможными комбинациями dataset и model. Результат сохраняется в каталог multirun (вместо outputs). Структура папки после запуска:

multirun
└── 2022-03-29
    └── 16-42-49
        ├── 0
        │   ├── .hydra
        │   └── main.log
        │   └── simple.jpg
        ├── 1
        │   ├── .hydra
        │   └── main.log
        │   └── casual.jpg
        ├── 2
        │   ├── .hydra
        │   └── main.log
        │   └── heu.jpg
        ├── 3
        │   ├── .hydra
        │   └── main.log
        │   └── insight.jpg
        └── multirun.yaml

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

Весь исходный код, файлы примеров и получившиеся картинки находятся в репозитории SberPM-parameter-management-with-Hydra.

В сухом остатке

Использование Hydra вместе со SberPM сокращает время исследования журналов событий. Особенно это актуально при большом количестве параметров, поскольку созданная нами система берёт на себя большую часть рутинных задач по передаче и сохранению параметров.

Библиотека Hydra ― настоящая палочка-выручалочка, если вы периодически меняете параметры модели, константы, датасеты и если вам важна воспроизводимость. Также стоит отметить, что библиотека поддерживается и постоянно развивается. Единственный заметный недостаток состоит в том, что всё это работает только из консоли, соответственно, запуск в Jupyter notebook ― это проблема.

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

© Habrahabr.ru