GraphRAG介紹

ZacksTang發表於2024-07-24

GraphRAG

GraphRAG 是一種基於圖的檢索增強方法,由微軟開發並開源。它透過結合LLM和圖機器學習的技術,從非結構化的文字中提取結構化的資料,構建知識圖譜,以支援問答、摘要等多種應用場景。GraphRAG的特色在於利用圖機器學習演算法進行語意聚合和層次化分析,從而能夠回答一些高層次的抽象或總結性問題,這是常規RAG系統的短板。

GraphRAG的核心在於其處理流程,包含2個階段:Indexing和Querying。

  • 在Indexing階段:GraphRAG將輸入文字分割為多個可分析單元(稱為TextUnits)。使用LLM提取實體、關係和關鍵宣告。然後透過層次聚類技術(例如Leiden演算法)對圖譜進行社群劃分,並生成每個社群的摘要
  • 在Querying階段:這些結構被用來提供材料,以作為LLM的context來回答問題。查詢模式包括全域性搜尋和區域性搜尋:
    • 全域性搜尋:透過利用社群的總結來推理關於語料庫整體問題的答案
    • 區域性搜尋:透過擴充套件到特定實體的鄰居和相關概念來推理關於特定實體的問題

相對於傳統的RAG方法,在處理私有資料集時展現出顯著的效能提升。它透過構建知識圖譜和社群分層,以及利用圖機器學習技術,增強了對複雜資訊的問答效能,尤其是在需要全面理解大型資料集或單一大型文件的語意概念時。

基於微軟官方釋出的部落格來看[1],微軟採用了一個LLM評分器來給GraphRAG和Baseline RAG的表現進行評估。基於一系列的指標,包括全面性(提取的上下文內容的完整性,包括隱含的資訊)、人類賦權(提供來源的材料或其他上下文資訊)和多樣性(提供不同的視角或問題的角度)。從初步評測結果來看,GraphRAG在這些指標上都優於Baseline RAG。

除此之外,微軟還實用了SelfCheckGPT來進行忠實度的絕對測量,來確保基於原材料的事實性和連貫性結果。結果也顯示出GraphRAG在忠誠度上達到了與Baseline RAG類似的水平。目前微軟也在開發一個評估框架,用來進一步衡量上述問題型別的效能。

1. 測試GraphRAG

需要注意的是,預設情況下,GraphRAG使用的是OpenAI的模型。如果要使用AWS Bedrock提供的模型,建議可以使用亞馬遜雲科技提供的Bedrock Access Gateway方案[2]。

下面是部署流程

# 建立conda環境

conda create -n grag python=3.10

conda activate grag

# 安裝graphrag

pip3 install graphrag

# 設定環境

mkdir gragdemo

cd gragdemo/

# 初始化環境,建立 .env 和 settings.yaml檔案以及預設的配置項

python3 -m graphrag.index --init --root ./ragtest

# 基於使用的llm配置 settings.yaml 檔案以及.env檔案

# 載入示例資料

mkdir -p ./ragtest/input

curl https://www.gutenberg.org/cache/epub/24022/pg24022.txt > ragtest/input/book.txt

# 生成index

python -m graphrag.index --root ./ragtest

# 對應Indexing部分輸出為

⠴ GraphRAG Indexer

├── Loading Input (text) - 1 files loaded

├── create_base_text_units

├── create_base_extracted_entities

├── create_summarized_entities

├── create_base_entity_graph

├── create_final_entities

├── create_final_nodes

├── create_final_communities

├── join_text_units_to_entity_ids

├── create_final_relationships

├── join_text_units_to_relationship_ids

├── create_final_community_reports

├── create_final_text_units

├── create_base_documents

└── create_final_documents

🚀 All workflows completed successfully.

# 執行查詢

python -m graphrag.query \

--root ./ragtest \

--method local \

"Who is Scrooge, and what are his main relationships?"

# 查詢返回

SUCCESS: Local Search Response: # Ebenezer Scrooge and His Key Relationships

## Ebenezer Scrooge: The Miserly Central Character

## Scrooge's Past Relationship with Belle

## Scrooge's Deceased Business Partner: Jacob Marley

