Graph 5 — Thuật toán đồ thị (GDS)

13 thg 7, 2026 2 lượt xem
#sql
#graph-database
#neo4j
#gds
#graph-algorithms

Graph 5 — Thuật toán đồ thị (GDS)

Cypher nâng cao cho ta đi nhiều bước (multi-hop), truy vết đường đi, tìm vòng bằng truy vấn. Nhưng có một lớp câu hỏi mà Cypher thuần trả lời rất vụng: "Trong toàn mạng 20 triệu tài khoản, ai là node cầu nối quan trọng nhất? Có bao nhiêu cụm tài khoản dính chặt vào nhau? Tài khoản này giống tài khoản kia đến mức nào về hành vi?" Đây là câu hỏi về cấu trúc toàn cục của mạng — cần thuật toán chuyên biệt, chạy trên toàn đồ thị, tối ưu song song. Đó là địa hạt của Neo4j Graph Data Science (GDS).

GDS là thư viện (plugin) chính thức của Neo4j, cung cấp ~65 thuật toán đồ thị đã cài đặt tối ưu, gọi qua các thủ tục gds.* trong Cypher. Nó nối lý thuyết đồ thị (mà bài thuật toán đồ thị nền tảng trình bày — BFS, DFS, Dijkstra, connected components…) với thực chiến quy mô lớn. Bài này không dạy lại lý thuyết; nó cho bạn bản đồ dùng thuật toán nào cho câu hỏi nghiệp vụ nào.

Lưu ý: mọi block cypher trong bài là cú pháp Neo4j/GDS, không phải SQL Postgreskhông chạy được trên sandbox Knowledge Base. Chỉ block đánh dấu -- ▶ Chạy được (SQL thuần) mới thực thi. GDS còn cần cài plugin riêng ngoài Neo4j core.

Tư tưởng lớn: biến cấu trúc mạng thành insight và feature

Trước khi vào từng thuật toán, cần nắm ý niệm bao trùm. Trong bảng SQL truyền thống, một khách hàng là một dòng với các cột: tuổi, thu nhập, số dư. Nhưng có những đặc trưng không nằm trong bất kỳ cột nào — chúng nằm ở vị trí của khách trong mạng lưới: người này có phải điểm nghẽn dòng tiền không? Có thuộc một cụm tài khoản đáng ngờ không? Có "giống" một khách đã bị gắn cờ gian lận không?

Thuật toán đồ thị trích xuất những đặc trưng cấu trúc đó thành con số. Kết quả (điểm centrality, ID cộng đồng, vector embedding) có thể: (1) ghi ngược vào node làm property để truy vấn/xếp hạng, hoặc (2) xuất ra làm feature cho mô hình ML downstream (chấm điểm rủi ro, phát hiện gian lận). Đây là giá trị cốt lõi: network structure → number → insight/feature. Giữ ý này trong đầu khi đọc từng nhóm thuật toán bên dưới.

Graph projection — chiếu đồ thị vào bộ nhớ

GDS không chạy trực tiếp trên database lưu trên đĩa. Thay vào đó, nó làm việc trên in-memory graph projection: một bản chiếu đồ thị con — chỉ những label node và type relationship bạn cần — nạp vào một cấu trúc nén, tối ưu cho tính toán song song trong RAM. Lý do:

  • Tốc độ: thuật toán như PageRank lặp hàng chục vòng trên toàn đồ thị; đọc đĩa mỗi vòng thì quá chậm. Bản chiếu trong RAM cho phép quét cực nhanh.
  • Chọn lọc: đa số thuật toán chỉ cần một phần đồ thị. Truy vết dòng tiền chỉ cần node Account và cạnh SENT — không cần kéo cả Customer, Device, Address.
  • Tách biệt: tính toán nặng không đụng vào workload giao dịch (OLTP) đang chạy trên database gốc.

Quy trình GDS gần như luôn theo ba nhịp: project → run → write/stream.

// Bước 1: chiếu đồ thị con "money_flow" vào bộ nhớ
CALL gds.graph.project(
  'money_flow',
  'Account',                                  // node label cần chiếu
  { SENT: { properties: 'amount' } }          // relationship type + property trọng số
)

Sau khi có bản chiếu tên money_flow, mọi thuật toán chạy trên nó bằng cách tham chiếu tên đó. Ba execution mode cần phân biệt rõ:

