-
Notifications
You must be signed in to change notification settings - Fork 81
Development‐Features
У нас возникла проблема: все пишут байндинги, исключения и примеры по-разному. Данный документ — это попытка донести до всех, что должно быть.
Байндинги — это код, который позволяет выполнять определённый набор действий с найденными инстансами примитива в Python. Этот набор очень важен, так как определяет удобство использования результатов Desbordante. Перечислим эти действия от самого обязательного к менее обязательному. При этом, кажется, что в идеале они должны быть реализованы для каждого примитива все.
Итак, надо чтобы:
- инстансы примитива можно было получать в Python.
- инстансы примитива можно было получать по одному, итерируясь по ним, и знать общее их количество.
- каждый инстанс примитива можно было печатать "в лоб" — to_string.
- каждый инстанс примитива можно было обрабатывать по частям: получить левую часть, получить правую часть, перечислить атрибуты правой части, получить threshold X, получить threshold Y и так далее.
- инстансы примитива можно было сравнивать, помещать во множество (финальная цель пользователей — пересекать, вычитать множества) и т.д.
- можно было инициализировать объект примитива, причем объекты у валидатора и майнера совпадали (то есть, можно было сразу взять и вставить намайненный объект в валидатор).
- Чтобы инстансы примитива можно было сериализовывать и десериализовывать, причем чтобы сохранялось максимум информации (например, схема таблицы).
- Обновить stubs: https://github.com/Desbordante/desbordante-stubs
- Подумать о необходимости вывести в консоль: https://github.com/Desbordante/desbordante-cli
- Возможность пробрасывать user-defined metric (код на Python) в C++ ядро.
- Когда-то мы придем к ситуации (для CFD, например), когда один алгоритм дает объект с метриками confidence и support, а другой — без них. Надо подумать о том, что тогда делать (набор конверсий)?
В общем случае исключения (также называемые объяснениями, подсветкой и др.) — это фрагменты данных, которые (возможно) мешают выполнению конкретного инстанса примитива на конкретном датасете. Предполагается, что исключения помогут пользователям выявлять различные неточности и ошибки в данных. Идея Desbordante заключается в следующем: при верификации инстанса примитива, если он не выполняется, необходимо предоставить пользователю возможность увидеть, что в данных нужно убрать или изменить, чтобы он выполнялся. Соответственно, с каждым верификатором должна быть связана определённая функциональность, нацеленная на это.
Замечание: слово "возможно" добавляется, потому что не всегда можно точно установить "виновника" без дополнительных знаний. Поэтому мы показываем все подозрительные фрагменты, но при этом стремимся предоставить минимальный набор, который можно установить, а не возвращаем весь датасет, что обессмыслило бы всю идею. Предполагается, что дополнительную фильтрацию этого набора выполнит пользователь, если ему это понадобится.
Сейчас у нас есть два типа исключений: кластеры (FD, UCC) и наборы пар (DC). Кластеры являются предпочтительным способом представления исключений, так как они сразу дают контекст для поиска ошибки. В случае, когда мы не можем представить результат в виде кластеров, мы используем пары. С ними работать сложнее, их нужно как-то трактовать — например, как граф. Наконец, возможно, что у вас есть примитив, для которого эти формы исключений не подходят. В таком случае придется придумывать новую форму, например, для графовых примитивов. Подумайте об этом, обсудим.
Теперь о том, как реализовать исключения на кластерах и парах. Реализация исключений на основе кластеров включает в себя:
- Перечисление найденных кластеров и получение их количества.
- Для каждого кластера получить: а) множество строк, его составляющих; б) количество этих строк в кластере; в) наиболее частое значение в кластере.
Реализация исключений на основе набора пар включает в себя:
- Перечисление найденных пар и получение их количества.
Еще есть мысль про необходимость отключаемости вычисления исключений при валидации инстанса примитива.
Это довольно творческий процесс (и в каждом примитиве пример имеет свои особенности), но я выделю несколько важных пунктов. Они идут в порядке, в котором должны появляться в примере.
Считается, что человек сначала ознакомится с выдачей примера, а затем посмотрит код. То есть, мы обучаем людей как теории (что за примитив), так и практике — как писать код, чтобы с ним что-то сделать. Поэтому эти два компонента необходимо правильно отразить.
Итак:
- В начале должно быть указание на то, что за примитив — по какой статье он определяется (название, авторы, год, конференция/журнал).
- Упомянуть, что есть и другие примеры (если они имеются) по этому примитиву, например, про майнинг/валидацию (если у вас валидация/майнинг), или что есть второй-третий пример, если вы делаете несколько.
- Дать определение примитива и пояснить, что оно значит на реальном примере.
- Рассказать о параметрах алгоритма: что они делают и какие значения могут принимать.
- Упомянуть, что есть несколько алгоритмов, если это так.
- Напечатать датасет и объяснить, что это за датасет. Он должен быть понятным и обозримым (не более 15 строк и 6 столбцов; меньше — лучше). Если это невозможно (например, для алгоритмов с сэмплингом), можно использовать более крупный датасет. Датасеты для примеров хранятся отдельно в examples/datasets. Путь в коде должен указываться из корневой папки, т.е. "examples/datasets/...". То есть, мы предполагаем, что мы запускаем все из desbordante-core.
- Поискать/верифицировать примитивы на датасете, при этом в коде показать больше приемов работы с ним (как итерироваться по найденным инстансам, как получить правую часть, как печатать и т.д.).
- Показать, как меняется поведение алгоритма при изменении важных параметров или при смене данных. Частый пример: верифицировали/нашли инстанс, поняли, что, вероятно, в датасете ошибка, исправили строчки в датасете, показали, перепроверили и увидели, что теперь результат стал другим (лучше).
- В конце сделать отсылку к следующему примеру по этому примитиву, если он есть.
Дополнительные замечания:
- Каждый пример должен быть добавлен в CI, не забудьте обновить snapshot-*.
- Если примитив использует встроенные метрики, надо описать все поддерживаемые.
- Если примитив позволяет задавать user-defined metric (пробрасывать функцию из Python в С++ ядро) - сделать такой пример.
- Если алгоритм рандомизированный, рассказать про это явно (и пояснить почему) и зафиксировать seed. Причем, проверить что выставленный seed действительно фиксирует результат (для этого понадобится другая машина, например можно использовать colab). Если seed не фиксирует его, то это ошибка и надо чинить алгоритм: будут валиться тесты на примеры и текст к выдаче в colab будет не соответствовать выдаче.
- Постараться придумать пример как можно использовать примитив для поиска и исправления ошибок в данных. Если примитив имеет правую и левую части, показать как искать ошибки в левых и в правых частях (это сразу два примера). Это требует некоторой фантазии, плюс вольности в рассуждениях. Что произошло и почему, не бойтесь придумывать.
- Обычно работа с примитивами требует экспериментирования, например при подборе параметров или при поиске опечаток. Говорите об этом (где-то ближе к концу примера).
Стиль:
- Используйте LLM чтобы поправить язык.
- Старайтесь бить текст на абзацы и отделять их пустой строкой.
- Можно выделять важные моменты цветом.