## Scrooge's Relationship with Bob Cratchit and His Family

## Scrooge's Nephew and Niece

Throughout the story, Ebenezer Scrooge's relationships with these characters serve as catalysts for his personal growth and redemption, as he is confronted with the consequences of his actions and the importance of kindness, generosity, and embracing the Christmas spirit.

由於查詢返回的內容較多,所以並未全部貼出來。從回覆的框架來看,在GraphRAG下,對人物的人際關係有了更全面的掌握,這是標準RAG下非常難以實現的效果。

2. 流程解析

GraphRAG的執行過程(例如前面看到的Indexing過程列印出了很多個步驟)是由一個data pipeline組成,目標是將非結構化的資料經過LLM處理,抽取出有意義的資訊,並儲存為結構化的資料。在Indexing流程執行完畢後,可以在 output/xxx/artifacts/ 目錄下看到一組parquet檔案,用來儲存提取出來(或者經由LLM處理過後的)有意義的資訊。

3. Indexing過程

在前面對文件做Indexing過程時,我們可以看到有一系列的過程,包括:

├── Loading Input (text) - 1 files loaded

├── create_base_text_units

├── create_base_extracted_entities

├── create_summarized_entities

├── create_base_entity_graph

├── create_final_entities

├── create_final_nodes

├── create_final_communities

├── join_text_units_to_entity_ids

├── create_final_relationships

├── join_text_units_to_relationship_ids

├── create_final_community_reports

├── create_final_text_units

├── create_base_documents

└── create_final_documents

GraphRAG的Indexing過程由一組workflow、task、prompt template 以及input/output adapters組成。在上述測試中的預設標準pipeline為:

  1. 文件切片(chunk),做Embedding,生成Text Units
  2. 從文字中提取entities、relationships和claims
  3. 在entities中執行community detection
  4. 在多個粒度級別生成community summaries和reports
  5. 將entities嵌入到graph vector space
  6. 將text chunk嵌入到textual vector space

下面我們結合官方文件[3],梳理整個Indexing的預設workflow流程,一共分為6個階段。

階段1:構建TextUnits

Workflow的第1個階段是將輸入文件處理並轉為TextUnits。TextUnit是在做graph extraction技術的基本單元(也就是一個chunk),也是作為源引用的基本單元。

Chunk size由使用者指定,預設為300(overlap預設100)。使用更大的chunk size可以加快處理速度(另一方面,經由驗證,改為1200的chunk size,可以得到更positive的結果),但是也會導致較低保真度的輸出和較少的有意義的參考文字。

預設為1個document對應多個chunks的關係,但也可以配置多個documents對應多個chunks的關係(適用於document非常短,需要多個文件組成一個有意義的分析單元的場景,例如聊天記錄或者推文)。

對於每個text-unit,都會經過text-embedding的操作,並傳遞給處理pipeline的下一個階段。

第一階段的流程如下所示:

GraphRAG介紹

TextUnits實際資料

前面提到,在構建完索引後,可以在output的artifacts中找到生成的parquet結構化資料檔案。其中即包含create_base_text_units.parquet。對應內容為:

GraphRAG介紹

可以看到id即為chunk_id。document_ids即為原始document的id。Chunk為切分的原始內容。

階段2:圖提取(Graph Extraction)

在這個階段,會對text unit進行分析,並提取出組成graph的基本單元:Entities(實體)、Relationships(關係)和Claims(宣告)。

流程圖為:

GraphRAG介紹

Entities & Relationship提取

在第一步Graph Extraction中,我們使用LLM抽取text unit中的實體和關係。輸出為每個text unit的sub-graph,包含一組entities及其對應的名稱、型別和描述;以及一組relationships及其源、目標和描述。

然後,將這些sub-graph進行合併。任何具有相同名稱和型別的entity視為同一個entity,並將對應的描述內容合併為陣列。同樣,任何具有相同源和目標的relationship視為同一個relationship,並將其描述合併為陣列。

Entity & Relationship Summarization

現在我們有了一個實體和關係的圖,每個實體和關係都有一個描述列表,我們可以將這些列表總結為每個實體和關係的單個描述(透過 LLM 對所有描述做摘要)。這樣,所有的實體和關係都可以有一個簡潔的描述。

