什么是大模型幻觉
假设我们有一个基于大型生成模型(如GPT-3)的问答系统,该系统用于回答药企内部知识库中的问题。我们向其提出一个问题:"阿司匹林的主要药理作用是什么?"
正确的答案应该是:"阿司匹林主要通过抑制环氧酶(COX)酶的活性,降低前列腺素和血栓素的生成,从而起到抗血小板聚集、镇痛和解热的作用。"
然而,由于大模型幻觉问题,生成模型可能会给出一个在语法和流畅性上看似合理,但实际上并不准确的答案,例如:"阿司匹林的主要药理作用是通过抑制多巴胺受体来改善心血管功能。"这个答案中的信息是错误的,因为阿司匹林的药理作用与多巴胺受体无关。
如何减少幻觉
解决大模型幻觉问题需要采取多种策略来提高生成模型在自然语言处理任务中的准确性和可靠性。以下是一些常见的方法:
- 结合检索和生成:使用像RAG(Retrieval-Augmented Generation)这样的模型,在生成过程中结合检索相关文档或信息。这样可以在生成阶段利用更多外部知识,从而减少幻觉现象。
- 模型可解释性和后处理:分析生成模型的输出,以识别和纠正潜在的幻觉问题。这可以通过使用可解释性工具、规则引擎或其他后处理方法来实现。
- 模型融合与集成:将多个生成模型的输出进行融合或集成,以提高生成文本的准确性。这可以通过投票、加权平均或其他集成方法来实现。
- 优化生成策略:在生成过程中采用更合适的概率抽样策略,如束搜索(beam search)、拓扑抽样(top-k sampling)或者核心抽样(nucleus sampling),以平衡生成文本的多样性和准确性。
- 更精细的预训练和微调(fine-tuning):在预训练生成模型时,使用更高质量、更具代表性的数据集。更多有标签的数据或强化学习方法来提高模型。在微调阶段,使用与目标任务更相关的数据集,以便模型更好地适应特定场景。
- 提示词工程:使用更好的提示词进行正确性引导。
- 增加多样性:在生成回答时,可以引入一些随机性或多样性,以避免模型过于依赖于检索到的特定信息。例如,可以通过引入不同的词汇、短语或句子结构来增加生成的多样性。
通过采取这些方法,研究人员和工程师可以在一定程度上解决大模型幻觉问题,提高生成模型在自然语言处理任务中的表现。然而,这仍然是一个具有挑战性的研究领域,需要进一步的探索和创新。
RAG是什么
RAG(Retrieval-Augmented Generation)是一种结合了检索和生成的技术,旨在提高自然语言处理任务的性能。在RAG中,外部检索(或搜索)系统和大模型被集成在了一起,它会首先根据用户词从外部的文本数据中检索与当前任务相关的信息,然后利用这些信息和用户词一起构建一个更丰富的上下文信息,从而生成回答或完成任务。
RAG(中文为检索增强生成) = 检索技术 + LLM 提示
RAG(Retrieval-Augmented Generation)的详细过程主要包括以下步骤:
- 输入:首先,RAG接收一个输入,这个输入可以是一个问题、一个句子或者一个段落,取决于具体的任务需求。
- 检索阶段:RAG模型会从预先构建的知识库或文档集合中检索出与输入相关的文档或片段。这个过程通常使用一个检索模型,例如基于BM25的模型或者基于向量空间模型的最近邻搜索。检索到的文档或片段将作为上下文信息,供下一阶段的生成模型使用。
- 上下文整合:检索到相关文档或片段后,RAG模型会将这些文档与输入整合在一起,形成一个统一的上下文。
- 生成阶段:在这个阶段,RAG模型会使用一个预训练的生成模型(例如GPT-3)来生成回答或输出。这个过程是条件生成的,意味着生成的文本不仅依赖于输入,还依赖于检索阶段提供的上下文信息。生成模型会考虑这些上下文信息,以生成一个与输入相关,且在内容上更准确和可靠的回答或输出。
- 输出:最后,RAG模型会输出生成的文本。这个输出应该充分考虑了检索到的相关文档中的信息,从而减少了幻觉现象,提高了生成文本的准确性和可靠性。
通过这种结合检索和生成的方式,RAG模型能够在生成过程中利用更多的外部知识,从而提高生成文本的质量,并减少生成模型的幻觉现象。然而,RAG模型的效果往往依赖于检索阶段的效果,如果检索阶段不能提供足够相关和有用的文档或片段,那么生成阶段的效果可能会受到影响。因此 ,如何优化检索阶段的效果,是RAG模型的一个重要研究方向,也是一个非常大的挑战。
检索技术
RAG中模型的检索阶段主要用于从大规模知识库或文档集合中检索与输入问题或文本相关的文档或片段。这个阶段主要决定了最终的效果。
目前主流的检索技术包括以下几种:
- 基于关键词的检索:这种方法主要依赖于关键词匹配来检索相关文档。一个常用的技术是BM25,它是一种基于词频-逆文档频率(TF-IDF)的信息检索算法。BM25为每个文档计算一个得分,该得分表示文档与查询之间的相关性。然后,根据得分对文档进行排序,并返回最相关的文档。
- 基于向量空间的检索:这种方法使用词嵌入或句子嵌入技术将文本表示为高维向量。对于输入问题或文本,也计算其向量表示。然后,使用余弦相似度或欧几里得距离等度量方法计算输入向量与文档向量之间的相似性。最后,根据相似性得分对文档进行排序,并返回最相关的文档。常用的向量表示方法包括Word2Vec、GloVe、BERT等。
- 基于深度学习的检索:这种方法使用深度学习模型(如神经网络)来学习输入问题或文本与文档之间的相关性。训练时,模型学习从输入问题或文本到相关文档的映射。在检索阶段,模型根据输入计算每个文档的相关性得分,并返回最相关的文档。这种方法通常需要大量的有标签训练数据,以便模型能够学习到有效的映射。
- 混合检索技术:在实际应用中,可能会将以上多种检索技术结合在一起,以实现更高效和准确的检索。例如,可以先使用基于关键词的检索方法进行粗略筛选,然后使用基于向量空间的检索方法进一步优化结果。
向量数据库
向量数据库是一种专门用于存储和检索高维向量数据的数据库。与传统的关系型数据库或文档数据库不同,向量数据库针对向量数据的特点进行了优化,以实现高效的相似性搜索和最近邻查询。
向量数据库的过程通常是把资料或者文档等所有内容转化成向量(这个过程称之为 Vector Embedding),然后当用户发起检索时,会将用户的搜索内容转换成向量,然后在数据库中搜索最相似的向量,匹配最相似的几个上下文,最后将上下文返回。
近似最近邻算法
向量数据库中的搜索算法主要用于实现最近邻搜索,即在高维向量空间中找到与给定向量最接近的其他向量。
各种向量搜索层出不穷的目的在于提升效率,这主要有两个方向:1. 降低向量大小(降维、降低长度)2. 缩小搜索空间。
以下是一些常见的ANN(Approximate Nearest Neighbors)算法实现:
- KD树(K-Dimensional Tree):KD树是一种空间划分树,用于在多维空间中组织数据。KD树的搜索过程可以高效地剪枝,从而减少搜索空间。然而,KD树在处理高维数据时效率会降低。
- 球树(Ball Tree):球树是一种以超球体来划分空间的树形数据结构,比KD树更适合处理高维数据。球树可以更好地处理数据的内在结构,从而提高搜索效率。
- 局部敏感哈希(Locality-Sensitive Hashing,LSH):LSH是一种哈希方法,可以将相近的点映射到相同或相近的哈希桶。LSH适用于大规模数据的相似性搜索,但精度较低。
- 乘积量化(Product Quantization):乘积量化是一种用于大规模最近邻搜索的向量量化方法。乘积量化可以将高维向量映射到紧凑的代码,从而大幅度减少存储和计算需求。
- 层次化导航(Hierarchical Navigable Small World,HNSW):HNSW是一种基于图的搜索方法,利用小世界网络的性质来加速最近邻搜索。HNSW可以处理大规模高维数据,且在精度和效率之间有良好的平衡。
向量数据库产品
目前市场上有许多向量数据库可供选择,包括开源和商业产品。一些常见的向量数据库包括:
- Faiss:由Facebook AI Research开发的一款高性能的向量数据库。Faiss提供了多种索引结构和算法,支持大规模的相似性搜索和最近邻查询。
- Annoy:由Spotify开发的一款近似最近邻搜索库,适用于推荐系统和其他需要高效相似性搜索的场景。
- Milvus:一个开源的向量数据库,提供多种索引和查询算法,支持分布式部署和可扩展性。
- Weaviate:一个开源的、实时的、可扩展的和基于语义的向量搜索引擎。Weaviate允许用户通过RESTful API或者GraphQL API进行数据的存储、检索和搜索。
- Elasticsearch:虽然Elasticsearch主要是一个文档数据库,但它也支持向量数据的存储和相似性搜索。通过内置的向量字段类型和相似性度量函数,Elasticsearch可以实现基本的最近邻查询。
RAG的架构
实现检索增强生成系统的方案很多,实践过程中我们可以参考具体的需求和数据的细微差别。下面是一个常用的工作流程,加深对于流程的基本理解。
代码实现
使用langchain加载文档
python
import requests
from langchain.document_loaders import TextLoader
url = "xxx"
res = requests.get(url)
with open("test.txt", "w") as f:
f.write(res.text)
loader = TextLoader('./test.txt')
documents = loader.load()
对文档切分,保证处于LLM窗口限制之下
python
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)
嵌入并存储这些文本块,使用 Weaviate 向量数据库
python
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Weaviate
import weaviate
from weaviate.embedded import EmbeddedOptions
client = weaviate.Client(
embedded_options = EmbeddedOptions()
)
vectorstore = Weaviate.from_documents(
client = client,
documents = chunks,
embedding = OpenAIEmbeddings(),
by_text = False
)
检索并增强生成
python
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser
template = """You are an assistant for question-answering tasks.
Use the following pieces of retrieved context to answer the question.
If you don't know the answer, just say that you don't know.
Use three sentences maximum and keep the answer concise.
Question: {question}
Context: {context}
Answer:
"""
prompt = ChatPromptTemplate.from_template(template)
print(prompt)
retriever = vectorstore.as_retriever()
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
query = "阿司匹林的主要药理作用是什么?"
rag_chain.invoke(query)
参考: