
在设计语义搜索引擎、RAG应用程序或其他使用嵌入的系统时,有一个决定性的因素可能会影响搜索质量:如何对数据进行分块。
分块是将大型文本分解为较小的、语义上有意义的片段,以便进行单独嵌入和搜索的过程。当文档超出嵌入模型的上下文窗口,或者需要检索特定的相关片段而不是整个文档时,这一过程就显得尤为必要。传统的文本搜索是按原样索引文档,而基于向量的系统需要一种不同的方法,主要基于以下两个原因:
请记住,您选择的嵌入模型对您的分块策略有重大影响:
在这篇博客中,我们将讨论分块的基础知识,并探讨使用分块策略如何影响Elasticsearch中的语义搜索结果。
分块是将大型文档在创建嵌入和索引之前分解为较小片段的过程。与将一篇50页的文本转换为单个嵌入不同,分块将其拆分为可以单独搜索的较小逻辑单元。

在Elasticsearch中,semantic_text字段类型使用分块技术在自动生成嵌入前将文本分解为较小的片段。
如果我们在生成嵌入之前不应用分块,会遇到一些问题,比如:
每个块的大小在向量搜索中形成了一个权衡:
目标是选择一个块大小,在最小化信息丢失的同时仍然包含有用的上下文。 一般来说,可以这样考虑块的大小:
较小的块:
较大的块:
为了找到适合您使用场景的合适块大小,您可以使用自己的内容进行测试。监控是否得到了过多不相关的结果(块太大)或缺乏足够上下文的答案(块太小)。
Elasticsearch允许我们选择不同的方法来分割文档。您可以使用此对比表来选择最适合您用例的策略,考虑文档结构和您使用的模型:

