使用嵌入相似度进行语义分块
实现语义分块,在连续句子之间语义距离最大的地方切分文本,同时将主题一致的内容保留在一起。
使用嵌入相似度进行语义分块 是 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 反馈 — 无需本地设置。
此课程中的所有课时
- 朴素分块为何会损害检索效果
- 使用嵌入相似度进行语义分块
- 父子分块与由小到大的检索
- 针对代码和 HTML 的文档专用策略