深入解析SentenceWindowNodeParser:一种高效的文本节点解析器
在自然语言处理(NLP)领域,文本解析是一个基础且关键的步骤。它涉及将文档拆分成更小的单元,以便于进一步处理和分析。今天,我们将深入探讨一种名为 SentenceWindowNodeParser
的文本节点解析器,它能够将文档拆分成句子,并在每个节点中包含前后句子的窗口信息。这种解析器在处理长文档时尤为有用,因为它可以帮助我们更好地理解上下文。
前置知识
在深入了解 SentenceWindowNodeParser
之前,我们需要掌握以下几个概念:
- 节点(Node):在NLP中,节点是文档的基本单元。它可以是一个句子、一个段落或一个词语。
- 元数据(Metadata):元数据是关于数据的数据,用于描述节点的额外信息。
- 回调管理器(CallbackManager):用于管理和调用回调函数,以便在特定事件发生时执行相应的操作。
- Pydantic:一个用于数据验证和设置的Python库,常用于定义数据模型。
SentenceWindowNodeParser 的实现
SentenceWindowNodeParser
是一个基于 NodeParser
接口的类,它通过将文档拆分成句子并包含前后句子的窗口信息来创建节点。下面是其实现的详细解析:
导入必要的模块
首先,我们需要导入一些必要的模块和函数:
from typing import Any, Callable, List, Optional, Sequence
from llama_index.core.bridge.pydantic import Field
from llama_index.core.callbacks.base import CallbackManager
from llama_index.core.node_parser.interface import NodeParser
from llama_index.core.node_parser.node_utils import (
build_nodes_from_splits,
default_id_func,
)
from llama_index.core.node_parser.text.utils import split_by_sentence_tokenizer
from llama_index.core.schema import BaseNode, Document
from llama_index.core.utils import get_tqdm_iterable
定义默认参数
接下来,我们定义一些默认参数,这些参数将在类的初始化过程中使用:
DEFAULT_WINDOW_SIZE = 3
DEFAULT_WINDOW_METADATA_KEY = "window"
DEFAULT_OG_TEXT_METADATA_KEY = "original_text"
定义 SentenceWindowNodeParser 类
现在,我们定义 SentenceWindowNodeParser
类,并为其添加必要的属性和方法:
class SentenceWindowNodeParser(NodeParser):
"""Sentence window node parser.
Splits a document into Nodes, with each node being a sentence.
Each node contains a window from the surrounding sentences in the metadata.
Args:
sentence_splitter (Optional[Callable]): splits text into sentences
include_metadata (bool): whether to include metadata in nodes
include_prev_next_rel (bool): whether to include prev/next relationships
"""
sentence_splitter: Callable[[str], List[str]] = Field(
default_factory=split_by_sentence_tokenizer,
description="The text splitter to use when splitting documents.",
exclude=True,
)
window_size: int = Field(
default=DEFAULT_WINDOW_SIZE,
description="The number of sentences on each side of a sentence to capture.",
gt=0,
)
window_metadata_key: str = Field(
default=DEFAULT_WINDOW_METADATA_KEY,
description="The metadata key to store the sentence window under.",
)
original_text_metadata_key: str = Field(
default=DEFAULT_OG_TEXT_METADATA_KEY,
description="The metadata key to store the original sentence in.",
)
@classmethod
def class_name(cls) -> str:
return "SentenceWindowNodeParser"
@classmethod
def from_defaults(
cls,
sentence_splitter: Optional[Callable[[str], List[str]]] = None,
window_size: int = DEFAULT_WINDOW_SIZE,
window_metadata_key: str = DEFAULT_WINDOW_METADATA_KEY,
original_text_metadata_key: str = DEFAULT_OG_TEXT_METADATA_KEY,
include_metadata: bool = True,
include_prev_next_rel: bool = True,
callback_manager: Optional[CallbackManager] = None,
id_func: Optional[Callable[[int, Document], str]] = None,
) -> "SentenceWindowNodeParser":
callback_manager = callback_manager or CallbackManager([])
sentence_splitter = sentence_splitter or split_by_sentence_tokenizer()
id_func = id_func or default_id_func
return cls(
sentence_splitter=sentence_splitter,
window_size=window_size,
window_metadata_key=window_metadata_key,
original_text_metadata_key=original_text_metadata_key,
include_metadata=include_metadata,
include_prev_next_rel=include_prev_next_rel,
callback_manager=callback_manager,
id_func=id_func,
)
def _parse_nodes(
self,
nodes: Sequence[BaseNode],
show_progress: bool = False,
**kwargs: Any,
) -> List[BaseNode]:
"""Parse document into nodes."""
all_nodes: List[BaseNode] = []
nodes_with_progress = get_tqdm_iterable(nodes, show_progress, "Parsing nodes")
for node in nodes_with_progress:
nodes = self.build_window_nodes_from_documents([node])
all_nodes.extend(nodes)
return all_nodes
def build_window_nodes_from_documents(
self, documents: Sequence[Document]
) -> List[BaseNode]:
"""Build window nodes from documents."""
all_nodes: List[BaseNode] = []
for doc in documents:
text = doc.text
text_splits = self.sentence_splitter(text)
nodes = build_nodes_from_splits(
text_splits,
doc,
id_func=self.id_func,
)
# add window to each node
for i, node in enumerate(nodes):
window_nodes = nodes[
max(0, i - self.window_size) : min(
i + self.window_size + 1, len(nodes)
)
]
node.metadata[self.window_metadata_key] = " ".join(
[n.text for n in window_nodes]
)
node.metadata[self.original_text_metadata_key] = node.text
# exclude window metadata from embed and llm
node.excluded_embed_metadata_keys.extend(
[self.window_metadata_key, self.original_text_metadata_key]
)
node.excluded_llm_metadata_keys.extend(
[self.window_metadata_key, self.original_text_metadata_key]
)
all_nodes.extend(nodes)
return all_nodes
详细解析
属性解析
- sentence_splitter:这是一个可调用对象,用于将文本拆分成句子。默认情况下,它使用
split_by_sentence_tokenizer
函数。 - window_size:表示每个节点包含的前后句子的数量。默认值为3。
- window_metadata_key:用于存储句子窗口信息的元数据键。默认值为 “window”。
- original_text_metadata_key:用于存储原始句子信息的元数据键。默认值为 “original_text”。
方法解析
- class_name:这是一个类方法,返回类的名称。
- from_defaults:这是一个类方法,用于从默认参数创建
SentenceWindowNodeParser
实例。 - _parse_nodes:这是一个实例方法,用于将文档解析成节点。
- build_window_nodes_from_documents:这是一个实例方法,用于从文档构建包含窗口信息的节点。
实际应用示例
为了更好地理解 SentenceWindowNodeParser
的工作原理,我们来看一个实际的应用示例:
# 示例文档
document = Document(text="这是一个示例文档。它包含多个句子。每个句子都将被拆分成一个节点。")
# 创建 SentenceWindowNodeParser 实例
node_parser = SentenceWindowNodeParser.from_defaults()
# 解析文档
nodes = node_parser.get_nodes_from_documents([document])
# 输出节点信息
for node in nodes:
print(f"Node ID: {node.id_}")
print(f"Original Text: {node.metadata[node_parser.original_text_metadata_key]}")
print(f"Window: {node.metadata[node_parser.window_metadata_key]}")
print("-" * 40)
在这个示例中,我们首先创建了一个包含多个句子的文档,然后使用 SentenceWindowNodeParser
将其拆分成节点,并输出每个节点的原始文本和窗口信息。
输出结果
假设文档被拆分成以下三个句子:
- “这是一个示例文档。”
- “它包含多个句子。”
- “每个句子都将被拆分成一个节点。”
那么,输出的节点信息可能如下:
Node ID: 1
Original Text: 这是一个示例文档。
Window: 这是一个示例文档。 它包含多个句子。
----------------------------------------
Node ID: 2
Original Text: 它包含多个句子。
Window: 这是一个示例文档。 它包含多个句子。 每个句子都将被拆分成一个节点。
----------------------------------------
Node ID: 3
Original Text: 每个句子都将被拆分成一个节点。
Window: 它包含多个句子。 每个句子都将被拆分成一个节点。
----------------------------------------
从输出结果可以看出:
- 每个节点包含一个
Node ID
,用于唯一标识该节点。 - 每个节点包含一个
Original Text
,即原始句子信息。 - 每个节点包含一个
Window
,即前后句子的窗口信息。
总结
SentenceWindowNodeParser
是一个高效的文本节点解析器,它能够将文档拆分成句子,并在每个节点中包含前后句子的窗口信息。通过这种方式,我们可以更好地理解文档的上下文,从而提高NLP任务的准确性。希望这篇博客能够帮助你全面理解 SentenceWindowNodeParser
的工作原理及实际应用。