LLM实战系列(1)—强强联合Langchain-Vicuna应用实战

背景

本文主要介绍一下,基于Langchain与Vicuna-13B的外挂OceanBase知识库项目实战以及QA使用,项目地址:

github.com/csunny/DB-G…

在开始之前,我们还是先看看效果~

在这里插入图片描述
在这里插入图片描述

自Meta发布LLaMA大模型以来, 围绕LLaMA微调的模型也是层出不穷。 从alpaca 到 vicuna,再到刚刚发布的中医垂直领域微调大模型华佗GPT, 可谓是风光无限。 但其中最出名、效果最好的当属vicuna-13B。如下图所示,当前在众多大模型当中,Vicuna-13B的效果非常接近ChatGPT,有其92%的效果。 这意味着什么呢? 意味着,我们基于开源的Vicuna-13B即可搞定决大多数的任务与需求。 当然什么外挂知识库QA这样的简单需求自然不在话下。

那Langchain又是什么呢?

毫无疑问,Langchain是目前大语言模型领域最炙手可热的LLM框架。

LangChain 是一个构建在LLM之上的应用开发框架。想让应用变得更强大,更加不同,单单通过调用大模型的API肯定是不够的, 还需要有以下特性:

  1. 数据思维: 连接大模型到其他的元数据上。
  2. 代理思维: 语言模型可以与环境交互。

以上就是Langchain的设计理念, It’s very simple, but enough nature. 是的,足够简单,但很贴近本质。我们也是被Langchain的理念深深的吸引。 所以,我们来了~

方案

既然是一个实战项目,那么在项目开始之前,我们有必要对项目整体的架构做一个清晰的梳理。

添加图片注释,不超过 140 字(可选)

如图所示,是我们整体的架构图。 从图中我们可以看到,左侧有一条线是知识库 -> Embedding -> 向量存储 -> 大模型(Vicuna-13B) -> Generate 的路径。 在我们本文中,就是依赖此路径外挂知识库进行推理、总结,以完成QA的工作。

所以我们整体将以上过程拆分为如下所示的四个步骤。

  1. 知识库准备: 如同所示中,因为我们是面向DB领域的GPT,所以我们准备了主流数据库的文档,并进行了分类。

  2. Embedding: embedding这一步是需要将文本转换成向量进行存储,当然了,存储媒介是向量数据库,关于向量数据库的了解,大家可以从这里了解向量数据库

  3. Embedding之后的知识,会存储在向量数据库当中,用于后面的检索。

  4. 利用大模型的能力,通过ICL(In-Context-Learning) 让大模型实现基于现有知识的推理、总结。

  5. 这样我们就可以实现一个基于现有知识库QA的项目了。

整个知识库的处理过程,也可以参考Langchain-ChatGLM项目中的一张图。

图片来自Langchain-ChatGLM

代码说明

既然是实战,那肯定少不了代码,毕竟我们一贯坚持的是:

Talk is cheap, show me the code.

模型加载

目前基本主流的模型都是基于HuggingFace的标准,所以模型加载代码其实就变得很简单了。 如下所示,为模型加载类,所需要的参数只需要传一个model_path, 在这个类当中,我们实现了一个方法,loader方法,通过这个方法我们可以获得两个对象。 1. tokenizer 2. model, 根据这两个对象,我们就得到一个模型了,后面的事情,关注使用就可以啦。

