支持私有云部署
AI知识库

53AI知识库

学习大模型的前沿技术与行业应用场景


ES × BM25:打造高效文档检索的秘密

发布日期:2025-03-30 06:08:03 浏览次数: 1554 作者:Machi
推荐语

掌握Elasticsearch与BM25,提升大数据检索效率。

核心内容:
1. Elasticsearch与文档检索基础
2. 倒排索引的构建与原理
3. BM25算法在文档排序中的应用

杨芳贤
53A创始人/腾讯云(TVP)最具价值专家

    在当今信息大爆炸的时代,各种场景都需要在海量数据中快速找到想要的信息。Elasticsearch(ES)作为一个分布式、高可用的搜索引擎,在处理海量数据的全文检索场景下表现突出。而 BM25 又是 ES 默认使用的相关性评分算法之一,能够帮助我们从众多文档中找到“最匹配”的结果。本文将通过一个简单的示例,带你一步步理解 ES 的倒排索引机制,以及 BM25 算法如何计算出搜索结果的排序。

1. 初识 Elasticsearch 与文档检索 

Elasticsearch(简称 ES)是一个分布式搜索和分析引擎,广泛应用于日志分析、全文检索、业务监控等领域。它之所以能够快速检索到海量数据中的关键信息,核心原因在于以下两点:

  1. 使用倒排索引(Inverted Index)来管理文档内容。
  2. 采用高效的相关性算法(默认是 BM25)来对匹配的文档进行排序。

全文检索:与常规数据库相比,ES 更偏重于对非结构化或半结构化数据的检索。比如自然语言、日志信息等,通过分词、倒排索引和基于统计的算法,能快速从大量文本中找到最相关的文档。

2. 倒排索引:搜索引擎的“读心术” 

    在传统的关系型数据库中,如果进行模糊查询,需要遍历整张表才能找到包含关键字的字段,这样既慢又耗资源。而搜索引擎发明了一种叫做“倒排索引”的结构,极大地提高了查询速度和效率。

2.1. 什么是倒排索引?

倒排索引的核心思路是:

  • 将文档中的词语拆分(分词)并进行标准化处理。
  • 记录“词语 → 出现在哪些文档、在哪些位置”。

这与字典(或图书索引)有些相似:

  • 正排索引更像“文档 → 包含的词语”。
  • 倒排索引则是“词语 → 出现的文档列表”。

    通过倒排索引,想要查找包含某个词语的文档时,直接从索引中就能定位到所有相应的文档,从而大幅度加快检索过程。

3. 示例:搭建小型倒排索引 

为了让你更好地理解倒排索引的构建,我们用一个极简的示例说明。假设我们有以下 3 个文档:

Doc1: "Elasticsearch is a powerful search engine"
Doc2: "Elasticsearch is used for full-text search"
Doc3: "Search engines are powerful tools"

第一步:分词与标准化

    Elasticsearch 内置的分词器(如 Standard Analyzer)会对文档进行分词,并做一些常见的文本处理(如去除大小写、去除停用词、词干提取等)。在这里,我们假设分词的结果如下:

  • Doc1: ["elasticsearch", "is", "a", "powerful", "search", "engine"]
  • Doc2: ["elasticsearch", "is", "used", "for", "full", "text", "search"]
  • Doc3: ["search", "engines", "are", "powerful", "tools"]

第二步:构建倒排索引

    我们把每个词语出现在哪些文档里,以什么位置存起来,就形成了一个简单的倒排索引表。示例如下:

词语
文档ID
位置
elasticsearch
Doc1, Doc2
Doc1:[0], Doc2:[0]
is
Doc1, Doc2
Doc1:[1], Doc2:[1]
a
Doc1
Doc1:[2]
powerful
Doc1, Doc3
Doc1:[3], Doc3:[3]
search
Doc1, Doc2, Doc3
Doc1:[4], Doc2:, Doc3:[0]
engine
Doc1
Doc1:[5]
used
Doc2
Doc2:[2]
for
Doc2
Doc2:[3]
full
Doc2
Doc2:[4]
text
Doc2
Doc2:[5]
engines
Doc3
Doc3:[1]
are
Doc3
Doc3:[2]
tools
Doc3
Doc3:[4]

通过这样一个简单的表,我们就能快速找到一个词在哪些文档中出现。

