三、知识库搭建

知识库搭建

  • 1 介绍
    • 词向量
    • 向量数据库
  • 2 使用embedding API
  • 3 数据处理
    • 数据加载
    • 数据清洗
    • 文档分割
  • 4 搭建并使用向量数据库
    • 4.1 自定义embedding封装
    • 4.2 chroma数据库
    • 4.3 向量检索

详细代码参考:https://github.com/lin902/llm-application

1 介绍

词向量

词向量就是把词、句、文段通过向量的形式表示出语义信息。比如猫和狗在向量相似度上就比猫和苹果要近。词向量的优势是可以通过向量存储、相似度语义上的查询(而非关键词搜索);同时可以将多模态的信息映射成统一的向量形式。

向量数据库

存储词向量的数据库。比如图数据库neo4j,本次要用的langchain数据库chroma.

2 使用embedding API

智谱

from dotenv import load_dotenv, find_dotenv
from zhipuai import ZhipuAI
import os_ = load_dotenv(find_dotenv())def zhipu_embedding(text: str):api_key = os.environ['ZHIPUAI_API_KEY']client = ZhipuAI(api_key=api_key)response = client.embeddings.create(model="embedding-2",input=text,)return responsetext = '要生成 embedding 的输入文本,字符串形式。'
response = zhipu_embedding(text=text)
print(f'response类型为:{type(response)}')
print(f'embedding类型为:{response.object}')
print(f'生成embedding的model为:{response.model}')
print(f'生成的embedding长度为:{len(response.data[0].embedding)}')
print(f'embedding(前10)为: {response.data[0].embedding[:10]}')

3 数据处理

数据加载

# 先看一下路径
# 计算上一级目录的路径
current_path = os.getcwd()
parent_path = os.path.dirname(current_path)# 获取上一级目录下的所有文件和文件夹
file_list = os.listdir(parent_path)# 输出上一级目录下的全部文件名
print("上一级目录下的文件名:")
for file in file_list:print(file)

pdf的文档提取

from langchain.document_loaders.pdf import PyMuPDFLoader
# print(os.getcwd())
# print(os.path.dirname(os.getcwd()))
# 创建一个 PyMuPDFLoader Class 实例,输入为待加载的 pdf 文档路径
loader = PyMuPDFLoader("../llm-universe/data_base/knowledge_db/pumkin_book/pumpkin_book.pdf") # 路径一定要对# 调用 PyMuPDFLoader Class 的函数 load 对 pdf 文件进行加载
pdf_pages = loader.load()

markdown文档提取

from langchain.document_loaders.markdown import UnstructuredMarkdownLoaderloader = UnstructuredMarkdownLoader("../llm-universe/data_base/knowledge_db/prompt_engineering/1. 简介 Introduction.md")
md_pages = loader.load()

数据清洗

在这里插入图片描述

可以看到,加载后的内容有句间的换行\n,还有标点符号·,把这些符号替换成空格。注意\n在文字与符号之间是要保留的,所以我们只删除文字与文字之间、符号与符号之间的换行\n.

# 处理掉字符之间换行/n,^\u4e00-\u9fff指非文字
import re
# print(pdf_page)
pattern = re.compile(r'[^\u4e00-\u9fff](\n)[^\u4e00-\u9fff]', re.DOTALL)
# 换行符(\n)替换为空字符串,但仅当这些换行符位于非汉字字符之间时。
pdf_page.page_content = re.sub(pattern, lambda match: match.group(0).replace('\n', ''), pdf_page.page_content)
print(pdf_page.page_content)
#
# 句间换行没有了
# 处理句中字之间换行符号
import re# 编译一个正则表达式模式,匹配汉字之间的换行符
pattern = re.compile(r'[\u4e00-\u9fff]+(\n)+[\u4e00-\u9fff]+', re.DOTALL)# 使用re.sub()函数替换匹配到的模式,将换行符替换为空字符串
pdf_page.page_content = re.sub(pattern, lambda match: match.group().replace('\n', ''), pdf_page.page_content)print(pdf_page.page_content)pdf_page.page_content = pdf_page.page_content.replace('•', '')
pdf_page.page_content = pdf_page.page_content.replace(' ', '')
# pdf_page.page_content = pdf_page.page_content.replace('\n', '') 不能直接全换,字与符号之间的换行有必要留
print(pdf_page.page_content)
# 处理 · 和空格
#print(pdf_page)# 段与段之间还有换行符
md_page.page_content = md_page.page_content.replace('\n\n', '\n')
print(md_page.page_content)

