將大文檔分割成較小的分塊是一項(xiàng)關(guān)鍵而復(fù)雜的任務(wù),對(duì)RAG系統(tǒng)的性能有著重大的影響。一般地,RAG系統(tǒng)旨在通過(guò)將基于檢索的方法和基于生成的方法相結(jié)合,提高產(chǎn)出的質(zhì)量和相關(guān)性。有多種框架提供了文檔分塊方法,每種方法都有自己的優(yōu)點(diǎn)和典型用例?;蛟S,利用主題感知的句子嵌入來(lái)識(shí)別文檔中的主題變更,確保每個(gè)塊封裝一個(gè)主題會(huì)是一種不錯(cuò)的選擇。
1.回顧RAG
RAG系統(tǒng)是一個(gè)復(fù)雜的機(jī)器學(xué)習(xí)模型,它融合了基于檢索的技術(shù)和生成式AI。RAG 系統(tǒng)的主要目標(biāo)是通過(guò)合并從數(shù)據(jù)集中檢索的信息來(lái)提高生成內(nèi)容的質(zhì)量和相關(guān)性?;仡櫼幌?RAG 系統(tǒng)的工作原理:
- 檢索階段: 系統(tǒng)首先根據(jù)輸入查詢檢索相關(guān)文檔或信息。這個(gè)階段依賴于搜索算法和索引方法來(lái)快速識(shí)別大量集合中最相關(guān)的數(shù)據(jù)。
- 生成階段: 一旦檢索到相關(guān)文檔,就會(huì)使用一個(gè)通常是基于transformer的大語(yǔ)言模型,如 GPT-4來(lái)創(chuàng)建一個(gè)連貫的、與上下文相適應(yīng)的響應(yīng)。此模型使用檢索到的信息來(lái)確保生成的內(nèi)容是準(zhǔn)確的,而且信息豐富。
RAG 系統(tǒng)的混合特性使它們對(duì)于知識(shí)密集型任務(wù)特別有效,在這些任務(wù)中,檢索和生成的結(jié)合極大地提高了總體性能。
2. 常見(jiàn)的文本分塊技術(shù)
文本分塊是許多自然語(yǔ)言處理任務(wù)的基礎(chǔ)步驟,可以采用多種技術(shù)來(lái)確保分塊方式保留了語(yǔ)義和上下文。根據(jù)任務(wù)的具體要求,可以以多種方式來(lái)實(shí)現(xiàn)文本分塊,下面是針對(duì)不同需求分塊方法:
2.1 按字符分塊
此方法將文本分解為單個(gè)字符。它適用于需要細(xì)粒度文本分析的任務(wù),例如字符級(jí)語(yǔ)言模型或某些類型的文本預(yù)處理。
2.2 按Token分塊
將文本分割成token,是自然語(yǔ)言處理中的一種標(biāo)準(zhǔn)方法?;诹钆频慕M塊對(duì)于文本分類、語(yǔ)言建模和其他依賴于token化輸入的 NLP 應(yīng)用程序等任務(wù)來(lái)說(shuō)是必不可少的。
2.3 按段落分塊
按段落分段整理文本有助于維護(hù)文檔的整體結(jié)構(gòu)和流程。此方法適用于需要較大上下文的任務(wù),如文檔摘要或內(nèi)容提取。
2.4 遞歸分塊
這涉及到重復(fù)地將數(shù)據(jù)分解成更小的塊,通常用于分層數(shù)據(jù)結(jié)構(gòu)。遞歸組塊有利于需要多級(jí)分析的任務(wù),如主題建?;?qū)哟尉垲悺?/span>
2.5 語(yǔ)義分塊
根據(jù)意義而非結(jié)構(gòu)元素對(duì)文本進(jìn)行分組對(duì)于需要理解數(shù)據(jù)上下文的任務(wù)至關(guān)重要。語(yǔ)義塊利用諸如句子嵌入等技術(shù)來(lái)確保每個(gè)塊代表一個(gè)連貫的主題或想法。
2.6 代理分塊
這種方法的重點(diǎn)是在識(shí)別和分組文本的基礎(chǔ)上增加參與的代理,如人或組織。它在信息抽取和實(shí)體識(shí)別任務(wù)中非常有用,因?yàn)槔斫獠煌瑢?shí)體之間的角色和關(guān)系非常重要。
3.基于Langchain的文本分塊技術(shù)——5行代碼
Langchain 框架中提供了很多可以開箱即用的技術(shù),常見(jiàn)的文本分塊技術(shù)如下:
- 遞歸字符分塊
- token分塊
- 句子分塊
- 正則分塊
- Markdown分塊
3.1 遞歸字符文本分塊
此方法基于字符數(shù)來(lái)遞歸地分割文本。每個(gè)塊都保持在指定的長(zhǎng)度以下,這對(duì)于具有自然段落或句子間斷的文檔特別有用,確保了塊的可管理性和易于處理性,而不會(huì)丟失文檔的固有結(jié)構(gòu)。
Langchain中的遞歸字符文本分割器方法根據(jù)字符數(shù)將文本分割成塊,以確保每個(gè)塊低于指定的長(zhǎng)度。這種方法有助于保持文檔中段落或句子的自然斷開。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = " long document text here..."
# 初始化 RecursiveCharacterTextSplitter,塊大小1k字符以及50個(gè)跨文本字符
charSplitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=50)
# 分塊
chunks = charSplitter.split_text(text)
# 打印輸出
for chunk in chunks:
print(chunk)
3.2 Token文本分塊
這種技術(shù)使用token劃分文檔,token可以是單詞或詞元。在處理具有token限制的大語(yǔ)言模型時(shí),它確保了每個(gè)塊都符合模型的約束。在自然語(yǔ)言處理任務(wù)中,通常使用基于token分塊來(lái)保持文本的完整性,同時(shí)遵守模型的限制。
from langchain.text_splitter import TokenSplitter
text = " long document text ..."
# 初始化TokenSplitter,最大token限制為 512
splitter = TokenSplitter(max_tokens=512)
chunks = splitter.split_text(text)
for chunk in chunks:
print(chunk)
3.3 句子分塊
通過(guò)在句子邊界上分割文本,保持了文本的上下文完整性。句子通常代表完整的思想,這使得這種方法非常適合那些對(duì)內(nèi)容有連貫理解的場(chǎng)景。
from langchain.text_splitter import SentenceSplitter
text = "long document text ..."
# 初始化SentenceSplitter ,每個(gè)塊最多5個(gè)句子
splitter = SentenceSplitter(max_length=5)
chunks = splitter.split_text(text)
for chunk in chunks:
print(chunk)
3.4 正則分塊
此方法使用正則表達(dá)式來(lái)自定義拆分點(diǎn)。它為各種用例提供了最高的靈活性,允許用戶根據(jù)特定于他們的用例模式來(lái)拆分文檔。例如,可以在特定關(guān)鍵字或標(biāo)點(diǎn)符號(hào)的每個(gè)實(shí)例上文檔拆分。
from langchain.text_splitter import RegexSplitter
# Example long document text
text = "Your long document text goes here..."
# 用一個(gè)模式初始化 RegexSplitter,以雙換行符分割文本
splitter = RegexSplitter(pattern=r'\n\n+')
chunks = splitter.split_text(text)
for chunk in chunks:
print(chunk)
3.5 Markdown 的文檔分塊
該方法專為 markdown文檔定制,根據(jù)特定元素(如標(biāo)題、列表和代碼塊)分割文本,保留了標(biāo)記文檔的結(jié)構(gòu)和格式,使其適合于技術(shù)文檔和內(nèi)容管理。
from langchain.text_splitter import MarkdownSplitter
text = "long markdown document..."
splitter = MarkdownSplitter()
chunks = splitter.split_text(text)
for chunk in chunks:
print(chunk)
4. 面向主題的分塊技術(shù)
大型文檔,如學(xué)術(shù)論文、長(zhǎng)篇報(bào)告和詳細(xì)文章,通常包含多個(gè)主題。langchain中的分割技術(shù),都難以準(zhǔn)確識(shí)別主題轉(zhuǎn)換點(diǎn)。這些方法經(jīng)常會(huì)錯(cuò)過(guò)細(xì)微的轉(zhuǎn)換或錯(cuò)誤地識(shí)別它們,導(dǎo)致分塊重疊。
面向主題的分塊技術(shù)旨在使用句子嵌入來(lái)識(shí)別文檔中主題的變化。通過(guò)標(biāo)識(shí)主題轉(zhuǎn)移的位置,確保每個(gè)塊封裝一個(gè)單一的、連貫的主題,具體包括:
- 句子嵌入: 句子嵌入將句子轉(zhuǎn)換成高維向量,從而捕捉句子的語(yǔ)義。通過(guò)分析這些向量,我們可以確定主題變化的點(diǎn)。
- 主題檢測(cè): 使用為主題建模的相關(guān)算法,檢測(cè)主題的變化并確定分割文檔的最佳點(diǎn)。這確保了每個(gè)塊在主題上是一致的。
- 增強(qiáng)的檢索和嵌入: 通過(guò)確保每個(gè)塊代表一個(gè)主題,RAG 系統(tǒng)中的檢索和嵌入步驟變得更加有效。每個(gè)塊的嵌入更有意義,從而提高檢索性能和響應(yīng)的準(zhǔn)確性。
這種技術(shù)已經(jīng)在過(guò)去主題建模的場(chǎng)景下得到了證明,但是它同樣適用于 RAG 系統(tǒng)。通過(guò)采用這種方法,RAG 系統(tǒng)可以在其生成的內(nèi)容中實(shí)現(xiàn)更高的準(zhǔn)確性和相關(guān)性,使其更有效地完成復(fù)雜和知識(shí)密集型的任務(wù)。
4.1 生成句子嵌入
可以使用Sentence-BERT (SBERT) 為單個(gè)句子生成嵌入,這些嵌入是密集的向量表示,封裝了句子的語(yǔ)義內(nèi)容,使我們能夠衡量它們的相似性。
from sentence_transformers import SentenceTransformer
sentences = ["Sentence 1...", "Sentence 2...", ...]
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
embeddings = model.encode(sentences)
4.2 計(jì)算相似度
句子之間的相似度是通過(guò)余弦距離或者其他距離度量來(lái)衡量的,比如曼哈頓或者歐氏距離。這有助于識(shí)別連續(xù)句之間的連貫性。
from sklearn.metrics.pairwise import cosine_similarity
similarity_matrix = cosine_similarity(embeddings)
4.3 差異評(píng)分
為了檢測(cè)主題轉(zhuǎn)換,我們定義了一個(gè)參數(shù) n,指定要比較的句子數(shù)。該算法根據(jù)余弦距離計(jì)算差距得分。
import numpy as np
#定義參數(shù)
n = 2
# 計(jì)算差異評(píng)分
gap_scores = []
for i in range(len(embeddings) - n):
similarity = cosine_similarity(embeddings[i:i+n], embeddings[i+n:i+2*n])
gap_scores.append(np.mean(similarity))
為了解決差異分?jǐn)?shù)中的噪聲,可以采用平滑算法,窗口大小 k 決定了平滑的程度。
# 定義窗口大小 k
k = 3
# 平滑差異評(píng)分
smoothed_gap_scores = np.convolve(gap_scores, np.ones(k)/k, mode='valid')
4.4 邊界檢測(cè)
通過(guò)分析平滑后的差距得分來(lái)識(shí)別局部極小值,這表明潛在的話題轉(zhuǎn)換,可以用閾值來(lái)確定重要的邊界。
# 檢測(cè)本地極小值
local_minima = (np.diff(np.sign(np.diff(smoothed_gap_scores))) > 0).nonzero()[0] + 1
# 設(shè)置閾值 c
C = 1.5
# 確定顯著的界限
significant_boundaries = [i for i in local_minima if smoothed_gap_scores[i] < np.mean(smoothed_gap_scores) - c * np.std(smoothed_gap_scores)]
4.5 分段的聚類
對(duì)于較長(zhǎng)的文檔,類似的主題可能會(huì)重新出現(xiàn)。為了處理這個(gè)問(wèn)題,使用類似的內(nèi)容聚類算法,可以減少冗余并確保每個(gè)主題都是唯一表示的。
from sklearn.cluster import KMeans
# 轉(zhuǎn)化為embedding
segment_embeddings = [np.mean(embeddings[start:end], axis=0) for start, end in zip(significant_boundaries[:-1], significant_boundaries[1:])]
# Kmeans 聚類示例
kmeans = KMeans(n_clusters=5)
clusters = kmeans.fit_predict(segment_embeddings)
這里的代碼只是示意, 還可以通過(guò)自動(dòng)參數(shù)優(yōu)化、采用 transformer 模型、基于知識(shí)圖譜的層次分類等方法來(lái)進(jìn)一步增強(qiáng)面向主題感知的分塊技術(shù)。
5.一句話小結(jié)
在RAG系統(tǒng)中, 文本分塊技術(shù)是必不可少的。對(duì)于大型文檔而言,可以嘗試采用面向主題感知的句子嵌入來(lái)提升RAG 系統(tǒng)的性能,使其生成更相關(guān)且一致的內(nèi)容。