paint-brush
रिट्रीवल-ऑगमेंटेड जेनरेशन के लिए नॉलेज ग्राफ़ का उपयोग कैसे करें - बिना ग्राफ़ DB केद्वारा@datastax
2,896 रीडिंग
2,896 रीडिंग

रिट्रीवल-ऑगमेंटेड जेनरेशन के लिए नॉलेज ग्राफ़ का उपयोग कैसे करें - बिना ग्राफ़ DB के

द्वारा DataStax12m2024/04/23
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

यह पोस्ट RAG के लिए ज्ञान ग्राफ के उपयोग की पड़ताल करता है, भंडारण के लिए DataStax Astra DB का उपयोग करता है। उदाहरणों के लिए कोड इस नोटबुक में है, जिसमें Astra DB का उपयोग करके ज्ञान ग्राफ को संग्रहीत करने और पुनर्प्राप्त करने के लिए कुछ प्रोटोटाइप कोड का उपयोग किया गया है।
featured image - रिट्रीवल-ऑगमेंटेड जेनरेशन के लिए नॉलेज ग्राफ़ का उपयोग कैसे करें - बिना ग्राफ़ DB के
DataStax HackerNoon profile picture

पुनर्प्राप्ति संवर्धित पीढ़ी (आरएजी) सूचना प्राप्त करने और जनरेटिव एआई के लिए प्रासंगिक जानकारी प्रदान करने के लिए इसका उपयोग करने की विभिन्न तकनीकों को संदर्भित करता है। सबसे आम रूप टेक्स्ट चंक्स पर काम करता है और इसमें शामिल हैं:


  1. मूल दस्तावेज़ों (HTML, PDF, मार्कडाउन, आदि) से पाठ निकालना।


  2. दस्तावेज़ संरचना और अर्थविज्ञान के आधार पर पाठ को विशिष्ट आकारों में विभाजित करना।


  3. खंड को एक वेक्टर डाटाबेस में संग्रहीत करना, खंड के एम्बेडिंग द्वारा कुंजीबद्ध किया जाता है।


  4. उत्तर तैयार करते समय संदर्भ के रूप में उपयोग के लिए प्रश्न से संबंधित अंशों को पुनः प्राप्त करना।


हालाँकि, वेक्टर समानता पर आधारित RAG में कुछ कमज़ोरियाँ हैं। चूँकि यह प्रश्न के समान जानकारी पर ध्यान केंद्रित करता है, इसलिए कई विषयों से जुड़े प्रश्नों और/या कई हॉप्स की आवश्यकता वाले प्रश्नों का उत्तर देना कठिन होता है - उदाहरण के लिए। इसके अतिरिक्त, यह पुनर्प्राप्त किए गए खंडों की संख्या को सीमित करता है।


प्रत्येक खंड एक अलग स्रोत से आता है, इसलिए ऐसे मामलों में जहां काफी हद तक समान जानकारी कई स्थानों पर मौजूद होती है, उसे जानकारी की कई प्रतियों को प्राप्त करने (और संभवतः अन्य जानकारी को छोड़ देने) या अधिक भिन्न खंड प्राप्त करने के लिए केवल एक प्रति को चुनने के बीच चयन करना पड़ता है, जिससे अन्य स्रोतों की बारीकियां छूट जाती हैं।


ज्ञान रेखांकन वेक्टर-आधारित चंक रिट्रीवल के विकल्प या पूरक के रूप में इस्तेमाल किया जा सकता है। ज्ञान ग्राफ में, नोड्स विशिष्ट संस्थाओं के अनुरूप होते हैं, और किनारे संस्थाओं के बीच संबंधों को दर्शाते हैं। जब RAG के लिए उपयोग किया जाता है, तो प्रश्न से संबंधित संस्थाओं को निकाला जाता है, और फिर उन संस्थाओं और उनके बारे में जानकारी वाले ज्ञान उप-ग्राफ को पुनर्प्राप्त किया जाता है।


