如何使用 RAG 提高 LLM 成绩

初学者友好的介绍 w/ Python 代码

本文是关于在实践中使用大型语言模型的更大系列的一部分。在上一篇文章中,我们使用 QLoRA 对 Mistral-7b-Instruct 进行了微调,以回应 YouTube 评论。尽管微调后的模型在回应观众反馈时成功捕捉了我的风格,但它对技术问题的回答与我的解释并不匹配。在这里,我将讨论如何通过检索增强生成(即 RAG)来提高 LLM 的性能。

在这里插入图片描述

大型语言模型(LLMs)在响应用户查询时展示了存储和部署大量知识的惊人能力。虽然这使得像 ChatGPT 这样的强大 AI 系统得以创建,但以这种方式压缩世界知识有两个关键限制

首先,LLM 的知识是静态的,即不会随着新信息的出现而更新。其次,LLM 可能对其训练数据中不显著的利基和专业信息缺乏足够的“理解”。这些限制可能导致模型对用户查询的回答不理想(甚至是虚构的)。

我们可以通过通过专业和可变的知识库增强模型来缓解这些限制,例如客户常见问题解答、软件文档或产品目录。这使得创建更强大和适应性更强的 AI 系统成为可能。

检索增强生成,或称 RAG,就是这样一种方法。在这里,我提供 RAG 的高级介绍,并分享使用 LlamaIndex 实现 RAG 系统的示例 Python 代码。

什么是 RAG?

LLM 的基本用法是给它一个提示并获取响应。

在这里插入图片描述

RAG 通过在这个基本过程中添加一个步骤来工作。具体来说,执行一个检索步骤,根据用户的提示,从外部知识库中提取相关信息,并在传递给 LLM 之前将其注入到提示中。

在这里插入图片描述

我们为什么关心

请注意,RAG 并没有从根本上改变我们使用 LLM 的方式;它仍然是 提示输入和响应输出。RAG 只是增强了这个过程(因此得名)。

这使得 RAG 成为一种灵活且(相对)简单的方式来改善基于 LLM 的系统。此外,由于知识存储在外部数据库中,更新系统知识就像从表中添加或删除记录一样简单。

为什么不进行微调?

本系列之前的文章讨论了微调,即为特定用例调整现有模型。虽然这是一种赋予LLM专业知识的替代方法,但从经验来看,微调似乎在这方面的效果不如RAG [1]。

它是如何工作的

RAG 系统有两个关键要素:检索器知识库

Retriever

检索器接收用户提示并从知识库中返回相关项目。这通常使用所谓的 文本嵌入,即文本在概念空间中的数值表示。换句话说,这些是 表示给定文本的 *含义* 的数字

文本嵌入可以用来计算用户查询与知识库中每个项目之间的相似性得分。这个过程的结果是 每个项目与输入查询相关性的排名

然后,检索器可以选择前 k 个(例如 k=3)最相关的项目,并将它们注入到用户提示中。这个增强的提示随后被传递给 LLM 进行生成。

在这里插入图片描述

知识库

RAG 系统的下一个关键要素是知识库。这个 包含了您希望提供给 LLM 的所有信息。虽然有无数种方法可以构建 RAG 的知识库,但在这里我将重点介绍如何从一组文档中构建一个知识库。

这个过程可以分为 4 个关键步骤 [2,3].

  1. 加载文档 — 这包括收集一组文档并确保它们处于可解析的格式(稍后会详细介绍)。
  2. **分块文档—**由于 LLM 的上下文窗口有限,文档必须被拆分成更小的块 (例如, 256 或 512 个字符长)。
  3. 嵌入块 — 使用文本嵌入模型将每个块转换为数字。
  4. 加载到向量数据库— 将文本嵌入加载到数据库(即向量数据库)中。

在这里插入图片描述

一些细微差别

虽然构建 RAG 系统的步骤在概念上很简单,但一些细微差别可能使得在现实世界中构建一个系统变得更加复杂。

文档准备—RAG 系统的质量取决于从源文档中提取有用信息的能力。例如,如果一个文档格式混乱,充满了图像和表格,那么解析起来会比一个格式良好的文本文件更困难。

选择合适的块大小—我们已经提到由于 LLM 上下文窗口的需要进行分块。然而,还有 2 个额外的分块动机。

