检索增强生成(RAG)实践:基于LlamaIndex和Qwen1.5搭建智能问答系统

  • 什么是 RAG

LLM 会产生误导性的 “幻觉”,依赖的信息可能过时,处理特定知识时效率不高,缺乏专业领域的深度洞察,同时在推理能力上也有所欠缺。

正是在这样的背景下,检索增强生成技术(Retrieval-Augmented Generation,RAG)应时而生,成为 AI 时代的一大趋势。RAG 通过在语言模型生成答案之前,先从广泛的文档数据库中检索相关信息,然后利用这些信息来引导生成过程,极大地提升了内容的准确性和相关性。RAG 有效地缓解了幻觉问题,提高了知识更新的速度,并增强了内容生成的可追溯性,使得大型语言模型在实际应用中变得更加实用和可信

一个典型的 RAG 的例子:

这里面主要包括包括三个基本步骤:

  1. 索引 — 将文档库分割成较短的 Chunk,并通过编码器构建向量索引。
  2. 检索 — 根据问题和 chunks 的相似度检索相关文档片段。
  3. 生成 — 以检索到的上下文为条件,生成问题的回答。

1.通义千问Qwen 1.5

1.1 简介

Qwen1.5 版本年前开源了包括 0.5B、1.8B、4B、7B、14B 和 72B 在内的六种大小的基础和聊天模型,同时,也开源了量化模型。不仅提供了 Int4 和 Int8 的 GPTQ 模型,还有 AWQ 模型,以及 GGUF 量化模型。为了提升开发者体验,Qwen1.5 的代码合并到 Hugging Face Transformers 中,开发者现在可以直接使用 transformers>=4.37.0 而无需 trust_remote_code。

与之前的版本相比,Qwen1.5 显著提升了聊天模型与人类偏好的一致性,并且改善了它们的多语言能力。所有模型提供了统一的上下文长度支持,支持 32K 上下文。还有,基础语言模型的质量也有所小幅改进

Qwen1.5 全系列统一具备强大的链接外部系统能力(agent/RAG/Tool-use/Code-interpreter)

正因为 Qwen1.5 作为中文 LLM 率先合入了 Transformers,我们也可以使用 LLaMaIndex 的原生 HuggingFaceLLM 来加载模型。

当前基础模型已经稳定训练了大规模高质量且多样化的数据,覆盖多语言(当前以中文和英文为主),总量高达3万亿token。在相关基准评测中,Qwen系列模型拿出非常有竞争力的表现,显著超出同规模模型并紧追一系列最强的闭源模型。此外,我们利用SFT和RLHF技术实现对齐,从基座模型训练得到对话模型。Qwen-Chat具备聊天、文字创作、摘要、信息抽取、翻译等能力,同时还具备一定的代码生成和简单数学推理的能力。在此基础上,我们针对LLM对接外部系统等方面针对性地做了优化,当前具备较强的工具调用能力,以及最近备受关注的Code Interpreter的能力和扮演Agent的能力。我们将各个大小模型的特点列到了下表。

模型 开源日期 最大上下文长度 System Prompt强化 预训练token数 微调(Q-Lora)最小GPU用量 生成2048个token的最小显存占用 工具调用
Qwen-1.8B 23.11.30 32K 2.2T 5.8GB 2.9GB
Qwen-7B 23.08.03 32K 2.4T 11.5GB 8.2GB
Qwen-14B 23.09.25 8K 3.0T 18.7GB 13.0GB
Qwen-72B 23.11.30 32K 3.0T 61.4GB 48.9GB

Qwen系列模型相比同规模模型均实现了效果的显著提升。我们评测的数据集包括MMLU、C-Eval、 GSM8K、 MATH、HumanEval、MBPP、BBH等数据集,考察的能力包括自然语言理解、知识、数学计算和推理、代码生成、逻辑推理等。Qwen-72B在所有任务上均超越了LLaMA2-70B的性能,同时在10项任务中的7项任务中超越GPT-3.5.

Model MMLU C-Eval GSM8K MATH HumanEval MBPP BBH CMMLU
5-shot 5-shot 8-shot 4-shot 0-shot 3-shot 3-shot 5-shot
LLaMA2-7B 46.8 32.5 16.7 3.3 12.8 20.8 38.2 31.8
LLaMA2-13B 55.0 41.4 29.6 5.0 18.9 30.3 45.6 38.4
LLaMA2-34B 62.6 - 42.2 6.2 22.6 33.0 44.1 -
ChatGLM2-6B 47.9 51.7 32.4 6.5 - - 33.7 -
InternLM-7B 51.0 53.4 31.2 6.3 10.4 14.0 37.0 51.8
InternLM-20B 62.1 58.8 52.6 7.9 25.6 35.6 52.5 59.0
Baichuan2-7B 54.7 56.3 24.6 5.6 18.3 24.2 41.6 57.1
Baichuan2-13B 59.5 59.0 52.8 10.1 17.1 30.2 49.0 62.0
Yi-34B 76.3 81.8 67.9 15.9 26.2 38.2 66.4 82.6
XVERSE-65B 70.8 68.6 60.3 - 26.3 - - -
Qwen-1.8B 45.3 56.1 32.3 2.3 15.2 14.2 22.3 52.1
Qwen-7B 58.2 63.5 51.7 11.6 29.9 31.6 45.0 62.2
Qwen-14B 66.3 72.1 61.3 24.8 32.3 40.8 53.4 71.0
Qwen-72B 77.4 83.3 78.9 35.2 35.4 52.2 67.7 83.6