class ModelLoader:"""Model loader is a class for model loadArgs: model_path"""kwargs = {}def __init__(self, model_path) -> None:self.device = "cuda" if torch.cuda.is_available() else "cpu"self.model_path = model_path self.kwargs = {"torch_dtype": torch.float16,"device_map": "auto",}def loader(self, num_gpus, load_8bit=False, debug=False):if self.device == "cpu":kwargs = {}elif self.device == "cuda":kwargs = {"torch_dtype": torch.float16}if num_gpus == "auto":kwargs["device_map"] = "auto"else:num_gpus = int(num_gpus)if num_gpus != 1:kwargs.update({"device_map": "auto","max_memory": {i: "13GiB" for i in range(num_gpus)},})else:raise ValueError(f"Invalid device: {self.device}")if "chatglm" in self.model_path:tokenizer = AutoTokenizer.from_pretrained(self.model_path, trust_remote_code=True)model = AutoModel.from_pretrained(self.model_path, trust_remote_code=True).half().cuda()else:tokenizer = AutoTokenizer.from_pretrained(self.model_path, use_fast=False)model = AutoModelForCausalLM.from_pretrained(self.model_path,low_cpu_mem_usage=True, **kwargs)if load_8bit:compress_module(model, self.device)if (self.device == "cuda" and num_gpus == 1):model.to(self.device)if debug:print(model)return model, tokenizer

知识库准备

准备知识库,没什么特别需要讲的,可以是pdf、txt、md等等的吧。 在这里,我们准备的是一个md文档,知识库是基于开源的OceanBase官方文档。 预备好的知识库地址: OceanBase文档。

注:这里特别说明一下,为什么没有直接下载pdf。 两个原因 1. OB pdf文档有很多的格式,这些格式在向量处理的过程中也会保存下来, 默认处理后的知识没有压扁平,不利于后续的大模型使用。 2. pdf文档相对比较大,在本地跑,通过模型抽向量的过程会比较长,因此我们准备了一个简单的MarkDown文件来做演示。

知识转向量并存储到向量数据库

这里我们实现了一个Knownledge2Vector的类。这个类顾命思意,就是把知识库转换为向量。 当然我们转换成向量之后会持久化到数据库存储。 (问题1: 类名没有体现存数据库,是不是应该在斟酌一下?KnownLedge2VectorStore会更好? 🤔)

class KnownLedge2Vector:"""KnownLedge2Vector class is order to load document to vector and persist to vector store.Args: - model_nameUsage:k2v = KnownLedge2Vector()persist_dir = os.path.join(VECTORE_PATH, ".vectordb") print(persist_dir)for s, dc in k2v.query("what is oceanbase?"):print(s, dc.page_content, dc.metadata)"""embeddings: object = None model_name = LLM_MODEL_CONFIG["sentence-transforms"]top_k: int = VECTOR_SEARCH_TOP_Kdef __init__(self, model_name=None) -> None:if not model_name:# use default embedding modelself.embeddings = HuggingFaceEmbeddings(model_name=self.model_name) def init_vector_store(self):persist_dir = os.path.join(VECTORE_PATH, ".vectordb")print("向量数据库持久化地址: ", persist_dir)if os.path.exists(persist_dir):# 从本地持久化文件中Loadprint("从本地向量加载数据...")vector_store = Chroma(persist_directory=persist_dir, embedding_function=self.embeddings)# vector_store.add_documents(documents=documents)else:documents = self.load_knownlege()# 重新初始化vector_store = Chroma.from_documents(documents=documents, embedding=self.embeddings,persist_directory=persist_dir)vector_store.persist()return vector_store def load_knownlege(self):docments = []for root, _, files in os.walk(DATASETS_DIR, topdown=False):for file in files:filename = os.path.join(root, file)docs = self._load_file(filename)# 更新metadata数据new_docs = [] for doc in docs:doc.metadata = {"source": doc.metadata["source"].replace(DATASETS_DIR, "")} print("文档2向量初始化中, 请稍等...", doc.metadata)new_docs.append(doc)docments += new_docsreturn docmentsdef _load_file(self, filename):# 加载文件if filename.lower().endswith(".pdf"):loader = UnstructuredFileLoader(filename) text_splitor = CharacterTextSplitter()docs = loader.load_and_split(text_splitor)else:loader = UnstructuredFileLoader(filename, mode="elements")text_splitor = CharacterTextSplitter()docs = loader.load_and_split(text_splitor)return docsdef _load_from_url(self, url):"""Load data from url address"""passdef query(self, q):"""Query similar doc from Vector """vector_store = self.init_vector_store()docs = vector_store.similarity_search_with_score(q, k=self.top_k)for doc in docs:dc, s = docyield s, dc

