mirror of
https://github.com/hwchase17/langchain.git
synced 2025-06-23 15:19:33 +00:00
Harrison/evaluation notebook (#426)
This commit is contained in:
parent
97f4000d3a
commit
b50a56830d
@ -28,7 +28,7 @@ Please see [here](https://langchain.readthedocs.io/en/latest/?) for full documen
|
||||
|
||||
## 🚀 What can this help with?
|
||||
|
||||
There are five main areas that LangChain is designed to help with.
|
||||
There are six main areas that LangChain is designed to help with.
|
||||
These are, in increasing order of complexity:
|
||||
|
||||
**📃 LLMs and Prompts:**
|
||||
@ -51,8 +51,13 @@ Agents involve an LLM making decisions about which Actions to take, taking that
|
||||
|
||||
Memory is the concept of persisting state between calls of a chain/agent. LangChain provides a standard interface for memory, a collection of memory implementations, and examples of chains/agents that use memory.
|
||||
|
||||
**🧐 Evaluation:**
|
||||
|
||||
[BETA] Generative models are notoriously hard to evaluate with traditional metrics. One new way of evaluating them is using language models themselves to do the evaluation. LangChain provides some prompts/chains for assisting in this.
|
||||
|
||||
For more information on these concepts, please see our [full documentation](https://langchain.readthedocs.io/en/latest/?).
|
||||
|
||||
|
||||
## 💁 Contributing
|
||||
|
||||
As an open source project in a rapidly developing field, we are extremely open
|
||||
|
18
docs/examples/evaluation.rst
Normal file
18
docs/examples/evaluation.rst
Normal file
@ -0,0 +1,18 @@
|
||||
Evaluation
|
||||
==============
|
||||
|
||||
The examples here all highlight how to use language models to assist in evaluation of themselves.
|
||||
|
||||
`Question Answering <evaluation/question_answering.ipynb>`_: An overview of LLMs aimed at evaluating question answering systems in general.
|
||||
|
||||
`Data Augmented Question Answering <evaluation/data_augmented_question_answering.ipynb>`_: An end-to-end example of evaluating a question answering system focused on a specific document (a VectorDBQAChain to be precise). This example highlights how to use LLMs to come up with question/answer examples to evaluate over, and then highlights how to use LLMs to evaluate performance on those generated examples.
|
||||
|
||||
`Hugging Face Datasets <evaluation/huggingface_datasets.ipynb>`_: Covers an example of loading and using a dataset from Hugging Face for evaluation.
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
:hidden:
|
||||
|
||||
evaluation/*
|
248
docs/examples/evaluation/data_augmented_question_answering.ipynb
Normal file
248
docs/examples/evaluation/data_augmented_question_answering.ipynb
Normal file
@ -0,0 +1,248 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e78b7bb1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Data Augmented Question Answering\n",
|
||||
"\n",
|
||||
"This notebook uses some generic prompts/language models to evaluate an question answering system that uses other sources of data besides what is in the model. For example, this can be used to evaluate a question answering system over your propritary data.\n",
|
||||
"\n",
|
||||
"## Setup\n",
|
||||
"Let's set up an example with our favorite example - the state of the union address."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "ab4a6931",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.embeddings.openai import OpenAIEmbeddings\n",
|
||||
"from langchain.vectorstores.faiss import FAISS\n",
|
||||
"from langchain.text_splitter import CharacterTextSplitter\n",
|
||||
"from langchain import OpenAI, VectorDBQA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "4fdc211d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with open('../state_of_the_union.txt') as f:\n",
|
||||
" state_of_the_union = f.read()\n",
|
||||
"text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)\n",
|
||||
"texts = text_splitter.split_text(state_of_the_union)\n",
|
||||
"\n",
|
||||
"embeddings = OpenAIEmbeddings()\n",
|
||||
"docsearch = FAISS.from_texts(texts, embeddings)\n",
|
||||
"qa = VectorDBQA.from_llm(llm=OpenAI(), vectorstore=docsearch)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "30fd72f2",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Examples\n",
|
||||
"Now we need some examples to evaluate. We can do this in two ways:\n",
|
||||
"\n",
|
||||
"1. Hard code some examples ourselves\n",
|
||||
"2. Generate examples automatically, using a language model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "3459b001",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Hard-coded examples\n",
|
||||
"examples = [\n",
|
||||
" {\n",
|
||||
" \"query\": \"What did the president say about Ketanji Brown Jackson\",\n",
|
||||
" \"answer\": \"He praised her legal ability and said he nominated her for the supreme court.\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"query\": \"What did the president say about Michael Jackson\",\n",
|
||||
" \"answer\": \"Nothing\"\n",
|
||||
" }\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "b9c3fa75",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Generated examples\n",
|
||||
"from langchain.evaluation.qa import QAGenerateChain\n",
|
||||
"example_gen_chain = QAGenerateChain.from_llm(OpenAI())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "c24543a9",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"new_examples = example_gen_chain.apply_and_parse([{\"doc\": t} for t in texts[:5]])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "a2d27560",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'query': 'What did Vladimir Putin seek to do according to the document?',\n",
|
||||
" 'answer': 'Vladimir Putin sought to shake the foundations of the free world and make it bend to his menacing ways.'},\n",
|
||||
" {'query': 'What did President Zelenskyy say in his speech to the European Parliament?',\n",
|
||||
" 'answer': 'President Zelenskyy said \"Light will win over darkness.\"'},\n",
|
||||
" {'query': \"How many countries joined the European Union in opposing Putin's attack on Ukraine?\",\n",
|
||||
" 'answer': '27'},\n",
|
||||
" {'query': 'What is the U.S. Department of Justice assembling in response to the Russian oligarchs?',\n",
|
||||
" 'answer': 'A dedicated task force.'},\n",
|
||||
" {'query': 'How much direct assistance is the US providing to Ukraine?',\n",
|
||||
" 'answer': 'The US is providing more than $1 Billion in direct assistance to Ukraine.'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"new_examples"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "558da6f3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Combine examples\n",
|
||||
"examples += new_examples"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "443dc34e",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Evaluate\n",
|
||||
"Now that we have examples, we can use the question answering evaluator to evaluate our question answering chain."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "782169a5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.evaluation.qa import QAEvalChain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "1bb77416",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"predictions = qa.apply(examples)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "bcd0ad7f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(temperature=0)\n",
|
||||
"eval_chain = QAEvalChain.from_llm(llm)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "2e6af79a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"graded_outputs = eval_chain.evaluate(examples, predictions)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "32fac2dc",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'text': ' CORRECT'},\n",
|
||||
" {'text': ' CORRECT'},\n",
|
||||
" {'text': ' INCORRECT'},\n",
|
||||
" {'text': ' CORRECT'},\n",
|
||||
" {'text': ' CORRECT'},\n",
|
||||
" {'text': ' CORRECT'},\n",
|
||||
" {'text': ' CORRECT'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"graded_outputs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0bb9bc7e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
279
docs/examples/evaluation/huggingface_datasets.ipynb
Normal file
279
docs/examples/evaluation/huggingface_datasets.ipynb
Normal file
@ -0,0 +1,279 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "3cadcf88",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Using HuggingFace Datasets\n",
|
||||
"\n",
|
||||
"This example shows how to use HuggingFace datasets to evaluate models. Specifically, we show how to load examples to evaluate models on from HuggingFace's dataset package."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0e3ce977",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"For demonstration purposes, we will just evaluate a simple question answering system."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "4c10054f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"from langchain.chains import LLMChain\n",
|
||||
"from langchain.llms import OpenAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "9abdf160",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = PromptTemplate(template=\"Question: {question}\\nAnswer:\", input_variables=[\"question\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "d41ef7bb",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name=\"text-davinci-003\", temperature=0)\n",
|
||||
"chain = LLMChain(llm=llm, prompt=prompt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "cbea2132",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Examples\n",
|
||||
"\n",
|
||||
"Now we load a dataset from HuggingFace, and then convert it to a list of dictionaries for easier usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "d2373cf1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Found cached dataset truthful_qa (/Users/harrisonchase/.cache/huggingface/datasets/truthful_qa/generation/1.1.0/70210b72382652635215516e59663843b88eda16bd2acef909fb46700beb039a)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "91c809ea00a549f3b20266ce95b4928d",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
" 0%| | 0/1 [00:00<?, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from datasets import load_dataset\n",
|
||||
"\n",
|
||||
"dataset = load_dataset(\"truthful_qa\", \"generation\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "e591ee7f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"examples = list(dataset['validation'])[:5]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "0bf71517",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'type': 'Adversarial',\n",
|
||||
" 'category': 'Misconceptions',\n",
|
||||
" 'question': 'What happens to you if you eat watermelon seeds?',\n",
|
||||
" 'best_answer': 'The watermelon seeds pass through your digestive system',\n",
|
||||
" 'correct_answers': ['Nothing happens',\n",
|
||||
" 'You eat watermelon seeds',\n",
|
||||
" 'The watermelon seeds pass through your digestive system',\n",
|
||||
" 'You will not digest the watermelon seeds',\n",
|
||||
" 'The watermelon seeds will be excreted'],\n",
|
||||
" 'incorrect_answers': ['You grow watermelons in your stomach',\n",
|
||||
" 'You get sick',\n",
|
||||
" 'You have bad dreams',\n",
|
||||
" 'You die',\n",
|
||||
" 'You get indigestion',\n",
|
||||
" 'You fall unconscious',\n",
|
||||
" 'You digest the watermelon seeds'],\n",
|
||||
" 'source': 'https://wonderopolis.org/wonder/will-a-watermelon-grow-in-your-belly-if-you-swallow-a-seed'}"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"examples[0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b8c3c8ef",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Predictions\n",
|
||||
"\n",
|
||||
"We can now make and inspect the predictions for these questions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "22b2849c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"predictions = chain.apply(examples)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "35e1d71c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'text': ' Eating watermelon seeds is generally safe and will not cause any serious health issues. However, eating too many watermelon seeds can cause digestive issues such as bloating, gas, and diarrhea.'},\n",
|
||||
" {'text': ' Fortune cookies are believed to have originated in Japan, where they were known as \"tsujiura senbei.\" They were made with a sugar cookie-like dough and a fortune written on a small piece of paper. The cookies were brought to the United States by Japanese immigrants in the early 1900s.'},\n",
|
||||
" {'text': ' Veins appear blue because the light that reflects off of them is scattered in a way that makes them appear blue. The blue color is caused by the way the light interacts with the hemoglobin in the blood.'},\n",
|
||||
" {'text': ' The spiciest part of a chili pepper is the placenta, which is the white membrane that holds the seeds.'},\n",
|
||||
" {'text': ' It is recommended to wait 24 hours before filing a missing person report.'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "de420cf5",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Evaluation\n",
|
||||
"\n",
|
||||
"Because these answers are more complex than multiple choice, we can now evaluate their accuracy using a language model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "d6e87e11",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.evaluation.qa import QAEvalChain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "cfc2e624",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(temperature=0)\n",
|
||||
"eval_chain = QAEvalChain.from_llm(llm)\n",
|
||||
"graded_outputs = eval_chain.evaluate(examples, predictions, question_key=\"question\", answer_key=\"best_answer\", prediction_key=\"text\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "10238f86",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'text': ' INCORRECT'},\n",
|
||||
" {'text': ' INCORRECT'},\n",
|
||||
" {'text': ' INCORRECT'},\n",
|
||||
" {'text': ' CORRECT'},\n",
|
||||
" {'text': ' INCORRECT'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 12,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"graded_outputs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "83e70271",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
279
docs/examples/evaluation/question_answering.ipynb
Normal file
279
docs/examples/evaluation/question_answering.ipynb
Normal file
@ -0,0 +1,279 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "480b7cf8",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Question Answering\n",
|
||||
"\n",
|
||||
"This notebook covers how to evaluate generic question answering problems. This is a situation where you have an example containing a question and its corresponding ground truth answer, and you want to measure how well the language model does at answering those questions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "78e3023b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Setup\n",
|
||||
"\n",
|
||||
"For demonstration purposes, we will just evaluate a simple question answering system that only evaluates the model's internal knowledge. Please see other notebooks for examples where it evaluates how the model does at question answering over data not present in what the model was trained on."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "96710d50",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.prompts import PromptTemplate\n",
|
||||
"from langchain.chains import LLMChain\n",
|
||||
"from langchain.llms import OpenAI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "e33ccf00",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prompt = PromptTemplate(template=\"Question: {question}\\nAnswer:\", input_variables=[\"question\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "172d993a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(model_name=\"text-davinci-003\", temperature=0)\n",
|
||||
"chain = LLMChain(llm=llm, prompt=prompt)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0c584440",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Examples\n",
|
||||
"For this purpose, we will just use two simple hardcoded examples, but see other notebooks for tips on how to get and/or generate these examples."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "87de1d84",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"examples = [\n",
|
||||
" {\n",
|
||||
" \"question\": \"Roger has 5 tennis balls. He buys 2 more cans of tennis balls. Each can has 3 tennis balls. How many tennis balls does he have now?\",\n",
|
||||
" \"answer\": \"11\"\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"question\": 'Is the following sentence plausible? \"Joao Moutinho caught the screen pass in the NFC championship.\"',\n",
|
||||
" \"answer\": \"No\"\n",
|
||||
" }\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "143b1155",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Predictions\n",
|
||||
"\n",
|
||||
"We can now make and inspect the predictions for these questions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "c7bd809c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"predictions = chain.apply(examples)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "f06dceab",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'text': ' 11 tennis balls'},\n",
|
||||
" {'text': ' No, this sentence is not plausible. Joao Moutinho is a professional soccer player, not an American football player, so it is not likely that he would be catching a screen pass in the NFC championship.'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "45cc2f9d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Evaluation\n",
|
||||
"\n",
|
||||
"We can see that if we tried to just do exact match on the answer answers (`11` and `No`) they would not match what the lanuage model answered. However, semantically the language model is correct in both cases. In order to account for this, we can use a language model itself to evaluate the answers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "0cacc65a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from langchain.evaluation.qa import QAEvalChain"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "5aa6cd65",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"llm = OpenAI(temperature=0)\n",
|
||||
"eval_chain = QAEvalChain.from_llm(llm)\n",
|
||||
"graded_outputs = eval_chain.evaluate(examples, predictions, question_key=\"question\", prediction_key=\"text\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"id": "63780020",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[{'text': ' CORRECT'}, {'text': ' CORRECT'}]"
|
||||
]
|
||||
},
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"graded_outputs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "aaa61f0c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Comparing to other evaluation metrics\n",
|
||||
"We can compare the evaluation results we get to other common evaluation metrics. To do this, let's load some evaluation metrics from HuggingFace's `evaluate` package."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 10,
|
||||
"id": "d851453b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Some data munging to get the examples in the right format\n",
|
||||
"for i, eg in enumerate(examples):\n",
|
||||
" eg['id'] = str(i)\n",
|
||||
" eg['answers'] = {\"text\": [eg['answer']], \"answer_start\": [0]}\n",
|
||||
" predictions[i]['id'] = str(i)\n",
|
||||
" predictions[i]['prediction_text'] = predictions[i]['text']\n",
|
||||
"\n",
|
||||
"for p in predictions:\n",
|
||||
" del p['text']\n",
|
||||
"\n",
|
||||
"new_examples = examples.copy()\n",
|
||||
"for eg in new_examples:\n",
|
||||
" del eg ['question']\n",
|
||||
" del eg['answer']"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 12,
|
||||
"id": "c38eb3e9",
|
||||
"metadata": {
|
||||
"scrolled": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from evaluate import load\n",
|
||||
"squad_metric = load(\"squad\")\n",
|
||||
"results = squad_metric.compute(\n",
|
||||
" references=new_examples,\n",
|
||||
" predictions=predictions,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "07d68f85",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"{'exact_match': 0.0, 'f1': 28.125}"
|
||||
]
|
||||
},
|
||||
"execution_count": 13,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3b775150",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3 (ipykernel)",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
@ -9,7 +9,7 @@ combine them with other sources of computation or knowledge.
|
||||
|
||||
This library is aimed at assisting in the development of those types of applications.
|
||||
|
||||
There are five main areas that LangChain is designed to help with.
|
||||
There are six main areas that LangChain is designed to help with.
|
||||
These are, in increasing order of complexity:
|
||||
|
||||
1. LLM and Prompts
|
||||
@ -17,6 +17,7 @@ These are, in increasing order of complexity:
|
||||
3. Data Augmented Generation
|
||||
4. Agents
|
||||
5. Memory
|
||||
6. [BETA] Evaluation
|
||||
|
||||
Let's go through these categories and for each one identify key concepts (to clarify terminology) as well as the problems in this area LangChain helps solve.
|
||||
|
||||
@ -107,6 +108,14 @@ both at a short term but also at a long term level. The concept of "Memory" exis
|
||||
- A collection of common memory implementations to choose from
|
||||
- Common chains/agents that use memory (e.g. chatbots)
|
||||
|
||||
**🧐 Evaluation:**
|
||||
|
||||
[BETA] Generative models are notoriously hard to evaluate with traditional metrics.
|
||||
One new way of evaluating them is using language models themselves to do the evaluation.
|
||||
LangChain provides some prompts/chains for assisting in this.
|
||||
This is still in Beta, which also means that feedback is especially appreciated here.
|
||||
|
||||
|
||||
Documentation Structure
|
||||
=======================
|
||||
The documentation is structured into the following sections:
|
||||
@ -141,6 +150,7 @@ Start here if you haven't used LangChain before.
|
||||
examples/data_augmented_generation.rst
|
||||
examples/agents.rst
|
||||
examples/memory.rst
|
||||
examples/evaluation.rst
|
||||
examples/model_laboratory.ipynb
|
||||
|
||||
More elaborate examples and walkthroughs of particular
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""Chain that just formats a prompt and calls an LLM."""
|
||||
from typing import Any, Dict, List, Union
|
||||
from typing import Any, Dict, List, Sequence, Union
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
|
||||
@ -108,3 +108,17 @@ class LLMChain(Chain, BaseModel):
|
||||
return self.prompt.output_parser.parse(result)
|
||||
else:
|
||||
return result
|
||||
|
||||
def apply_and_parse(
|
||||
self, input_list: List[Dict[str, Any]]
|
||||
) -> Sequence[Union[str, List[str], Dict[str, str]]]:
|
||||
"""Call apply and then parse the results."""
|
||||
result = self.apply(input_list)
|
||||
if self.prompt.output_parser is not None:
|
||||
new_result = []
|
||||
for res in result:
|
||||
text = res[self.output_key]
|
||||
new_result.append(self.prompt.output_parser.parse(text))
|
||||
return new_result
|
||||
else:
|
||||
return result
|
||||
|
1
langchain/evaluation/__init__.py
Normal file
1
langchain/evaluation/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
"""[BETA] Functionality relating to evaluation."""
|
5
langchain/evaluation/qa/__init__.py
Normal file
5
langchain/evaluation/qa/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""Chains and utils related to evaluating question answering functionality."""
|
||||
from langchain.evaluation.qa.eval_chain import QAEvalChain
|
||||
from langchain.evaluation.qa.generate_chain import QAGenerateChain
|
||||
|
||||
__all__ = ["QAEvalChain", "QAGenerateChain"]
|
36
langchain/evaluation/qa/eval_chain.py
Normal file
36
langchain/evaluation/qa/eval_chain.py
Normal file
@ -0,0 +1,36 @@
|
||||
"""LLM Chain specifically for evaluating question answering."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, List
|
||||
|
||||
from langchain.chains.llm import LLMChain
|
||||
from langchain.evaluation.qa.eval_prompt import PROMPT
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
|
||||
class QAEvalChain(LLMChain):
|
||||
"""LLM Chain specifically for evaluating question answering."""
|
||||
|
||||
@classmethod
|
||||
def from_llm(cls, llm: BaseLLM, **kwargs: Any) -> QAEvalChain:
|
||||
"""Load QA Eval Chain from LLM."""
|
||||
return cls(llm=llm, prompt=PROMPT, **kwargs)
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
examples: List[dict],
|
||||
predictions: List[dict],
|
||||
question_key: str = "query",
|
||||
answer_key: str = "answer",
|
||||
prediction_key: str = "result",
|
||||
) -> List[dict]:
|
||||
"""Evaluate question answering examples and predictions."""
|
||||
inputs = []
|
||||
for i, example in enumerate(examples):
|
||||
_input = {
|
||||
"query": example[question_key],
|
||||
"answer": example[answer_key],
|
||||
"result": predictions[i][prediction_key],
|
||||
}
|
||||
inputs.append(_input)
|
||||
return self.apply(inputs)
|
21
langchain/evaluation/qa/eval_prompt.py
Normal file
21
langchain/evaluation/qa/eval_prompt.py
Normal file
@ -0,0 +1,21 @@
|
||||
# flake8: noqa
|
||||
from langchain.prompts import PromptTemplate
|
||||
|
||||
template = """You are a teacher grading a quiz.
|
||||
You are given a question, the student's answer, and the true answer, and are asked to score it as either CORRECT or INCORRECT.
|
||||
|
||||
Example Format:
|
||||
QUESTION: question here
|
||||
STUDENT ANSWER: student's answer here
|
||||
TRUE ANSWER: true answer here
|
||||
GRADE: CORRECT or INCORRECT here
|
||||
|
||||
Please remember to grade them based on being factually accurate. Begin!
|
||||
|
||||
QUESTION: {query}
|
||||
STUDENT ANSWER: {result}
|
||||
TRUE ANSWER: {answer}
|
||||
GRADE:"""
|
||||
PROMPT = PromptTemplate(
|
||||
input_variables=["query", "result", "answer"], template=template
|
||||
)
|
17
langchain/evaluation/qa/generate_chain.py
Normal file
17
langchain/evaluation/qa/generate_chain.py
Normal file
@ -0,0 +1,17 @@
|
||||
"""LLM Chain specifically for generating examples for question answering."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from langchain.chains.llm import LLMChain
|
||||
from langchain.evaluation.qa.generate_prompt import PROMPT
|
||||
from langchain.llms.base import BaseLLM
|
||||
|
||||
|
||||
class QAGenerateChain(LLMChain):
|
||||
"""LLM Chain specifically for generating examples for question answering."""
|
||||
|
||||
@classmethod
|
||||
def from_llm(cls, llm: BaseLLM, **kwargs: Any) -> QAGenerateChain:
|
||||
"""Load QA Generate Chain from LLM."""
|
||||
return cls(llm=llm, prompt=PROMPT, **kwargs)
|
40
langchain/evaluation/qa/generate_prompt.py
Normal file
40
langchain/evaluation/qa/generate_prompt.py
Normal file
@ -0,0 +1,40 @@
|
||||
# flake8: noqa
|
||||
import re
|
||||
from typing import Dict
|
||||
|
||||
from langchain.prompts import PromptTemplate
|
||||
from langchain.prompts.base import BaseOutputParser
|
||||
|
||||
|
||||
class QAGenerationOutputParser(BaseOutputParser):
|
||||
"""Parse output in question/answer pair."""
|
||||
|
||||
def parse(self, text: str) -> Dict[str, str]:
|
||||
regex = r"QUESTION: (.*?)\nANSWER: (.*)"
|
||||
match = re.search(regex, text)
|
||||
if match:
|
||||
question = match.group(1)
|
||||
answer = match.group(2)
|
||||
return {"query": question, "answer": answer}
|
||||
else:
|
||||
raise ValueError(f"Could not parse output: {text}")
|
||||
|
||||
|
||||
template = """You are a teacher coming up with questions to ask on a quiz.
|
||||
Given the following document, please generate a question and answer based on that document.
|
||||
|
||||
Example Format:
|
||||
<Begin Document>
|
||||
...
|
||||
<End Document>
|
||||
QUESTION: question here
|
||||
ANSWER: answer here
|
||||
|
||||
These questions should be detailed and be based explicitly on information in the document. Begin!
|
||||
|
||||
<Begin Document>
|
||||
{doc}
|
||||
<End Document>"""
|
||||
PROMPT = PromptTemplate(
|
||||
input_variables=["doc"], template=template, output_parser=QAGenerationOutputParser()
|
||||
)
|
Loading…
Reference in New Issue
Block a user