समानता-आधारित दृष्टिकोण की तुलना में इस दृष्टिकोण के कई लाभ हैं:

  1. एक ही स्रोत से कई तथ्य निकाले जा सकते हैं और ज्ञान ग्राफ के भीतर कई संस्थाओं से जुड़े हो सकते हैं। इससे किसी दिए गए स्रोत से सिर्फ़ प्रासंगिक तथ्यों को ही निकाला जा सकता है, न कि अप्रासंगिक जानकारी सहित पूरे हिस्से को।


  2. यदि कई स्रोत एक ही बात कहते हैं, तो वे एक ही नोड या किनारा बनाते हैं। इन्हें अलग-अलग तथ्यों के रूप में मानने (और कई प्रतियाँ प्राप्त करने) के बजाय, उन्हें एक ही नोड या किनारा माना जा सकता है और केवल एक बार प्राप्त किया जा सकता है। यह तथ्यों की एक विस्तृत विविधता को पुनः प्राप्त करने और/या केवल उन तथ्यों पर ध्यान केंद्रित करने में सक्षम बनाता है जो कई स्रोतों में दिखाई देते हैं।


  3. ग्राफ को कई चरणों से गुज़ारा जा सकता है - न केवल प्रश्न में मौजूद इकाइयों से सीधे संबंधित जानकारी प्राप्त करना, बल्कि 2 या 3 कदम दूर की चीज़ों को वापस खींचना भी। पारंपरिक RAG दृष्टिकोण में, इसके लिए कई चरणों की क्वेरी की आवश्यकता होगी।


RAG के लिए नॉलेज ग्राफ का उपयोग करने के लाभों के अलावा, LLM ने नॉलेज ग्राफ बनाना भी आसान बना दिया है। नॉलेज ग्राफ को सावधानीपूर्वक तैयार करने के लिए विषय वस्तु विशेषज्ञों की आवश्यकता के बजाय, दस्तावेज़ों से जानकारी निकालने के लिए LLM और प्रॉम्प्ट का उपयोग किया जा सकता है।


यह पोस्ट RAG के लिए ज्ञान ग्राफ के उपयोग की पड़ताल करता है, डेटास्टैक्स एस्ट्रा डीबी भंडारण के लिए। उदाहरणों के लिए कोड इस में है स्मरण पुस्तक एस्ट्रा डीबी का उपयोग करके ज्ञान ग्राफ को संग्रहीत करने और पुनर्प्राप्त करने के लिए कुछ प्रोटोटाइप कोड का उपयोग करना यह भंडार हम दस्तावेजों से ज्ञान ग्राफ निकालने के लिए लैंगचेन के "एलएलएमग्राफट्रांसफॉर्मर" का उपयोग करेंगे, उन्हें एस्ट्रा में लिखेंगे, और ज्ञान निष्कर्षण के लिए उपयोग किए जाने वाले प्रॉम्प्ट को ट्यून करने की तकनीकों पर चर्चा करेंगे।


फिर हम प्रश्न से इकाइयों को निकालने और संबंधित उप-ग्राफ़ को पुनः प्राप्त करने के लिए लैंगचेन रननेबल्स बनाएंगे। हम देखेंगे कि ज्ञान ग्राफ़ का उपयोग करके RAG को लागू करने के लिए आवश्यक संचालन के लिए ग्राफ़ डेटाबेस या ग्राफ़ क्वेरी भाषाओं की आवश्यकता नहीं होती है, जिससे आप पहले से ही उपयोग कर रहे एक विशिष्ट डेटा स्टोर का उपयोग करके दृष्टिकोण को लागू कर सकते हैं।

ज्ञान ग्राफ

जैसा कि पहले बताया गया है, नॉलेज ग्राफ अलग-अलग इकाइयों को नोड्स के रूप में दर्शाता है। उदाहरण के लिए, एक नोड “मैरी क्यूरी” व्यक्ति या “फ़्रेंच” भाषा का प्रतिनिधित्व कर सकता है। लैंगचेन में, प्रत्येक नोड का एक नाम और एक प्रकार होता है। हम नोड की विशिष्ट पहचान करते समय दोनों पर विचार करेंगे, ताकि “फ़्रेंच” भाषा को “फ़्रेंच” राष्ट्रीयता से अलग किया जा सके।


