AAYMA
RU
Войти

Граф денег

Объяснимый пайплайн AML-анализа: каждому из 2 248 клиентов присваивается гипотеза о роли (одна из шести), граф кластеризуется методом Louvain, узлы…

Рабочий MVP
Команда «PsychAI» 5💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: PsychAI
Кейс HackAlem.ai: Финансы — Freedom
Стадия: Рабочий MVP

Задача кейса: «Граф денег»: восстановить структуру организованной группы по сети транзакций (AML)

Суть

Объяснимый пайплайн AML-анализа: каждому из 2 248 клиентов присваивается гипотеза о роли (одна из шести), граф кластеризуется методом Louvain, узлы ранжируются. Три CSV и локальный HTML-дашборд с поиском по gid.

Как устроено

run_pipeline.py валидирует Parquet-файлы и сверяет транзакции с рёбрами. Затем считает степени, суммы, взвешенный PageRank, достижимость от исходных клиентов и порядок переводов по дням (для роли transit). Роли consolidator, transit, distributor, terminal, coordinator и peripheral назначаются детерминированными правилами с порогами, рассчитанными по данным. Louvain даёт кластеры, гипотезы о группах строятся по составу ролей, приоритет считает отдельная формула. viewer.py поднимает локальный SVG-дашборд. LLM не используется.

Ключевые решения

  • NetworkX
  • weighted PageRank
  • Louvain
  • rule-based roles
  • temporal transit check

Особенности

Команда verify_project.py заново считает результат во временной папке и сравнивает его с submission/, что даёт воспроизводимость. Для роли transit проверяется порядок переводов по календарным дням. На Windows демо запускается двойным щелчком.

Стек

Python · pandas · pyarrow · NetworkX · NumPy · SciPy · HTML/CSS/SVG


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-2c05448e-psychai.

README

README.md

Граф денег

Решение кейса HackAlem AI «Граф денег: восстановление финансовой структуры организованной группы по транзакционной сети».

Задача и пользователь

Аналитику нужно изучить сеть переводов вокруг 81 исходного клиента. Вручную проследить 2 248 участников и 3 119 направленных связей трудно. Проект рассчитывает наблюдаемые признаки, присваивает каждому клиенту гипотезу о роли, группирует клиентов и предлагает порядок дальнейшей проверки. Роль и приоритет не означают виновность и не являются вероятностью правонарушения.

Что реализовано

  • Проверка входных Parquet-файлов и согласованности отдельных транзакций с агрегированными рёбрами.
  • Направленный взвешенный граф со всеми 2 248 gid, включая изолированные исходные узлы.
  • Шесть объяснимых ролей: consolidator, transit, distributor, terminal, coordinator, peripheral. Для transit учитывается порядок переводов по календарным дням.
  • Кластеры Louvain, осторожные гипотезы о группах на основе состава ролей, рейтинг узлов и числовые обоснования решений.
  • Три CSV-файла по схеме кейса, готовый снимок выгрузок в submission/ и локальная страница: обзор сети с числом клиентов, связей, исходных клиентов и групп, состав ролей в виде полосы, группы с крупнейшим внутренним оборотом, поиск любого gid, сравнение его видимого входа и выхода, направленные связи с отдельной строкой и крупной суммой для каждого соседа, подсветка при наведении, усиленная обводка членов того же кластера и причины попадания в топ-20.
  • Автоматические проверки полного сценария, воспроизводимости результатов и ограничений данных.
  • Команда verify_project.py проверяет версию Python и зависимости, заново рассчитывает результаты во временной папке, сравнивает их с submission/ и формирует страницу просмотра.
  • Для Windows есть запуск двойным щелчком по LaunchDemo.vbs после установки зависимостей: расчёт и браузер запускаются без терминала; кнопка на странице останавливает локальный сервер.