ModeÝ nghĩaKhi dùng
streamTrả kết quả về như dòng dữ liệu, không ghi lạiKhám phá, thử nghiệm, đẩy ra ứng dụng ngoài
writeGhi kết quả ngược vào node/cạnh làm propertyCần lưu điểm để truy vấn/xếp hạng lâu dài
mutateGhi vào bản chiếu trong RAM (không đụng DB)Nối nhiều thuật toán thành pipeline
statsChỉ trả thống kê tổng hợpƯớc lượng nhanh phân bố kết quả

Ngoài ra có estimate để ước tính bộ nhớ cần trước khi chạy — bắt buộc với đồ thị lớn để tránh tràn RAM. Nhớ: bản chiếu là ảnh chụp tại thời điểm project; dữ liệu mới trong DB không tự phản ánh vào — phải chiếu lại. Khi xong việc, gds.graph.drop('money_flow') để giải phóng RAM.

Nhóm 1 — Centrality: đo độ quan trọng của node

Centrality trả lời: "node nào quan trọng?" — nhưng "quan trọng" có nhiều nghĩa khác nhau, mỗi thuật toán đo một kiểu.

Degree Centrality — đơn giản nhất: đếm số cạnh của node. Trong mạng giao dịch, degree cao (đặc biệt in-degree — số tài khoản gửi tiền tới) có thể là tài khoản gom tiền (funnel account) đáng ngờ. Cypher thuần đếm được, nhưng GDS chuẩn hoá và chạy nhanh trên toàn mạng.

PageRank — thuật toán làm nên Google, đo ảnh hưởng: một node quan trọng nếu được nhiều node quan trọng khác trỏ tới. Không chỉ đếm số cạnh, mà tính cả chất lượng nguồn. Ứng dụng ngân hàng: xếp hạng tài khoản theo mức độ trung tâm trong dòng tiền — tài khoản nhận tiền từ nhiều tài khoản-có-ảnh-hưởng-cao sẽ nổi lên, dù degree không lớn.

// PageRank trên mạng dòng tiền, ghi điểm ngược vào node
CALL gds.pageRank.write('money_flow', {
  relationshipWeightProperty: 'amount',   // gửi tiền lớn có trọng số cao hơn
  writeProperty: 'pagerankScore'
})
YIELD nodePropertiesWritten, ranIterations

Betweenness Centrality — đo mức độ node nằm trên đường đi ngắn nhất giữa các node khác: tần suất một node đóng vai cầu nối. Đây là thuật toán vàng cho AML: kẻ trung gian rửa tiền thường là node betweenness cao — tiền bẩn từ nguồn phải đi qua nó để tới đích, nó là điểm nghẽn của nhiều luồng. Một tài khoản betweenness cao bất thường mà degree lại thấp là dấu hiệu điển hình của layering (phân tầng dòng tiền). Lưu ý: betweenness đầy đủ rất tốn tính toán O(V·E), nên với đồ thị lớn GDS có biến thể lấy mẫu (gds.betweenness với tham số samplingSize).

Closeness Centrality — đo node gần trung tâm mạng tới đâu: tổng khoảng cách ngắn nhất tới mọi node khác nhỏ thì closeness cao. Ứng dụng: tài khoản có thể tiếp cận phần lớn mạng qua ít bước — hữu ích để tìm điểm khuếch tán (một tin nhắn lừa đảo, một luồng tiền có thể lan nhanh từ đâu).

Thuật toán"Quan trọng" nghĩa làTín hiệu ngân hàng
DegreeNhiều kết nối trực tiếpTài khoản gom/phát tiền, hub
PageRankĐược node quan trọng trỏ tớiTrung tâm ảnh hưởng dòng tiền
BetweennessNằm trên nhiều đường điTrung gian rửa tiền, layering
ClosenessGần mọi node khácĐiểm khuếch tán nhanh

Nhóm 2 — Community Detection: phát hiện cụm

Community detection tìm các nhóm node dính chặt vào nhau hơn hẳn phần còn lại của mạng. Trong ngân hàng, một cụm tài khoản liên kết dày đặc mà không có lý do nghiệp vụ rõ ràng thường là dấu hiệu vòng gian lận (fraud ring) — các tài khoản do cùng một nhóm điều khiển, luân chuyển tiền cho nhau.

Weakly / Strongly Connected Components (WCC/SCC) — nền tảng nhất. WCC gom các node nối được với nhau (bỏ qua hướng cạnh) thành component; SCC yêu cầu nối được theo cả hai chiều theo đúng hướng cạnh. Ứng dụng trực tiếp: một SCC trong mạng SENT nghĩa là tiền chảy vòng tròn — A gửi B, B gửi C, C gửi lại A — dấu hiệu kinh điển của cycling để làm sạch nguồn gốc tiền. WCC dùng để tách đồ thị thành các "hòn đảo" độc lập, hoặc gom các node cùng chỉ về một thực thể (entity resolution: cùng số điện thoại, cùng thiết bị).