Entity解析(預設未啟用)

圖提取的最後一步是解析“在同一個世界裡(或者空間裡),不同名稱的實體,但實際是同一個實體的情況”,透過LLM完成。目前正在探索其他實體解析技術,以希望達到更保守的、非破壞性的方法。

Claim Extraction & Emission

最後,從原始text unit中提取claim。這些claims代表具有評估狀態和時間範圍的積極事實陳述,儲存為Covariates(包含關於實體的陳述,這些陳述可能是time-bound)並輸出

Entity Graph實際資料

原始entities & relationships抽取後生成的對應檔案為create_base_extracted_entities.parquet檔案。可以看到檔案內容只有1行,對應的是entity_graph,可以看到其是一個GraphML格式的圖表示。

GraphRAG介紹

既然是GraphML的格式,那就表示我們可以將其視覺化,對應的視覺化圖(使用Gephi生成,輸出的檔案可能不是規整檔案,需要二次處理):

GraphRAG介紹

從抽取的實體關係來看,可以看到關鍵人物的實體,例如實體BOB(ID與LABEL均為BOB),型別為”PERSON”(其描述為一個列表,包含了各個相關chunk中對BOB的描述),與之有關係的實體包括TINY TIM、PERTER、BOB’s CHILD等。另一方面,我們也可以看到一些相對來說沒有太大意義的實體,例如WEATHER、FIRE、HIM、SUMMER、GAIN等,這些實體對應的型別和描述均為None,且source chunk也僅有1個。

除了節點外,各個邊也有相應的描述,例如BOB和PETER之間的關係描述(Bob is Peter’s father...):

GraphRAG介紹

在原始圖構建出來後,會再次進行加工,生成summarized_entities圖。透過對比同一個實體BOB來看。在原始entity圖中對BOB的描述為多段,透過空格分隔。在summarized_entities圖中,對BOB的描述為1段總結描述。對“邊”的描述也是同樣的處理方式。

階段3:圖增強(Graph Augmentation)

現在有了一個可用的實體和關係圖,接下來希望瞭解它們的社群結構,並用附加資訊來增強這個圖。這分為兩個步驟進行:Community Detection和Graph Embedding。使得我們能夠以顯式(community)和隱式(embedding)的方式理解圖的拓撲結構。

GraphRAG介紹

Community Detection

在此步驟中,使用Hierarchical Leiden 演算法生成實體社群的層次結構。此方法將對我們的圖進行遞迴社群聚類,直到達到社群大小閾值。這使我們能夠理解圖的社群結構,並提供一種在不同粒度級別上導航和總結圖的方法。

Graph Embedding

在此步驟中,使用 Node2Vec 演算法生成圖的向量表示。這將使我們能夠理解圖的隱式結構,並在查詢階段提供一個額外的向量空間,用於搜尋相關概念。

Graph Tables Emission

一旦圖增強步驟完成,經過文字嵌入後(Entity描述做embedding寫入向量資料庫),生成最終的Entities表和Relationships表。

Graph Augment實際資料

在這個workflow中,我們知道首先會做社群檢測,對應的結果會儲存在create_final_communities.parquet檔案中,下面是檔案部分內容:

GraphRAG介紹

可以看到每行是一個社群的資訊,包括所屬層級,所包含的關係id以及text unit id。透過pandas進行分析可以發現,一共生成了67個社群,社群層級劃分為4個層級。

上文也提到,這個流程最終會生成Entities表和Relationships表的資料,對應的檔案分別為create_final_entities.parquet 和 create_final_relationships.parquet。

最終Entities表的內容為:

GraphRAG介紹

GraphRAG介紹

可以看到對每個實體,都標註了其型別(例如上圖的BOB實體型別為PERSON),描述,以及描述對應的embedding向量(會存入向量資料庫,預設為lancedb)。

對應的relationship儲存結構化後的關係表示:

GraphRAG介紹

階段4:社群總結(Community Summarization)

此時,我們已經擁有了實體和關係的功能性圖譜、實體的社群層次結構以及 node2vec 生成圖的嵌入式表達。