Основной сценарий и архитектура

  1. run_pipeline.py читает включённые в репозиторий data/nodes.parquet, data/edges.parquet и data/transactions.parquet.
  2. Программа проверяет схему, количество записей, идентификаторы и суммы рёбер. Затем она вычисляет степени узлов, видимые суммы, PageRank, достижимость от исходных клиентов и поддержку позднейших исходящих переводов.
  3. Детерминированные правила назначают одну роль каждому узлу. Louvain разбивает граф на кластеры; состав ролей в группе формирует гипотезу для проверки, отдельная формула рассчитывает приоритет узлов.
  4. Программа создаёт в out/ файлы nodes_roles.csv, clusters.csv и top_nodes.csv и проверяет их содержимое.
  5. viewer.py показывает локальную страницу: аналитик сначала видит обзор набора и распределение ролей, затем вводит gid, читает обоснование и видит входящие и исходящие связи. Сортировка групп по внутреннему обороту не является оценкой угрозы.

Схема компонентов находится в ARCHITECTURE.md. Основной расчёт не зависит от LLM, внешнего API или ручного редактирования данных.

Технологии

КомпонентНазначение
Python 3.10Расчёт, проверки и локальный HTTP-сервер
pandas, pyarrowЧтение Parquet, агрегирование транзакций, запись CSV
NetworkX, NumPy, SciPyГрафовые показатели, PageRank, кластеры Louvain
run_pipeline.pyВалидация → признаки → роли → кластеры → рейтинг → CSV
viewer.pyЛокальный HTML/CSS/SVG-дашборд с переходом от обзора к выбранному gid
verify_project.pyБыстрая офлайн-проверка установки и опубликованных результатов
LaunchDemo.vbs, launch_demo.pywWindows-запуск без терминала; ошибки записываются в out/launcher.log
tests/test_pipeline.pyПроверка полного сценария и контрольных графов

AI-модели, GPU, облачные сервисы и API OpenAI/NVIDIA для запуска не используются. AI-агент Codex применялся при разработке проекта.

Системные требования и настройка

  • Проверенная среда: 64-битная Windows 10, 64-битный Python 3.10.11. Команды для macOS/Linux приведены ниже, но отдельно на этих системах не проверялись.
  • Нужны модули Python venv и pip, браузер для просмотра результата и доступ к Python Package Index либо локальным пакетам при установке зависимостей.
  • Точные версии пяти библиотек закреплены в requirements.txt. Все входные данные находятся в data/; отдельная загрузка датасета не нужна.
  • Переменные окружения не требуются. Ключи API, личные аккаунты, подписки и демонстрационные учётные записи не нужны. После установки пакетов расчёт и просмотр работают без интернета.
  • По умолчанию входная папка — data/, выходная — out/, адрес страницы — 127.0.0.1:8765. Параметры --data и --out доступны обеим программам; --host и --port — просмотрщику.

Установка и запуск

Откройте PowerShell в корне репозитория (там находятся README.md, requirements.txt, run_pipeline.py и папка data/). Выполните команды по порядку:

python --version
python -m venv .venv
.venv\Scripts\python -m pip install -r requirements.txt
.venv\Scripts\python run_pipeline.py

Первая команда должна показать Python 3.10.x. Последняя печатает Validated 2248 nodes, 3119 edges, количество кластеров, путь к out/ и время работы. На проверенной машине расчёт занимает около одной секунды; требование кейса — менее пяти минут.

Запуск без терминала на Windows после установки: дважды щёлкните LaunchDemo.vbs. Программа заново рассчитает out/ и откроет страницу в браузере. Внизу страницы нажмите «Остановить сервер», когда закончите работу. Отдельный .exe в репозитории не нужен: запуск использует установленный Python из .venv и стандартные средства Windows. Если браузер не открылся, адрес и сообщения находятся в out/launcher.log; при ошибке появится окно с причиной. Для жюри также сохранён обычный запуск командами ниже.