संस्थाओं के बीच संबंध ग्राफ में किनारों के अनुरूप होते हैं। प्रत्येक किनारे में स्रोत (उदाहरण के लिए, मैरी क्यूरी व्यक्ति), लक्ष्य (नोबेल पुरस्कार पुरस्कार) और एक प्रकार शामिल होता है, जो दर्शाता है कि स्रोत लक्ष्य से कैसे संबंधित है (उदाहरण के लिए, "जीता")।


लैंगचेन का उपयोग करके मैरी क्यूरी के बारे में एक पैराग्राफ से निकाला गया एक उदाहरण ज्ञान ग्राफ नीचे दिखाया गया है:


अपने लक्ष्यों के आधार पर, आप नोड्स और किनारों में गुण जोड़ना चुन सकते हैं। उदाहरण के लिए, आप नोबेल पुरस्कार कब जीता गया था और किस श्रेणी में था, यह पहचानने के लिए आप गुण का उपयोग कर सकते हैं। पुनर्प्राप्ति के दौरान ग्राफ़ को पार करते समय किनारों और नोड्स को फ़िल्टर करने के लिए ये उपयोगी हो सकते हैं।

निष्कर्षण: ज्ञान ग्राफ बनाना

ज्ञान ग्राफ में शामिल इकाइयों और संबंधों को सीधे बनाया जा सकता है या मौजूदा ज्ञात-अच्छे डेटा स्रोतों से आयात किया जा सकता है। यह तब उपयोगी होता है जब आप ज्ञान को सावधानीपूर्वक क्यूरेट करना चाहते हैं, लेकिन इससे नई जानकारी को जल्दी से शामिल करना या बड़ी मात्रा में जानकारी को संभालना मुश्किल हो जाता है।


सौभाग्य से, एलएलएम से विषय-वस्तु से जानकारी निकालना आसान हो जाता है, इसलिए हम ज्ञान ग्राफ निकालने के लिए उनका उपयोग कर सकते हैं।


नीचे, मैं उपयोग करता हूँ एलएलएमग्राफट्रांसफॉर्मर मैरी क्यूरी के बारे में कुछ जानकारी से ग्राफ निकालने के लिए LangChain से। यह एक LLM को दस्तावेज़ से नोड्स और किनारों को निकालने का निर्देश देने के लिए एक प्रॉम्प्ट का उपयोग करता है। इसका उपयोग किसी भी दस्तावेज़ के साथ किया जा सकता है जिसे LangChain लोड कर सकता है, जिससे इसे मौजूदा LangChain प्रोजेक्ट में जोड़ना आसान हो जाता है।


LangChain अन्य विकल्पों का समर्थन करता है जैसे डिफबॉट , और आप कुछ उपलब्ध ज्ञान निष्कर्षण मॉडल भी देख सकते हैं, जैसे बागी


 from langchain_experimental.graph_transformers import LLMGraphTransformer from langchain_openai import ChatOpenAI from langchain_core.documents import Document # Prompt used by LLMGraphTransformer is tuned for Gpt4. llm = ChatOpenAI(temperature=0, model_name="gpt-4") llm_transformer = LLMGraphTransformer(llm=llm) text = """ Marie Curie, was a Polish and naturalised-French physicist and chemist who conducted pioneering research on radioactivity. She was the first woman to win a Nobel Prize, the first person to win a Nobel Prize twice, and the only person to win a Nobel Prize in two scientific fields. Her husband, Pierre Curie, was a co-winner of her first Nobel Prize, making them the first-ever married couple to win the Nobel Prize and launching the Curie family legacy of five Nobel Prizes. She was, in 1906, the first woman to become a professor at the University of Paris. """ documents = [Document(page_content=text)] graph_documents = llm_transformer.convert_to_graph_documents(documents) print(f"Nodes:{graph_documents[0].nodes}") print(f"Relationships:{graph_documents[0].relationships}")