1.2 推理性能

测算了BF16、Int8和Int4模型在生成2048个token时的平均推理速度(tokens/s)和显存使用。结果如下所示:

Model Size Quantization Speed (Tokens/s) GPU Memory Usage
1.8B BF16 54.09 4.23GB
Int8 55.56 3.48GB
Int4 71.07 2.91GB
7B BF16 40.93 16.99GB
Int8 37.47 11.20GB
Int4 50.09 8.21GB
14B BF16 32.22 30.15GB
Int8 29.28 18.81GB
Int4 38.72 13.01GB
72B BF16 8.48 144.69GB (2xA100)
Int8 9.05 81.27GB (2xA100)
Int4 11.32 48.86GB
72B + vLLM BF16 17.60 2xA100

1.3 训练需要配置

下面记录7B和14B模型在单GPU使用LoRA(LoRA (emb)指的是embedding和输出层参与训练,而LoRA则不优化这部分参数)和QLoRA时处理不同长度输入的显存占用和训练速度的情况。本次评测运行于单张A100-SXM4-80G GPU,使用CUDA 11.8和Pytorch 2.0,并使用了flash attention 2。我们统一使用batch size为1,gradient accumulation为8的训练配置,记录输入长度分别为256、512、1024、2048、4096和8192的显存占用(GB)和训练速度(s/iter)。我们还使用2张A100测了Qwen-7B的全参数微调。受限于显存大小,我们仅测试了256、512和1024token的性能。

对于 Qwen-7B,我们额外测试了多机微调的性能。我们在两台服务器上运行评测,每台服务器包含两张A100-SXM4-80G GPU,其余配置与Qwen-7B的其他评测相同。多机微调的结果在表中以 LoRA (multinode) 标示。对于 Qwen-72B,我们测试了两种方案:1)使用4个 A100-SXM4-80G GPUs,通过 Lora + DeepSpeed ZeRO 3 微调和2)使用单张A100-SXM4-80G GPU,通过 QLora (int4) 微调。请注意,使用 LoRA (emb) 微调和不带 DeepSpeed ZeRO 3 的 LoRA 微调在4个A100-SXM4-80G GPUs 上都会出现OOM(你可以通过将--deepspeed finetune/ds_config_zero3.json参数传给finetune/finetune_lora_ds.sh来打开 DeepSpeed ZeRO 3 配置)。

具体数值如下所示:

Model Size Method #Nodes #GPUs per node Sequence Length
256 512 1024 2048 4096
1.8B LoRA 1 1 6.7G / 1.0s/it
LoRA (emb) 1 1 13.7G / 1.0s/it 14.0G / 1.0s/it
Q-LoRA 1 1 5.8G / 1.4s/it 6.0G / 1.4s/it
Full-parameter 1 1 43.5G / 2.1s/it 43.5G / 2.2s/it
7B LoRA 1 1 20.1G / 1.2s/it
LoRA (emb) 1 1 33.7G / 1.4s/it 34.1G / 1.6s/it
Q-LoRA 1 1 11.5G / 3.0s/it 11.5G / 3.0s/it
Full-parameter 1 2 139.2G / 4.0s/it 148.0G / 4.0s/it
LoRA (multinode) 2 2 74.7G / 2.09s/it 77.6G / 3.16s/it
14B LoRA 1 1 34.6G / 1.6s/it
LoRA (emb) 1 1 51.2 / 1.7s/it 51.1G / 2.6s/it
Q-LoRA 1 1 18.7G / 5.3s/it 18.4G / 6.3s/it
72B LoRA + Deepspeed Zero3 1 4 215.4G / 17.6s/it
Q-LoRA 1 1 61.4G / 27.4s/it 61.4G / 31.5s/it

2. LLaMaIndex

2.1 简介

LlamaIndex 是一个基于 LLM 的应用程序的数据框架,受益于上下文增强。 这种 LLM 系统被称为 RAG 系统,代表 “检索增强生成”。LlamaIndex 提供了必要的抽象,可以更轻松地摄取、构建和访问私有或特定领域的数据,以便将这些数据安全可靠地注入 LLM 中,以实现更准确的文本生成。

首先,它有助于“摄取”数据,这意味着将数据从原始来源获取到系统中。其次,它有助于“结构化”数据,这意味着以语言模型易于理解的方式组织数据。第三,它有助于“检索”,这意味着在需要时查找和获取正确的数据。最后,它简化了“集成”,使您更容易将数据与各种应用程序框架融合在一起。

  • LllamaIndex 以专用索引的形式提供独特的数据结构:

    • 向量存储索引:最常用,允许您回答对大型数据集的查询。
    • 树索引:对于总结文档集合很有用。
    • 列表索引:对于合成一个结合了多个数据源信息的答案很有用。
    • 关键字表索引:用于将查询路由到不同的数据源。
    • 结构化存储索引:对于结构化数据(例如 SQL 查询)很有用。
    • 知识图谱索引:对于构建知识图谱很有用。

