大模型技术实践(三)|用LangChain和Llama 2打造心灵疗愈机器人

news2024/11/14 20:50:43

上期文章我们实现了Llama 2-chat-7B模型的云端部署和推理,本期文章我们将用“LangChain+Llama 2”的架构打造一个定制化的心灵疗愈机器人。有相关知识背景的读者可以直接阅读「实战」部分。


2839f867ef83f2dea1707cc44903fff9.jpeg


01 背景


1.1 微调 vs. 知识库


由于大模型在垂直行业领域的问答效果仍有待提升,因此,领域知识的注入成为了最直接的解决方案之一。知识注入方法可以分为领域微调(Fine-tuning)和外挂知识库(Knowledge Base)两种。


1. 领域微调

微调是通过少量特定用例的增量数据对基础模型进行进一步训练,改变其神经网络中的参数权重。微调适用于任务或域定义明确,且有足够的标记数据的场景,比如风格微调。目前常用的微调方法包括Freeze,P-tuning和LoRA,相关细节会在下期文章中详细介绍。


然而,微调方法的不足之处在于:

▪ 高质量训练数据集的构建,微调训练所需的算力以及微调模型定期更新等开销都不容小觑

▪ 试错成本较高,特定领域数据一般难以覆盖模型已学到的参数,且可能会导致模型其他下游任务的表现下降


2. 外挂知识库

外挂知识库的本质在于不修改基座模型参数,通过提示词工程(Prompt Engineering)将特定知识作为prompt中的context,即召回相关性最高的几个文档,让模型分析这些蕴含知识后,并返回答案。知识库适合要求输出明确且精度高的任务。


相对于微调,知识库的优势在于:

▪ 回答精确度更高,基于相关文档中的最相关特定段落进行语义搜索能消除查询歧义以生成更精确的答案

▪ 适应性更强,用户可以通过轻松更新信息源来调整和适配新的领域

但大模型上下文窗口长度的限制和Prompt的构造等因素带来的潜在精度下降也需要纳入知识库构建的考量。


为了打造特定领域(Domain-specific Knowledge)的知识问答系统,我们需要借助提供了外挂知识库的搜索方案LangChain框架。


1.2 LangChain模块


LangChain是一个由语言模型驱动的用于开发应用程序的框架。LangChain主要的两个能力是:

▪ Data-aware:将不同数据源接入到语言模型中

▪ Agentic:允许语言模型和LangChain环境交互


LangChain的核心模块包括Models,Prompts,Chains,Indexes,Agents等 [1]。对于每一个模块,LangChain都提供了标准化的可拓展接口。


ed2836ae1a4b02373970971284656e19.jpeg

图1:LangChain部分模块 [2]


除了用LLM Wrapper可以接入众多的大模型(如 OpenAI、Cohere、Hugging Face),LangChain同时也通过VectorStore Wrapper接口集成了主流的向量数据库(如 Milvus、Pinecone、Chroma等)来优化语义搜索。


LangChain能接入的数据类型涵盖了文本、PPT、图片、HTML、Pdf等非结构化文件。相较于传统数据库的精确搜索,即完全匹配,向量数据库使用最邻近(Approximate Nearest Neighbor,ANN)算法和相似度度量(如余弦相似度,内积等)来找到和查询问题最相似的向量。


基于本地知识库问答的大致流程如下:

加载文档 -> 文本拆分 -> 根据question/query语义检索匹配文本 -> 构建prompt -> LLM生成回答


这里以Milvus数据库和ChatGPT作为示例:

975b0ea4b3ae4b21989b9f6fb7dcfd60.jpeg

图2:LangChian + Milvus + ChatGPT pipeline [3]

 

02 实战


目前,我们已经拆解完了LangChain+LLM文档问答的大致链路,接下来我们正式进入实战环节。


2.1 环境搭建


a. 安装LangChain

确保Python 版本≥ 3.8.1 且 <4.0。

pip install langchain


b.&nbsp;部署LLama 2

▪&nbsp;关于Llama 2模型的部署,详情可参见上期文章《大模型技术实践(二)|关于Llama 2你需要知道的那些事儿》


▪ UCloud官方的“LLaMA2 模型快速部署”文档:https://docs.ucloud.cn/gpu/practice/LLaMA2?id=llama2-模型快速部署


c.&nbsp;下载Embedding 模型

