Урок 13: Патерни, тренди та кластеризація

Усі уроки · Домашнє завдання

Слайди (Презентаційний текст)

Слайд 1 — Патерни, тренди та кластеризація: Neo4j і Gephi на даних Bitcoin

  • Заняття №13
  • AI для OSINT і розвідки

Слайд 2 — ПЛАН ЗАНЯТТЯ

  • Від воркшопу до масштабу: три питання
  • Дані: BitcoinTemporalGraph та Elliptic++
  • Neo4j: імпорт, Cypher-патерни, GDS
  • Кластеризація: евристики та спільноти
  • Тренди: час, дрейф, закриття ринку
  • Gephi: від підграфа до висновку
  • Обмеження, етика та практика

Слайд 3 — ВІД ВОРКШОПУ ДО МАСШТАБУ

  • Минулого заняття — воркшоп
  • Ручне трасування в оглядачах і графах аналітичних платформ
  • Peeling-ланцюги, пулінг, адреси-«сховища» міксерів
  • Кейс A7A5: Garantex → A7A5 → Grinex
  • Neo4j згадали як інструмент — сьогодні працюємо в ньому

Сьогодні

  • 822 942 гаманці замість десятка адрес
  • Патерн = запит, який можна повторити
  • Кластер = алгоритм із параметрами, а не «на око»
  • Тренд = метрика в часі + перевірка дрейфу
  • Візуалізація в Gephi як аргумент у звіті
  • Мета заняття: перетворити інтуїцію з воркшопу на відтворювані запити, метрики та візуалізації.

Слайд 4 — ТРИ ПИТАННЯ АНАЛІТИКА

ПАТЕРН

  • Як рухаються кошти?
  • Fan-in, fan-out, peel chain, цикли, CoinJoin
  • Структура, а не окрема транзакція
  • Інструмент: Cypher-шаблони шляхів

ТРЕНД

  • Що змінюється в часі?
  • Обсяги illicit, нові вузли, зникнення сервісів
  • Дрейф після подій
  • Інструмент: Агрегації за кроком часу, Timeline у Gephi

КЛАСТЕР

  • Хто діє разом?
  • Евристики адрес, спільноти, схожа поведінка
  • Групи як одиниця аналізу
  • Інструмент: GDS: WCC, Louvain, Leiden, k-means
  • Кожна відповідь — гіпотеза для офчейн-перевірки, а не вирок.

Слайд 5 — ЧАСТИНА 1

  • Дані: два датасети
  • BitcoinTemporalGraph: як будують граф
  • Формула ребра на прикладі
  • OSINT-розмітка: BitcoinTalk + LLM
  • Мітки: джерела та розподіл
  • Критичне читання статті
  • Elliptic++: файли, мітки, час

Слайд 6 — ДВА ДАТАСЕТИ

На слайді порівняльна таблиця BitcoinTemporalGraph і Elliptic++ (масштаб, мітки, час, формат).


Слайд 7 — ЯК ПОБУДОВАНО BITCOINTEMPORALGRAPH

    1. Сирі дані
  • Вузол Bitcoin Core → файли blk*.dat; перші 700 000 блоків, ~13 років історії
    1. Скрипти
  • Кожен вихід (TXO) захищений locking script; нульові TXO відкинуто → понад 874 млн скриптів
    1. Кластери
  • Евристики об’єднують скрипти одного власника → ~252 млн сутностей, це і є вузли
    1. Ребра
  • Чиста сума v(a) = отримано − витрачено: v > 0 — отримувач, v < 0 — відправник
    1. Фільтри
  • CoinJoin (JoinMarket, Wasabi, Whirlpool) і colored coins (Omni, Open Assets, EPOBC) виключено
    1. Атрибути
  • Ребро: reveal, last_seen, total, min/max/total_sent. Вузол: ступені, суми, часові межі, розмір кластера

Слайд 8 — ФОРМУЛА РЕБРА НА ПРИКЛАДІ

Приклад транзакції з рештою (схема на слайді): входи A = 1,0 BTC, B = 0,5 BTC; вихід C = 1,2 BTC; решта на A = 0,29 BTC; комісія майнеру 0,01 BTC.

  • v(a) = Σ виходів a − Σ входів a
  • v(A) = 0,29 − 1,0 = −0,71 → відправник
  • v(B) = 0 − 0,5 = −0,5 → відправник
  • v(C) = 1,2 − 0 = +1,2 → отримувач
  • Частки входів: A = 1,0 / 1,5 = ⅔, B = ⅓
  • Ребра: A → C = ⅔ × 1,2 = 0,8 BTC; B → C = ⅓ × 1,2 = 0,4 BTC
  • Решта A → A не створює ребра
  • Модельне рішення: розподіл за часткою входів — фіксуйте це в методології.

Слайд 9 — OSINT-РОЗМІТКА: BITCOINTALK + LLM