LlamaIndex 有用性的核心是其有助于构建 LLM 应用程序的功能和工具。在这里,我们详细讨论它们:

  • 数据连接器

LlamaIndex 提供数据连接器,可以提取您现有的数据源和格式。无论是 API、PDF、文档还是 SQL 数据库,LlamaIndex 都可以与它们无缝集成,为您的 LLM 准备数据。

  • 数据结构

使用 LLM 的主要挑战之一是以易于使用的方式构建数据。LlamaIndex 提供了在索引或图表中构建数据的工具。

  • 高级检索/查询界面

LlamaIndex 不仅仅是摄取和构建数据。它还为您的数据提供高级检索或查询界面。只需输入任何 LLM 输入提示,LlamaIndex 将返回检索到的上下文和知识增强输出。

  • 与其他框架集成

LlamaIndex 允许与您的外部应用程序框架轻松集成。您可以将它与 LangChain、Flask、Docker、ChatGPT 以及您的项目可能需要的任何其他工具一起使用。

  • 高级和低级 API

无论您的熟练程度如何,LlamaIndex 都能满足您的需求。初学者用户会喜欢高级 API,它允许使用 LlamaIndex 以仅五行代码来摄取和查询他们的数据。另一方面,高级用户可以根据需要利用较低级别的 API 自定义和扩展任何模块(数据连接器、索引、检索器、查询引擎、重新排名模块)。

2.2 快速使用

  • 使用 pip 安装 LlamaIndex 非常简单:
pip install llama-index
  • 如何构建向量存储索引并查询它的简单示例:
import os
os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY' from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader('data').load_data()
index = GPTVectorStoreIndex.from_documents(documents) #o query:
query_engine = index.as_query_engine()
query_engine.query("<question_text>?") #By default, data is stored in-memory. To persist to disk (under ./storage):
index.storage_context.persist() #To reload from disk:
from llama_index import StorageContext, load_index _from_storage
#rebuild storage context
storage_context = StorageContext.from_defaults(persist_dir='./storage')
#load index
index = load_index_from_storage(storage_context)

LlamaIndex 不仅仅是一个数据框架;它是更大的工具和资源生态系统的一部分:

LlamaHub:数据加载器的社区库。

LlamaLab:使用 LlamaIndex 的尖端 AGI 项目平台。

3.GTE 文本向量

3.1 简介

文本表示是自然语言处理 (NLP) 领域的核心问题, 其在很多 NLP、信息检索的下游任务中发挥着非常重要的作用。近几年, 随着深度学习的发展,尤其是预训练语言模型的出现极大的推动了文本表示技术的效果, 基于预训练语言模型的文本表示模型在学术研究数据、工业实际应用中都明显优于传统的基于统计模型或者浅层神经网络的文本表示模型。这里, 我们主要关注基于预训练语言模型的文本表示。

文本表示示例, 输入一个句子, 输入一个固定维度的连续向量:

  • 输入: 吃完海鲜可以喝牛奶吗?
  • 输出: [0.27162,-0.66159,0.33031,0.24121,0.46122,...]

文本的向量表示通常可以用于文本聚类、文本相似度计算、文本向量召回等下游任务中。

基于监督数据训练的文本表示模型通常采用 Dual Encoder 框架, 如下图所示。在 Dual Encoder 框架中, Query 和 Document 文本通过预训练语言模型编码后, 通常采用预训练语言模型 [CLS] 位置的向量作为最终的文本向量表示。基于标注数据的标签, 通过计算 query-document 之间的 cosine 距离度量两者之间的相关性。

  • GTE-zh 模型使用 retromae 初始化训练模型,之后利用两阶段训练方法训练模型:

    • 第一阶段利用大规模弱弱监督文本对数据训练模型,
    • 第二阶段利用高质量精标文本对数据以及挖掘的难负样本数据训练模型。

具体训练方法请参考论文 Towards General Text Embeddings with Multi-stage Contrastive Learning

  • 使用方式:直接推理,对给定文本计算其对应的文本向量表示,向量维度 768

  • 使用范围:本模型可以使用在通用领域的文本向量表示及其下游应用场景, 包括双句文本相似度计算、query & 多 doc 候选的相似度排序

在 ModelScope 框架上,提供输入文本 (默认最长文本长度为 128),即可以通过简单的 Pipeline 调用来使用 GTE 文本向量表示模型。ModelScope 封装了统一的接口对外提供单句向量表示、双句文本相似度、多候选相似度计算功能

3.2 代码示例

