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

2023-11-11

上期文章我们实现了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”框架快速搭建了知识增强后的问答机器人——心灵疗愈师,并探讨了提升模型的内容理解和执行能力的潜在优化方向。


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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • 物联网全称_物联网的魔力世界

    物联网顾名思义就是一种万物相连的网 英文全称 Internet of Things 缩写IoT 物联网可以让所有能行使独立功能的物体实现相互连接 通过物联网技术 可以用中心计算机对机器 设备或人员进行集中管理 控制 也可以对家用电器 汽车等
  • 构建 react应用程序 (二)(react-scripts实现原理)

    在前面讲到了使用create react app来创建项目 这节我们来分析下原理 react scripts有以下支持 都帮你配置好了 React JSX ES6 and Flow syntax support Language extra
  • xShell操作Linux的常用命令

    我们需要在本地连接Linux服务器 可以用winscp来进行连接 优点是图形化界面 文件的层级关系类似于Windows 更容易操作 也可以使用xShell来进行连接 查看和操作文件就需要使用Linux命令 文件的层级关系没有前者直观 但作为
  • 力扣刷题-210.课程表Ⅱ、图的表示方式、BFS

    一 图的基本概念 定义和基本术语 图是由节点以及连接这些节点边组成 无向图 每条边连接的两个节点可以双向访问 有向图 每条边连接的两个节点只能单向访问 出度 有向图的某个节点作为起点的次数和 入度 有向图的某个节点作为终点的次数和 权重 图
  • springMVC 绑定前台穿来的时间类型数据

    当前台传来的数据 对时间类型的数据其实是无法绑定的 解决办法1 DataTimeFormat pattern yyyy MM dd 标注在实体类的时间字段上 解释 这个注解的支持是来自
  • Cmake简介和简单使用方法

    cmake 简介 CMake是一个跨平台的安装 编译 工具 可以用简单的语句来描述所有平台的安装 编译过程 他能够输出各种各样的makefile或者project文件 能测试编译器所支持的C 特性 类似UNIX下的automake CMak
  • 定义字符串

    目录 一 什么是字符串 二 定义字符串的具体格式 三 求字符数组和字符串长度比较 一 什么是字符串 所谓字符串本质上就是以 0 作为结尾的特殊字符数组 二 定义字符串的具体格式 1 char 字符串名称 字符串长度 字符串所含元素 注意点
  • LabView的简单例题,尽量做到一题多解

    Question 1 亮灯 难度 以组合框为输入条件 分别完成亮红灯 亮黄灯 亮绿灯 灯全灭 Question 2 波形图和波形图表的区别 难度 在波形图和波形图表中显示一个sin函数 sin函数没有定性要求 注意 波形图表输入是数字 而波
  • svn 命令行操作

    文章目录 help checkout help svn help update up checkout co checkout checkout co svn co URL username xxx password xxx svn co
  • IMX6学习记录(10)-挂载SD卡,数据掉电存储

    上面是我的微信和QQ群 欢迎新朋友的加入 目前使用的文件系统是打包成镜像文件的 通过UBOOT挂载镜像 在RAM里面运行起来了 这有个问题 数据不会被存储下来 掉电就会丢失 1 挂载SD卡 fdisk l mount dev mmcblk0
  • Java垃圾回收器

    Java垃圾回收器 1 GC分类与性能指标 1 1 垃圾回收器概述 1 2 垃圾回收器分类 1 2 1 按照线程分 1 2 2 按照工作模式分 1 2 3 碎片处理方式分 1 2 4 工作的内存区间分 1 3 评估GC的性能指标 1 4 吞
  • 另一个Kubernetes(k8s)指南

    从头开始学习Kubernetes Photo by Bobby Burch on Unsplash 近年来 Kubernetes k8s 的受欢迎程度激增 如果您要部署许多容器化的应用程序 那么无论在私有云还是公共云环境中 k8无疑都是当前
  • easyx图形库-----贴图技巧之双缓冲消除闪屏(BeginBatchDraw 与 EndBatchDraw)

    目录 前言 案例1 作图时闪屏 案例2 贴图时闪屏 双缓冲解决闪屏问题 原理 前言 学了easyx图形库的朋友们都知道 我们可以在easyx图形库上面画出连续运动的图片 或者通过贴图的方式把每一帧图片连起来达到动态效果 但是我们每次贴完图片
  • 【react从入门到精通】React父子组件通信方式详解(有示例)

    文章目录 人工智能福利文章 前言 React技能树 通过 props 实现父子组件通信 通过 state 实现父子组件通信 通过回调函数实现父子组件通信 使用 React Context 实现组件通信 总结 写在最后 人工智能福利文章 分享
  • 参考文献期刊首字大写_干货

    解读论文写作与发表策略 助您成功发表 关注 在论文的写作过程中 参考文献必不可少 参考文献的引用体现作者对所研究领域的熟悉程度 作者的学术眼光以及对他人学术成果的尊重 根据GB T 7714 2015 信息与文献 参考文献著录规则 参考文献
  • 给大家排个雷,ensp中nat不成功原因

    用ensp做网络设计 nat一直不成功 内网ping外网能通 但是抓包显示地址没有转换成功 检查了所有配置没问题 最后将Router换成了AR1 配置不变 实验成功 建议大家以后做实验不要用Router
  • Docker中搭建FastDFS文件系统(多图)

    关于FastDFS FastDFS 是以 C 语言开发的一项开源轻量级分布式文件系统 他对文件进行管理 主要功能有 文件存储 文件同步 文件访问 文件上传 下载 等 特别适合以文件为载体的在线服务 如 图片网站 视频网站等 外话 FastD
  • 一种排序方法 C#

    int array new int 10 3 2 4 90 50 20 34 22 49 int newArray new int array Length int Min 100000000 for int i 0 i lt array
  • 【习题之Python篇】习题4

    1 输入一个字符串 内容是个带小数的实数 例如123 45 输出是两个整数变量x和y x是整数部分123 y是小数部分45 s input 输入带小数的数字 s s split print int s 0 是整数部分 int s 1 是小数
  • 大模型技术实践(三)|用LangChain和Llama 2打造心灵疗愈机器人

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