首先,它可以降低(计算)成本。你在提示中注入的文本越多,生成完成所需的计算就越多。第二是性能。特定查询的相关信息往往集中在源文档中(通常仅一句话就可以回答一个问题)。分块有助于最小化传递给模型的无关信息的数量 [4]。

改善搜索 — 虽然文本嵌入提供了一种强大且快速的搜索方式,但它并不总是能如人所愿地工作。换句话说,它可能返回与用户查询“相似”的结果,但对回答问题并没有帮助,例如,“洛杉矶的天气怎么样?”可能返回“纽约的天气怎么样?”。

缓解这一问题的最简单方法是通过良好的文档准备和分块。然而,对于某些用例,可能需要额外的策略来改善搜索,例如为每个块使用 元标签、采用结合关键词和嵌入搜索的 混合搜索,或使用 重排序器,这是一种专门计算两段文本相似性的模型。

示例代码:使用 RAG 改进 YouTube 评论响应器

在对 RAG 工作原理有基本了解后,让我们看看如何在实践中使用它。我将基于 上一篇文章 中的示例,在其中我使用 QLoRA 对 Mistral-7B-Instruct 进行了微调,以响应 YouTube 评论。我们将使用 LlamaIndex 为之前微调的模型添加 RAG 系统。

示例代码可在 Colab Notebook 中免费获得,该 Notebook 可以在提供的(免费)T4 GPU 上运行。此示例的源文件可在 GitHub 仓库 中找到。

🔗 Google Colab | GitHub Repo

导入

我们首先安装并导入必要的 Python 库。

!pip install llama-index
!pip install llama-index-embeddings-huggingface
!pip install peft
!pip install auto-gptq
!pip install optimum
!pip install bitsandbytes
## 如果不是在 Colab 上运行,请确保也安装 transformers
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor

设置知识库

我们可以通过定义我们的嵌入模型、块大小和块重叠来配置我们的知识库。在这里,我们使用来自BAAI的~33M参数bge-small-en-v1.5嵌入模型,该模型可在Hugging Face hub上获取。其他嵌入模型选项可以在这个text embedding leaderboard上找到。

## import any embedding model on HF hub
Settings.embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")

Settings.llm = None # we won't use LlamaIndex to set up LLM
Settings.chunk_size = 256
Settings.chunk_overlap = 25

接下来,我们加载源文档。在这里,我有一个名为“articles”的文件夹,其中包含我在fat tails上写的3篇Medium文章的PDF版本。如果在Colab中运行,您必须从GitHub repo下载文章文件夹并手动上传到您的Colab环境。

对于该文件夹中的每个文件,下面的函数将从PDF中读取文本,将其拆分成块(基于之前定义的设置),并将每个块存储在名为documents的列表中。

documents = SimpleDirectoryReader("articles").load_data()

由于这些博客是直接从Medium下载为PDF的,因此它们更像是网页,而不是格式良好的文章。因此,一些块可能包含与文章无关的文本,例如网页标题和Medium文章推荐。

在下面的代码块中,我对documents中的块进行精炼,删除文章主体前后的大部分块。

print(len(documents)) # prints: 71
for doc in documents:
    if "Member-only story" in doc.text:
        documents.remove(doc)
        continue

    if "The Data Entrepreneurs" in doc.text:
        documents.remove(doc)

    if " min read" in doc.text:
        documents.remove(doc)

print(len(documents)) # prints: 61

最后,我们可以将精炼后的块存储在向量数据库中。

index = VectorStoreIndex.from_documents(documents)

设置检索器

在我们的知识库建立之后,我们可以使用 LlamaIndex 的 VectorIndexRetriever() 创建一个检索器,它返回与用户查询最相似的 3 个块。

## set number of docs to retreive
top_k = 3

## configure retriever
retriever = VectorIndexRetriever(
    index=index,
    similarity_top_k=top_k,
)

接下来,我们定义一个查询引擎,使用检索器和查询返回一组相关的块。

## assemble query engine
query_engine = RetrieverQueryEngine(
    retriever=retriever,
    node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.5)],
)

使用查询引擎

现在,随着我们的知识库和检索系统的建立,让我们使用它来返回与查询相关的内容。在这里,我们将传递我们向ShawGPT(YouTube评论回复者)提出的相同技术问题,来自上一篇文章。

query = "What is fat-tailedness?"
response = query_engine.query(query)

查询引擎返回一个响应对象,其中包含文本、元数据和相关块的索引。下面的代码块返回该信息的更易读版本。

