普通视图

发现新文章,点击刷新页面。
昨天 — 2025年1月17日首页

Langchain.js | Document Transformer👈| 文本这样转变😱

作者 浪遏
2025年1月17日 20:10

前言

书接上文 , 参考官方文档学习了 Ducument loader , 他的作用是加载不同的数据源 , 并且探讨了一些设计思想 , 这一次 ,还是这张图 , 但是学习的主题变了 , 我今天想学习 : 在加载数据之后 , 向量化之前 , 进行的文本转化处理, 这其实是文本转化器(Document Transformer)在作祟 , 对应图中红色方框的阶段 ~

同样 , 还是从宏观入手,文档转化主要有以下工作 :

  • 文本分割

    • 完整文档内容 分割为 chunk(文本块)
  • 文本元数据提取

    • 提取核心数据
  • 文本翻译

    • 全球化下 , 处理多语言文档已经成为常态
  • 生成文本问答

    • 向量储存的知识库中 , 通常以 QA 对出现 , 我们主要是在向量化之前转化为问答的形式 , 利于向量化过程中 , 增强相关性 , 聚焦核心要点

上面的四个步骤中 , 文本分割 是向量化之前的处理, 后面的三个步骤主要是在向量化前起优化作用


在 python 中提供 DocTran 库 , 用于上述文本元数据提取、文本翻译、生成文本为问答 ,因为 DocTran

参考自官网 : python.langchain.ac.cn/docs/integr…

但在 langchain.js 中 , 我简单搜索了下官网 , 没有找到集成的 DocTran库 ,

有时间再去 langchain 的 github 上看看 , 地址如下 : github.com/langchain-a…

如果还没有 , 或许我们可以自己拓展

我就不看了 , 学校放假🤡 , 我要去愉快的玩耍一下 , 之后有时间再来研究研究 , 这篇文章主要总结我分割文本的经验 ~

文本分割

思考 :

  • 为什么要文本分割 ?

  • 怎么分割 ?

    • 随便分吗 ? 肯定不是 🤡👈

为什么要文本分割 ?

主要有以下原因 :

  • 处理非统一文档长度
    现实世界的文档集合通常包含不同大小的文本。分割确保对所有文档进行一致的处理。
  • 克服模型限制
    许多嵌入模型和语言模型都有最大输入大小限制。分割使我们能够处理否则会超出这些限制的文档。
  • 提升表示质量
    对于较长的文档,嵌入或其他表示的质量可能会随着尝试捕捉过多信息而下降。拆分可以导致每个部分的表示更加专注和准确。
  • 增强检索精度
    在信息检索系统中,拆分可以提高搜索结果的粒度,允许更精确地匹配查询到相关文档部分。
  • 优化计算资源
    使用更小的文本块可以提高内存效率,并允许更好地并行化处理任务。

怎么分割 ?

关键概念

  • Chunk Size

    • 块大小:末端块长度(以字符为单位)
  • Chunk Overlap

    • 块重叠:连续块共享的重叠或交叉量

如何理解 ?

比如我们对《少年中国说》这篇文章进行分割

我们使用官网自带的ChunkViz v0.1 (块可视化工具)

上传文章后 , 选择下面的分割标准

文本分割如下 :

上面发生了什么?

上面其实就是使用 langchain.js 提供的RecursiveCharacterTextSplitter(递归字符文本分割) , 并且设置

  • Chunk size
  • Chunk overlap

这两个的参数大小

比如我上面设置 Chunk size 大小为 5 , 那么基本每个颜色块都是 5 个字符

由于可视化工具 Chunk overlap(块重叠) 无法展示 , 所以块与块之间没有重复的字段

我举个例子 : 一段分块是"日本人之称我中国也,一则曰老大帝国,再则曰老大帝国。" ,如果有重叠块可能会出现"再则曰老大帝国。是语也,盖袭译欧西人之言也。" , 如加粗的文字就是重叠部分 .

