Python实现用于高维数据的近似索引结构：Locality-Sensitive Hashing（LSH）

admin
Thu, 27 Nov 2025 13:22:27 +0000
Python, locality, python, sensitive

Python实现高维数据的近似索引结构：Locality-Sensitive Hashing (LSH) 大家好，今天我们来深入探讨一个在高维数据检索中非常重要的技术：Locality-Sensitive Hashing，简称LSH。在高维空间中进行精确的最近邻搜索通常是计算密集型的，而LSH提供了一种高效的近似解决方案。我们将用Python来实现LSH，并逐步讲解其背后的原理。 1. 什么是Locality-Sensitive Hashing (LSH)? LSH 是一种将相似数据点映射到相同哈希桶中的哈希技术。 它的核心思想是：如果两个数据点在高维空间中是“相似的”，那么它们在经过 LSH 函数的哈希后，更有可能被分配到同一个桶中。反之，如果两个数据点不相似，它们被哈希到同一个桶中的概率就比较低。 这种技术的核心在于“locality-sensitive”的特性，意味着哈希函数的设计要能捕捉到数据点之间的局部相似性。通过这种方式，我们可以将原本在高维空间中的搜索问题，转化为在哈希桶内的搜索问题，从而大大降低了计算复杂度。 2. LSH 的基本原理 LSH 的工作流程大致如下： 哈希 …

继续阅读“Python实现用于高维数据的近似索引结构：Locality-Sensitive Hashing（LSH）”

The post Python实现用于高维数据的近似索引结构：Locality-Sensitive Hashing（LSH） first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

Python实现高维数据的近似索引结构：Locality-Sensitive Hashing (LSH)

大家好，今天我们来深入探讨一个在高维数据检索中非常重要的技术：Locality-Sensitive Hashing，简称LSH。在高维空间中进行精确的最近邻搜索通常是计算密集型的，而LSH提供了一种高效的近似解决方案。我们将用Python来实现LSH，并逐步讲解其背后的原理。

1. 什么是Locality-Sensitive Hashing (LSH)?

LSH 是一种将相似数据点映射到相同哈希桶中的哈希技术。 它的核心思想是：如果两个数据点在高维空间中是“相似的”，那么它们在经过 LSH 函数的哈希后，更有可能被分配到同一个桶中。反之，如果两个数据点不相似，它们被哈希到同一个桶中的概率就比较低。

这种技术的核心在于“locality-sensitive”的特性，意味着哈希函数的设计要能捕捉到数据点之间的局部相似性。通过这种方式，我们可以将原本在高维空间中的搜索问题，转化为在哈希桶内的搜索问题，从而大大降低了计算复杂度。

2. LSH 的基本原理

LSH 的工作流程大致如下：

1. 哈希函数族的选择： 选择一个或多个LSH函数族，每个函数族包含多个哈希函数。不同的LSH函数族适用于不同的距离度量（例如，欧氏距离、余弦相似度、Jaccard相似度）。

2. 哈希表的构建： 对数据集中的每个数据点，使用选定的 LSH 函数族中的多个哈希函数进行哈希，并将数据点存储到对应的哈希桶中。 可以创建多个哈希表，每个哈希表使用不同的哈希函数族或同一函数族的不同哈希函数。

3. 查询： 对于给定的查询点，使用与构建哈希表时相同的哈希函数进行哈希，找到对应的哈希桶。

4. 候选集生成： 检索哈希桶中的所有数据点，作为候选的最近邻。

5. 距离计算与排序： 计算查询点与候选集中每个数据点之间的距离，并选择距离最近的几个点作为近似的最近邻。

3. LSH 的关键组件

LSH 函数族： LSH 函数族是一组哈希函数，它们具有 “locality-sensitive” 的特性。不同的 LSH 函数族适用于不同的距离度量。

哈希表： 哈希表用于存储经过 LSH 函数哈希后的数据点。通常会创建多个哈希表，以提高召回率。

查询过程： 查询过程包括使用 LSH 函数对查询点进行哈希，查找对应的哈希桶，并从哈希桶中检索候选的最近邻。

近似最近邻： 由于 LSH 是一种近似算法，因此返回的最近邻可能不是真正的最近邻，但通常能够以较高的概率找到近似的最近邻。

4. 基于欧氏距离的LSH实现 (E2LSH)

对于基于欧氏距离的 LSH，常用的方法是 E2LSH (Exact Euclidean LSH)。它的基本思想是将高维空间划分为多个超平面，并使用随机超平面来生成哈希函数。

4.1 随机超平面生成

E2LSH 的哈希函数形式如下：

h(v) = floor((a · v + b) / w)

其中：

v 是一个数据点 (向量)。

a 是一个随机向量，其元素服从标准正态分布 N(0, 1)。a 的维度与 v 相同。

b 是一个随机数，服从均匀分布 U[0, w]。

w 是一个桶宽参数，用于控制哈希桶的大小。

· 表示向量的点积。

floor() 表示向下取整。

4.2 Python 代码实现

import numpy as np