現在我們要基於社群資料生成每個社群的報告。這使我們能夠在多個粒度上對圖進行高層次理解。例如,如果社群 A 是頂級社群,我們將獲得關於整個圖的報告。如果社群是低階社群,我們將獲得關於本地叢集的報告。

GraphRAG介紹

生成社群報告

在這個步驟中,使用 LLM 為每個社群生成摘要。這使我們能夠理解每個社群包含的獨特資訊,並從高層次或低層次的角度提供對圖的範圍理解。這些報告包含一個概述,以及引用社群子結構中的關鍵實體、關係和宣告。

總結社群報告

在此步驟中,每個社群報告透過 LLM 進行總結,進行簡化。

Community Embedding

在此步驟中,我們透過生成社群報告、社群報告摘要和社群報告標題這三段文字的文字嵌入,生成社群的向量表示。

社群表生成

在此時,進行一些記錄工作並生成Communities和CommunityReports兩張表。

社群總結實際資料

社群總結後的資料會寫入create_final_comunity_reports.parquet檔案:

GraphRAG介紹

可以看到,在表中記錄了社群的描述、總結,以及對其做的重要性評級與原因。

階段5: 文件處理

這階段為知識模型建立Documents表。

GraphRAG介紹

增加列(僅限 CSV 資料)

如果處理的是 CSV 資料,可以配置工作流向文件輸出新增額外欄位。這些欄位應存在於輸入的 CSV 表中。

連結到Unit Text

在此步驟中,將每個文件與第一階段建立的text-unit連結。這使我們能夠理解哪些document與哪些text-unit相關,反之亦然。

文件嵌入

在此步驟中,透過文件片段的平均嵌入生成文件的向量表示。首先對文件進行re-chunk,且不使用overlap,然後為每個chunk生成embedding。然後建立這些chunk的加權平均值(根據 token 數量加權)並將作為document embedding。這可以幫助我們理解文件之間的隱含關係,並幫助我們生成文件的網路表示。

文件表輸出

將文件表輸出到知識模型中。

文件處理實際資料

這個步驟會輸出create_final_documents.parquet檔案,內容為:

GraphRAG介紹

由於我們只有1個文件,所以僅有1行。

階段6:網路視覺化

在這個階段,執行一些步驟以支援現有圖中高維向量空間的網路視覺化。此時,有兩個邏輯圖在發揮作用:實體關係圖和文件圖。

網路視覺化工作流

對於每個邏輯圖,我們進行 UMAP 降維以生成圖的二維表示。這將使我們能夠在二維空間中視覺化圖,並理解圖中節點之間的關係。UMAP embedding的結果會作為Nodes表輸出。該表的行包括一個指示符,表明節點是文件還是實體,以及 UMAP 座標。

網路視覺化實際資料

這部分資料會生成create_final_nodes.parquet檔案。從檔案內容來看,每個節點是一個實體資訊,以及對應的社群、degree、源id等。但是graph_embedding與x、y座標均為空或0。

4. Querying過程

從上面的介紹,我們瞭解到在構建索引的過程中,GraphRAG會生成實體關係圖、社群層級結構,以及它們的sumamry、source chunk等各種維度的資訊,以向量和結構化的方式進行儲存。下面我們介紹在檢索時如何使用這些資訊來做資訊增強。

Query分兩種型別,分別為Local Search和Global Search。

4.1. Local Search

Local Search是一種基於Entity的回答模式。它結合知識圖譜中的結構化資料和輸入文件中的非結構化資料,在查詢時透過相關實體資訊擴充套件 LLM 上下文。該方法非常適合回答需要理解輸入文件中提到的具體實體的問題(例如,“洋甘菊的治療屬性是什麼?”)。

其流程圖如下所示:

GraphRAG介紹

給定使用者查詢(或加上對話歷史記錄),Local Search會從知識圖譜中識別出一組與使用者輸入在語義上相關的實體。這些實體作為進入知識圖譜的入口點,能夠提取進一步相關的細節,如相連實體、關係、實體協變數(與實體相關的變數)和社群報告。此外,它還從原始輸入文件中提取與已識別實體相關的相關文字chunk。然後對這些候選資料來源進行優先順序排序和過濾,以適應預定義大小的單個上下文視窗,該視窗用於生成對使用者查詢的響應。

