GNN на GPU: GraphSAGE/GAT и выборка

Задача страницы. Инженерный гид по построению графовых моделей (GNN) на GPU: GraphSAGE и GAT, мини‑батч‑обучение с neighbor sampling, офлайн расчёт эмбеддингов и онлайн‑сервинг для рекомендаций/поиска/аналитики. Разберём пайплайны single‑/multi‑GPU, кэш‑план (HBM↔UVA↔NVMe), метрики p50/p95, экономику, конфиги и траблшутинг. Смежные темы: производительность и тюнинг, распределёнка, хранилища/ETL, сервинг (ссылки в конце).

TL;DR

  • **Два режима: Interruptible (train/offline‑embed) — обучение/перерасчёт эмбеддингов узлов/рёбер по расписанию. On‑Demand (serving) — быстрая выдача по готовым эмбеддингам (retrieval/ANN), опционально «до‑вычисление» локальным k‑hop.
  • Mini‑batch + sampling: слои GraphSAGE/GAT с fanout на каждый слой (напр. [15, 10]), выборка без/с возвращением, importance/random‑walk; фьюзинг выборки и копирования в GPU, pinned/UVA.
  • GraphSAGE vs GAT: GraphSAGE — базовый для крупных графов (стабилен и лёгок), GAT — лучше локального внимания, но тяжелее по памяти/латентности (число «голов» и размер скрытого слоя — главный рычаг).
  • Кэш/память: горячие фичи/узлы — в HBM; тёплые — pinned host (UVA/zero‑copy); холодные — NVMe mmap. Контролируйте fanout и «cap» для high‑degree узлов.
  • Инференс: слой‑за‑слоем по всему графу (layer‑wise full‑graph) для офлайна, или батч‑инференс по партиям; выгрузка эмбеддингов → ANN‑индекс и ранжирование. См. https://cloudcompute.ru/solutions/recsys/
  • Метрики: step_time, loader_time, edges_per_sec, gpu_util/HBM, dup_ratio (дубли узлов при выборке), качество (ROC‑AUC/NDCG@K), и пиковая VRAM.
  • Тюнинг: AMP (FP16/BF16), pinned/zero‑copy, предвыборка, микробатчинг, фиксированный cap степеней, шардирование по GPU/NCCL. См. https://cloudcompute.ru/solutions/performance-tuning/, https://cloudcompute.ru/solutions/fp8-bf16/

Сценарии (когда это нужно)

  • Рекомендательные системы: user‑item граф, link prediction для кандидатов, офлайн эмбеддинги узлов → ANN retrieval → ранкер DLRM. См. https://cloudcompute.ru/solutions/recsys/
  • Антифрод/транзакции: граф платежей/сессий; классификация узлов/рёбер, детект аномалий по окрестности.
  • Социальные/контентные графы: друзья‑друзей, co‑view/co‑buy; персональная навигация/поиск.
  • Знания/каталоги: enrichment каталога связями (brand↔category↔item), маршрутизация контента по близости.

Архитектуры и пайплайны 1) Обучение (mini‑batch GraphSAGE/GAT с neighbor sampling)

Parquet (nodes, edges, features) on NVMe/Object
 └─> Graph Store (CSR/CSC, degree caps, feature mmap)
 └─> Neighbor Sampler (fanout per layer, UVA/pinned)
 └─> GPU: GNN Forward (L layers, AMP)
 └─> Loss (node/edge classification or link prediction)
 └─> Backward + Optimizer
 └─> Checkpoint + Metrics

Особенности: fanout=[f1,f2,...], cap high‑degree узлов, предвыборка «горячих» фичей в HBM‑кэш, перекрытие sampler↔forward (prefetch, 2 очереди).

2) Офлайн‑эмбеддинги → ANN для сервинга

Trained GNN ──> Layer-wise Inference over full graph
 └─> Node Embeddings (FP16/FP32)
 └─> Write Parquet (NVMe)
 └─> ANN Index Build (GPU)
 └─> Serving: Retrieval → Ranker

Особенности: батч‑инференс без выборки, слой‑за‑слоем; результат — эмбеддинги на узел/тип узла.

3) Онлайн‑сценарий (добавление новых узлов/рёбер)

Stream/Kafka ─> Micro-batch ETL ─> Update Graph Store ─┬─> On-demand local k-hop encode (fast path)
 └─> Nightly full refresh embeddings (slow path)