Louvain — thuật toán community detection phổ biến nhất, tối ưu độ đo modularity (mật độ cạnh trong cụm so với kỳ vọng ngẫu nhiên). Nó phân cấp (hierarchical) và mở rộng tốt lên hàng triệu node. Ứng dụng: chia toàn mạng khách hàng–thiết bị–tài khoản thành cộng đồng; cộng đồng nào có tỷ lệ tài khoản đã-gắn-cờ cao bất thường là ứng viên fraud ring cần điều tra.

// Louvain: gán mỗi node một communityId, ghi ngược vào node
CALL gds.louvain.write('fraud_net', {
  writeProperty: 'communityId',
  relationshipWeightProperty: 'amount'
})
YIELD communityCount, modularity

Label Propagation (LPA) — nhanh hơn Louvain, gần tuyến tính: mỗi node lặp lại việc "nhận nhãn phổ biến nhất trong hàng xóm" cho tới khi ổn định. Kém ổn định hơn (kết quả có yếu tố ngẫu nhiên) nhưng cực nhanh — hợp để quét sơ bộ đồ thị rất lớn trước khi chạy Louvain kỹ hơn trên phần nghi ngờ.

Sau khi có communityId, câu hỏi nghiệp vụ trở thành truy vấn Cypher thường: "cộng đồng nào có ≥ 3 tài khoản gắn cờ và tổng dòng tiền nội bộ > X". Đây chính là structure → feature → insight: cấu trúc cụm đã được nén thành một con số (communityId) để lọc.

Nhóm 3 — Pathfinding: tìm đường

Pathfinding trả lời câu hỏi "đi từ A tới B như thế nào là tốt nhất" — nền tảng để truy vết dòng tiền.

Dijkstra — đường đi rẻ nhất (trọng số nhỏ nhất) giữa hai node. Trong AML, nếu đặt trọng số cạnh khéo (ví dụ nghịch với số tiền, hoặc theo phí/độ trễ), Dijkstra tìm được đường tiền có khả năng cao nhất nối một tài khoản nguồn đáng ngờ tới một tài khoản đích. GDS có cả gds.shortestPath.dijkstra (một nguồn, một đích) và biến thể một-nguồn-tới-mọi-đích.

A* — như Dijkstra nhưng dùng thêm hàm heuristic (ước lượng khoảng cách còn lại, ví dụ dựa trên toạ độ địa lý chi nhánh) để định hướng tìm kiếm, nhanh hơn khi có thông tin không gian. Yen's k-shortest paths tìm k đường ngắn nhất — hữu ích khi muốn thấy nhiều tuyến luân chuyển tiền thay thế, không chỉ một.

Khác biệt với Cypher: truy vấn multi-hop trong Cypher nâng cao tìm có tồn tại đường đi không; GDS pathfinding tối ưu đường tốt nhất theo trọng số trên đồ thị lớn với thuật toán chuyên dụng — hiệu quả hơn nhiều khi cạnh có trọng số và mạng rộng.

// Đường dòng tiền "rẻ nhất" từ tài khoản nghi vấn tới đích, theo trọng số
MATCH (src:Account {accountNo: $tk_nguon}), (dst:Account {accountNo: $tk_dich})
CALL gds.shortestPath.dijkstra.stream('money_flow', {
  sourceNode: src, targetNode: dst,
  relationshipWeightProperty: 'cost'
})
YIELD path
RETURN path

Nhóm 4 — Similarity: node giống nhau tới đâu

Similarity đo hai node giống nhau đến mức nào dựa trên cấu trúc kết nối — không dựa vào thuộc tính hồ sơ. Trực giác: hai khách hàng giống nhau nếu họ tương tác với cùng một tập thực thể (cùng merchant, cùng thiết bị, cùng đối tác giao dịch).

Jaccard Similarity là độ đo lõi: với hai node, lấy giao tập hàng xóm chia cho hợp tập hàng xóm — tỷ lệ hàng xóm chung. Hai tài khoản cùng gửi tiền tới đúng một nhóm 8 tài khoản đích thì Jaccard rất cao, dù hồ sơ khai báo khác nhau — dấu hiệu chúng có thể do cùng một người điều khiển.

Node Similarity (gds.nodeSimilarity) là thuật toán GDS tính Jaccard (hoặc overlap/cosine) theo hàng xóm chung, trả về các cặp giống nhau nhất kèm điểm. Ứng dụng: gợi ý "khách giống nhau" cho cross-sell; hoặc gom các tài khoản có hành vi giao dịch trùng lặp bất thường trong phòng chống gian lận.

