首页 > 其他分享 >LangChain=>RecursiveCharacterTextSplitter

LangChain=>RecursiveCharacterTextSplitter

时间:2023-11-06 13:45:50浏览次数:43  
标签:RecursiveCharacterTextSplitter chunkSize text chunk List LangChain var new

 

.Net版本LangChain源码:

github.com

RecursiveCharacterTextSplitter调用方法:

var state_of_the_union_txt = "text-Content";
var textSplitter = new RecursiveCharacterTextSplitter(chunkSize: 300, chunkOverlap: 30);
var texts = textSplitter.CreateDocuments(new List<string>() { state_of_the_union_txt });

RecursiveCharacterTextSplitter有4个参数:

public RecursiveCharacterTextSplitter(List<string>? separators=null, int chunkSize = 4000, int chunkOverlap = 200, Func<string, int>? lengthFunction = null) : base(chunkSize, chunkOverlap, lengthFunction)

separators: 可以定义分隔符,如果不指定自定义的分隔符,默认的分隔符为:{ "\n\n", "\n", " ", "" };

chunkSize:段落的长度;

chunkOverlap:段落重叠部分的长度;

主要包含两个方法:SplitText 和 MergeSplits。

using LangChain.Base;

namespace LangChain.TextSplitters;

/// <summary>
/// Implementation of splitting text that looks at characters.
/// Recursively tries to split by different characters to find one
/// that works.
/// </summary>
public class RecursiveCharacterTextSplitter:TextSplitter
{
    private readonly List<string> _separators;

    public RecursiveCharacterTextSplitter(List<string>? separators=null, int chunkSize = 4000, int chunkOverlap = 200, Func<string, int>? lengthFunction = null) : base(chunkSize, chunkOverlap, lengthFunction)
    {
        _separators = separators ?? new List<string> { "\n\n", "\n", " ", "" };
    }

    public override List<string> SplitText(string text)
    {
        List<string> finalChunks = new List<string>();
        string separator = _separators.Last();

        foreach (string _s in _separators)
        {
            if (_s.Length == 0)
            {
                separator = _s;
                break;
            }

            if (text.Contains(_s))
            {
                separator = _s;
                break;
            }
        }

        List<string> splits;
        if (separator.Length!=0)
        {
            splits = text.Split(new string[] {separator}, StringSplitOptions.None).ToList();
        }
        else
        {
            splits = text.ToCharArray().Select(c => c.ToString()).ToList();
        }

    
        List<string> goodSplits = new List<string>();

        foreach (string s in splits)
        {
            if (s.Length < base.ChunkSize)
            {
                goodSplits.Add(s);
            }
            else
            {
                if (goodSplits.Any())
                {
                    List<string> mergedText = MergeSplits(goodSplits, separator);
                    finalChunks.AddRange(mergedText);
                    goodSplits.Clear();
                }

                List<string> otherInfo = SplitText(s);
                finalChunks.AddRange(otherInfo);
            }
        }

        if (goodSplits.Any())
        {
            List<string> mergedText = MergeSplits(goodSplits, separator);
            finalChunks.AddRange(mergedText);
        }

        return finalChunks;
    }
}
RecursiveCharacterTextSplitter

1. SplitText 方法:

SplitText函数的主要功能是将输入的文本字符串按照一定的分隔符进行分割。它首先尝试找到一个合适的分隔符(从 _separators 列表中选择),然后使用这个分隔符来分割文本。如果没有找到合适的分隔符,它会将文本转换为字符数组,并将每个字符作为一个单独的元素。然后,它会检查每个分割的部分,如果长度小于 base.ChunkSize,则将其添加到 goodSplits 列表中。如果长度大于 base.ChunkSize,则将 goodSplits 列表中的元素合并,并将结果添加到 finalChunks 列表中。最后,如果 goodSplits 列表中还有元素,它会再次进行合并,并将结果添加到 finalChunks 列表中。

using LangChain.Docstore;

namespace LangChain.Base;

/// <summary>
/// Functionality for splitting text.
/// <remarks>
/// - ported from langchain/text_splitter.py
/// 
/// </remarks>
/// </summary>
public abstract class TextSplitter
{
    private readonly int _chunkSize;
    private readonly int _chunkOverlap;
    private readonly Func<string, int> _lengthFunction;

    