Баланс fast/slow‑path: холодный старт действует через локальную окрестность с ограниченным fanout и меньшим числом слоёв.

Профили GPU и ориентиры

Инженерные ориентиры для обучения GraphSAGE/GAT при U≈0.7, L=2–3, fanout≈[15,10], dim=128–256, AMP включён. Фактическая скорость зависит от распределения степеней, коллизий и I/O.

**Профиль GPU** **Память** **Типичный стек** **Обучение (edges/sec)\*** **Инференс (nodes/sec)\*\*** **Комментарии**
24 ГБ (Compact) 24 ГБ GraphSAGE 2‑слоя, dim=128 2–6 млн 0.5–1.5 млн Базовые графы, аккуратный fanout/cap.
48 ГБ (Balanced) 48 ГБ GraphSAGE/GAT (4‑8 голов), dim=256 6–12 млн 1.5–3 млн Баланс качества/скорости.
80 ГБ (HQ) 80 ГБ GAT большой/высокий fanout, dim=256–384 10–20+ млн 3–6 млн Тяжёлые графы, аггр. фичи, многослойные сети.

* «edges/sec» — посещённые рёбра за шаги (с учётом выборки). ** «nodes/sec» — офлайн‑инференс узлов (layer‑wise). Тюнинг и распределёнка: https://cloudcompute.ru/solutions/multi-gpu/, https://cloudcompute.ru/solutions/performance-tuning/

Конфиги и скелеты кода

Docker Compose (train + offline‑infer + ANN‑build)

version: "3.9"
x-env: &env
 DATA_DIR: /data/graph
 CHECKPOINT_DIR: /checkpoints
 PRECISION: "fp16" # bf16|fp16|fp32
 MODEL: "graphsage" # graphsage|gat
 LAYERS: "2"
 HIDDEN_DIM: "256"
 FANOUT: "15,10"
 BATCH_SIZE: "2048"
 NEGATIVE_SAMPLING: "uniform" # для link prediction: uniform|hard
 UVA_ENABLE: "true"
 PINNED_POOL_MB: "4096"
services:
 gnn-train:
 image: cloudcompute/gnn-train:latest
 environment: *env
 deploy:
 resources:
 reservations:
 devices: [{ capabilities: ["gpu"] }]
 volumes:
 - /nvme/graph:/data/graph
 - /nvme/ckpt:/checkpoints
 command: ["python","train.py","--data","/data/graph","--ckpt","/checkpoints"]
 gnn-infer:
 image: cloudcompute/gnn-infer:latest
 environment: *env
 deploy:
 resources:
 reservations:
 devices: [{ capabilities: ["gpu"] }]
 volumes:
 - /nvme/graph:/data/graph
 - /nvme/emb:/emb
 - /nvme/ckpt:/checkpoints
 command: ["python","infer_full_graph.py","--data","/data/graph","--out","/emb"]
 ann-build:
 image: cloudcompute/vector-index:latest
 volumes:
 - /nvme/emb:/emb
 - /nvme/index:/index
 command: ["python","build_index.py","--emb","/emb","--out","/index/main"]

K8s (онлайн‑сервинг эмбеддингов + ANN)

apiVersion: apps/v1
kind: Deployment
metadata: { name: gnn-serving }
spec:
 replicas: 3
 selector: { matchLabels: { app: gnn-serving } }
 template:
 metadata: { labels: { app: gnn-serving } }
 spec:
 containers:
 - name: serving
 image: cloudcompute/gnn-serving:latest
 ports: [{ containerPort: 9100 }]
 env:
 - { name: PRECISION, value: "fp16" }
 - { name: ANN_INDEX_PATH, value: "/index/main" }
 volumeMounts:
 - { name: index, mountPath: /index }
 - { name: emb, mountPath: /emb }
 resources:
 limits: { nvidia.com/gpu: 1, cpu: "4", memory: "24Gi" }
 volumes:
 - name: index ; hostPath: { path: /nvme/index }
 - name: emb ; hostPath: { path: /nvme/emb }

Конфиг пайплайна (YAML)

graph:
 nodes: "/data/graph/nodes.parquet" # id, type, features[*]
 edges: "/data/graph/edges.parquet" # src, dst, type, ts
 features:
 dim: 256
 dtype: "fp16"
 sampling:
 fanout: [15, 10]
 with_replacement: false
 degree_cap: 50
 prefetch_batches: 2
 uva: true