文档分割

模型上下文长度受限,因此在构建知识库的时候,需要对文档进行分割,分割成若干个chunk,转化成词向量,保存在数据库中。
如何对文档进行分割,其实是数据处理中最核心的一步,其往往决定了检索系统的下限。但是,如何选择分割方式,往往具有很强的业务相关性——针对不同的业务、不同的源数据,往往需要设定个性化的文档分割方式。
递归字符文本分割器: 通过递归的方式进行,意味着它会重复地将文本分割,直到满足特定的条件

''' 
* RecursiveCharacterTextSplitter 递归字符文本分割
RecursiveCharacterTextSplitter 将按不同的字符递归地分割(按照这个优先级["\n\n", "\n", " ", ""]),这样就能尽量把所有和语义相关的内容尽可能长时间地保留在同一位置
RecursiveCharacterTextSplitter需要关注的是4个参数:* separators - 分隔符字符串数组
* chunk_size - 每个文档的字符数量限制
* chunk_overlap - 两份文档重叠区域的长度
* length_function - 长度计算函数
'''
#导入文本分割器
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 知识库中单段文本长度
CHUNK_SIZE = 500# 知识库中相邻文本重合长度
OVERLAP_SIZE = 50
# 使用递归字符文本分割器 通过递归的方式进行,意味着它会重复地将文本分割,直到满足特定的条件
text_splitter = RecursiveCharacterTextSplitter(chunk_size=CHUNK_SIZE,chunk_overlap=OVERLAP_SIZE
)
text_splitter.split_text(pdf_page.page_content[0:1000])
# 使用递归字符文本分割器 通过递归的方式进行,意味着它会重复地将文本分割,直到满足特定的条件
text_splitter = RecursiveCharacterTextSplitter(chunk_size=CHUNK_SIZE,chunk_overlap=OVERLAP_SIZE
)
text_splitter.split_text(pdf_page.page_content[0:1000])

在这里插入图片描述

print(f"切分后的字符数(可以用来大致评估 token 数):{sum([len(doc.page_content) for doc in split_docs])}")

在这里插入图片描述

4 搭建并使用向量数据库

4.1 自定义embedding封装

langchain内置了llama 千帆等emdedding但并没有包含所有大模型。要实现自定义 Embeddings,需要定义一个自定义类继承自 LangChain 的 Embeddings 基类,然后定义两个函数:① embed_query 方法,用于对单个字符串(query)进行 embedding;②embed_documents 方法,用于对字符串列表(documents)进行 embedding。
封装在.py文件中与调用文件保存在同一目录

from __future__ import annotationsimport logging
from typing import Dict, List, Anyfrom langchain.embeddings.base import Embeddings
from langchain.pydantic_v1 import BaseModel, root_validatorlogger = logging.getLogger(__name__)class ZhipuAIEmbeddings(BaseModel, Embeddings):"""`Zhipuai Embeddings` embedding models."""client: Any"""`zhipuai.ZhipuAI"""@root_validator()def validate_environment(cls, values: Dict) -> Dict:"""实例化ZhipuAI为values["client"]Args:values (Dict): 包含配置信息的字典,必须包含 client 的字段.Returns:values (Dict): 包含配置信息的字典。如果环境中有zhipuai库,则将返回实例化的ZhipuAI类;否则将报错 'ModuleNotFoundError: No module named 'zhipuai''."""from zhipuai import ZhipuAIvalues["client"] = ZhipuAI()return valuesdef embed_query(self, text: str) -> List[float]:"""生成输入文本的 embedding.Args:texts (str): 要生成 embedding 的文本.Return:embeddings (List[float]): 输入文本的 embedding,一个浮点数值列表."""embeddings = self.client.embeddings.create(model="embedding-2",input=text)return embeddings.data[0].embeddingdef embed_documents(self, texts: List[str]) -> List[List[float]]:"""生成输入文本列表的 embedding.Args:texts (List[str]): 要生成 embedding 的文本列表.Returns:List[List[float]]: 输入列表中每个文档的 embedding 列表。每个 embedding 都表示为一个浮点值列表。"""return [self.embed_query(text) for text in texts]async def aembed_documents(self, texts: List[str]) -> List[List[float]]:"""Asynchronous Embed search docs."""raise NotImplementedError("Please use `embed_documents`. Official does not support asynchronous requests")async def aembed_query(self, text: str) -> List[float]:"""Asynchronous Embed query text."""raise NotImplementedError("Please use `aembed_query`. Official does not support asynchronous requests")

