大模型应用:如何使用Langchain+Qwen部署一套Rag检索系统

一、TL;DR 

  1. 从0-1使用qwen chat model+ langchain的链式架构搭建一套rag系统
  2. 详细介绍了Langchain的工具链的调用流程
  3. 简单介绍了可能会出现什么问题

二、方法

参考开源链接:https://github.com/Aphasia0515/self_llm/

2.1 硬件和软件依赖

 类型需求备注
硬件      
  1. 显存 >= 24B

实测:

  1. Qwen7B >= 24B
  2. Qwen32B >= 60B 左右
  3. Qwen1B 大概在6B左右(记忆里)
软件
  1. ubuntu20.04
  2. cuda11.8
  3. py3.8
  1. 其实现在大多数的社区镜像基本满足要求,因为Qwen的镜像都是比较新的依赖
  2. 但是训练镜像尤其要注意,我之前就遇到了Qwen和InternVL的训练镜像有冲突的问题

2.2 模型下载和准备

注意:模型下载记得从mirror-huggingface上下载,国内打不开huggingface

模型作用 备注
QwenRag系统的生成器、chat模型
  1. 注意根据自己的显存选择合适的模型
  2. 注意:1B/4B/7B的差异还是比较明显的(从天梯图里可以看到)
 Sentence Transformer 对文本进行向量化无

2.3 Rag的文本库准备

这一节就不仔细讲了,将所有你需要的参考文件知识库放在某个指定目录下,本文只做txt和markdown的文档拆分(用于后续的建库)

三、知识库提取和向量化

3.1 得到所有文档的纯本文内容

先遍历2.3节的指定目录,得到所有的markdown和txt文件,并存成list:

import os 
def get_files(dir_path):# args:dir_path,目标文件夹路径file_list = []for filepath, dirnames, filenames in os.walk(dir_path):# os.walk 函数将递归遍历指定文件夹for filename in filenames:# 通过后缀名判断文件类型是否满足要求if filename.endswith(".md"):# 如果满足要求,将其绝对路径加入到结果列表file_list.append(os.path.join(filepath, filename))elif filename.endswith(".txt"):file_list.append(os.path.join(filepath, filename))return file_list

接下来对list里面的内容进行读取和加载,此处使用LangChain提供的FileLoader进行加载:

  1. 注意:此处得到的其实还是纯文本loader,而非真正的文本块
from tqdm import tqdm
from langchain.document_loaders import UnstructuredFileLoader
from langchain.document_loaders import UnstructuredMarkdownLoaderdef get_text(dir_path):# args:dir_path,目标文件夹路径# 首先调用上文定义的函数得到目标文件路径列表file_lst = get_files(dir_path)# docs 存放加载之后的纯文本对象docs = []# 遍历所有目标文件for one_file in tqdm(file_lst):file_type = one_file.split('.')[-1]if file_type == 'md':loader = UnstructuredMarkdownLoader(one_file)elif file_type == 'txt':loader = UnstructuredFileLoader(one_file)else:# 如果是不符合条件的文件,直接跳过continuedocs.extend(loader.load())return docs

3.2 对所有的文本进行分块并向量化

LangChain使用多种文本分块工具,示例代码使用的时字符串递归分割器,并选择分块大小时500,块重叠长度是150,分块代码如下所示:

注意:我上一篇博客所说的,分块的大小不同的模型有不同的选择,只有最合适的,没有固定的

from langchain.text_splitter import RecursiveCharacterTextSplittertext_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=150)
split_docs = text_splitter.split_documents(docs)

分块完后,使用2.2节的下载好的模型(sentence Transformer)此时进一步进行向量化,LangChain 提供了直接引入 HuggingFace 开源社区中的模型进行向量化的接口::

注意:这个是直接加载本地路径

from langchain.embeddings.huggingface import HuggingFaceEmbeddingsembeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model")

加载玩模型后,使用 Chroma 作为向量数据库,基于上文分块后的文档以及加载的开源向量化模型,将语料加载到指定路径下的向量数据库:

from langchain.vectorstores import Chroma# 定义持久化路径
persist_directory = 'data_base/vector_db/chroma'
# 加载数据库
vectordb = Chroma.from_documents(documents=split_docs,embedding=embeddings,persist_directory=persist_directory  # 允许我们将persist_directory目录保存到磁盘上
)
# 将加载的向量数据库持久化到磁盘上
vectordb.persist()

