Penskalaan inferensi dalam kecerdasan buatan (AI) mengacu pada teknik yang meningkatkan kinerja model dengan mengalokasikan sumber daya komputasi selama fase inferensi (saat model menghasilkan output) daripada mengandalkan kumpulan data pelatihan yang lebih besar atau arsitektur model. Karena model bahasa besar (LLM) terus berkembang dalam parameter model dan skala kumpulan data, mengoptimalkan waktu inferensi dan mengelola penskalaan komputasi inferensi, khususnya pada perangkat keras GPU, telah menjadi tantangan utama dalam menerapkan sistem Retrieval-Augmented Generation (RAG) multimodal berkinerja tinggi.
Kemajuan terbaru dalam strategi inferensi yang meningkatkan sumber daya komputasi dan menggunakan algoritma kompleks pada waktu pengujian—meninjau kembali bagaimana LLM menangani tugas-tugas penalaran yang kompleks dan memberikan output berkualitas lebih tinggi di beragam modalitas input. Penskalaan inferensi mengoptimalkan rantai pemikiran (CoT) dengan memperluas kedalaman penalaran. Perluasan ini memungkinkan model untuk menghasilkan rantai pemikiran yang lebih panjang dan lebih terperinci melalui dorongan berulang atau pembuatan dengan banyak langkah. Penskalaan inferensi dapat dimanfaatkan untuk meningkatkan multimodal RAG dengan berfokus pada interaksi antara ukuran model, anggaran komputer, dan optimalisasi praktis waktu inferensi untuk aplikasi dunia nyata.
Selain itu, hukum penskalaan dan hasil tolok ukur menekankan pada kompromi antara prapelatihan, penyempurnaan, strategi waktu inferensi, dan algoritma tingkat lanjut untuk output pemilihan. Baik model yang lebih besar maupun model yang lebih kecil mendapat manfaat dari penskalaan inferensi karena ini juga memungkinkan sistem dengan sumber daya terbatas mendekati kinerja LLM canggih. Tutorial ini menunjukkan dampak teknik pengoptimalan pada kinerja model, menawarkan panduan yang dapat ditindaklanjuti untuk menyeimbangkan akurasi, latensi, dan biaya dalam penerapan RAG multimodal.
Tutorial ini dirancang untuk pengembang kecerdasan buatan, peneliti, dan penggemar yang ingin meningkatkan pengetahuan mereka tentang manajemen dokumen dan teknik pemrosesan bahasa alami (NLP) tingkat lanjut. Anda akan belajar cara memanfaatkan kekuatan penskalaan inferensi untuk meningkatkan saluran RAG multimodal yang dibuat dalam resep sebelumnya. Meskipun tutorial ini berfokus pada strategi untuk skalabilitas dalam RAG multimodal yang secara khusus berfokus pada model bahasa besar IBM Granite, prinsip serupa berlaku untuk sebagian besar model populer termasuk model dari OpenAI (misalnya, GPT-4, GPT-4o, ChatGPT) dan DeepMind.
Tutorial ini memandu Anda melalui proses berikut:
Selama tutorial ini, Anda juga akan menggunakan tiga teknologi mutakhir:
Pada akhir tutorial ini, Anda akan mencapai hal berikut:
Model bahasa tradisional berjuang dengan konteks yang panjang karena beberapa alasan:
Teknik dalam tutorial ini mengatasi tantangan ini melalui alokasi strategis perhitungan inferensi.
Lebih lanjut tentang dua teknik penskalaan inferensi tingkat lanjut ini (DRAG dan IterDRAG) dapat ditemukan dalam makalah riset “Inference Scaling for Long-Context Retrieval Augmented Generation“
Metode ini menunjukkan bahwa komputasi inferensi penskalaan dapat meningkatkan kinerja RAG hampir secara linier ketika dialokasikan secara optimal, memungkinkan sistem RAG untuk memanfaatkan kemampuan konteks panjang LLM modern dengan lebih baik. Untuk implementasi ini, kita akan menggunakan model IBM Granite yang mampu memproses modalitas berbeda. Anda akan membuat sistem AI untuk menjawab pertanyaan pengguna real-time dari data tidak terstruktur, menerapkan prinsip-prinsip dari makalah ini.
Pastikan Anda menjalankan Python 3.10, 3.11, atau 3.12 di lingkungan virtual yang baru dibuat. Perhatikan, Anda juga dapat mengakses tutorial ini di GitHub.
import sys
assert sys.version_info >= (3, 10) and sys.version_info < (3, 13), "Use Python 3.10, 3.11, or 3.12 to run this notebook."
! pip install "git+https://github.com/ibm-granite-community/utils.git" \
transformers \
pillow \
langchain_community \
langchain_huggingface \
langchain_milvus \
docling \
replicate
Untuk melihat beberapa informasi pencatatan, kita dapat mengonfigurasi level log INFO.
CATATAN: Anda dapat melewatkan menjalankan sel ini.
import logging
logging.basicConfig(level=logging.INFO)
Tentukan model penanaman yang akan digunakan untuk menghasilkan vektor menanamkan teks. Di sini kita akan menggunakan salah satu model Penanaman Granite.
Untuk menggunakan model penanaman yang berbeda, ganti sel kode ini dengan salah satu dari resep Model Penanaman ini.
from langchain_huggingface import HuggingFaceEmbeddings
from transformers import AutoTokenizer
embeddings_model_path = "ibm-granite/granite-embedding-30m-english"
embeddings_model = HuggingFaceEmbeddings(
model_name=embeddings_model_path,
)
embeddings_tokenizer = AutoTokenizer.from_pretrained(embeddings_model_path)
Tentukan MLLM yang akan digunakan untuk pemahaman gambar. Kami akan menggunakan model visi Granite.
from ibm_granite_community.notebook_utils import get_env_var
from langchain_community.llms import Replicate
from transformers import AutoProcessor
vision_model_path = "ibm-granite/granite-vision-3.2-2b"
vision_model = Replicate(
model=vision_model_path,
replicate_api_token=get_env_var("REPLICATE_API_TOKEN"),
model_kwargs={
"max_tokens": embeddings_tokenizer.max_len_single_sentence, # Set the maximum number of tokens to generate as output.
"min_tokens": 100, # Set the minimum number of tokens to generate as output.
"temperature": 0.01,
},
)
vision_processor = AutoProcessor.from_pretrained(vision_model_path)
Tentukan model bahasa yang akan digunakan untuk operasi pembuatan RAG. Di sini kami menggunakan klien Replicate LangChain untuk terhubung ke model Granite dari ibm-graniteorg di Replicate.
Untuk menyiapkan Replicate, lihat Memulai Replicate.
Untuk terhubung ke model pada penyedia selain Replicate, ganti sel kode ini dengan salah satu dari resep komponen LLM.
model_path = "ibm-granite/granite-3.3-8b-instruct"
model = Replicate(
model=model_path,
replicate_api_token=get_env_var("REPLICATE_API_TOKEN"),
model_kwargs={
"max_tokens": 1000, # Set the maximum number of tokens to generate as output.
"min_tokens": 100, # Set the minimum number of tokens to generate as output.
"temperature": 0.01
},
)
tokenizer = AutoTokenizer.from_pretrained(model_path)
from docling.document_converter import DocumentConverter, PdfFormatOption
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import PdfPipelineOptions
pdf_pipeline_options = PdfPipelineOptions(
do_ocr=False,
generate_picture_images=True,
)
format_options = {
InputFormat.PDF: PdfFormatOption(pipeline_options=pdf_pipeline_options),
}
converter = DocumentConverter(format_options=format_options)
sources = [
"https://midwestfoodbank.org/images/AR_2020_WEB2.pdf",
]
conversions = { source: converter.convert(source=source).document for source in sources }
Setelah dokumen diproses, kami kemudian memproses lebih lanjut elemen teks dalam dokumen dan memotongnya menjadi ukuran yang sesuai untuk model penanaman yang kami gunakan. Daftar dokumen LangChain dibuat dari potongan teks.
from docling_core.transforms.chunker.hybrid_chunker import HybridChunker
from docling_core.types.doc import DocItem, TableItem
from langchain_core.documents import Document
doc_id = 0
texts: list[Document] = []
for source, docling_document in conversions.items():
for chunk in HybridChunker(tokenizer=embeddings_tokenizer).chunk(docling_document):
items: list[DocItem] = chunk.meta.doc_items # type: ignore
if len(items) == 1 and isinstance(items[0], TableItem):
continue # we will process tables later
refs = " ".join(map(lambda item: item.get_ref().cref, items))
print(refs)
text = chunk.text
document = Document(
page_content=text,
metadata={
"doc_id": (doc_id:=doc_id+1),
"source": source,
"ref": refs,
},
)
texts.append(document)
print(f"{len(texts)} text document chunks created")
Selanjutnya, kami memproses tabel apa pun dalam dokumen. Kami mengonversi data tabel ke format markdown sehingga model bahasa dapat memprosesnya. Daftar dokumen LangChain dibuat dari rendering markdown tabel.
from docling_core.types.doc import DocItemLabel
doc_id = len(texts)
tables: list[Document] = []
for source, docling_document in conversions.items():
for table in docling_document.tables:
if table.label in [DocItemLabel.TABLE]:
ref = table.get_ref().cref
print(ref)
text = table.export_to_markdown(docling_document)
document = Document(
page_content=text,
metadata={
"doc_id": (doc_id:=doc_id+1),
"source": source,
"ref": ref
},
)
tables.append(document)
print(f"{len(tables)} table documents created")
Terakhir, kami memproses gambar apa pun dalam dokumen. Di sini kita menggunakan model bahasa visi untuk memahami konten gambar. Dalam contoh ini, kami tertarik pada informasi tekstual apa pun dalam gambar.
Memilih prompt gambar yang sesuai sangat penting karena ini mengarahkan aspek apa dari gambar yang akan menjadi fokus model. Sebagai contoh:
CATATAN: Pemrosesan gambar mungkin memerlukan waktu pemrosesan yang signifikan berdasarkan jumlah gambar dan layanan yang menjalankan model bahasa visi.
import base64
import io
import PIL.Image
import PIL.ImageOps
def encode_image(image: PIL.Image.Image, format: str = "png") -> str:
image = PIL.ImageOps.exif_transpose(image) or image
image = image.convert("RGB")
buffer = io.BytesIO()
image.save(buffer, format)
encoding = base64.b64encode(buffer.getvalue()).decode("utf-8")
uri = f"data:image/{format};base64,{encoding}"
return uri
# Feel free to experiment with this prompt
image_prompt = "Give a detailed description of what is depicted in the image"
conversation = [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": image_prompt},
],
},
]
vision_prompt = vision_processor.apply_chat_template(
conversation=conversation,
add_generation_prompt=True,
)
pictures: list[Document] = []
doc_id = len(texts) + len(tables)
for source, docling_document in conversions.items():
for picture in docling_document.pictures:
ref = picture.get_ref().cref
print(ref)
image = picture.get_image(docling_document)
if image:
text = vision_model.invoke(vision_prompt, image=encode_image(image))
document = Document(
page_content=text,
metadata={
"doc_id": (doc_id:=doc_id+1),
"source": source,
"ref": ref,
},
)
pictures.append(document)
print(f"{len(pictures)} image descriptions created")
Kita kemudian dapat menampilkan dokumen LangChain yang dibuat dari dokumen input.
import itertools
from docling_core.types.doc import RefItem
from IPython.display import display
# Print all created documents
for document in itertools.chain(texts, tables):
print(f"Document ID: {document.metadata['doc_id']}")
print(f"Source: {document.metadata['source']}")
print(f"Content:\n{document.page_content}")
print("=" * 80) # Separator for clarity
for document in pictures:
print(f"Document ID: {document.metadata['doc_id']}")
source = document.metadata['source']
print(f"Source: {source}")
print(f"Content:\n{document.page_content}")
docling_document = conversions[source]
ref = document.metadata['ref']
picture = RefItem(cref=ref).resolve(docling_document)
image = picture.get_image(docling_document)
print("Image:")
display(image)
print("=" * 80) # Separator for clarity
Dengan menggunakan model penanaman, kami memuat dokumen dari potongan teks dan keterangan gambar yang dihasilkan ke dalam basis data vektor. Membuat basis data vektor ini memungkinkan kita untuk dengan mudah melakukan pencarian kemiripan semantik di seluruh dokumen.
CATATAN: Pengisian basis data vektor mungkin memerlukan waktu pemrosesan yang signifikan, tergantung pada model penanaman dan layanan Anda.
Tentukan basis data yang akan digunakan untuk menyimpan dan mengambil vektor penanaman. Untuk keperluan tutorial ini kami akan menggunakan Milvus melalui Langchain. Sebagai basis data vektor, Milvus akan menyimpan, membuat indeks, dan mengelola penanaman numerik yang dihasilkan oleh neural networks dan berbagai algoritma ML.
Untuk terhubung ke basis data vektor selain Milvus, ganti sel kode ini dengan salah satu dari resep Penyimpanan Vektor ini.
import tempfile
from langchain_core.vectorstores import VectorStore, VectorStoreRetriever
from langchain_milvus import Milvus
db_file = tempfile.NamedTemporaryFile(prefix="vectorstore_", suffix=".db", delete=False).name
print(f"The vector database will be saved to {db_file}")
vector_db: VectorStore = Milvus(
embedding_function=embeddings_model,
connection_args={"uri": db_file},
auto_id=True,
enable_dynamic_field=True,
index_params={"index_type": "AUTOINDEX"},
)
Sekarang, kami menambahkan semua dokumen LangChain untuk teks, tabel, dan deskripsi gambar ke basis data vektor.
import itertools
documents = list(itertools.chain(texts, tables, pictures))
ids = vector_db.add_documents(documents)
print(f"{len(ids)} documents added to the vector database")
retriever: VectorStoreRetriever = vector_db.as_retriever(search_kwargs={"k": 10})
Sekarang setelah berhasil mengonversi dokumen dan melakukan vektorisasi, kami dapat mengatur saluran RAG.
Di sini kita menguji basis data vektor dengan mencari potongan dengan informasi yang relevan dengan kueri kita di ruang vektor. Kami menampilkan dokumen yang terkait dengan deskripsi gambar yang diambil.
Langkah validasi ini penting untuk membantu memastikan bahwa sistem pengambilan kami bekerja dengan benar sebelum membangun saluran RAG lengkap. Kami ingin melihat apakah dokumen yang ditampilkan relevan dengan kueri kami.
Jangan ragu untuk mencoba pertanyaan yang berbeda.
query = "Analyze how Midwest Food Bank's financial efficiency changed during the pandemic by comparing their 2019 and 2020 performance metrics. What specific pandemic adaptations had the greatest impact on their operational capacity, and how did their volunteer management strategy evolve to maintain service levels despite COVID-19 restrictions? Provide specific statistics from the report to support your analysis."
for doc in vector_db.as_retriever().invoke(query):
print(doc)
print("=" * 80) # Separator for clarity
Dokumen yang ditampilkan harus responsif terhadap kueri. Mari kita lanjutkan dan membangun saluran RAG.
Dokumen yang ditampilkan harus responsif terhadap kueri. Mari kita lanjutkan dan membangun saluran RAG.
Pertama, kami membuat prompt untuk Granite untuk melakukan kueri RAG. Kami menggunakan templat obrolan Granite dan memberikan nilai placeholder yang akan digantikan oleh saluran LangChain RAG.
{context} akan menyimpan potongan yang diambil, seperti yang ditunjukkan dalam pencarian sebelumnya, dan memasukkannya ke model sebagai konteks dokumen untuk menjawab pertanyaan kami.
Kemudian, kami membangun saluran RAG menggunakan templat prompt Granite yang kami buat.
from ibm_granite_community.notebook_utils import escape_f_string
from langchain.prompts import PromptTemplate
from langchain.chains.retrieval import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
# Create a Granite prompt for question-answering with the retrieved context
prompt = tokenizer.apply_chat_template(
conversation=[{
"role": "user",
"content": "{input}",
}],
documents=[{
"doc_id": "0",
"text": "{context}",
}],
add_generation_prompt=True,
tokenize=False,
)
prompt_template = PromptTemplate.from_template(template=escape_f_string(prompt, "input", "context"))
# Create a Granite document prompt template to wrap each retrieved document
document_prompt_template = PromptTemplate.from_template(template="""\
<|end_of_text|>
<|start_of_role|>document {{"document_id": "{doc_id}"}}<|end_of_role|>
{page_content}""")
document_separator=""
# Assemble the retrieval-augmented generation chain
combine_docs_chain = create_stuff_documents_chain(
llm=model,
prompt=prompt_template,
document_prompt=document_prompt_template,
document_separator=document_separator,
)
rag_chain = create_retrieval_chain(
retriever=retriever,
combine_docs_chain=combine_docs_chain,
)
Saluran ini menggunakan kueri untuk menemukan dokumen dari basis data vektor dan menggunakannya sebagai konteks untuk kueri.
outputs = rag_chain.invoke({"input": query})
print(outputs['answer'])
Meskipun pendekatan RAG standar bekerja dengan cukup baik, namun pendekatan ini memiliki beberapa keterbatasan utama ketika berurusan dengan konten yang panjang atau kompleks:
Teknik penskalaan inferensi mengatasi keterbatasan ini dengan mengalokasikan lebih banyak komputasi secara strategis pada waktu inferensi.
Sekarang kita akan mengimplementasikan teknik DRAG dari makalah riset "Inference Scaling for Long-Context Retrieval Augmented Generation" untuk meningkatkan sistem RAG kita.
DRAG menggunakan contoh dalam konteks untuk mendemonstrasikan pada model cara mengekstraksi dan menggunakan informasi dari dokumen, sehingga meningkatkan kinerja untuk skenario dengan konteks panjang.
Ini biasanya berasal dari kumpulan data yang dikurasi dari pasangan QA berkualitas tinggi. Untuk tujuan ini, kita akan membuat beberapa contoh sintetis yang sesuai dengan domain yang diharapkan.
Di sini, kita menetapkan kelas data untuk menunjukkan tiap demonstrasi dan kemudian membuat beberapa demonstrasi.
from dataclasses import dataclass, field, InitVar
from langchain_core.documents import Document
@dataclass
class DRAG_Demonstration:
query: str
answer: str
retriever: InitVar[VectorStoreRetriever] = field(kw_only=True)
documents: list[Document] = field(default_factory=list, kw_only=True)
def __post_init__(self, retriever: VectorStoreRetriever):
if not self.documents:
self.documents = retriever.invoke(self.query)
def __format__(self, format_spec: str) -> str:
formatted_documents = "\n".join(
f"Document {i+1}:\n{document.page_content}"
for i, document in enumerate(self.documents)
)
return f"""\
{formatted_documents}
Question: {self.query}
Answer: {self.answer}
"""
def create_enhanced_drag_demonstrations(vector_db: VectorStore) -> list[DRAG_Demonstration]:
"""Create high-quality demonstrations for DRAG technique that showcase effective document analysis"""
demonstration_retriever: VectorStoreRetriever = vector_db.as_retriever(search_kwargs={"k": 5})
demonstrations = [
DRAG_Demonstration(
query="How did the COVID-19 pandemic impact Midwest Food Bank's operations in 2020?",
answer="The COVID-19 pandemic significantly impacted Midwest Food Bank's operations in 2020. Despite challenges, MFB remained open and responsive to increased needs. They implemented safety protocols, reduced volunteer numbers for social distancing, and altered their distribution model to allow partner agencies to receive food safely. The pandemic created unprecedented food insecurity, with many people seeking assistance for the first time. MFB distributed 37% more food than in 2019, with a record 179 semi-loads of Disaster Relief family food boxes sent nationwide. The organization also faced supply chain disruptions and food procurement challenges in the early months but continued to find and distribute food. Community, business, and donor support helped fund operations and food purchases. Additionally, MFB began participating in the USDA Farmers to Families Food Box program in May 2020, distributing over $52 million worth of nutritious produce, protein, and dairy products.",
retriever=demonstration_retriever
),
DRAG_Demonstration(
query="What role did volunteers play at Midwest Food Bank during 2020, and how were they affected by the pandemic?",
answer="Volunteers were described as 'the life-blood of the organization' in the 2020 annual report. Despite the pandemic creating safety challenges, volunteers demonstrated courage and dedication by increasing their hours to meet growing needs. MFB implemented safety protocols at each location and limited volunteer group sizes to allow for social distancing. This created a challenge as food needs increased while fewer volunteers were available to help. To address this gap, multiple MFB locations received assistance from the National Guard, who filled vital volunteer positions driving trucks, operating forklifts, and helping with food distributions. In 2020, 17,930 individuals volunteered 300,898 hours of service, equivalent to 150 full-time employees. The volunteer-to-staff ratio was remarkable with 450 volunteers for every 1 paid MFB staff member, highlighting the volunteer-driven nature of the organization during the crisis.",
retriever=demonstration_retriever
),
DRAG_Demonstration(
query="How did Midwest Food Bank's international programs perform during 2020, particularly in Haiti and East Africa?",
answer="In 2020, Midwest Food Bank's international operations in East Africa and Haiti faced unique challenges but continued to serve communities. In East Africa (operated as Kapu Africa), strict lockdowns led to mass hunger, especially in slum areas. Kapu Africa distributed 7.2 million Tender Mercies meals, working with partner ministries to share food in food-insecure slums. A notable outcome was a spiritual awakening among recipients, with many asking why they were receiving help. In Haiti, the pandemic added to existing challenges, closing airports, seaports, factories, and schools. MFB Haiti more than doubled its food shipments to Haiti, delivering over 160 tons of food relief, nearly three-quarters being Tender Mercies meals. As Haitian children primarily receive nourishment from school lunches, MFB Haiti distributed Tender Mercies through faith-based schools and also partnered with over 20 feeding centers serving approximately 1,100 children daily. Nearly 1 million Tender Mercies meals were distributed in Haiti during 2020.",
retriever=demonstration_retriever
),
]
return demonstrations
Kami kemudian memformat semua demonstrasi bersama-sama untuk prompt.
# Format all demonstrations together
demonstrations = create_enhanced_drag_demonstrations(vector_db)
formatted_demonstrations = "\n\n".join(
f"Example {i+1}:\n{demo}"
for i, demo in enumerate(demonstrations)
)
Kemudian kami membuat prompt DRAG untuk model yang menyertakan contoh demonstrasi yang diformat.
drag_prompt = tokenizer.apply_chat_template(
conversation=[{
"role": "user",
"content": f"""\
Here are examples of effectively extracting information from documents to answer questions.
{formatted_demonstrations}
Follow these examples when answering the user's question:
{{input}}""",
}],
documents=[{
"doc_id": "0",
"text": "Placeholder{context}",
}],
add_generation_prompt=True,
tokenize=False,
)
# Convert to prompt template
drag_prompt_template = PromptTemplate.from_template(template=escape_f_string(drag_prompt, "input", "context"))
Biasanya pengambil akan menampilkan dokumen dalam urutan kesamaan, dokumen yang paling mirip akan ditampilkan pertama. Kami menetapkan agar pengambil penyusunan ulang membalikkan urutan hasil. Urutan sekarang menampilkan dokumen yang paling mirip sebagai urutan terakhir, sehingga lebih dekat ke akhir prompt.
import typing
from langchain_core.retrievers import BaseRetriever, RetrieverInput, RetrieverOutput
from langchain_core.callbacks.manager import CallbackManagerForRetrieverRun
class ReorderingRetriever(BaseRetriever):
base_retriever: BaseRetriever
def _get_relevant_documents(
self, query: RetrieverInput, *, run_manager: CallbackManagerForRetrieverRun, **kwargs: typing.Any
) -> RetrieverOutput:
docs = self.base_retriever._get_relevant_documents(query, run_manager=run_manager, **kwargs)
return list(reversed(docs)) # Reverse the order so higher-ranked docs are closer to query in prompt
reordering_retriever = ReorderingRetriever(base_retriever=retriever)
Kami membuat saluran untuk kueri DRAG menggunakan templat prompt DRAG dan pengambil penyusunan ulang.
drag_combine_docs_chain = create_stuff_documents_chain(
llm=model,
prompt=drag_prompt_template,
document_prompt=document_prompt_template,
document_separator=document_separator,
)
drag_chain = create_retrieval_chain(
retriever=reordering_retriever,
combine_docs_chain=drag_combine_docs_chain,
)
drag_outputs = drag_chain.invoke({"input": query})
print("\n=== DRAG-Enhanced Answer ===")
print(drag_outputs['answer'])
Bagus, sepertinya kami mendapatkan sejumlah perbaikan dalam jawaban dengan memberikan beberapa contoh. Mari kita coba teknik RAG yang lebih menyeluruh selanjutnya!
IterDrag memperluas DRAG dengan menguraikan kueri kompleks menjadi subkueri yang lebih sederhana dan melakukan pengambilan yang disisipkan. Pendekatan ini sangat efektif untuk pertanyaan multihop kompleks yang harus mengintegrasikan informasi dari berbagai sumber atau penalaran di beberapa langkah.
Manfaat utama dari pendekatan iteratif:
Langkah dekomposisi sangat penting karena mengambil kueri kompleks dan memecahnya menjadi subkueri yang lebih sederhana dan lebih terfokus yang dapat dijawab satu per satu.
decompose_prompt = tokenizer.apply_chat_template(
conversation=[{
"role": "user",
"content": """\
You are a helpful assistant that breaks down complex questions into simpler sub-questions.
For multi-part or complex questions, generate 1-3 sub-questions that would help answer the main question.
Here are examples of how to decompose complex questions:
{demonstrations}
Follow the above examples when breaking down the user's question.
If the following question is already simple enough, just respond with "No follow-up needed."
Otherwise, break down the following question into simpler sub-questions. Format your response as:
Follow up: [sub-question]
Question: {input}"""
}],
add_generation_prompt=True,
tokenize=False,
)
decompose_prompt_template = PromptTemplate.from_template(template=escape_f_string(decompose_prompt, "input", "demonstrations"))
decompose_chain = decompose_prompt_template | model
Komponen penjawab subkueri menangani setiap subkueri dengan mengambil dokumen yang relevan dan menghasilkan jawaban antara yang terfokus.
intermediate_prompt = tokenizer.apply_chat_template(
conversation=[{
"role": "user",
"content": """\
You are a helpful assistant that answers specific questions based on the provided documents.
Focus only on the sub-question and provide a concise intermediate answer.
Please answer the following sub-question based on the provided documents.
Format your response as:
Intermediate answer: [your concise answer to the sub-question]
Sub-question: {input}
"""
}],
documents=[{
"doc_id": "0",
"text": "Placeholder{context}",
}],
add_generation_prompt=True,
tokenize=False,
)
intermediate_prompt_template = PromptTemplate.from_template(template=escape_f_string(intermediate_prompt, "input", "context"))
intermediate_combine_docs_chain = create_stuff_documents_chain(
llm=model,
prompt=intermediate_prompt_template,
document_prompt=document_prompt_template,
document_separator=document_separator,
)
intermediate_chain = create_retrieval_chain(
retriever=reordering_retriever,
combine_docs_chain=intermediate_combine_docs_chain,
)
Komponen pembuatan jawaban akhir menggabungkan semua jawaban antara untuk menghasilkan respons yang komprehensif terhadap pertanyaan awal.
final_prompt = tokenizer.apply_chat_template(
conversation=[{
"role": "user",
"content": """\
You are a helpful assistant that provides comprehensive answers to questions.
Use the intermediate answers to sub-questions to formulate a complete final answer.
Please provide a final answer to the main question based on the intermediate answers to sub-questions.
Format your response as:
So the final answer is: [your comprehensive answer to the main question]
Main question: {input}
Sub-questions and intermediate answers:
{context}"""
}],
add_generation_prompt=True,
tokenize=False,
)
final_prompt_template = PromptTemplate.from_template(template=escape_f_string(final_prompt, "input", "context"))
final_chain = final_prompt_template | model
Membuat demonstrasi yang efektif sangat penting untuk kinerja IterDrag. Semua contoh ini menunjukkan model cara untuk:
@dataclass
class IterDRAG_Demonstration_Base:
query: str
answer: str
@dataclass
class IterDRAG_Demonstration(IterDRAG_Demonstration_Base):
intermediate: list[IterDRAG_Demonstration_Base]
def __format__(self, format_spec: str) -> str:
sub_questions="\n".join(
f"Follow up: {sub.query}"
for sub in self.intermediate
)
return f"Question: {self.query}\n{sub_questions}"
def create_iterdrag_demonstrations() -> list[IterDRAG_Demonstration]:
"""Create examples showing how to decompose and answer complex questions"""
demonstrations = [
IterDRAG_Demonstration(
query="What impact did the pandemic have on the food bank's operations and distribution?",
answer="The pandemic had a profound impact on food bank operations and distribution. Distribution volume increased by 60% to over 100 million pounds of food in 2020. Operationally, the food bank faced supply chain disruptions, volunteer shortages, and safety protocol challenges. In response, they implemented contactless distribution, expanded mobile pantries, created emergency food boxes for vulnerable populations, and developed virtual nutrition education. Despite these challenges, they successfully scaled operations to meet the unprecedented community need during the crisis.",
intermediate=[
IterDRAG_Demonstration_Base(
query="How did food distribution volume change during the pandemic?",
answer="Food distribution volume increased by 60% during the pandemic, rising from approximately 62 million pounds in 2019 to over 100 million pounds in 2020.",
),
IterDRAG_Demonstration_Base(
query="What operational challenges did the food bank face during the pandemic?",
answer="The food bank faced challenges including supply chain disruptions, volunteer shortages due to social distancing requirements, and the need to implement new safety protocols for food handling and distribution.",
),
IterDRAG_Demonstration_Base(
query="What new programs were implemented in response to the pandemic?",
answer="New programs included contactless distribution methods, expanded mobile pantry operations, emergency food boxes for vulnerable populations, and virtual nutrition education classes.",
),
],
),
IterDRAG_Demonstration(
query="How does the food bank's financial management compare to industry standards for non-profits?",
answer="The food bank demonstrates excellent financial management compared to industry standards. With 94% of its budget allocated to program services and only 6% to administrative and fundraising costs, it exceeds the industry benchmark of 85-90% for program spending. This financial efficiency places the food bank among the top-performing non-profits in terms of maximizing donor impact and minimizing overhead expenses.",
intermediate=[
IterDRAG_Demonstration_Base(
query="What percentage of the food bank's budget goes to program services versus administrative costs?",
answer="94% of the food bank's budget goes directly to program services, with only 6% allocated to administrative and fundraising costs.",
),
IterDRAG_Demonstration_Base(
query="What are the industry standards for program spending versus overhead for food banks?",
answer="Industry standards suggest that well-run food banks typically allocate 85-90% of their budget to program services, with 10-15% for administrative and fundraising expenses.",
),
],
),
]
return demonstrations
Fungsi ini mengatur seluruh proses berulang:
import re
def iterative_drag(main_question: str) -> dict[str, typing.Any]:
"""
Implements IterDRAG: decomposing queries, retrieving documents for sub-queries,
and generating a final answer based on intermediate answers.
"""
print(f"\n=== Processing query with IterDRAG: '{main_question}' ===")
# Step 1: Decompose the main question into sub-questions
print("Step 1: Decomposing the query into sub-questions...")
iterdrag_demonstrations = create_iterdrag_demonstrations()
formatted_demonstrations = "\n\n".join(
f"Example {i+1}:\n{demo}"
for i, demo in enumerate(iterdrag_demonstrations)
)
decompose_result = decompose_chain.invoke({
"input": main_question,
"demonstrations": formatted_demonstrations,
})
decompose_answer = decompose_result
# Extract sub-questions using regex
sub_questions = re.findall(r"Follow up: (.*?)(?=Follow up:|\n|$)", decompose_answer, re.DOTALL)
sub_questions = [sq.strip() for sq in sub_questions if sq.strip()]
if not sub_questions:
print("No decomposition needed or found. Using standard DRAG approach.")
return drag_chain.invoke({"input": main_question})
print(f"Decomposed into {len(sub_questions)} sub-questions")
# Step 2: Answer each sub-question
intermediate_pairs: list[dict[str, str]] = []
for i, sub_question in enumerate(sub_questions):
print(f"\nStep 2.{i+1}: Processing sub-question: '{sub_question}'")
# Generate answer for this sub-question
intermediate_result = intermediate_chain.invoke({"input": sub_question})
intermediate_answer = intermediate_result["answer"]
# Extract intermediate answer using regex
intermediate_answer_match = re.search(r"Intermediate answer: (.*?)$", intermediate_answer, re.DOTALL)
if intermediate_answer_match:
intermediate_answer = intermediate_answer_match.group(1).strip()
print(f"Generated intermediate answer: {intermediate_answer[:100]}...")
# Store the sub-question and its answer
intermediate_pairs.append({"input": sub_question, "answer": intermediate_answer})
# Step 3: Generate the final answer based on sub-question answers
print("\nStep 3: Generating final answer based on intermediate answers...")
final_result = final_chain.invoke({
"input": main_question,
"context": "\n\n".join(
f"Sub-question: {pair['input']}\nIntermediate answer: {pair['answer']}"
for pair in intermediate_pairs
),
})
final_answer = final_result
# Extract final answer
final_answer_match = re.search(r"So the final answer is: (.*?)$", final_answer, re.DOTALL)
if final_answer_match:
final_answer = final_answer_match.group(1).strip()
return {"input": main_question, "answer": final_answer, "intermediate": intermediate_pairs}
Sekarang setelah kita menyiapkan ketiga pendekatan RAG, mari kita bandingkan respons mereka terhadap kueri yang sama, kali ini jauh lebih rumit untuk melihat perbedaannya.
Perbandingan ini akan membantu kita memahami manfaat dari setiap pendekatan dan kapan waktu paling tepat untuk menggunakan tiap pendekatan.
# Run all approaches on the same complex query
comparison_query = "What was the full impact chain of the National Guard's assistance during the pandemic? Specifically, how did their involvement affect volunteer operations, what specific tasks did they perform, and how did this ultimately translate to community impact in terms of food distribution capabilities and reach?"
print("\n=== Standard RAG ===")
standard_result = rag_chain.invoke({"input": comparison_query})
print(standard_result["answer"])
print("\n=== DRAG ===")
drag_result = drag_chain.invoke({"input": comparison_query})
print(drag_result["answer"])
print("\n=== IterDRAG ===")
iterdrag_result = iterative_drag(comparison_query)
print(iterdrag_result["answer"])
Di sini kami merangkum perbedaan kinerja antara ketiga pendekatan RAG yang diterapkan:
Pendekatan
| Kekuatan
| Batasan
| Contoh Penggunaan Terbaik
|
|---|---|---|---|
RAG Standar |
|
|
|
DRAG |
|
|
|
IterDRAG |
|
|
|
Seperti yang telah kita lihat dalam implementasi teknik penskalaan inferensi, seperti DRAG dan IterDRAG, dapat secara signifikan meningkatkan kinerja RAG. Metode ini terutama berlaku untuk kueri kompleks yang membutuhkan analisis mendalam pada beberapa dokumen.
Dalam tutorial ini, kami telah menjelajahi bagaimana penskalaan inferensi dapat secara dramatis meningkatkan kinerja RAG. Dengan mengalokasikan komputasi tambahan secara strategis pada waktu inferensi melalui teknik seperti DRAG dan IterDRAG, kami dapat mencapai peningkatan substansial dalam kualitas respons untuk kueri yang kompleks.
Inferensi mahal: Model berbasis transformator yang menggunakan mekanisme perhatian mandiri memiliki biaya inferensi yang berskala kuadratik terhadap panjang input. Metode ini membuat penanganan konteks yang panjang menjadi mahal secara komputasi, sehingga membatasi aplikasi praktis RAG pada dokumen yang lebih pendek atau membutuhkan pemotongan yang agresif.
Pemanfaatan konteks terbatas: Sistem RAG Standar sering kali mengambil dan memproses sejumlah dokumen tertentu yang mungkin tidak mencukupi untuk kueri yang kompleks dan multihop. Kinerja naik seiring dengan meningkatnya panjang konteks, terutama melampaui 128.000 token, karena model kesulitan menggabungkan informasi di banyak bagian yang diambil.
Alokasi komputasi yang tidak efisien: Tanpa alokasi yang cermat, menambahkan lebih banyak dokumen atau konteks yang diambil hanya akan meningkatkan biaya komputasi tanpa peningkatan akurasi yang proporsional, yang mengarah pada pengurangan hasil atau bahkan penurunan kinerja karena kelebihan informasi.
RAG berbasis demonstrasi (DRAG):
DRAG memanfaatkan beberapa contoh yang diambil, question and answer sebagai demonstrasi dalam prompt, sehingga model dapat mempelajari dalam konteks cara menemukan dan menerapkan informasi yang relevan.
Pendekatan ini sangat efektif untuk panjang konteks efektif yang lebih pendek karena model dapat memanfaatkan konteks yang kaya tanpa membebani mekanisme perhatian, sehingga meningkatkan kualitas pengambilan dan pembuatan.
RAG berbasis demonstrasi iteratif (IterDrag):
IterDRAG menguraikan kueri yang kompleks menjadi subkueri yang lebih sederhana, mengambil dan membuat jawaban untuk setiap subkueri secara berulang.
Dengan menyisipkan pengambilan dan pembangkitan, IterDRAG membangun rantai penalaran yang menjembatani kesenjangan untuk kueri multihop, membuatnya sangat efektif untuk konteks yang sangat panjang.
Proses ini memungkinkan model untuk mengalokasikan komputasi secara lebih efisien, berfokus pada informasi yang paling relevan pada setiap langkah, dan menghindari risiko perhatian yang berlebihan pada konteks yang panjang. Dengan menerapkan teknik penskalaan inferensi ini ke aplikasi RAG, Anda dapat mencapai kinerja yang jauh lebih baik pada tugas padat pengetahuan tanpa mengubah model yang mendasarinya.
Latih, validasi, lakukan tuning, dan terapkan AI generatif, model dasar, dan kemampuan machine learning dengan IBM watsonx.ai, studio perusahaan generasi berikutnya untuk pembangun AI. Bangun aplikasi AI dalam waktu singkat, dengan sedikit data.
Gunakan AI di bisnis Anda dalam perpaduan antara keahlian AI terdepan di industri dari IBM dan portofolio solusi Anda.
Temukan kembali alur kerja dan operasi yang penting dengan menambahkan AI untuk memaksimalkan pengalaman, pengambilan keputusan secara real-time, dan nilai bisnis.
1. “A Survey of Frontiers in LLM Reasoning: Inference Scaling, Learning to Reason, and Agentic Systems,” Ke, Zixuan, Fangkai Jiao, Yifei Ming, Xuan-Phi Nguyen, Austin Xu, Do Xuan Long, Minzhi Li, et al., ArXiv.org, 2025.
2. “Reasoning in Granite 3.2 Using Inference Scaling,” Lastras, Luis. 2025, IBM Research, IBM, 26 Februari 2025.
3. “Inference Scaling for Long-Context Retrieval Augmented Generation,” Zhenrui Yue, Honglei Zhuang, Aijun Bai, Kai Hui, Rolf Jagerman, Hansi Zeng, Zhen Qin, Dong Wang, Xuanhui Wang, Michael Bendersky, ArXiv.org, 2024.