BitcoinTalk: 14 067 713 постів, 546 440 тем (англ.); контекст = тема, пост, деталі tx + курс USD на дату.

gpt-4o-mini: 4 типи промптів (депозит, виведення, hot/cold) → міркування, назва сутності, тип, адреси.

Мітка вузла: назва → категорія; конфлікт → без мітки.

  • Депозит: скарга, що кошти не зараховано → адреса депозиту належить сервісу
  • Виведення: сервіс повідомив txid → входи tx = гарячий гаманець сервісу
  • Виведення без txid: пошук tx ±3 дні на адресу з сумою; якщо одна — атрибуція
  • Назву сутності мапують на 11 категорій; адреса → скрипти → кластер (вузол)
  • Кілька різних міток в одному кластері → мітку не ставлять
  • Це класичний OSINT: офчейн-контекст дає атрибуцію ончейн-адрес.

Слайд 10 — МІТКИ: ДЖЕРЕЛА ТА РОЗПОДІЛ

  • Джерела міток адрес (рядки таблиці 4)
  • Типи сутностей серед вузлів з мітками
  • 34 098 вузлів з мітками = 0,014% графа (≈ 1 вузол із 7 400). Мітки рідкісні — сигнал треба брати зі структури.

Слайд 11 — ЧИТАЄМО СТАТТЮ ЯК OSINT-ДЖЕРЕЛО

  • Що в статті
  • Перевірка
  • Висновок
  • Щільність графа «≈ 1%»
  • 785 954 737 / (252 219 007 × 252 219 006) ≈ 1,2 × 10⁻⁸
  • Помилка на ~6 порядків; середній ступінь ≈ 6,2
  • «101 186 адрес з мітками»
  • Сума рядків табл. 3 і табл. 4 = 103 619; відсотки рахують саме від неї
  • Розбіжність у 2 433 адреси не пояснено
  • «SDN List веде Мін’юст США»
  • Список SDN веде OFAC (Мінфін США); посилання статті — на sanctionslist.ofac.treas.gov
  • Неточна атрибуція джерела
  • Табл. 1: b — «alias of the sender»
  • Ребро a → b: b має бути отримувачем
  • Опечатка в схемі даних
  • Препринт — це джерело, а не істина: перераховуйте ключові цифри, перш ніж цитувати їх у звіті.

Слайд 12 — ELLIPTIC++: ЩО ВСЕРЕДИНІ

  • Файл
  • Зміст
  • Рядків
  • txs_features.csv
  • 183 ознаки: крок часу + 93 local + 72 aggregated + 17 augmented
  • 203 769 — txs_classes.csv
  • Клас: 1 illicit · 2 licit · 3 unknown
  • 203 769 — txs_edgelist.csv
  • tx → tx (потік коштів)
  • 234 355 — wallets_features.csv
  • 56 ознак гаманця, рядок = гаманець × крок
  • 1 268 260 — wallets_classes.csv
  • Клас гаманця
  • 822 942 — AddrAddr_edgelist.csv
  • Адреса → адреса
  • 2 868 964 — AddrTx_edgelist.csv
  • Адреса → tx (вхід)
  • 477 117 — TxAddr_edgelist.csv
  • tx → адреса (вихід)
  • 837 124 — Граф адреса → tx → адреса для illicit-гаманця 39sfuA…5K. Рисунок: Elmougy & Liu, Elliptic++

Слайд 13 — ELLIPTIC++: МІТКИ ТА ЧАС

Транзакції (203 769)

  • 4 545 — illicit · 2,2%
  • 42 019 — licit · 20,6%
  • 157 205 — unknown · 77,1%

Гаманці (822 942)

  • 14 266 — illicit · 1,7%
  • 251 088 — licit · 30,5%
  • 557 588 — unknown · 67,8%

Час

  • 49 кроків, ~2 тижні між ними
  • Крок = транзакції, підтверджені в межах < 3 годин
  • Перевірено: 49 компонент = 49 кроків, ребер між кроками — 0
  • Гаманець: перший і останній крок появи
  • unknown ≠ licit: три з чотирьох транзакцій не розмічені — це простір для гіпотез, а не «чисті» дані.

Слайд 14 — ЧАСТИНА 2

  • Neo4j: від CSV до патернів
  • Чому графова база даних
  • Встановлення: Desktop або Docker
  • Модель даних і підготовка CSV
  • Імпорт Elliptic++
  • Галерея патернів і сигнатури
  • Cypher-запити та Graph Data Science

Слайд 15 — ЧОМУ ГРАФОВА БАЗА ДАНИХ

SQL (PostgreSQL): рекурсивний CTE для 3 хопів — довгий і важкий для підтримки.

Cypher (Neo4j): той самий запит компактніше:

