AI Engineering Academy · 课时

使用嵌入相似度进行语义分块

实现语义分块,在连续句子之间语义距离最大的地方切分文本,同时将主题一致的内容保留在一起。

第 2 / 4 课13 个步骤

使用嵌入相似度进行语义分块 是 CoddyKit 上的免费 AI Engineering Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 AI Engineering Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 AI Engineering Academy 课程共包含 4 节课。

什么是语义分块?

语义分块是一种在主题发生显著变化的位置切分文本的技术,而不是按照固定的字符数切分。它不再问“我们是否已经达到 500 个标记?”,而是利用嵌入相似度回答“下一个句子是否与当前分块属于同一主题?”

核心思路:嵌入距离

该算法会为每个句子(或一小组句子)生成嵌入,并计算相邻句子嵌入之间的余弦相似度。当相似度急剧下降——意味着主题发生了转变——算法就会插入分块边界。讨论同一概念的句子会保留在同一个分块中。

第 1 步:句子级嵌入

第一步是使用句子分词器将文档拆分成单独的句子,然后使用快速嵌入模型为每个句子生成嵌入。您需要的是句子级嵌入,而不是文档级嵌入,这样才能在遍历文本时检测局部主题变化。

from openai import OpenAI
from nltk.tokenize import sent_tokenize
import numpy as np

client = OpenAI()

def embed_sentences(text):
    sentences = sent_tokenize(text)
    response = client.embeddings.create(
        model='text-embedding-3-small',
        input=sentences
    )
    vectors = [item.embedding for item in response.data]
    return sentences, np.array(vectors)

第 2 步:计算相邻相似度

获得句子嵌入后,计算每一对相邻句子之间的余弦相似度:句子 i 和句子 i+1。结果是一个相似度分数列表,每个句子边界对应一个分数。较低的分数表示相邻句子讨论的是不同主题,这些位置就是候选切分点。

def cosine_similarity_adjacent(vectors):
    similarities = []
    for i in range(len(vectors) - 1):
        a = vectors[i]
        b = vectors[i + 1]
        sim = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
        similarities.append(sim)
    return similarities

第 3 步:检测断点

断点是相似度低于某个阈值的句子边界。您可以使用固定阈值(例如 0.6),也可以使用基于百分位数的阈值,让阈值适应文档内容——例如,只要相似度低于该文档所有相似度分数的第 25 百分位数,就进行切分。

def find_breakpoints(similarities, percentile=25):
    threshold = np.percentile(similarities, percentile)
    breakpoints = []
    for i, sim in enumerate(similarities):
        if sim < threshold:
            breakpoints.append(i + 1)  # split AFTER sentence i
    return breakpoints

第 4 步:组装分块

确定断点后,您就可以将每个断点之间的连续句子连接起来,组装成分块。每个生成的分块都包含一组围绕同一主题、语义连贯的句子。分块边界与原始文档中自然的主题转换相一致。

def assemble_chunks(sentences, breakpoints):
    chunks = []
    start = 0
    for bp in breakpoints:
        chunk = ' '.join(sentences[start:bp])
        chunks.append(chunk)
        start = bp
    chunks.append(' '.join(sentences[start:]))  # last chunk
    return chunks

完整的语义分块器示例

将所有步骤整合到一个函数中:为句子生成嵌入、计算相邻相似度、查找断点、组装分块。输出是一个语义连贯的文本片段列表,可以作为完整分块生成嵌入并存储到向量数据库中。

def semantic_chunk(text, percentile=25):
    sentences, vectors = embed_sentences(text)
    similarities = cosine_similarity_adjacent(vectors)
    breakpoints = find_breakpoints(similarities, percentile)
    chunks = assemble_chunks(sentences, breakpoints)
    return chunks

chunks = semantic_chunk(my_document)
print(f'Produced {len(chunks)} semantic chunks')
for i, c in enumerate(chunks):
    print(f'Chunk {i+1}: {len(c)} chars')

选择百分位数阈值

百分位数阈值控制分块的粒度。较低的百分位数(例如第 10 百分位数)意味着只在主题发生重大变化时切分,从而生成更少、更长的分块。较高的百分位数(例如第 40 百分位数)会进行更积极的切分,从而生成许多更小、更聚焦的分块。请根据检索命中率评估集对其进行调节。

LangChain 语义分块器

LangChain 提供了内置的 SemanticChunker,用于实现该算法。它接受一个嵌入模型和一种断点阈值类型。这让您无需从头实现算法,并且可以直接与 LangChain 文档加载器和向量存储集成。

from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model='text-embedding-3-small')

chunker = SemanticChunker(
    embeddings,
    breakpoint_threshold_type='percentile',
    breakpoint_threshold_amount=25
)

chunks = chunker.create_documents([long_document_text])
print(f'{len(chunks)} semantic chunks created')

与固定大小分块的权衡

语义分块能够生成质量更高、主题连贯性更好的分块,但成本也更高:为了确定分块边界,必须为每个句子生成嵌入,而此时分块甚至还没有建立索引。对于一份 100 页的文档,这意味着仅分块就要调用嵌入模型数千次。当检索质量比建立索引的速度更重要时,请使用语义分块。

何时使用语义分块

语义分块非常适合长篇叙事内容,例如博客文章、研究论文、法律文档和书籍,因为这些内容的主题会自然转换。对于产品目录、FAQ 列表或代码文件等高度结构化的文档,语义分块就不那么必要了;这类文档更适合使用直接遵循其结构的文档感知分割器。

快速检查

测试您对本课中语义分块的理解。

课程回顾

在本课中,您了解到:语义分块利用嵌入相似度检测主题变化,百分位数阈值控制分块粒度,并且LangChain 的 SemanticChunker 开箱即用地实现了这一功能。接下来我们将探索父子分块,它将小而精确的分块与上下文丰富的大型父级段落结合起来。

免费开始

用 AI 导师学习 Python — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
30
课程
120

常见问题解答

「使用嵌入相似度进行语义分块」课时是免费的吗?

是的 — 「使用嵌入相似度进行语义分块」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 AI Engineering Academy 课程的其余内容,请升级到 CoddyKit PRO。 AI Engineering Academy 课程共包含 4 节课。

「使用嵌入相似度进行语义分块」这节课中我会学到什么?

实现语义分块,在连续句子之间语义距离最大的地方切分文本,同时将主题一致的内容保留在一起。 你通过在浏览器中直接运行的动手代码来练习 AI Engineering Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 AI Engineering Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 AI Engineering Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「使用嵌入相似度进行语义分块」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 AI Engineering Academy 课中编写并运行代码吗?

能。每节 AI Engineering Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 朴素分块为何会损害检索效果
  2. 使用嵌入相似度进行语义分块
  3. 父子分块与由小到大的检索
  4. 针对代码和 HTML 的文档专用策略
← 返回 AI Engineering Academy