यह दिखाता है कि LangChain के LLMGraphTransformer उपयोग करके ज्ञान ग्राफ कैसे निकाला जाता है। आप दृश्य निरीक्षण के लिए LangChain GraphDocument रेंडर करने के लिए रिपॉजिटरी में पाए गए render_graph_document उपयोग कर सकते हैं।


भविष्य की पोस्ट में, हम चर्चा करेंगे कि आप ज्ञान ग्राफ को उसकी संपूर्णता में तथा प्रत्येक दस्तावेज़ से निकाले गए उप-ग्राफ की जांच कैसे कर सकते हैं, तथा स्वचालित निष्कर्षण को बेहतर बनाने के लिए आप त्वरित इंजीनियरिंग और ज्ञान इंजीनियरिंग का प्रयोग कैसे कर सकते हैं।

पुनर्प्राप्ति: उप-ज्ञान ग्राफ के साथ उत्तर देना

ज्ञान ग्राफ का उपयोग करके प्रश्नों का उत्तर देने के लिए कई चरणों की आवश्यकता होती है। हम सबसे पहले यह पहचानते हैं कि ज्ञान ग्राफ के हमारे ट्रैवर्सल को कहाँ से शुरू करना है। इस उदाहरण के लिए, मैं प्रश्न से इकाइयों को निकालने के लिए एक LLM को संकेत दूँगा। फिर, उन शुरुआती बिंदुओं की दी गई दूरी के भीतर सभी संबंधों को पुनः प्राप्त करने के लिए ज्ञान ग्राफ को पार किया जाता है। डिफ़ॉल्ट ट्रैवर्सल गहराई 3 है। प्राप्त संबंधों और मूल प्रश्न का उपयोग LLM के लिए प्रश्न का उत्तर देने के लिए एक संकेत और संदर्भ बनाने के लिए किया जाता है।

प्रश्न से निकाय निकालना

ज्ञान ग्राफ के निष्कर्षण की तरह, किसी प्रश्न में इकाइयों को निकालना एक विशेष मॉडल या किसी विशिष्ट प्रॉम्प्ट वाले LLM का उपयोग करके किया जा सकता है। सरलता के लिए, हम निम्नलिखित प्रॉम्प्ट वाले LLM का उपयोग करेंगे जिसमें प्रश्न और निकालने के प्रारूप के बारे में जानकारी दोनों शामिल हैं। उचित संरचना प्राप्त करने के लिए हम नाम और प्रकार के साथ एक पायडैंटिक मॉडल का उपयोग करते हैं।

 QUERY_ENTITY_EXTRACT_PROMPT = ( "A question is provided below. Given the question, extract up to 5 " "entity names and types from the text. Focus on extracting the key entities " "that we can use to best lookup answers to the question. Avoid stopwords.\n" "---------------------\n" "{question}\n" "---------------------\n" "{format_instructions}\n" ) def extract_entities(llm): prompt = ChatPromptTemplate.from_messages([keyword_extraction_prompt]) class SimpleNode(BaseModel): """Represents a node in a graph with associated properties.""" id: str = Field(description="Name or human-readable unique identifier.") type: str = optional_enum_field(node_types, description="The type or label of the node.") class SimpleNodeList(BaseModel): """Represents a list of simple nodes.""" nodes: List[SimpleNode] output_parser = JsonOutputParser(pydantic_object=SimpleNodeList) return ( RunnablePassthrough.assign( format_instructions=lambda _: output_parser.get_format_instructions(), ) | ChatPromptTemplate.from_messages([QUERY_ENTITY_EXTRACT_PROMPT]) | llm | output_parser | RunnableLambda( lambda node_list: [(n["id"], n["type"]) for n in node_list["nodes"]]) )


उपरोक्त उदाहरण को चलाने पर हम निकाले गए निकायों को देख सकते हैं:

 # Example showing extracted entities (nodes) extract_entities(llm).invoke({ "question": "Who is Marie Curie?"}) # Output: [Marie Curie(Person)]


बेशक, एक प्रश्न से इकाइयों को निकालने के लिए एक श्रृंखला में लैंगचेन रननेबल का उपयोग किया जा सकता है।


