基于文本結(jié)構(gòu)分塊 - 文本分塊(Text Splitting),RAG不可缺失的重要環(huán)節(jié)
在 RAG 的核心步驟中,有一個(gè)至關(guān)重要的步驟:“文本分塊(Text Splitting)”。
它的主要作用就是把一大段文本切分成更小、更合理的片段,這樣模型才能更好地理解、處理或者存儲(chǔ)這些內(nèi)容。
如果一整篇文章不拆開,那 embedding 的顆粒度太粗,問答的時(shí)候很容易不準(zhǔn)。所以切得好不好,直接影響最后答案的相關(guān)性和準(zhǔn)確性。
最基本的分塊方法是根據(jù)文檔的長度進(jìn)行拆分。這種簡(jiǎn)單而有效的方法確保每個(gè)塊不會(huì)超過指定的大小限制。
基于長度拆分的主要好處:簡(jiǎn)單明了的實(shí)現(xiàn)、一致的塊大小、易于適應(yīng)不同模型的要求。缺點(diǎn)就是: 過于死板,忽視文本結(jié)構(gòu)
1. 基于文本結(jié)構(gòu)分塊
一般的文本會(huì)自然地組織成層次單位,如段落、句子和詞。
我們可以利用這種固有結(jié)構(gòu)來指導(dǎo)我們的拆分策略,創(chuàng)建保持自然語言流暢、保持拆分內(nèi)的語義連貫,并適應(yīng)不同文本粒度水平的拆分。
LangChain 的 ??RecursiveCharacterTextSplitter?
? 實(shí)現(xiàn)了這個(gè)概念:
- ?
?RecursiveCharacterTextSplitter?
? 嘗試保持較大單位(例如段落)的完整性。 - 如果一個(gè)單位超出了塊大小,它將移到下一個(gè)層級(jí)(例如句子)。
- 如果必要,這個(gè)過程將繼續(xù)到單詞級(jí)別。
2. RecursiveCharacterTextSplitter的實(shí)現(xiàn)思路
2.1 挑選分隔符
- 從提供的分隔符列表中找到第一個(gè)在文本中存在的分隔符
- 如果找到合適的分隔符,將其后的所有分隔符保存起來,用于后續(xù)可能的遞歸分割
- 如果找不到任何分隔符,就使用最后一個(gè)分隔符(通常是空字符串)
舉個(gè)例子:
假設(shè)分隔符列表是 ["\n\n", "\n", " ", ""],對(duì)于文本 "Hello\nWorld" :
- 首先檢查 "\n\n" ,文本中不存在
- 然后檢查 "\n" ,文本中存在
- 選擇 "\n" 作為分隔符
- 保存 [" ", ""] 作為 new_separators ,供后續(xù)使用
separator = separators[-1]
new_separators = []
for i, _s in enumerate(separators):
_separator = _s if self._is_separator_regex else re.escape(_s)
if _s == "":
separator = _s
break
if re.search(_separator, text):
separator = _s
new_separators = separators[i + 1 :]
break
_separator = separator if self._is_separator_regex else re.escape(separator)
2.2 按分隔符分割文本
splits = _split_text_with_regex(text, _separator, self._keep_separator)
2.3 整理分割好的塊
- 對(duì)每個(gè)分割后的文本塊進(jìn)行處理:
- 如果文本塊小于指定大小,添加到臨時(shí)列表
- 如果文本塊大于指定大小,且還有其他分隔符可用,則遞歸分割
- 如果文本塊大于指定大小,但沒有其他分隔符,則直接添加
- 合并所有符合大小要求的文本塊
- 返回最終的分割結(jié)果
for s in splits:
if self._length_function(s) < self._chunk_size:
_good_splits.append(s)
else:
if _good_splits:
merged_text = self._merge_splits(_good_splits, _separator)
final_chunks.extend(merged_text)
_good_splits = []
ifnot new_separators:
final_chunks.append(s)
else:
other_info = self._split_text(s, new_separators)
final_chunks.extend(other_info)
if _good_splits:
merged_text = self._merge_splits(_good_splits, _separator)
final_chunks.extend(merged_text)
return final_chunks
3. 代碼實(shí)現(xiàn)
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = """
《誅仙》
作者:蕭鼎
第一集
序章
時(shí)間:不明,應(yīng)該在很早很早以前。
地點(diǎn):神州浩土。
自太古以來,人類眼見周遭世界,諸般奇異之事,電閃雷鳴,狂風(fēng)暴雨,又有天災(zāi)人禍,傷亡無數(shù),哀鴻遍野,絕非人力所能為,所能抵擋。遂以為九天之上,有諸般神靈,九幽之下,亦是陰魂歸處,閻羅殿堂。
于是神仙之說,流傳于世。無數(shù)人類子民,誠心叩拜,向著自己臆想創(chuàng)造出的各種神明頂禮膜拜,祈福訴苦,香火鼎盛。
自古以來,凡人無不有一死。但世人皆惡死愛生,更有地府閻羅之說,平添了幾分苦懼,在此之下,遂有長生不死之說。
相較其它生靈物種,人類或在體質(zhì)上處于劣勢(shì),但萬物靈長,卻是絕無虛言。在追求長生的原動(dòng)力下,一代代聰明才智之士,前赴后繼,投入畢生精力,苦苦鉆研。
至今為止,雖然真正意義上的長生不死仍未找到,卻有一些修真煉道之士參透些許天地造化,以凡人之身,掌握強(qiáng)橫力量,借助各般秘寶法器之力,竟可震撼天地,有雷霆之威。
而一些得道高深的前輩,更傳說已活上千年之久而不死。世上之人以為得道成仙,便有更多人投入修真煉道之路。
神州浩土,廣瀚無邊。唯有中原大地,最是豐美肥沃,天下人口十之八九聚居于此。而東南西北邊荒之地,山險(xiǎn)水惡,多兇獸猛禽,多惡瘴毒物,亦多蠻族夷民,茹毛飲血,是以人跡罕至。而人間自古相傳,有洪荒遺種,殘存人世,藏于深山密谷,壽逾萬年,卻是無人得見。
時(shí)至今日,人間修真煉道之人,多如過江之鯽,數(shù)不勝數(shù)。又以神州浩土之廣闊,人間奇人異士之多,故修煉之法道林林總總,俱不相同。長生之法還未找到,彼此間卻逐漸有了門派之分,正邪之別。由之而起的門戶之見,勾心斗角乃至爭(zhēng)伐殺戮,在所多有。
當(dāng)長生不死看起來那般遙遠(yuǎn)而不可捉摸,修煉中所帶來的力量,便逐漸成了許多人的目標(biāo)。
方今之世,正道大昌,邪魔退避。中原大地山靈水秀,人氣鼎盛,物產(chǎn)豐富,為正派諸家牢牢占據(jù)。其中尤以「青云門」、「天音寺」和「焚香谷」為三大支柱,是為領(lǐng)袖。
這個(gè)故事,便是從「青云門」開始的。
"""
text_splitter = RecursiveCharacterTextSplitter(chunk_size=150)
docs = text_splitter.create_documents([text])
for doc in docs:
print('-' * 50)
print(doc)
4. 拆分結(jié)果
通過觀察文本的分塊結(jié)果,可以看出 RecursiveCharacterTextSplitter 在 chunk_size=150 的設(shè)置下,將整個(gè)文本分成了7個(gè)完整的塊。
分割時(shí)優(yōu)先考慮了段落間的自然分隔(\n\n),使每個(gè)塊都保持了相對(duì)獨(dú)立的主題。
這種分塊方式既保證了每塊內(nèi)容的語義連貫性,又控制了文本長度在合理范圍內(nèi),為后續(xù)的文本處理和分析提供了良好的基礎(chǔ)。
--------------------------------------------------
page_cnotallow='《誅仙》
作者:蕭鼎
第一集
序章
時(shí)間:不明,應(yīng)該在很早很早以前。
地點(diǎn):神州浩土。
自太古以來,人類眼見周遭世界,諸般奇異之事,電閃雷鳴,狂風(fēng)暴雨,又有天災(zāi)人禍,傷亡無數(shù),哀鴻遍野,絕非人力所能為,所能抵擋。遂以為九天之上,有諸般神靈,九幽之下,亦是陰魂歸處,閻羅殿堂。'
--------------------------------------------------
page_cnotallow='于是神仙之說,流傳于世。無數(shù)人類子民,誠心叩拜,向著自己臆想創(chuàng)造出的各種神明頂禮膜拜,祈福訴苦,香火鼎盛。
自古以來,凡人無不有一死。但世人皆惡死愛生,更有地府閻羅之說,平添了幾分苦懼,在此之下,遂有長生不死之說。'
--------------------------------------------------
page_cnotallow='相較其它生靈物種,人類或在體質(zhì)上處于劣勢(shì),但萬物靈長,卻是絕無虛言。在追求長生的原動(dòng)力下,一代代聰明才智之士,前赴后繼,投入畢生精力,苦苦鉆研。'
--------------------------------------------------
page_cnotallow='至今為止,雖然真正意義上的長生不死仍未找到,卻有一些修真煉道之士參透些許天地造化,以凡人之身,掌握強(qiáng)橫力量,借助各般秘寶法器之力,竟可震撼天地,有雷霆之威。
而一些得道高深的前輩,更傳說已活上千年之久而不死。世上之人以為得道成仙,便有更多人投入修真煉道之路。'
--------------------------------------------------
page_cnotallow='神州浩土,廣瀚無邊。唯有中原大地,最是豐美肥沃,天下人口十之八九聚居于此。而東南西北邊荒之地,山險(xiǎn)水惡,多兇獸猛禽,多惡瘴毒物,亦多蠻族夷民,茹毛飲血,是以人跡罕至。而人間自古相傳,有洪荒遺種,殘存人世,藏于深山密谷,壽逾萬年,卻是無人得見。'
--------------------------------------------------
page_cnotallow='時(shí)至今日,人間修真煉道之人,多如過江之鯽,數(shù)不勝數(shù)。又以神州浩土之廣闊,人間奇人異士之多,故修煉之法道林林總總,俱不相同。長生之法還未找到,彼此間卻逐漸有了門派之分,正邪之別。由之而起的門戶之見,勾心斗角乃至爭(zhēng)伐殺戮,在所多有。'
--------------------------------------------------
page_cnotallow='當(dāng)長生不死看起來那般遙遠(yuǎn)而不可捉摸,修煉中所帶來的力量,便逐漸成了許多人的目標(biāo)。
方今之世,正道大昌,邪魔退避。中原大地山靈水秀,人氣鼎盛,物產(chǎn)豐富,為正派諸家牢牢占據(jù)。其中尤以「青云門」、「天音寺」和「焚香谷」為三大支柱,是為領(lǐng)袖。
這個(gè)故事,便是從「青云門」開始的。'
5. 圖形化顯示分塊
通過www.chunkviz.com可以以圖形化的方式看到分塊結(jié)果
總結(jié)
文本分塊不僅是技術(shù)實(shí)現(xiàn)的問題,更是影響 RAG 系統(tǒng)最終效果的核心策略。
簡(jiǎn)單分塊雖易上手但效果有限,結(jié)構(gòu)化遞歸分塊則在保留語義、提升相關(guān)性方面表現(xiàn)更優(yōu)。
想要構(gòu)建高質(zhì)量問答系統(tǒng),分塊方式絕不能隨便選,而是要結(jié)合文本特點(diǎn)和應(yīng)用場(chǎng)景精細(xì)設(shè)計(jì)。
本文轉(zhuǎn)載自??AI取經(jīng)路??,作者:AI取經(jīng)路
