ナレッジグラフ検索用に強化された GraphRAG (Neo4j コードに基づいて実装)

WBOY
リリース: 2024-06-12 10:32:28
オリジナル
1035 人が閲覧しました

グラフ検索拡張生成 (Graph RAG) は徐々に普及しており、従来のベクトル検索方法を強力に補完するものとなっています。この方法では、グラフ データベースの構造的特徴を利用してデータをノードと関係の形式で編成し、それによって取得された情報の深さと文脈上の関連性が強化されます。グラフには、相互に関連する多様な情報を表現および保存するという自然な利点があり、異なるデータ型間の複雑な関係やプロパティを簡単に把握できます。ベクトル データベースはこの種の構造化情報を処理できず、高次元ベクトルで表される非構造化データの処理に重点を置いています。 RAG アプリケーションでは、構造化グラフ データと非構造化テキスト ベクトル検索を組み合わせることで、両方の利点を同時に享受できます。これについてこの記事で説明します。

ナレッジ グラフの構築は、多くの場合、グラフ データ表現の力を活用する上で最も難しいステップです。データの収集と整理が必要であり、そのためにはドメインの知識とグラフ モデリングについての深い理解が必要です。このプロセスを簡素化するには、既存のプロジェクトを参照するか、LLM を使用してナレッジ グラフを作成し、検索と呼び出しに重点を置いて LLM の生成フェーズを改善します。以下の関連コードを練習してみましょう。

1. ナレッジグラフの構築

ナレッジグラフのデータを保存するには、まず Neo4j インスタンスを構築する必要があります。最も簡単な方法は、Neo4j データベースのクラウド バージョンを提供する Neo4j Aura で無料のインスタンスを起動することです。もちろん、Docker を介してローカルで起動し、グラフ データを Neo4j データベースにインポートすることもできます。

ステップ I: Neo4j 環境のセットアップ

以下はローカルで docker を実行する例です:

docker run -d \--restart always \--publish=7474:7474 --publish=7687:7687 \--env NEO4J_AUTH=neo4j/000000 \--volume=/yourdockerVolume/neo4j:/data \neo4j:5.19.0
ログイン後にコピー

ステップ II: グラフ データのインポート

デモでは、エリザベス 1 世の Wikipedia ページを使用できます。 LangChain ローダーを使用して、Wikipedia からドキュメントを取得して分割し、Neo4j データベースに保存します。中国語で効果をテストするために、Github 上のこのプロジェクト (QASystemOnMedicalKG) から医療ナレッジ グラフをインポートしました。これには、約 35,000 個のノードと 300,000 セットのトリプルが含まれています。おおよそ次のような結果が得られました。または、LangChainLangChain ローダーを使用して、大まかに次の手順に示すように、Wikipedia からドキュメントを取得して分割します。

# 读取维基百科文章raw_documents = WikipediaLoader(query="Elizabeth I").load()# 定义分块策略text_splitter = TokenTextSplitter(chunk_size=512, chunk_overlap=24)documents = text_splitter.split_documents(raw_documents[:3])llm=ChatOpenAI(temperature=0, model_name="gpt-4-0125-preview")llm_transformer = LLMGraphTransformer(llm=llm)# 提取图数据graph_documents = llm_transformer.convert_to_graph_documents(documents)# 存储到 neo4jgraph.add_graph_documents(graph_documents, baseEntityLabel=True, include_source=True)
ログイン後にコピー
ナレッジグラフ検索用に強化された GraphRAG (Neo4j コードに基づいて実装)2. ナレッジ グラフの取得 ナレッジ グラフを取得する前に、エンティティと関連属性をベクトル埋め込んで保存する必要があります。 Neo4j データベースへ:

エンティティ情報ベクトル埋め込み: エンティティ名とエンティティの説明情報を結合した後、ベクトル表現モデルを使用してベクトル埋め込みを実行します (以下のサンプル コードの add_embeddings メソッドで示されているように)。

グラフの構造的取得: グラフの構造的取得は 4 つのステップに分かれています: ステップ 1、グラフからクエリに関連するエンティティを取得します。ステップ 2、グローバル インデックスからエンティティのタグを取得します。 、エンティティ タグに基づいて、対応するノードの隣接ノード パスをクエリします。ステップ 4 では、多様性を維持するために関係をフィルタリングします (取得プロセス全体は、以下のサンプル コードの Structured_retriever メソッドに示されています)。