भविष्य में, हम इकाई निष्कर्षण को बेहतर बनाने के तरीकों पर चर्चा करेंगे, जैसे कि नोड गुणों पर विचार करना या प्रासंगिक शुरुआती बिंदुओं की पहचान करने के लिए वेक्टर एम्बेडिंग और समानता खोज का उपयोग करना। इस पहली पोस्ट को सरल रखने के लिए, हम उपरोक्त संकेत के साथ बने रहेंगे, और knowledge-subgraph को पुनः प्राप्त करने के लिए ज्ञान ग्राफ को पार करने के लिए आगे बढ़ेंगे और संकेत में संदर्भ के रूप में इसे शामिल करेंगे।

उप-ज्ञान ग्राफ को पुनः प्राप्त करना

पिछली श्रृंखला हमें प्रश्न में नोड्स देती है। हम प्रासंगिक ज्ञान त्रिगुणों को पुनः प्राप्त करने के लिए उन संस्थाओं और ग्राफ स्टोर का उपयोग कर सकते हैं। RAG के साथ, हम उन्हें संदर्भ के भाग के रूप में प्रॉम्प्ट में डालते हैं और उत्तर उत्पन्न करते हैं।

 def _combine_relations(relations): return "\n".join(map(repr, relations)) ANSWER_PROMPT = ( "The original question is given below." "This question has been used to retrieve information from a knowledge graph." "The matching triples are shown below." "Use the information in the triples to answer the original question.\n\n" "Original Question: {question}\n\n" "Knowledge Graph Triples:\n{context}\n\n" "Response:" ) chain = ( { "question": RunnablePassthrough() } # extract_entities is provided by the Cassandra knowledge graph library # and extracts entitise as shown above. | RunnablePassthrough.assign(entities = extract_entities(llm)) | RunnablePassthrough.assign( # graph_store.as_runnable() is provided by the CassandraGraphStore # and takes one or more entities and retrieves the relevant sub-graph(s). triples = itemgetter("entities") | graph_store.as_runnable()) | RunnablePassthrough.assign( context = itemgetter("triples") | RunnableLambda(_combine_relations)) | ChatPromptTemplate.from_messages([ANSWER_PROMPT]) | llm )


किसी प्रश्न का उत्तर देने के लिए उपरोक्त श्रृंखला को क्रियान्वित किया जा सकता है। उदाहरण के लिए:

 chain.invoke("Who is Marie Curie?") # Output AIMessage( content="Marie Curie is a Polish and French chemist, physicist, and professor who " "researched radioactivity. She was married to Pierre Curie and has worked at " "the University of Paris. She is also a recipient of the Nobel Prize.", response_metadata={ 'token_usage': {'completion_tokens': 47, 'prompt_tokens': 213, 'total_tokens': 260}, 'model_name': 'gpt-4', ... } )

यात्रा करें, प्रश्न न करें

हालांकि ज्ञान ग्राफ को संग्रहीत करने के लिए ग्राफ डीबी का उपयोग करना सहज लग सकता है, लेकिन यह वास्तव में आवश्यक नहीं है। कुछ नोड्स के आसपास उप-ज्ञान ग्राफ को पुनः प्राप्त करना एक सरल ग्राफ ट्रैवर्सल है, जबकि ग्राफ डीबी को गुणों के विशिष्ट अनुक्रमों वाले पथों की खोज करने वाले अधिक जटिल प्रश्नों के लिए डिज़ाइन किया गया है। इसके अलावा, ट्रैवर्सल अक्सर केवल 2 या 3 की गहराई तक होता है, क्योंकि दूर स्थित नोड्स बहुत जल्दी प्रश्न के लिए अप्रासंगिक हो जाते हैं। इसे सरल क्वेरीज़ के कुछ राउंड (प्रत्येक चरण के लिए एक) या SQL जॉइन के रूप में व्यक्त किया जा सकता है।


अलग से ग्राफ डेटाबेस की आवश्यकता को समाप्त करने से नॉलेज ग्राफ का उपयोग करना आसान हो जाता है। इसके अतिरिक्त, एस्ट्रा डीबी या अपाचे कैसंड्रा का उपयोग करके ग्राफ और उसी स्थान पर संग्रहीत अन्य डेटा दोनों में लेन-देन संबंधी लेखन को सरल बनाया जाता है, और संभवतः बेहतर स्केल किया जाता है। यह ओवरहेड केवल तभी सार्थक होगा जब आप ग्रेमलिन या साइफर या कुछ इसी तरह का उपयोग करके ग्राफ क्वेरी उत्पन्न और निष्पादित करने की योजना बना रहे हों।


लेकिन यह उप-ज्ञान ग्राफ को पुनः प्राप्त करने के लिए अत्यधिक है, और यह कई अन्य समस्याओं के लिए द्वार खोलता है, जैसे कि ऐसे प्रश्न जो प्रदर्शन के मामले में पटरी से उतर जाते हैं।


इस ट्रैवर्सल को पायथन में लागू करना आसान है। CQL और कैसंड्रा ड्राइवर का उपयोग करके इसे (सिंक्रोनस और एसिंक्रोनस दोनों तरह से) लागू करने के लिए पूरा कोड यहाँ पाया जा सकता है। कोष एसिंक्रोनस ट्रैवर्सल का मूल चित्रण नीचे दिया गया है:


 def fetch_relation(tg: asyncio.TaskGroup, depth: int, source: Node) -> AsyncPagedQuery: paged_query = AsyncPagedQuery( depth, session.execute_async(query, (source.name, source.type)) ) return tg.create_task(paged_query.next()) results = set() async with asyncio.TaskGroup() as tg: if isinstance(start, Node): start = [start] discovered = {t: 0 for t in start} pending = {fetch_relation(tg, 1, source) for source in start} while pending: done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED) for future in done: depth, relations, more = future.result() for relation in relations: results.add(relation) # Schedule the future for more results from the same query. if more is not None: pending.add(tg.create_task(more.next())) # Schedule futures for the next step. if depth < steps: # We've found a path of length `depth` to each of the targets. # We need to update `discovered` to include the shortest path. # And build `to_visit` to be all of the targets for which this is # the new shortest path. to_visit = set() for r in relations: previous = discovered.get(r.target, steps + 1) if depth < previous: discovered[r.target] = depth to_visit.add(r.target) for source in to_visit: pending.add(fetch_relation(tg, depth + 1, source)) return results


