解决LangChain构建知识向量库的过程中官方API无法自定义文本切割方式的问题-例如按行切分

自定义切分构成知识向量库的文本o( ̄▽ ̄)ブ

在使用大模型和知识向量库进行问题问答的过程中,由于一些LangChain切分文本功能上的限制影响了模型的回答效果为了解决该问题故诞生此文档,如果有说的不对的,或者想交流的非常欢迎交流指导。


理解本文内容可能需要前置的基础知识基础如果您对内容没有详细讲解的地方有疑问,请移步以下愚作:

1.调用阿里通义千问大语言模型API-小白新手教程-python

2.LangChain结合通义千问的自建知识库

3.使用LangChain结合通义千问API基于自建知识库的多轮对话和流式输出

文章目录

  • 自定义切分构成知识向量库的文本o(* ̄▽ ̄*)ブ
    • 问题分析(需求来源)🧐
    • 切分对比测试
    • 对话对比测试
    • 完整代码💻(有基础可以直接看代码)
    • 总结+可能会被问的问题的回答
      • * Q1:LangChain不支持按行切分吗?
      • * Q2:RecursiveCharacterTextSplitter没有设置按行切分的选项吗?
      • * Q3:你的代码运行效率是不是会低

问题分析(需求来源)🧐

首先在构建知识向量库的过程中读取和切分文档中通常官方给的导入和切分文本用的是以下代码。那么在使用该方法导入文档的时候存在着几个问题。

# 导入文本
loader = UnstructuredFileLoader("test.txt")
data = loader.load()

# 文本切分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=0)
split_docs = text_splitter.split_documents(data)
  1. 只能设置最大样本长度,经常会把一句话切的半残,那在搜索相似本文的时候就会出现以下尴尬情况。
    例如:句子:该用户的名字是爱新觉罗斯通斯。
    切分文本1:该用户的名字是 切分文本2:爱新觉罗斯通斯
    然后你给大模型输入问用户的名字通过embedding模型只能找到切分文本1然后找不到切分文本2那根据搜索到的内容进行总结回答的结果肯定也不对。如果情况非常简单是可以直接通过切分长度和重叠部分来调整的,但是情况多了那肯定就不对了。

  2. 有时候会将一些字符转换成’\n
    例如:网址:www.chen-hao.blog.csdn.net
    读取之后变成:www.chen\nhao.blog.csdn.net
    然后最后总结的时候他总结成: www.chen.hao.blog.csdn.net
    这样的话网站怎么点也点不进去啊

因此为了解决这个问题需要自定义切分函数,从而避免半残切分和其对一些字符的替换。

替换字符的本意最大限避免一些非法字符引起大模型的输入错误,据简单了解是这样

接下来直接给出替换代码,这是一个按行切分的示例,在读取txt的时候将每一行实例化成一个Document类。
split_docs的本质是一个元素都为Document类的列表,因此值要自己生成这个列表就好了
Q:那为什么一定要转换成这个格式呢,不能直接输入字符串列表呢
A:因为在使用Chroma.from_documents(doc, embeddings,persist_directory="./test/news_test")生成知识向量库的时候里面的Doc必须得是这个格式

# 实例化Docment类
class Document:
    def __init__(self,text):
        self.page_content = text
        self.metadata = {'source':'Own'}

# 按行读取
with open('test.txt','r',encoding='utf-8') as file:
    lines = file.readlines()

doc = [Document(line.strip()) for line in lines]

切分对比测试

OK 我们还是使用之前博客里使用的文本。

CSDN中浩浩的科研笔记博客的作者是陈浩
博客的地址为 www.chen-hao.blog.csdn.net
其原力等级为5级,在其学习评价中,其技术能力超过了99.6%的同码龄作者,且超过了97.9%的研究生用户。
该博客中包含了,单片机,深度学习,数学建模,优化方法等,相关的博客信息,其中访问量最多的博客是《Arduino 让小车走实现的秘密 增量式PID 直流减速编码电机》。
其个人能力主要分布在Python,和Pytorch方面,其中python相对最为擅长,希望可以早日成为博客专家。

首先来看一下两种方式切分出的效果

  • 使用RecursiveCharacterTextSplitter切分(有些情况下会出现)