## reformat response
context = "Context:\n"
for i in range(top_k):
    context = context + response.source_nodes[i].text + "\n\n"

print(context)
Context:
Some of the controversy might be explained by the observation that log-
normal distributions behave like Gaussian for low sigma and like Power Law
at high sigma [2].
However, to avoid controversy, we can depart (for now) from whether some
given data fits a Power Law or not and focus instead on fat tails.
Fat-tailedness — measuring the space between Mediocristan
and Extremistan
Fat Tails are a more general idea than Pareto and Power Law distributions.
One way we can think about it is that “fat-tailedness” is the degree to which
rare events drive the aggregate statistics of a distribution. From this point of
view, fat-tailedness lives on a spectrum from not fat-tailed (i.e. a Gaussian) to
very fat-tailed (i.e. Pareto 80 – 20).
This maps directly to the idea of Mediocristan vs Extremistan discussed
earlier. The image below visualizes different distributions across this
conceptual landscape [2].

print("mean kappa_1n = " + str(np.mean(kappa_dict[filename])))
    print("")
Mean κ (1,100) values from 1000 runs for each dataset. Image by author.
These more stable results indicate Medium followers are the most fat-tailed,
followed by LinkedIn Impressions and YouTube earnings.
Note: One can compare these values to Table III in ref [3] to better understand each
κ value. Namely, these values are comparable to a Pareto distribution with α
between 2 and 3.
Although each heuristic told a slightly different story, all signs point toward
Medium followers gained being the most fat-tailed of the 3 datasets.
Conclusion
While binary labeling data as fat-tailed (or not) may be tempting, fat-
tailedness lives on a spectrum. Here, we broke down 4 heuristics for
quantifying how fat-tailed data are.

Pareto, Power Laws, and Fat Tails
What they don’t teach you in statistics
towardsdatascience.com
Although Pareto (and more generally power law) distributions give us a
salient example of fat tails, this is a more general notion that lives on a
spectrum ranging from thin-tailed (i.e. a Gaussian) to very fat-tailed (i.e.
Pareto 80 – 20).
The spectrum of Fat-tailedness. Image by author.
This view of fat-tailedness provides us with a more flexible and precise way of
categorizing data than simply labeling it as a Power Law (or not). However,
this begs the question: how do we define fat-tailedness?
4 Ways to Quantify Fat Tails

将 RAG 添加到 LLM

我们首先从 Hugging Face hub 下载 微调模型。

## load fine-tuned model from hub
from peft import PeftModel, PeftConfig
from transformers import AutoModelForCausalLM, AutoTokenizer

model_name = "TheBloke/Mistral-7B-Instruct-v0.2-GPTQ"
model = AutoModelForCausalLM.from_pretrained(model_name,
                                             device_map="auto",
                                             trust_remote_code=False,
                                             revision="main")

config = PeftConfig.from_pretrained("shawhin/shawgpt-ft")
model = PeftModel.from_pretrained(model, "shawhin/shawgpt-ft")

## load tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name, use_fast=True)

作为基线,我们可以观察模型在没有任何文章上下文的情况下如何回应技术问题。为此,我们使用 lambda 函数创建一个提示模板,该函数接受观众评论并返回 LLM 的提示。有关此提示来源的更多详细信息,请参见本系列的 上一篇文章。

## prompt (no context)
intstructions_string = f"""ShawGPT, functioning as a virtual data science \
consultant on YouTube, communicates in clear, accessible language, escalating \
to technical depth upon request. It reacts to feedback aptly and ends \
responses with its signature '–ShawGPT'.

ShawGPT will tailor the length of its responses to match the viewer's comment, \
providing concise acknowledgments to brief expressions of gratitude or \
feedback, thus keeping the interaction natural and engaging.

Please respond to the following comment.
"""

prompt_template = lambda comment: f'''[INST] {intstructions_string} \n{comment} \n[/INST]'''
comment = "What is fat-tailedness?"

prompt = prompt_template(comment)
print(prompt)
[INST] ShawGPT, functioning as a virtual data science consultant on YouTube, communicates in clear, accessible language, escalating to technical depth upon request. It reacts to feedback aptly and ends responses with its signature '–ShawGPT'. ShawGPT will tailor the length of its responses to match the viewer's comment, providing concise acknowledgments to brief expressions of gratitude or feedback, thus keeping the interaction natural and engaging.

Please respond to the following comment.

What is fat-tailedness? 
[/INST]

