觉得langchain的文本分割器种类与参数难以界定?自己动手编写文本分割函数!

觉得langchain的文本分割器种类与参数难以界定?自己动手编写文本分割函数!

a.背景信息:将一个chatgpt所需的参考下文(文档问答+基础数据)进行文本分割

参考下文的文档信息(节选)如下:
在这里插入图片描述
参考下文的基础数据信息(节选)如下:
在这里插入图片描述

a. 使用langchain文本分割出的文本块问题:

在这里插入图片描述

首先,经过langchain的TokenTextSplitter分割器去分割,会造成的问题之一为,size的不均匀,有些块会过小。(即文本块的方差较大)且如果按照tokens计算的方式去划分的话造成问题:

  1. 一个问题的回答可能会被分成两部分,导致第二部分不匹配任何问题的答案,就会变为无关信息。
  2. 因为我自己最终采用的策略是将最相关的4个文本块进行拼接作为下为提供给chatgpt,是按照最大的tokens走的,如果文本块的tokens过小,会使得chatgpt参考的上下文过少从而导致问题回答出现偏差。
b. 如何解决?

为了解决以上问题,决定自己编写文本分割函数。

​ 首先为一个计算tokens的函数:

import tiktoken
def tokens_count(text:str):
    encoder=tiktoken.encoding_for_model("gpt-3.5-turbo")
    tokens=len(encoder.encode(text))
    return tokens

​ 利用encoder=tiktoken.encoding_for_model得到大语言模型的编码器,使用它进行编码得到的是字符串序列转化为的tokens序列

token序列解释:

​ token序列为一个大列表其中列表的每个元素代表一个token,(一个token可能代表一个字符,也可能代表多个字符,也能多个token代表一个字符)

​ 如果在切片解码过程中,多个token代表的一个字符被分开的话,会在末尾产生’�’字符。

from langchain.schema.document import Document
def my_split(path, block_length, stop_list, overlap_range):
    with open(path, "r", encoding="utf-8") as f:
        text = f.readlines()
    text_str = str()
    for i in range(len(text)):
        text_str += text[i]
    
    encoder=tiktoken.encoding_for_model("gpt-3.5-turbo")
    encode_text_str=encoder.encode(text_str)
    blocks = []
    start_idx = 0
    remaining_tokens=tokens_count(text_str)
    remaining_str=copy(text_str)
    #文本分块
    while remaining_tokens!=0:
        #print("dfd")
        if block_length >remaining_tokens:
            blocks.append(remaining_str)
            break
        temp_str=encoder.decode(encode_text_str[:block_length-overlap_range])[:-1]
        remaining_str=remaining_str[len(temp_str):]
        encode_text_str=encoder.encode(remaining_str)
        toprange_str=encoder.decode(encode_text_str[:overlap_range])
        stop_idx = None
        for j in stop_list:
            for i in range(len(toprange_str) - 1, -1, -1):
                if toprange_str[i] == j:
                    stop_idx = i
                    break
        if stop_idx:
            temp_str+=toprange_str[:stop_idx+1]
            remaining_str=remaining_str[stop_idx+1:]
            encode_text_str=encoder.encode(remaining_str)
            blocks.append(temp_str) 
        else:
            temp_str+=toprange_str
            remaining_str=remaining_str[len(toprange_str):]
            encode_text_str=encoder.encode(remaining_str)
            blocks.append(temp_str)
        remaining_tokens=tokens_count(remaining_str)
    #去掉标记并形成document对象
    for i in range(len(blocks)):
        blocks[i] = blocks[i].replace('#', '')
        blocks[i] = Document(page_content=blocks[i], metadata={"source": path})
    return blocks
A. 标记:

在这里插入图片描述

  1. 在文本设置’#‘作为最高切分优先级标记,即将按照’#'进行切分的文本块,作为一个完美切分。完美切分即一个文本块内所有的问题都有一个完整的回复。
  2. 优先级标记是需要根据自己的文本内容进行自定义的
B. 参数解释:

path: 为原始文本路径

block_length:一个文本块的最大tokens

stop_list: 优先级标记切分列表

overlap_range: block_length-overlap_range为一个文本块的最小tokens,即在**[block_length-overlap_range,block_length]**范围内按照标记优先级进行检索,找到最优切分点。

C. 运行流程:
1. 初始化
  1. 首先读取文件得到文件字符串:text_str

  2. 之后设置

remaining_str作为剩余的字符串

encode_text_str作为tokens序列(为剩余字符串的)

remaining_tokens作为剩余tokens

2. 循环:
  1. 条件: remaining_tokens!=0

  2. 一个循环内流程:

    1. encoder.decode(encode_text_str[:block_length-overlap_range])[:-1] 作为确定字符串(-1是为了取消’�’字符造成的影响)

    2. toprange_str=encoder.decode(encode_text_str[:overlap_range]) 作为判断是否存在切分点的未确定字符串

    3. for j in stop_list:
                  for i in range(len(toprange_str) - 1, -1, -1):
                      if toprange_str[i] == j:
                          stop_idx = i
                          break
      

      为了让tokens最接近最大tokens,使用倒序遍历字符串,找到最优先标记并返回索引

    4. 最终判断:

       if stop_idx:
                  temp_str+=toprange_str[:stop_idx+1]
                  remaining_str=remaining_str[stop_idx+1:]
                  encode_text_str=encoder.encode(remaining_str)
                  blocks.append(temp_str) 
              else:
                  temp_str+=toprange_str
                  remaining_str=remaining_str[len(toprange_str):]
                  encode_text_str=encoder.encode(remaining_str)
                  blocks.append(temp_str)
              remaining_tokens=tokens_count(remaining_str)
       
      

      就是将不确定字符串与确定字符串进行拼接,并更新remaining_tokens,remaining_str,encode_text_str

c.最终结果:

在这里插入图片描述

​ 我们由上图可以看出文本块的tokens数值方差较小且由于我们的优先级标记机制,每个文本块都是最完美或是接近切分的。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值