from langchain.document_loaders import HuggingFaceDatasetLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from transformers import AutoTokenizer, AutoModelForCausalLM,pipeline
from langchain_huggingface import HuggingFacePipeline
from langchain.chains import RetrievalQA技术支持
3. 为访问受限模型和数据集,建议使用 API 密钥登录 Hugging Face hub。
from huggingface_hub import login
from kaggle_secrets import UserSecretsClient
user_secrets = UserSecretsClient()
hf_token = user_secrets.get_secret("HUGGINGFACE_TOKEN")
login(token = hf_token)技术支持
# Specify the dataset name
dataset_name ="ruslanmv/ai-medical-chatbot"# Create a loader instance using dataset columns
loader_doctor = HuggingFaceDatasetLoader(dataset_name,"Doctor")# Load the data
doctor_data = loader_doctor.load()# Select the first 1000 entries
doctor_data = doctor_data[:1000]
doctor_data[:2]技术支持
如您所见,“Doctor” 列是页面内容,其余列被视为元数据。
6. 从 Hugging Face 加载嵌入模型,并设置如启用 GPU 加速等参数。
7. 用示例文本测试嵌入模型。
# Define the path to the embedding model
modelPath ="sentence-transformers/all-MiniLM-L12-v2"# GPU acceleration
model_kwargs ={'device':'cuda'}# Create a dictionary with encoding options
encode_kwargs ={'normalize_embeddings':False}# Initialize an instance of HuggingFaceEmbeddings with the specified parameters
embeddings = HuggingFaceEmbeddings(
model_name=modelPath,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs
)
text ="Why are you a doctor?"
query_result = embeddings.embed_query(text)
query_result[:3]技术支持
vector_db = FAISS.from_documents(doctor_data, embeddings)
vector_db.save_local("/kaggle/working/faiss_doctor_index")
question ="Hi Doctor, I have a headache, help me."
searchDocs = vector_db.similarity_search(question)print(searchDocs[0].page_content)技术支持
from transformers import AutoTokenizer,AutoModelForCausalLM,pipeline
import torch
base_model ="/kaggle/input/fine-tuned-adapter-to-full-model/llama-3-8b-chat-doctor/"
tokenizer = AutoTokenizer.from_pretrained(base_model)
model = AutoModelForCausalLM.from_pretrained(
base_model,
return_dict=True,
low_cpu_mem_usage=True,
torch_dtype=torch.float16,
device_map="auto",
trust_remote_code=True,)技术支持
3. 将聊天模板应用到消息上。
4. 使用模型和分词器创建文本生成管道。
5. 向管道对象提供提示并生成回答。
messages =[{"role":"user","content":"Hi Doctor, I have a headache, help me."}]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
pipe = pipeline("text-generation",
model=model,
tokenizer=tokenizer,
torch_dtype=torch.float16,
device_map="auto",)
outputs = pipe(prompt, max_new_tokens=120, do_sample=True)print(outputs[0]["generated_text"])技术支持
该回答与数据集非常相似。风格一致,但并非直接给出答案,而是建议患者进一步检查。
6. 我们来问第二个问题。
messages =[{"role":"user","content":"Hello doctor, I have bad acne. How do I get rid of it?"}]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
outputs = pipe(prompt, max_new_tokens=120, do_sample=True)print(outputs[0]["generated_text"])技术支持
大多数大型语言模型(LLM),如 GPT-4,都是在通用且往往过时的数据集上训练的。它们擅长回答一般性问题,但在涉及最新新闻、最新进展以及特定领域的问题时往往力不从心。在这些情况下,模型可能会出现幻觉或给出不准确的回答。
尽管更强的模型(如 Claude 3.5 Sonnet)不断涌现,我们仍需要通过微调来生成定制化回答,或借助检索增强生成(RAG)系统为基础模型提供额外上下文。
在本教程中,我们将探讨 RAG 和微调这两种用于改进 LLM 回答的不同技术。我们将比较它们的差异,并通过评估结果把理论付诸实践。
此外,我们还将深入了解混合技术,将微调后的模型与 RAG 系统结合,以兼得二者之长。最后,我们将学习如何根据具体用例和需求在这三种方法之间做出选择。
RAG 与微调概览
RAG 和微调都能提升特定领域问答的生成质量,但它们本质上是完全不同的技术。下面来分别了解。
检索增强生成(RAG)
检索增强生成是一种让大型语言模型(如 GPT-4o)利用外部数据源获得上下文感知能力的过程。它由检索器与生成器组合而成。检索器从互联网或向量数据库中获取数据,并将其与用户的原始查询一同提供给生成器。生成器利用这些额外上下文生成高度准确且相关的回答。
想了解更多,请阅读我们的文章 What is Retrieval Augmented Generation (RAG)? A Guide to the Basics,了解 RAG 应用的工作机制及其多种用例。
微调
微调是使用特定领域数据集对预训练模型进行调优的过程。预训练模型通常在从互联网抓取的大规模通用语料上训练。它们擅长回答一般性问题,但在回答特定领域问题时会吃力,甚至产生幻觉。
例如,一个预训练模型也许在日常对话上很熟练,但当被问到复杂的医疗操作或法律判例时,可能会给出错误答案。
在医疗或法律数据集上进行微调,可让模型在这些领域的问题上理解更深、回答更准确、更相关。
请参阅教程 An Introductory Guide to Fine-Tuning LLMs,通过可视化指南学习如何定制预训练模型。
RAG vs. 微调
我们已经分别了解了改进 LLM 生成效果的方法。接下来对比它们的差异,以便更好地理解。
1. 学习方式
RAG 采用动态学习方式,使语言模型能够访问并使用来自数据库、互联网甚至 API 的最新且最准确的数据。这确保了生成的回答始终与时俱进且相关。
微调 则是静态学习,模型在训练阶段通过新数据集进行学习。该方法能让模型适应特定领域的生成,但训练后无法在不重新训练的情况下整合新信息。
2. 适应性
RAG 擅长泛化。它通过检索流程从不同数据源提取信息。RAG 并不改变模型本身的生成能力,只是提供额外信息来引导模型。
微调 会定制模型输出,并提升与训练数据集密切相关的特定领域性能。它也会改变回答风格,有时比 RAG 系统更能给出切题的答案。
3. 资源消耗
RAG 在推理阶段执行,因此资源消耗较大。与不使用 RAG 的简单 LLM 相比,RAG 需要更多内存和算力。
微调 计算开销大,但只需进行一次。在训练过程中需要多块 GPU 和高内存,但此后相较于 RAG 系统更节省资源。
4. 成本
RAG 为了更好的生成效果,需要顶级的嵌入模型和 LLM,还需要快速的向量数据库。API 与运维成本可能会迅速攀升。
微调 的大头成本主要发生在训练过程中,但之后的推理成本相较 RAG 便宜不少。
总体而言,若全面考量,平均来看微调的总成本往往高于 RAG。
5. 实施复杂度
RAG 系统可由软件工程师构建,对技术深度要求中等。您需要了解 LLM 架构、向量数据库、嵌入、提示工程等,虽然需要时间,但大致一个月即可入门。
微调 则需要较高的技术门槛。从数据集准备、调参到性能监控,都需要在自然语言处理领域多年的经验。
通过实用示例检验理论
我们将对同一个提示分别在微调模型、RAG 应用和混合方法上进行测试,并评估结果。混合方法将微调模型与 RAG 应用结合。示例中我们使用 Hugging Face 的 ruslanmv/ai-medical-chatbot 数据集,其中包含患者与医生就各种健康问题的对话。
使用 Llama 3 构建 RAG 应用
我们将从使用 Llama 3 与 LangChain 生态构建 RAG 应用开始。
您也可以按照以下代码实操,学习用 LlamaIndex 构建 RAG 应用: Retrieval Augmented Generation with LlamaIndex。
1. 安装所有必要的 Python 包。
2. 从 LangChain 和 Transformers 库加载所需函数。
3. 为访问受限模型和数据集,建议使用 API 密钥登录 Hugging Face hub。
4. 通过向
HuggingFaceDatasetLoader提供数据集名称和列名来加载数据集。“Doctor” 列将作为我们的主文档,其余列将作为元数据。5. 将数据集限制为前 1000 行。缩小数据集有助于减少向量数据库的数据存储时间。
如您所见,“Doctor” 列是页面内容,其余列被视为元数据。
6. 从 Hugging Face 加载嵌入模型,并设置如启用 GPU 加速等参数。
7. 用示例文本测试嵌入模型。
8. 将数据转换为嵌入并保存到向量数据库。
9. 将向量数据库保存到本地目录。
10. 使用示例提示执行相似度搜索。
11. 将向量数据库实例转换为检索器,以便创建 RAG 链。
12. 使用 Llama 3 8B Chat 模型加载分词器和模型。
13. 使用它们创建文本生成管道。
14. 将该管道转换为 LangChain 的 LLM 客户端。
15. 使用检索器、用户查询、RAG 提示与 LLM 创建问答链。
16. 向医生提问来测试问答链。
结果与数据集相当接近,但没有抓住风格。模型理解了上下文,并用自己的风格写出了回应。
再换一个问题试试。
这是一个非常直接的回答。对于医患聊天机器人,也许我们该用微调而不是 RAG。
如果您运行代码遇到困难,请参考 Kaggle 笔记本:Building RAG Application using Llama 3。
通过 Chunking、重排序与查询变换等技术来提升 RAG 系统表现,请参阅教程 How to Improve RAG Performance: 5 Key Techniques with Examples。
在医疗数据上微调 Llama 3
我们这次不会在医患数据集上进行微调,因为此前的教程已完成该步骤:Fine-Tuning Llama 3 and Using It Locally: A Step-by-Step Guide。我们要做的是加载微调后的模型,并用相同问题来评估结果。该微调模型可在 Hugging Face 和 Kaggle 获取。
如果您想使用 OpenAI API 微调 GPT-4 模型,可参考这篇易上手的 DataCamp 教程 Fine-Tuning OpenAI's GPT-4: A Step-by-Step Guide。
来源:kingabzpro/llama-3-8b-chat-doctor
1. 使用 transformers 库加载分词器和模型。
2. 请确保在 Kaggle GPU T4 x2 环境中使用正确参数加载模型。
3. 将聊天模板应用到消息上。
4. 使用模型和分词器创建文本生成管道。
5. 向管道对象提供提示并生成回答。
该回答与数据集非常相似。风格一致,但并非直接给出答案,而是建议患者进一步检查。
6. 我们来问第二个问题。
风格一致,且回答更具同理心和解释性。
如果您运行代码遇到困难,请参考 Kaggle 笔记本:Fine-tuned Llama 3 HF Inference。
混合方法(RAG + 微调)
现在我们将为微调模型提供额外上下文,以进一步优化回答,寻找平衡点。
我们不再重复书写全部代码,而是直接使用问答链进行生成。如果您想查看我们如何将微调模型与 RAG 问答链结合的完整代码,请参阅这份 Hybrid Approach (RAG + Fine-tuning) Kaggle 笔记本。
将与之前在 RAG 与微调模型中提出的相同问题提供给该链。
答案相当准确,且采用了医生的表达风格。
再问第二个问题。
这有点奇怪。我们并未提供关于痤疮是否化脓等额外上下文。也许混合模型并不适用于某些查询。
在医患聊天机器人的场景中,微调模型在风格把握和准确性上表现更佳。不过,其他用例可能不同,因此务必要进行充分测试,以确定最适合您特定用例的方法。
这种混合方法的正式称谓是 RAFT(Retrieval Augmented Fine-Tuning,检索增强微调)。想要进一步了解,请阅读What is RAFT? Combining RAG and Fine-Tuning To Adapt LLMs To Specialized Domains 博文。
如何在 RAG、微调与 RAFT 之间做选择
这完全取决于您的用例与可用资源。如果您是资源有限的初创公司,可以尝试使用 OpenAI API 与 LangChain 框架构建一个 RAG 概念验证。为此,所需资源、专业知识和数据集都较少。
如果您是中型公司,希望通过微调来提升回答准确性并在云端部署开源模型,您需要招聘数据科学家与 MLOps 工程师等专家。微调需要顶级 GPU、大内存、清洗过的数据集,以及懂 LLM 的技术团队。
混合方案在资源与算力上都更为密集,还需要能在微调与 RAG 之间做好平衡的 LLMOps 工程师。当您希望进一步提升生成质量、兼顾 RAG 与微调模型的优势时,可考虑这种方案。
请参考下表以获得 RAG、微调与 RAFT 的概览。
RAG
微调
RAFT
优势
具备上下文理解,减少幻觉,易于适配新数据,成本较低。
任务专长、可定制、准确性提升、鲁棒性增强。
结合 RAG 与微调之长,理解更深入、上下文更充分。
劣势
数据源管理、系统复杂度。
数据偏差、资源密集、计算成本高、内存需求大,耗时且需高水平专业能力。
实现复杂度高,需要平衡检索与微调流程。
实施复杂度
高于提示工程。
高于 RAG。需要高度专业人才。
三者中最复杂。
学习方式
动态
静态
动态 + 静态
适应性
易于适应新数据与变化的事实。
将输出定制到特定任务与领域。
既适应实时数据,又适配特定任务。
成本
低
中
高
资源消耗
低。主要在推理阶段消耗资源。
中。主要在微调阶段消耗资源。
高
结语
大型语言模型是当今 AI 发展的核心。各家公司都在寻找无需投入巨额训练成本就能改进与定制这些模型的方法。他们通常从参数优化与提示工程入手,随后选择 RAG 或进行微调,以进一步提升回答并减少幻觉。尽管还有其他改进手段,但这些是当前最常用的选项。
在本教程中,我们通过理论与实践示例了解了 RAG 与微调的差异,也探讨了混合模型,并比较了哪种方法更适合您。
想进一步了解 LLM 的部署及相关技术,请查看我们的代码实操 RAG with LLaMAIndex,以及我们的课程 deploying LLM applications with LangChain。