from langchain_community.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

loader = UnstructuredFileLoader("test.txt")
data = loader.load()

# 文本切分
text_splitter = RecursiveCharacterTextSplitter(chunk_size=50, chunk_overlap=0)
split_docs = text_splitter.split_documents(data)


for i in split_docs:
    print("切分文本:",i.page_content)
'''
切分文本: CSDN中浩浩的科研笔记博客的作者是啊浩

博客的地址为 www.chen
切分文本: hao.blog.csdn.net
切分文本: 其原力等级为5级,在其学习评价中,其技术能力超过了99.6%的同码龄作者,且超过了97.9%的研究
切分文本: 生用户。
切分文本: 该博客中包含了,单片机,深度学习,数学建模,优化方法等,相关的博客信息,其中访问量最多的博客是《A
切分文本: rduino
切分文本: 让小车走实现的秘密 增量式PID 直流减速编码电机》。
切分文本: 其个人能力主要分布在Python,和Pytorch方面,其中python相对最为擅长,希望可以早日
切分文本: 成为博客专家。

'''

在这里插入图片描述

  • 本文方法切分出的效果
class Document:
    def __init__(self,text):
        self.page_content = text
        self.metadata = {'source':'Own'}


with open('test.txt','r',encoding='utf-8') as file:
    lines = file.readlines()

doc = [Document(line.strip()) for line in lines]


for i in doc:
    print("切分文本:",i.page_content)
'''
切分文本: CSDN中浩浩的科研笔记博客的作者是啊浩
切分文本: 博客的地址为 www.chen-hao.blog.csdn.net
切分文本: 其原力等级为5级,在其学习评价中,其技术能力超过了99.6%的同码龄作者,且超过了97.9%的研究生用户。
切分文本: 该博客中包含了,单片机,深度学习,数学建模,优化方法等,相关的博客信息,其中访问量最多的博客是《Arduino 让小车走实现的秘密 增量式PID 直流减速编码电机》。
切分文本: 其个人能力主要分布在Python,和Pytorch方面,其中python相对最为擅长,希望可以早日成为博客专家。
'''

  • 非文件导入版本简单版示例

document = '''CSDN中浩浩的科研笔记博客的作者是啊浩
博客的地址为 www.chen-hao.blog.csdn.net
其原力等级为5级,在其学习评价中,其技术能力超过了99.6%的同码龄作者,且超过了97.9%的研究生用户。
该博客中包含了,单片机,深度学习,数学建模,优化方法等,相关的博客信息,其中访问量最多的博客是《Arduino 让小车走实现的秘密 增量式PID 直流减速编码电机》。
其个人能力主要分布在Python,和Pytorch方面,其中python相对最为擅长,希望可以早日成为博客专家。'''


class Document:
    def __init__(self,text):
        self.page_content = text
        self.metadata = {'source':'Own'}


doc = [Document(line.strip()) for line in document.split('\n')]


for i in doc:
    print("切分文本:",i.page_content)

'''
切分文本: CSDN中浩浩的科研笔记博客的作者是啊浩
切分文本: 博客的地址为 www.chen-hao.blog.csdn.net
切分文本: 其原力等级为5级,在其学习评价中,其技术能力超过了99.6%的同码龄作者,且超过了97.9%的研究生用户。
切分文本: 该博客中包含了,单片机,深度学习,数学建模,优化方法等,相关的博客信息,其中访问量最多的博客是《Arduino 让小车走实现的秘密 增量式PID 直流减速编码电机》。
切分文本: 其个人能力主要分布在Python,和Pytorch方面,其中python相对最为擅长,希望可以早日成为博客专家。
'''

对话对比测试

首先我们定义三个问题

  • Q1:作者访问量最多的博客是什么
  • Q2:作者希望可以早日成为什么
  • Q2:博客网址是什么

首先是官方切分方式,生成知识向量库的完整代码如下,代码详细讲解见同专栏前作。

from langchain_community.vectorstores import Chroma
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
import sentence_transformers
from langchain_community.document_loaders import UnstructuredFileLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 导入文本
from dashscope import Generation
from dashscope.api_entities.dashscope_response import Role
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings

messages = []

model_name = r"Model\bce-embedding-vase_v1"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}
embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)

# 这里选择官方切割方式
db = Chroma(persist_directory="./official", embedding_function=embeddings)

while True:
    message = input('user:')

    similarDocs = db.similarity_search(message, k=5)
    summary_prompt = "".join([doc.page_content for doc in similarDocs])

    send_message = f"下面的信息({summary_prompt})是否有这个问题({message})有关,如果你觉得无关请告诉我无法根据提供的上下文回答'{message}'这个问题,否则请根据{summary_prompt}{message}的问题进行回答"
    messages.append({'role': Role.USER, 'content': send_message})
    whole_message = ''
    # 切换模型
    responses = Generation.call(Generation.Models.qwen_max, messages=messages, result_format='message', stream=True, incremental_output=True)
    # responses = Generation.call(Generation.Models.qwen_turbo, messages=messages, result_format='message', stream=True, incremental_output=True)
    print('system:',end='')
    for response in responses:
        whole_message += response.output.choices[0]['message']['content']
        print(response.output.choices[0]['message']['content'], end='')
    print()
    messages.append({'role': 'assistant', 'content': whole_message})

结果这里面网址回答对了是因为我后来我又生成了几遍,最后生成的时候他不把’-'转换成’\n‘了,但是其他问题可以看出来,这种切分方式会出现很奇怪的问题,你也不知道这个切分的长度设置成多少合适。

在这里插入图片描述

本文的切分方式的到的知识向量库进行对话代码如下

from dashscope import Generation
from dashscope.api_entities.dashscope_response import Role
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings

messages = []

model_name = r"Model\bce-embedding-vase_v1"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}
embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
db = Chroma(persist_directory="./my", embedding_function=embeddings)

while True:
    message = input('user:')

    similarDocs = db.similarity_search(message, k=5)
    summary_prompt = "".join([doc.page_content for doc in similarDocs])

    send_message = f"下面的信息({summary_prompt})是否有这个问题({message})有关,如果你觉得无关请告诉我无法根据提供的上下文回答'{message}'这个问题,否则请根据{summary_prompt}{message}的问题进行回答"
    messages.append({'role': Role.USER, 'content': send_message})
    whole_message = ''
    # 切换模型
    responses = Generation.call(Generation.Models.qwen_max, messages=messages, result_format='message', stream=True, incremental_output=True)
    # responses = Generation.call(Generation.Models.qwen_turbo, messages=messages, result_format='message', stream=True, incremental_output=True)
    print('system:',end='')
    for response in responses:
        whole_message += response.output.choices[0]['message']['content']
        print(response.output.choices[0]['message']['content'], end='')
    print()
    messages.append({'role': 'assistant', 'content': whole_message})

可以看到回答基本都是合理的,通过这种方式就可以保障知识向量库中每一个知识向量来源的文本的完整性。
在这里插入图片描述

完整代码💻(有基础可以直接看代码)

完整代码分两部分,一部分是自定义切分,生成知识向量库,一部分是对话代码。

第一部分

from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma

text_name = 'test.txt'

class Document:
    def __init__(self,text):
        self.page_content = text
        self.metadata = {'source':'Own'}


with open('test.txt','r',encoding='utf-8') as file:
    lines = file.readlines()

doc = [Document(line.strip()) for line in lines]



model_name = r"Model\bce-embedding-vase_v1"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}
embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)


db = Chroma.from_documents(doc, embeddings,persist_directory="./my")
# 持久化
db.persist()

第二部分对话部分

from dashscope import Generation
from dashscope.api_entities.dashscope_response import Role
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings.huggingface import HuggingFaceEmbeddings

messages = []

model_name = r"Model\bce-embedding-vase_v1"
model_kwargs = {'device': 'cpu'}
encode_kwargs = {'normalize_embeddings': False}
embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs=model_kwargs,
    encode_kwargs=encode_kwargs
)
db = Chroma(persist_directory="./my", embedding_function=embeddings)

