MinHash 分词过滤器

编辑

使用 MinHash 技术为分词流生成签名。您可以使用 MinHash 签名来估计文档的相似度。参见 使用 min_hash 分词过滤器进行相似度搜索

min_hash 过滤器按顺序对分词流执行以下操作:

  1. 对流中的每个分词进行哈希。
  2. 将哈希值分配到桶中,只保留每个桶中最小的哈希值。
  3. 输出每个桶中最小的哈希值作为分词流。

此过滤器使用 Lucene 的 MinHashFilter

可配置参数

编辑
bucket_count
(可选,整数) 分配哈希值的桶数。默认为 512
hash_count
(可选,整数) 对流中每个分词进行哈希的次数。默认为 1
hash_set_size

(可选,整数) 从每个桶中保留的哈希数。默认为 1

哈希值按升序大小保留,从桶中最小的哈希值开始。

with_rotation
(可选,布尔值) 如果为 true,则如果 hash_set_size1,过滤器将使用第一个非空桶的右侧循环值填充空桶。如果 bucket_count 参数大于 1,则此参数默认为 true。否则,此参数默认为 false

配置 min_hash 过滤器的技巧

编辑
  • min_hash 过滤器输入分词通常应该是从 Shingle 分词过滤器生成的 k-词组 shingle。您应该选择足够大的 k 值,以使任何给定 shingle 出现在文档中的概率很低。同时,由于内部每个 shingle 都被哈希成 128 位哈希值,因此您应该选择足够小的 k 值,以便所有可能的不同 k-词组 shingle 都可以被哈希成 128 位哈希值,并最大限度地减少冲突。
  • 我们建议您测试 hash_countbucket_counthash_set_size 参数的不同值。

    • 为了提高精度,请增加 bucket_counthash_set_size 参数的值。较高的 bucket_counthash_set_size 值会增加不同分词被索引到不同桶中的可能性。
    • 为了提高召回率,请增加 hash_count 参数的值。例如,将 hash_count 设置为 2 会以两种不同的方式对每个分词进行哈希,从而增加搜索的潜在候选数量。
  • 默认情况下,min_hash 过滤器为每个文档生成 512 个分词。每个分词的大小为 16 字节。这意味着每个文档的大小将增加大约 8KB。
  • min_hash 过滤器用于 Jaccard 相似度。这意味着文档包含某个分词多少次并不重要,重要的是它是否包含该分词。

使用 min_hash 分词过滤器进行相似度搜索

编辑

min_hash 分词过滤器允许您对文档进行哈希以进行相似度搜索。相似度搜索,或最近邻搜索是一个复杂的问题。一个简单的解决方案需要在查询文档和索引中的每个文档之间进行详尽的成对比较。如果索引很大,这是一个非常耗时的操作。已经开发了许多近似最近邻搜索解决方案,以使相似度搜索更实用且在计算上更可行。其中一种解决方案涉及文档的哈希。

文档以一种方式进行哈希,使得相似的文档更有可能产生相同的哈希码并被放入相同的哈希桶中,而不相似的文档更有可能被哈希到不同的哈希桶中。这种类型的哈希称为局部敏感哈希 (LSH)。

根据文档之间相似性的构成,已经提出了各种 LSH 函数 已被提出。对于 Jaccard 相似度,一个流行的 LSH 函数是 MinHash。MinHash 为文档生成签名的基本思想是,对整个索引词汇表应用随机排列(词汇表的随机编号),并记录文档此排列的最小值(文档中存在的词汇词的最小编号)。排列运行几次;将所有排列的最小值组合起来将构成文档的签名。

实际上,不是使用随机排列,而是选择多个哈希函数。哈希函数计算文档每个分词的哈希码,并选择其中的最小哈希码。所有哈希函数的最小哈希码组合起来构成文档的签名。

自定义并添加到分析器

编辑

要自定义 min_hash 过滤器,请复制它以创建新自定义分词过滤器的基础。您可以使用其可配置参数修改过滤器。