train:
 task: "link_prediction" # node_classification|link_prediction
 batch_size: 2048
 epochs: 5
 lr: 0.001
 negative_sampling: "uniform"
 fp_precision: "fp16"
model:
 type: "graphsage" # graphsage|gat
 layers: 2
 hidden_dim: 256
 heads: 4 # для GAT
 dropout: 0.1
infer:
 layerwise_full_graph: true
 shard: 8
 output_embeddings_path: "/emb"
serving:
 ann_topk: 200
 min_score: 0.05

Python (скелет): GraphSAGE/GAT + neighbor sampling (PyTorch)

import torch, torch.nn as nn, torch.nn.functional as F
from torch.cuda.amp import autocast, GradScaler
# Предполагаем наличие loader, который выдаёт батчи k-hop подграфов:
# for seeds, blocks, labels in loader: blocks = [ (x, edge_index), ... ]
class SAGE(nn.Module):
 def __init__(self, in_dim, hidden, out_dim, layers=2):
 super().__init__()
 self.lins = nn.ModuleList()
 dims = [in_dim] + [hidden]*(layers-1) + [out_dim]
 for a, b in zip(dims[:-1], dims[1:]):
 self.lins.append(nn.Linear(a, b))
 def forward(self, x, blocks):
 h = x
 for i, (x_src, edge_index) in enumerate(blocks):
 h_dst = h[:x_src.shape[0]] # convention: dst first
 h = self.lins[i](h)
 h = F.relu(h)
 # mean aggregation (псевдокод)
 # h = aggregate_mean(h, edge_index)
 return h
class GAT(nn.Module):
 def __init__(self, in_dim, hidden, out_dim, layers=2, heads=4):
 super().__init__()
 self.layers = nn.ModuleList()
 for i in range(layers):
 h_in = in_dim if i==0 else hidden*heads
 h_out = out_dim if i==layers-1 else hidden
 self.layers.append(nn.MultiheadAttention(embed_dim=h_out*heads, num_heads=heads, batch_first=True))
 def forward(self, x, blocks):
 h = x
 for attn, (x_src, edge_index) in zip(self.layers, blocks):
 # attention over neighborhood (псевдокод: соберите соседей по edge_index)
 h, _ = attn(h, h, h, need_weights=False)
 h = F.elu(h)
 return h
def train(model, loader, feats, optimizer, epochs=5, device="cuda"):
 scaler = GradScaler()
 model.to(device).train()
 for ep in range(epochs):
 for seeds, blocks, labels in loader:
 x = gather_features(feats, blocks).to(device, non_blocking=True) # UVA/pinned
 labels = labels.to(device, non_blocking=True)
 optimizer.zero_grad(set_to_none=True)
 with autocast(enabled=True):
 out = model(x, blocks)
 loss = F.binary_cross_entropy_with_logits(out, labels.float()) # для link prediction
 scaler.scale(loss).backward()
 scaler.step(optimizer); scaler.update()

Инференс слой‑за‑слоем (псевдокод)

def full_graph_inference(model, graph, feats, batch_nodes):
 model.eval()
 # для каждого слоя вычисляем представления узлов батчами, переиспользуя k-hop соседей
 H = feats
 for l in range(model_layers):
 for seeds in iter_batches(all_nodes, batch_nodes):
 neigh = neighbors_of(seeds, graph) # без выборки — все соседи слоя
 x = gather_feats(H, seeds, neigh)
 H[seeds] = model_layer_forward(l, x, neigh)
 return H