class E2LSH:
    def __init__(self, num_hash_functions, w, dim):
        """
        E2LSH 初始化

        Args:
            num_hash_functions: 哈希函数的数量
            w: 桶宽
            dim: 数据维度
        """
        self.num_hash_functions = num_hash_functions
        self.w = w
        self.dim = dim
        self.a = np.random.randn(num_hash_functions, dim)  # 随机向量
        self.b = np.random.uniform(0, w, num_hash_functions) # 随机偏移量

    def hash(self, v):
        """
        计算哈希值

        Args:
            v: 数据点 (向量)

        Returns:
            哈希值 (整数)
        """
        return np.floor((np.dot(self.a, v) + self.b) / self.w).astype(int)

    def create_hash_table(self, data):
        """
        创建哈希表

        Args:
            data: 数据集 (二维 numpy 数组)

        Returns:
            哈希表 (字典，键为哈希值，值为数据点索引列表)
        """
        hash_table = {}
        for i, v in enumerate(data):
            hash_values = tuple(self.hash(v))  # 将哈希值转换为元组，因为字典的键必须是不可变的
            if hash_values not in hash_table:
                hash_table[hash_values] = []
            hash_table[hash_values].append(i) # 存储数据点索引
        return hash_table

    def query(self, query_point, hash_table, data, top_k=10):
        """
        查询最近邻

        Args:
            query_point: 查询点 (向量)
            hash_table: 哈希表
            data: 数据集 (二维 numpy 数组)
            top_k: 返回的最近邻数量

        Returns:
            最近邻的索引列表
        """
        hash_values = tuple(self.hash(query_point))
        if hash_values not in hash_table:
            return []  # 如果哈希桶为空，则返回空列表

        candidate_indices = hash_table[hash_values]
        candidates = data[candidate_indices] # 获取候选数据点

        # 计算距离
        distances = np.linalg.norm(candidates - query_point, axis=1)

        # 获取最近邻的索引
        nearest_neighbor_indices = np.argsort(distances)[:top_k]

        # 返回原始数据中的索引
        return [candidate_indices[i] for i in nearest_neighbor_indices]

4.3 代码解释

__init__: 初始化函数，用于设置哈希函数的数量 (num_hash_functions)、桶宽 (w) 和数据维度 (dim)。同时，生成随机向量 a 和随机偏移量 b。

hash: 哈希函数，根据公式计算哈希值。

create_hash_table: 创建哈希表，遍历数据集中的每个数据点，计算其哈希值，并将数据点的索引存储到对应的哈希桶中。

query: 查询函数，根据查询点的哈希值找到对应的哈希桶，并计算查询点与哈希桶中所有数据点之间的距离，返回距离最近的 top_k 个数据点的索引。

4.4 使用示例

# 生成一些随机数据
data = np.random.rand(1000, 128)  # 1000 个数据点，每个数据点 128 维
query_point = np.random.rand(128)

# 设置 LSH 参数
num_hash_functions = 10
w = 1.0 # 桶宽可以调整
dim = 128

# 创建 E2LSH 对象
lsh = E2LSH(num_hash_functions, w, dim)

# 创建哈希表
hash_table = lsh.create_hash_table(data)

# 查询最近邻
nearest_neighbors = lsh.query(query_point, hash_table, data, top_k=5)

print("Nearest Neighbors:", nearest_neighbors)

# 验证结果 (可选)
# 可以计算查询点与所有数据点之间的距离，并与 LSH 的结果进行比较

5. 基于余弦相似度的LSH实现 (Cosine LSH)

对于基于余弦相似度的 LSH，常用的方法是使用随机超平面来划分空间。

5.1 随机超平面生成

Cosine LSH 的哈希函数形式如下：

h(v) = sign(a · v)

其中：

v 是一个数据点 (向量)。

a 是一个随机向量，其元素服从标准正态分布 N(0, 1)。a 的维度与 v 相同。

· 表示向量的点积。

sign() 函数返回 1 如果 a · v 大于 0，否则返回 -1。

5.2 Python 代码实现

import numpy as np

class CosineLSH:
    def __init__(self, num_hash_functions, dim):
        """
        CosineLSH 初始化

        Args:
            num_hash_functions: 哈希函数的数量
            dim: 数据维度
        """
        self.num_hash_functions = num_hash_functions
        self.dim = dim
        self.a = np.random.randn(num_hash_functions, dim) # 随机向量

    def hash(self, v):
        """
        计算哈希值

        Args:
            v: 数据点 (向量)

        Returns:
            哈希值 (二进制字符串)
        """
        return "".join(['1' if x > 0 else '0' for x in np.dot(self.a, v)])

    def create_hash_table(self, data):
        """
        创建哈希表

        Args:
            data: 数据集 (二维 numpy 数组)

        Returns:
            哈希表 (字典，键为哈希值，值为数据点索引列表)
        """
        hash_table = {}
        for i, v in enumerate(data):
            hash_value = self.hash(v)
            if hash_value not in hash_table:
                hash_table[hash_value] = []
            hash_table[hash_value].append(i)
        return hash_table

    def query(self, query_point, hash_table, data, top_k=10):
        """
        查询最近邻

        Args:
            query_point: 查询点 (向量)
            hash_table: 哈希表
            data: 数据集 (二维 numpy 数组)
            top_k: 返回的最近邻数量

        Returns:
            最近邻的索引列表
        """
        hash_value = self.hash(query_point)
        if hash_value not in hash_table:
            return []

        candidate_indices = hash_table[hash_value]
        candidates = data[candidate_indices]

        # 计算余弦相似度
        similarity = np.dot(candidates, query_point) / (np.linalg.norm(candidates, axis=1) * np.linalg.norm(query_point))

        # 获取最近邻的索引
        nearest_neighbor_indices = np.argsort(similarity)[::-1][:top_k]  # 从大到小排序，取前 k 个

        # 返回原始数据中的索引
        return [candidate_indices[i] for i in nearest_neighbor_indices]