MATCH (:Wallet {address: '1Abc...'})-[:SENT_TO*1..3]->(w:Wallet)
RETURN DISTINCT w.address;
  • PostgreSQL (як у статті): масове сховище на 120 ГБ, вибірки, агрегати
  • Neo4j: багатокрокові шляхи, патерни, алгоритми GDS, швидка візуальна перевірка
  • Практичне правило: велике сховище — у SQL, підграф розслідування — у Neo4j.

Слайд 16 — NEO4J: ВСТАНОВЛЕННЯ

Версії (листопад 2026)

  • Neo4j 2026.10.1 — Cypher 25 за замовчуванням
  • GDS і APOC 2026.10.1 уже в дистрибутиві: products/ і labs/

Neo4j Desktop: база → Plugins → APOC, Graph Data Science → Install

Tarball або Docker: скопіювати jar-файли в plugins/ або:

docker run -d --name elliptic \
  -p 7474:7474 -p 7687:7687 \
  -v $PWD/import:/import \
  -v $PWD/data:/data \
  -e NEO4J_AUTH=neo4j/osint2026pass \
  -e NEO4J_PLUGINS='["apoc","graph-data-science"]' \
  -e NEO4J_apoc_export_file_enabled=true \
  -e NEO4J_server_memory_heap_max__size=4G \
  -e NEO4J_server_memory_pagecache_size=2G \
  neo4j:2026.10.1
# Браузер: http://localhost:7474

Перевірка: RETURN gds.version(), apoc.version();

У Neo4j Browser запити з CALL … IN TRANSACTIONS запускайте з префіксом :auto.


Слайд 17 — МОДЕЛЬ ДАНИХ У NEO4J

Три графи в одному датасеті:

ЕлементЗміст
(:Transaction)txId, timeStep, class, nIn, nOut, totalBTC, fees, outMin, outMax
(:Wallet)address, class, firstStep, lastStep
[:FLOWS_TO]tx → tx (потік коштів)
[:INPUT] / [:OUTPUT]wallet ↔ tx (двочастковий)
[:SENT_TO {n}]wallet → wallet; n — скільки разів пара зустрілась

Один датасет — три графи: потік коштів (tx → tx), взаємодія гаманців (addr → addr), двочастковий (addr → tx → addr).


Слайд 18 — ПІДГОТОВКА CSV У PANDAS

Скрипт prep.py стискає сирі CSV Elliptic++:

  • 10 зі 184 колонок: 695 МБ → 14 МБ
  • Гаманець: один рядок + перший і останній крок
  • Self-loops (решта на ту саму адресу): прибрано 45 981
  • Дублікати пар → вага n: 2 822 983 → 2 775 637 пар, n до 87
  • Гігієна даних: кожне перетворення — у скрипті поруч з даними, а не вручну в таблиці.

Слайд 19 — ІМПОРТ: LOAD CSV

Обмеження унікальності — до імпорту. Повний скрипт — 01_import.cypher.

CREATE CONSTRAINT tx_id IF NOT EXISTS
FOR (t:Transaction) REQUIRE t.txId IS UNIQUE;
CREATE CONSTRAINT wallet_addr IF NOT EXISTS
FOR (w:Wallet) REQUIRE w.address IS UNIQUE;
 
LOAD CSV WITH HEADERS FROM 'file:///tx_nodes.csv' AS row
CALL (row) {
  CREATE (:Transaction {txId: toInteger(row.txId), ...})
} IN TRANSACTIONS OF 10000 ROWS;
 
LOAD CSV WITH HEADERS FROM 'file:///sent_to.csv' AS row
CALL (row) {
  MATCH (a:Wallet {address: row.input_address})
  MATCH (b:Wallet {address: row.output_address})
  CREATE (a)-[:SENT_TO {n: toInteger(row.n)}]->(b)
} IN TRANSACTIONS OF 20000 ROWS;

Імпорт: 4 хв 17 с на 2 CPU / 7 ГБ RAM; найдовше — SENT_TO (147 с).


Слайд 20 — ГАЛЕРЕЯ ПАТЕРНІВ

  • Fan-in · консолідація
  • Багато відправників → 1 отримувач
  • Fan-out · розпилення
  • 1 відправник → багато отримувачів
  • Peel chain
  • Ланцюг tx: малий «відщеп» + решта далі
  • Цикл · round-trip
  • Кошти повертаються до старту
  • CoinJoin
  • Входи різних власників → рівні виходи
  • Міст · хаб
  • Вузол між двома щільними групами

Слайд 21 — ПАТЕРН → СИГНАТУРА → ЗАПИТ

Патерн — гіпотеза: у кожного є легітимне пояснення, яке треба виключити до висновку.

На слайді таблиця «патерн → сигнатура → запит → легітимне пояснення».


Слайд 22 — CYPHER: КОНСОЛІДАЦІЯ ТА ЕКСПОЗИЦІЯ

  • Fan-in: хто збирає кошти
