news 2026/6/9 2:12:20

HNSW:分层可导航小世界图

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HNSW:分层可导航小世界图

NSW(Navigable Small World)基础

小世界网络理论

小世界网络两个特性:

  1. 聚类性:邻居之间互相连接,形成局部团

  2. 短路径:任意两点之间有较短的路径

普通图:A → B → C → D → E(5步) 小世界图:A → B → E(2步)

NSW 的构建

import random ​ class NSWNode: def __init__(self, id, vector): self.id = id self.vector = vector self.neighbors = [] # 邻居列表 ​ class NSW: def __init__(self, max_neighbors=6): self.nodes = {} self.max_neighbors = max_neighbors ​ def insert(self, id, vector): """插入新节点""" node = NSWNode(id, vector) self.nodes[id] = node ​ # 找到最近的几个已有节点作为邻居 candidates = list(self.nodes.values()) if len(candidates) > 1: nearest = self._find_nearest(vector, candidates, k=self.max_neighbors) node.neighbors = nearest ​ # 反向链接:让邻居也指向新节点 for neighbor_node in nearest: if len(neighbor_node.neighbors) < self.max_neighbors: neighbor_node.neighbors.append(node) ​ def _find_nearest(self, vector, candidates, k): """找到最近的 k 个候选""" distances = [] for cand in candidates: d = self._distance(vector, cand.vector) distances.append((cand, d)) ​ distances.sort(key=lambda x: x[1]) return [c for c, d in distances[:k]] ​ def _distance(self, v1, v2): return sum((a - b) ** 2 for a, b in zip(v1, v2)) ** 0.5 ​ def search(self, query, k=5): """搜索最近的 k 个向量""" # 随机选一个节点开始 start = random.choice(list(self.nodes.values())) ​ # Greedy search:沿着最临近的方向走 current = start best_dist = self._distance(query, current.vector) ​ while True: # 找当前节点邻居中比它更近的 better = None for neighbor in current.neighbors: d = self._distance(query, neighbor.vector) if d < best_dist: best_dist = d better = neighbor ​ if better is None: break # 无法再近,停止 current = better ​ # 此时 current 是局部最优 # 需要搜索更多起点找全局最优 results = [current] ​ # 从多个起点搜索,取最优 for _ in range(5): start = random.choice(list(self.nodes.values())) candidate = self._greedy_search(start, query) results.append(candidate) ​ # 排序返回最近的 k 个 results.sort(key=lambda n: self._distance(query, n.vector)) return results[:k] ​ def _greedy_search(self, start, query): """从某个节点开始的贪心搜索""" current = start best_dist = self._distance(query, current.vector) ​ while True: better = None for neighbor in current.neighbors: d = self._distance(query, neighbor.vector) if d < best_dist: best_dist = d better = neighbor ​ if better is None: break current = better ​ return current

HNSW:分层 NSW

HNSW 在 NSW 基础上增加了分层机制:

核心思想

高层:稀疏,跨度大,适合快速定位大致区域 底层:稠密,连接多,保证精度
HNSW 结构:
​ Layer 2: ●━━━━━━━━━━━━● ← 只有少量节点,跨度大 ┃ Layer 1: ●━●━━●━━●━━●━━●━● ← 中等密度 ┃ Layer 0: ●● ●● ●● ●● ●● ●● ← 最稠密,包含所有节点

搜索过程(从上层开始)

def hnsw_search(query, top_k=5): """HNSW 搜索:从顶层开始,逐层向下""" ​ # Step 1: 从顶层开始,找一个局部最优节点 # 顶层只有少量节点,快速定位大概位置 current = entry_points_at_top_layer current = greedy_search_in_layer(current, query, layer=TOP) ​ # Step 2: 下降到下一层,继续搜索 for layer in range(TOP - 1, -1, -1): # 在这一层继续从 current 出发贪心搜索 while True: better_found = False for neighbor in current.neighbors[layer]: if distance(query, neighbor) < distance(query, current): current = neighbor better_found = True if not better_found: break ​ # 当前层搜索完成后,作为下一层的起点 entry_point = current ​ # Step 3: 在底层收集更多候选 candidates = [current] # 用优先队列维护最近的候选 # 继续搜索直到无法找到更近的节点 ​ # Step 4: 返回最近的 top_k return get_top_k(candidates, query, k=top_k)

构建过程(从底层向上)