这个类的使用也非常简单, 首先实例化,参数也是只有一个model_name, 需要注意的是,这里的model_name 是转向量的模型,跟我们前面的大模型不是同一个,当然这里能不能是同一个,当然也是可以的。(问题2: 可以思考一下,这里我们为什么没有选择LLM抽向量?)

这个类里面我们干的事情其实也不多,总结一下就那么3件。 1. 读文件(_load_file) 2. 转向量+持久化存储(init_vector_store) 3. 查询(query), 代码整体比较简单,在进一步的细节我这里就不解读了,还是相对容易看明白的。

注: 特别说明一下,我们这里用的抽向量的模型是Sentence-Transformer, 它是Bert的一个变种模型,Bert想必大家是知道的。如果有不太熟悉的同学,可以转到我这边文章,来了解Bert的来龙去脉。Magic:LLM-GPT原理介绍与本地(M1)微调实战

# persist_dir = os.path.join(VECTORE_PATH, ".vectordb") 
# print(persist_dir)k2v = KnownLedge2Vector()
for s, dc in k2v.query("what is oceanbase?"):print(s, dc.page_content, dc.metadata)

知识查询

通过上面的步骤,我们轻轻松松将知识转换为了向量。 那么接下来,我们就是根据Query查询相关知识了。

我们定义了一个KnownLedgeBaseQA, 这个类只有短短十几行代码, 所以看起来也不费劲。 核心的方法就一个,get_similar_answer, 这个方法只接收一个query字符串,根据这个query字符串,我们就可以在我们之前准备好的知识库当中,查询到相关知识。

class KnownLedgeBaseQA:def __init__(self) -> None:k2v = KnownLedge2Vector()self.vector_store = k2v.init_vector_store()self.llm = VicunaLLM()def get_similar_answer(self, query):prompt = PromptTemplate(template=conv_qa_prompt_template,input_variables=["context", "question"])retriever = self.vector_store.as_retriever(search_kwargs={"k": VECTOR_SEARCH_TOP_K})docs = retriever.get_relevant_documents(query=query)context = [d.page_content for d in docs] result = prompt.format(context="\n".join(context), question=query)return result

推理&QA

知识都查出来了,剩下的就交给大模型吧。 我们这里使用的是vicuna-13b的模型,具体的示例代码如下,

是的,这里也没什么难的,就是构造一个参数,然后发一个POST,也没啥特别好讲的。

def generate(query):template_name = "conv_one_shot"state = conv_templates[template_name].copy()pt = PromptTemplate(template=conv_qa_prompt_template,input_variables=["context", "question"])result = pt.format(context="This page covers how to use the Chroma ecosystem within LangChain. It is broken into two parts: installation and setup, and then references to specific Chroma wrappers.",question=query)print(result)state.append_message(state.roles[0], result)state.append_message(state.roles[1], None)prompt = state.get_prompt()params = {"model": "vicuna-13b","prompt": prompt,"temperature": 0.7,"max_new_tokens": 1024,"stop": "###"}response = requests.post(url=urljoin(VICUNA_MODEL_SERVER, vicuna_stream_path), data=json.dumps(params))skip_echo_len = len(params["prompt"]) + 1 - params["prompt"].count("</s>") * 3for chunk in response.iter_lines(decode_unicode=False, delimiter=b"\0"):if chunk:data = json.loads(chunk.decode())if data["error_code"] == 0:output = data["text"][skip_echo_len:].strip()state.messages[-1][-1] = output + "▌"yield(output) 

最后,让我们看看知识问答的效果吧。如果觉得效果好,为我们点个赞吧👍

image.png

小结

综上所属,我们讲了当前开源主流的两个扛把子强强联合的应用实战。 Vicuna-13B与Langchain在整个AI的生态里面,做的是完全不同的事情。 一个是定义框架做标准跟链接, 一个是深入核心做技术跟效果。很显然,这两条路都获得了重大的成功。 整个发展的思路,我相信很值得我们借鉴,通过本文的介绍,希望能对大家有一些帮助。

