0%

文本切分器semchunk的使用

开发基于大模型的RAG应用时,文本拆分是一个非常基础的功能。在拆分一个大文本时我们往往会要求拆分效率高并且拆分后的chunk语义完整,完整的语义对后面检索出精确的内容非常有意义。

semchunk 是一个快速、轻量且易于使用的 Python 库,用于将文本拆分为语义上有意义的块。我们切分文本数据时,可以自定义文本切分逻辑,可以使用 langchain 的 RecursiveCharacterTextSplitter,也可以使用semchunk 来切分文本。

semchunk的原理是:递归地拆分文本,直到所有生成的块都等于或小于指定的块大小。

  1. 使用语义上最有意义的分割器来分割文本;
  2. 递归地分割生成的块,直到产生一组等于或小于指定块大小的块;
  3. 将小于块大小的所有块合并在一起,直到达到块大小;
  4. 如果这样做不会使块超过块大小,则将所有非空白分割器重新附加到块的末尾(最后一个块除外),否则将非空白分割器添加为它们自己的块。

为了确保块尽可能具有语义意义, semchunk 按优先顺序使用以下分割器:

  1. 最大的换行符序列( \n )和/或回车符序列( \r );
  2. 最大的标签序列;
  3. 最大的空白字符序列(由正则表达式的 \s 字符类定义),或者,自版本 3.2.0 起,如果最大的空白字符序列只有一个字符,并且存在以任何语义上有意义的非空白字符开头的空白字符下面列出(按相同的优先级顺序),那么只有那些特定的空白字符;
  4. 句子终止符( .?!* );
  5. 子句分隔符( ; , ()[]'''" 和 ``` );
  6. 句子中断词( : );
  7. 单词连接符( /\&- );以及
  8. 所有其他字符。

如果请求了重叠的块, semchunk 还会:

  1. 在内部将块大小减小到 min(overlap, chunk_size - overlap) (对于相对重叠, overlap 计算为 floor(chunk_size * overlap) ,对于绝对重叠,重叠计算为 min(overlap, chunk_size - 1) );并且
  2. 从第一个块开始合并每 floor(original_chunk_size / reduced_chunk_size) 个块,然后跳过 floor((original_chunk_size - overlap) / reduced_chunk_size) 个块,直到到达最后一个块。

安装:

1
pip install semchunk -i https://pypi.tuna.tsinghua.edu.cn/simple

使用 chunkerify() 方法返回一个切分器实例,并用这个实例切分文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import semchunk
import tiktoken


# 每个chunk的最大token数
chunk_size = 10


# 创建分块器对象 memoize: 是否缓存token计数器 cache_maxsize: 缓存的token计数器中最大的token-text对的数量,默认是None,表示无限制,只有当memoize为True时才生效
chunker = semchunk.chunkerify(tiktoken.encoding_for_model('gpt-4'), chunk_size)
# chunker = semchunk.chunkerify('gpt-4', chunk_size)
# chunker = semchunk.chunkerify('cl100k_base', chunk_size)


# 切分单个文本
text = '滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢。古今多少事,都付笑谈中。'
ret = chunker(text)
print(ret)

# 切分多个文本
text = ['滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢。古今多少事,都付笑谈中。',
'试看书林隐处,几多俊逸儒流。虚名薄利不关愁,裁冰及剪雪,谈笑看吴钩']
# progress: 是否显示进度条 processes: 使用多个进程切分文本 offsets: 是否显示每个chunk起始和结束的偏移量 overlap: 每个chunk重叠的token数
ret = chunker(text, progress=True, proc:wqesses=2, offsets=True, overlap=4)
print(ret)

使用 chunk() 方法切分文本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import semchunk
import tiktoken


# 每个chunk的最大token数
chunk_size = 10


def calc_text_tokens(data: str) -> int:
encoding = tiktoken.get_encoding("cl100k_base")
return len(encoding.encode(data))


# 切分文本 token_counter: 计算字符串token数的可调用对象
text = '滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。青山依旧在,几度夕阳红。白发渔樵江渚上,惯看秋月春风。一壶浊酒喜相逢。古今多少事,都付笑谈中。'
ret = semchunk.chunk(text, chunk_size=chunk_size, token_counter=calc_text_tokens,
offsets=True, overlap=4, memoize=True, cache_maxsize=20)
print(ret)

semchunk的github地址:https://github.com/isaacus-dev/semchunk