निष्कर्ष

इस लेख में दिखाया गया है कि प्रश्न उत्तर के लिए ज्ञान ग्राफ निष्कर्षण और पुनर्प्राप्ति का निर्माण और उपयोग कैसे करें। मुख्य बात यह है कि आज ऐसा करने के लिए आपको ग्रेमलिन या साइफर जैसी ग्राफ क्वेरी भाषा वाले ग्राफ डेटाबेस की आवश्यकता नहीं है। एस्ट्रा जैसा एक बेहतरीन डेटाबेस जो समानांतर रूप से कई क्वेरी को कुशलतापूर्वक संभालता है, पहले से ही इसे संभाल सकता है।


वास्तव में, आप किसी विशिष्ट क्वेरी का उत्तर देने के लिए आवश्यक उप-ज्ञान ग्राफ़ को पुनः प्राप्त करने के लिए क्वेरीज़ का एक सरल अनुक्रम लिख सकते हैं। यह आपकी वास्तुकला को सरल रखता है (कोई अतिरिक्त निर्भरता नहीं) और आपको अनुमति देता है तुरंत शुरू करें !


हमने कैसंड्रा और एस्ट्रा डीबी के लिए ग्राफआरएजी पैटर्न को लागू करने के लिए इन्हीं विचारों का उपयोग किया है। हम उन्हें लैंगचेन में योगदान देने जा रहे हैं और भविष्य में एलएलएम के साथ ज्ञान ग्राफ के उपयोग में अन्य सुधार लाने पर काम करेंगे!


बेन चैम्बर्स, डेटास्टैक्स द्वारा