Chunking:基于大模型RAG系統(tǒng)中的文檔分塊
共 6050字,需瀏覽 13分鐘
·
2024-08-04 20:24
【引】“枯萎,無法回避,如人之生老病死;荒蕪,無法接受,如碌碌無為一生。” 這是周六回鄉(xiāng)下除草的感受。有所得,有所感,對工程技術(shù)也是如此。
將大文檔分割成較小的分塊是一項關(guān)鍵而復雜的任務(wù),對RAG系統(tǒng)的性能有著重大的影響。一般地,RAG系統(tǒng)旨在通過將基于檢索的方法和基于生成的方法相結(jié)合,提高產(chǎn)出的質(zhì)量和相關(guān)性。有多種框架提供了文檔分塊方法,每種方法都有自己的優(yōu)點和典型用例。或許,利用主題感知的句子嵌入來識別文檔中的主題變更,確保每個塊封裝一個主題會是一種不錯的選擇。
1.回顧RAG
RAG系統(tǒng)是一個復雜的機器學習模型,它融合了基于檢索的技術(shù)和生成式AI。RAG 系統(tǒng)的主要目標是通過合并從數(shù)據(jù)集中檢索的信息來提高生成內(nèi)容的質(zhì)量和相關(guān)性。回顧一下 RAG 系統(tǒng)的工作原理:
檢索階段: 系統(tǒng)首先根據(jù)輸入查詢檢索相關(guān)文檔或信息。這個階段依賴于搜索算法和索引方法來快速識別大量集合中最相關(guān)的數(shù)據(jù)。
生成階段: 一旦檢索到相關(guān)文檔,就會使用一個通常是基于transformer的大語言模型,如 GPT-4來創(chuàng)建一個連貫的、與上下文相適應(yīng)的響應(yīng)。此模型使用檢索到的信息來確保生成的內(nèi)容是準確的,而且信息豐富。
RAG 系統(tǒng)的混合特性使它們對于知識密集型任務(wù)特別有效,在這些任務(wù)中,檢索和生成的結(jié)合極大地提高了總體性能。關(guān)于RAG 的更多信息,可以參考《大模型系列——解讀RAG》和《RAG的10篇論文-2024Q1》。
2. 常見的文本分塊技術(shù)
文本分塊是許多自然語言處理任務(wù)的基礎(chǔ)步驟,可以采用多種技術(shù)來確保分塊方式保留了語義和上下文。根據(jù)任務(wù)的具體要求,可以以多種方式來實現(xiàn)文本分塊,下面是針對不同需求分塊方法:
2.1 按字符分塊
此方法將文本分解為單個字符。它適用于需要細粒度文本分析的任務(wù),例如字符級語言模型或某些類型的文本預處理。
2.2 按Token分塊
將文本分割成token,是自然語言處理中的一種標準方法。基于令牌的組塊對于文本分類、語言建模和其他依賴于token化輸入的 NLP 應(yīng)用程序等任務(wù)來說是必不可少的。
2.3 按段落分塊
按段落分段整理文本有助于維護文檔的整體結(jié)構(gòu)和流程。此方法適用于需要較大上下文的任務(wù),如文檔摘要或內(nèi)容提取。
2.4 遞歸分塊
這涉及到重復地將數(shù)據(jù)分解成更小的塊,通常用于分層數(shù)據(jù)結(jié)構(gòu)。遞歸組塊有利于需要多級分析的任務(wù),如主題建模或?qū)哟尉垲悺?/span>
2.5 語義分塊
根據(jù)意義而非結(jié)構(gòu)元素對文本進行分組對于需要理解數(shù)據(jù)上下文的任務(wù)至關(guān)重要。語義塊利用諸如句子嵌入等技術(shù)來確保每個塊代表一個連貫的主題或想法。
2.6 代理分塊
這種方法的重點是在識別和分組文本的基礎(chǔ)上增加參與的代理,如人或組織。它在信息抽取和實體識別任務(wù)中非常有用,因為理解不同實體之間的角色和關(guān)系非常重要。
3.基于Langchain的文本分塊技術(shù)——5行代碼
Langchain 框架中提供了很多可以開箱即用的技術(shù),常見的文本分塊技術(shù)如下:
遞歸字符分塊
token分塊
句子分塊
正則分塊
Markdown分塊
3.1 遞歸字符文本分塊
此方法基于字符數(shù)來遞歸地分割文本。每個塊都保持在指定的長度以下,這對于具有自然段落或句子間斷的文檔特別有用,確保了塊的可管理性和易于處理性,而不會丟失文檔的固有結(jié)構(gòu)。
Langchain中的遞歸字符文本分割器方法根據(jù)字符數(shù)將文本分割成塊,以確保每個塊低于指定的長度。這種方法有助于保持文檔中段落或句子的自然斷開。
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = " long document text here..."
# 初始化 RecursiveCharacterTextSplitter,塊大小1k字符以及50個跨文本字符
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限制的大語言模型時,它確保了每個塊都符合模型的約束。在自然語言處理任務(wù)中,通常使用基于token分塊來保持文本的完整性,同時遵守模型的限制。
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 句子分塊
通過在句子邊界上分割文本,保持了文本的上下文完整性。句子通常代表完整的思想,這使得這種方法非常適合那些對內(nèi)容有連貫理解的場景。
from langchain.text_splitter import SentenceSplitter
text = "long document text ..."
# 初始化SentenceSplitter ,每個塊最多5個句子
splitter = SentenceSplitter(max_length=5)
chunks = splitter.split_text(text)
for chunk in chunks:
print(chunk)
3.4 正則分塊
此方法使用正則表達式來自定義拆分點。它為各種用例提供了最高的靈活性,允許用戶根據(jù)特定于他們的用例模式來拆分文檔。例如,可以在特定關(guān)鍵字或標點符號的每個實例上文檔拆分。
from langchain.text_splitter import RegexSplitter
# Example long document text
text = "Your long document text goes here..."
# 用一個模式初始化 RegexSplitter,以雙換行符分割文本
splitter = RegexSplitter(pattern=r'\n\n+')
chunks = splitter.split_text(text)
for chunk in chunks:
print(chunk)
3.5 Markdown 的文檔分塊
該方法專為 markdown文檔定制,根據(jù)特定元素(如標題、列表和代碼塊)分割文本,保留了標記文檔的結(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ù)
大型文檔,如學術(shù)論文、長篇報告和詳細文章,通常包含多個主題。langchain中的分割技術(shù),都難以準確識別主題轉(zhuǎn)換點。這些方法經(jīng)常會錯過細微的轉(zhuǎn)換或錯誤地識別它們,導致分塊重疊。
面向主題的分塊技術(shù)旨在使用句子嵌入來識別文檔中主題的變化。通過標識主題轉(zhuǎn)移的位置,確保每個塊封裝一個單一的、連貫的主題,具體包括:
句子嵌入: 句子嵌入將句子轉(zhuǎn)換成高維向量,從而捕捉句子的語義。通過分析這些向量,我們可以確定主題變化的點。
主題檢測: 使用為主題建模的相關(guān)算法,檢測主題的變化并確定分割文檔的最佳點。這確保了每個塊在主題上是一致的。
增強的檢索和嵌入: 通過確保每個塊代表一個主題,RAG 系統(tǒng)中的檢索和嵌入步驟變得更加有效。每個塊的嵌入更有意義,從而提高檢索性能和響應(yīng)的準確性。
這種技術(shù)已經(jīng)在過去主題建模的場景下得到了證明,但是它同樣適用于 RAG 系統(tǒng)。通過采用這種方法,RAG 系統(tǒng)可以在其生成的內(nèi)容中實現(xiàn)更高的準確性和相關(guān)性,使其更有效地完成復雜和知識密集型的任務(wù)。
4.1 生成句子嵌入
可以使用Sentence-BERT (SBERT) 為單個句子生成嵌入,這些嵌入是密集的向量表示,封裝了句子的語義內(nèi)容,使我們能夠衡量它們的相似性。
from sentence_transformers import SentenceTransformer
sentences = ["Sentence 1...", "Sentence 2...", ...]
model = SentenceTransformer('paraphrase-MiniLM-L6-v2')
embeddings = model.encode(sentences)
4.2 計算相似度
句子之間的相似度是通過余弦距離或者其他距離度量來衡量的,比如曼哈頓或者歐氏距離。這有助于識別連續(xù)句之間的連貫性。
from sklearn.metrics.pairwise import cosine_similarity
similarity_matrix = cosine_similarity(embeddings)
4.3. 差異評分
為了檢測主題轉(zhuǎn)換,我們定義了一個參數(shù) n,指定要比較的句子數(shù)。該算法根據(jù)余弦距離計算差距得分。
import numpy as np
#定義參數(shù)
n = 2
# 計算差異評分
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))
為了解決差異分數(shù)中的噪聲,可以采用平滑算法,窗口大小 k 決定了平滑的程度。
# 定義窗口大小 k
k = 3
# 平滑差異評分
smoothed_gap_scores = np.convolve(gap_scores, np.ones(k)/k, mode='valid')
4.4 邊界檢測
通過分析平滑后的差距得分來識別局部極小值,這表明潛在的話題轉(zhuǎn)換,可以用閾值來確定重要的邊界。
# 檢測本地極小值
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 分段的聚類
對于較長的文檔,類似的主題可能會重新出現(xiàn)。為了處理這個問題,使用類似的內(nèi)容聚類算法,可以減少冗余并確保每個主題都是唯一表示的。
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)
這里的代碼只是示意, 還可以通過自動參數(shù)優(yōu)化、采用 transformer 模型、基于知識圖譜的層次分類等方法來進一步增強面向主題感知的分塊技術(shù)。
5.一句話小結(jié)
在RAG系統(tǒng)中, 文本分塊技術(shù)是必不可少的。對于大型文檔而言,可以嘗試采用面向主題感知的句子嵌入來提升RAG 系統(tǒng)的性能,使其生成更相關(guān)且一致的內(nèi)容。
【關(guān)聯(lián)閱讀】
