一、前言
在 LangChain 框架中,文档分割器是一种将大段文本拆分成较小块或片段的算法或方法。其目标是创建可单独处理的可管理的片段,这在处理大型文档或数据集时通常是必要的。
而自定义文档分割器可以根据特定的需求和数据类型进行定制和配置,以便更好地适应应用程序的需求。例如,可以根据特定的文档结构或语义相关性进行分割,确保语义相关的内容在同一块中组合在一起。在回答问题或执行依赖于文档中存在的上下文信息的任务时,自定义文档分割器尤为重要。它可以提高大型语言模型处理输入的效率,同时保持信息的语义完整性。通过自定义文档分割器,可以更好地管理和处理文本数据,尤其是在处理大型文档或有特定处理需求时。
二、术语
2.1.LangChain
是一个全方位的、基于大语言模型这种预测能力的应用开发工具。LangChain的预构建链功能,就像乐高积木一样,无论你是新手还是经验丰富的开发者,都可以选择适合自己的部分快速构建项目。对于希望进行更深入工作的开发者,LangChain 提供的模块化组件则允许你根据自己的需求定制和创建应用中的功能链条。
LangChain本质上就是对各种大模型提供的API的套壳,是为了方便我们使用这些 API,搭建起来的一些框架、模块和接口。
LangChain的主要特性:
1.可以连接多种数据源,比如网页链接、本地PDF文件、向量数据库等
2.允许语言模型与其环境交互
3.封装了Model I/O(输入/输出)、Retrieval(检索器)、Memory(记忆)、Agents(决策和调度)等核心组件
4.可以使用链的方式组装这些组件,以便最好地完成特定用例。
5.围绕以上设计原则,LangChain解决了现在开发人工智能应用的一些切实痛点。
2.2.文档分割器
主要作用是将加载的文档分割成更小的片段或块。这是因为在许多自然语言处理任务中,模型通常具有有限的输入长度限制,无法一次性处理整个长文档。文档分割器可以根据特定的规则或策略,将文档分割成适当大小的部分,以便更好地适应模型的处理能力。
三、前提条件
3.1. 基础环境
- 操作系统:不限
3.2. 安装虚拟环境
conda create --name langchain python=3.10
conda activate langchain
pip install langchain jieba
3.3. 准备txt文件
文件名:test.txt
中国乒乓球大满贯赛正如火如荼进行中。率先进行的资格赛中,就有冷门出现,也可见本次赛事的激烈!
咱们都知道,中国大满贯赛是巴黎奥运会之后的首个国际顶级大赛。无论是赛事积分还是奖金都非常高。也因此吸引了很多优秀运动员参赛。再加上赛事规则进行了修改,增加了资格赛的参赛名额,所以从首轮资格赛开始,残酷的淘汰就拉开大幕了!
作为男单资格赛的头号种子,罗马尼亚名将约内斯库的对手是斯洛文尼亚队科祖尔。世界排名第58位的约内斯库,前两局比赛虽然遇到了挑战,但还是顺利以11-8和11-7拿下。大比分2-0,约内斯库迎来了赛点局。关键的第三局比赛,约内斯库一度以8-5领先并以10-8率先拿到局点。也许是胜利在望,约内斯库心态发生了变化。科祖尔出手果断,挽救两个赛点并以12-10逆转扳回一城。第四局比赛,科祖尔乘胜追击,以11-3拿下。双方2-2战平,又回到同一起跑线。决胜局的较量,约内斯库还是展现出来头号种子的实力,在9-4落后的情况下,以10-9拿到赛点。然而科祖尔把握机会能力更强。最终以15-13拿下。这样一来,约内斯库2-3惜败科祖尔,资格赛首轮爆冷,遭遇一轮游。
中国大满贯赛的资格赛,中国乒乓球队有共计14名球员要参加资格赛的争夺。首个比赛日,中国乒乓球队有13名球员出战。他们取得了12胜1负的战绩。输球的球员是中国国乒后起之秀黄友政。在面对沃尔瑟的时候,以0-3溃败。
看比赛过程,更加令人遗憾!首局比赛,沃尔瑟率先进入状态。而黄友政似乎找不到感觉,很快就以5-11告负。来到第二局,黄友政有点起色,但对手的气势更足。黄友政以7-11失利。大比分已经0-2落后,球迷都认为黄友政应该放手一搏了,然而情况和第二局如出一辙。黄友政7-11再输一局。最终黄友政0-3不敌沃尔瑟,资格赛首轮就被淘汰出局。他也是中国乒乓球队出局的第一人。这位被球迷称之为小王楚钦的黄友政,资格赛首轮被淘汰令人失望!值得一提的是,黄友政可是全国冠军,他在去年的时候,帮助北京队3-2击败对手,拿下了全国锦标赛的冠军,成为了全国冠军,可以说他无论是实力还是名气都要高过对手,但是很遗憾,他首轮就爆大冷输球出局!
其他国乒选手中,虽然有的比赛过程不顺利,但最终都顺利进入到下一轮资格赛。也期待他们在接下来的比赛中有更精彩的表现!
四、技术实现
4.1. 需求
- 实现一个自定义文档分割器
- 根据指定分隔符切分文本,对分割后的文本提取指定数量的关键词
4.2.示例一
# -*- coding: utf-8 -*-
from typing import List
import jieba.analyse
from langchain_community.document_loaders import UnstructuredFileLoader
from langchain_text_splitters import TextSplitter
class CustomTextSplitter(TextSplitter):
def __init__(self, seperator: str, top_k: int = 10, **kwargs):
super().__init__(**kwargs)
self._seperator = seperator
self._top_k = top_k
def split_text(self, text: str) -> List[str]:
split_texts = text.split(self._seperator)
text_keywords = []
for split_text in split_texts:
text_keywords.append(
jieba.analyse.extract_tags(split_text, self._top_k)
)
return [",".join(keywords) for keywords in text_keywords]
if __name__ == '__main__':
# 1.加载文档
loader = UnstructuredFileLoader("test.txt")
documents = loader.load()
print(documents)
# 2.加载文档
text_splitter = CustomTextSplitter("\n\n", 5)
chunks = text_splitter.split_documents(documents)
# 3.循环遍历文档信息
for chunk in chunks:
print(chunk.page_content)
调用结果:
说明:
- 部分符号被误识别成换行符,如:11-8识别成11\n\n8
- 文档粒度切分过细
- 返回的关键词包含低价值内容
结论是文档分割效果不符合预期
4.2.示例二
在上一个示例的基础上进行优化
# -*- coding: utf-8 -*-
from typing import List
import jieba.analyse
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import TextSplitter
class CustomTextSplitter(TextSplitter):
def __init__(self, seperator: str, top_k: int = 10, **kwargs):
super().__init__(**kwargs)
self._seperator = seperator
self._top_k = top_k
def split_text(self, text: str) -> List[str]:
# print(f'text:{ text}')
split_texts = text.split(self._seperator)
text_keywords = []
for split_text in split_texts:
text_keywords.append(jieba.analyse.extract_tags(split_text, self._top_k,allowPOS=['ns', 'n', 'vn', 'v','nr']))
return [",".join(keywords) for keywords in text_keywords]
if __name__ == '__main__':
# 1.加载文档
loader = TextLoader("test.txt", encoding="utf-8")
documents = loader.load()
print(documents)
# 2.加载文档
text_splitter = CustomTextSplitter("\n", 5)
chunks = text_splitter.split_documents(documents)
# 3.循环遍历文档信息
for chunk in chunks:
print(chunk.page_content)
调用结果:
说明:
- 使用词性标注过滤低价值的关键词
- 'ns'代表名词中的地名类别
- 'n'代表最基本的名词类别,涵盖各种普通名词
- 'vn'代表具有动词和名词双重特性的词汇
- 'v'代表动作或行为的词汇
- 'nr'代表名词中的人名类别
结论是自定义文档分割器效果符合预期
五、总结
5.1. 使用词性标注过滤低价值的关键词
一、定义
- 词性标注是给文本中的每个单词标注一个特定的词性,如名词、动词、形容词、副词等。词性标注旨在根据单词在句子中的语法功能和语义角色,为其分配一个合适的词性标签。
二、词性标注的意义
- 语法分析
- 在构建语法树时,词性标注是重要的基础。例如,对于句子 “The dog chased the cat”,通过词性标注(“The” - 冠词,“dog” - 名词,“chased” - 动词,“the” - 冠词,“cat” - 名词),可以分析出句子的主谓宾结构,即 “dog”(主语)执行 “chased”(谓语)的动作,对象是 “cat”(宾语)。
- 语义理解
- 有助于理解句子的整体语义。不同词性的单词组合在一起表达特定的意义。例如,在 “美丽的花朵” 中,“美丽的”(形容词)用来修饰 “花朵”(名词),词性标注有助于识别这种修饰关系,从而更好地理解句子的语义。
- 信息检索与文本分类
- 在信息检索中,如果用户搜索 “旅游景点”,系统可以通过对文档进行词性标注,更准确地找到包含名词性短语 “旅游景点” 的文档。在文本分类任务中,词性分布特征也可以作为分类的依据之一。
三、词性标注的方法
- 基于规则的方法
- 规则构建:语言学家根据语法规则制定词性标注的规则。例如,在英语中,以 “- ing” 结尾的单词,在很多情况下是动名词或现在分词形式,就可以制定这样的规则来标注。
- 局限性:需要大量的人工构建规则,而且对于复杂的语言现象和语言的灵活性难以完全覆盖。
- 基于统计的方法
- 语料库利用:使用大量已标注词性的语料库,统计单词在不同语境下的词性分布概率。例如,对于单词 “bank”,在语料库中统计它作为名词(表示 “银行” 或 “河岸”)和动词(表示 “倾斜” 等)的概率。
- 模型训练:常用的模型有隐马尔可夫模型(HMM)、最大熵模型(MEM)、条件随机场(CRF)等。这些模型通过学习语料库中的词性分布规律来对新的文本进行词性标注。
- 基于深度学习的方法
- 神经网络模型:如循环神经网络(RNN)及其变体长短期记忆网络(LSTM)、门控循环单元(GRU),还有卷积神经网络(CNN)以及 Transformer 架构(如 BERT 等)都被用于词性标注任务。
- 预训练与微调:利用大规模的预训练模型,在特定的词性标注任务上进行微调,可以取得较好的效果。例如,先在大规模的通用文本上预训练一个 BERT 模型,然后在特定领域(如医学文献)的词性标注任务上进行微调。
四、常见的词性标注集
- 通用词性标注集
- 在英语中,如 Penn Treebank 词性标注集,其中包含了诸如 “NN”(名词,单数或不可数)、“VB”(动词,原形)、“JJ”(形容词)等标签。
- 在汉语中,像北大的词性标注集,有 “n”(名词)、“v”(动词)、“a”(形容词)等标注。
- 领域特定词性标注集
- 在医学领域,可能会有专门针对医学术语的词性标注集,用于标注医学文献中的单词词性,例如将特定的医学名词、手术动词等进行特殊标注,以满足医学信息处理的需求。