这里我们选择text2vec-large-chinese [4]这个Embedding模型,下载地址为:

https://huggingface.co/GanymedeNil/text2vec-large-chinese


对于中文的场景,也有其他优秀的开源模型可供选择,如m3e和bge等[5]。


d.&nbsp;下载数据集

心灵鸡汤文本数据集:https://huggingface.co/datasets/soulteary/warm-chicken-soup/


这个数据集是从Google网页上爬取的一些心灵鸡汤引用短文,共包含631条文本

&nbsp;

2.2 文档解析


a.&nbsp;加载数据集

LangChain对于不同格式的数据源内置了不同的解析脚本,最终这些数据都将转换为纯txt文本格式,以实现文本标准化。

from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

loader = UnstructuredFileLoader("数据集存放地址") &nbsp;
docs = loader.load()


b.&nbsp;文本切分

文本切分中的chunk_size指定了切分后的文本块的字数,chunk_overlap指定了切分文本块之间的重叠字数。由于鸡汤引用文本总长度较短,且文本内部语义关联度高,所以这里的chunk_size设置为50,chunk_overlap设置为20。

text_splitter = RecursiveCharacterTextSplitter(chunk_size=50,chunk_overlap=20)
docs = text_splitter.split_documents(docs)


c.&nbsp;文本嵌入和向量库

文本切分后,我们需要将文本进行向量化表示,将其映射为低维稠密的向量并存储到然向量数据库中。向量数据库选用了无需注册的FAISS。

from langchain.embeddings.huggingface import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS

# 导入向量模型
import os
embeddings = HuggingFaceEmbeddings(
&nbsp;&nbsp;&nbsp;&nbsp;model_name = "{你的地址}/text2vec-large-chinese",
&nbsp;&nbsp;&nbsp;&nbsp;model_kwargs = {'device': 'cuda'})

# 如果没有本地faiss仓库,先读取doc向量库,再将向量库保存到本地
if os.path.exists("{你的地址}/my_faiss_store.faiss") == False:
&nbsp;&nbsp;&nbsp;&nbsp;vector_store = FAISS.from_documents(docs,embeddings)
&nbsp;&nbsp;&nbsp;&nbsp;vector_store.save_local("{你的地址}/my_faiss_store.faiss")
# 如果faiss仓库已存在,则直接读取
else:
&nbsp;&nbsp;&nbsp;&nbsp;vector_store = FAISS.load_local(
&nbsp;&nbsp;&nbsp;&nbsp;"{你的地址}/my_faiss_store.faiss",
&nbsp;&nbsp;&nbsp;&nbsp;embeddings=embeddings)


2.3 加载模型


import torch
from transformers import AutoTokenizer, AutoModelForCausalLM

# 加载tokenizer
tokenizer = AutoTokenizer.from_pretrained(
&nbsp;&nbsp;&nbsp;&nbsp;'/opt/Llama-2-7b-chat-hf',
&nbsp;&nbsp;&nbsp;&nbsp;trust_remote_code=True)

# 加载模型 Llama 2-chat-7B
base_model = AutoModelForCausalLM.from_pretrained(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"/opt/Llama-2-7b-chat-hf",
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;torch_dtype=torch.float16,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;device_map='auto',
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;trust_remote_code=True
&nbsp;&nbsp;&nbsp;&nbsp;)
llm = base_model.eval()


2.4 语义检索


接下来,我就能根据构建好的向量数据库召回对应文本片段。


a.&nbsp;向量化召回

FAISS默认使用L2(欧式距离),召回的文档按照相似度结果从大到小排序。

query = "面对求职屡屡碰壁的大学生,请说一句话来鼓励他?"
docs = vector_store.similarity_search(query) # 计算相似度,并把相似度高的chunk放在前面
context = [doc.page_content for doc in docs] # 提取chunk的文本内容
print(context)


b.&nbsp;设置提示词模板

以下是Llama 2默认的提示词模板

&nbsp;#qa_template = """Use the following pieces of information to answer the user's question.
&nbsp;#If you don't know the answer, just say that you don't know, don't try to make up an answer.
&nbsp;#Context: {context}
&nbsp;#Question: {question}
&nbsp;#Only return the helpful answer below and nothing else.
&nbsp;#Helpful answer: """


我们可以参考上面的模板,根据场景定制化自己的模板来拼接Query和召回结果