例如,以下 创建索引 API 请求使用以下自定义分词过滤器来配置新的 自定义分析器

  • my_shingle_filter,一个自定义的 shingle 过滤器my_shingle_filter 只输出五词 shingle。
  • my_minhash_filter,一个自定义的 min_hash 过滤器。my_minhash_filter 对每个五词 shingle 进行一次哈希。然后它将哈希值分配到 512 个桶中,只保留每个桶中最小的哈希值。

该请求还将自定义分析器分配给 fingerprint 字段映射。

resp = client.indices.create(
    index="my-index-000001",
    settings={
        "analysis": {
            "filter": {
                "my_shingle_filter": {
                    "type": "shingle",
                    "min_shingle_size": 5,
                    "max_shingle_size": 5,
                    "output_unigrams": False
                },
                "my_minhash_filter": {
                    "type": "min_hash",
                    "hash_count": 1,
                    "bucket_count": 512,
                    "hash_set_size": 1,
                    "with_rotation": True
                }
            },
            "analyzer": {
                "my_analyzer": {
                    "tokenizer": "standard",
                    "filter": [
                        "my_shingle_filter",
                        "my_minhash_filter"
                    ]
                }
            }
        }
    },
    mappings={
        "properties": {
            "fingerprint": {
                "type": "text",
                "analyzer": "my_analyzer"
            }
        }
    },
)
print(resp)
response = client.indices.create(
  index: 'my-index-000001',
  body: {
    settings: {
      analysis: {
        filter: {
          my_shingle_filter: {
            type: 'shingle',
            min_shingle_size: 5,
            max_shingle_size: 5,
            output_unigrams: false
          },
          my_minhash_filter: {
            type: 'min_hash',
            hash_count: 1,
            bucket_count: 512,
            hash_set_size: 1,
            with_rotation: true
          }
        },
        analyzer: {
          my_analyzer: {
            tokenizer: 'standard',
            filter: [
              'my_shingle_filter',
              'my_minhash_filter'
            ]
          }
        }
      }
    },
    mappings: {
      properties: {
        fingerprint: {
          type: 'text',
          analyzer: 'my_analyzer'
        }
      }
    }
  }
)
puts response
const response = await client.indices.create({
  index: "my-index-000001",
  settings: {
    analysis: {
      filter: {
        my_shingle_filter: {
          type: "shingle",
          min_shingle_size: 5,
          max_shingle_size: 5,
          output_unigrams: false,
        },
        my_minhash_filter: {
          type: "min_hash",
          hash_count: 1,
          bucket_count: 512,
          hash_set_size: 1,
          with_rotation: true,
        },
      },
      analyzer: {
        my_analyzer: {
          tokenizer: "standard",
          filter: ["my_shingle_filter", "my_minhash_filter"],
        },
      },
    },
  },
  mappings: {
    properties: {
      fingerprint: {
        type: "text",
        analyzer: "my_analyzer",
      },
    },
  },
});
console.log(response);
PUT /my-index-000001
{
  "settings": {
    "analysis": {
      "filter": {
        "my_shingle_filter": {      
          "type": "shingle",
          "min_shingle_size": 5,
          "max_shingle_size": 5,
          "output_unigrams": false
        },
        "my_minhash_filter": {
          "type": "min_hash",
          "hash_count": 1,          
          "bucket_count": 512,      
          "hash_set_size": 1,       
          "with_rotation": true     
        }
      },
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "standard",
          "filter": [
            "my_shingle_filter",
            "my_minhash_filter"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "fingerprint": {
        "type": "text",
        "analyzer": "my_analyzer"
      }
    }
  }
}

配置自定义 shingle 过滤器以仅输出五词 shingle。

流中的每个五词 shingle 都进行一次哈希。

哈希值被分配到 512 个桶中。

只保留每个桶中最小的哈希值。

过滤器用相邻桶的值填充空桶。