LLM-Embedding 構建問答系統的侷限性及優化方案
近期 LangChain + LLM 方案高速發展,降低了知識問答等下游應用的開發門檻。但隨着業務深入,一些侷限性也日漸顯露,比如:LLM 意圖識別準確性較低,交互鏈路長導致時間開銷大;Embedding 不適合多詞條聚合匹配等。本文結合實際案例,分析上述問題存在的根源,並提出基於傳統 NLP 技術的解決思路。
背景
筆者在基於大語言模型構建知識問答系統一文中描述了,以 LLM 爲基礎的知識問答系統構建方法,核心在於:
- 將用戶問題和本地知識進行 Embedding,通過向量相似度 (Vector Similarity) 實現召回;
- 通過 LLM 對用戶問題進行意圖識別;並對原始答案加工整合。
原文通過 OpenAI API 和 ChatGPT 交互,缺陷也很明顯:
- 上層應用和模型基座綁死。切換模型基座時,上層邏輯不得不大量修改。在 LLM 蓬勃發展的當下,無論是處於成本、License、研究熱點還是性能等各方面考慮,基座變更幾乎不可避免。
- 處理環節不完善,開發成本高。比如:向量存儲和搜索,LLM 提示詞生成,數據鏈路(導入、分片、加工)等等。如果全部手擼,成本過高。
- 缺少標準化。輪子不但要豐富還要標準化,以結合場景靈活編排。一個粗淺的比喻是——我們需要 “樂高” 。
LangChain 的誕生就是解決這些問題的,它抽象了業務應用和 LLM 交互的方式,內置通用環節實現,標準化工具鏈交互接口,極大提升開發效率。LangChain 的大致結構如下所示:
基於 LangChain + LLM 的組合,下游應用在:“場景拓展” 和 “效能提升” 方面取得長足進步,但垂直領域一些深層問題也逐漸暴露出來。本文就開發問答系統時遇到的實際案例,說明上述方式的侷限性,並提出解決方案。
侷限性分析
問題簡介
首當其衝的是:多知識點聚合處理場景下,Embedding-Search 召回精度較低 的問題。典型應用範式是:
- 一個倉庫有 N 條記錄,每個記錄有 M 個屬性;
- 用戶希望對 x 條記錄的 y 個屬性進行查詢、對比、統計等處理。
這種場景在遊戲攻略問答中很常見,以體育遊戲 NBA2K Online2 爲例:
多知識點——簡單查詢
Q: 皮蓬、英格利什和布蘭德的身高、體重各是多少?
多知識點——篩選過濾
Q: 皮蓬、英格利什和布蘭德誰的第一位置是 PF?
多知識點——求最值
Q: 皮蓬、英格利什和布蘭德誰的金徽章數最多?
Embedding 缺陷
原始的 Embedding Search 在面對多知識點聚合處理時,存在幾個問題:
- 本地知識建立索引時,通常對單個知識點進行 Embedding;不會也不可能,爲不同知識點的排列組合分別製作索引。不難想象,不同記錄和屬性的組合方式有
QH2NpW (C 問題數,如過濾,查詢,最值;A 即排列),所有組合都建立索引的開銷是巨大的。
- 原始問題直接 Embedding ,和單條知識點的向量相似度比較低。爲了避免召回結果有遺漏,就需要 降低 相似度評分下限 (vector similarity score threshold),同時提高召回結果數量上限 (top k)。併產生不好的副效應:
- 召回結果,有效信息密度大幅降低;score threshold 過高或 top k 過低,會導致某些有效知識點無法命中;反之,很多無效知識點或噪聲會被引入。且由於總 token 數量的限制,導致本地知識點被截斷,遺漏相似度較低但有效的知識點。
- 召回結果的膨脹,增加了和 LLM 交互的 token 開銷;增加了 LLM 處理的時間複雜度。再直白點:既慢又花錢。
- 更糟糕的是,給 LLM 的分析處理帶來額外噪聲,影響最終答案的正確性。
下面給出一個示例:
- 問題是:皮蓬、英格利什和布蘭德誰的推薦位置是什麼? 這句話中的關鍵詞是:三個球員 “姓名” 和屬性“位置”。
- 召回結果如下所示:按照相似度降序排列。依次是:姓名、位置單詞條命中;多姓名同時命中;位置命中。實際上,中部信息是冗餘的。
再看看 LLM( ChatGLM-6B )對召回結果整合後的反饋,耗時 16s 用於處理大量輸入,結果讓人啼笑皆非:
皮蓬、鶯利什和布蘭德的推薦位置是前鋒 / 後衛。
幾處很明顯的錯誤:英格利什莫名其妙變爲鶯利什;且三人的位置回答不準確,由於過多冗餘信息導致混淆。也許和 LLM 自身的性能有關,但也能說明 Embedding Search 低效且不可靠。
方案分析
針對上面的問題重新整理思路,想要更加精準的回答問題,需要從兩個層面入手:
1. 問題理解——準確識別用戶意圖
這是問答系統的基礎:通過解析用戶問題,精準把握意圖後,才能更好的規劃後續處理步驟:
-
根據意圖,制定計劃,拆分爲若干步驟;在每個步驟選擇合適的工具進行處理;根據每個步驟返回的結果,動態決定下一步的方案。比如:要分析球員的位置,應按下屬步驟拆解:
-
瞭解到底有哪些球員
-
查詢這些球員的位置各是什麼
-
對位置信息進行處理:基於召回結果,對用戶問題進行推理。
-
規劃生成的單個步驟,做好抽象和封裝,明確輸入和輸出以及執行邏輯。
langchain 中的 Agents 已經對上述步驟提供了標準化接口,它相對於 Chains 一個重要不同在於:結合應用場景,通過提示詞引導 LLM 和用戶多輪交互,摸清在什麼時候應該需要選擇什麼樣的工具鏈進行處理。其中 Plan-and-Execute Agents 用於制定動作計劃;Action Agents 決定實施何種動作。
有了 Agents 和 Chains 的標準抽象,下面再來看看摸清用戶意圖的幾種方法,這樣才能開發合適的工具:
- Hypothetical Document Embeddings(HyDE) 。Precise Zero-Shot Dense Retrieval without Relevance Labels 一文面向 zero-shot 場景下的稠密檢索 ,使用基礎模型在訓練過程中已經掌握的相關語料,面向用戶問題,生成虛構的文檔。該文檔的作用,不是輸出最終結果,而是通過 LLM 對問題的理解能力,生成與之相關的內容。這相當於自動化生成相關性標籤,避免外部輸入。虛構文檔生成後,再使用無監督檢索器進行 Embedding;然後將生成的向量在本地知識庫中進行相似性檢索,尋找最終結果。上述過程在原論文中也提供了示意圖:
HyDE 要求與用戶問題相關的知識,已經存在於 LLM 基礎模型中。但專業領域知識,可能本來就是未聯網、未公開的;LLM 生成的虛構文檔,可能包含很多噪音,所以效果不一定很明顯;另外生成文檔的額外交互,進一步增加了時間開銷。目前,LangChain 提供了 HyDE Chain,LlamaIndex 也提供了類似的能力;大家有興趣可以嘗試,面對中文可能需要自定義 Prompt Template。
-
在爲用戶提供服務的預設場景下,細分用戶各種意圖的類別,定製對應的語義槽,每個槽位可以視爲在語義層面體現意圖的基本單位。
-
通過深度學習、統計學習,甚至 LLM ,理解用戶問題提取語義槽中需要的內容。在基於大語言模型構建知識問答系統一文中給過例子:通過 System Role 告知 LLM 需要提取槽位信息,讓 LLM 通過多輪對話引導用戶給出所有槽位信息。還是以遊戲攻略爲例,玩家諮詢球員的打法,那麼必須提供:球員姓名,年代(比如 2020/2022 年),比賽模式。對應的語義槽可以定義爲:
"球員打法" : {
"球員名稱" : ____,
"年代" : ____,
"比賽模式": ____,
}
- 搜索召回——提升精度
將用戶的訴求轉化爲語義槽後,可以較爲準確的體現問答意圖,這有助於提升搜索命中精度。原始知識點在建立索引時,除了原始的 Embedding 方法,可以做更多優化:
- 增加 關鍵詞、主題詞檢索。主題詞由機構定義和發佈的規範詞,通常是專有名詞或名詞短語;主題詞檢索結果包括檢索詞的近義詞、同義詞以及同一概念詞的不同書寫形式等,通過主題詞檢索可以很大程度降低漏檢和誤檢。關鍵詞由作者自定義或自動分析提取,通常是可以高度概括該文章主題的,使用關鍵詞搜索可搜出更加明確的主題方向的文章。
- 對相同知識點建立多級索引。同一知識點通常涉及多個維度,建立多級索引可以讓其在多維度查詢下發揮作用。比如,球員信息涉及:姓名、球隊、年齡、進攻、防守、榮譽等多項屬性。可以針對多屬性分別建立索引,這樣可以高效查詢指定的屬性數據,而不必每次將所有內容全部提取。
- 把原始知識庫轉化爲知識圖譜。知識圖譜的三元組:實體、屬性和關係。實體表示現實世界中的某個事物或對象,屬性表示這個事物或對象的特徵或屬性,關係表示實體之間的關係。知識三元組可以幫助人們更好地理解和組織知識,並支持推理和問題解決。在問答系統中可以通過提示詞引導 LLM 從用戶的問題中提取知識三元組,然後在知識圖數據庫中進行查詢。
解決思路
意圖識別和召回優化,屬於一體兩面,均有助於提升問答系統的精度。搜索層面知識圖譜相對於 Embedding 方式,加工成本較高。在遵從奧卡姆剃刀的前提下,有沒有什麼高性價比的方式來解決這個問題?
筆者想到,意圖識別和召回,其實有一個共同點:均可用 關鍵詞/主題詞 表示。那麼問題可以轉變爲:
-
關鍵詞 / 主題詞提取:
-
面向知識點,提取的結果作爲索引入口。
-
面向問題,提取的結果作爲語義槽內容。
-
複用 Embedding 框架。從原始問題和知識點直接 Embedding 後的匹配,轉化爲對兩者提取的關鍵詞進行 Embedding 後再進行匹配。
基於關鍵詞 Embedding 的入庫和搜索的流程圖如下所示:
這種方式怎麼解決原始 Embedding 存在的幾個侷限呢?
- 多知識點聚合處理侷限。在關鍵詞提取過程中,可以將並列的關鍵詞短語拆分,平鋪後分別檢索。這樣就可以降低 Embedding 的噪音,提高命中精度。最後將生成不同關鍵詞短語召回的結果組合即可。舉個例子:
語句:姚明和奧尼爾的內線與三分能力。
關鍵詞提取後,按照從屬關係叉乘,得到的結果應該是:
- 姚明內線
- 姚明三分
- 奧尼爾內線
- 奧尼爾三分
- LLM 過度依賴導致的性能開銷。首先通過關鍵詞短語召回已經可以大大提升命中精度,所以不必設置較低的 score threshold 或者較大的 top k,這本來就過濾了很多無關詞條。另外,關鍵詞提取如果可以不依賴於 LLM,而是使用傳統的 NLP 技術,那麼可以避免和 LLM 的多輪交互,節省了響應時間。這樣,對於大量的知識點文檔,也可以做關鍵詞提取。
帶着這樣的思路,下文主要描述關鍵詞提取實現方法和效果。
關鍵詞提取
我們的目標是,從無標註文本(零樣本)是實現信息抽取(Information Extraction,IE),因爲很少涉及人爲干預,該問題非常具有挑戰性。IE 包含三類任務:
-
實體關係三元組抽取 (RE, Relation Extraction)。三元組 (triples) 即 (主體, 關係, 客體) ,用英文表示爲 (Subject, Relation, Object) 。關係抽取,即實體三元組抽取。涉及兩件事:
-
識別文本中的主體、客體,即實體識別任務。
-
判斷這兩個實體屬於哪種關係,即關係分類。
-
命名實體識別 (NER, Name-Entity Recognition)。又稱作專名識別、命名實體,是指識別文本中具有特定意義的實體,主要包括人名、地名、機構名、專有名詞等,以及時間、數量、貨幣、比例數值等文字。指的是可以用專有名詞(名稱)標識的事物,一個命名實體一般代表唯一一個具體事物個體,包括人名、地名等。
-
事件抽取 (EE, Event Extraction)。指的是若干與特定矛盾相關的事物,在某一時空內的運動,從自然語言角度通過 “主謂賓” 等語義單元體現。事件抽取就是從半結構化、非結構化數據中,識別一個與目標相關的事件的重要元素識別出來。
筆者針對知識問答這樣的特定場景,將上述三類任務簡化爲兩個過程:
- 名詞短語提取。即:主語、賓語。通常由名詞,和名詞的限定詞或者修飾詞組成。比如:
姚主席如何評價周琦的組織進攻能力?
主語:姚主席
賓語:能力。但真正具有實際意義的是能力的限定修飾詞,即 “周琦組織進攻能力”。
- 謂語。體現名詞短語之間的事件。比如:評價。在更多場景中是:比較、查詢、過濾、統計等,存在動態計算、處理的需求。因此即使忽略謂語,僅通過名詞短語的組合也可以獲得完備的知識。謂語所代表的事件,可以交給 LLM 處理。
筆者在經過對多種業務場景的分析後,最終萌生出下面的 關鍵詞提取方式:
-
關鍵詞只考慮名詞短語,將其作爲知識點的索引用於召回。
-
事件體現的是不同知識點的關係。重點在於事件關聯者,獲取事件關聯者對應的語料後,通過 LLM 理解事件進行處理:
-
知識點通常不是按照事件組織的,而是按實體。因此索引建立和關鍵詞抽取,應該以名詞短語爲主要目標。
-
主語、賓語之間存在並列和限定關係。將限定關係按級聯方式處理,同一名詞短語內如果存在並列關係則與其他部分叉乘。
將該思路應用到上面的例子中:
如圖所示,使用 NLU 的基本處理流程,分析原始問句:
-
分詞
-
詞性標註 (Part-Of-Speech Tagging, Penn Chinese Treebank),用於識別名詞短語。比如:NR 表示人名、機構、地名等專有名詞;NT 表示時間名詞,NOI 表示漢字順序顛倒產生的噪聲,NN 表示其它類型名詞。
-
NER 識別。比如姚,周琦等。
-
然後還需要考慮語義間的依存關係,確定名詞間的關係,比如:
- 並列
- 修飾。比如:“組織進攻能力”
- 從屬、限定等等。比如:["周琦", "組織進攻能力"]
這樣不難得到應該搜索的關鍵詞列表:['姚主席', '周琦', '組織進攻能力']。下面來看看這種思路究竟如何實現,還是分爲兩大類:LLM、傳統 NLP。
基於 LLM 提取
LLM 已經天然具備 NLU 和 NLG 的能力,所以做名詞短語提取按道理應該手到擒來。業界已經有嘗試,比如《Zero-Shot Information Extraction via Chatting with ChatGPT》一文,提出兩階段多輪問答信息提取方式:
在該思想的啓發下,筆者使用 langchain 開發了信息提取 Chain,LLM 使用的是 ChatGLM-6B 本地部署,提取部分測試片段如下所示:
發現問題了嗎:明明是分析張三丰;結果李四和王五怎麼冒出來了。要知道,Prompt 已經添加了 “不要編造內容” 的說明,但無濟於事。總體而言,使用 LLM 提取信息效果如下:
- 可以支持部分程度的 NER;對非專業名詞、或業務自定義術語識別不好,需要 Fine-Tuning。
- 可以提取名詞短語,但無法做精確的語義分析,大量測試案例下總是存在不少偏差。
- 即使明確了輸出格式,但還是頻繁出錯。
- 耗時較長。提取 6 條測試案例耗時 12s。
簡單而言:不是不能做,但對 LLM 的要求較高;結果不準確、開銷也大。
傳統 NLP 方法提取
在 LLM 做信息提取,效果不盡如人意的情況下,轉而嘗試傳統 NLP 技術。前面提到的:分詞、詞性標註、NER 等,業界已經有不少傳統 NLP 工具可以使用。
- spacy,測試結果較差,很多語句識別不出來,比如:
“請比較詹姆斯和麥迪的屬性”
—— 無法識別任何人名
“詹姆斯、字母哥和傑林. 布朗的金徽章有何差異?”
—— 只能識別出布朗,且將 "傑林" 拋棄
經過測試後最終選擇 HanLP 作爲基礎工具,原因在於:
- 對中文支持好
- 依賴少,開箱即用
- 基礎工具完備,接口整潔,便於二次開發
下面基於 HanLP,看看究竟如何解決前文提出的:多知識點聚合查詢問題?其核心點在於三步:
- 詞性標註
- 名詞短語關係分析:修飾、限定、並列等
- 名詞短語重新組織:修飾、限定關係級聯;並列關係與其他部分叉乘。
名詞短語提取與整合
舉一個更加複雜的例子,在主語和賓語中添加:並列和修飾關係。首先分詞,然後添加詞性標註,結果如下:
依存分析
詞性有了,下面需要進一步提取句子中單詞與單詞之間的語法關係,即 依存句法分析;筆者採取 Stanford Dependencies Chinese (SDC) 標準進行分析,並且爲了清晰展示,將分析結果轉化爲 Pandas DataFrame 的形式,如下圖所示:
- dep_tok_id,表示當前分詞所依賴的中心詞的 id。比如:“指導” 的中心詞 ID 是 5。
- dep_rel,表示當前分詞和中心詞的關係。比如:“指導”和中心詞 “主席” 的關係是 conj ,根據 SDC 標準其含義爲 “連接”,即指導和主席屬於並列關係。類似的,“詹姆斯”、“約基奇” 和“張伯倫”都屬於並列關係;但是 “張伯倫” 的中心詞是“能力”,關係爲 assmod ,即關聯關係(修飾限定)。
- sdp_tok_ids 和 sdp_rel。是在依存句法分析的基礎上,進一步提供 語義依存分析,旨在分析一個句子中單詞與單詞之間的語義關係 (sdp_rel),並將其表示爲圖結構的任務。不同於依存句法分析的地方是:圖結構中每個節點可以有任意個目標節點。在本例當中,特別需要關注的是:Agt(施事者),Poss(領事者),eCoo(並列關係)等。
根據 DataFrame 中提供的依賴關係,可以直觀地獲取名詞短語:
- 主體:並列關係 [張指導, 姚主席]
- 客體:[三分能力],三分和能力是修飾關係
- 客體還存在三個並列的領事關係(所屬關係):[詹姆斯, 約基奇, 張伯倫]
所以實際的客體,應該將並列的領事關係和本體叉乘後平鋪,結果就是:
['詹姆斯三分能力', '約基奇三分能力', '張伯倫三分能力']
用這三個關鍵詞,Embedding 後去本地知識庫搜索就可大大提升命中精度。不過大家如果細心的話可以發現:語義依存分析的結果其實 不準確,比如:
- “指導” 一詞的 sdp_rel 爲 root,即被識別爲動詞作爲語義樹的根部
- “和”與 “主席” 被識別爲領事關係
這種語義偏差的原因在於:同一個分詞的語義容易產生混淆。比如 “指導” 既可以作爲名詞,也可以作爲動詞。中文多義詞和語境比較複雜,導致語義依存分析有不小的概率會出現誤判。那麼怎麼解決這個問題呢?一定要使用有監督訓練方式,對基礎語義依存分析模型進行 Fine-Tuning 嗎?一旦涉及有監督,成本就不容小區了。不過幸好:詞性標註和依存句法分析,相對語義依存分析,準確率是更高的,所以筆者想到另外一種方式:
基於正確的詞性標註,構建出語法樹,從語法角度提取名詞短語。
成分句法分析
分析一個句子在語法上的遞歸構成,在 NLU 領域是經典的 成分句法分析 問題。下面羅列 Chinese Treebank 標準片段,描述對分詞單元類型的定義,請大家關注藍色背景內容。
再次回顧我們要解決的多知識點聚合處理的目標:
- 名詞短語 (Noun Phrase) 提取。即提取 NP;
- 處理好修飾名詞短語間的修飾限定問題。比如:DNP 等
- 處理好並列、連接關係。比如:CC 等
- 將修飾關係的名詞短語級聯;將並列關係的 NP 和修飾關係的 NP 叉乘。
基於該思路,上文提到實例,可以轉化爲下面的成分句法分析樹:
這是典型的樹結構:
-
頂層的 NP 有兩個:分別是主體和客體。客體是 VP(動詞短語),表示主體的施事對象,和施事行爲。
-
每個 NP 是一顆子樹。變種很多,但核心有三種形式:
-
並列。比如:['張指導', '姚主席'] 通過明確的連接詞 "和" 建立關係;['詹姆斯', '約基奇', '張伯倫'] 則通過非終結的標點服務建立關係。
-
堆疊。由修飾詞和名詞聯合形成一個名詞短語。
-
從屬。通過 "的" 這樣 DEG 詞性的結構,明確表示前後兩個短語間的從屬關係。比如:"三分及搶斷",隸屬於 "詹姆斯"。
這樣來看,NP 的提取非常簡單,通過遞歸方式對三種基礎形式的樹結構進行識別即可,提取時注意區分:
- 主體、客體。
- NP 子樹中各個單元的組織關係。並列單元在和其它單元組合時,需要叉乘輸出;其它鄰接單元直接合並即可。
在上述算法下,樣例中提取的名詞短語列表最終如下所示:
- 第一個列表爲主語列表
- 第二個列表爲平鋪後的賓語列表
- 在性能方面,單條語句處理方式是 5ms;之前基於 LLM 的方式處理 6 條語句,需要 12s;性能相差數百倍。
至此,已經介紹完筆者關於關鍵詞提取的基本思路,完整的處理流程如下圖所示:
應用效果
關鍵詞提取實現後,配合前文提到的召回方法,應用到文章開始的幾個示例問題,結果如下所示:
最後彙總筆者提出的解決多知識點聚合處理問題的方案:
-
基於傳統 NLP 的成分句法分析,提取名詞短語;再通過短語間的依存關係,生成關鍵詞列表
-
從完整語句的 Embedding,切換爲關鍵詞 Embedding:
-
知識庫構建時。基於單知識點入庫,入庫時提取關鍵詞列表進行 Embedding,用於檢索。
-
查詢時。對用戶的問題提取關鍵詞列表進行 Embedding 後,從本地知識庫命中多條記錄。
-
將單問句中的多知識點拆解後檢索,將召回的多條記錄交付給 LLM 整合。
該方法的優勢在於:
- 相比傳統 Embedding,大幅提升召回精準度。
- 支持單次交互,對多知識點進行聚合處理。而不必讓用戶,手動分別查詢單個知識點,然後讓 LLM 對會話歷史中的單個知識點進行彙總。
- 使用傳統 NLP 在專項問題處理上,相比 LLM 提供更好的精度和性能。
- 減少了對 LLM 的交互頻次;提升了交付給 LLM 的有效信息密度;大大提升問答系統的交互速度。
總結
LLM 的出現,推動下游應用激烈變革,各種探索如火如荼地展開。但在熱潮背後,我們還有一些細節問題需要仔細對待。LLM 的未來是偉大的,甚至可能是邁向 AGI 的重要里程碑;但現在並不能宣判傳統 NLP 技術的死亡。
本文提供了傳統 NLP 和 LLM 結合的一種可能,通過用戶問題和本地知識庫關鍵詞提取,能較好的解決 Embedding 精度缺失,以及 HyDE、LLM NLU 的精度和性能問題。實現單輪對話,對多知識點的聚合處理。後續將繼續探索 LLM 和傳統技術的角色分工,進一步提升綜合收益。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://zhuanlan.zhihu.com/p/641132245