На macOS/Linux создайте окружение командой python3 -m venv .venv, затем используйте .venv/bin/python вместо .venv\Scripts\python в остальных командах. Если входные или выходные файлы расположены иначе, запустите run_pipeline.py --data PATH --out PATH, а затем viewer.py --data PATH --out PATH с теми же путями.

Как жюри проверить решение

Быстрая проверка одной командой после установки зависимостей:

.venv\Scripts\python verify_project.py

Ожидаются четыре строки OK и сообщение PASS. Команда не меняет out/ или submission/: новый расчёт делается во временной папке. При отсутствии зависимости, входного файла или несовпадении CSV она завершится с конкретной причиной и ненулевым кодом. Это также удобно выполнить перед отправкой итоговой версии.

  1. После расчёта должны появиться out/nodes_roles.csv (2 248 строк данных), out/clusters.csv (66 строк данных на предоставленном наборе) и out/top_nodes.csv (20 строк данных). В nodes_roles.csv у каждого gid заполнены role, role_score, cluster_id, priority_score, evidence; оценки лежат в [0, 1].

  2. Запустите тесты:

    .venv\Scripts\python -m unittest discover -s tests -v
    

    Ожидается Ran 6 tests и OK. Тесты повторно рассчитывают CSV, сравнивают их с готовыми файлами в submission/, проверяют полный охват gid, защиту обрыва четвёртого колена, порядок переводов, поиск произвольного узла, открытие браузера и завершение сервера кнопкой с токеном сеанса.

  3. Запустите страницу просмотра:

    .venv\Scripts\python viewer.py
    

    Страница откроется в браузере автоматически. Если браузер не открылся, используйте напечатанный адрес, обычно http://127.0.0.1:8765/. Вверху виден обзор сети, ниже — первый узел рейтинга. В таблице групп нажмите «Открыть участника» или скопируйте любой gid из out/nodes_roles.csv, вставьте в поиск и нажмите «Показать». Должны появиться роль, числовое обоснование, кластер и направленные связи. Поле ввода хранит 18-значный gid как текст, без округления. Остановить сервер можно сочетанием Ctrl+C.

Если порт 8765 занят, используйте viewer.py --port 8766. В среде без браузера используйте viewer.py --no-browser: адрес останется в терминале. Если входной файл отсутствует либо имеет неверную структуру, расчёт завершится с сообщением об ошибке вместо фиктивного результата. Папка out/ создаётся локально и в Git не хранится; готовые проверяемые выгрузки находятся в submission/.

Данные и результаты

Источник — анонимизированный набор организаторов HackAlem AI за июль 2026 года; он включён в репозиторий. Полная схема описана в data/README.md.

Входной файлСодержимоеСтрок
data/nodes.parquetgid, минимальная глубина, признак исходного клиента2 248
data/edges.parquetПлательщик → получатель, общая сумма, число переводов3 119
data/transactions.parquetОтдельные переводы с датами и суммами4 840
Выходной файлОсновные поля
out/nodes_roles.csvgid, role, role_score, cluster_id, priority_score, evidence; также вычисленные признаки
out/clusters.csvcluster_id, n_nodes, n_seed, sum_kzt_internal, top_gids, hypothesis
out/top_nodes.csvrank, gid, role, priority_score, why

Внешних интеграций при работе программы нет. Сторонние AML-датасеты не нужны и не загружаются.

Три файла в submission/ являются опубликованным результатом на предоставленных данных; команда run_pipeline.py позволяет жюри независимо получить их заново.

Правила ролей и приоритета

Признаки вычисляются только по видимым переводам: количество контрагентов на входе и выходе, суммы и число переводов, взвешенный PageRank, глубина, достижимость от исходных клиентов и отношение out_kzt / in_kzt. Для предоставленных данных расчётные пороги: fan-in ≥3, fan-out ≥8, вход ≥166 819,7 KZT, выход ≥397 500 KZT, PageRank ≥0,00180444, двусторонний поток ≥43 500 KZT. Программа печатает пороги при каждом запуске.