    protected TextSplitter(int chunkSize = 4000, int chunkOverlap = 200, Func<string,int>? lengthFunction = null)
    {
        if (chunkOverlap > chunkSize)
        {
            throw new ArgumentException($"Chunk overlap ({chunkOverlap}) is greater than chunk size ({chunkSize}).");
        }

        _chunkSize = chunkSize;
        _chunkOverlap = chunkOverlap;
        _lengthFunction = lengthFunction ?? new Func<string, int>((str) => str.Length);
    }

    protected int ChunkSize => _chunkSize;

    protected int ChunkOverlap => _chunkOverlap;

    public abstract List<string> SplitText(string text);

    /// <summary>
    /// Create documents from a list of texts.
    /// </summary>
    /// <exception cref="ArgumentException">
    /// If the number of texts and metadata(when not null) are not equal, this method will throw an ArgumentException.
    /// </exception>
    public List<Document> CreateDocuments(List<string> texts, List<Dictionary<string, object>>? metadatas = null)
    {
        var documents = new List<Document>();

        // if no metadata is provided, create a list of empty dictionaries
        metadatas ??= Enumerable.Repeat(new Dictionary<string, object>(), texts.Count).ToList();

        if (texts.Count != metadatas.Count)
        {
            throw new ArgumentException("Number of texts and metadata must be equal.");
        }


        // each text is split into chunks, and each chunk is added to the list of documents
        for (int i = 0; i < texts.Count; i++)
        {
            var text = texts[i];
            var metadata = metadatas[i];

            foreach (var chunk in SplitText(text))
            {
                documents.Add(new Document(chunk, metadata));
            }
        }

        return documents;
    }

    public List<Document> SplitDocuments(List<Document> documents)
    {
        var texts = documents.Select(doc => doc.PageContent).ToList();
        var metadatas = documents.Select(doc => doc.Metadata).ToList();

        return CreateDocuments(texts, metadatas);
    }

    /// <summary>
    /// Joins a list of strings with a separator and returns null if the resulting string is empty
    /// </summary>
    protected string? JoinDocs(List<string> docs, string separator)
    {
        var text = string.Join(separator, docs).Trim();
        return string.IsNullOrEmpty(text) ? null : text;
    }

    /// <summary>
    /// Merges a list of texts into chunks of size chunk_size with overlap
    /// </summary>
    protected List<string> MergeSplits(IEnumerable<string> splits, string separator)
    {
        var separatorLen = _lengthFunction(separator);
        var docs = new List<string>(); // result of chunks
        var currentDoc = new List<string>(); // documents of current chunk
        int total = 0;

        foreach (var split in splits)
        {
            int len = _lengthFunction(split);
            
            // if we can't fit the next split into current chunk
            if (total + len + (currentDoc.Count>0?separatorLen:0)>= _chunkSize)
            {
                // if the chunk is already was too big
                if (total > _chunkSize)
                {
                    // todo: Implement a logger
                    // todo: Log a warning about a split that is larger than the chunk size
                }


                if (currentDoc.Count > 0)
                {
                    // join all the docs in current chunk and add to the result
                    var doc = JoinDocs(currentDoc, separator);
                    if (doc != null)
                    {
                        docs.Add(doc);
                    }

                    // start erasing docs from the beginning of the chunk until we can fit the next split
                    while (total > _chunkOverlap || (total + len + (currentDoc.Count > 1 ? separatorLen : 0) > _chunkSize && total > 0))
                    {
                        total -= _lengthFunction(currentDoc[0]) + (currentDoc.Count > 1 ? separatorLen : 0);
                        currentDoc.RemoveAt(0);
                    }
                }
            }

            // add the next split to the current chunk
            currentDoc.Add(split);
            total += len + (currentDoc.Count > 1 ? separatorLen : 0); // recalculate the total length of the current chunk
        }

        // add the last chunk
        var lastDoc = JoinDocs(currentDoc, separator);
        if (lastDoc != null)
        {
            docs.Add(lastDoc);
        }

        return docs;
    }

    // todo: Implement from_huggingface_tokenizer
    // todo: Implement from_tiktoken_encoder


}
TextSplitter

2. MergeSplits 方法:
这个方法的主要目的是将输入的分割部分(splits)合并成一个文档。它首先计算分隔符的长度,然后遍历每个分割部分。如果当前文档(currentDoc)加上下一个分割部分的长度大于 _chunkSize,它会将当前文档的元素合并,并将结果添加到 docs 列表中。然后,它会从当前文档的开始部分删除元素,直到可以容纳下一个分割部分。最后,它会将最后一个文档的元素合并,并将结果添加到 docs 列表中。