``` ## **Наблюдаемость/метрики/алерты**

**Perf/Latency:**

- gnn\_step\_time\_seconds (p50/p95), gnn\_loader\_time\_seconds, gnn\_forward\_time\_seconds, gnn\_backward\_time\_seconds.
- gnn\_edges\_per\_sec, gnn\_nodes\_per\_batch, gnn\_dup\_ratio (доля дублей узлов в батче).
- gpu\_utilization, gpu\_memory\_bytes, gpu\_mem\_peak\_bytes, pinned\_host\_bytes, nvme\_{read,write}\_mb\_s.
- sampler\_queue\_depth, prefetch\_ahead\_batches.

**Quality (задача):**

- Классификация узлов/рёбер: roc\_auc, f1, pr\_auc.
- Link‑prediction: roc\_auc, hits@K, ndcg@K, mrr@K.

**Алерты (примеры):**

- gnn\_loader\_time / gnn\_step\_time > 0.4 — CPU/I/O узкое место: увеличить prefetch, включить UVA, поднять workers и pinned‑pool.
- gpu\_mem\_peak/HBM > 0.9 — уменьшить fanout, degree\_cap, batch\_size, перейти на FP16/BF16.
- dup\_ratio > 0.3 — много дублей узлов: снижайте fanout, применяйте sampling без возвращения/importance.
- edges\_per\_sec ↓ при стабильном loader — перегрузка модели (особенно GAT): уменьшить heads/hidden, сократить слои.

Дашборды и логирование: <https://cloudcompute.ru/solutions/monitoring-logging/>, <https://cloudcompute.ru/solutions/llm-inference/observability/>

## **Экономика и формулы**

Обозначения: f\_l — fanout слоя l, B — размер батча (узлов‑семян), L — число слоёв, Eps — edges/sec, N — число узлов в графе, U — целевая загрузка GPU, c\_gpu — цена/час.

- **Сколько узлов/рёбер в батче (оценка):
 Nodes\_per\_batch ≈ B × (1 + f₁ + f₁·f₂ + ... + ∏\_{i=1}^L f\_i)
 Edges\_per\_batch ≈ B × (f₁ + f₁·f₂ + ... + ∏\_{i=1}^L f\_i)
- **Память под фичи в батче:
 Mem\_feats ≈ Nodes\_per\_batch × dim × bytes\_per\_comp (добавьте промежуточные тензоры и граф‑структуры).
- **Время эпохи:
 T\_epoch ≈ (Total\_edges\_visited / Eps); где Total\_edges\_visited ≈ (|Seeds|/B) × Edges\_per\_batch.
- **Стоимость эпохи:
 Cost\_epoch ≈ c\_gpu × T\_epoch / U.
- **Офлайн инференс:
 T\_infer ≈ N / nodes\_per\_sec, Cost\_infer ≈ c\_gpu × T\_infer / U.

Планирование бюджета и компромиссов — см. <https://cloudcompute.ru/solutions/cost-planner/>, <https://cloudcompute.ru/solutions/throughput-vs-latency/>

**Безопасность/политики**

- **PII/идентификаторы:** храните хэш‑ID; маскируйте чувствительные атрибуты; разграничивайте доступ к узлам/рёбрам по тенантам.
- **Ретеншн:** TTL для временных подграфов/эмбеддингов/чекпоинтов; аудит выгрузок.
- **Изоляция:** пулы GPU для тренинга (Interruptible) и сервинга (On‑Demand); отдельные бакеты для графа и эмбеддингов.
- **Экспорт моделей/индексов:** фиксируйте версии и схемы, журналируйте операции экспорта.

Подробнее: <https://cloudcompute.ru/solutions/security/>, <https://cloudcompute.ru/solutions/storage-data/>

**Траблшутинг**

<table><tbody><tr><td>**Симптом**

</td><td>**Возможная причина**

</td><td>**Решение**

</td></tr><tr><td>CUDA OOM при обучении

</td><td>Завышенный fanout/batch\_size, high‑degree узлы

</td><td>Уменьшить fanout/батч, ограничить degree cap, включить AMP, вынести «холодные» фичи в pinned‑host

</td></tr><tr><td>GPU простаивает

</td><td>Медленный sampler/I/O

</td><td>Увеличить prefetch/num\_workers, UVA/zero‑copy, хранить CSR/фичи на NVMe, прогрев кэша

</td></tr><tr><td>Взрыв памяти в GAT

</td><td>Много «голов» и большой hidden

</td><td>Снизить heads/hidden, уменьшить слои, перейти на GraphSAGE

</td></tr><tr><td>Качество не растёт

</td><td>Пересемплирование лёгких соседей

</td><td>Importance‑sampling, баланс классов/негативов, регуляризация/Dropout

</td></tr><tr><td>Переносимая инстанс‑утечка

</td><td>Дубликаты и пересечение батчей

</td><td>Dedup узлов в батче, контроль dup\_ratio, sampling без возвращения

</td></tr><tr><td>Плохая онлайн‑скорость

</td><td>Тяжёлый GAT на сервинге

</td><td>Предрассчитывать эмбеддинги офлайн; онлайн — локальный k‑hop GraphSAGE

</td></tr><tr><td>Дрифт эмбеддингов

</td><td>Изменился граф/каталог

</td><td>Регулярный перерасчёт, канареечный индекс, мониторинг метрик качества

</td></tr><tr><td>Нестабильная утилизация

</td><td>Конкуренция задач на GPU

</td><td>Ограничить конкурентность, разнести тренинг/инференс по пулам GPU

</td></tr></tbody></table>

См. также: <https://cloudcompute.ru/solutions/interruptible-patterns/>, <https://cloudcompute.ru/solutions/performance-tuning/>

**Как запустить в cloudcompute.ru**

1. Откройте **Шаблоны запусков**: <https://cloudcompute.ru/solutions/templates/> и выберите **GNN (Train + Infer)** или **GNN Serving + ANN**.
2. Выберите профиль GPU: **24/48/80 ГБ** по размеру графа, dim, типу модели (GraphSAGE/GAT) и SLA.
3. Подключите диски: /nvme/graph (узлы/рёбра/фичи), /nvme/ckpt, /nvme/emb, /nvme/index.
4. Задайте параметры пайплайна по config.yaml (fanout, degree cap, batch, precision, задача).
5. Для продакшна: пулы **Interruptible** (обучение/офлайн‑эмбеды) и **On‑Demand** (retrieval), автоскейл по U/queue, дашборды и алерты.

Дополнительно:
[ https://cloudcompute.ru/solutions/triton-inference-server/](https://cloudcompute.ru/solutions/triton-inference-server/) — сервинг моделей/эмбеддингов.
[ https://cloudcompute.ru/solutions/recsys/](https://cloudcompute.ru/solutions/recsys/) — retrieval→ranking стек.
[ https://cloudcompute.ru/solutions/rapids/](https://cloudcompute.ru/solutions/rapids/) и <https://cloudcompute.ru/solutions/spark-rapids/> — GPU‑ETL перед графом.
[ https://cloudcompute.ru/solutions/multi-gpu/](https://cloudcompute.ru/solutions/multi-gpu/) — распределёнка и NCCL.
[ https://cloudcompute.ru/solutions/performance-tuning/](https://cloudcompute.ru/solutions/performance-tuning/) — низкоуровневый тюнинг.
[ https://cloudcompute.ru/solutions/cost-planner/](https://cloudcompute.ru/solutions/cost-planner/) — планирование бюджета.

**Чек‑лист перед продом**

- Достигнуты целевые edges\_per\_sec/nodes\_per\_sec и step\_time p95.
- Подобраны fanout/degree\_cap/batch\_size под HBM и стабильный p95.
- AMP (FP16/BF16) включён; UVA/pinned‑pool настроены; кэш горячих узлов/фичей реализован.
- Отработан офлайн‑инференс и сборка ANN‑индекса; версионирование эмбеддингов.
- Дашборды/алерты: loader\_time, edges\_per\_sec, dup\_ratio, HBM/pinned/NVMe, качество (ROC‑AUC/Hits@K).
- Политики PII/ретеншна/экспорта внедрены; разнесены пулы On‑Demand/Interruptible.
- Канареечный релиз индекса/модели и план отката готовы; нагрузочный прогон ≥ 30 мин.

**Навигация**

- Хаб «Решения»: <https://cloudcompute.ru/solutions/>
- Рекомендательные системы: <https://cloudcompute.ru/solutions/recsys/>
- RAPIDS (cuDF/cuML): <https://cloudcompute.ru/solutions/rapids/>
- Spark RAPIDS: <https://cloudcompute.ru/solutions/spark-rapids/>
- Хранилища и данные: <https://cloudcompute.ru/solutions/storage-data/>
- Производительность и тюнинг: <https://cloudcompute.ru/solutions/performance-tuning/>
- Throughput vs Latency: <https://cloudcompute.ru/solutions/throughput-vs-latency/>
- Multi‑GPU: <https://cloudcompute.ru/solutions/multi-gpu/>
- Планирование стоимости: <https://cloudcompute.ru/solutions/cost-planner/>
- Мониторинг и логи: <https://cloudcompute.ru/solutions/monitoring-logging/>
- Наблюдаемость пайплайнов: <https://cloudcompute.ru/solutions/llm-inference/observability/>
- Interruptible‑паттерны: <https://cloudcompute.ru/solutions/interruptible-patterns/>
- Triton Inference Server: <https://cloudcompute.ru/solutions/triton-inference-server/>
- CI/CD контейнеров: <https://cloudcompute.ru/solutions/containers-ci-cd/>

Готовы запустить?

Запустить GPU-сервер