class GraphRag(object):def __init__(self):"""Any embedding function implementing `langchain.embeddings.base.Embeddings` interface."""self._database = 'neo4j'self.label = 'Med'self._driver = neo4j.GraphDatabase.driver(uri=os.environ["NEO4J_URI"],auth=(os.environ["NEO4J_USERNAME"],os.environ["NEO4J_PASSWORD"]))self.embeddings_zh = HuggingFaceEmbeddings(model_name=os.environ["EMBEDDING_MODEL"])self.vectstore = Neo4jVector(embedding=self.embeddings_zh, username=os.environ["NEO4J_USERNAME"], password=os.environ["NEO4J_PASSWORD"], url=os.environ["NEO4J_URI"], node_label=self.label, index_name="vector" )def query(self, query: str, params: dict = {}) -> List[Dict[str, Any]]:"""Query Neo4j database."""from neo4j.exceptions import CypherSyntaxErrorwith self._driver.session(database=self._database) as session:try:data = session.run(query, params)return [r.data() for r in data]except CypherSyntaxError as e:raise ValueError(f"Generated Cypher Statement is not valid\n{e}")def add_embeddings(self):"""Add embeddings to Neo4j database."""# 查询图中所有节点,并且根据节点的描述和名字生成embedding,添加到该节点上query = """MATCH (n) WHERE not (n:{}) RETURN ID(n) AS id, labels(n) as labels, n""".format(self.label)print("qurey node...")data = self.query(query)ids, texts, embeddings, metas = [], [], [], []for row in tqdm(data,desc="parsing node"):ids.append(row['id'])text = row['n'].get('name','') + row['n'].get('desc','')texts.append(text)metas.append({"label": row['labels'], "context": text})self.embeddings_zh.multi_process = Falseprint("node embeddings...")embeddings = self.embeddings_zh.embed_documents(texts)print("adding node embeddings...")ids_ret = self.vectstore.add_embeddings(ids=ids,texts=texts,embeddings=embeddings,metadatas=metas)return ids_ret# Fulltext index querydef structured_retriever(self, query, limit=3, simlarity=0.9) -> str:"""Collects the neighborhood of entities mentioned in the question"""# step1 从图谱中检索与查询相关的实体。docs_with_score = self.vectstore.similarity_search_with_score(query, k=topk)entities = [item[0].page_content for item in data if item[1] > simlarity] # scoreself.vectstore.query("CREATE FULLTEXT INDEX entity IF NOT EXISTS FOR (e:Med) ON EACH [e.context]")result = ""for entity in entities:qry = entity# step2 从全局索引中查出entity label,query1 =f"""CALL db.index.fulltext.queryNodes('entity', '{qry}') YIELD node, score return node.label as label,node.context as context, node.id as id, score LIMIT {limit}"""data1 = self.vectstore.query(query1)# step3 根据label在相应的节点中查询邻居节点路径for item in data1:node_type = item['label']node_type = item['label'] if type(node_type) == str else node_type[0]node_id = item['id']query2 = f"""match (node:{node_type})-[r]-(neighbor) where ID(node) = {node_id} RETURN type(r) as rel, node.name+' - '+type(r)+' - '+neighbor.name as output limit 50"""data2 = self.vectstore.query(query2)# step4 为了保持多样性,对关系进行筛选rel_dict = defaultdict(list)if len(data2) > 3*limit:for item1 in data2:rel_dict[item1['rel']].append(item1['output'])if rel_dict:rel_dict = {k:random.sample(v, 3) if len(v)>3 else v for k,v in rel_dict.items()}result += "\n".join(['\n'.join(el) for el in rel_dict.values()]) +'\n'else:result += "\n".join([el['output'] for el in data2]) +'\n'return result
ログイン後にコピー
  • 3. LLM 生成と組み合わせる
  • 最後に、大規模言語モデル (LLM) を使用して、ナレッジ グラフから取得した構造化情報に基づいて最終応答を生成します。次のコードでは、Tongyi Qianwen のオープンソースの大規模言語モデルを例として取り上げます:
ステップ I: LLM モデルをロードします
from langchain import HuggingFacePipelinefrom transformers import pipeline, AutoTokenizer, AutoModelForCausalLMdef custom_model(model_name, branch_name=None, cache_dir=None, temperature=0, top_p=1, max_new_tokens=512, stream=False):tokenizer = AutoTokenizer.from_pretrained(model_name, revision=branch_name, cache_dir=cache_dir)model = AutoModelForCausalLM.from_pretrained(model_name,device_map='auto',torch_dtype=torch.float16,revision=branch_name,cache_dir=cache_dir)pipe = pipeline("text-generation",model = model,tokenizer = tokenizer,torch_dtype = torch.bfloat16,device_map = 'auto',max_new_tokens = max_new_tokens,do_sample = True)llm = HuggingFacePipeline(pipeline = pipe,model_kwargs = {"temperature":temperature, "top_p":top_p,"tokenizer":tokenizer, "model":model})return llmtongyi_model = "Qwen1.5-7B-Chat"llm_model = custom_model(model_name=tongyi_model)tokenizer = llm_model.model_kwargs['tokenizer']model = llm_model.model_kwargs['model']
ログイン後にコピー

ステップ II: 検索データを入力して応答を生成します

final_data = self.get_retrieval_data(query)prompt = ("请结合以下信息,简洁和专业的来回答用户的问题,若信息与问题关联紧密,请尽量参考已知信息。\n""已知相关信息:\n{context} 请回答以下问题:{question}".format(cnotallow=final_data, questinotallow=query))messages = [{"role": "system", "content": "你是**开发的智能助手。"},{"role": "user", "content": prompt}]text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)model_inputs = tokenizer([text], return_tensors="pt").to(self.device)generated_ids = model.generate(model_inputs.input_ids,max_new_tokens=512)generated_ids = [output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids)]response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]print(response)
ログイン後にコピー
4 結論

クエリ質問は個別にテストされました。RAG を使用せずに LLM のみを使用して応答を生成する状況と比較して、GraphRAG を使用すると、LLM モデルはより大量の情報に応答し、より正確になります。

以上がナレッジグラフ検索用に強化された GraphRAG (Neo4j コードに基づいて実装)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:51cto.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!