// Tìm các cặp tài khoản có tập đối tác giao dịch giống nhau nhất
CALL gds.nodeSimilarity.stream('money_flow', { topK: 5 })
YIELD node1, node2, similarity
WHERE similarity > 0.8
RETURN gds.util.asNode(node1).accountNo AS tk1,
       gds.util.asNode(node2).accountNo AS tk2,
       similarity
ORDER BY similarity DESC

Ngoài ra K-Nearest Neighbors (KNN) trong GDS tính similarity dựa trên vector property của node (ví dụ vector embedding, xem dưới) thay vì hàng xóm — mở rộng tốt hơn Node Similarity trên đồ thị rất lớn.

Link prediction dự đoán cạnh nào có khả năng xuất hiện trong tương lai (hoặc đang thiếu). Ý tưởng: cặp node đã "gần nhau" về cấu trúc (nhiều hàng xóm chung, cùng cộng đồng) dễ hình thành liên kết. GDS có các hàm đặc trưng như commonNeighbors, adamicAdar, preferentialAttachment — sinh điểm cho từng cặp node làm feature; và ở mức nâng cao là pipeline Link Prediction kết hợp embedding + mô hình phân loại. Ứng dụng: gợi ý sản phẩm (dựa trên láng giềng trong mạng) hay dự báo quan hệ gian lận tiềm ẩn chưa lộ. Ở đây chỉ nêu để bạn biết nó tồn tại và thuộc họ nào.

Nhóm 6 — Graph Embeddings: feature cho ML

Đây là cầu nối mạnh nhất giữa đồ thị và ML truyền thống. Graph embedding biến mỗi node thành một vector số chiều cố định (ví dụ 128 chiều) sao cho node gần nhau trong đồ thị thì vector gần nhau trong không gian số. Toàn bộ vị trí và vai trò cấu trúc của node được nén thành một dãy số — mà mô hình ML (XGBoost, mạng nơ-ron, logistic regression) ăn được trực tiếp.

  • FastRP (Fast Random Projection): rất nhanh, mở rộng tốt lên đồ thị lớn, là lựa chọn mặc định thực tế của GDS cho embedding. Dựa trên phép chiếu ngẫu nhiên lặp qua các hàng xóm.
  • node2vec: dựa trên random walk (bước ngẫu nhiên có điều hướng) rồi học vector kiểu word2vec; nắm cả cấu trúc cục bộ lẫn vai trò. Chậm hơn FastRP nhưng biểu diễn phong phú.
  • GraphSAGE: inductive — học được vector cho node mới chưa thấy khi train, hợp môi trường sản xuất luôn có tài khoản mới.
// FastRP: sinh vector 128 chiều cho mỗi tài khoản, ghi làm property
CALL gds.fastRP.write('money_flow', {
  embeddingDimension: 128,
  relationshipWeightProperty: 'amount',
  writeProperty: 'embedding'
})
YIELD nodePropertiesWritten

Quy trình thực chiến: chạy embedding → xuất vector embedding của mỗi tài khoản ra feature store → nối cùng các feature bảng truyền thống (số dư, tuổi tài khoản, tần suất) → train mô hình phát hiện gian lận. Kết quả thường tốt hơn hẳn chỉ dùng feature bảng, vì embedding mang thông tin mạng lưới mà feature phẳng không có. Đây là điểm giao với MLOps/triển khaichấm điểm tín dụng.

Sơ đồ: centrality và community trên mạng giao dịch

Đọc sơ đồ: cụm A-B-C tạo thành SCC (tiền chảy vòng A→B→C→A) — Louvain gom chúng vào một communityId, cờ đỏ vòng gian lận. Node M nhận tiền từ ba nguồn rồi phân vào cụm và đẩy ra Đích: Mbetweenness cao (mọi luồng tiền từ nguồn tới đích đều qua nó) dù mỗi degree không quá lớn — chân dung điển hình của mule/trung gian rửa tiền. Cùng một mạng, hai thuật toán soi ra hai tín hiệu bổ sung nhau.

Use case thực tế

Bối cảnh. Đội phân tích rủi ro NCB nhận cảnh báo: một số tài khoản mở mới trong tháng có dòng tiền vào-ra bất thường. Cần xác định (a) có tồn tại nhóm tài khoản luân chuyển tiền cho nhau không, và (b) tài khoản nào là trung gian điều phối, để chuyển hồ sơ cho bộ phận AML (xem giám sát giao dịch). Dữ liệu (:Account)-[:SENT {amount, txnAt}]->(:Account) đã có trong Neo4j sau migrate.