5.3 代码解释

__init__: 初始化函数，用于设置哈希函数的数量 (num_hash_functions) 和数据维度 (dim)。同时，生成随机向量 a。

hash: 哈希函数，根据公式计算哈希值，将结果转换为二进制字符串。

create_hash_table: 创建哈希表，遍历数据集中的每个数据点，计算其哈希值，并将数据点的索引存储到对应的哈希桶中。

query: 查询函数，根据查询点的哈希值找到对应的哈希桶，并计算查询点与哈希桶中所有数据点之间的余弦相似度，返回相似度最高的 top_k 个数据点的索引。

5.4 使用示例

# 生成一些随机数据
data = np.random.rand(1000, 128)  # 1000 个数据点，每个数据点 128 维
query_point = np.random.rand(128)

# 设置 LSH 参数
num_hash_functions = 10
dim = 128

# 创建 CosineLSH 对象
lsh = CosineLSH(num_hash_functions, dim)

# 创建哈希表
hash_table = lsh.create_hash_table(data)

# 查询最近邻
nearest_neighbors = lsh.query(query_point, hash_table, data, top_k=5)

print("Nearest Neighbors:", nearest_neighbors)

# 验证结果 (可选)
# 可以计算查询点与所有数据点之间的余弦相似度，并与 LSH 的结果进行比较

6. LSH 的参数选择

LSH 的性能很大程度上取决于参数的选择，如哈希函数的数量、桶宽 (对于 E2LSH) 等。

哈希函数的数量： 增加哈希函数的数量可以提高召回率，但也会增加计算复杂度。 通常需要根据数据集的特点进行调整。 可以使用多个哈希表来提高召回率。

桶宽 (w)： 桶宽的选择会影响哈希桶的大小和数据点的分布。需要根据数据集的尺度进行调整。

一般来说，参数的选择需要通过实验来确定，根据实际的应用场景和性能要求进行调整。

7. LSH 的优点和缺点

优点：

高效性： LSH 能够显著降低高维数据检索的计算复杂度，将原本的线性搜索转化为亚线性搜索。

可扩展性： LSH 易于扩展到大规模数据集，可以通过增加哈希表或使用分布式计算来处理海量数据。

灵活性： LSH 适用于不同的距离度量，可以根据实际应用场景选择合适的 LSH 函数族。

缺点：

近似性： LSH 是一种近似算法，可能无法保证找到真正的最近邻。

参数敏感： LSH 的性能对参数的选择比较敏感，需要进行调优。

空间占用： 多个哈希表会增加空间占用。

8. LSH 的应用场景

LSH 在许多领域都有广泛的应用，包括：

图像检索： 查找相似的图像。

文本检索： 查找相似的文档或网页。

推荐系统： 推荐相似的商品或用户。

数据挖掘： 查找相似的数据点或模式。

基因组学： 查找相似的基因序列。

9. 如何提高LSH的性能

多哈希表： 使用多个哈希表可以显著提高召回率。

级联 LSH： 将多个 LSH 函数族级联起来，可以提高精度。

数据预处理： 对数据进行预处理，例如降维或归一化，可以提高 LSH 的性能。

参数调优： 仔细选择 LSH 的参数，例如哈希函数的数量和桶宽，可以优化性能。

并行化： 使用并行计算可以加速 LSH 的构建和查询过程。

10. LSH 的改进和变种

Multi-Probe LSH： Multi-Probe LSH 通过探测多个哈希桶来提高召回率，而不仅仅是查询点所在的哈希桶。

Tree-based LSH： Tree-based LSH 将 LSH 与树结构相结合，可以实现更高效的索引和查询。

Learning to Hash： Learning to Hash 通过机器学习方法来学习哈希函数，可以更好地适应数据的特点。

11. 总结：LSH在近似近邻搜索中扮演关键角色

我们深入探讨了 Locality-Sensitive Hashing (LSH) 的原理和实现，包括基于欧氏距离的 E2LSH 和基于余弦相似度的 Cosine LSH。LSH 是一种强大的近似最近邻搜索技术，在高维数据处理中具有重要的应用价值。 通过理解 LSH 的基本原理、参数选择和优化方法，我们可以更好地利用 LSH 来解决实际问题。

更多IT精英技术系列讲座，到智猿学院