context ="\n".join(context)
prompt = f"基于以上内容:\n{context} \n 请回答:{query} \n 字数限制在30字以内"


2.5 推理示例


我们对LLM的参数进行设置,例如最大令牌(max_new_tokens)、最高k值(top_k)、温度(temperature)和重复惩罚(repetition_penalty)等等。最后,将prompt喂给模型。

# 检查显存占用
nvidia-smi

inputs = tokenizer([f"Human:{prompt}\nAssistant:"], return_tensors="pt")
input_ids = inputs["input_ids"].to('cuda')

# llm参数设置
param_config = {
&nbsp;&nbsp;&nbsp;&nbsp;"input_ids":input_ids,
&nbsp;&nbsp;&nbsp;&nbsp;"max_new_tokens":1024,
&nbsp;&nbsp;&nbsp;&nbsp;"do_sample":True,
&nbsp;&nbsp;&nbsp;&nbsp;"top_k":5,
&nbsp;&nbsp;&nbsp;&nbsp;"top_p":0.95,
&nbsp;&nbsp;&nbsp;&nbsp;"temperature":0.1,
&nbsp;&nbsp;&nbsp;&nbsp;"repetition_penalty":1.3
}
result &nbsp;= llm.generate(**param_config)

answer = tokenizer.decode(result[0], skip_special_tokens=True)

print(answer)
# output&nbsp;example

# Q:面对求职屡屡碰壁的大学生,请说一句话来鼓励他?
# A:坚持不懈,机会终将降临


03&nbsp;外挂知识库的问题和优化


3.1 LLM+Embedding-Search的局限


外挂知识库将用户问题和本地知识向量化,比较两者的向量相似度(Vector Similarity)进行召回。然而,这种全量的Embedding-Search在面对多知识点聚合处理的场景下,存在召回精度低的问题。因为知识库的构建是对单个知识点进行索引,而非对不同知识点的排列组合分别索引。


居里夫人的出生年月 -> 单索引
居里夫人、爱因斯坦、奥本海默的出生年月 -> 组合索引

Q: 居里夫人、爱因斯坦和奥本海默三人中谁最早出生?


为了避免召回遗漏,直观的处理方法包括降低相似度阈值(similarity score threshold)和增加召回数量(top_k),但这不免会引入无关的知识点噪声且增加和LLM交互的token开销。


3.2 效果优化方向


意图识别和召回优化

提升问答系统的精度可以从意图识别和召回优化两个角度考虑,且两者都可以用关键词表示,即从直接将用户query和知识点进行embedding转变为对两者提取关键词后再进行匹配。意图识别可以通过关键词提取(Information Extraction, IE)和槽位填充(Slot Filling,SF)实现。


1.&nbsp;关键词提取


a.&nbsp;面向query——槽位填充

利用LLM思维链(Chain-of-Thought,COT)的提示能力来引导用户多轮对话并进行信息总结。针对我们的心灵疗愈机器人的场景,比如用户查询心灵鸡汤的句子,那么就要求用户的提供年龄段,情绪问题和情感需求等信息。语义槽格式如下:

&nbsp; &nbsp;"心灵鸡汤" : {&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"用户年龄段" : ____, # 青年,中年,老年
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"情绪问题" : ____, # 焦虑,失恋
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"情感需求" : ____, # 寻求安慰,寻求激励
&nbsp;&nbsp;&nbsp;&nbsp;}


b.&nbsp;面向知识点——索引入口


对于知识点可以从以下两个方面考虑:

i. 对相同知识点建立多级索引,有助于实现对维度查询。比如对一位奥运冠军的姓名,竞赛项目,年龄,获奖时间等分别建立索引。

ii. 将知识库转化为以关系三元组为核心的知识图谱。三元组的抽取除了传统的命名实体识别(NER)等方法,也可以通过prompt让大模型来进行抽取。


基于关键词的embedding入库和搜索流程如下:

464272ac6a1fbc048615af4340cbdb9a.jpeg


2.&nbsp;多路召回

类似于Bert时代的垂直领域问答系统,我们可以将语义检索和传统的Elasticsearch(ES)关键词搜索并行,对两者进行加权打分投票来获取最终的top_k。


5fc6e6fafa40723f60fe83db5aca389f.jpeg