MATCH (s:Wallet)-[:SENT_TO]->(w:Wallet)
WITH w, count(s) AS senders
WHERE senders >= 100
RETURN w.address, w.class, senders
ORDER BY senders DESC LIMIT 10;
  • Експозиція: куди доходять кошти illicit
MATCH (i:Wallet {class: 1})
-[:SENT_TO*1..2]->(u:Wallet {class: 3})
WITH u, count(DISTINCT i) AS exposure
WHERE exposure >= 5
RETURN u.address, exposure
ORDER BY exposure DESC LIMIT 10;
  • Результат на Elliptic++ (4,3 с): ≥ 100 відправників мають 2 680 гаманців — 2 078 licit, 578 unknown, лише 24 illicit
  • Топ-1: 11 213 відправників, licit. Fan-in — передусім сервіси, а не злочин
  • Результат (0,5 с): два unknown-гаманці отримують кошти від 1 061 illicit-гаманця кожен, далі — 319 і 190
  • Однакові значення — підказка, що це адреси одного сервісу: перевірте fan-in і офчейн

Слайд 23 — CYPHER: PEEL CHAIN, ЦИКЛИ, ШЛЯХИ

// Peel chain: >= 5 tx поспіль з 2 виходами, «відщеп» < 10% від більшого виходу
MATCH p = (s:Transaction {class: 1})-[:FLOWS_TO*5..8]->(:Transaction)
WHERE all(t IN nodes(p) WHERE t.nOut = 2 AND t.outMin < 0.1 * t.outMax)
RETURN length(p) AS hops, [t IN nodes(p) | t.txId] AS chain LIMIT 5;
// Цикли: повернення за 2–4 кроки
MATCH p = (w:Wallet {class: 1})
-[:SENT_TO*2..4]->(w)
RETURN [x IN nodes(p) | x.address] LIMIT 5;
// Найкоротший шлях між двома гаманцями
MATCH (a:Wallet {address: $from})
MATCH (b:Wallet {address: $to})
MATCH p = shortestPath((a)-[:SENT_TO*..8]->(b))
RETURN [x IN nodes(p) | x.address], length(p);
  • Peel chain (0,14 с): умову «2 виходи, малий < 10%» задовольняють 102 777 tx — половина датасету; ланцюг ≥ 5 стартує з 366 illicit-tx, найдовший — 182 кроки. Поріг надто загальний
  • Цикли (1,8 с): приклад проходить через хаб-гаманець з 11 213 відправниками (licit) — round-trip через сервіс, а не обов’язково «відмивання»
  • Шлях: від illicit-гаманця до головного отримувача illicit-коштів — 2 кроки через інший illicit-гаманець

Слайд 24 — GRAPH DATA SCIENCE: ЦИКЛ РОБОТИ

    1. Проєкція
    1. Оцінка пам’яті
    1. Алгоритм
    1. Аналіз у Cypher
    1. Експорт у Gephi
  • // 1. Граф у пам’яті: гаманці, неорієнтовано, вага n
  • CALL gds.graph.project(‘wallets’, ‘Wallet’,
  • {SENT_TO: {orientation: ‘UNDIRECTED’, properties: ‘n’}});
  • // 2. Скільки пам’яті потрібно?
  • CALL gds.louvain.write.estimate(‘wallets’,
  • {writeProperty: ‘louvain’}) YIELD requiredMemory;
  • // 3. Алгоритм -> властивість у базі
  • CALL gds.louvain.write(‘wallets’, {writeProperty: ‘louvain’,
  • relationshipWeightProperty: ‘n’})
  • YIELD communityCount, modularity;

Режими виконання

  • stream — подивитися результат
  • stats — лише підсумок
  • mutate — у проєкцію, для наступного алгоритму
  • write — записати у вузли бази
  • Проєкція живе в пам’яті: після роботи — CALL gds.graph.drop(‘wallets’).

Слайд 25 — АЛГОРИТМИ GDS ДЛЯ РОЗСЛІДУВАНЬ

На слайді карта алгоритмів GDS для розслідувань (WCC, Louvain/Leiden, PageRank, Degree, FastRP + k-means тощо).


Слайд 26 — ПЕРЕРВА

  • 5 хвилин
  • Після перерви:
  • Кластеризація адрес і спільноти
  • Тренди та дрейф у часі
  • Gephi: імпорт, ForceAtlas2, фільтри
  • ML-бенчмарк і обмеження
  • Практика після заняття

Слайд 27 — ЧАСТИНА 3

  • Кластеризація
  • Три значення слова «кластер»
  • Евристики кластеризації адрес
  • CoinJoin: чому його виключають
  • Louvain і Leiden
  • Результати на Elliptic++ і ліди
  • ML-бенчмарк статті