from modelscope.models import Model
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks model_id = "iic/nlp_gte_sentence-embedding_chinese-base"
pipeline_se = pipeline(Tasks.sentence_embedding,
model=model_id,
sequence_length=512
) # sequence_length 代表最大文本长度,默认值为128 #当输入包含“soure_sentence”与“sentences_to_compare”时,会输出source_sentence中首个句子与sentences_to_compare中每个句子的向量表示,以及source_sentence中首个句子与sentences_to_compare中每个句子的相似度。
inputs = {
"source_sentence": ["吃完海鲜可以喝牛奶吗?"],
"sentences_to_compare": [
"不可以,早晨喝牛奶不科学",
"吃了海鲜后是不能再喝牛奶的,因为牛奶中含得有维生素C,如果海鲜喝牛奶一起服用会对人体造成一定的伤害",
"吃海鲜是不能同时喝牛奶吃水果,这个至少间隔6小时以上才可以。",
"吃海鲜是不可以吃柠檬的因为其中的维生素C会和海鲜中的矿物质形成砷"
]
} result = pipeline_se(input=inputs)
print (result)
'''
{'text_embedding': array([[ 1.6415151e-04, 2.2334497e-02, -2.4202393e-02, ...,
2.7710509e-02, 2.5980933e-02, -3.1285528e-02],
[-9.9107623e-03, 1.3627578e-03, -2.1072682e-02, ...,
2.6786461e-02, 3.5029035e-03, -1.5877936e-02],
[ 1.9877627e-03, 2.2191243e-02, -2.7656069e-02, ...,
2.2540951e-02, 2.1780970e-02, -3.0861111e-02],
[ 3.8688166e-05, 1.3409532e-02, -2.9691193e-02, ...,
2.9900728e-02, 2.1570563e-02, -2.0719109e-02],
[ 1.4484422e-03, 8.5943500e-03, -1.6661938e-02, ...,
2.0832840e-02, 2.3828523e-02, -1.1581291e-02]], dtype=float32), 'scores': [0.8859604597091675, 0.9830712080001831, 0.966042160987854, 0.891857922077179]}
''' #当输入仅含有soure_sentence时,会输出source_sentence中每个句子的向量表示。
inputs2 = {
"source_sentence": [
"不可以,早晨喝牛奶不科学",
"吃了海鲜后是不能再喝牛奶的,因为牛奶中含得有维生素C,如果海鲜喝牛奶一起服用会对人体造成一定的伤害",
"吃海鲜是不能同时喝牛奶吃水果,这个至少间隔6小时以上才可以。",
"吃海鲜是不可以吃柠檬的因为其中的维生素C会和海鲜中的矿物质形成砷"
]
}
result = pipeline_se(input=inputs2)
print (result)
'''
{'text_embedding': array([[-9.9107623e-03, 1.3627578e-03, -2.1072682e-02, ...,
2.6786461e-02, 3.5029035e-03, -1.5877936e-02],
[ 1.9877627e-03, 2.2191243e-02, -2.7656069e-02, ...,
2.2540951e-02, 2.1780970e-02, -3.0861111e-02],
[ 3.8688166e-05, 1.3409532e-02, -2.9691193e-02, ...,
2.9900728e-02, 2.1570563e-02, -2.0719109e-02],
[ 1.4484422e-03, 8.5943500e-03, -1.6661938e-02, ...,
2.0832840e-02, 2.3828523e-02, -1.1581291e-02]], dtype=float32), 'scores': []}
'''

默认向量维度 768, scores 中的 score 计算两个向量之间的内成积距离得到

  • 训练示例代码
#需在GPU环境运行
#加载数据集过程可能由于网络原因失败,请尝试重新运行代码
from modelscope.metainfo import Trainers
from modelscope.msdatasets import MsDataset
from modelscope.trainers import build_trainer
import tempfile
import os tmp_dir = tempfile.TemporaryDirectory().name
if not os.path.exists(tmp_dir):
os.makedirs(tmp_dir) #load dataset
ds = MsDataset.load('dureader-retrieval-ranking', 'zyznull')
train_ds = ds['train'].to_hf_dataset()
dev_ds = ds['dev'].to_hf_dataset()
model_id = 'iic/nlp_gte_sentence-embedding_chinese-base'
def cfg_modify_fn(cfg):
cfg.task = 'sentence-embedding'
cfg['preprocessor'] = {'type': 'sentence-embedding','max_length': 256}
cfg['dataset'] = {
'train': {
'type': 'bert',
'query_sequence': 'query',
'pos_sequence': 'positive_passages',
'neg_sequence': 'negative_passages',
'text_fileds': ['text'],
'qid_field': 'query_id'
},
'val': {
'type': 'bert',
'query_sequence': 'query',
'pos_sequence': 'positive_passages',
'neg_sequence': 'negative_passages',
'text_fileds': ['text'],
'qid_field': 'query_id'
},
}
cfg['train']['neg_samples'] = 4
cfg['evaluation']['dataloader']['batch_size_per_gpu'] = 30
cfg.train.max_epochs = 1
cfg.train.train_batch_size = 4
return cfg
kwargs = dict(
model=model_id,
train_dataset=train_ds,
work_dir=tmp_dir,
eval_dataset=dev_ds,
cfg_modify_fn=cfg_modify_fn)
trainer = build_trainer(name=Trainers.nlp_sentence_embedding_trainer, default_args=kwargs)
trainer.train()

3.3 模型效果评估

  • 中文多任务向量评测榜单C-MTEB结果如下:
Model Size Method #Nodes #GPUs per node Sequence Length
256 512 1024 2048 4096
1.8B LoRA 1 1 6.7G / 1.0s/it
LoRA (emb) 1 1 13.7G / 1.0s/it 14.0G / 1.0s/it
Q-LoRA 1 1 5.8G / 1.4s/it 6.0G / 1.4s/it
Full-parameter 1 1 43.5G / 2.1s/it 43.5G / 2.2s/it
7B LoRA 1 1 20.1G / 1.2s/it
LoRA (emb) 1 1 33.7G / 1.4s/it 34.1G / 1.6s/it
Q-LoRA 1 1 11.5G / 3.0s/it 11.5G / 3.0s/it
Full-parameter 1 2 139.2G / 4.0s/it 148.0G / 4.0s/it
LoRA (multinode) 2 2 74.7G / 2.09s/it 77.6G / 3.16s/it
14B LoRA 1 1 34.6G / 1.6s/it
LoRA (emb) 1 1 51.2 / 1.7s/it 51.1G / 2.6s/it
Q-LoRA 1 1 18.7G / 5.3s/it 18.4G / 6.3s/it
72B LoRA + Deepspeed Zero3 1 4 215.4G / 17.6s/it
Q-LoRA 1 1 61.4G / 27.4s/it 61.4G / 31.5s/it
  • 英文多任务向量评测榜单MTEB结果如下:
Model Name Model Size (GB) Dimension Sequence Length Average (56) Clustering (11) Pair Classification (3) Reranking (4) Retrieval (15) STS (10) Summarization (1) Classification (12)
gte-large 0.67 1024 512 63.13 46.84 85.00 59.13 52.22 83.35 31.66 73.33
gte-base 0.22 768 512 62.39 46.2 84.57 58.61 51.14 82.3 31.17 73.01
e5-large-v2 1.34 1024 512 62.25 44.49 86.03 56.61 50.56 82.05 30.19 75.24
e5-base-v2 0.44 768 512 61.5 43.80 85.73 55.91 50.29 81.05 30.28 73.84
gte-small 0.07 384 512 61.36 44.89 83.54 57.7 49.46 82.07 30.42 72.31
text-embedding-ada-002 - 1536 8192 60.99 45.9 84.89 56.32 49.25 80.97 30.8 70.93
e5-small-v2 0.13 384 512 59.93 39.92 84.67 54.32 49.04 80.39 31.16 72.94
sentence-t5-xxl 9.73 768 512 59.51 43.72 85.06 56.42 42.24 82.63 30.08 73.42
all-mpnet-base-v2 0.44 768 514 57.78 43.69 83.04 59.36 43.81 80.28 27.49 65.07
sgpt-bloom-7b1-msmarco 28.27 4096 2048 57.59 38.93 81.9 55.65 48.22 77.74 33.6 66.19
all-MiniLM-L12-v2 0.13 384 512 56.53 41.81 82.41 58.44 42.69 79.8 27.9 63.21
all-MiniLM-L6-v2 0.09 384 512 56.26 42.35 82.37 58.04 41.95 78.9 30.81 63.05
contriever-base-msmarco 0.44 768 512 56.00 41.1 82.54 53.14 41.88 76.51 30.36 66.68
sentence-t5-base 0.22 768 512 55.27 40.21 85.18 53.09 33.63 81.14 31.39 69.81

4.魔搭社区最佳实践

4.1 逐步解析

  • 环境配置与安装

    1. python 3.10 及以上版本
    2. pytorch 1.12 及以上版本,推荐 2.0 及以上版本
    3. 建议使用 CUDA 11.4 及以上本文主要演示的模型推理代码可在魔搭社区免费实例 PAI-DSW 的配置下运行(显存 24G) :
  • 第一步:点击模型右侧 Notebook 快速开发按钮,选择 GPU 环境

  • 第二步:新建 Notebook

安装依赖库1

!pip install llama-index llama-index-llms-huggingface ipywidgets
!pip install transformers -U
import logging
import sys logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout)) from IPython.display import Markdown, display
import torch
from llama_index.llms.huggingface import HuggingFaceLLM
from llama_index.core.prompts import PromptTemplate
from modelscope import snapshot_download
from llama_index.core.base.embeddings.base import BaseEmbedding, Embedding
from abc import ABC
from typing import Any, List, Optional, Dict, cast
from llama_index.core import (
VectorStoreIndex,
ServiceContext,
set_global_service_context,
SimpleDirectoryReader,
)
  • 加载大语言模型

因为 Qwen 本次支持了 Transformers,使用 HuggingFaceLLM 加载模型,模型为(Qwen1.5-4B-Chat)1

#Model names
qwen2_4B_CHAT = "qwen/Qwen1.5-4B-Chat" selected_model = snapshot_download(qwen2_4B_CHAT) SYSTEM_PROMPT = """You are a helpful AI assistant.
""" query_wrapper_prompt = PromptTemplate(
"[INST]<<SYS>>\n" + SYSTEM_PROMPT + "<</SYS>>\n\n{query_str}[/INST] "
) llm = HuggingFaceLLM(
context_window=4096,
max_new_tokens=2048,
generate_kwargs={"temperature": 0.0, "do_sample": False},
query_wrapper_prompt=query_wrapper_prompt,
tokenizer_name=selected_model,
model_name=selected_model,
device_map="auto",
# change these settings below depending on your GPU
model_kwargs={"torch_dtype": torch.float16},
)
  • 加载数据:导入测试数据