目前类似于以上优化思路已经落地的有“智海-录问”法律大模型 [6],其基座模型为Baichuan-7B。智海-录问知识增强的完整链路如图3。值得注意的是,智海-录问在知识库中对每一个知识点是以 [key, value] pair 形式存储的。key是知识点的内容简介,用于检索;value是知识点的具体内容,用于模型输入。实现细节请参照其Hugging Face仓库。


&nbsp;

516f59096a239564a94a037b6171aeb1.jpeg

图3:“智海-录问”知识增强链路


其他优化方向

除了Embedding部分,“LangChain+LLM”(图2)链路内的其他组件也有进一步优化的空间:


1.&nbsp;知识库细化

当用户手动选择分区后,分区检索可以明显提高召回的精度。


7b650225b45ac9080e5d919dcb1f963b.jpeg

图4:“智海-录问”的交互界面


2.&nbsp;文本切分方式

由于文本重叠(overlap)的大小没有统一标准,如何保证语义完整和连贯都需要不断测试。


3.&nbsp;提示词的质量

在提示词模板的设计上要增加明确约束条件的指令,减少大模型出现幻觉现象的几率。


4.&nbsp;大模型的选型

选择基座模型还是微调后的模型,以及对中文的支持程度的需求都需要结合下游场景进行判别。

&nbsp;

本期文章带你基于“LangChain+LLM”框架快速搭建了知识增强后的问答机器人——心灵疗愈师,并探讨了提升模型的内容理解和执行能力的潜在优化方向。


下期文章我们将深入解读目前主流的大模型微调技术,敬请期待~

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

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

相关文章

基于Simulink的用于电力系统动态分析

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

zemax双透镜公差分析

公差分析&#xff0c;就是在设计了一个理想的系统后&#xff0c;想看看实际生产过程中如果产生公差&#xff08;误差&#xff09;&#xff0c;系统会坏到什么程度&#xff0c;也就是光学性能受到多大影响。 先设计出双透镜&#xff1a; 在zemax中找到公差选项卡&#xff0c;准…

算法专题:前缀和

文章目录 Acwing&#xff1a;前缀和示例2845.统计趣味子数组的数目思路容易理解的写法&#xff1a;前缀和两层循环存在问题&#xff1a;超时 优化写法&#xff1a;两数之和思路&#xff0c;转换为哈希表 前缀和&#xff0c;就是求数组中某一段的所有元素的和。 求子数组中某一…

输运方程的推导

1 概述 对于流场中守恒的物理量&#xff0c;均可采用输运方程&#xff08;transport equation&#xff09;进行描述其随时间变化和在空间的分布规律。输运方程的通用形式为&#xff1a; 输运方程描述了流动过程中的物理量守恒&#xff0c;其包括瞬态&#xff08;transient&…

FPGA实战小项目2

基于FPGA的贪吃蛇游戏 基于FPGA的贪吃蛇游戏 基于fpga的数字密码锁ego1 基于fpga的数字密码锁ego1 基于fpga的数字时钟 basys3 基于fpga的数字时钟 basys3

ElMessageBox.prompt 点击确认校验成功后关闭