Слайд 28 — ТРИ ЗНАЧЕННЯ «КЛАСТЕРИЗАЦІЇ»

  • 1 — Кластери адрес
  • Entity resolution
  • Питання: які адреси належать одному власнику?
  • Метод: евристики спільних входів і решти
  • Результат: вузол = сутність (як у BitcoinTemporalGraph)
  • 2 — Спільноти в графі
  • Community detection
  • Питання: хто взаємодіє щільніше, ніж з рештою?
  • Метод: WCC, Louvain, Leiden
  • Результат: групи гаманців або сутностей
  • 3 — Кластери ознак
  • Feature clustering
  • Питання: хто поводиться схоже?
  • Метод: k-means на ознаках або ембедингах (FastRP)
  • Результат: поведінкові профілі
  • Пишіть у звіті, яку саме кластеризацію застосовано: це три різні твердження.

Слайд 29 — ЕВРИСТИКИ КЛАСТЕРИЗАЦІЇ АДРЕС

На слайді схема tx з адресами A–E: синій = одна оцінена сутність (A, B, C — спільні входи; E — імовірна решта).

  • Спільні входи (common-input ownership): усі входи однієї tx підписує один власник → адреси об’єднуються
  • Адреса решти (change): новий вихід того ж типу, «некругла» сума, адреса з’являється вперше
  • У статті: евристики з arXiv 2403.00523 — 874 млн скриптів → ~252 млн кластерів
  • Хибне злиття незворотне: один CoinJoin, пропущений фільтром, «склеює» сторонніх людей в одну сутність.

Слайд 30 — COINJOIN: ЧОМУ ЙОГО ВИКЛЮЧАЮТЬ

  • CoinJoin
  • Кольори входів — різні власники; 4 виходи по 0,1 BTC — нерозрізненні

Що відбувається

  • Кілька користувачів в одній транзакції
  • Рівні виходи ховають, хто кому заплатив
  • Евристика спільних входів дає хибне злиття

Що зробили автори

  • Детекція евристиками для JoinMarket, Wasabi, Whirlpool (arXiv 2311.12491)
  • Виключено і з кластеризації, і з ребер графа
  • CoinJoin — сигнал бажання приватності, а не доказ злочину.

Слайд 31 — СПІЛЬНОТИ: LOUVAIN І LEIDEN

Модулярність Q

  • Частка ребер усередині спільнот мінус очікувана при випадкових зв’язках

Louvain (Blondel et al., 2008)

  • Жадібно переносить вузли між спільнотами й «згортає» граф рівнями

Leiden (Traag et al., 2019)

  • Додає крок уточнення: спільноти гарантовано зв’язні

Resolution γ

  • Більше γ → більше дрібних спільнот
  • CALL gds.louvain.write(‘wallets’, {
  • writeProperty: ‘louvain’,
  • relationshipWeightProperty: ‘n’})
  • YIELD communityCount, modularity;
  • CALL gds.leiden.write(‘wallets’, {
  • writeProperty: ‘leiden’,
  • relationshipWeightProperty: ‘n’,
  • gamma: 1.0})
  • YIELD communityCount, modularity;
  • Біржі й великі сервіси — «супервузли»: вони склеюють спільноти. Позначте або виключіть їх перед запуском.

Слайд 32 — РЕЗУЛЬТАТ НА ELLIPTIC++

  • 2 — компоненти WCC
  • 450 — спільнот Louvain
  • 0,927 — модулярність Louvain
  • 428 — спільнот Leiden
  • Спільнота
  • Розмір
  • Illicit
  • Illicit, %
  • 617545 — 3 985
  • 1 123 — 28,2
  • 323119 — 2 448
  • 501 — 20,5
  • 512119 — 4 694
  • 894 — 19,0
  • 670389 — 2 616
  • 473 — 18,1
  • 137179 — 1 397
  • 239 — 17,1
  • Базова частка illicit серед гаманців — 1,7%. Лідер — 28,2%: у 16 разів вище
  • Спільнота 323119: 2 448 гаманців, 3 399 ребер; illicit 501 — червоні, licit 506 — сині, unknown 1 441 — сірі; розмір — PageRank. ForceAtlas2 (networkx)

Слайд 33 — ВІД СПІЛЬНОТИ ДО ЛІДА

    1. Спільнота
  • Частка illicit у рази вища за базову (1,7%): поріг ≥ 10%. На Elliptic++ максимум — 28%, а не 50%
    1. Кандидати
  • Невідомі гаманці всередині, ранжовані за PageRank. На Elliptic++: 10 спільнот ≥ 10% → 31 811 кандидатів; топ-1 — 1781T…mqt, він же лідер експозиції
    1. Патерни
  • Fan-in, peel chain, експозиція навколо кандидата — чи є структурне підтвердження?
    1. Офчейн
  • Пошук адреси: форуми (BitcoinTalk), мітки оглядачів, санкційні списки OFAC, звіти
    1. Висновок
  • Рівень впевненості, альтернативні пояснення, обмеження даних
  • Поріг рахуйте від базової частки, а не «на око». Лід — адреса для перевірки, а не підозрюваний.

