什么是重叠 token?
重叠 token 是指在分词过程中,同一个文本片段被分词器切分成多个不同的 token,而这些 token 在文本中的位置有交集。换句话说,同一段文本被分词器多次识别为不同的词汇,每个词汇都被当作一个独立的 token。
为什么会出现重叠 token?
重叠 token 的出现主要是因为分词器的 最大正向匹配 策略。这种策略的目标是 尽可能多地匹配出所有可能的词语,以提高召回率。例如,`ik_max_word` 模式会尝试从最长的可能词语开始向下匹配,从而生成多个重叠的 token。
举例说明
假设输入文本是 “中国农业银行”,使用 `ik_max_word` 分词器进行分词:
Token 文本 起始字符位置 结束字符位置 位置编号 (pos)
中国农业银行 0 6 1
中国农业 0 4 2
中国 0 2 3
国农 1 3 4
农业银行 2 6 5
农业银 2 5 6
农业 2 4 7
银行 4 6 8
在这个例子中:
– “中国农业银行” 和 “中国农业” 都是从第 0 个字符开始的,因此它们的起始位置重叠。
– “中国农业银行” 和 “中国” 都包含前两个字符 “中国”。
– “农业银行” 和 “农业银行” 的整个文本重叠。
– “农业银” 和 “农业银行” 的前几个字符重叠。
重叠 token 的特点
1. 位置编号不同:
– 每个 token 都有一个独立的位置编号(pos),从 1 开始递增。这些位置编号是 Lucene 在处理查询和位置运算时所依赖的关键信息。
2. 字符范围有交集:
– 这些 token 在原始文本中的字符范围有重叠。例如,“中国农业银行” 和 “中国农业” 都覆盖了文本的前几个字符。
3. 多个 token 表示同一段文本:
– 同一段文本被分词器切成了多个不同的词条,每个词条都被当作一个独立的 token。这些 token 在位置上会有交集,因此被称为“重叠 token”。
重叠 token 的影响
1. 查询时的复杂性:
– 在使用 `SpanNearQuery` 或其他基于位置的查询时,重叠 token 会导致位置计算变得更加复杂。例如,“中国” 和 “农业银行” 的真实位置跨度可能比你预期的要大,因为它们可能不是直接相邻的 token。
2. 索引大小:
– 重叠 token 会增加索引的大小,因为相同的文本片段被存储为多个 token。
如何避免重叠 token?
如果你希望避免重叠 token,可以使用 `ik_smart` 模式。`ik_smart` 模式更倾向于保持术语的完整性,减少重叠 token 的产生。例如,对 “中国农业银行”,`ik_smart` 通常会将其切分为一个单独的 token:
“`
pos=1: 中国农业银行
“`
示例代码
下面是一个简单的示例代码,展示如何使用 `ik_smart` 和 `ik_max_word` 分词器,并打印分词结果:
“`java
import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.ik.IKAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import java.io.StringReader;
public class IKAnalyzerDemo {
public static void main(String[] args) throws Exception {
// 输入文本
String text = “中国农业银行”;
// 使用 ik_smart 分词器
IKAnalyzer ikSmart = new IKAnalyzer(true); // true 表示使用智能分词模式
System.out.println(“=== ik_smart 分词结果 ===”);
printTokens(ikSmart, text);
// 使用 ik_max_word 分词器
IKAnalyzer ikMaxWord = new IKAnalyzer(false); // false 表示使用最大正向匹配模式
System.out.println(“=== ik_max_word 分词结果 ===”);
printTokens(ikMaxWord, text);
// 关闭分词器
ikSmart.close();
ikMaxWord.close();
}
private static void printTokens(Analyzer analyzer, String text) throws Exception {
try (TokenStream ts = analyzer.tokenStream(“f”, new StringReader(text))) {
CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);
PositionIncrementAttribute posInc = ts.addAttribute(PositionIncrementAttribute.class);
ts.reset();
int pos = 0;
while (ts.incrementToken()) {
pos += posInc.getPositionIncrement();
System.out.println(“pos=” + pos + ” term=” + term.toString());
}
ts.end();
}
}
}
“`
输出结果
运行上述代码后,你可能会看到类似以下的输出:
“`
=== ik_smart 分词结果 ===
pos=1 term=中国农业银行
=== ik_max_word 分词结果 ===
pos=1 term=中国农业银行
pos=2 term=中国农业
pos=3 term=中国
pos=4 term=国农
pos=5 term=农业银行
pos=6 term=农业银
pos=7 term=农业
pos=8 term=银行
“`
总结
> 重叠 token = 同一段文本被分词器切成了多个不同的词条,这些词条在位置上会有交集。
这种设计虽然有助于提高召回率,但也增加了查询时的复杂性和索引大小。如果你希望减少这种复杂性,可以考虑使用 `ik_smart` 模式,它更倾向于保持术语的完整性,减少重叠 token 的产生。
那什么是token的位置呢?
我们首先明确 token 的位置 的定义和用途,然后结合实际案例进行解释。
什么是 token 的位置?
Token 的位置 是指在分词过程中,分词器为每个 token 分配的一个 顺序编号。这个编号从 1 开始,表示 token 在分词结果中的相对顺序。它是 Lucene 和其他搜索引擎框架在处理基于位置的查询(如短语查询、邻近查询等)时的关键信息。
Token 的位置 vs. 原始文本中的字符位置
– Token 的位置 是一个逻辑上的编号,表示 token 在分词结果中的顺序。
– 原始文本中的字符位置 是 token 在原文本中的起始和结束字符的索引。
在 Lucene 中,我们主要关注的是 token 的位置,因为它用于位置运算和查询。
示例解释
假设输入文本是 “中国农业银行”,我们分别用 `ik_smart` 和 `ik_max_word` 分词器进行分词:
1. 使用 `ik_smart` 分词器
分词结果:
“`
pos=1: 中国农业银行
“`
– token: “中国农业银行”
– 位置编号 (pos): 1
解释:
– 分词器将整个文本切分为一个 token。
– 这个 token 的位置编号是 1。
2. 使用 `ik_max_word` 分词器
分词结果:
“`
pos=1: 中国农业银行
pos=2: 中国农业
pos=3: 中国
pos=4: 国农
pos=5: 农业银行
pos=6: 农业银
pos=7: 农业
pos=8: 银行
“`
– token: “中国农业银行” → 位置编号 1
– token: “中国农业” → 位置编号 2
– token: “中国” → 位置编号 3
– token: “国农” → 位置编号 4
– token: “农业银行” → 位置编号 5
– token: “农业银” → 位置编号 6
– token: “农业” → 位置编号 7
– token: “银行” → 位置编号 8
解释:
– 分词器将文本切分为多个 token,每个 token 都有一个独立的位置编号。
– 这些 token 在位置上会有重叠(重叠 token),因为同一个文本片段被切分成了多个不同的词条。
Token 的位置的作用
1. 位置运算的基础:
– 在 Lucene 中,基于位置的查询(如 `SpanNearQuery`、`SpanFirstQuery` 等)依赖 token 的位置编号来计算 token 之间的相对位置关系。
2. 查询匹配的关键:
– 例如,`SpanNearQuery` 用于查找两个 token 在指定距离内的匹配。位置编号用于计算两个 token 之间的跨度。
示例代码
下面是一个简单的示例代码,展示如何使用 `ik_smart` 和 `ik_max_word` 分词器,并打印分词结果中的 token 和其位置编号:
“`java
import org.apache.lucene.analysis.*;
import org.apache.lucene.analysis.ik.IKAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import java.io.StringReader;
public class IKAnalyzerDemo {
public static void main(String[] args) throws Exception {
// 输入文本
String text = “中国农业银行”;
// 使用 ik_smart 分词器
IKAnalyzer ikSmart = new IKAnalyzer(true); // true 表示使用智能分词模式
System.out.println(“=== ik_smart 分词结果 ===”);
printTokens(ikSmart, text);
// 使用 ik_max_word 分词器
IKAnalyzer ikMaxWord = new IKAnalyzer(false); // false 表示使用最大正向匹配模式
System.out.println(“=== ik_max_word 分词结果 ===”);
printTokens(ikMaxWord, text);
// 关闭分词器
ikSmart.close();
ikMaxWord.close();
}
private static void printTokens(Analyzer analyzer, String text) throws Exception {
try (TokenStream ts = analyzer.tokenStream(“f”, new StringReader(text))) {
CharTermAttribute term = ts.addAttribute(CharTermAttribute.class);
PositionIncrementAttribute posInc = ts.addAttribute(PositionIncrementAttribute.class);
ts.reset();
int pos = 0;
while (ts.incrementToken()) {
pos += posInc.getPositionIncrement();
System.out.println(“pos=” + pos + ” term=” + term.toString());
}
ts.end();
}
}
}
“`
输出结果
运行上述代码后,你可能会看到类似以下的输出:
“`
=== ik_smart 分词结果 ===
pos=1 term=中国农业银行
=== ik_max_word 分词结果 ===
pos=1 term=中国农业银行
pos=2 term=中国农业
pos=3 term=中国
pos=4 term=国农
pos=5 term=农业银行
pos=6 term=农业银
pos=7 term=农业
pos=8 term=银行
“`
总结
> Token 的位置编号是分词器在分词过程中为每个 token 分配的顺序编号,用于表示 token 在分词结果中的相对顺序。它是 Lucene 查询和位置运算的关键。
希望这能清晰地解答你的问题!
暂无评论内容