!mkdir -p 'data/xianjiaoda/'
!wget 'https://modelscope.oss-cn-beijing.aliyuncs.com/resource/rag/xianjiaoda.md' -O 'data/xianjiaoda/xianjiaoda.md'
documents = SimpleDirectoryReader("/mnt/workspace/data/xianjiaoda/").load_data()
documents
  • 构建 Embedding 类

加载 GTE 模型,使用 GTE 模型构造 Embedding 类

embedding_model = "iic/nlp_gte_sentence-embedding_chinese-base"
class ModelScopeEmbeddings4LlamaIndex(BaseEmbedding, ABC):
embed: Any = None
model_id: str = "iic/nlp_gte_sentence-embedding_chinese-base" def __init__(
self,
model_id: str,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
try:
from modelscope.models import Model
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
# 使用modelscope的embedding模型(包含下载)
self.embed = pipeline(Tasks.sentence_embedding, model=self.model_id) except ImportError as e:
raise ValueError(
"Could not import some python packages." "Please install it with `pip install modelscope`."
) from e def _get_query_embedding(self, query: str) -> List[float]:
text = query.replace("\n", " ")
inputs = {"source_sentence": [text]}
return self.embed(input=inputs)['text_embedding'][0].tolist() def _get_text_embedding(self, text: str) -> List[float]:
text = text.replace("\n", " ")
inputs = {"source_sentence": [text]}
return self.embed(input=inputs)['text_embedding'][0].tolist() def _get_text_embeddings(self, texts: List[str]) -> List[List[float]]:
texts = list(map(lambda x: x.replace("\n", " "), texts))
inputs = {"source_sentence": texts}
return self.embed(input=inputs)['text_embedding'].tolist() async def _aget_query_embedding(self, query: str) -> List[float]:
return self._get_query_embedding(query)
  • 建设索引

加载数据后,基于文档对象列表(或节点列表),建设他们的 index,就可以方便的检索他们。1

embeddings = ModelScopeEmbeddings4LlamaIndex(model_id=embedding_model)
service_context = ServiceContext.from_defaults(embed_model=embeddings, llm=llm)
set_global_service_context(service_context) index = VectorStoreIndex.from_documents(documents)
  • 查询和问答

搭建基于本地知识库的问答引擎1


query_engine = index.as_query_engine()
response = query_engine.query("西安交大是由哪几个学校合并的?")
print(response)

4.2 完整代码

!pip install pypdf langchain unstructured transformers_stream_generator
!pip install modelscope nltk pydantic tiktoken llama-index
!wget https://modelscope.oss-cn-beijing.aliyuncs.com/resource/rag/averaged_perceptron_tagger.zip
!wget https://modelscope.oss-cn-beijing.aliyuncs.com/resource/rag/punkt.zip
!wget https://modelscope.oss-cn-beijing.aliyuncs.com/resource/rag/xianjiaoda.md !mkdir -p /root/nltk_data/tokenizers
!mkdir -p /root/nltk_data/taggers
!cp /mnt/workspace/punkt.zip /root/nltk_data/tokenizers
!cp /mnt/workspace/averaged_perceptron_tagger.zip /root/nltk_data/taggers
!cd /root/nltk_data/tokenizers; unzip punkt.zip;
!cd /root/nltk_data/taggers; unzip averaged_perceptron_tagger.zip; !mkdir -p /mnt/workspace/custom_data
!mv /mnt/workspace/xianjiaoda.md /mnt/workspace/custom_data !cd /mnt/workspace
import os
from abc import ABC
from typing import Any, List, Optional, Dict, cast import torch
from langchain_core.language_models.llms import LLM
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from modelscope import AutoModelForCausalLM, AutoTokenizer
from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader
from llama_index import ServiceContext
from llama_index.embeddings.base import BaseEmbedding
from llama_index import set_global_service_context
from langchain_core.retrievers import BaseRetriever
from langchain_core.callbacks import CallbackManagerForRetrieverRun
from langchain_core.documents import Document
from llama_index.retrievers import VectorIndexRetriever # configs for LLM
llm_name = "Qwen/Qwen-1_8B-Chat"
llm_revision = "master" # configs for embedding model
embedding_model = "damo/nlp_gte_sentence-embedding_chinese-small" # file path for your custom knowledge base
knowledge_doc_file_dir = "/mnt/workspace/custom_data/"
knowledge_doc_file_path = knowledge_doc_file_dir + "xianjiaoda.md" # define our Embedding class to use models in Modelscope
class ModelScopeEmbeddings4LlamaIndex(BaseEmbedding, ABC):
embed: Any = None
model_id: str = "damo/nlp_gte_sentence-embedding_chinese-small" def __init__(
self,
model_id: str,
**kwargs: Any,
) -> None:
super().__init__(**kwargs)
try:
from modelscope.models import Model
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
self.embed = pipeline(Tasks.sentence_embedding, model=self.model_id) except ImportError as e:
raise ValueError(
"Could not import some python packages." "Please install it with `pip install modelscope`."
) from e def _get_query_embedding(self, query: str) -> List[float]:
text = query.replace("\n", " ")
inputs = {"source_sentence": [text]}
return self.embed(input=inputs)['text_embedding'][0] def _get_text_embedding(self, text: str) -> List[float]:
text = text.replace("\n", " ")
inputs = {"source_sentence": [text]}
return self.embed(input=inputs)['text_embedding'][0] def _get_text_embeddings(self, texts: List[str]) -> List[List[float]]:
texts = list(map(lambda x: x.replace("\n", " "), texts))
inputs = {"source_sentence": texts}
return self.embed(input=inputs)['text_embedding'] async def _aget_query_embedding(self, query: str) -> List[float]:
return self._get_query_embedding(query) # define our Retriever with llama-index to co-operate with Langchain
# note that the 'LlamaIndexRetriever' defined in langchain-community.retrievers.llama_index.py
# is no longer compatible with llamaIndex code right now.
class LlamaIndexRetriever(BaseRetriever):
index: Any
"""LlamaIndex index to query.""" def _get_relevant_documents(
self, query: str, *, run_manager: CallbackManagerForRetrieverRun
) -> List[Document]:
"""Get documents relevant for a query."""
try:
from llama_index.indices.base import BaseIndex
from llama_index.response.schema import Response
except ImportError:
raise ImportError(
"You need to install `pip install llama-index` to use this retriever."
)
index = cast(BaseIndex, self.index)
print('@@@ query=', query) response = index.as_query_engine().query(query)
response = cast(Response, response)
# parse source nodes
docs = []
for source_node in response.source_nodes:
print('@@@@ source=', source_node)
metadata = source_node.metadata or {}
docs.append(
Document(page_content=source_node.get_text(), metadata=metadata)
)
return docs def torch_gc():
os.environ["TOKENIZERS_PARALLELISM"] = "false"
DEVICE = "cuda"
DEVICE_ID = "0"
CUDA_DEVICE = f"{DEVICE}:{DEVICE_ID}" if DEVICE_ID else DEVICE
a = torch.Tensor([1, 2])
a = a.cuda()
print(a) if torch.cuda.is_available():
with torch.cuda.device(CUDA_DEVICE):
torch.cuda.empty_cache()
torch.cuda.ipc_collect() # global resources used by QianWenChatLLM (this is not a good practice)
tokenizer = AutoTokenizer.from_pretrained(llm_name, revision=llm_revision, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(llm_name, revision=llm_revision, device_map="auto",
trust_remote_code=True, fp16=True).eval() # define QianWen LLM based on langchain's LLM to use models in Modelscope
class QianWenChatLLM(LLM):
max_length = 10000
temperature: float = 0.01
top_p = 0.9 def __init__(self):
super().__init__() @property
def _llm_type(self):
return "ChatLLM" def _call(
self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager=None,
**kwargs: Any,
) -> str:
print(prompt)
response, history = model.chat(tokenizer, prompt, history=None)
torch_gc()
return response # STEP1: create LLM instance
qwllm = QianWenChatLLM()
print('STEP1: qianwen LLM created') # STEP2: load knowledge file and initialize vector db by llamaIndex
print('STEP2: reading docs ...')
embeddings = ModelScopeEmbeddings4LlamaIndex(model_id=embedding_model)
service_context = ServiceContext.from_defaults(embed_model=embeddings, llm=None)
set_global_service_context(service_context) # global config, not good llamaIndex_docs = SimpleDirectoryReader(knowledge_doc_file_dir).load_data()
llamaIndex_index = GPTVectorStoreIndex.from_documents(llamaIndex_docs, chunk_size=512)
retriever = LlamaIndexRetriever(index=llamaIndex_index)
print(' 2.2 reading doc done, vec db created.') # STEP3: create chat template
prompt_template = """请基于内的内容回答问题。"{context}
我的问题是:{question}。
"""
prompt = ChatPromptTemplate.from_template(template=prompt_template)
print('STEP3: chat prompt template created.') # STEP4: create RAG chain to do QA
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| qwllm
| StrOutputParser()
)
chain.invoke('西安交大的校训是什么?')
# chain.invoke('魔搭社区有哪些模型?')
# chain.invoke('modelscope是什么?')
# chain.invoke('萧峰和乔峰是什么关系?')

更多优质内容请关注公号:汀丶人工智能;会提供一些相关的资源和优质文章,免费获取阅读。

检索增强生成(RAG)实践:基于LlamaIndex和Qwen1.5搭建智能问答系统的更多相关文章

  1. 《在纹线方向上进行平滑滤波,在纹线的垂直方向上进行锐化滤波》 --Gabor增强的具体实践

    <在纹线方向上进行平滑滤波,在纹线的垂直方向上进行锐化滤波>                                          --Gabor增强的具体实践     一.问 ...

  2. 以小25倍参数量媲美GPT-3的检索增强自回归语言模型:RETRO

    NLP论文解读 原创•作者 | 吴雪梦Shinemon 研究方向 | 计算机视觉 导读说明: 一个具有良好性能的语言模型,一定量的数据样本必不可少.现有的各种语言模型中,例如GPT3具有1750亿的参 ...

  3. XML文件生成C++代码(基于rapidxml)

    简述 与XML文件生成C++代码(基于pugixml)中的功能一致,只是这里改用的rapidxml来实现.就不多说了,直接放代码. 代码 #include "rapidxml-1.13/ra ...

  4. 基于STM32+华为云IOT设计智能称重系统

    摘要:选择部署多个重量传感器和必要的算法.通过WiFi 通信模块.GPS定位模块,采集车辆称重数据一地理位置信息,并通过网络发送至云平台,设计图形化UI界面展示称重.地图位置等重要信息,实现对称重系统 ...

  5. 基于springboot+bootstrap+mysql+redis搭建一套完整的权限架构【六】【引入bootstrap前端框架】

    https://blog.csdn.net/linzhefeng89/article/details/78752658 基于springboot+bootstrap+mysql+redis搭建一套完整 ...

  6. Ubuntu 基于Docker的TensorFlow 环境搭建

    基于Docker的TensorFlow 环境搭建 基于(ubuntu 16.04LTS/ubuntu 14.04LTS) 一.docker环境安装 1)更新.安装依赖包 sudo apt-get up ...

  7. vue进阶:基于vue-cli创建项目(搭建手脚架)

    vue-cli安装.创建项目 基于vue-cli创建的项目进行开发 使用vue-cli图形化界面搭建项目 插件与工具 一.vue-cli简介.安装.创建项目 Vue-cli是基于Vue.js进行快速开 ...

  8. 从零搭建基于webpack的Electron-Vue3项目(1)——基于webpack的Vue3项目搭建

    从零搭建基于webpack的Electron-Vue3项目(1)--基于webpack的Vue3项目搭建 前言 本篇文章内容,主要是基于webpack的Vue3项目开发环境进行搭建,暂时还不涉及到El ...

  9. 基于Labelstudio的UIE半监督智能标注方案(本地版)

    基于Labelstudio的UIE半监督智能标注方案(本地版) 更多技术细节参考上一篇项目,本篇主要侧重本地端链路走通教学,提速提效: 基于Labelstudio的UIE半监督深度学习的智能标注方案( ...

  10. 基于认证的代理平台搭建配置squid-20130730

    基于认证的代理平台搭建配置squid-20130730 功能:通过squid代理实现 (1)基于用户名密码认证的出口ip路由选择 (2)基于client源ip的出口ip路由选择 (3)基于连接本机ip ...

随机推荐

  1. KingbaseES V8R6集群运维系列 -- 修改ssh通信为 sys_securecmdd 通信

    一.适用于: 本文档使用于KingbaseES V008R006版本. 二.关于SYS_SECURECMDD: sys_securecmdd是KingbaseES集群自带的工具,集群监控.管理集群时通 ...

  2. yml和properties打印SQL日志信息

    1.配置文件里面配置 第一种是properties类型如下 logging.level.com.datayes.mdi.dao.rdb.mommp.**=debug其中 com.datayes.mdi ...

  3. 01 jQuery初使用

    01 jQuery初使用 jQuery是一个曾经火遍大江南北的一个Javascript的第三方库. jQuery的理念: write less do more. 其含义就是让前端程序员从繁琐的js代码 ...

  4. OpenHarmony将携新成果亮相HDC2022

     第四届华为开发者大会 2022(Together)将于11月4日-6日在东莞召开,OpenAtom OpenHarmony(以下简称"OpenHarmony")将携新生态成果亮相 ...

  5. 本周四晚19:00知识赋能第3期直播丨OpenHarmony智能家居项目之控制面板功能实现

    OpenAtom OpenHarmony(以下简称"OpenHarmony")开源开发者成长计划项目自 2021 年 10 月 24 日上线以来,在开发者中引发高度关注. 成长计划 ...

  6. Ansible 学习笔记 - 批量巡检站点 URL 状态

    前言 不拖泥带水,不东拉西扯. 速战速决,五分钟学到一个工作用得上的技巧. 通过一个个具体的实战案例,来生动演示 Ansible 的用法. 需求 我需要定期巡检或定时监控我公司的所有站点的首页的可用性 ...

  7. HTML基础之input系列

    <form action=""> <div> 用户名:<input type="text" name="user&quo ...

  8. sql 语句系列(计算的进阶)[八百章之第十六章]

    前言 介绍两个实用的sql查询语句. 1.计算平均数时候,去除最大值和最小值. 2.修改累计值. 计算平均数时候,去除最大值和最小值 sql server: select AVG(sal) from( ...

  9. Java进行excel的导入导出操作

    excel表格的导出导入在业务中经常会遇到,下面介绍hutool和easyExcel两种操作excel的工具 测试的实体类 通过mybatis-plus生成的,用于导出数据的实体类 @Getter @ ...

  10. background-blend-mode

    由于 mix-blend-mode 这个属性的强大,很多应用场景和动效的制作不断完善和被发掘出来,遂另起一文继续介绍一些使用 mix-blend-mode 制作的酷炫动画. CSS3 新增了一个很有意 ...