新词发现
新词发现用于从大规模语料中自动找出词典里没有的候选词(未登录词),常见用途是扩充用户词典、构建领域词表。
import dev.znlp.Znlp;import entity.newwords.dev.znlp.NewWord;
List<NewWord> words = Znlp.discoverNewWords(new File("/path/to/corpus"));// 或传路径字符串List<NewWord> words2 = Znlp.discoverNewWords("/path/to/corpus");参数是目录,会递归扫描目录下的所有文本文件。门面版本使用默认配置、自适应打分、排除核心词典中已有的词,结果按分数降序排列。
NewWordDiscoveryTool
Section titled “NewWordDiscoveryTool”newwords.dev.znlp.NewWordDiscoveryTool 是完整的入口类,通过 Builder 配置:
import newwords.dev.znlp.NewWordDiscoveryTool;import engine.newwords.dev.znlp.INewWordFindEngine;
INewWordFindEngine engine = INewWordFindEngine.create();
NewWordDiscoveryTool tool = new NewWordDiscoveryTool.Builder(corpusDir, engine) .charset(StandardCharsets.UTF_8) // 默认 UTF-8 .verbose(true) // Builder 默认 true .build();
ArrayList<NewWord> words = tool.discover();discover() 内部执行两轮扫描,流程是 firstScan → finishFirst → secondScan → endSecond → result。
tool.saveToFile(new File("newwords.txt"));
tool.saveToFile(file, includeHeader, charset);
tool.saveToFile( file, includeHeader, // 是否写表头 delimiter, // 列分隔符 chineseHeader, // 表头是否用中文 charset);
// 静态方法,直接导出已有结果NewWordDiscoveryTool.saveResults(words, file, ...);引擎:INewWordFindEngine
Section titled “引擎:INewWordFindEngine”engine.newwords.dev.znlp.INewWordFindEngine 是统计引擎接口,实现类为 NewWordFindEngineOptimized。
// 默认配置,不排除核心词典INewWordFindEngine engine = INewWordFindEngine.create();
// 自定义配置 + 是否排除核心词典 + 核心词典实例INewWordFindEngine engine = INewWordFindEngine.create( config, excludeCoreDict, coreDictionary);excludeCoreDict = true 时会过滤掉已在核心词典中的词,只留下真正的未登录词。门面版本就是这么做的。
// Config 是 record(minGroup, maxGroup, minOccurCount)INewWordFindEngine.Config config = INewWordFindEngine.Config.defaultConfig();// 默认值:minGroup=3, maxGroup=12, minOccurCount=10| 参数 | 默认 | 含义 |
|---|---|---|
minGroup |
3 | 候选词最小长度(字) |
maxGroup |
12 | 候选词最大长度(字) |
minOccurCount |
10 | 最低出现次数,低于此值不参与统计 |
调小 minOccurCount 会召回更多低频候选但噪声上升;调大 maxGroup 会增加内存和耗时。
生命周期方法
Section titled “生命周期方法”如果要自己控制扫描过程(比如语料来自数据库而非文件):
engine.firstScan(text); // 第一轮:统计 n-gram 频次engine.finishFirst(); // 第一轮结束,计算互信息engine.secondScan(text); // 第二轮:统计左右邻接熵engine.endSecond(); // 第二轮结束List<NewWord> result = engine.result();
engine.setScorer(scorer); // 替换打分器engine.getScorer();两轮扫描是必须的,且第一轮的所有文本喂完之后才能调 finishFirst(),第二轮同理。顺序错了统计结果无效。
scorer.newwords.dev.znlp.NewWordScorerFactory 提供多种打分器:
| 工厂方法 | 说明 |
|---|---|
createDefault() |
固定阈值打分 |
createAdaptive() |
自适应打分(门面默认) |
createAdaptive(FilterStrategy) |
自适应 + 指定过滤策略 |
createSemiAdaptive(...) |
半自适应 |
createAdaptiveManual(...) |
手动参数的自适应 |
自适应打分不依赖固定阈值,而是根据语料的统计分布动态决定截断点,因此换语料时不需要重新调参。这是门面选它作为默认值的原因。
NewWord 结果实体
Section titled “NewWord 结果实体”entity.newwords.dev.znlp.NewWord 暴露的 getter:
| 方法 | 含义 |
|---|---|
getWord() |
候选词 |
getLen() |
词长 |
getFreq() |
出现频次 |
getDocFreq() |
文档频次 |
getMi() |
互信息( Mutual Information ) |
getAvg_mi() |
平均互信息 |
getEntropy() |
信息熵 |
getLe() / getRe() |
左熵 / 右熵 |
getLeftVariety() / getRightVariety() |
左右邻接种类数 |
getIdf() |
逆文档频率 |
getScore() |
综合得分(排序依据) |
判定一个字符串是不是词,靠两个指标:
- **互信息(MI)**衡量内部凝固度。一个真实词的各部分倾向于一起出现,互信息高;偶然相邻的字串互信息低。
- 信息熵衡量边界自由度。真实词的左右邻接字应当多样(熵高);如果某个字串右边永远只跟同一个字,它更可能是更长词的一部分而非独立词。
两轮扫描正对应这两个指标:第一轮统计 n-gram 频次算互信息,第二轮统计左右邻接算熵。
仓库在 data/newwords/newword_test/ 下提供了一批新闻文本,可以直接用来验证:
List<NewWord> words = Znlp.discoverNewWords("data/newwords/newword_test");words.stream().limit(50).forEach(w -> System.out.println(w.getWord() + "\t" + w.getFreq() + "\t" + w.getScore()));另外根目录的 data-dir-meta.txt 说明了一个不随仓库分发的外部大数据集约定:放在项目根的同级目录 znlp-data/ 下,其中 newword_test_small 是缩小版的新词发现语料。
- 发现的新词如何回灌到分词:中文分词的用户词典部分
- 模块内部的详细说明:
newwords/README.md与newwords/scorer/README*.md