def hnsw_insert(node, layer): """ HNSW 插入: - 随机决定节点出现在哪些层(越高层概率越低) - 从顶层插入,逐层向下 """ ​ # 决定节点的最大层(随机,指数衰减概率) max_layer = get_random_layer(probability=0.5) # 概率递减 ​ # 从最高层开始插入 current = entry_point[max_layer] ​ for l in range(max_layer, -1, -1): # 在这一层找到最近邻居 neighbors = greedy_search(node.vector, layer=l) ​ # 选择前 M 个作为邻居 node.neighbors[l] = neighbors[:MAX_M] ​ # 更新邻居的反向链接 for neighbor in neighbors[:MAX_M]: if len(neighbor.neighbors[l]) < MAX_M: neighbor.neighbors[l].append(node) ​ # 下一层的起点是这一层找到的最近节点 current = neighbors[0] if neighbors else current

关键参数

# HNSW 参数说明 ​ M = 16 # 每层每个节点的最多连接数 # M 越大,精度越高,但内存占用越大、构建越慢 # M=16:推荐默认 # M=8:内存受限场景 ​ efConstruction = 200 # 构建时搜索范围 # efConstruction 越大,构建质量越高,但越慢 # 推荐:100-200 ​ efSearch = 64 # 搜索时搜索范围 # efSearch 越大,精度越高,但越慢 # 推荐:50-200 ​ max_level = 16 # 最大层数(自动计算)

M 对精度和内存的影响

M内存占用构建速度搜索精度
8
16
32很高

HNSW vs IVF 对比

特性HNSWIVF
搜索速度极快
构建速度中等
内存占用中等
搜索精度中等
动态插入较慢(影响多层)快(只需更新局部)
适用规模1亿+1000万
参数复杂度中(M, ef)低(nlist, nprobe)

什么时候选 HNSW?

选择 HNSW 的场景: - 延迟要求极低(<10ms) - 数据量在千万级以上 - 数据相对稳定(插入不频繁) - 内存充裕 ​ 选择 IVF 的场景: - 数据量中等(百万-千万) - 内存有限 - 需要频繁插入/删除 - 需要精确召回

面试常问

Q: HNSW 的分层思想是什么?

A:

  • 高层:节点稀疏,连接跨度大,快速定位大致区域

  • 低层:节点稠密,连接多,保证最终精度

  • 搜索时从顶层开始,逐层向下精确定位

Q: HNSW 和 NSW 的区别?

A:

  • NSW:单层图,贪心搜索可能陷入局部最优

  • HNSW:多层结构,从高层快速定位,再逐层向下

Q: efConstruction 和 efSearch 是什么?如何调参?

A:

  • efConstruction:构建索引时搜索范围,越大构建质量越高但越慢

  • efSearch:搜索时搜索范围,越大搜索精度越高但越慢

  • 调参建议:efConstruction=100-200,efSearch=50-200,从大到小实验

Q: HNSW 的缺点是什么?

A:

  • 内存占用大(每条向量需要额外存储邻居指针)

  • 构建慢(需要计算多层连接)

  • 动态插入效率低(插入可能影响多层)

  • 参数调优复杂(M, ef两个参数)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 2:09:12

2026年探访佛山三水灌装机工厂,实地对比后这样选

2026年探访佛山三水灌装机工厂&#xff0c;实地对比后这样选跑遍佛山三水&#xff0c;看完几家灌装机厂&#xff0c;说实话&#xff0c;差点被忽悠。刚开始只盯着进口设备&#xff0c;预算高得吓人&#xff0c;售后周期动辄两个月。后来某厂家销售跟我说&#xff1a;“我们设备…

作者头像 李华
网站建设 2026/6/9 2:08:48

OOMMF高级玩法:用MIF 2.1的Tcl脚本实现自定义初始磁化与复杂磁场

OOMMF高级技巧&#xff1a;用MIF 2.1脚本实现动态磁化控制与场建模微磁模拟作为研究纳米尺度磁性材料行为的重要工具&#xff0c;其核心在于对磁化状态演化的精确控制。传统MIF文件作为静态配置文件的使用方式&#xff0c;往往限制了模拟的灵活性和创造性。本文将深入探索MIF 2…

作者头像 李华
网站建设 2026/6/9 2:07:09

亦唐科技在自动化生产中的应用与前景

随着全球制造业的智能化转型&#xff0c;自动化生产已成为提升制造效率、降低生产成本和提升产品质量的重要手段。亦唐科技&#xff08;YIKTONG&#xff09;作为国内领先的自动化设备制造商&#xff0c;深耕自动化生产领域&#xff0c;以其创新的技术和智能化解决方案&#xff…

作者头像 李华
网站建设 2026/6/9 2:06:17

Video2X终极指南:免费AI视频无损放大到4K的完整教程

Video2X终极指南&#xff1a;免费AI视频无损放大到4K的完整教程 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/video2x…

作者头像 李华