Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Bài 4 — Index: HNSW, IVF và các tham số cần chỉnh

Đã đọc 0/8 mục · còn khoảng 3 phút
Cách chúng tôi ghi nhận

Một mục được tính là đã đọc khi nó nằm giữa màn hình đủ lâu so với độ dài của nó (bảng và sơ đồ tính một nửa), bạn vẫn đang cuộn hoặc bấm phím, và bạn đã thấy tới cuối mục. Lướt nhanh hoặc để tab mở không được tính. Chúng tôi chỉ lưu thời gian đọc từng mục — không lưu gì khác.

Tìm chính xác vector gần nhất trong 10 triệu vector bằng cách so từng cái (brute-force) là bất khả thi về tốc độ. Vì vậy vector DB dùng ANN index (Approximate Nearest Neighbor) — chấp nhận gần đúng để nhanh gấp hàng trăm lần. Bài này giúp bạn chỉnh index đúng thay vì để mặc định rồi than chậm.

Hai họ index phổ biến

HNSW (Hierarchical Navigable Small World) — đồ thị nhiều tầng, đi từ node này sang node gần hơn.

  • Tham số build: m (số kết nối mỗi node), ef_construction (nỗ lực khi xây).
  • Tham số query: ef_search (số ứng viên xét khi tìm). ef_search cao → chính xác hơn nhưng chậm hơn.
  • Ưu: recall cao, truy vấn nhanh. Nhược: tốn RAM, build lâu.
IVF (Inverted File) — chia không gian thành nlist cụm; query chỉ xét nprobe cụm gần nhất.
  • nprobe cao → chính xác hơn, chậm hơn.
  • Ưu: nhẹ RAM hơn. Nhược: cần "train" trên dữ liệu mẫu; recall phụ thuộc phân bố dữ liệu.
Mặc định thực dụng: HNSW cho hầu hết agent memory (dữ liệu vừa, cần recall tốt).

Khái niệm sống còn: Recall vs Latency

Bạn luôn đánh đổi:

  • Recall = tỉ lệ tìm đúng các vector thực sự gần nhất.
  • Latency = độ trễ truy vấn.
Tăng ef_search/nprobe → recall lên, latency lên. Việc của bạn là tìm điểm cân bằng cho use case.

Ví dụ cấu hình

-- pgvector HNSW
CREATE INDEX ON mem USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);
-- lúc query, chỉnh ef_search:
SET hnsw.ef_search = 100;
# Qdrant: chỉnh hnsw_config khi tạo collection
c.create_collection(
  "mem",
  vectors_config={"size":1536,"distance":"Cosine"},
  hnsw_config={"m":16,"ef_construct":100}
)
# query với ef cao hơn cho câu quan trọng
c.search("mem", query_vector=q, limit=5, search_params={"hnsw_ef":128})

Quy trình tuning (các bước)

  • Tạo tập đánh giá: ~50–100 câu hỏi kèm đáp án đúng (mẩu nào cần trả về).
  • Đo baseline: chạy với tham số mặc định, ghi recall@k và latency.
  • Quét ef_search/nprobe: thử 50, 100, 200... vẽ đường recall–latency.
  • Chọn điểm gối (knee): nơi tăng thêm latency không còn cải thiện recall đáng kể.
  • Cố định và ghi lại tham số vào tài liệu vận hành.

Khung tư duy

  • Kết quả thiếu mẩu đúng (recall thấp)? → tăng ef_search/nprobe, hoặc m/ef_construction khi build lại.
  • Truy vấn chậm? → giảm ef_search, bật quantization (nén vector), hoặc thêm shard.
  • RAM cạn? → cân nhắc IVF hoặc scalar/product quantization để nén.

Quantization (nén) — mẹo tiết kiệm

Vector float32 rất nặng. Scalar quantization (về int8) hoặc product quantization giảm RAM 4–8 lần, đổi lại recall giảm nhẹ. Với agent memory hàng triệu vector, đây là cứu cánh chi phí.

Sai lầm thường gặp

  • Để mặc định rồi kết luận "công cụ chậm" — 90% vấn đề latency/recall giải quyết được bằng tuning ef_search/nprobe.
  • Không có tập đánh giá: tuning mù, chỉnh theo cảm giác. Luôn đo recall trên bộ câu hỏi thật.
  • Đánh đồng "index build xong" với "sẵn sàng": với IVF phải train; với dữ liệu tăng liên tục cần rebuild/optimize định kỳ.
  • Bật quantization mà không đo lại recall — có thể tụt chất lượng ngầm.
  • Quên index cho metadata filter: filter user_id không có index phụ sẽ quét toàn bảng, phá tan hiệu năng (xem bài 6).
Bài sau: bắt tay dựng bộ nhớ thật với pgvector — từ CREATE TABLE tới truy vấn.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi