來自工業界的知識庫 RAG 服務(三),FinGLM 競賽獲獎項目詳解

背景介紹

前面介紹過工業界的 RAG 服務 QAnything 和 RagFlow 的詳細設計,也介紹過來自學術界的 一些優化手段。

前一陣子剛好看到智譜組織的一個金融大模型比賽 FinGLM,主要做就是 RAG 服務的競賽,深入研究了其中的幾個獲獎作品,感覺還是有不少亮點。整理一些獲獎項目的設計方案,希望對大家有所啟發。

FinGLM 比賽

FinGLM 比賽介紹

FinGLM 是基于一定數量的上市公司財報構建知識庫,使用 ChatGLM-6B 作為大模型完成知識庫問答。需要回答的問題包含三類:

  1. 初級問題,可以直接從原文中獲得信息進行回答,比如直接問特定公司某一年的研發費用,考察的是能否正確檢索到內容的能力;
  2. 中級問題,需要對原文中內容進行統計分析和關聯,比如問某公司某一年研發費用的增長率,考慮的是能否檢索到內容并進行二次加工得到結果的能力;
  3. 高級問題,安全開放的問題,比如問研發項目是否涉及國家戰略,考察的是檢索到內容并綜合處理的能力;

可以看到使用一個相對小的大模型 ChatGLM-6B,需要能準確回答上面的這些問題,對于內容的檢索以及架構精細設計要求還是很高的,直接使用 最原始的 RAG 框架 肯定是不夠的, 發揮不穩定的向量檢索大概率是無法幫你獲獎的。

比賽難點

我根據決賽答辯過程中的一些反饋,整理此比賽中的一些難點:

  1. 財報中包含大量的數據,摻雜文本內容與表格數據,很多精細的問題都需要依賴表格進行回答,如何進行精細的處理,保證原始文檔中的內容可以正確檢索到;
  2. 不同類型的問題需要不同的處理方案,如何區分不同的問題進行有針對性的解決;
  3. ChatGLM-6B 模型較弱,稍微復雜的情況就無法正確處理,甚至模型的輸出就不可控了,如果保證穩定輸出正確的答案;
  4. 用戶問題與文檔使用中詞匯可能不是完全一致的,這會導致精確的檢索更加困難;
獲獎項目介紹

在天池的 決賽文章 可以看到最終獲獎的隊伍,本文主要想介紹的是項目是決賽獲得第三名的 “ChatGLM反卷總局” 團隊的項目,為什么沒有選擇前面的團隊的項目,主要原因是:“ChatGLM反卷總局” 是獲獎作品中性價比最高的實現方案:

  1. 其他團隊或多或少都做了模型的微調,甚至獲得第一名團隊微調了 2 到 3 個模型,“ChatGLM反卷總局” 沒有做任何的微調就取得了不錯的成績;
  2. 在原始 chatGLM 做關鍵詞提取效果很差的情況下,其他團隊都是靠微調解決,“ChatGLM反卷總局” 基于原始模型 + 正則表達式就實現了不錯的效果;
  3. 實現方案比較輕量,沒有引入太多額外的服務,生產環境可以方便應用;

項目方案詳解

方案設計

項目設計的整體流程如下所示:

請添加圖片描述

整理流程主要包含三個核心模塊:

  1. 問題分類,不同類型的問題需要采取的方案完全不同,在選擇處理方案時需要先確定是什么類型的問題,方便進行有針對性的處理;
  2. 關鍵詞抽取,因為內容的檢索主要使用關鍵詞檢索,因此需要從原始問題中提取對應的關鍵詞;
  3. 表格與文檔內容的檢索與回答,根據問題類型的不同從不同來源獲取數據,并執行必要的外部處理(主要是額外的數學計算),處理后提供給大模型得出最終結果;
問題分類

不同類型的問題存在不同的處理方案,因此在回答前需要先進行分類,分類的正確性對結果影響很大。分類一般是根據實際的測試問題進行歸納總結得出類型和判斷依據, 之后就可以自動化分類。這部分不少團隊是通過微調 chatGLM 模型實現線上分類的。

此項目是基于簡單的規則判斷,這個只能算是一個取巧方案,沒有太多可說的東西。可以簡單看看對應的實現:

if '保留' in q['question'] and com_match != '':question_type = 'calculate'
elif com_match != '' and len(year_match) > 0 and ('分析' in q['question'] or '介紹' in q['question'] or '如何' in q['question'] or '描述' in q['question'] or '是否' in q['question'] or '原因' in q['question'] or '哪些' in q['question'] or len(com_normal_keywords) > 0) and '元' not in q['question'] and len(com_info_keywords) == 0:question_type = 'com_normal'
elif com_match != '' and len(year_match) > 0:question_type = 'com_info'
elif com_match == '' and judge_tongji(q['question']):question_type = 'com_statis'
else:question_type = 'normal'q['question_type'] = question_type
if '增長率' in q['question']:q['question_type'] = 'com_info'

代碼有點糙,應該是根據實際問題總結出來的。但是在實際生產環境中,如果問題分類可以通過簡單規則直接區分出來,確實沒必要做大模型的微調或復雜的意圖識別了。

關鍵詞抽取

常規的 RAG 服務一般是基于向量進行檢索,但是此項目主要使用的是關鍵詞進行檢索,支持了關鍵詞 + 向量的混合檢索,貌似決賽還因為鏡像過大最終沒有使用向量檢索,因此主要依賴的就是關鍵詞檢索。

而關鍵詞抽取的效果直接影響關鍵詞檢索的效果,因此這一步相當重要,此項目使用的兩種方案:

  1. 正則關鍵詞抽取;
  2. 基于 LLM 的關鍵詞抽取

同一內容在問題中的使用的關鍵詞可能與文檔中使用的關鍵詞存在一些差異,因此還需要進行關鍵詞的泛化。

正則關鍵詞提取

正則關鍵詞的提取完全是通過已有的關鍵詞表進行匹配得到的,效果優劣與原始的關鍵詞表的覆蓋范圍有很大關系。具體的實現簡化如下:

# 各個來源的關鍵詞列表匯總find_keywords = list(attr_mapping_title.keys() | gongshi_mapping.keys() | com_normal_attr_mapping_title.keys())
find_keywords.sort(key=len, reverse=True)# 構建正則表達式,可以匹配關鍵詞表中存在的關鍵詞attr_regex_pattern = r'(?:' + '|'.join(find_keywords) + r')'
attr_regex = re.compile(attr_regex_pattern, re.IGNORECASE)# 從原始問題 q 中提取關鍵詞,并實施去重attr_match = attr_regex.findall(q['question'])
attr_match.extend(attr_regex.findall(q['question'].replace('的','')))
keywords = list(set(attr_match))

這個是通過簡單的匹配覆蓋常規的關鍵詞提取,主打的就是快,即使無法命中也有 LLM 提取關鍵詞兜底。

基于 LLM 的關鍵詞提取

常規的 ChatGLM-6B 模型較小,抽取關鍵詞的效果很可能不佳。常規方案是構造訓練數據進行微調,但是此項目用了一種有意思的方案:

項目使用 Few Shot 去提升 ChatGLM-6B 的提取關鍵詞的效果,與常規 Few Shot 不同在于,構造的 Few Shot 是放在歷史聊天記錄中的,而不是拼接在 prompt 中的。

對應的實現簡化后如下所示:

cls_history = [("現在你需要幫我完成信息抽取的任務,你需要幫我抽取出句子中三元組,如果沒找到對應的值,則設為空,并按照JSON的格式輸出", '好的,請輸入您的句子。'),("<year><company>電子信箱是什么?\n\n提取上述句子中的關鍵詞,并按照json輸出。", '{"關鍵詞":["電子信箱"]}'),("根據<year>的年報數據,<company>的公允價值變動收益是多少元?\n\n提取上述句子中的關鍵詞,并按照json輸出。",'{"關鍵詞":["公允價值變動收益"]}'),("<company>在<year>的博士及以上人員數量是多少?\n\n提取上述句子中的關鍵詞,并按照json輸出。",'{"關鍵詞":["博士及以上人員數量"]}'),("<company><year>年銷售費用和管理費用分別是多少元?\n\n提取上述句子中的關鍵詞,并按照json輸出。",'{"關鍵詞":["銷售費用","管理費用"]}'),("<company><year>的衍生金融資產和其他非流動金融資產分別是多少元?\n\n提取上述句子中的關鍵詞,并按照json輸出。",'{"關鍵詞":["衍生金融資產","其他非流動金融資產"]}'),...
]prompt = f'{question}\n\n提取上述句子中的關鍵詞,并按照json輸出。'response, history = model.chat(tokenizer, prompt, history=cls_history, top_p=0.7, temperature=1.0)

通過這種方案,最終大模型提取關鍵詞的表現更穩定,下面的團隊給出的對比情況:

在這里插入圖片描述
在這里插入圖片描述

關鍵詞泛化

因為原始問題中的關鍵詞與實際文檔中的關鍵詞不是完全一致的,此時就無法正確匹配到文檔中內容,因此需要執行必要的泛化,從而提升匹配的概率。

項目是通過 fuzzywuzzy 實現的,此項目目前已經遷移至 thefuzz 了,思路是通過 Levenshtein_distance 實現模糊的字符串匹配,其實就是基于編輯距離確定字符串的相似度。

項目中的關鍵詞泛化的實現如下所示:

from fuzzywuzzy import processdef find_best_match_new(question, mapping_list, threshold=40):# 與系統中關鍵詞列表通過編輯距離進行匹配matches = process.extract(question, mapping_list)# 使用閾值進行過濾,并按照相似度進行排序best_matches = [match for match in matches if match[1] >= threshold]best_matches = sorted(best_matches, key=lambda x: (-x[1], -len(x[0])))# 返回最匹配的內容和得分match_score = 0total_q = ""if len(best_matches) > 0:total_q = best_matches[0][0]match_score = best_matches[0][1]return total_q, match_score
數據庫與文檔內容的檢索與回答

表格數據檢索與回答

表格數據是在預處理階段提取出來,保存在 excel 文件中,初始化時轉換為 pandas 對象進行檢索。

通過前面的關鍵詞提取與泛化后,得到的關鍵詞與文檔中的關鍵詞就保持一致了,此時通過關鍵詞就可以準確地匹配所需的內容。這部分就可以看到精確匹配的優勢所在,處理得當的情況下結果相對準確。

除了簡單直接匹配表格數據的情況,實際問題中還存在需要通過公式計算最終結果的情況,是否能直接提供原始數據給 chatGLM-6B 進行計算呢,這個就有點強 GPT 所難了,實際測試往往容易出錯。

目前是通過外部提取所需的原始后直接調用 Python 進行計算,具體的實現如下所示:


financial_formulas = {"研發經費與利潤比值": (["研發費用", "凈利潤"], "研發費用 / 凈利潤"),"企業研發經費占費用": (["銷售費用", "財務費用", "管理費用", "研發費用"], "研發費用 / (研發費用+管理費用+財務費用+銷售費用)"),"研發人員占職工": (["研發人員", "職工人數"], "研發人員 / 職工人數"),"碩士及以上學歷人員占職工": (["研發人員", "職工人數"], "研發人員 / 職工人數"),"流動比率": (["流動資產合計", "流動負債合計"], "流動資產合計 / 流動負債合計"),"速動比率": (["流動資產合計", "存貨", "流動負債合計"], "(流動資產合計 - 存貨) / 流動負債合計"),"碩士及以上人員占職工": (["碩士以上人數", "職工人數"], "碩士以上人數 / 職工人數"),"研發經費與營業收入比值": (["研發費用", "營業收入"], "研發費用 / 營業收入"),...
}# 根據關鍵詞獲得所需的公式項formula_data = financial_formulas[index_name]
data_values = {}# 獲取計算所需的原始數據finance_data = dq.get_financial_data(year_, stock_name)# 將原始數據與公式所需的元素組合為鍵值對for field in formula_data[0]:value = finance_data.get(field)data_values[field] = value# 獲取對應的公式計算字符串formula_str = formula_data[1]
calculation_str = formula_str.replace(" ", "")
for key, value in data_values.items():calculation_str = calculation_str.replace(key, str(value))# 調用 Python 執行計算result_value = eval(calculation_str)

文本數據檢索與回答

對于文本檢索,大量的信息會存在文本的各級標題中,而常規的文件分片中除了與標題直接相連的分片,其他分片中標題的信息是缺失的,效果類似如下所似:
在這里插入圖片描述

為了解決這個問題,項目會通過正則表達式識別標題行,之后通過堆棧記錄標題層級結構,在各個分片中增加標題信息,實現流程如下所示:

在這里插入圖片描述
通過上面的修復,最終各個分片中都會包含各級標題的信息,檢索更容易命中。修復后分片效果如下所示:

在這里插入圖片描述
分片的數據是保存在 ES 中的,這樣就可以比較方便的實現關鍵詞檢索,實際檢索時關鍵詞命中標題或文本內容都能被正確召回。效果如下所示:
在這里插入圖片描述
實際的文本召回實現就相對簡單了,只是 ES 客戶端的簡單調用:

def get_context(company, final_year, query, size=3, es_index="tianchi", keyword="", recall_titles={}, title_keyword=""):force_search_body = {"size": size,"_source": ["texts"],"query": {"bool": {"filter": [{"term": {"companys": company}},{"terms": {"year": final_year}},]}},}# 構造 query 檢索force_search_body["query"]["bool"]["should"] = [{"match": {"texts": {"query": query}}}]# 額外的關鍵詞檢索增強if len(keyword) > 0:force_search_body["query"]["bool"]["should"].append({"match_phrase": {"texts": {"query": keyword}}})force_search_body["query"]["bool"]["filter"].append({"terms": {"titles_cut.keyword": recall_titles[keyword]}})if len(title_keyword) > 0:force_search_body["query"]["bool"]["should"].append({"match": {"titles_cut": {"query": title_keyword}}})# ES 檢索調用search_result = es.search(index=es_index, body=force_search_body)hits = search_result["hits"]["hits"]recall_texts = [hit["_source"]["texts"] for hit in hits]return recall_texts

總結

本文對 “ChatGLM反卷總局” 在 FinGLM 比賽的項目進行了詳細解讀。比賽中獲獎的項目往往會采取各種奇技淫巧的,毫無疑問大部分手段在當前項目都是有效的(要不然就無法獲獎了),但是往往過于定制化,無法應用在常規的生產環境中,這個在 FinGLM 的很多獲獎項目中都有看到。

在實際了解 “ChatGLM反卷總局” 項目過程中,有兩個亮點可能對 RAG 的生產環境有不錯的借鑒意義:

  1. 特殊的 Few Shot 設計,將 Few Shot 內容構造至聊天歷史中,相對直接放入 prompt 中存在明顯提升;
  2. 層次化的文件解析,將各級文件標題拼接至文件分片中,可以大幅提升文檔召回率;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/24028.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/24028.shtml
英文地址,請注明出處:http://en.pswp.cn/web/24028.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Pyramid Vision Transformer, PVT(ICCV 2021)原理與代碼解讀

paper&#xff1a;Pyramid Vision Transformer: A Versatile Backbone for Dense Prediction without Convolutions official implementation&#xff1a;GitHub - whai362/PVT: Official implementation of PVT series 存在的問題 現有的 Vision Transformer (ViT) 主要設計…