其中,

Dict:用于注解字典类型。
List:用于注解列表类型。
Any:用于注解时表示可以接受任何类型。
BaseModel是pydantic库中的一个类,用于创建带有数据验证和设置的模型类。root_validator是一个装饰器,用于在模型实例化后立即对所有根级别数据进行验证。
embed_query 是对单个文本(str)计算 embedding 的方法,这里我们重写该方法,调用验证环境时实例化的ZhipuAI来 调用远程 API 并返回 embedding 结果。
embed_documents 是对字符串列表(List[str])计算embedding 的方法,对于这种类型输入我们采取循环方式挨个计算列表内子字符串的 embedding 并返回。

4.2 chroma数据库

加载文档

import os
from dotenv import load_dotenv, find_dotenv# 读取本地/项目的环境变量。
# find_dotenv()寻找并定位.env文件的路径
# load_dotenv()读取该.env文件,并将其中的环境变量加载到当前的运行环境中  
# 如果你设置的是全局的环境变量,这行代码则没有任何作用。
_ = load_dotenv(find_dotenv())# 如果你需要通过代理端口访问,你需要如下配置
# os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:7890'
# os.environ["HTTP_PROXY"] = 'http://127.0.0.1:7890'# 获取folder_path下所有文件路径,储存在file_paths里
file_paths = []
folder_path = '../llm-universe/data_base/knowledge_db'
for root, dirs, files in os.walk(folder_path):   # os.walk()生成一个三元组(root, dirs, files)for file in files:file_path = os.path.join(root, file)file_paths.append(file_path)
print(file_paths[:3])
print(file_paths)

读取文档

from langchain.document_loaders.pdf import PyMuPDFLoader
from langchain.document_loaders.markdown import UnstructuredMarkdownLoader# 遍历文件路径并把实例化的loader存放在loaders里
loaders = []for file_path in file_paths:file_type = file_path.split('.')[-1]if file_type == 'pdf':loaders.append(PyMuPDFLoader(file_path))elif file_type == 'md':loaders.append(UnstructuredMarkdownLoader(file_path))
# 下载文件并存储到text
texts = []for loader in loaders: texts.extend(loader.load())
text = texts[1]
print(f"每一个元素的类型:{type(text)}.", f"该文档的描述性数据:{text.metadata}", f"查看该文档的内容:\n{text.page_content[0:]}", sep="\n------\n")

文档分割

from langchain.text_splitter import RecursiveCharacterTextSplitter# 切分文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)split_docs = text_splitter.split_documents(texts)

构建数据库

# 使用 OpenAI Embedding
# from langchain.embeddings.openai import OpenAIEmbeddings
# 使用百度千帆 Embedding
# from langchain.embeddings.baidu_qianfan_endpoint import QianfanEmbeddingsEndpoint
# 使用我们自己封装的智谱 Embedding,需要将封装代码下载到本地使用
from zhipuai_embedding import ZhipuAIEmbeddings# 定义 Embeddings
# embedding = OpenAIEmbeddings() 
embedding = ZhipuAIEmbeddings()
# embedding = QianfanEmbeddingsEndpoint()# 定义持久化路径
persist_directory = '../llm-universe/data_base/vector_db/chroma'
!rm -rf '../llm-universe/data_base/vector_db/chroma'  # 删除旧的数据库文件(如果文件夹中有文件的话),windows电脑请手动删除
from langchain.vectorstores.chroma import Chromavectordb = Chroma.from_documents(documents=split_docs[:5], # 为了速度,只选择前 20 个切分的 doc 进行生成;使用千帆时因QPS限制,建议选择前 5 个docembedding=embedding,persist_directory=persist_directory  # 允许我们将persist_directory目录保存到磁盘上
)
# 保存
vectordb.persist()
print(f"向量库中存储的数量:{vectordb._collection.count()}")

4.3 向量检索

  1. 余弦相似度
 question="什么是大语言模型"
sim_docs = vectordb.similarity_search(question,k=3)
print(f"检索到的内容数:{len(sim_docs)}")
for i, sim_doc in enumerate(sim_docs):print(f"检索到的第{i}个内容: \n{sim_doc.page_content[:200]}", end="\n--------------\n")

可以看到是相关,但没啥有用的信息。如果只考虑检索出内容的相关性会导致内容过于单一,可能丢失重要信息。所以下面,最大边际相关性 (MMR, Maximum marginal relevance) 可以帮助我们在保持相关性的同时,增加内容的丰富度。核心思想是选择一个相关性高的文章+相关性低但内容丰富的文章。(互补嘛)
2. MMR检索

