Multi-modal eval: Baseline#
Multi-modal slide decks
is a public dataset that contains a dataset of question-answer pairs from slide decks with visual content.
The question-answer pairs are derived from the visual content in the decks, testing the ability of RAG to perform visual reasoning.
As a baseline, we evaluate this dataset using text-based RAG pipeline, below.
This will not reason about visual content and will simply load the text from the slides.
Pre-requisites#
# %pip install -U langchain langsmith langchain_benchmarks
# %pip install --quiet chromadb openai pypdf pandas
import getpass
import os
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
env_vars = ["LANGCHAIN_API_KEY", "OPENAI_API_KEY"]
for var in env_vars:
if var not in os.environ:
os.environ[var] = getpass.getpass(prompt=f"Enter your {var}: ")
Dataset#
We can browse the available LangChain benchmark datasets for retrieval.
from langchain_benchmarks import clone_public_dataset, registry
registry = registry.filter(Type="RetrievalTask")
registry
Name | Type | Dataset ID | Description |
---|---|---|---|
LangChain Docs Q&A | RetrievalTask | 452ccafc-18e1-4314-885b-edd735f17b9d | Questions and answers based on a snapshot of the LangChain python docs. The environment provides the documents and the retriever information. Each example is composed of a question and reference answer. Success is measured based on the accuracy of the answer relative to the reference answer. We also measure the faithfulness of the model's response relative to the retrieved documents (if any). |
Semi-structured Reports | RetrievalTask | c47d9617-ab99-4d6e-a6e6-92b8daf85a7d | Questions and answers based on PDFs containing tables and charts. The task provides the raw documents as well as factory methods to easily index them and create a retriever. Each example is composed of a question and reference answer. Success is measured based on the accuracy of the answer relative to the reference answer. We also measure the faithfulness of the model's response relative to the retrieved documents (if any). |
Multi-modal slide decks | RetrievalTask | 40afc8e7-9d7e-44ed-8971-2cae1eb59731 | This public dataset is a work-in-progress and will be extended over time. Questions and answers based on slide decks containing visual tables and charts. Each example is composed of a question and reference answer. Success is measured based on the accuracy of the answer relative to the reference answer. |
Multi-modal slide decks
is the relevant dataset for our task.
task = registry["Multi-modal slide decks"]
task
Name | Multi-modal slide decks |
Type | RetrievalTask |
Dataset ID | 40afc8e7-9d7e-44ed-8971-2cae1eb59731 |
Description | This public dataset is a work-in-progress and will be extended over time. Questions and answers based on slide decks containing visual tables and charts. Each example is composed of a question and reference answer. Success is measured based on the accuracy of the answer relative to the reference answer. |
Retriever Factories | |
Architecture Factories | |
get_docs | {} |
Clone the dataset so that it’s available in our LangSmith datasets.
clone_public_dataset(task.dataset_id, dataset_name=task.name)
Dataset Multi-modal slide decks already exists. Skipping.
You can access the dataset at https://smith.langchain.com/o/ebbaf2eb-769b-4505-aca2-d11de10372a4/datasets/08a29acb-5ad6-42ce-a482-574c9e2e5306.
Fetch the associated PDFs from remote cache for the dataset so that we can perform ingestion.
from langchain_benchmarks.rag.tasks.multi_modal_slide_decks import get_file_names
file_names = list(get_file_names()) # PosixPath
Load#
Load and split the files for indexing.
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
def load_and_split(file):
"""
Load and split PDF files
:param file: PosixPath path for pdf
:return: A list of text chunks
"""
loader = PyPDFLoader(str(file))
pdf_pages = loader.load()
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
chunk_size=100, chunk_overlap=50
)
# Get chunks
docs = text_splitter.split_documents(pdf_pages)
texts = [d.page_content for d in docs]
print(f"There are {len(texts)} text elements in {file.name}")
return texts
texts = []
for fi in file_names:
texts.extend(load_and_split(fi))
There are 98 text elements in DDOG_Q3_earnings_deck.pdf
Index#
Embed (OpenAIEmbeddings) and store splits in a vectorstore (Chroma).
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
vectorstore_baseline = Chroma.from_texts(
texts=texts, collection_name="baseline-multi-modal", embedding=OpenAIEmbeddings()
)
retriever_baseline = vectorstore_baseline.as_retriever()
RAG#
Create a pipeline for retrieval of relevant chunks based on semantic similarity to the input question.
Pass the images to GPT-4 for answer synthesis.
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
def rag_chain(retriever):
"""
RAG pipeline for the indexed presentations
:param retriever: PosixPath path for pdf
"""
# Prompt template
template = """Answer the question based only on the following context, which can include text and tables:
{context}
Question: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
# LLM
model = ChatOpenAI(temperature=0, model="gpt-4")
# RAG pipeline
chain = (
{
"context": retriever | (lambda x: "\n\n".join([i.page_content for i in x])),
"question": RunnablePassthrough(),
}
| prompt
| model
| StrOutputParser()
)
return chain
# Create RAG chain
chain = rag_chain(retriever_baseline)
Eval#
Run evaluation on our dataset:
task.name
is the dataset of QA pairs that we clonedeval_config
specifies the LangSmith evaluator for our dataset, which will use GPT-4 as a graderThe grader will evaluate the chain-generated answer to each question relative to ground truth
import uuid
from langchain.smith import RunEvalConfig
from langsmith.client import Client
# Evaluator configuration
client = Client()
eval_config = RunEvalConfig(
evaluators=["cot_qa"],
)
# Experiments
chain_map = {
"baseline": chain,
}
# Run evaluation
run_id = uuid.uuid4().hex[:4]
test_runs = {}
for project_name, chain in chain_map.items():
test_runs[project_name] = client.run_on_dataset(
dataset_name=task.name,
llm_or_chain_factory=lambda: (lambda x: x["Question"]) | chain,
evaluation=eval_config,
verbose=True,
project_name=f"{run_id}-{project_name}",
project_metadata={"chain": project_name},
)
View the evaluation results for project '866f-baseline' at:
https://smith.langchain.com/o/ebbaf2eb-769b-4505-aca2-d11de10372a4/datasets/08a29acb-5ad6-42ce-a482-574c9e2e5306/compare?selectedSessions=30199d47-50d7-4c5c-a55a-e74157e05951
View all tests for Dataset Multi-modal slide decks at:
https://smith.langchain.com/o/ebbaf2eb-769b-4505-aca2-d11de10372a4/datasets/08a29acb-5ad6-42ce-a482-574c9e2e5306
[------------------------------------------------->] 10/10
Experiment Results:
output | feedback.COT Contextual Accuracy | error | execution_time | |
---|---|---|---|---|
count | 10 | 10.000000 | 0 | 10.000000 |
unique | 10 | NaN | 0 | NaN |
top | Datadog has 20 total customers. | NaN | NaN | NaN |
freq | 1 | NaN | NaN | NaN |
mean | NaN | 0.200000 | NaN | 4.674478 |
std | NaN | 0.421637 | NaN | 0.864273 |
min | NaN | 0.000000 | NaN | 3.307960 |
25% | NaN | 0.000000 | NaN | 4.113816 |
50% | NaN | 0.000000 | NaN | 4.700962 |
75% | NaN | 0.000000 | NaN | 5.018359 |
max | NaN | 1.000000 | NaN | 6.188082 |