需要重叠块的原因是 , 两个块之间 , 需要有语义的联系 , 使得块向量化的时候 , 知道块与块之间的联系

以上便是分割的原理 , 以下使用代码实现

RecursiveCharacterTextSplitter

默认的分隔符列表是 ["\n\n", "\n", " ", ""] , 即分割的顺序是 : 段落("\n\n") => 句子("\n") =>单词(" ")

使用加载器加载 data 目录下的《少年中国说为 Document 对象 , 使用RecursiveCharacterTextSplitter 切割这个对象 , 代码如下

import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import { TextLoader } from "langchain/document_loaders/fs/text";

const loader = new TextLoader("data/少年中国说.txt");
const docs = await loader.load();

const splitter = new RecursiveCharacterTextSplitter({
    chunkSize: 64,
    chunkOverlap: 0,
  });

const splitDocs = await splitter.splitDocuments(docs);
console.log(splitDocs);

部分输出如下图 :

How to split code

上面说到 , RecursiveCharacterTextSplitter 默认按照["\n\n", "\n", " ", ""] 分割 , 其实还包含预构建的分隔符列表,这些分隔符对于在特定编程语言中分割文本非常有用。

支持的语言有

"html" | "cpp" | "go" | "java" | "js" | "php" | "proto" | "python" | "rst" | "ruby" | "rust" | "scala" | "swift" | "markdown" | "latex" | "sol"

举两个例子就知道了~

Markdown

这里是一个使用 Markdown 分隔符进行分割的示例:

const markdownText = `
# 🦜️🔗 LangChain

⚡ Building applications with LLMs through composability ⚡

## Quick Install


# Hopefully this code block isn't split
pip install langchain

`;
const mdSplitter = RecursiveCharacterTextSplitter.fromLanguage("markdown", {
  chunkSize: 60,
  chunkOverlap: 0,
});
const mdDocs = await mdSplitter.createDocuments([markdownText]);

mdDocs;

效果如下 :


[
  Document {
    pageContent: "# 🦜️🔗 LangChain",
    metadata: { loc: { lines: { from: 2, to: 2 } } }
  },
  Document {
    pageContent: "⚡ Building applications with LLMs through composability ⚡",
    metadata: { loc: { lines: { from: 4, to: 4 } } }
  },
  Document {
    pageContent: "## Quick Install",
    metadata: { loc: { lines: { from: 6, to: 6 } } }
  },
  Document {
    pageContent: "```bash\n# Hopefully this code block isn't split",
    metadata: { loc: { lines: { from: 8, to: 9 } } }
  },
  Document {
    pageContent: "pip install langchain",
    metadata: { loc: { lines: { from: 10, to: 10 } } }
  },
  Document {
    pageContent: "```",
    metadata: { loc: { lines: { from: 11, to: 11 } } }
  },
  Document {
    pageContent: "As an open-source project in a rapidly developing field, we",
    metadata: { loc: { lines: { from: 13, to: 13 } } }
  },
  Document {
    pageContent: "are extremely open to contributions.",
    metadata: { loc: { lines: { from: 13, to: 13 } } }
  }
]

JavaScript
const JS_CODE = `
function helloWorld() {
  console.log("Hello, World!");
}

// Call the function
helloWorld();
`;

const jsSplitter = RecursiveCharacterTextSplitter.fromLanguage("js", {
  chunkSize: 60,
  chunkOverlap: 0,
});
const jsDocs = await jsSplitter.createDocuments([JS_CODE]);

jsDocs;
[
  Document {
    pageContent: 'function helloWorld() {\n  console.log("Hello, World!");\n}',
    metadata: { loc: { lines: { from: 2, to: 4 } } }
  },
  Document {
    pageContent: "// Call the function\nhelloWorld();",
    metadata: { loc: { lines: { from: 6, to: 7 } } }
  }
]

参考 : js.langchain.com/docs/how_to…

总结

学习文本分割的处理思想和方法 , 为后续向量化做准备 ~

可以在本次学习上加餐: token

昨天以前首页
❌
❌