Слайд 34 — ЧИ ПЕРЕДБАЧАЄ ГРАФ ТИП СУТНОСТІ?

  • Модель
  • Macro-F1
  • Exchange
  • Mining
  • Gambling
  • Ponzi
  • Individual
  • Ransomware
  • Bet

GCN

  • 0,56 — 0,43
  • 0,45 — 0,56
  • 0,46 — 0,88
  • 0,19 — 0,98
  • GraphSAGE
  • 0,62 — 0,51
  • 0,57 — 0,58
  • 0,55 — 0,93
  • 0,25 — 0,98

GAT

  • 0,64 — 0,49
  • 0,57 — 0,62
  • 0,57 — 0,93
  • 0,32 — 0,98

GIN

  • 0,63 — 0,54
  • 0,62 — 0,60
  • 0,94 — 0,21
  • 0,98 — GBC (без графа)
  • 0,57 — 0,54
  • 0,45 — 0,57
  • 0,30 — 0,94
  • 0,50 — 0,89
  • Задача: за ознаками вузла та його 2-крокового сусідства передбачити один із 7 типів
  • Сусідство: 10 сусідів на 1-му кроці, 5 на 2-му; 12 вибірок на вузол
  • Моделі: GNN з PyTorch Geometric (2 шари, 256 нейронів) і GBC зі sklearn
  • Розбиття: 40 / 30 / 30, класи вирівняні до 300–1500 зразків
  • Найкраща модель — GAT (macro-F1 0,64): сусідство допомагає. Але ransomware краще знаходить GBC без графа (0,50).

Слайд 35 — ДЕ ПОМИЛЯЄТЬСЯ МОДЕЛЬ

  • Справжній ↓ / прогноз →
  • Exch.
  • Mining
  • Gamb.
  • Ponzi
  • Indiv.
  • Rans.
  • Bet
  • Exchange
  • 64% — 3%
  • 7% — 4%
  • 19% — 2%
  • 1% — Mining
  • 5% — 70%
  • 3% — 17%
  • 1% — 0%
  • Gambling
  • 7% — 1%
  • 68% — 6%
  • 15% — 1%
  • 3% — Ponzi
  • 5% — 1%
  • 4% — 75%
  • 14% — 0%
  • 1% — Individual
  • 3% — 2%
  • 3% — 2%
  • 88% — 2%
  • 0% — Ransomware
  • 14% — 0%
  • 3% — 0%
  • 23% — 60%
  • 0% — Bet
  • 0% — 1%
  • 0% — 99%

Що видно

  • Усе «стікає» в Individual: 14–23% вузлів кожного класу, крім Bet
  • Ransomware: recall 60%, але F1 0,32 → точність ≈ 0,22: ~4 з 5 «ransomware»-прогнозів хибні
  • Bet — 99%: окрема адреса на кожну ставку
  • Для OSINT: ML-оцінка — це черга перевірки, а не атрибуція. Рахуйте точність, а не лише «скільки знайшли».

Слайд 36 — ЧАСТИНА 4

  • Тренди
  • Час у двох датасетах
  • Illicit за кроками часу
  • Дрейф після закриття ринку (t = 43)
  • Тренди в Cypher і Timeline у Gephi

Слайд 37 — ЧАС У ДАНИХ

Elliptic++: кроки

  • 49 кроків, ~2 тижні між ними
  • Крок — транзакції, підтверджені в межах < 3 годин
  • Ребер між кроками немає (перевірено: 49 компонент, 0 ребер між кроками)
  • Гаманець: firstStep, lastStep

BitcoinTemporalGraph: блоки

  • Мітка часу — індекс блоку (~10 хв)
  • Ребро: reveal (перша tx) і last_seen (остання)
  • Вузол: перша й остання вхідна та вихідна tx
  • Вік = max(last) − min(first)
  • Активність ділять на вік; суми → USD за медіанним курсом за період активності
  • Порівнюйте швидкості, а не абсолютні суми: 1 BTC у 2011 і у 2021 — різні «гроші».

Слайд 38 — ТРЕНД: ILLICIT ЗА КРОКАМИ ЧАСУ

  • 104 — illicit tx на крок, кроки 1–42
  • 24 — illicit tx на крок, кроки 43–49
  • 11,0% → 2,5% — частка illicit серед розмічених: кроки 1–42 → 43–49

Слайд 39 — ДРЕЙФ: ЧОМУ МОДЕЛІ «СЛІПНУТЬ»

Weber et al., 2019 (Elliptic)

  • Навчання на кроках 1–34, тест на 35–49 (70 : 30 за часом)
  • Крок 43: раптове закриття даркнет-ринку
  • Після нього жоден метод не виявляє нові illicit-транзакції
  • Навіть Random Forest, перенавчений після кожного кроку

