评估是确保语言模型(LLM)问答系统质量的重要步骤,它有助于检测模型在不同文档上的表现,发现不足之处,并通过比较不同模型选择最优方案。定期评估还能监测模型性能是否下降。
评估目的包括确认LLM是否满足验收标准,以及分析变更对性能的影响。
基本策略是使用LLM和链来评估其他LLM、链和应用。本章以文档问答应用为例,探讨LangChain中评估的处理和考量。
首先,按照 langchain 链的方式构建一个 LLM 的文档问答应用
from langchain.chains import RetrievalQA #检索QA链,在文档上进行检索from langchain.chat_models import ChatOpenAI #openai模型from langchain.document_loaders import CSVLoader #文档加载器,采用csv格式存储from langchain.indexes import VectorstoreIndexCreator #导入向量存储索引创建器from langchain.vectorstores import DocArrayInMemorySearch #向量存储#加载中文数据file = '../data/product_data.csv'loader = CSVLoader(file_path=file)data = loader.load()#查看数据import pandas as pdtest_data = pd.read_csv(file,skiprows=0)display(test_data.head()) d
product_name | description | |
---|---|---|
0 | 全自动咖啡机 | 规格:\n大型 - 尺寸:13.8’’ x 17.3’‘。\n中型 - 尺寸:11.5’’ … |
1 | 电动牙刷 | 规格:\n一般大小 - 高度:9.5’‘,宽度:1’'。\n\n为什么我们热爱它:\n我们的… |
2 | 橙味维生素C泡腾片 | 规格:\n每盒含有20片。\n\n为什么我们热爱它:\n我们的橙味维生素C泡腾片是快速补充维… |
3 | 无线蓝牙耳机 | 规格:\n单个耳机尺寸:1.5’’ x 1.3’'。\n\n为什么我们热爱它:\n这款无线蓝… |
4 | 瑜伽垫 | 规格:\n尺寸:24’’ x 68’'。\n\n为什么我们热爱它:\n我们的瑜伽垫拥有出色的… |
# 将指定向量存储类,创建完成后,我们将从加载器中调用,通过文档记载器列表加载index = VectorstoreIndexCreator( vectorstore_cls=DocArrayInMemorySearch).from_loaders([loader])#通过指定语言模型、链类型、检索器和我们要打印的详细程度来创建检索QA链llm = ChatOpenAI(temperature = 0.0)qa = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=index.vectorstore.as_retriever(), verbose=True, chain_type_kwargs = { "document_separator": "<<<<>>>>>" }) d
上述代码的主要功能及作用在上一章节中都已说明,这里不再赘述
1.1 设置测试的数据
我们查看一下经过档加载器 CSVLoad 加载后生成的 data 内的信息,这里我们抽取 data 中的第九条和第十条数据,看看它们的主要内容:
第九条数据:
data[10] dDocument(page_content=“product_name: 高清电视机\ndescription: 规格:\n尺寸:50’'。\n\n为什么我们热爱它:\n我们的高清电视机拥有出色的画质和强大的音效,带来沉浸式的观看体验。\n\n材质与护理:\n使用干布清洁。\n\n构造:\n由塑料、金属和电子元件制成。\n\n其他特性:\n支持网络连接,可以在线观看视频。\n配备遥控器。\n在韩国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。”, metadata={‘source’: ‘…/data/product_data.csv’, ‘row’: 10}) d
第十条数据:
data[11] dDocument(page_content=“product_name: 旅行背包\ndescription: 规格:\n尺寸:18’’ x 12’’ x 6’'。\n\n为什么我们热爱它:\n我们的旅行背包拥有多个实用的内外袋,轻松装下您的必需品,是短途旅行的理想选择。\n\n材质与护理:\n可以手洗,自然晾干。\n\n构造:\n由防水尼龙制成。\n\n其他特性:\n附带可调节背带和安全锁。\n在中国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。”, metadata={‘source’: ‘…/data/product_data.csv’, ‘row’: 11}) d
看上面的第一个文档中有高清电视机,第二个文档中有旅行背包,从这些细节中,我们可以创建一些例子查询和答案
1.2 手动创建测试数据
需要说明的是这里我们的文档是 csv 文件,所以我们使用的是文档加载器是 CSVLoader ,CSVLoader 会对 csv 文件中的每一行数据进行分割,所以这里看到的 data[10], data[11]的内容则是 csv 文件中的第10条,第11条数据的内容。下面我们根据这两条数据手动设置两条“问答对”,每一个“问答对”中包含一个 query ,一个 answer :
examples = [ { "query": "高清电视机怎么进行护理?", "answer": "使用干布清洁。" }, { "query": "旅行背包有内外袋吗?", "answer": "有。" }] d
1.3 通过LLM生成测试用例
在前面的内容中,我们使用的方法都是通过手动的方法来构建测试数据集,比如说我们手动创建10个问题和10个答案,然后让 LLM 回答这10个问题,再将 LLM 给出的答案与我们准备好的答案做比较,最后再给 LLM 打分,评估的流程大概就是这样。但是这里有一个问题,就是我们需要手动去创建所有的问题集和答案集,那会是一个非常耗费时间和人力的成本。那有没有一种可以自动创建大量问答测试集的方法呢?那当然是有的,今天我们就来介绍 Langchain 提供的方法:QAGenerateChain,我们可以通过QAGenerateChain来为我们的文档自动创建问答集:
由于QAGenerateChain类中使用的PROMPT是英文,故我们继承QAGenerateChain类,将PROMPT加上“请使用中文输出”。下面是generate_chain.py文件中的QAGenerateChain类的源码
from langchain.evaluation.qa import QAGenerateChain #导入QA生成链,它将接收文档,并从每个文档中创建一个问题答案对# 下面是langchain.evaluation.qa.generate_prompt中的源码,在template的最后加上“请使用中文输出”from langchain.output_parsers.regex import RegexParserfrom langchain.prompts import PromptTemplatefrom langchain.base_language import BaseLanguageModelfrom typing import Anytemplate = """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 hereANSWER: answer hereThese questions should be detailed and be based explicitly on information in the document. Begin!<Begin Document>{doc}<End Document>请使用中文输出"""output_parser = RegexParser( regex=r"QUESTION: (.*?)\nANSWER: (.*)", output_keys=["query", "answer"])PROMPT = PromptTemplate( input_variables=["doc"], template=template, output_parser=output_parser)# 继承QAGenerateChainclass ChineseQAGenerateChain(QAGenerateChain): """LLM Chain specifically for generating examples for question answering.""" @classmethod def from_llm(cls, llm: BaseLanguageModel, **kwargs: Any) -> QAGenerateChain: """Load QA Generate Chain from LLM.""" return cls(llm=llm, prompt=PROMPT, **kwargs)example_gen_chain = ChineseQAGenerateChain.from_llm(ChatOpenAI())#通过传递chat open AI语言模型来创建这个链new_examples = example_gen_chain.apply([{"doc": t} for t in data[:5]]) #查看用例数据new_examples d
[{'qa_pairs': {'query': '这款全自动咖啡机的尺寸是多少?', 'answer': "大型尺寸为13.8'' x 17.3'',中型尺寸为11.5'' x 15.2''。"}}, {'qa_pairs': {'query': '这款电动牙刷的规格是什么?', 'answer': "一般大小 - 高度:9.5'',宽度:1''。"}}, {'qa_pairs': {'query': '这种产品的名称是什么?', 'answer': '这种产品的名称是橙味维生素C泡腾片。'}}, {'qa_pairs': {'query': '这款无线蓝牙耳机的尺寸是多少?', 'answer': "该无线蓝牙耳机的尺寸为1.5'' x 1.3''。"}}, {'qa_pairs': {'query': '这款瑜伽垫的尺寸是多少?', 'answer': "这款瑜伽垫的尺寸是24'' x 68''。"}}] d
在上面的代码中,我们创建了一个QAGenerateChain,然后我们应用了QAGenerateChain的 apply 方法对 data 中的前5条数据创建了5个“问答对”,由于创建问答集是由 LLM 来自动完成的,因此会涉及到 token 成本的问题,所以我们这里出于演示的目的,只对 data 中的前5条数据创建问答集。
new_examples[0] d
{'qa_pairs': {'query': '这款全自动咖啡机的尺寸是多少?', 'answer': "大型尺寸为13.8'' x 17.3'',中型尺寸为11.5'' x 15.2''。"}} d
源数据:
data[0] dDocument(page_content=“product_name: 全自动咖啡机\ndescription: 规格:\n大型 - 尺寸:13.8’’ x 17.3’‘。\n中型 - 尺寸:11.5’’ x 15.2’'。\n\n为什么我们热爱它:\n这款全自动咖啡机是爱好者的理想选择。 一键操作,即可研磨豆子并沏制出您喜爱的咖啡。它的耐用性和一致性使它成为家庭和办公室的理想选择。\n\n材质与护理:\n清洁时只需轻擦。\n\n构造:\n由高品质不锈钢制成。\n\n其他特性:\n内置研磨器和滤网。\n预设多种咖啡模式。\n在中国制造。\n\n有问题? 请随时联系我们的客户服务团队,他们会解答您的所有问题。”, metadata={‘source’: ‘…/data/product_data.csv’, ‘row’: 0}) d
1.4 整合测试集
还记得我们前面手动创建的两个问答集吗?现在我们需要将之前手动创建的问答集合并到QAGenerateChain创建的问答集中,这样在答集中既有手动创建的例子又有 llm 自动创建的例子,这会使我们的测试集更加完善。
接下来我们就需要让之前创建的文档问答链qa来回答这个测试集里的问题,来看看 LLM 是怎么回答的吧:
examples += [ v for item in new_examples for k,v in item.items()]qa.run(examples[0]["query"]) d
> Entering new RetrievalQA chain...> Finished chain.'高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。' d
这里我们看到qa回答了第0个问题:“高清电视机怎么进行护理?” ,这里的第0个问题就是先前我们手动创建的第一个问题,并且我们手动创建的 answer 是:“使用干布清洁。” 这里我们发现问答链qa回答的也是“您只需要使用干布清洁即可”,只是它比我们的答案还多了一段说明:“高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。”。
二、 人工评估
你想知道qa是怎么找到问题的答案的吗?下面让我们打开debug,看看qa是如何找到问题的答案!
import langchainlangchain.debug = True#重新运行与上面相同的示例,可以看到它开始打印出更多的信息qa.run(examples[0]["query"]) d
[chain/start] [1:chain:RetrievalQA] Entering Chain run with input:{ "query": "高清电视机怎么进行护理?"}[chain/start] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain] Entering Chain run with input:[inputs][chain/start] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain] Entering Chain run with input:{ "question": "高清电视机怎么进行护理?", "context": "product_name: 高清电视机\ndescription: 规格:\n尺寸:50''。\n\n为什么我们热爱它:\n我们的高清电视机拥有出色的画质和强大的音效,带来沉浸式的观看体验。\n\n材质与护理:\n使用干布清洁。\n\n构造:\n由塑料、金属和电子元件制成。\n\n其他特性:\n支持网络连接,可以在线观看视频。\n配备遥控器。\n在韩国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。<<<<>>>>>product_name: 空气净化器\ndescription: 规格:\n尺寸:15'' x 15'' x 20''。\n\n为什么我们热爱它:\n我们的空气净化器采用了先进的HEPA过滤技术,能有效去除空气中的微粒和异味,为您提供清新的室内环境。\n\n材质与护理:\n清洁时使用干布擦拭。\n\n构造:\n由塑料和电子元件制成。\n\n其他特性:\n三档风速,附带定时功能。\n在德国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。<<<<>>>>>product_name: 宠物自动喂食器\ndescription: 规格:\n尺寸:14'' x 9'' x 15''。\n\n为什么我们热爱它:\n我们的宠物自动喂食器可以定时定量投放食物,让您无论在家或外出都能确保宠物的饮食。\n\n材质与护理:\n可用湿布清洁。\n\n构造:\n由塑料和电子元件制成。\n\n其他特性:\n配备LCD屏幕,操作简单。\n可以设置多次投食。\n在美国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。<<<<>>>>>product_name: 玻璃保护膜\ndescription: 规格:\n适用于各种尺寸的手机屏幕。\n\n为什么我们热爱它:\n我们的玻璃保护膜可以有效防止手机屏幕刮伤和破裂,而且不影响触控的灵敏度。\n\n材质与护理:\n使用干布擦拭。\n\n构造:\n由高强度的玻璃材料制成。\n\n其他特性:\n安装简单,适合自行安装。\n在日本制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。"}[llm/start] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain > 5:llm:ChatOpenAI] Entering LLM run with input:{ "prompts": [ "System: Use the following pieces of context to answer the users question. \nIf you don't know the answer, just say that you don't know, don't try to make up an answer.\n----------------\nproduct_name: 高清电视机\ndescription: 规格:\n尺寸:50''。\n\n为什么我们热爱它:\n我们的高清电视机拥有出色的画质和强大的音效,带来沉浸式的观看体验。\n\n材质与护理:\n使用干布清洁。\n\n构造:\n由塑料、金属和电子元件制成。\n\n其他特性:\n支持网络连接,可以在线观看视频。\n配备遥控器。\n在韩国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。<<<<>>>>>product_name: 空气净化器\ndescription: 规格:\n尺寸:15'' x 15'' x 20''。\n\n为什么我们热爱它:\n我们的空气净化器采用了先进的HEPA过滤技术,能有效去除空气中的微粒和异味,为您提供清新的室内环境。\n\n材质与护理:\n清洁时使用干布擦拭。\n\n构造:\n由塑料和电子元件制成。\n\n其他特性:\n三档风速,附带定时功能。\n在德国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。<<<<>>>>>product_name: 宠物自动喂食器\ndescription: 规格:\n尺寸:14'' x 9'' x 15''。\n\n为什么我们热爱它:\n我们的宠物自动喂食器可以定时定量投放食物,让您无论在家或外出都能确保宠物的饮食。\n\n材质与护理:\n可用湿布清洁。\n\n构造:\n由塑料和电子元件制成。\n\n其他特性:\n配备LCD屏幕,操作简单。\n可以设置多次投食。\n在美国制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。<<<<>>>>>product_name: 玻璃保护膜\ndescription: 规格:\n适用于各种尺寸的手机屏幕。\n\n为什么我们热爱它:\n我们的玻璃保护膜可以有效防止手机屏幕刮伤和破裂,而且不影响触控的灵敏度。\n\n材质与护理:\n使用干布擦拭。\n\n构造:\n由高强度的玻璃材料制成。\n\n其他特性:\n安装简单,适合自行安装。\n在日本制造。\n\n有问题?请随时联系我们的客户服务团队,他们会解答您的所有问题。\nHuman: 高清电视机怎么进行护理?" ]}[llm/end] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain > 5:llm:ChatOpenAI] [2.86s] Exiting LLM run with output:{ "generations": [ [ { "text": "高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。", "generation_info": { "finish_reason": "stop" }, "message": { "lc": 1, "type": "constructor", "id": [ "langchain", "schema", "messages", "AIMessage" ], "kwargs": { "content": "高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。", "additional_kwargs": {} } } } ] ], "llm_output": { "token_usage": { "prompt_tokens": 823, "completion_tokens": 58, "total_tokens": 881 }, "model_name": "gpt-3.5-turbo" }, "run": null}[chain/end] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain > 4:chain:LLMChain] [2.86s] Exiting Chain run with output:{ "text": "高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。"}[chain/end] [1:chain:RetrievalQA > 3:chain:StuffDocumentsChain] [2.87s] Exiting Chain run with output:{ "output_text": "高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。"}[chain/end] [1:chain:RetrievalQA] [3.26s] Exiting Chain run with output:{ "result": "高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。"}'高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。' d
我们可以看到它首先深入到检索 QA 链中,然后它进入了一些文档链。如上所述,我们正在使用 stuff 方法,现在我们正在传递这个上下文,可以看到,这个上下文是由我们检索到的不同文档创建的。因此,在进行问答时,当返回错误结果时,通常不是语言模型本身出错了,实际上是检索步骤出错了,仔细查看问题的确切内容和上下文可以帮助调试出错的原因。
然后,我们可以再向下一级,看看进入语言模型的确切内容,以及 OpenAI 自身,在这里,我们可以看到传递的完整提示,我们有一个系统消息,有所使用的提示的描述,这是问题回答链使用的提示,我们可以看到提示打印出来,使用以下上下文片段回答用户的问题。
如果您不知道答案,只需说您不知道即可,不要试图编造答案。然后我们看到一堆之前插入的上下文,我们还可以看到有关实际返回类型的更多信息。我们不仅仅返回一个答案,还有 token 的使用情况,可以了解到 token 数的使用情况
由于这是一个相对简单的链,我们现在可以看到最终的响应,通过链返回给用户。这部分我们主要讲解了如何查看和调试单个输入到该链的情况。
三、 通过LLM进行评估实例
来简要梳理一下问答评估的流程:
首先,我们使用 LLM 自动构建了问答测试集,包含问题及标准答案。然后,同一 LLM 试图回答测试集中的所有问题,得到响应。下一步,需要评估语言模型的回答是否正确。这里奇妙的是,我们再使用另一个 LLM 链进行判断,所以 LLM 既是“球员”,又是“裁判”。具体来说,第一个语言模型负责回答问题。第二个语言模型链用来进行答案判定。最后我们可以收集判断结果,得到语言模型在这一任务上的效果分数。需要注意的是,回答问题的语言模型链和答案判断链是分开的,职责不同。这避免了同一个模型对自己结果的主观判断。
总之,语言模型可以自动完成构建测试集、回答问题和判定答案等全流程,使评估过程更加智能化和自动化。我们只需要提供文档并解析最终结果即可。
langchain.debug = False#为所有不同的示例创建预测predictions = qa.apply(examples) # 对预测的结果进行评估,导入QA问题回答,评估链,通过语言模型创建此链from langchain.evaluation.qa import QAEvalChain #导入QA问题回答,评估链#通过调用chatGPT进行评估llm = ChatOpenAI(temperature=0)eval_chain = QAEvalChain.from_llm(llm)#在此链上调用evaluate,进行评估graded_outputs = eval_chain.evaluate(examples, predictions) d
> Entering new RetrievalQA chain...> Finished chain.> Entering new RetrievalQA chain...> Finished chain.> Entering new RetrievalQA chain...> Finished chain.> Entering new RetrievalQA chain...> Finished chain.> Entering new RetrievalQA chain...> Finished chain.> Entering new RetrievalQA chain...> Finished chain.> Entering new RetrievalQA chain...> Finished chain. d
#我们将传入示例和预测,得到一堆分级输出,循环遍历它们打印答案for i, eg in enumerate(examples): print(f"Example {i}:") print("Question: " + predictions[i]['query']) print("Real Answer: " + predictions[i]['answer']) print("Predicted Answer: " + predictions[i]['result']) print("Predicted Grade: " + graded_outputs[i]['results']) print() d
Example 0:Question: 高清电视机怎么进行护理?Real Answer: 使用干布清洁。Predicted Answer: 高清电视机的护理非常简单。您只需要使用干布清洁即可。避免使用湿布或化学清洁剂,以免损坏电视机的表面。Predicted Grade: CORRECTExample 1:Question: 旅行背包有内外袋吗?Real Answer: 有。Predicted Answer: 是的,旅行背包有多个实用的内外袋,可以轻松装下您的必需品。Predicted Grade: CORRECTExample 2:Question: 这款全自动咖啡机的尺寸是多少?Real Answer: 大型尺寸为13.8'' x 17.3'',中型尺寸为11.5'' x 15.2''。Predicted Answer: 这款全自动咖啡机有两种尺寸可选:- 大型尺寸为13.8'' x 17.3''。- 中型尺寸为11.5'' x 15.2''。Predicted Grade: CORRECTExample 3:Question: 这款电动牙刷的规格是什么?Real Answer: 一般大小 - 高度:9.5'',宽度:1''。Predicted Answer: 这款电动牙刷的规格是:高度为9.5英寸,宽度为1英寸。Predicted Grade: CORRECTExample 4:Question: 这种产品的名称是什么?Real Answer: 这种产品的名称是橙味维生素C泡腾片。Predicted Answer: 这种产品的名称是儿童益智玩具。Predicted Grade: INCORRECTExample 5:Question: 这款无线蓝牙耳机的尺寸是多少?Real Answer: 该无线蓝牙耳机的尺寸为1.5'' x 1.3''。Predicted Answer: 这款无线蓝牙耳机的尺寸是1.5'' x 1.3''。Predicted Grade: CORRECTExample 6:Question: 这款瑜伽垫的尺寸是多少?Real Answer: 这款瑜伽垫的尺寸是24'' x 68''。Predicted Answer: 这款瑜伽垫的尺寸是24'' x 68''。Predicted Grade: CORRECT d
从上面的返回结果中我们可以看到,在评估结果中每一个问题中都包含了Question,Real Answer,Predicted Answer和Predicted Grade 四组内容,其中Real Answer是有先前的QAGenerateChain创建的问答测试集中的答案,而Predicted Answer则是由我们的qa链给出的答案,最后的Predicted Grade则是由上面代码中的QAEvalChain回答的。
在本章中,我们学习了使用 LangChain 框架实现 LLM 问答效果自动化评估的方法。与传统手工准备评估集、逐题判断等方式不同,LangChain 使整个评估流程自动化。它可以自动构建包含问答样本的测试集,然后使用语言模型对测试集自动产生回复,最后通过另一个模型链自动判断每个回答的准确性。这种全自动的评估方式极大地简化了问答系统的评估和优化过程,开发者无需手动准备测试用例,也无需逐一判断正确性,大大提升了工作效率。
借助LangChain的自动评估功能,我们可以快速评估语言模型在不同文档集上的问答效果,并可以持续地进行模型调优,无需人工干预。这种自动化的评估方法解放了双手,使我们可以更高效地迭代优化问答系统的性能。
总之,自动评估是 LangChain 框架的一大优势,它将极大地降低问答系统开发的门槛,使任何人都可以轻松训练出性能强大的问答模型。