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-сервер