原创 超乎想象 [ 超乎想象的科技圈 ](javascript:void(0)😉 2024年08月13日 00:28 北京
概述
根据使用RAG技术构建企业级文档问答系统之基础流程中的RAG流程描述,有一个很重要步骤是从知识库中检索相关的文档片段,由于RAG是一个典型的串行流程,即先检索,再生成,因此,提升检索性能,通常可以提升RAG的效果。
针对RAG检索部分的优化,已经有不少优化手段,典型的有如下这些:
- Embedding模型优化
- 混合检索(BM25+Embedding)
- Multi Query
- RAG Fusion
- Hypothetical Document Embeddings(HyDE)
- Rerank
- …
从本文开始,会陆续覆盖这些优化方法,每次优化,会分别计算检索的命中率,和问答的准确率,以便大家直观地感受不同优化手段带来的性能提升。
检索的命中率,会根据《使用RAG技术构建企业级文档问答系统之基础流程》中介绍的HitRate,问答的准确率,然后评估,考虑到大家访问OpenAI的API可能不便,打分统一替换为Qwen2-72B-Instruct。
本文首先介绍Embedding模型优化。通常我们所使用的Embedding模型,由于要考虑到通用型,不会特别针对某个领域做专门的优化。针对Embedding模型的优化,主要有两个部分可以做:
- 对Embedding做二次预训练
- 对Embedding模型做微调
对Embedding做二次预训练通常不会有显著的效果提升,本文主要介绍对Embedding模型的微调。所选取的模型是BAAI/bge-large-zh-v1.5(HuggingFace中的模型ID),是BAAI(智源)开源的一个Embedding,这个基本上也是目前RAG中用得比较多的模型了,虽然BGE发布后陆续有新的模型不断刷新了榜单,但综合对比下来,BGE还是很能打的。
本文会介绍Embedding微调时,涉及的如下几点:
- 微调样本构建
- 微调脚本
- 训练过程监控:W&B监控
- 模型效果评估
本文优化后的模型评估效果见下表,可以看出,检索的HitRate,Embedding微调后的模型,都是显著优于Baseline(基础流程中介绍的方法)的,问答全流程,使用3个知识片段的Embedding微调后的模型进行检索,也取得了目前为止的最好效果
本文代码已开源,地址在:https://github.com/Steven-Luo/MasteringRAG
其中,样本构建代码为:
- build_embedding_sample_v1.ipynb
- build_embedding_sample_v2.ipynb:最终使用版本
微调脚本为:
- finetune_bge_embedding_v1.sh
- finetune_bge_embedding_v2.sh
- finetune_bge_embedding_v3.sh
- finetune_bge_embedding_v4.sh:最终使用版本
RAG全流程代码为:retrieval/01_bge_embedding_ft.ipynb
环境准备
Embedding模型的训练,虽然对机器的性能要求没有对训练LLM那么高,但也还是有一定要求的,GPU是需要的
硬件环境
本文所用硬件环境如下:
- CPU:i7-9700K
- 内存:64GB
- GPU:GTX 1080Ti(11G显存)
软件环境
软件环境列举主要的Python依赖:
- Python:3.10.9
- pytorch包:2.2.1
- FlagEmbedding包:1.2.10
模型训练
微调样本构建
微调样本的构建过程,其实就是找出跟一个query相似的句子——正样本,以及不相似的句子——负样本,Embedding在微调时,会使用对比学习loss来让模型提高辨别正负样本的能力。
此处只展示核心代码,完整代码可以访问代码仓库
def build_qa_samples(df, neg_batch_size=-1, n_neg_batch=5):
"""
构建qa样本
:param df: 包含qa的DataFrame,共两列,question和answer
:param neg_batch_size: 负样本数量,为-1时表示将所有负样本和单个正样本配对,否则会将负样本拆开,结果中的query可能会重复
"""
from tqdm.auto import tqdm
import math
data = []
for idx, row in tqdm(df.iterrows(), total=len(df)):
question = row['question']
answer = row['answer']
# 筛选同category的,增加难度
neg_samples = df[df['question'] != question]['answer'].values.tolist()
neg_batch_count = math.ceil((len(df) - 1) / neg_batch_size)
neg_batch_count = min(n_neg_batch, neg_batch_count)
for neg_batch_idx in range(neg_batch_count):
batch_neg_samples = neg_samples[neg_batch_idx * neg_batch_size: (neg_batch_idx + 1) * neg_batch_size]
batch_neg_samples = [item for item in batch_neg_samples if item != answer]
data.append({
'query': question,
'pos': [answer],
'neg': batch_neg_samples
})
return data
def write_samples(samples, save_filename)
import json
with open(save_filename, 'w') as f:
for sample in samples:
f.write(json.dumps(sample, ensure_ascii=False))
f.write('\n')
其中df样例如下:
build_qa_samples
函数的返回结果样例如下:
{"query": "美元指数在2023年的走势如何?",
"pos": ["美元指数高位震荡后走弱"],
"neg": ["全球货物贸易量指数和价格指数下行,主要经济体出口贸易同比增速下降。",
"欧美央行货币政策延续收紧态势,但步伐整体放缓。",
"高利率环境抑制债券融资需求,债券违约风险持续上升,美国政府债务可持续性问题引发市场关注。",
"展望2024年,预计全球经济复苏将依旧疲软,主要经济体增长态势和货币政策将进一步分化。",
"发达经济体增速明显放缓,预计2023年增速较2022年下降1个百分点。",
"新兴经济体增速与2022年大致持平,预计2023年增速比2022年下降0.1个百分点。",
"美国GDP环比增长折年率为4.9%,比二季度增速高2.8个百分点。",
"其中,主要新兴经济体工业生产指数普遍走高,如俄罗斯、土耳其、南非等,而发达经济体中的美国和韩国回升,英国、德国、意大利下行,日本波动较大,整体趋于平稳。",
"主要新兴经济体工业生产指数普遍走高,如俄罗斯、土耳其、南非等",
"全球融资环境收紧和经济下行压力对工业生产前景带来较大影响",
"欧洲各国消费指数整体维持稳定(图4)",
"是上半年免于陷入衰退的主要动力。",
"OECD消费者信心指数从7月开始连续3个月回落",
"美国私人投资在2023年一季度触底后逐渐反弹",
"2023年二季度,欧元区固定资本形成总额环比增长0.1%,比一季度增速下降0.3个百分点",
"房地产对GDP环比增长拉动率转为负值"]}
微调脚本
此处原始参考文档来自BGE官方仓库:https://github.com/FlagOpen/FlagEmbedding/tree/master/examples/finetune
构造好微调样本后,就可以开始微调模型了。代码仓库中包含了4个版本的微调脚本,总体大同小异,此处以finetune_bge_embedding_v4.sh
为例
#!/bin/bash
SCRIP_DIR=$(echo `cd $(dirname $0); pwd`)
export PATH=/work/cache/env/miniconda3/bin:$PATH
# 此处替换为“微调样本构建”步骤产出文件的路径,代码仓库中也有此文件
export TRAIN_DATASET=outputs/v1_20240713/emb_samples_qd_v2.jsonl
export N_EPOCH=1
export TRAIN_GROUP_SIZE=8
export GRADIENT_ACCUMULATION_STEPS=64
export PER_DEVICE_TRAIN_BATCH_SIZE=1
export N_NODES=1
export BATCH_SIZE=`expr ${GRADIENT_ACCUMULATION_STEPS} \* ${PER_DEVICE_TRAIN_BATCH_SIZE} \* ${N_NODES}`
export VERSION=ft_v4_bge_large_epoch_${N_EPOCH}_bz_${BATCH_SIZE}_trgrp_${TRAIN_GROUP_SIZE}_$(date +"%Y%m%d_%H%M")
# WANDB相关环境变量:如果需要将微调过程中相关指标上传W&B,可以设置这些环境变量,否则可以注释掉
export WANDB_PROJECT=RAG-From-Scratch-Embedding-Finetune
export WANDB_API_KEY=替换为自己的W&B key
export WANDB_NAME=${VERSION}
export OUTPUT_DIR=experiments/embedding/finetune/${VERSION}
if [ ! -d "${OUTPUT_DIR}" ]; then
mkdir -p "${OUTPUT_DIR}"
fi
# model_name_or_path替换为自己的本机路径,或者BAAI/bge-large-zh-v1.5
torchrun --nproc_per_node ${N_NODES} \
-m FlagEmbedding.baai_general_embedding.finetune.run \
--output_dir ${OUTPUT_DIR} \
--model_name_or_path /DataScience/HuggingFace/Models/BAAI/bge-large-zh-v1.5 \
--train_data ${TRAIN_DATASET} \
--learning_rate 1e-5 \
--fp16 \
--num_train_epochs ${N_EPOCH} \
--per_device_train_batch_size ${PER_DEVICE_TRAIN_BATCH_SIZE} \
--gradient_accumulation_steps ${GRADIENT_ACCUMULATION_STEPS} \
--dataloader_drop_last True \
--normlized True \
--temperature 0.02 \
--query_max_len 64 \
--passage_max_len 512
--train_group_size ${TRAIN_GROUP_SIZE} \
--negatives_cross_device \
--logging_steps 5 \
--save_steps 50 \
--save_total_limit 10 \
--warmup_ratio 0.05 \
--lr_scheduler_type cosine \
--query_instruction_for_retrieval ""
cp "$SCRIP_DIR/$0" ${OUTPUT_DIR}
启动微调:
bash finetune_bge_embedding_v4.sh
微调后的模型已经上传HuggingFace,大家可以搜索这个模型ID使用:stevenluo/bge-large-zh-v1.5-ft-v4
模型监控
4个版本的完整训练过程监控,可以访问下方的链接查看:
https://wandb.ai/steven-luog/RAG-From-Scratch-Embedding-Finetune/reports/MasteringRAG-Embedding-Finetune–Vmlldzo5MDA3Mjg1?accessToken=vnwdand2uzh1v5wfgv16vtb5rgk1chng81hj8tc7c4gy8lxmo15xwa0xblh9mrrc
以下是截图:
小结
上文介绍过,微调样本需要准备query或者question,正样本列表,负样本列表,query自然是用户问题(下面“-”前面的Q),根据正负样本的来源(下面“-”后面的部分),通常可以分为如下几种:
- Q-Q(question-question):这种方式适合已经积累了比较多FAQ的企业,希望对用户问题检索FAQ库中的Q,这种情况下,使用Q-Q方式构建的样本,优化的模型检索效果会比较好
- Q-A(question-answer):这种方式比较有误导性,看起来感觉最应该用这种方式构建,但实际上线后,要检索的,是一堆documents,而不是answer,如果你真的用这个方式构建过样本,看一些case就会发现,answer跟实际的文档相差非常远,导致模型微调后,性能反而出现下降
- Q-D(question-document):这种方式,在几个项目中实践下来,基本上是最适合的构建方式,因为实际检索时,就是拿问题去检索文档,确保训练、推理时任务的一致性,也是减少模型性能损失最主要的一个方法
模型使用
模型的使用方式,与使用RAG技术构建企业级文档问答系统之基础流程中介绍的完全一致,只是替换模型路径model_path
即可
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。
我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
第一阶段(10天):初阶应用
该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。
- 大模型 AI 能干什么?
- 大模型是怎样获得「智能」的?
- 用好 AI 的核心心法
- 大模型应用业务架构
- 大模型应用技术架构
- 代码示例:向 GPT-3.5 灌入新知识
- 提示工程的意义和核心思想
- Prompt 典型构成
- 指令调优方法论
- 思维链和思维树
- Prompt 攻击和防范
- …
第二阶段(30天):高阶应用
该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。
- 为什么要做 RAG
- 搭建一个简单的 ChatPDF
- 检索的基础概念
- 什么是向量表示(Embeddings)
- 向量数据库与向量检索
- 基于向量检索的 RAG
- 搭建 RAG 系统的扩展知识
- 混合检索与 RAG-Fusion 简介
- 向量模型本地部署
- …
第三阶段(30天):模型训练
恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。
到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?
- 为什么要做 RAG
- 什么是模型
- 什么是模型训练
- 求解器 & 损失函数简介
- 小实验2:手写一个简单的神经网络并训练它
- 什么是训练/预训练/微调/轻量化微调
- Transformer结构简介
- 轻量化微调
- 实验数据集的构建
- …
第四阶段(20天):商业闭环
对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。
- 硬件选型
- 带你了解全球大模型
- 使用国产大模型服务
- 搭建 OpenAI 代理
- 热身:基于阿里云 PAI 部署 Stable Diffusion
- 在本地计算机运行大模型
- 大模型的私有化部署
- 基于 vLLM 部署大模型
- 案例:如何优雅地在阿里云私有部署开源大模型
- 部署一套开源 LLM 项目
- 内容安全
- 互联网信息服务算法备案
- …
学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。
如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
】
![](https://img-blog.csdnimg.cn/img_convert/0dc35641cd2d85e39db6e8cb368fa5cb.png)