4. 查询流程:从键入关键字到获取结果 

    当用户输入查询词,比如 “powerful search” 时,Elasticsearch 的做法是:

  1. 对查询进行分词和标准化
    将用户输入的查询进行同样的分词处理,得到 ["powerful", "search"]。

  2. 在倒排索引中查找

  • “powerful” 出现在:Doc1, Doc3
  • “search” 出现在:Doc1, Doc2, Doc3
    综合得出候选文档:Doc1, Doc2, Doc3。
  • 计算相关性分数(BM25)
    找到的文档可能都和查询有关系,但哪一个最相关?这就需要相关性算法来评分。在 ES 中,BM25 是默认算法。

  • 根据评分排序并返回
    最后将匹配到的文档按照分值从高到低排序,并返回给用户。

  • 5. BM25:背后的数学公式 

        BM25(Best Matching 25)是一种计算“文档与查询”匹配度的打分函数。简化后的核心公式如下:

    其中:

    •  表示查询词的集合,比如 
    •  表示某个文档。
    •  表示查询中的某个单词。
    •  表示单词  在文档  中出现的次数(词频)。
    •  表示单词  的逆文档频率,体现某个词在所有文档中“多么稀有”。它的常见公式是:
      • :语料库中的文档总数。
      • :带有词  的文档数。
    •  表示文档  的长度(词数)。
    •  表示所有文档的平均长度。
    •  和  是可调参数,常用 ,用来平衡词频和文档长度的影响。

        简单来说,BM25 在考虑一个词(或查询词)的稀有程度(IDF)以及该词在文档中出现的次数(TF),再结合文档长度做了一个平衡。最后得到的分数越高,说明该文档越符合用户查询意图。

    6. BM25 计算示例:深度解析 

    让我们基于前面的示例来做一次“powerful search”最相关文档的打分。

    1. 统计信息

    • 文档总数3
    • 包含词 "powerful" 的文档数 2(Doc1, Doc3)
    • 包含词 "search" 的文档数 3(Doc1, Doc2, Doc3)

    2. 计算 IDF

    3. 计算文档长度与平均长度

    • Doc1: 6 个词
    • Doc2: 7 个词
    • Doc3: 5 个词

    4. 逐文档计算 BM25

    此处我们假设 ( k_1 = 1.2, b = 0.75 ),来演示计算过程。

    Doc1: "Elasticsearch is a powerful search engine"

    • "powerful" 的 TF = 1
    • "search" 的 TF = 1

    令 
    其中

    Doc2: "Elasticsearch is used for full-text search"

    • "powerful" 的 TF = 0
    • "search" 的 TF = 1

    其中 "powerful" 的 TF = 0,所以该项为 0。计算 "search" 部分:

    Doc3: "Search engines are powerful tools"

    • "powerful" 的 TF = 1
    • "search" 的 TF = 1(注意此处分词器把首字母大写的 "Search" 统一成 "search")

    (为了便于对比,我们稍微修改一下,可能与上表略有细微不同;但思路一样。)

    5. 最终排序

    • Doc3:0.69
    • Doc1:0.66
    • Doc2:0.14

    显而易见,Doc3 在这个查询场景下获得最高分,Doc1 次之,Doc2 最后。

    7. 总结 

        Elasticsearch 之所以能够在海量数据中快速找到最相关的文档,主要依赖以下关键点:

    1. 倒排索引:将文档中的单词拆分并记录到索引结构里,从而能高效搜索到包含特定词语的文档。
    2. 查询分词:对用户输入进行同样的分词处理,使得后续的匹配和打分更准确。
    3. BM25 算法:结合词频、逆文档频率、文档长度等因素来给匹配文档打分,综合反映一个文档对当前查询的“匹配度”。

    8. ES实战 

    8.1 安装与配置ES

    首先,我们需要安装并启动Elasticsearch。以下是基本步骤:

    1. 下载ES
    # 下载ES 8.x版本
    wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.x.x-linux-x86_64.tar.gz

    # 解压
    tar -xzf elasticsearch-8.x.x-linux-x86_64.tar.gz

    # 进入ES目录
    cd elasticsearch-8.x.x/
    1. 修改配置编辑config/elasticsearch.yml
    # 集群名称
    cluster.name: my-es-cluster

    # 节点名称
    node.name: node-1

    # 数据和日志存储路径
    path.data: /path/to/data
    path.logs: /path/to/logs

    # 网络设置
    network.host: 0.0.0.0
    http.port: 9200

    # 开发环境设置
    discovery.type: single-node
    1. 启动ES
    # 启动
    ./bin/elasticsearch

    # 检查是否启动成功
    curl http://localhost:9200

    8.2 创建索引并定义映射

    让我们创建一个用于存储文章的索引:

    PUT /articles
    {
      "settings": {
        "number_of_shards"1,
        "number_of_replicas"1,
        "analysis": {
          "analyzer": {
            "my_analyzer": {
              "type""custom",
              "tokenizer""standard",
              "filter": [
                "lowercase",
                "stop",
                "snowball"
              ]
            }
          }
        }
      },
      "mappings": {
        "properties": {
          "title": {
            "type""text",
            "analyzer""my_analyzer",
            "fields": {
              "keyword": {
                "type""keyword"
              }
            }
          },
          "content": {
            "type""text",
            "analyzer""my_analyzer"
          },
          "author": {
            "type""keyword"
          },
          "publish_date": {
            "type""date"
          }
        }
      }
    }

    8.3 索引文档

    插入一些测试文档:

    POST /articles/_doc/1
    {
      "title""Elasticsearch Guide",
      "content""Elasticsearch is a powerful search engine based on Lucene",
      "author""John Doe",
      "publish_date""2024-03-15"
    }

    POST /articles/_doc/2
    {
      "title""Advanced Search Techniques",
      "content""Learn how to use BM25 algorithm for better search results",
      "author""Jane Smith",
      "publish_date""2024-03-16"
    }

    POST /articles/_doc/3
    {
      "title""Understanding Text Analysis",
      "content""Text analysis is crucial for search engine performance",
      "author""Mike Johnson",
      "publish_date""2024-03-17"
    }

    8.4 查询示例

    1. 简单全文搜索
    GET /articles/_search
    {
      "query": {
        "match": {
          "content""search engine"
        }
      }
    }
    1. 多字段搜索
    GET /articles/_search
    {
      "query": {
        "multi_match": {
          "query""search engine",
          "fields": ["title""content"]
        }
      }
    }
    1. 使用bool查询组合多个条件
    GET /articles/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "content""search"
              }
            }
          ],
          "should": [
            {
              "match": {
                "title""elasticsearch"
              }
            }
          ],
          "filter": [
            {
              "range": {
                "publish_date": {
                  "gte""2024-03-15"
                }
              }
            }
          ]
        }
      }
    }

    8.5 分析搜索结果

    当执行搜索请求时,ES会返回包含相关性分数的结果:

    {
      "took"5,
      "hits": {
        "total": {
          "value"2,
          "relation""eq"
        },
        "max_score"1.3862944,
        "hits": [
          {
            "_index""articles",
            "_id""1",
            "_score"1.3862944,
            "_source": {
              "title""Elasticsearch Guide",
              "content""Elasticsearch is a powerful search engine based on Lucene"
            }
          },
          {
            "_index""articles",
            "_id""2",
            "_score"0.9530659,
            "_source": {
              "title""Advanced Search Techniques",
              "content""Learn how to use BM25 algorithm for better search results"
            }
          }
        ]
      }
    }

    8.6 调优搜索相关性

    1. 自定义评分
    GET /articles/_search
    {
      "query": {
        "script_score": {
          "query": {
            "match": {
              "content""search engine"
            }
          },
          "script": {
            "source""_score * (doc['publish_date'].value.millis - 1710000000000) / 86400000"
          }
        }
      }
    }
    1. 调整字段权重
    GET /articles/_search
    {
      "query": {
        "multi_match": {
          "query""search engine",
          "fields": [
            "title^3",
            "content"
          ]
        }
      }
    }

    8.7 性能优化建议

    1. 合理设置分片数

    • 单个分片大小建议在20GB-40GB之间
    • 分片数 = 数据总量 / 单个分片大小
  • 使用合适的映射

    • 对于不需要分词的字段使用keyword类型
    • 对于需要分词的字段使用text类型
    • 合理使用fields设置多字段映射
  • 优化内存使用

    • 设置合适的JVM堆大小
    • 使用doc_valuesfielddata优化聚合性能
  • 查询优化

    • 尽量使用过滤器(filter)代替查询(query)
    • 避免使用通配符查询
    • 合理使用缓存

    好的,我来介绍Docker化安装ES以及界面化管理工具。

    9. Docker化安装ES 

    9.1 安装单节点ES

    1. 创建docker-compose.yml文件
    version: '3'
    services:
      elasticsearch:
        image: elasticsearch:8.11.1
        container_name: elasticsearch
        environment:
          - discovery.type=single-node
          - ES_JAVA_OPTS=-Xms512m -Xmx512m
          - xpack.security.enabled=false
        volumes:
          - ./es/data:/usr/share/elasticsearch/data
          - ./es/plugins:/usr/share/elasticsearch/plugins
        ports:
          - "9200:9200"
          - "9300:9300"
        networks:
          - elastic
        
    networks:
      elastic:
        driver: bridge
    1. 启动ES
    # 创建目录
    mkdir -p ./es/data ./es/plugins

    # 设置目录权限
    chmod 777 ./es/data ./es/plugins

    # 启动容器
    docker-compose up -d
    1. 验证安装
    # 检查容器状态
    docker ps

    # 测试ES是否正常运行
    curl http://localhost:9200

    9.2 安装Kibana(官方可视化工具)

    1. 更新docker-compose.yml,添加Kibana服务
    version: '3'
    services:
      elasticsearch:
        # ... ES配置保持不变 ...

      kibana:
        image: kibana:8.11.1
        container_name: kibana
        environment:
          - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
        ports:
          - "5601:5601"
        depends_on:
          - elasticsearch
        networks:
          - elastic

    networks:
      elastic:
        driver: bridge
    1. 启动Kibana
    docker-compose up -d

    访问 http://localhost:5601 即可打开Kibana界面。

    9.3 安装Elasticsearch-head(轻量级可视化工具)

    version: '3'
    services:
      elasticsearch:
        # ... ES配置保持不变 ...

      elasticsearch-head:
        image: mobz/elasticsearch-head:5
        container_name: elasticsearch-head
        ports:
          - "9100:9100"
        networks:
          - elastic

    访问 http://localhost:9100 即可使用elasticsearch-head。

    10 可视化工具对比 

    10.1 Kibana(官方工具)

    优点:

    • 功能最完整
    • 与ES版本完全兼容
    • 支持复杂的数据可视化
    • 支持开发工具控制台
    • 支持安全认证

    主要功能:

    1. Dev Tools

    • 提供交互式控制台
    • 支持自动补全
    • 支持多请求组合
  • Index Management

    • 索引创建和管理
    • 映射配置
    • 索引模板管理
  • 数据可视化

    • 支持多种图表类型
    • 可创建仪表板
    • 实时数据监控

    10.2 Elasticsearch-head

    优点:

    • 界面简洁
    • 启动快速
    • 资源占用少
    • 适合简单操作

    主要功能:

    1. 集群监控

    • 查看集群状态
    • 节点信息
    • 索引状态
  • 数据浏览

    • 查看索引数据
    • 简单的增删改查
    • JSON格式化显示
  • REST请求

    • 简单的REST客户端
    • 支持基本查询

    10.3 Cerebro(另一个选择)

    添加Cerebro到docker-compose.yml:

    version: '3'
    services:
      # ... 其他服务配置 ...

      cerebro:
        image: lmenezes/cerebro:0.9.4
        container_name: cerebro
        ports:
          - "9000:9000"
        networks:
          - elastic

    优点:

    • 现代化界面
    • 支持多集群管理
    • 操作简单直观

    主要功能:

    1. 集群管理

    • 多集群监控
    • 节点状态查看
    • 分片分配可视化
  • 索引管理

    • 创建/删除索引
    • 管理别名
    • 查看索引设置
  • REST API

    • 内置REST客户端
    • 请求历史记录
    • 响应格式化

    11. 推荐使用方案 

    1. 开发环境

    • Kibana + Elasticsearch-head
    • Kibana用于复杂操作和可视化
    • Elasticsearch-head用于快速查看数据
  • 生产环境

    • 主要使用Kibana
    • 配置适当的安全认证
    • 根据需要开放必要的功能
  • 配置建议

  • version: '3'
    services:
      elasticsearch:
        image: elasticsearch:8.11.1
        container_name: elasticsearch
        environment:
          - discovery.type=single-node
          - ES_JAVA_OPTS=-Xms512m -Xmx512m
          - xpack.security.enabled=false
        volumes:
          - ./es/data:/usr/share/elasticsearch/data
          - ./es/plugins:/usr/share/elasticsearch/plugins
        ports:
          - "9200:9200"
        networks:
          - elastic
        
      kibana:
        image: kibana:8.11.1
        container_name: kibana
        environment:
          - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
        ports:
          - "5601:5601"
        depends_on:
          - elasticsearch
        networks:
          - elastic

      elasticsearch-head:
        image: mobz/elasticsearch-head:5
        container_name: elasticsearch-head
        ports:
          - "9100:9100"
        networks:
          - elastic

    networks:
      elastic:
        driver: bridge

    使用以上配置,你可以同时拥有Kibana和Elasticsearch-head的所有功能,既可以进行复杂的数据分析和可视化,也可以快速查看集群状态和数据。


    53AI,企业落地大模型首选服务商

    产品:场景落地咨询+大模型应用平台+行业解决方案

    承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业

    联系我们

    售前咨询
    186 6662 7370
    预约演示
    185 8882 0121

    微信扫码

    添加专属顾问

    回到顶部

    加载中...

    扫码咨询