РольФормальное условие кандидата
consolidatorЧисло входящих контрагентов не ниже 90-го процентиля среди положительных значений (минимум 3), а видимый вход — не ниже 75-го процентиля. Для исходных клиентов оценка роли ограничена из-за неполноты входящих данных.
distributorЧисло исходящих контрагентов не ниже 90-го процентиля (минимум 3), а видимый выход — не ниже 75-го процентиля.
transitНе исходный клиент; есть вход и выход; меньшая из сумм не ниже медианы двустороннего потока; отношение выхода к входу в [0.8, 1.2]; поддержанный более поздними днями выход не ниже 50% меньшей из двух сумм.
coordinatorНе менее двух входящих и двух исходящих связей, достижимость от двух исходных клиентов и PageRank не ниже 98-го процентиля.
terminalНе исходный клиент на глубине менее 4, с видимым входом и без видимого выхода. Это конечная точка в пределах выгрузки.
peripheralБолее сильного ролевого признака нет. Узел глубины 4 без видимого выхода не считается автоматически terminal.

Для временно́й проверки дневные суммы обрабатываются по порядку. Исходящий перевод может опираться только на видимый вход из предыдущего календарного дня или ранее: внутри одного дня время операций неизвестно. Это не доказывает, что далее ушли те же деньги. Порог 50% — открытое эвристическое решение, а не норматив или обученная модель. Столбцы temporal_support_kzt и temporal_support_ratio позволяют проверить его влияние.

Если одному узлу подходят несколько ролей, выбирается наиболее сильный сигнал с фиксированным разрешением равенства. role_score означает силу видимых признаков, не вероятность преступления. Для кластеров Louvain используется ненаправленная проекция с логарифмом сумм и фиксированным начальным значением; направление сохраняется при расчёте ролей и показе связей. priority_score объединяет равновесные процентильные ранги силы роли, числа контрагентов, видимого оборота, PageRank и связи с исходными клиентами. Узлы на границе четвёртого колена получают понижение приоритета. В evidence и why приводятся числовые основания.

Гипотеза кластера описывает сочетание наблюдаемых ролей: признаки сбора и/или распределения, возможного транзита либо отсутствие достаточного сигнала. Она дополняется числом узлов, исходных клиентов и внутренним оборотом. Общая цель участников по этим данным неизвестна; каждая гипотеза требует проверки аналитиком.

Известные ограничения

  • Обход ограничен четырьмя коленами. У 444 узлов глубины 4 без видимого выхода дальнейшие переводы неизвестны.
  • Выгрузка построена по исходящим переводам от исходных клиентов. Входящие переводы к ним извне графа и переводы меньше 5 000 KZT не видны; наблюдаемый поток не равен полному балансу.
  • Даты имеют точность до дня. Внутридневной порядок переводов определить нельзя; временная проверка не устанавливает происхождение средств.
  • В наборе нет эталонной разметки ролей. Качество ролей нельзя честно представить как точность классификатора; это объяснимые гипотезы для аналитика.
  • Просмотрщик выводит до 16 крупнейших входящих и исходящих рёбер выбранного узла; суммарные счётчики учитывают все связи.
  • Реализация рассчитана на предоставленные 2 248 узлов. Для графа на миллионы узлов понадобятся другие средства хранения и обработки.
  • Испытания выполнены на Windows 10; запуск на других системах отдельно не подтверждён. Командная репетиция пятиминутной демонстрации остаётся организационной задачей.

Что изменится при росте до ~1 млн узлов