运行上述脚本,就得到一个本地构建已持久化的向量数据库,后续直接导入该数据库即可,无需重复构建。

四、接入LLM-Chat Model

注意:本步骤接入任何llm模型其实都是可以的,主要还是看这个检索框架的整体实现。

4.1 QwenLM接入LangChain

本地部署Qwen llm模型,然后将QwenLM接入到Langchain框架里面,完成自定义 LLM 类之后,可以以完全一致的方式调用 LangChain 的接口,而无需考虑底层模型调用的不一致。

  1. 从 LangChain.llms.base.LLM 类继承一个子类,并重写构造函数与 _call 函数
from langchain.llms.base import LLM
from typing import Any, List, Optional
from langchain.callbacks.manager import CallbackManagerForLLMRun
from transformers import AutoTokenizer, AutoModelForCausalLM, GenerationConfigclass QwenLM(LLM):# 基于本地 Qwen 自定义 LLM 类tokenizer : AutoTokenizer = Nonemodel: AutoModelForCausalLM = Nonedef __init__(self, model_path :str):# model_path: Qwen 模型路径# 从本地初始化模型super().__init__()print("正在从本地加载模型...")model_dir = '/root/autodl-tmp/qwen/Qwen-7B-Chat'self.tokenizer = AutoTokenizer.from_pretrained(model_dir, trust_remote_code=True)self.model = AutoModelForCausalLM.from_pretrained(model_dir, device_map="auto", trust_remote_code=True).eval()# Specify hyperparameters for generationself.model.generation_config = GenerationConfig.from_pretrained(model_dir, trust_remote_code=True) # 可指定不同的生成长度、top_p等相关超参print("完成本地模型的加载")def _call(self, prompt : str, stop: Optional[List[str]] = None,run_manager: Optional[CallbackManagerForLLMRun] = None,**kwargs: Any):# 重写调用函数response, history = self.model.chat(self.tokenizer, prompt , history=[])return response@propertydef _llm_type(self) -> str:return "QwenLM"

上述代码在构造函数里直接加载了qwen模型,如果有其他的llm model也可以一并加载进去, _call 函数是 LLM 类的核心函数,LangChain 会调用该函数来调用 LLM来使用qwen的chat能力。

开源仓库里面将上述代码封装为 LLM.py,后续将直接从该文件中引入自定义的 LLM 类,直接使用from LLM import QwenLM。

五、构建检索问答链

LangChain 通过提供检索问答链对象来实现对于 RAG 全流程的封装:

  1. 我们可以调用一个 LangChain 提供的 RetrievalQA 对象,通过初始化时填入已构建的数据库和自定义 LLM 作为参数,来简便地完成检索增强问答的全流程,LangChain 会自动完成基于用户提问进行检索、获取相关文档、拼接为合适的 Prompt 并交给 LLM 问答的全部流程。

5.1 导入向量数据库

from langchain.vectorstores import Chroma
from langchain.embeddings.huggingface import HuggingFaceEmbeddings
import os# 定义 Embeddings
embeddings = HuggingFaceEmbeddings(model_name="/root/autodl-tmp/embedding_model")# 向量数据库持久化路径
persist_directory = 'data_base/vector_db/chroma'# 加载数据库
vectordb = Chroma(persist_directory=persist_directory, embedding_function=embeddings
)

注意:向量数据库里面存储的是分块的文档和对应的向量,是embedding和index的关系

5.2 实例化自定义的QwenLM对象

from LLM import QwenLM
llm = QwenLM(model_path = "/root/autodl-tmp/qwen")
llm.predict("你是谁")

5.3 构建Prompt Template

prompt Template的作用是通过将用户input、rag检索得到的知识片段组合成input:

  1. 是一个带变量的字符串
  2. 在检索之后,LangChain 会将检索到的相关文档片段填入到 Template 的变量中,从而实现带知识的 Prompt 构建。
from langchain.prompts import PromptTemplate# 我们所构造的 Prompt 模板
template = """使用以下上下文来回答最后的问题。如果你不知道答案,就说你不知道,不要试图编造答案。尽量使答案简明扼要。总是在回答的最后说“谢谢你的提问!”。
{context}
问题: {question}
有用的回答:"""# 调用 LangChain 的方法来实例化一个 Template 对象,该对象包含了 context 和 question 两个变量,在实际调用时,这两个变量会被检索到的文档片段和用户提问填充
QA_CHAIN_PROMPT = PromptTemplate(input_variables=["context","question"],template=template)