接下来,我们可以使用以下代码将此提示传递给模型。

model.eval()

inputs = tokenizer(prompt, return_tensors="pt")
outputs = model.generate(input_ids=inputs["input_ids"].to("cuda"), 
                          max_new_tokens=280)

print(tokenizer.batch_decode(outputs)[0])

这是模型的响应(没有上下文)。

Great question!

Fat-tailedness is a statistical property of a distribution. In simple terms, 
it refers to the presence of extreme outliers or heavy tails in the 
distribution.

For instance, consider the distribution of heights in a population. A normal 
distribution would have most people clustered around an average height with a 
few people deviating slightly from the mean. However, in a fat-tailed 
distribution, you would observe a larger number of people being significantly 
taller or shorter than the average.

In finance, fat-tailedness is often used to describe the presence of extreme 
market moves or outliers in financial returns. It's an important concept in 
risk management and financial modeling.

I hope this explanation helps! Let me know if you have any questions.
–ShawGPT

虽然响应的风格和格式都很好,但模型对 fat-tailedness 的解释与我在我的 视频 和 博客系列 中定义的有所不同。

让我们看看当我们包含适当的上下文时模型的响应会发生什么。为此,我们创建另一个提示模板,该模板也可以从 RAG 系统中获取上下文。

## prompt (with context)
prompt_template_w_context = lambda context, comment: f"""[INST]ShawGPT, functioning as a virtual data science consultant on YouTube, communicates in clear, accessible language, escalating to technical depth upon request. \
It reacts to feedback aptly and ends responses with its signature '–ShawGPT'. \
ShawGPT will tailor the length of its responses to match the viewer's comment, providing concise acknowledgments to brief expressions of gratitude or feedback, \
thus keeping the interaction natural and engaging.

{context}
Please respond to the following comment. Use the context above if it is helpful.

{comment}
[/INST]
"""

接下来,我们将查询引擎中的上下文和观众评论传递给模型。

prompt = prompt_template_w_context(context, comment)

inputs = tokenizer(prompt, return_tensors="pt")
outputs = model.generate(input_ids=inputs["input_ids"].to("cuda"), max_new_tokens=280)

print(tokenizer.batch_decode(outputs)[0])

这是新的响应(带上下文)。

Great question!

Fat-tailedness is a property of a distribution that describes how much the 
presence of extreme values (rare events) affects the overall statistics of the 
distribution.

In the context of the video, the author discusses how Mediocristan and
Extremistan distributions differ in terms of fat-tailedness. Mediocristan 
distributions have a relatively small impact from extreme values, while
Extremistan distributions are heavily influenced by extreme values.

The author also mentions that fat-tailedness is a more general concept than 
specific distributions like Pareto or Power Laws. Instead, it lives on a 
spectrum ranging from thin-tailed (Gaussian) to very fat-tailed (Pareto 80-20).

I hope that helps clarify things a bit! Let me know if you have any questions.
–ShawGPT

这比没有上下文的响应更好地捕捉了我对 fat tails 的解释,并且甚至提到了 Mediocristan 和 Extremistan 的细分概念。

如何学习大模型

现在社会上大模型越来越普及了,已经有很多人都想往这里面扎,但是却找不到适合的方法去学习。

作为一名资深码农,初入大模型时也吃了很多亏,踩了无数坑。现在我想把我的经验和知识分享给你们,帮助你们学习AI大模型,能够解决你们学习中的困难。

下面这些都是我当初辛苦整理和花钱购买的资料,现在我已将重要的AI大模型资料包括市面上AI大模型各大白皮书、AGI大模型系统学习路线、AI大模型视频教程、实战学习,等录播视频免费分享出来,需要的小伙伴可以扫取。

一、AGI大模型系统学习路线

很多人学习大模型的时候没有方向,东学一点西学一点,像只无头苍蝇乱撞,我下面分享的这个学习路线希望能够帮助到你们学习AI大模型。

在这里插入图片描述

二、AI大模型视频教程

在这里插入图片描述

三、AI大模型各大学习书籍

在这里插入图片描述

四、AI大模型各大场景实战案例

在这里插入图片描述

五、结束语

学习AI大模型是当前科技发展的趋势,它不仅能够为我们提供更多的机会和挑战,还能够让我们更好地理解和应用人工智能技术。通过学习AI大模型,我们可以深入了解深度学习、神经网络等核心概念,并将其应用于自然语言处理、计算机视觉、语音识别等领域。同时,掌握AI大模型还能够为我们的职业发展增添竞争力,成为未来技术领域的领导者。