mmr_docs = vectordb.max_marginal_relevance_search(question,k=5)
for i, sim_doc in enumerate(mmr_docs):print(f"MMR 检索到的第{i}个内容: \n{sim_doc.page_content[:200]}", end="\n--------------\n")

还是感觉不咋有内容

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

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

相关文章

RK3568平台开发系列讲解(调试篇)分析内核调用的利器 ftrace

🚀返回专栏总目录 文章目录 一. 指定 ftrace 跟踪器二、设置要 trace 的函数三、ftrace 的开关四、查看 trace五、trace-cmd 的使用六、trace-cmd 的常用选项6.1、查看可以跟踪的事件6.2、跟踪特定进程的函数调用6.3、函数过滤6.4、限制跟踪深度6.5、追踪特定事件沉淀、分享、…

CesiumJS【Basic】- #016 多边形面渲染“花了”的问题

文章目录 多边形面渲染“花了”的问题1 目标2 问题代码3 修正后代码4 总结多边形面渲染“花了”的问题 1 目标 解决多边形的面“花了”的问题 2 问题代码 使用Cesium.PerInstanceColorAppearance渲染后出现色斑 import * as Cesium from "cesium";const viewer …

防火墙双机热备

防火墙双机热备 随着移动办公、网上购物、即时通讯、互联网金融、互联网教育等业务蓬勃发展,网络承载的业务越来越多,越来越重要。所以如何保证网络的不间断传输成为网络发展过程中急需解决的一个问题。 防火墙部署在企业网络出口处,内外网之…

Orangepi Zero2使用外设驱动库wiringOP配合时间函数驱动HC-SR04超声波测距模块

目录 一、HC-SR04超声波模块原理和硬件接线 1.1 超声波测距原理: 1.2 超声波时序图: 1.3 HC-SR04超声波模块硬件接线: 二、时间函数 2.1 时间函数gettimeofday()原型和头文件: 2.2 使用gettimeofday()函数获取当前时间的秒数…

【osgEarth】Ubuntu 22.04 源码编译osgEarth 3.5

下载源代码 git clone --depth1 https://dgithub.xyz/gwaldron/osgearth -b osgearth-3.5 下载子模块 git submodule update --init 如果下载不过来,就手动修改下.git/config文件,将子模块的地址替换成加速地址 (base) yeqiangyeqiang-Default-string…

打印一张A4纸多少钱?打印a4多少钱一张

在数字化日益发展的今天,打印服务依然是办公、学习和生活中不可或缺的一部分。对于广大用户来说,了解A4纸打印的价格成为选择打印服务的重要参考因素。那么,A4纸打印到底多少钱一张呢? 在琢贝云打印平台,打印价格非常实…

Arcengine 添加字段时,显示General function failuer

一、现象 Arcengine开发的时候,在addfield(添加字段)操作时,显示General function failuer。如下图所示: 二、问题原因 General function failuer是常规故障,问题原因是文件占用,只要把文件在…

文华wh6均线交易策略多空波段止盈止损提示主图指标公式源码

文华wh6均线交易策略多空波段止盈止损提示主图指标公式源码&#xff1a; EMA120:EMA(C,120); RSV:(CLOSE-LLV(LOW,9))/(HHV(HIGH,9)-LLV(LOW,9))*100; K:SMA(RSV,3,1); D:SMA(K,3,1); J:3*K-2*D; DRAWTEXT(C>EMA120&&J<0,L,多),VALIGN0; DRAWTEXT(C<EMA…

AcWing算法基础课笔记——求组合数3

求组合数Ⅲ 20万组数据&#xff0c; 1 ≤ b ≤ a ≤ 1 0 18 , 1 ≤ p ≤ 1 0 5 1 \le b \le a \le 10^{18}, 1\le p \le 10 ^5 1≤b≤a≤1018,1≤p≤105&#xff0c;使用卢卡斯定理。 卢卡斯定理&#xff1a; C a b ≡ C a m o d p b m o d p C a / p b / p ( m o d p ) C_a…

SSI 注入漏洞

0x00漏洞描述 SSI 英文是 Server Side Includes 的缩写&#xff0c;翻译成中文就是服务器端包含的意思。从技术角度上说&#xff0c;SSI 就是在 HTML 文件中&#xff0c;可以通过注入注释调用的命令或指针。SSI 具有强大的功能&#xff0c;只要使用一条简单的 SSI 命令就可以实…

day2-web安全漏洞攻防-基础-弱口令、HTML注入(米斯特web渗透测试)

day2-web安全漏洞攻防-基础-弱口令、HTML注入&#xff08;米斯特web渗透测试&#xff09; 1&#xff0c;漏洞2&#xff0c;弱口令3&#xff0c;爆破&#xff08;1&#xff09;Burpsuite&#xff08;2&#xff09;攻击类型 4&#xff0c;HTML针剂注入 1&#xff0c;漏洞 挖掘和利…

批量打造怀旧风情:视频批量剪辑将现代视频打造成怀旧经典老视频效果

在繁忙的现代生活中&#xff0c;我们时常怀念那些旧时光&#xff0c;那些充满岁月痕迹的老电影片段。它们不仅记录了一个时代的风貌&#xff0c;更承载了无数人的情感与记忆。你是否想过&#xff0c;将现代的视频素材打造成这种怀旧经典的老视频效果&#xff0c;让每一帧都充满…

【手眼标定】使用kalibr对imu和双目摄像头进行联合标定

使用kalibr对imu和双目摄像头进行联合标定 前言一、IMU标定二、双目摄像头标定三、手眼标定&#xff08;imu和双目摄像头的联合标定&#xff09; 前言 由于本文的imu、双目摄像头都是在ros2环境下开发&#xff0c;数据传输自然也是在ros2中。 但想要使用kalibr进行标定&#x…

骑马与砍杀战团mod制作-基础-军队笔记(一)

骑马与砍杀战团mod制作-基础-军队装备笔记&#xff08;一&#xff09; 资料来源 学习的资料来源&#xff1a; b站【三啸解说】手把手教你做【骑砍】MOD&#xff0c;基础篇&#xff0c;链接为&#xff1a; https://www.bilibili.com/video/BV19x411Q7No?p4&vd_sourcea507…

VOC格式转YOLO格式,xml文件转txt文件简单通用代码

目录 前言 思路介绍 代码 完整代码 拓展代码 前言 很多人在进行目标检测训练时习惯将得到的数据标注为XML文件的VOC格式&#xff0c;或者在网上获取的数据集被标注为XML文件&#xff0c;但是不同的标注工具进行的标注会产生不同的标注xml文件&#xff0c;这里我写了一种通用…

边缘混合计算智慧矿山视频智能综合管理方案:矿山安全生产智能转型升级之路

一、智慧矿山方案介绍 智慧矿山是以矿山数字化、信息化为前提和基础&#xff0c;通过物联网、人工智能等技术进行主动感知、自动分析、快速处理&#xff0c;实现安全矿山、高效矿山的矿山智能化建设。旭帆科技TSINGSEE青犀基于图像的前端计算、边缘计算技术&#xff0c;结合煤…

Java预约家政5.0服务本地服务源码(APP+小程序+公众号+H5)

预约家政本地服务平台系统&#xff1a;一站式解决家居需求&#x1f3e0;&#x1f4bc; 一、引言&#xff1a;开启便捷家居新时代 在快节奏的现代生活中&#xff0c;我们渴望拥有更多的时间和精力去享受生活&#xff0c;而不是被繁琐的家务所困扰。预约家政本地服务平台系统应…

创意设计师,如何在AIGC时代寻找价值?

在当今AIGC&#xff08;人工智能生成内容&#xff09;时代&#xff0c;技术的浪潮席卷了各个行业&#xff0c;创意设计领域也不例外。对于创意设计师来说&#xff0c;这既是一个充满挑战的时代&#xff0c;也是一个蕴藏无限机遇的时代。在这个时代背景下&#xff0c;如何寻找并…

MindSpore中NumPy变量转换为Tensor张量使用的Tensor.from_numpy()函数到底是深拷贝还是浅拷贝

在NumPy转换为Tensor使用的Tensor.from_numpy()函数到底是深拷贝还是浅拷贝 使用Tensor()将NumPy变量转换为Tensor变量。 类似数组转换张量的方法 n np.ones(5) t Tensor.from_numpy(n) print(f"t: {t}", type(t)) np.add(n, 1, outn) print(f"n: {n}"…

PHP 界的扛把子 Swoole 异步通信利器

大家好&#xff0c;我是码农先森。 引言 我今天主要介绍的内容是包括但不仅限于 Swoole &#xff0c;也有一部分 Go 语言的内容。 为什么要介绍 Swoole ? 先说一说背景吧&#xff0c;我们项目组之前要为《香港 01》开发一个积分系统的项目&#xff0c;这个系统的主要功能包…