C++結合ffmpeg獲取聲音的分貝值

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、分貝是什么&#xff1f;1.功率量2.場量 二、實際操作1.分析wav文件2.讀取麥克風 總結 前言 最近面對一個需求&#xff0c;就是需要傳遞聲音文件到模型里推…

鏈表的回文結構OJ

鏈表的回文結構_牛客題霸_牛客網對于一個鏈表&#xff0c;請設計一個時間復雜度為O(n),額外空間復雜度為O(1)的算法&#xff0c;判斷其是否為。題目來自【牛客題霸】https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId49&&tqId29370&rp1&a…

CodeMeter助力Hilscher,推動實現全球智能制造連接解決方案

Hilscher的旗艦店為開放工業4.0聯盟&#xff08;OI4&#xff09;社區提供了應用商店的便捷和開放性&#xff0c;將這一概念引入工業領域。該商店依托CodeMeter的許可證管理和加密保護&#xff0c;為工業用戶提供了豐富的應用和解決方案庫&#xff0c;滿足他們在車間自動化和連接…

2020年06月C語言二級真題

計算矩陣邊緣元素之和 題目描述 輸入一個整數矩陣&#xff0c;計算位于矩陣邊緣的元素之和。 所謂矩陣邊緣的元素&#xff0c;就是第一行和最后一行的元素以及第一列和最后一列的元素。 輸入格式 第一行分別為矩陣的行數n和列數m&#xff0c;兩者之間以一個空格分開。 接下來輸…

WPF中讀取Excel文件的內容

演示效果 實現方案 1.首先導入需要的Dll(這部分可能需要你自己搜一下) Epplus.dll Excel.dll ICSharpCode.SharpZipLib.dll 2.在你的解決方案的的依賴項->添加引用->瀏覽->選擇1中的這幾個Dll點擊確定。(添加依賴) 3.然后看代碼內容 附上源碼 using Excel; usi…

計網復習資料

一、選擇題&#xff08;每題2分&#xff0c;共40分&#xff09; 1. Internet 網絡本質上屬于&#xff08; &#xff09;網絡。 A.電路交換 B.報文交換 C.分組交換 D.虛電路 2.在 OSI 參考模型中,自下而上第一個提供端到端服務的是( )。 A.數據鏈路層 B.傳輸…

Thinkphp使用Elasticsearch查詢

在Thinkphp中調用ES&#xff0c;如果自己手寫json格式的query肯定是很麻煩的。我這里使用的是ONGR ElasticsearchDSL 構建 ES 查詢。ongr ElasticsearchDSL 的開源項目地址&#xff1a;GitHub - ongr-io/ElasticsearchDSL: Query DSL library for Elasticsearch。ONGR Elastics…

100V 15A TO-252 N溝道MOS管 HC070N10L 惠海

MOS管的工作原理是基于在P型半導體與N型半導體之間形成的PN結&#xff0c;通過改變柵極電壓來調整溝道內載流子的數量&#xff0c;從而改變溝道電阻和源極與漏極之間的電流大小。由于MOS管具有輸入電阻高、噪聲小、功耗低等優點&#xff0c;它們在大規模和超大規模集成電路中得…

package.json中resolutions的使用場景

文章目錄 用途配置示例使用方法注意事項和peerDependencies有什么不同peerDependenciesresolutions 總結 ?創作者&#xff1a;全棧弄潮兒 &#x1f3e1; 個人主頁&#xff1a; 全棧弄潮兒的個人主頁 &#x1f3d9;? 個人社區&#xff0c;歡迎你的加入&#xff1a;全棧弄潮兒的…

git【工具軟件】分布式版本控制工具軟件