标签:RecursiveCharacterTextSplitter,chunkSize,text,chunk,List,LangChain,var,new
From: https://www.cnblogs.com/yellow3gold/p/17812433.html

相关文章

  • Langchain-Chatchat项目:4.2-P-Tuning v2使用的数据集
      本文主要介绍P-tuning-v2论文中的5种任务,分别为Glue任务、NER任务、QA任务、SRL任务、SuperGlue任务,重点介绍了下每种任务使用的数据集。一.Glue任务  GLUE(GeneralLanguageUnderstandingEvaluation)是纽约大学、华盛顿大学等机构创建了一个多任务的自然语言理解基准和分......
  • LangChain实战
    1.概述最近,在研究LangChain时,发现一些比较有意思的点,今天笔者将给大家分享关于LangChain的一些内容。2.内容2.1什么是LangChain?LangChain是一项旨在赋能开发人员利用语言模型构建端到端应用程序的强大框架。它的设计理念在于简化和加速利用大型语言模型(LLM)和对话模型构建应用......
  • Langchain语言模型提问请求,提问使用非标准的sse请求获取流式数据,java后台版解决方式
    问题描述:请求后接收的数据流,不走EventSourceListener的onEvent事件,但onOpenonClosed都是正常走的。 问题原因:默认的接口返回是StreamingResponse不是EventSourceResponse,无法走标准sse协议的onEvent()方法 目标需求:在不改动模型方面接口的情况下,接收到流式数据并通过sse协......
  • 使用langchain与你自己的数据对话(一):文档加载与切割
    LangChain是一个基于大语言模型(如ChatGPT)用于构建端到端语言模型应用的Python框架。它提供了一套工具、组件和接口,可简化创建由大型语言模型(LLM)和聊天模型提供支持的应用程序的过程。LangChain可以轻松管理与语言模型的交互,将多个组件链接在一起,以便在不同的应用程序中使用......
  • langchain中的LLM模型使用介绍
    简介构建在大语言模型基础上的应用通常有两种,第一种叫做textcompletion,也就是一问一答的模式,输入是text,输出也是text。这种模型下应用并不会记忆之前的问题内容,每一个问题都是最新的。通常用来做知识库。还有一种是类似聊天机器人这种会话模式,也叫Chatmodels。这种模式下输入是......
  • 基于LangChain的LLM应用开发3——记忆
    此情可待成追忆,只是当时已惘然。我们人类会有很多或美好或痛苦的回忆,有的回忆会渐渐模糊,有的回忆午夜梦醒,会浮上心头。然而现在的大语言模型都是没有记忆的,都是无状态的,大语言模型自身不会记住和你对话之间的历史消息。根本用不着“时时勤拂拭”,天然就是“本来无一物”。每一次的请......
  • 基于LangChain的LLM应用开发3——记忆
    此情可待成追忆,只是当时已惘然。我们人类会有很多或美好或痛苦的回忆,有的回忆会渐渐模糊,有的回忆午夜梦醒,会浮上心头。然而现在的大语言模型都是没有记忆的,都是无状态的,大语言模型自身不会记住和你对话之间的历史消息。根本用不着“时时勤拂拭”,天然就是“本来无一物”。每一次的......
  • langchain
    对超长文本进行总结假如我们想要用openaiapi对一个段文本进行总结,我们通常的做法就是直接发给api让他总结。但是如果文本超过了api最大的token限制就会报错。这时,我们一般会进行对文章进行分段,比如通过tiktoken计算并分割,然后将各段发送给api进行总结,最后将各段的总......
  • Langchain-Chatchat项目:1-整体介绍
      基于Langchain与ChatGLM等语言模型的本地知识库问答应用实现。项目中默认LLM模型改为THUDM/chatglm2-6b[2],默认Embedding模型改为moka-ai/m3e-base[3]。一.项目介绍1.实现原理  本项目实现原理如下图所示,过程包括加载文件->读取文本->文本分割->文本向量化->问句向量化->......
  • Langchain-Chatchat项目:1.1-ChatGLM2项目整体介绍
      ChatGLM2-6B是开源中英双语对话模型ChatGLM-6B的第2代版本,引入新的特性包括更长的上下文(基于FlashAttention技术,将基座模型的上下文长度由ChatGLM-6B的2K扩展到了32K,并在对话阶段使用8K的上下文长度训练);更高效的推理(基于Multi-QueryAttention技术,ChatGLM2-6B有更高效的推理......