4.2. Global Search

Global Search是基於整個資料集的推理。常規RAG在處理需要跨資料集聚合資訊後進行組合回答的場景時很難有很好的表現。例如,“資料中的top 5主題是什麼?”這種問題查詢效果會很差,因為常規RAG的處理方式是:依賴於資料集中存在語意相似的文字內容的向量檢索。如果知識庫中沒有文字內容包含這個問題的答案,則無法給出高質量的回答。

然而,使用 GraphRAG 可以回答此類問題,因為 LLM 生成的知識圖譜的結構可以告訴我們整個資料集的結構(以及主題)。這使得私有資料集能夠組織成有意義的語義clusters,並且這些clusters已被預先總結。透過使用我們的全域性搜尋方法,LLM 在響應使用者查詢時可以使用這些cluster來總結這些主題,並回答使用者對整個資料集的問題。

其流程圖如下所示:

GraphRAG介紹

給定使用者查詢(或加上對話歷史記錄),Global Search使用從圖的社群層次結構中指定級別生成的一系列 LLM 社群報告作為上下文資料,以 map-reduce 方式生成響應。在 map 步驟中,社群報告被分割成預定義大小的文字chunks。每個文字chunk然後用於生成一箇中間響應,其中包含一個要點列表,每個要點都附有一個表示該要點重要性的數值評級。在 reduce 步驟中,從中間響應中篩選出最重要的要點進行彙總,並將其用作上下文生成最終響應。

全域性搜尋響應的質量會受到用於社群報告來源的社群層次結構級別的顯著影響。較低的層次級別報告更為詳細,通常會產生更為全面的響應,但由於報告數量的增加,這也可能增加生成最終響應所需的時間和 LLM 資源。

5. 總結

從初步使用以及對Indexing與Querying的流程瞭解,可以看到GraphRAG的優點非常明顯:

  1. 知識全面:得益於在Indexing過程中構建的實體關係圖、社群、原始文件切片等資訊,在內容檢索時(包含向量檢索與結構化資料檢索),它能夠獲取到更豐富、更多層的資訊,提供更全面的回答
  2. 有據可查:在生成各個索引的過程中,都引入了源文件chunk,保持對源資料的忠誠,使得回答可靠

另一方面,其缺點也非常明顯:

  1. 耗時長、成本高:在Indexing的過程中,需要頻繁呼叫LLM(例如抽取實體、實體和關係的總結、社群總結等)並呼叫外部演算法(例如社群檢測),會使得索引時間拉長,並且LLM呼叫成本高昂(這點在未來可能會有緩解,例如GPT-4o mini的推出,極大降低了LLM呼叫的成本)
  2. 擴充套件性:在資料集膨脹到更高數量級時,除了耗時與成本更長外,其構建Indexing過程的穩定性也需要進一步測試
  3. 延遲高:由於在檢索時需要進行多路召回、過濾和排序,所以在回答問題延遲上也會相應增加

除此之外,還一個可能的缺點是“指代消解”的情況。從上面我們看到的實體關係圖中,可以看到例如HIM、GAIN這類指示意義不明的詞,也會存在例如同一實體的不同名稱可能成為單獨實體節點的情況。從而埋下資訊不準確的隱患。

總的來說,筆者仍然認同GraphRAG是一個非常強大的工具,它可以提高從非結構化資料中提取Insight的能力,彌補現有RAG模式的不足。但它在耗時、成本、擴充套件性等方面仍會是在應用到生產環境中的幾大挑戰點。

最後,在測試階段我們使用了預設給的英文資料集。下一步我們使用中文語料構建一個GraphRAG場景,並結合程式碼的方式再深入介紹檢索流程。

References

[1] GraphRAG: Unlocking LLM discovery on narrative private data: https://www.microsoft.com/en-us/research/blog/graphrag-unlocking-llm-discovery-on-narrative-private-data/

[2] Bedrock Access Gateway: https://github.com/aws-samples/bedrock-access-gateway?tab=readme-ov-file

[3] Indexing Dataflow: https://microsoft.github.io/graphrag/posts/index/1-default_dataflow/