Текущие pandas и NetworkX подходят для предоставленных 2 248 узлов, но миллионный граф нельзя обоснованно обещать обработать тем же способом и за то же время. При таком росте чтение Parquet и агрегацию транзакций пришлось бы выполнять порциями, хранить рёбра в более компактной структуре и заменить графовые расчёты на инструменты для крупных графов, например igraph. Подсчёт seed_reach должен сохранить число разных исходных клиентов, достигающих узла: простой обход от всех seed сразу этого не даёт. PageRank и кластеризацию пришлось бы считать отдельно с контролем памяти и времени. Просмотрщик должен запрашивать по индексу только соседей выбранного gid, а не загружать в память всю таблицу рёбер. Это план развития, а не реализованная или измеренная производительность текущего проекта.

Развёрнутая версия

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

Источники и раскрытие сторонних материалов

  • data/ и data/README.md — набор, предоставленный организаторами HackAlem AI.
  • starter/ — предоставленный организаторами стартовый код для загрузки Parquet и базовых графовых метрик. Его выходные роли и приоритеты были заглушками; правила, проверки и просмотрщик этого проекта разработаны в ходе соревнования.
  • Перечень Python-библиотек и их версии находятся в requirements.txt; применяются соответствующие открытые лицензии.
  • Статья IBM AMLworld использована как методический источник терминов fan-in и fan-out. Паттерн транзакций не является меткой виновности конкретного клиента. Чужой код и внешние AML-датасеты в проект не включены.
  • Примеры дашбордов ZoomCharts послужили визуальным ориентиром для перехода от сводки к деталям. Их код, изображения, шаблоны и платные компоненты не включены; просмотрщик использует собственные HTML/CSS/SVG и не требует Power BI или лицензии ZoomCharts.
Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
16
Авторы
1
Файлы
24
Ветки
2

Авторы коммитов

Ahmet Sakbaev 16

История коммитов

  • 2026-09-23 17:34feat: make transaction links readable by rowAhmet Sakbaev
  • 2026-09-23 17:25feat: refine dashboard visual hierarchy and flow infographicAhmet Sakbaev
  • 2026-09-23 16:58feat: add Windows one-click demo launcherAhmet Sakbaev
  • 2026-09-23 16:38feat: open viewer in browser on startupAhmet Sakbaev
  • 2026-09-23 16:29feat: add offline submission verification commandAhmet Sakbaev
  • 2026-09-23 16:26feat: add explainable network overview dashboardAhmet Sakbaev
  • 2026-09-23 16:19feat: publish reproducible submission CSV artifactsAhmet Sakbaev
  • 2026-09-23 15:44feat: explain cluster hypotheses from observed rolesAhmet Sakbaev
Все коммиты (16)
  • 2026-09-23 15:40feat: explain analysis in local viewerAhmet Sakbaev
  • 2026-09-23 15:18docs: provide Russian jury setup and verification guideAhmet Sakbaev
  • 2026-09-23 15:10feat: validate transit timing and document reproducible setupAhmet Sakbaev
  • 2026-09-23 14:58fix: preserve CSV headers and full gid lookupAhmet Sakbaev
  • 2026-09-23 14:54feat: build explainable Money Graph pipeline and viewerAhmet Sakbaev
  • 2026-09-23 14:26docs: define Money Graph hackathon solution scopeAhmet Sakbaev
  • 2026-09-23 13:51chore: add project development baselineAhmet Sakbaev
  • 2026-09-23 13:35chore: establish pre-development project baselineAhmet Sakbaev
Структура репозитория (24 файлов)
.gitignore
AGENTS.md
ARCHITECTURE.md
CONSTITUTION.md
DEVELOPMENT.md
LaunchDemo.vbs
README.md
data/README.md
data/edges.parquet
data/nodes.parquet
data/transactions.parquet
launch_demo.pyw
requirements.txt
run_pipeline.py
starter/README.md
starter/requirements.txt
starter/starter.py
submission/README.md
submission/clusters.csv
submission/nodes_roles.csv
submission/top_nodes.csv
tests/test_pipeline.py
verify_project.py
viewer.py
Ветки (2)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.