最后,如果你觉得本教程里面的内容对你有帮助,并且想持续关注我们的项目,请帮忙在GitHub给我们的项目点个赞吧❤️💗💗😊😊😊。 项目地址: github.com/csunny/DB-G…

当然了,如你开篇所见,这仅仅是我们项目里面很小的一部分,同时这也会是一个系列教程。 如果关心我们的项目,或者对我们的工作感兴趣,欢迎持续关注我们。

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

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

相关文章

基于PHP+MySQL组合开发的微信活动投票小程序源码系统 带完整的安装代码包以及搭建部署教程

系统概述 在当今数字化时代&#xff0c;微信作为社交媒体的巨头&#xff0c;为企业和个人提供了丰富的互动营销平台。其中&#xff0c;投票活动作为一种有效的用户参与和互动方式&#xff0c;被广泛应用于各种场景。为了满足这一需求&#xff0c;我们推出了一款基于PHPMySQL组…

W1R3S靶机全通详细教程

文章目录 w1r3s主机发现主机扫描 端口扫描tcp端口扫描UDP扫描漏洞扫描 攻击面分析FTP渗透匿名登录 web渗透目录爆破 cuppa cms文件包含漏洞getshell提权 w1r3s 引言 近些日子看红笔大佬的靶机精讲视频时&#xff0c;他的一句话让我感受颇深&#xff0c;很多视频在讲解时&…

数据结构:线性表(下)

那么这篇就来总结一下栈和队列 一、栈 栈 (Stack) 只允许在有序的线性数据集合的一端&#xff08;称为栈顶 top&#xff09;进行加入数据&#xff08;push&#xff09;和移除数据&#xff08;pop&#xff09;。因而按照 后进先出&#xff08;LIFO, Last In First Out&#xf…

好用的抠图小技巧

在ps里的抠图方法 方法一&#xff1a;直接在菜单栏里选择主体&#xff0c;选中主体后会出现蚂蚁线&#xff0c;这个时候可能选区还不够完整&#xff0c;需要借助快速选择工具细化选取&#xff0c;选好之后按ctrlj复制选区就抠好啦 方法二&#xff1a;用快速选择工具直接选取人…

浏览器指纹技术:如何更改浏览器指纹?

“指纹信息”是一个人独有的身份象征&#xff0c;而“浏览器指纹”&#xff0c;就是网站和在线平台使用浏览器指纹来收集有关您的浏览器、设备和网络的详细信息&#xff0c;它可以说是你上网的身份象征&#xff0c;可让网站跟踪您的在线行为。 下面我们简单科普浏览器指纹的工…

【Python体验】第五天:目录搜索、数据爬虫(评论区里写作业)

文章目录 目录搜索 os、shutil库数据爬虫 request、re作业&#xff1a;爬取案例的top250电影的关键信息&#xff08;名称、类型、日期&#xff09;&#xff0c;并保存在表格中 目录搜索 os、shutil库 os 模块提供了非常丰富的方法用来处理文件和目录。 os.listdir(path)&#x…

C语言| 文件操作详解(二)

目录 四、有关文件的随机读写函数 4.1 fseek 4.2 ftell 4.3 rewind 五、判定文件读取结束的标准与读写文件中途发生错误的解决办法 5.1 判定文件读取结束的标准 5.2 函数ferror与feof 5.2.1 函数ferror 5.2.2 函数feof 在上一章中&#xff0c;我们主要介绍了文件类型…

MySQL:管理和操作数据表

数据表是数据库的重要组成部分&#xff0c;每一个数据库都是由若干个数据表组成的。没有数据表就无法在数据库中存放数据。MySQL数据表的管理和操作是数据库管理员和开发人员日常工作中不可或缺的一部分。 创建数据表 CREATE 创建数据表的过程是规定数据列的属性的过程&#…

网工内推 | 云运维工程师,最高19K,五险一金加补充医疗险

01 云计算运维工程师 &#x1f537;岗位职责 1、负责客户云计算解决方案的运维&#xff0c;负责云计算解决方案中云、虚拟化工作&#xff1b; 2、负责客户现场H3C产品的日常问题处理、变更维护、巡检、版本升级等工作&#xff0c;保障客户网络的稳定运行&#xff1b; 3、协调…

揭秘智能工牌:如何成为房企销售团队的数字化转型加速器

在这个竞争激烈的市场环境中&#xff0c;房企想要脱颖而出&#xff0c;不仅需要优质的产品和服务&#xff0c;更需要高效的销售团队。而销售团队的能力提升&#xff0c;离不开精细化管理和科技的赋能。DuDuTalk智能语音工牌&#xff0c;正是这样一款融合了AI技术与销售实战智慧…

Python中的yieId,比return更高效!

本文旨在深入探索"yield"的基本原理和实际应用&#xff0c;帮助你理解为什么它在Python编程中如此重要。 一、深入理解Yield "yield"与常用的"return"有本质的区别。"yield"不是真正返回一个值并退出函数&#xff0c;而是暂停函数执行…

springboot报错

springboot报错&#xff1a;g.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length 1 解决办法&#xff1a; file->settings 搜索encoding 然后选择File encodings 也可以直接找 File encodings 全部都更改整utf-8&#xff…

8.1IO进程线程

笔记 进程 一.多进程引入 1.1引入目的 程序员写程序时&#xff0c;一个程序可能由多个任务组成&#xff0c;如果使用的是单进程&#xff0c;或单任务&#xff0c;那么该任务执行阻塞时&#xff0c;其他任务就无法执行&#xff0c;必须等到该任务解除阻塞后&#xff0c;才能…

2024上半年热门内容透视 | 品牌种草解析

2024年上半年&#xff0c;小红书平台“考公上岸”、“不确定性”、“重养自己一遍”、“人生是旷野”、“原生家庭顶配”等话题热议之下&#xff0c;透露着消费者怎样的需求&#xff1f; 综合热门内容及小红书用户的分享发现&#xff0c;变数和不确定性成为新常态&#xff0c;消…

基于OpenCV C++的网络实时视频流传输——Windows下使用TCP/IP编程原理

1.TCP/IP编程 1.1 概念 IP 是英文 Internet Protocol &#xff08;网络之间互连的协议&#xff09;的缩写&#xff0c;也就是为计算机网络相互连接进行通信而设计的协议。任一系统&#xff0c;只要遵守 IP协议就可以与因特网互连互通。 所谓IP地址就是给每个遵循tcp/ip协议连…

3D打印随形透气钢:模具困气终结者

困气是模具经常遇到的问题&#xff0c;是制约生产效率与产品质量的关键因素之一。传统透气钢材料虽有所助益&#xff0c;但其在加工复杂度、形状适应性及性能均衡性上的局限性明显。在此背景下&#xff0c;3D打印技术的革新性应用——随形透气钢应运而生&#xff0c;为困气、排…

NLP与搜广推常见面试问题

1 auc指标 AUC的两种意义 一个是ROC曲线的面积另外一个是统计意义。从统计学角度理解&#xff0c;AUC等于随机挑选一个正样本和负样本时&#xff0c;模型对正样本的预测分数大于负样本的预测分数的概率。下图为搜广推场景下的一个计算auc的例子 2 GAUC指标 就是在推荐系统…

字符设备驱动基础—sys文件系统,udev介绍,驱动模块在内核空间注册设备

文章目录 sys文件系统介绍设计思想应用和功能 udev介绍主要功能工作原理使用 udevadm 工具 设备文件创建流程驱动程序的注册device_create函数详解示例代码效果图 sys文件系统介绍 sysfs 是 Linux 内核中的一种虚拟文件系统&#xff0c;它为用户空间和内核之间提供了一种统一的…