while True:
    message = input('user:')

    similarDocs = db.similarity_search(message, k=5)
    summary_prompt = "".join([doc.page_content for doc in similarDocs])

    send_message = f"下面的信息({summary_prompt})是否有这个问题({message})有关,如果你觉得无关请告诉我无法根据提供的上下文回答'{message}'这个问题,否则请根据{summary_prompt}{message}的问题进行回答"
    messages.append({'role': Role.USER, 'content': send_message})
    whole_message = ''
    # 切换模型
    responses = Generation.call(Generation.Models.qwen_max, messages=messages, result_format='message', stream=True, incremental_output=True)
    # responses = Generation.call(Generation.Models.qwen_turbo, messages=messages, result_format='message', stream=True, incremental_output=True)
    print('system:',end='')
    for response in responses:
        whole_message += response.output.choices[0]['message']['content']
        print(response.output.choices[0]['message']['content'], end='')
    print()
    messages.append({'role': 'assistant', 'content': whole_message})

总结+可能会被问的问题的回答

这是一个基础示例,内容上面对具体的业务场景一定可以进一步完善,例如自己设置一些分隔符切分,总之只要处理成自己需要的字符串就可以了,主要是给出一个通用的基础和理清一下概念。然后我猜测以下可能会被问的一些问题然后提前解答。

* Q1:LangChain不支持按行切分吗?

也许支持,可能是我没太找到,如果有非常欢迎交流,有更好的办法的话也可以有偿。
在这里插入图片描述

* Q2:RecursiveCharacterTextSplitter没有设置按行切分的选项吗?

我其实主要关注到的它其实只有设置最大样本长度和重叠长度这种,我尝试过直接让两个参数默认,那他会把整个文档作为一个向量返回。
在这里插入图片描述
在这里插入图片描述

* Q3:你的代码运行效率是不是会低

结论是我的代码运行的会快一些,其实如果有经验的的话一看就能明白,我的代码相当于把他的一大堆过程全省略了,各种特殊判断加载诸如此类,相当于直接把他底层的给直接抽出来,但是talk is cheap show me the code 是吧。
首先一顿复制把文本复制到1000行
在这里插入图片描述

然后分别测试两个代码在加载文档到生成Document列表的时间

首先是官方API用时2.0625s
在这里插入图片描述
本文方法用时间不到0.0001s所以显示不出来还是0,当然功能上肯定不如官方API完善,随着判断的情况的增加诸如此类可能会时间增加,但是总体上绝对不会因为算法的效率被否决。
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue3中computed、watch、watchEffect的区别

三者都是侦听工具,实现的是观察者模式,横向对比 (1)依赖:指的是响应性依赖,也就是侦听 ref、reactive 这类具有响应性的对象。 (2)watch:默认情况下,被侦听对…

Spring AOP常见面试题

目录 一、对于AOP的理解 二、Spring是如何实现AOP的 1、execution表达式 2、annotation 3、基于Spring API,通过xml配置的方式。 4、基于代理实现 三、Spring AOP的实现原理 四、Spring是如何选择使用哪种动态代理 1、Spring Framework 2、Spring Boot 五…

CUDA入门之统一内存

原文来自CUDA 编程入门之统一内存 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC)开发基础教程 🎀CSDN主页 发狂的小花 🌄人生秘诀:学习的本质…

X64 页表结构

PML4(Page Map Level 4)是x86-64架构中用于管理虚拟内存地址翻译的四级页表结构之一。它是一种树形结构,由多个页目录表(Page Directory Pointer Table,PDPT)组成,每个PDPT有512个指向下一级页表…

低功耗DC-DC电压调整器IU5528D

IU5528D是一款超微小型,超低功耗,高效率,升降压一体DC-DC调整器。适用于双节,三节干电池或者单节锂电池的应用场景。可以有效的延长电池的使用时间。IU5528D由电流模PWM控制环路,误差放大器,比较器和功率开关等模块组成。该芯片可在较宽负载范围内高效稳…

1_springboot_shiro_jwt_多端认证鉴权_Shiro入门