Що це означає для OSINT

  • Екосистема змінюється стрибками: закриття ринків, санкції, нові сервіси
  • Приклад з воркшопу: Garantex → A7A5 → Grinex
  • Старі патерни застарівають — перевіряйте якість по кроках часу, а не в середньому
  • Стежте за часткою unknown і оновлюйте мітки
  • Тренд без контексту подій — лише крива. Накладайте на шкалу реальні події: закриття, арешти, санкції.

Слайд 40 — ТРЕНДИ: CYPHER І TIMELINE У GEPHI

MATCH (t:Transaction)
RETURN t.timeStep AS step, count(*) AS total,
       count(CASE WHEN t.class = 1 THEN 1 END) AS illicit
ORDER BY step;

Експорт інтервалів часу для Gephi:

CALL apoc.export.csv.query(
  'MATCH (t:Transaction) RETURN t.txId AS Id, t.class AS class,
   t.timeStep AS start, t.timeStep + 1 AS end',
  'tx_time.csv', {});
  1. Імпорт вузлів із колонками start, end і ребер
  2. Data Laboratory → Merge Columns → Create time interval (Parse numbers)
  3. Увімкнути Timeline, вікно = 1 крок
  4. Play або тягнути вікно: як з’являються й зникають кластери
  5. Statistics рахуються для видимого вікна — порівнюйте кроки між собою

Слайд 41 — ЧАСТИНА 5

  • Gephi: від підграфа до висновку
  • Gephi у 2026: що нового
  • Пайплайн Neo4j → Gephi
  • Імпорт і статистики
  • ForceAtlas2 і Appearance
  • Як читати картинку
  • Фільтри та візуалізація як доказ

Слайд 42 — GEPHI У 2026

Gephi Desktop 0.11

  • Реліз 0.11 — травень 2026; актуальна версія на gephi.org — 0.11.4
  • Новий OpenGL-рушій: до ~10 млн елементів (за заявою розробників)
  • Панель Attributes, нові Settings, трансформація Mirror
  • Інтерфейс тепер і українською

Gephi Lite 1.0

  • Реліз — жовтень 2025, працює в браузері: lite.gephi.org
  • ForceAtlas2, Louvain, фільтри, Appearance
  • Для невеликих графів: на момент релізу — до ~10 тис. вузлів або 20 тис. ребер
  • Зручно, коли немає прав на встановлення
  • Ролі: Neo4j — зберігання, запити, алгоритми. Gephi — дослідження, layout і візуальна історія для звіту.

Слайд 43 — NEO4J → GEPHI: ПАЙПЛАЙН

Пайплайн: підграф (Cypher) → експорт (APOC) → імпорт у Gephi → Statistics → Layout + Appearance → Preview → SVG/PDF.

A. Его-мережа illicit-гаманця → GraphML

CALL apoc.export.graphml.query(
  'MATCH p = (:Wallet {address: "1Abc..."})-[:SENT_TO*1..2]-(:Wallet)
   RETURN p LIMIT 2000',
  'ego_illicit.graphml', {useTypes: true});

B. Спільнота → CSV для Import Spreadsheet

CALL apoc.export.csv.query(
  'MATCH (w:Wallet) WHERE w.louvain = 123
   RETURN w.address AS Id, w.class AS class, w.pagerank AS pagerank',
  'nodes.csv', {});
 
CALL apoc.export.csv.query(
  'MATCH (a:Wallet)-[r:SENT_TO]->(b:Wallet)
   WHERE a.louvain = 123 AND b.louvain = 123
   RETURN a.address AS Source, b.address AS Target, r.n AS Weight',
  'edges.csv', {});
  • Потрібно apoc.export.file.enabled=true; файли з’являються в папці import/
  • GraphML зберігає типи властивостей; CSV: колонки Id; Source, Target, Weight
  • Его-мережа на 2 кроки навіть з LIMIT 2000 — це 1 319 вузлів (центр має 644 сусіди)

Слайд 44 — GEPHI: ІМПОРТ І СТАТИСТИКИ

  • Імпорт
  • File → Import spreadsheet: спершу nodes.csv (Id, class, pagerank), потім edges.csv → Append to existing workspace; граф Directed
  • Перевірка
  • Data Laboratory: типи колонок (class — як текст для розфарбування), кількість вузлів і ребер
  • Ступені
  • Statistics → Average Degree: in-degree / out-degree = fan-in / fan-out
  • Спільноти
  • Statistics → Modularity: Resolution, Use weights, Randomize → Modularity Class
  • Ролі
  • PageRank; Network Diameter → Betweenness і Closeness (повільно на великих графах)
  • Компоненти
  • Connected Components → Component ID; фільтр Giant Component

Слайд 45 — GEPHI: FORCEATLAS 2 І APPEARANCE

ForceAtlas 2 (Jacomy et al., 2014)

  • Scaling — розтягує граф
  • Gravity — тягне до центру, тримає острови поруч
  • LinLog mode — компактніші кластери
  • Dissuade Hubs — хаби на периферію
  • Prevent Overlap — вмикати наприкінці
  • Approximate Repulsion (Barnes-Hut) — для великих графів