5.4 直接检索问答

最后,可以调用 LangChain 提供的检索问答链构造函数,基于我们的自定义 LLM、Prompt Template 和向量知识库来构建一个基于 Qwen 的检索问答链:

question = "什么是QwenLM"
result = qa_chain({"query": question})
print("检索问答链回答 question 的结果:")
print(result["result"])# 仅 LLM 回答效果
result_2 = llm(question)
print("大模型回答 question 的结果:")
print(result_2)

可以看到,使用检索问答链生成的答案更接近知识库里的内容。

六、部署WebDemo

略

七、可优化的点

上述用的是开源代码,如果实际工程中可以从哪里优化呢?

  1. 检索的文本是大文本或者超大文本(>2K文本):
    1. summary,对大文本进行 map-reduce summary
    2. k-means(BRV steps)
  2. 检索内容太多不准该怎么办?
  3. 等等 明天再写把

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/bicheng/85893.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

决策树:化繁为简的智能决策利器

本文来自「大千AI助手」技术实战系列,专注用真话讲技术,拒绝过度包装。 想象一个相亲决策过程: 对方收入 > 30万? → 是 → 见面否 → 颜值高? → 是 → 先聊聊否 → 放弃 这种层层递进的判断结构,正是…

html中的盒子标签div标签,有序列表,无序列表

div标签 div标签对于分析数据很重要&#xff0c;因为数据在页面中展示是以区域的形式展示的&#xff0c;而查找数据需要先找到盒子名称在继续向下找。前端页面布局中有两种布局方式&#xff0c;一种是通过表格布局&#xff0c;一种是通过divcss来布局。 <!DOCTYPE html>…

【Redis】解码Redis中hash类型:理解基础命令,以及内部编码方式和使用场景

&#x1f4da;️前言 &#x1f31f;&#x1f31f;&#x1f31f;精彩读导 本次我们将全面剖析Redis的核心技术要点&#xff0c;包括其丰富的数据类型体系、高效的编码方式以及秒级响应的性能奥秘。对于渴望深入理解Redis底层机制的技术爱好者&#xff0c;这是一次难得的学习机会…

AI工具在学术写作中的伦理边界与诚信规范的平衡

AI写作助手的兴起与争议 人工智能技术的飞速发展&#xff0c;学境思源&#xff0c;ChatGPT、Grok、Claude 等AI写作工具逐渐走入高校师生的视野。一键生成论文初稿&#xff01;从课程作业到毕业论文&#xff0c;不少学生已经尝试让AI参与写作过程&#xff0c;希望借此提升效率…

课程专注度分析系统项目

前端代码: <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>课堂专注度分析系统 - 科技…

区块链是什么

区块链的本质与机制 1. 核心定义 区块链 加密的分布式记账技术&#xff0c;融合密码学、网络学、金融学三大学科。 去中心化&#xff1a;数据存储于全网节点&#xff08;如百万台计算机&#xff09;&#xff0c;而非单一数据库。不可篡改&#xff1a;修改数据需控制全网51%以…

用可观测工具高效定位和查找设计中深度隐藏的bug

软件仿真拥有最佳的信号可见性和调试灵活性,被大多数工程师熟练使用,能够高效捕获很多显而易见的常见错误。 然而,由软件实现的数字仿真过程运行速度有限,很难做到100%代码覆盖。导致那些深度隐藏的设计问题,将不可避免的逃逸,只能以FPGA在线调试方式解决。 01 为什么全…

华为OD-2024年E卷-字符串化繁为简[200分] -- python

问题描述&#xff1a; 给定一个输入字符串&#xff0c;字符串只可能由英文字母(a~z、A~Z) 和左右小括号((、))组成。当字符串里存在小括号时&#xff0c;小括号是成对的&#xff0c;可以有一个或多个小括号对&#xff0c;小括号对不会嵌套&#xff0c;小括号对内可以包含1个或…

使用sealos安装k8s

一、准备工作&#xff08;所有节点需执行&#xff09;​ 1、系统要求 操作系统&#xff1a;本文为Ubuntu 20.0.4 配置&#xff1a;不同主机名、时间同步、SSH 免密互通、关闭防火墙/SELinux/swap。 资源&#xff1a;建议 ≥2核 CPU、2GB 内存&#xff08;生产环境需更高&am…

Pytorch 实战四 VGG 网络训练