一、Git 的介紹 git軟件的作用&#xff1a;管理軟件開發項目中的源代碼文件。 常用功能&#xff1a; 倉庫管理、文件管理、分支管理、標簽管理、遠程操作 功能指令&#xff1a; add&#xff0c;commit&#xff0c;log&#xff0c;branch&#xff0c;tag&#xff0c;remote…

Ubuntu Linux LTS 24.04 AMD64 桌面版安裝記錄

下載iso aria2c -x 4 -s 12 "https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/24.04/ubuntu-24.04-desktop-amd64.iso" "https://mirrors.163.com/ubuntu-releases/24.04/ubuntu-24.04-desktop-amd64.iso" "https://mirrors.zju.edu.cn/ubuntu…

[pyradiomics][python]pyradiomics所有whl文件下載地址匯總

源碼地址&#xff1a;https://github.com/AIM-Harvard/pyradiomics pyradiomics是一個開源的Python軟件包&#xff0c;專門用于從醫學影像中提取高通量的定量特征&#xff0c;這些特征被稱為影像組學(Radiomics)特征。以下是關于pyradiomics的詳細介紹&#xff1a; 一、基本概…

華為端云一體化開發 (起步1.0)(HarmonyOS學習第七課)

官方文獻&#xff1a; 為豐富HarmonyOS對云端開發的支持、實現端云聯動&#xff0c;DevEco Studio推出了云開發功能&#xff0c;開發者在創建工程時選擇云開發模板&#xff0c;即可在DevEco Studio內同時完成HarmonyOS應用/元服務的端側與云側開發&#xff0c;體驗端云一體化協…

大數據面試題第二期*6

題1、Namenode掛了怎么辦? 方法一&#xff1a;將SecondaryNameNode中數據拷貝到namenode存儲數據的目錄。 方法二&#xff1a;使用importCheckpoint選項啟動namenode守護進程&#xff0c;從而將SecondaryNameNode中數據拷貝到namenode目錄中。 題2、Hadoop 的namenode 宕機怎么…

論文代碼解讀STPGNN

1.前言 本次代碼文章來自于《2024-AAAI-Spatio-Temporal Pivotal Graph Neural Networks for Traffic Flow Forecasting》&#xff0c;基本模型結構如下圖所示&#xff1a; 文章講解視頻鏈接 代碼開源鏈接 接下來就開始代碼解讀了。 2.代碼解讀 class nconv(nn.Module):de…

NDIS Filter開發-網絡數據的傳輸

和NIC小端口驅動不同的是&#xff0c;無需考慮網絡數據具體是如何傳輸的&#xff0c;只需要針對NBL進行處理即可。Filter驅動程序可以啟動發送請求和接收指示&#xff0c;或“過濾”其他驅動程序的請求和指示。Filter模塊堆疊在微型端口適配器上。 驅動程序堆棧中的Filter模塊…

谷粒商城實戰(033 業務-秒殺功能4-高并發問題解決方案sentinel 1)

Java項目《谷粒商城》架構師級Java項目實戰&#xff0c;對標阿里P6-P7&#xff0c;全網最強 總時長 104:45:00 共408P 此文章包含第326p-第p331的內容 關注的問題 sentinel&#xff08;哨兵&#xff09; sentinel來實現熔斷、降級、限流等操作 騰訊開源的tendis&#xff0c…

ctfshow web

【nl】難了 <?php show_source(__FILE__); error_reporting(0); if(strlen($_GET[1])<4){echo shell_exec($_GET[1]); } else{echo "hack!!!"; } ?> //by Firebasky //by Firebasky ?1>nl //先寫個文件 ?1*>b //這樣子會把所有文件名寫在b里…

JSON 無法序列化

JSON 無法序列化通常出現在嘗試將某些類型的數據轉換為 JSON 字符串時&#xff0c;這些數據類型可能包含不可序列化的內容。 JSON 序列化器通常無法處理特定類型的數據&#xff0c;例如日期時間對象、自定義類實例等。在將數據轉換為 JSON 字符串之前&#xff0c;確保所有數據都…