Bước 1 — Chiếu đồ thị cửa sổ 90 ngày. Chỉ chiếu các cạnh SENT gần đây và các tài khoản liên quan vào bản chiếu recent_flow, để thuật toán chạy trên đúng phạm vi điều tra thay vì toàn bộ lịch sử.

Bước 2 — Tìm vòng bằng SCC. Chạy gds.scc trên recent_flow, ghi sccId vào node. Lọc các SCC có ≥ 3 thành viên — mỗi SCC là một chu trình dòng tiền. Kết quả: 4 SCC nghi vấn, trong đó một cụm 6 tài khoản luân chuyển ~2,1 tỷ VND vòng tròn trong 3 tuần (số minh hoạ).

Bước 3 — Xác định trung gian bằng Betweenness. Chạy gds.betweenness (mode write) trên cùng bản chiếu. Sắp xếp giảm dần theo betweennessScore: hai tài khoản đứng đầu có điểm cao gấp ~5 lần trung vị dù in-degree khiêm tốn — đúng đặc trưng node cầu nối. Đây là các tài khoản mule ứng viên.

Bước 4 — Xuất feature cho mô hình. Ghi thêm pagerankScore, communityId (Louvain), và vector embedding (FastRP) vào từng tài khoản. Toàn bộ được xuất ra feature store, nối với feature hồ sơ (tuổi tài khoản, KYC) để mô hình phát hiện mule chấm điểm — mạng đóng góp các feature mà dữ liệu phẳng không nắm được.

Kết quả. Từ một cảnh báo mơ hồ, quy trình project → SCC → Betweenness → embedding khoanh vùng được 1 fraud ring 6 tài khoản và 2 trung gian trong vài phút chạy GDS, thay vì rà tay hàng nghìn giao dịch. Cấu trúc mạng đã được biến thành con số hành động được — đúng tinh thần của toàn bộ bài.

Ghi nhớ

  • GDS là thư viện thuật toán đồ thị của Neo4j (~65 thuật toán, gọi qua gds.*) để chạy phân tích cấu trúc toàn cục mà Cypher thuần làm vụng. Nó nối lý thuyết đồ thị với thực chiến quy mô lớn.
  • Graph projection: chiếu đồ thị con (label + type cần dùng) vào RAM để chạy nhanh, tách khỏi OLTP. Quy trình project → run → stream/write/mutate; nhớ drop để giải phóng, và chiếu lại khi dữ liệu đổi.
  • Centrality = độ quan trọng: Degree (kết nối trực tiếp), PageRank (ảnh hưởng), Betweenness (node cầu nối → trung gian rửa tiền, layering), Closeness (gần trung tâm).
  • Community Detection = phát hiện cụm: WCC/SCC (SCC = vòng dòng tiền → cờ đỏ), Louvain (modularity, mở rộng tốt), Label Propagation (nhanh, sơ bộ). Cụm dính chặt bất thường → fraud ring.
  • Pathfinding: Dijkstra/A* tìm đường rẻ nhất theo trọng số → truy vết đường tiền; khác Cypher multi-hop ở chỗ tối ưu đường tốt nhất trên mạng lớn có trọng số.
  • Similarity (Jaccard, Node Similarity, KNN): node giống nhau theo hàng xóm chung → khách giống nhau, tài khoản do cùng người điều khiển. Link Prediction dự đoán cạnh tương lai/thiếu.
  • Graph embeddings (FastRP nhanh, node2vec phong phú, GraphSAGE inductive): nén vị trí mạng của node thành vector làm feature cho ML — mang thông tin mạng mà feature phẳng không có.
  • Tinh thần bao trùm: network structure → number → insight/feature. Dùng thuật toán để biến cấu trúc mạng thành đại lượng hành động được (điểm, ID cộng đồng, vector) ghi ngược vào node hoặc đẩy sang mô hình.

Bài viết liên quan

Cách index hoạt động (B-Tree), đọc EXPLAIN, seq scan vs index scan và mẫu tối ưu truy vấn.

13 thg 7, 2026 4

Khoá, ràng buộc, quan hệ và chuẩn hoá 1NF/2NF/3NF — thiết kế lược đồ đúng từ đầu.

13 thg 7, 2026 4

Kết nhiều bảng đúng cách: các loại JOIN, bẫy thường gặp và phép hợp tập.

13 thg 7, 2026 3

Truy vấn lồng, CTE (WITH), CTE đệ quy và hàm cửa sổ — vũ khí cho phân tích nâng cao.

13 thg 7, 2026 3