系列文章目录 文章目录 系列文章目录前言一、源码1. 解决线程冲突2.代码框架 二、代码详细介绍1.基础定义2. epoch 的定义3. 每组图片的训练和模型保存 前言 前面我们已经完成了数据集的制作&#xff0c;VGG 网络的搭建&#xff0c;现在进行网络模型的训练。 一、源码 import t…

课程专注度分析系统文档

一、项目概述 本项目基于 Flask 框架开发&#xff0c;结合计算机视觉技术&#xff08;利用 YOLOv10 等模型 &#xff09;&#xff0c;实现对课堂视频的智能分析。可检测视频中学生手机使用情况、面部表情&#xff08;专注、分心等 &#xff09;&#xff0c;统计专注度、手机使…

中国设计 全球审美 | 安贝斯新产品发布会:以东方美学开辟控制台仿生智造新纪元

6月17日&#xff0c;安贝斯&#xff08;武汉&#xff09;控制技术有限公司&#xff08;以下简称“安贝斯”&#xff09;在武汉隆重举行“新产品发布暨协会联合创新峰会”。近百位来自政府机构、行业协会、行业用户及战略合作伙伴的嘉宾齐聚现场&#xff0c;共同见证以“中国设计…

在微信小程序wxml文件调用函数实现时间转换---使用wxs模块实现

1. 创建 WXS 模块文件&#xff08;推荐单独存放&#xff09; 在项目目录下新建 utils.wxs 文件&#xff0c;编写时间转换逻辑&#xff1a; // utils.wxs module.exports {// 将毫秒转换为分钟&#xff08;保留1位小数&#xff09;convertToMinutes: function(ms) {if (typeo…

ByteMD 插件系统详解

ByteMD 插件系统详解 ByteMD 的插件系统是其强大扩展性的核心。它允许开发者在 Markdown 解析、AST 转换、HTML 渲染、以及编辑器 UI 交互的各个阶段注入自定义逻辑。这得益于 ByteMD 深度集成了 unified 处理器和其丰富的生态系统&#xff08;remark 用于 Markdown&#xff0c…

每日一练之 Lua 表

Lua 的 table 是什么数据结构&#xff1f;如何创建和访问&#xff1f; 数据结构:Lua的table是一种哈希表&#xff0c;使用键值对存储数据&#xff0c;支持动态扩容 创建方式: local t1 {} local t2 {10,20,30} local t3 {name"Alice",age25}访问方式&#xff1a…

实现自动胡批量抓取唯品会商品详情数据的途径分享(官方API、网页爬虫)

在电商领域&#xff0c;数据就是企业的核心资产。无论是市场分析、竞品研究&#xff0c;还是精准营销&#xff0c;都离不开对大量商品详情数据的深入挖掘。唯品会作为知名的电商平台&#xff0c;其丰富的商品信息对于众多从业者而言极具价值。本文将详细探讨实现自动批量抓取唯…

Zephyr 高阶实践:彻底讲透 west 构建系统、模块管理与跨平台 CI/CD 配置

本文是 Zephyr 项目管理体系的高阶解构与实战指南&#xff0c;全面覆盖 west 构建系统原理、模块解耦与 west.yml 多模块维护机制&#xff0c;结合企业级多平台 CI/CD 落地流程&#xff0c;深入讲解如何构建可靠、可维护、跨芯片架构的一体化 Zephyr 工程。 一、为什么 Zephyr …

我开源了一套springboot3快速开发模板

我开源了一套springboot3快速开发模板 开箱即用、按需组合、可快速二次开发的后端通用模板。 ✨ 主要特性 Spring Boot 3.x Java 17&#xff1a;跟随 Spring 最新生态&#xff0c;利用现代语法特性。多模块分层&#xff1a;common 抽象通用能力、starter 负责启动、modules…

OpenCV CUDA模块设备层-----在GPU上计算两个uchar1类型像素值的反正切(arctangent)比值函数atan2()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 对输入的两个 uchar1 像素值 a 和 b&#xff0c;先分别归一化到 [0.0, 1.0] 浮点区间&#xff0c;然后计算它们的 四象限反正切函数。 函数原型…

从C++编程入手设计模式——观察者模式

从C编程入手设计模式——观察者模式 ​ 观察者模式简直就是字如其名&#xff0c;观察观察&#xff0c;观察到了告诉别人。观察手的作用如此&#xff0c;观察者模式的工作机制也是如此。这个模式的核心思路是&#xff1a;一个对象的状态发生变化时&#xff0c;自动通知依赖它的…