现在让我们看看每种策略的实际应用:
这种策略将文本拆分为一个或多个完整句子,以优先考虑句子层级的可读性和语义连贯性。
输入:
人工智能正在通过预测分析改变医疗保健。机器学习算法可以分析患者数据以识别风险因素。疾病的早期检测可以挽救生命并降低成本。然而,数据隐私仍然是一个关键问题。医疗服务提供者必须在创新和患者保密之间取得平衡。结果数组(max_chunk_size: 50, sentence_overlap: 1):
[
"人工智能正在通过预测分析改变医疗保健。机器学习算法可以分析患者数据以识别风险因素。疾病的早期检测可以挽救生命并降低成本。",
"疾病的早期检测可以挽救生命并降低成本。然而,数据隐私仍然是一个关键问题。医疗服务提供者必须在创新和患者保密之间取得平衡。"
]这种策略将文本拆分为单个单词,直到达到max_chunk_size。这确保了块大小的一致性,为处理和存储提供了可预测性。缺点是可能会在多个块中拆分相关上下文。
输入:
人工智能正在通过预测分析改变医疗保健。机器学习算法可以分析患者数据以识别风险因素。疾病的早期检测可以挽救生命并降低成本。然而,数据隐私仍然是一个关键问题。医疗服务提供者必须在创新和患者保密之间取得平衡。结果数组(max_chunk_size: 25, overlap: 5):
[
"人工智能正在通过预测分析改变医疗保健。机器学习算法可以分析患者数据以识别风险因素。疾病的早期检测可以挽救生命",
"挽救生命并降低成本。然而,数据隐私仍然是一个关键问题。医疗服务提供者必须在创新和患者保密之间取得平衡。"
]这种策略基于分隔符列表(如换行)递归地拆分文本。按顺序应用分隔符后,如果块仍然太大,则回退到句子拆分。对于格式化内容(如markdown文档),这种策略尤其有效。
输入:
# 气候变化解决方案
## 可再生能源
太阳能和风能正在与化石燃料的成本竞争。投资于清洁能源基础设施创造了就业机会。
## 碳捕获
直接空气捕获技术从大气中去除二氧化碳。这些系统需要大量的能源输入,但在大规模部署中显示出希望。
## 政策变革
政府法规可以通过碳定价和可再生能源法规加速向清洁能源的转变。结果数组(max_chunk_size: 30, separators: "#", "\n\n"):
[
"# 气候变化解决方案\n\n## 可再生能源\n太阳能和风能正在与化石燃料的成本竞争。投资于清洁能源基础设施创造了就业机会。",
"## 碳捕获\n直接空气捕获技术从大气中去除二氧化碳。这些系统需要大量的能源输入,但在大规模部署中显示出希望。",
"## 政策变革\n政府法规可以通过碳定价和可再生能源法规加速向清洁能源的转变。"
]这种“策略”禁用分块,从完整文本创建嵌入。它适用于所有上下文始终需要的小型文本和文档。这一策略的潜在缺陷是,模型可能会在生成嵌入时丢弃多余的令牌。
输入:
人工智能正在通过预测分析改变医疗保健。机器学习算法可以分析患者数据以识别风险因素。疾病的早期检测可以挽救生命并降低成本。然而,数据隐私仍然是一个关键问题。医疗服务提供者必须在创新和患者保密之间取得平衡。结果:
["人工智能正在通过预测分析改变医疗保健。机器学习算法可以分析患者数据以识别风险因素。疾病的早期检测可以挽救生命并降低成本。然而,数据隐私仍然是一个关键问题。医疗服务提供者必须在创新和患者保密之间取得平衡。"]Elastic为分块策略提供了以下自定义参数:
让我们通过一个实际示例来看看在Elasticsearch中如何进行分块。
分块设置是在创建推理端点时指定的,Elasticsearch在索引文档时会自动处理分块过程。
在以下示例中,我们将创建一个text_embedding推理端点,使用句子分块策略,并检查如何在语义查询中生成和查找块:
PUT _inference/text_embedding/my-embedding-model
{
"service": "elasticsearch",
"service_settings": {
"num_allocations": 1,
"num_threads": 1,
"model_id": ".multilingual-e5-small_linux-x86_64"
},
"chunking_settings": {
"strategy": "sentence",
"max_chunk_size": 40,
"sentence_overlap": 0
}
}这里我们使用了40个单词的最大块大小,并且块之间没有重叠。句子策略将文本在自然句子边界处拆分。
确保已部署多语言e5小型模型。该模型的最大令牌窗口为512。
PUT chunking_test
{
"mappings": {
"properties": {
"my-semantic-field": {
"type": "semantic_text",
"inference_id": "my-embedding-model"
}
}
}
}当您索引文档时,Elasticsearch会自动应用分块策略,并为my-semantic-field字段中的每个块创建嵌入。
PUT chunking_test/_doc/1
{
"my-semantic-field": "制作完美咖啡需要注意几个关键因素。水温应在195-205°F之间以获得最佳提取效果。研磨大小显著影响酿造时间和风味强度。使用1:15的咖啡与水比例适用于大多数酿造方法。酿造时间因方法而异:手冲需要3-4分钟,而意式浓缩需要25-30秒。新鲜咖啡豆在两周内烘焙可以产生最佳效果。"
}该文档将根据我们的句子策略自动拆分为多个块。
GET /chunking_test/_search
{
"fields": ["_inference_fields"],
"query": {
"semantic": {
"field": "my-semantic-field",
"query": "我应该酿造咖啡多长时间?"
}
},
"highlight": {
"fields": {
"my-semantic-field": {
"order": "score",
"number_of_fragments": 1
}
}
}
}让我们逐步看看查询的每一部分:
"fields": ["_inference_fields"],在这里,我们要求返回标题和推理字段;这些字段包括关于端点、模型和块的信息。
"query": {
"semantic": {
"field": "my-semantic-field",
"query": "我应该酿造咖啡多长时间?"
}
}这是对my-semantic-field的语义查询,它自动使用我们在步骤1中创建的推理端点来搜索与用户查询中的术语匹配的内容。
"highlight": {
"fields": {
"my-semantic-field": {
"order": "score",
"number_of_fragments": 1
}
}
}这部分将返回从文本生成的最高得分块。
现在的响应:
"hits": [
{
"_index": "chunking_test",
"_id": "1",
"_score": 0.9537368,
"_source": {
"my-semantic-field": "制作完美咖啡需要注意几个关键因素。水温应在195-205°F之间以获得最佳提取效果。研磨大小显著影响酿造时间和风味强度。使用1:15的咖啡与水比例适用于大多数酿造方法。酿造时间因方法而异:手冲需要3-4分钟,而意式浓缩需要25-30秒。新鲜咖啡豆在两周内烘焙可以产生最佳效果。",
"_inference_fields": {
"my-semantic-field": {
"inference": {
"inference_id": "my-embedding-model",
"model_settings": {
"service": "elasticsearch",
"task_type": "text_embedding",
"dimensions": 384,
"similarity": "cosine",
"element_type": "float"
},
"chunks": {
"my-semantic-field": [
{
"start_offset": 0,
"end_offset": 135,
"embeddings": [
-0.047878783,
...
0.02849774
]
},
{
"start_offset": 135,
"end_offset": 261,
"embeddings": [
-0.019347992,
...
0.046932716
]
},
{
"start_offset": 261,
"end_offset": 356,
"embeddings": [
-0.021673936,
...
0.03294023
]
},
{
"start_offset": 356,
"end_offset": 418,
"embeddings": [
0.027161874,
...
0.033048477
]
}
]
}
}
}
}
},
"highlight": {
"my-semantic-field": [
"酿造时间因方法而异:手冲需要3-4分钟,而意式浓缩需要25-30秒。"
]
}
}
]让我们分解这个响应:
原始内容:
"_source": {
"my-semantic-field": "制作完美咖啡需要注意几个关键因素..."
}这是索引的完整原始文本。
分块详情:
"_inference_fields": {
"my-semantic-field": {
"inference": {
"inference_id": "my-embedding-model",
"chunks": {
"my-semantic-field": [
{
"start_offset": 0,
"end_offset": 135,
"embeddings": [
-0.047878783,
...
0.02849774
]
},
{
"start_offset": 135,
"end_offset": 261,
"embeddings": [
-0.019347992,
...
0.046932716
]
},
{
"start_offset": 261,
"end_offset": 356,
"embeddings": [
-0.021673936,
...
0.03294023
]
},
{
"start_offset": 356,
"end_offset": 418,
"embeddings": [
```javascript
0.027161874,
...
0.033048477
]
}
]
}
}
}
}这显示了:
最佳匹配块:
"highlight": {
"my-semantic-field": [
"酿造时间因方法而异:手冲需要3-4分钟,而意式浓缩需要25-30秒。"
]
}高亮显示了哪个特定块在我们的“我应该酿造咖啡多长时间?”查询中得分最高,展示了语义搜索如何找到最相关的内容,即使精确的词语并不匹配。
我们将使用美洲一些国家的维基百科页面。这些页面包含长文本,使我们可以展示分块与无分块策略的区别。我们准备了一个仓库来获取维基百科内容,创建适当的推理端点和映射以上传数据。
git clone https://github.com/Alex1795/embeddings_chunking_strategies_blog.git
cd embeddings_chunking_strategies_blogpip install -r requirements.txtexport ES_HOST="your-elasticsearch-endpoint"
export ES_API_KEY="your-api-key"python set_up.py这个脚本执行以下步骤:
这可能需要几分钟;请记住,Elastic正在语义文本字段上进行分块和生成嵌入。如果一切顺利,您将看到进程正确完成:


脚本run_semantic_search.py实现了几个辅助函数,在两个语义文本字段上执行相同的语义搜索,并以表格形式打印结果以便于比较。您可以简单地执行脚本,如下所示:
python run_semantic_search.py您将看到demo_queries列表中定义的每个查询的结果(在此处添加您自己的查询以进行测试):
demo_queries = [
"inca帝国的国家",
"咖啡生产",
"石油和石油出口",
"海滩目的地",
"曲棍球"
]让我们探索demo中的查询结果:
查询:inca帝国的国家
策略:句子分块

我们得到了预期的结果:秘鲁、厄瓜多尔、玻利维亚、智利、阿根廷和相关的块。例如,对于阿根廷,我们得到了一个非常具体的信息:
"在西北部的先进Diaguita定居贸易文化,约在1480年被Inca帝国征服"正如我们所见,我们可以很容易地确定每个文档为什么出现在结果中。
查询:inca帝国的国家
策略:无分块

在这里,我们可以看到相同的前四个结果,但最后我们看到墨西哥,墨西哥在任何方面都不是Incan帝国的一部分。我们还可以看到整个文档被接收到作为一个相关块,所以我们无法真正确定这些文档为什么相关。这些结果的问题是,嵌入是从文本的前512个令牌中形成的,因此丢失了很多信息。很可能我们没有获得关于每个国家历史的信息。
查询:曲棍球
策略:句子分块

我们可以看到与曲棍球直接相关的国家,如美国和加拿大,还有阿根廷、智利和巴拿马。在相关块列中,我们看到在每篇文章中如何提到曲棍球。
查询:曲棍球
策略:无分块

在这些结果中,我们可以看到一些通常与曲棍球(或冬季运动)无关的国家。由于相关文本是整篇文章,我们无法真正看出为什么这些出现在结果中。此外,请注意分数比之前低得多;这表明这些可能根本不是好结果,这些只是与查询“最接近”的,但几乎没有相关性。
正如我们所见,使用分块策略可以提升结果质量。没有分块,我们会遇到如下问题:
总而言之,处理比模型令牌限制更长的文本时,分块策略始终是个好主意。它改善结果并降低成本,是设计AI系统时的基础环节。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。