Appearance

  • Колір вузлів: Partition → class або Modularity Class
  • Палітра: illicit — червоний, licit — синій, unknown — сірий
  • Розмір: Ranking → PageRank або In-Degree
  • Ребра: товщина за Weight, колір за джерелом
  • Підписи: лише для топ-N вузлів
  • Однакові параметри layout і палітра для всіх рисунків звіту — інакше картинки непорівнювані.

Слайд 46 — ЯК ЧИТАТИ КАРТИНКУ

Elliptic++, крок 32: 4 525 tx, 4 952 ребра; illicit 342 — червоні, licit 981 — сині, unknown 3 202 — сірі (ForceAtlas2).

  1. Щільне ядро → спільнота
  2. Зірка → хаб: fan-in / fan-out
  3. Довга нитка → ланцюг, peel chain
  4. Вузол між групами → міст, посередник
  5. Острів → окрема компонента

Слайд 47 — GEPHI: ФІЛЬТРИ ДЛЯ РОЗСЛІДУВАННЯ

Фільтри комбінуються (перетягніть один в інший). Фільтр — частина методології: записуйте, що відкинуто.

На слайді огляд Ego Network, Giant Component, Degree Range, K-core, Partition, Range, Time Interval.


Слайд 48 — ВІЗУАЛІЗАЦІЯ ЯК ДОКАЗ

  • 1 Легенда кольорів і розмірів
  • 2 Джерело даних, дата, версія датасету
  • 3 Що відфільтровано і чому
  • 4 Параметри layout і збережений проєкт .gephi
  • 5 Мітки — це дані датасету, а не встановлені факти
  • 6 Експорт з Preview у SVG або PDF (вектор)
  • Картинка має бути відтворюваною: інший аналітик з тими самими даними отримує ту саму структуру.

Слайд 49 — ОБМЕЖЕННЯ ТА ЕТИКА

Дані

  • Мітки з форуму можуть бути хибними або дезінформацією
  • Лише англомовний розділ BitcoinTalk
  • LLM помиляється на короткому контексті
  • unknown ≠ licit

Методи

  • Евристики дають хибні злиття (CoinJoin, кастодіальні сервіси)
  • Супервузли бірж спотворюють спільноти
  • Дрейф у часі та дисбаланс класів

Висновки та люди

  • Близькість ≠ причетність
  • «Узгоджується з…» + рівень впевненості
  • Individual-мітки з профілів — персональні дані: мінімізуйте, не публікуйте
  • Для кожного патерну — альтернативне пояснення. Детальніше про упередження — на окремому занятті курсу.

Слайд 50 — ПРАКТИКА ПІСЛЯ ЗАНЯТТЯ

    1. Розгорнути
  • Neo4j + GDS + APOC, імпорт Elliptic++ скриптами заняття; знімок підрахунку вузлів і ребер
    1. Патерни
  • По одному прикладу fan-in, peel chain і експозиції: запит, результат, легітимне пояснення
    1. Спільноти
  • Louvain і Leiden, таблиця «чистоти»; обрати спільноту з часткою illicit ≥ 10% і порівняти з базовою
    1. Gephi
  • Візуалізація обраної спільноти: ForceAtlas2, колір = клас, розмір = PageRank, легенда
    1. Висновок
  • 1 сторінка: 3 ліди, рівні впевненості, альтернативи, обмеження даних
  • Здати: файли .cypher, проєкт .gephi, рисунок SVG/PNG і PDF з висновком.

Слайд 51 — ВИСНОВКИ

  • Патерни: fan-in, fan-out, peel chain, цикли, CoinJoin, мости — Cypher-шаблони з порогами, які треба обґрунтувати
  • Кластеризація: розрізняйте кластери адрес, спільноти (Louvain, Leiden) і поведінкові кластери (k-means)
  • Тренди: час — окремий вимір; нормалізуйте за віком і курсом, перевіряйте дрейф після подій
  • Інструменти: PostgreSQL — сховище, Neo4j — запити й алгоритми, Gephi — дослідження та візуальний аргумент
  • OSINT: мітки — це офчейн-розвідка; будь-яке джерело, навіть наукову статтю, перевіряйте

Матеріали для студентів

Сирі CSV Elliptic++ (~1,5 ГБ) завантажуйте з GitHub Elliptic++ (посилання на Google Drive — у README репозиторію).

Домашнє завдання

Індивідуальна робота на Elliptic++ (~90 хв): пройти ланцюжок із заняття (тренд → патерни → спільноти → Gephi) і сформулювати один обґрунтований лід — адресу з двома незалежними сигналами, альтернативним поясненням і рівнем упевненості.

Повний бриф, підготовка Neo4j, запити, критерії та самоперевірка: ДЗ-13: Патерни, тренди та кластеризація.