semchunk
semchunk
是一个快速轻量的Python库,用于将文本分割成语义有意义的块。
由于其复杂而高效的分块算法,semchunk
在语义准确性上比langchain.text_splitter.RecursiveCharacterTextSplitter
更优(参见工作原理🔍),同时也比semantic-text-splitter
快90%以上(参见基准测试📊)。
安装📦
可以使用pip
安装semchunk
:
pip install semchunk
使用👩💻
以下代码片段演示了如何使用semchunk
对文本进行分块:
import semchunk
from transformers import AutoTokenizer # `transformers`和`tiktoken`都不是必需的,
import tiktoken # 这里仅用于演示目的。
chunk_size = 2 # 这里使用较小的块大小仅用于演示。请注意,
# 除非使用自定义token计数器,否则`semchunk`不会考虑特殊token,
# 所以你可能需要将块大小减去tokenizer添加的特殊token数量。
text = 'The quick brown fox jumps over the lazy dog.'
# 如下所示,`semchunk.chunkerify`接受所有OpenAI模型名称、OpenAI `tiktoken`编码和
# Hugging Face模型(按此优先顺序),以及具有`encode()`方法的自定义tokenizer
#(如`tiktoken`、`transformers`和`tokenizers`的tokenizer),最后是任何能接受文本
# 并返回其token数量的函数。
chunker = semchunk.chunkerify('umarbutler/emubert', chunk_size) or \
semchunk.chunkerify('gpt-4', chunk_size) or \
semchunk.chunkerify('cl100k_base', chunk_size) or \
semchunk.chunkerify(AutoTokenizer.from_pretrained('umarbutler/emubert'), chunk_size) or \
semchunk.chunkerify(tiktoken.encoding_for_model('gpt-4'), chunk_size) or \
semchunk.chunkerify(lambda text: len(text.split()), chunk_size)
# 生成的`chunker`可以处理单个文本或文本列表,分别返回块列表或块列表的列表。
assert chunker(text) == ['The quick', 'brown', 'fox', 'jumps', 'over the', 'lazy', 'dog.']
assert chunker([text], progress = True) == [['The quick', 'brown', 'fox', 'jumps', 'over the', 'lazy', 'dog.']]
# 如果你有大量文本需要分块,且速度是一个考虑因素,你也可以通过将`processes`设置为大于1的数值来启用多进程。
assert chunker([text], processes = 2) == [['The quick', 'brown', 'fox', 'jumps', 'over the', 'lazy', 'dog.']]
Chunkerify
def chunkerify(
tokenizer_or_token_counter: str | tiktoken.Encoding | transformers.PreTrainedTokenizer | \
tokenizers.Tokenizer | Callable[[str], int],
chunk_size: int = None,
max_token_chars: int = None,
memoize: bool = True,
) -> Callable[[str | Sequence[str], bool, bool], list[str] | list[list[str]]]:
chunkerify()
构建一个分块器,将一个或多个文本分割成由指定tokenizer或token计数器确定大小的语义有意义的块。
tokenizer_or_token_counter
可以是:tiktoken
或transformers
tokenizer的名称(优先考虑前者);具有encode
属性的tokenizer(例如tiktoken
、transformers
或tokenizers
的tokenizer);或返回输入token数量的token计数器。
chunk_size
是一个块可能包含的最大令牌数。它默认为None
,在这种情况下,它将被设置为与分词器的model_max_length
属性相同的值(减去尝试对空字符串进行分词返回的令牌数),如果可能的话;否则将引发ValueError
。
max_token_chars
是一个令牌可能包含的最大字符数。它用于显著加快长输入的令牌计数。它默认为None
,在这种情况下,要么不使用它,要么如果可能,将其设置为分词器词汇表中最长令牌的字符数,由token_byte_values
或get_vocab
方法确定。
memoize
标志是否对令牌计数器进行记忆化。它默认为True
。
此函数返回一个分块器,它接受单个文本或一系列文本,如果提供了单个文本,则返回最多chunk_size
个令牌长的块列表,去除用于分割文本的任何空白;如果提供了多个文本,则返回块的列表列表,每个内部列表对应于所提供输入文本之一的块。
生成的分块器可以传递一个processes
参数,指定在对多个文本进行分块时要使用的进程数。
还可以传递一个progress
参数,如果设置为True
并且传递了多个文本,将显示一个进度条。
从技术上讲,分块器将是semchunk.Chunker
类的一个实例,以协助类型提示,尽管这应该不会影响其使用方式。
分块
def chunk(
text: str,
chunk_size: int,
token_counter: Callable,
memoize: bool = True,
) -> list[str]
chunk()
函数将文本分割成语义上有意义的块,大小由提供的令牌计数器确定。
text
是要分块的文本。
chunk_size
是一个块可能包含的最大令牌数。
token_counter
是一个可调用对象,它接受一个字符串并返回其中的令牌数。
memoize
标志是否对令牌计数器进行记忆化。它默认为True
。
此函数返回最多chunk_size
个令牌长的块列表,去除用于分割文本的任何空白。
工作原理 🔍
semchunk
通过递归分割文本,直到所有结果块等于或小于指定的块大小。具体而言,它:
- 使用可能的最具语义意义的分割器分割文本;
- 递归分割结果块,直到产生一组等于或小于指定块大小的块;
- 将任何小于块大小的块重新合并,直到达到块大小;
- 将任何非空白分割器重新附加到块的末尾(除最后一个块外),如果这样做不会使块超过块大小,否则将非空白分割器作为单独的块添加。
为确保块在语义上尽可能有意义,semchunk
按以下优先顺序使用以下分割器:
- 最大的换行符(
\n
)和/或回车符(\r
)序列; - 最大的制表符序列;
- 最大的空白字符序列(由正则表达式的
\s
字符类定义); - 句子终止符(
.
、?
、!
和*
); - 子句分隔符(
;
、,
、(
、)
、[
、]
、"
、"
、'
、'
、'
、"
和`
); - 句子中断符(
:
、—
和…
); - 单词连接符(
/
、\
、–
、&
和-
); - 所有其他字符。
基准测试 📊
在一台配备Ryzen 3600、64 GB RAM、Windows 11和Python 3.11.9的台式机上,semchunk
花费6.69秒将NLTK的Gutenberg语料库中的每个样本分割成512个令牌长的块,使用GPT-4的分词器(作为参考,该语料库包含18个文本和3,001,260个令牌)。相比之下,semantic-text-splitter
需要116.48秒将相同的文本分割成512个令牌长的块——差异为94.26%。
用于对semchunk
和semantic-text-splitter
进行基准测试的代码可在此处获得。
许可证 📄
该库根据MIT许可证授权。