再者,学习AI大模型也能为我们自己创造更多的价值,提供更多的岗位以及副业创收,让自己的生活更上一层楼。

因此,学习AI大模型是一项有前景且值得投入的时间和精力的重要选择。

### 大规模语言模型(LLM)简介 大规模语言模型(Large Language Model, LLM)是一种能够处理自然语言任务的人工智能技术,其核心在于通过对大量文本数据的学习,掌握复杂的语义模式语法结构[^1]。这些模型通常具有数十亿甚至数千亿的参数量,使其具备强大的泛化能力适应能力。 #### LLM 的基本原理 LLM 的工作原理主要依赖于两种训练方式:掩码语言建模(Masked Language Modeling, MLM)自回归建模(Autoregressive Modeling, AR)。 - **MLM** 是指通过遮蔽部分单词并让模型预测被遮蔽的内容来学习上下文关系[^5]。这种方式有助于提高模型对双向上下文的理解能力。 - **AR** 则是指按照顺序逐字生成下一个可能的词语或字符。这种方法更适用于生成连贯的文本序列。 此外,LLM 还可以通过微调(Fine-tuning)、提示工程(Prompt Engineering)等方式进一步提升性能,从而满足特定领域的需求[^3]。 --- ### 垂类模型与多模态模型的区别 尽管 LLM 功能强大,但它并非万能工具,在某些场景下仍需借助其他类型的模型完成任务。以下是几种常见模型及其特点: | 类型 | 描述 | |--------------|----------------------------------------------------------------------------------------| | **垂类模型** | 针对某一具体应用场景设计的小型专用模型,如医疗诊断、法律咨询等领域内的定制化解决方案[^4]。 | | **多模态模型** | 能够同时处理多种类型的数据源(例如图像、音频、视频),实现跨媒体分析的功能扩展版本。 | 相比通用的大规模预训练架构而言,上述两类细分方向往往拥有更高的针对性优势;然而它们也存在局限性——即缺乏足够的灵活性去应对未知环境下的新挑战。 因此,实际操作过程中往往会采用“基于大模型构建垂直应用”的策略,即将开源或者商业化的超大型基座网络作为起点,再结合目标行业的专业知识进行二次加工改造。 --- ### 如何利用 LLM 开发企业级应用? 当谈及如何将理论转化为生产力时,《从零开始:基于 LLM 大模型构建智能应用程序的完整指南》提供了详尽指导。该文档强调了整个流程涉及多个环节,包括但不限于需求调研、算法选型、数据准备直至最终部署上线等一系列动作要素考量因素众多。 值得注意的是,“幻觉效应”一直是制约此类项目成功与否的关键障碍之一。所谓‘hallucination’现象指的是即使输入条件完全相同情况下也可能得到截然不同的输出结果的现象。对此业界提出了诸如引入外部知识库检索机制(Retrieval-Augmented Generation,RAG)加以缓解措施建议方案[^2]。 另外值得一提的例子还有国内几家头部科技公司推出的明星产品线,像科大讯飞旗下的星火认知平台以及百度研发团队打造而成的文心一言系列等等均属于这一范畴之内,并且已经在各自擅长范围内取得了不俗成绩表现出来[见引用4关于实战案例部分内容说明]. --- ```python # 示例代码展示简单的 Fine-Tune 流程 (伪代码) from transformers import AutoTokenizer, AutoModelForCausalLM def fine_tune_llm(model_name="bigscience/bloom", dataset_path="./data"): tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name) # 加载本地数据集 with open(dataset_path, 'r') as f: data = [line.strip() for line in f] inputs = tokenizer(data, return_tensors='pt', padding=True, truncation=True) optimizer = ... # 定义优化器 loss_fn = ... # 设置损失函数 for epoch in range(num_epochs): outputs = model(**inputs) loss = loss_fn(outputs.logits, labels=...) loss.backward() optimizer.step() fine_tune_llm() ``` --- ### 总结 综上所述,虽然运用 LLM 技术创建智能化软件系统充满难度系数较高的特性,但凭借持续积累经验教训加上合理规划安排完全可以克服困难达成预期效果。未来随着算力资源日益充沛再加上框架生态愈发完善健全,相信会有越来越多高质量作品涌现问世造福社会大众群体。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值