1. Shiro简介 Shiro 是 Java 的一个安全框架,它相对比较简单。主要特性: Authentication(认证):用户身份识别,通常被称为用户“登录”,即 “你是谁”Authorization(授权&#xff…

webpack-dev-server5.0+ 版本问题

webpack-dev-server版本选择 在使用webpack-dev-server搭建新项目时,需要依赖node 和webpack以及webpack-cli 这是需要注意各个应用之间的版本问题 通过npm官网查看webpack-dev-server使用的版本依赖对象 先看package.json,可以看到当前的版本 再找到依…

YOLOv9使用训练好的权重检测目标

打开yolov9-main\detect.py文件 1修改为训练后权重文件的位置 2改为要检测图片的位置 3修改成数据集的yaml文件 运行detect.py文件并解决报错 打开报错文件yolov9-main\utils\general.py,在prediction = prediction[0]后边加上[0] 继续运行detect.py,成功检测 存在问题 当…

9个免费游戏后端平台

在这篇文章中,您将看到 九个免费的游戏服务平台提供商,这可以帮助您开始在线多人游戏,而无需预先投入大量资金。 每个提供商都有非常独特的功能,因此成本应该只是决定时要考虑的方面之一。 我还从低预算项目的角度对免费提供商进…

如何在Ubuntu系统部署DbGate数据库管理工具并结合cpolar内网穿透远程访问

文章目录 1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数据库管理工…

WordPress供求插件API文档:获取市场类型

请注意,该文档为: WordPress供求插件:一款专注于同城生活信息发布的插件-CSDN博客文章浏览阅读396次,点赞6次,收藏5次。WordPress供求插件:sliver-urban-life 是一款专注于提供同城生活信息发布与查看的插件…

CSS伪类与常用标签属性整理与块级、行级、行级块标签(文本,背景,列表,透明,display)

目录 文本 color:字体颜色 font-size:字体大小​编辑 front-family:字体 text-align:文本对齐 text-decoration:line-through:定义穿过文本下的一条线 text-decoration:underline:定义文本下的一条线…

mysqld.exe运行时,提示缺少msvcr100.dll,msvcp100.dll文件,导致mysql安装失败或mysql服务无法启动

mysqld.exe运行时,提示缺少msvcr100.dll,msvcp100.dll文件,导致mysql安装失败或无法启动 msvcr100.dll,msvcp100.dll时VC2010的动态链接库。 1、下载地址 https://www.microsoft.com/zh-cn/download/details.aspx?id26999&wd…

027—pandas 不同分类每天指定取值的比例

前言 本例我们将进行分组计算,分组后得到一个堆叠数据,并对堆叠数据解除堆叠,最后再按要求格式化为百分数样式。 此类操作会经常发生在业务数据透视场景下,一般都会有 Excel 来操作完成,今天我们使用 Python 的 panda…

钉钉扫码登录,sdk问题

别问我为啥会写这玩意。因为有人问到了 1.钉钉扫码登录,网上代码一大堆,但是小同学在抄的时候突然问我,为啥jar包倒入不了。pom添加了,镜像也是阿里的,还是不行 下载了包,按这个放啊发去操作就好了 1.先…

1.Datax数据同步之Windows下,mysql数据同步至另一个mysql数据库

目录 前言步骤操作大纲步骤明细其他问题 前言 Datax是什么? DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,实现包括 MySQL、SQL Server、Oracle、PostgreSQL、HDFS、Hive、HBase、OTS、ODPS 等各种异构数据源之间高效的数据同步功能。准备…

Hand 3D相关

看到一个不错的文献总结网址,如下 GitHub - SeanChenxy/Hand3DResearch 涉及的内容如下图:

基于SSM框架的民族文化传承与乡村扶贫网站设计与实现【附项目源码】分享

民族文化传承与乡村扶贫网站设计与开发: 源码地址:https://download.csdn.net/download/qq_41810183/88842794 一、引言 随着信息技术的飞速发展,互联网已成为文化传播与经济发展的重要平台。为了有效传承和弘扬民族文化,同时推动乡村地区…

【Linux】Shell编程【一】

shell是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。 Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。 Shell属于内置的…

Docker上部署LPG(loki+promtail+grafana)踩坑复盘

Docker上部署LPG(lokipromtailgrafana)踩坑复盘 声明网上配置部署踩坑多机采集 声明 参考掘金文章:https://juejin.cn/post/7008424451704356872 版本高的用docker compose命令,版本低的用docker-compose 按照文章描述&#xff0c…