ElMessageBox.prompt(, 验证取货码, {inputPattern: /^.{1,20}$/,inputErrorMessage: 请输入取货码,inputPlaceholder: 请输入取货码,beforeClose: (action, instance, done) > {if (action confirm) {if (instance.inputValue) {let flag false;if (flag) {done()} else …

OpenCV(二十五):边缘检测(一)

目录 1.边缘检测原理 2.Sobel算子边缘检测 3.Scharr算子边缘检测 4.两种算子的生成getDerivKernels() 1.边缘检测原理 其原理是基于图像中灰度值的变化来捕捉图像中的边界和轮廓。梯度则表示了图像中像素强度变化的强弱和方向。 所以沿梯度方向找到有最大梯度值的像素&…

信息检索与数据挖掘 |(一)介绍

文章目录 &#x1f4da;信息检索&#x1f407;概念&#x1f407;结构化与非结构化数据&#x1f407;信息检索的基本假设&#x1f407;信息检索小结&#x1f407;附&#xff1a;IR新课题 &#x1f4da;数据挖掘&#x1f407;定义&#x1f407;数据挖掘 vs 机器学习 &#x1f4da…

论文笔记:一分类及其在大数据中的潜在应用综述

0 概述 论文&#xff1a;A literature review on one‑class classification and its potential applications in big data 发表&#xff1a;Journal of Big Data 在严重不平衡的数据集中&#xff0c;使用传统的二分类或多分类通常会导致对具有大量实例的类的偏见。在这种情况…

2023 大学生数学建模竞赛-C题-第一问

题目&#xff1a; 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c;商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销…

深入浅出PyTorch函数torch.rand与torch.randn

torch.rand 和 torch.randn 都是PyTorch中用于生成随机张量的函数&#xff0c;但它们生成随机数的方式有所不同。 一、torch.rand torch.rand 生成在区间 [0, 1) 内均匀分布的随机数。 size 参数是一个表示所需张量形状的元组或整数。可以生成任何形状的随机张量。 二、torch.…

1、Flutter移动端App实战教程【环境配置、模拟器配置】

一、概述 Flutter是Google用以帮助开发者在IOS和Android 两个平台开发高质量原生UI的移动SDK&#xff0c;一份代码可以同时生成IOS和Android两个高性能、高保真的应用程序。 二、渲染机制 之所以说Flutter能够达到可以媲美甚至超越原生的体验&#xff0c;主要在于其拥有高性…

相似性搜索,第 4 部分:分层可导航小世界 (HNSW)

一、说明 SImilarity 搜索是一个问题&#xff0c;给定一个查询的目标是在所有数据库文档中找到与其最相似的文档。相似度搜索&#xff08;similarity search&#xff09;是指在大规模数据集中寻找与某个查询对象最相似的对象的过程。该过程通常涉及计算两个对象之间的相似度得分…

软件设计师学习笔记9-进程调度

目录 1. PV操作 1.1进程的同步与互斥 1.1.1互斥 1.1.2同步 1.2 PV操作 1.2.1信号量 1.2.2 PV操作的概念 2.信号量与PV操作 2.1 PV操作与互斥模型 2.2 PV操作与同步模型 2.3 互斥与同步模型结合 3.前趋图与PV操作 1. PV操作 1.1进程的同步与互斥 1.1.1互斥 互斥&…

Car Window Control Reset

大众汽车窗口自动升降失效&#xff0c;重置&#xff1a; 扣住5秒&#xff0c;重启汽车&#xff0c;试一下车钥匙&#xff0c;和再重试这个按钮&#xff0c;扣一下试一试

使用openWRT 配置SFTP 实现远程文件安全传输

文章目录 前言 1. openssh-sftp-server 安装2. 安装cpolar工具3.配置SFTP远程访问4.固定远程连接地址 前言 本次教程我们将在OpenWRT上安装SFTP服务&#xff0c;并结合cpolar内网穿透&#xff0c;创建安全隧道映射22端口&#xff0c;实现在公网环境下远程OpenWRT SFTP&#xf…

nginx空字节漏洞复现

将nginx复制到C盘根目录 cmd运行startup.bat 安装完成后访问 输入info.php 输入info.png 抓包使用00截断 可以看到phpinfo成功执行 在PHP的底层C语言里&#xff0c;%00代表着字符串结束&#xff0c;00截断可以用来绕过后端验证&#xff0c;后端验证的时候因为00截断认为文件是…

【已更新代码图表】2023数学建模国赛E题python代码--黄河水沙监测数据分析

E 题 黄河水沙监测数据分析 黄河是中华民族的母亲河。研究黄河水沙通量的变化规律对沿黄流域的环境治理、气候变 化和人民生活的影响&#xff0c;以及对优化黄河流域水资源分配、协调人地关系、调水调沙、防洪减灾 等方面都具有重要的理论指导意义。 附件 1 给出了位于小浪底水…

postgresql-使用plpgsql批量插入用户测试数据

目的 使用plpgsql批量插入用户测试数据 ❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤我是分割线❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤ 我的环境 客户端&#xff1a;windows 版pgadmin4 服务端&#xff1a;linux版PostgreSQL 15.4 ❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤❤…

Spring整合tomcat的WebSocket详细逻辑(图解)

主要解决存在的疑问 为什么存在2种spring整合websocket的方式&#xff0c;一种是使用ServerEndpoint注解的方式&#xff0c;一种是使用EnableWebSocket注解的方式&#xff0c;这2种有什么区别和联系&#xff1f;可以共存吗&#xff1f;它们实现的原理是什么&#xff1f;它们的各…