[nlp] pyhanlp 停用词使用
摘取了网上的两种调用方法:调用自带函数、调用自定义的生成字典的函数和去除停用词的函数。而不论是哪种方法,都是在原segment分词之后再进行停用词的筛除,这意味着停用词表是针对分出的词的,停用词的优先级低于原本算法的分词。
HanLP自带的停用词典调用方法
停用词表在“pyhanlp\static\data\dictionary”路径下的“stopwords.txt”文件中,CoreStopWordDictionary.apply
方法支持去除停用词。如果需要修改停用词表,则直接编辑文件“stopwords.txt”,之后删除路径下的“.bin”缓存文件,运行CoreStopWordDictionary.apply
后即可自动生效。
简单调用
-
停用词性
HanLP.Config.ShowTermNature = False
-
停用词过滤(原文的写法有问题)
CoreStopWordDictionary = JClass('com.hankcs.hanlp.dictionary.stopword.CoreStopWordDictionary') term_list = HanLP.segment(txt) CoreStopWordDictionary.apply(term_list) print(term_list)
复杂调用
自定义词语过滤方法
用户可以通过编写“pyhanlp\static”路径下的“MyFilter.java”文件设置自己的词语过滤方法。应当注意这里处理的语言单位是词语,而不是字。编辑完毕后需要编译该文件并生成字节码文件,之后运行CoreStopWordDictionary.apply
方法时就会自动调用用户自己的词语过滤方法了。
这里给出一个自定义过滤方法的编写示例代码。
import os
from pyhanlp.static import STATIC_ROOT, HANLP_JAR_PATH
java_code_path = os.path.join(STATIC_ROOT, 'MyFilter.java')
with open(java_code_path, 'w') as out:
java_code = """
import com.hankcs.hanlp.dictionary.stopword.CoreStopWordDictionary;
import com.hankcs.hanlp.dictionary.stopword.Filter;
import com.hankcs.hanlp.seg.common.Term;
public class MyFilter implements Filter
{
public boolean shouldInclude(Term term)
{
if (term.nature.startsWith('m')) return false; // 数词过滤
if (term.nature.startsWith('q')) return false; // 量词过滤
if (term.nature.startsWith('t')) return false; // 时间词过滤
if (term.nature.startsWith("w")) return false; // 过滤标点符号
return !CoreStopWordDictionary.contains(term.word); // 停用词过滤
}
}
"""
out.write(java_code)
os.system('javac -cp {} {} -d {}'.format(HANLP_JAR_PATH, java_code_path, STATIC_ROOT))
验证是否生效
本节给出停用词表修改后以及使用了自定义词语过滤方法的示例代码。
from pyhanlp import *
# 加载停用词类
CoreStopWordDictionary = JClass("com.hankcs.hanlp.dictionary.stopword.CoreStopWordDictionary")
# 加载自定义词语过滤逻辑
MyFilter = JClass('MyFilter')
CoreStopWordDictionary.FILTER = MyFilter()
term_list = HanLP.segment(text)
CoreStopWordDictionary.apply(term_list)
重复造轮子的调用方法
学习NLP的第7天——基于HanLP实现的停用词过滤(这个方法不需要删除缓存文件)
我们使用HanLP提供的双数组字典树来实现这个功能。
-
从HanLP实现停用词典的加载。
def load_from_file(path): """ 从词典文件加载DoubleArrayTrie :param path: 词典路径 :return: 双数组trie树 """ map = JClass('java.util.TreeMap')() # 创建TreeMap实例 with open(path) as src: for word in src: word = word.strip() # 去掉Python读入的\n map[word] = word return JClass('com.hankcs.hanlp.collection.trie.DoubleArrayTrie')(map)
停用词词典的格式为一个词一行,每行之间用换行符\n分隔。
-
提出分词结果中的停用词
针对分词结果,遍历每个词语,若它存在于停用词字典树中,则删除该词。这里使用列表生成式实现。
def remove_stopwords_termlist(termlist, trie): return [term.word for term in termlist if not trie.containsKey(term.word)] if __name__ == '__main__': HanLP.Config.ShowTermNature = False trie = load_from_file(HanLP.Config.CoreStopWordDictionaryPath) text = "停用词的意义相对而言无关紧要的词吧" segment = DoubleArrayTrieSegment() termlist = segment.seg(text) print("分词结果:", termlist) print("分词结果去除停用词:", remove_stopwords_termlist(termlist, trie))
运行结果
分词结果: [停用, 词, 的, 意义, 相对而言, 无关紧要, 的, 词, 吧] 分词结果去除停用词: ['停用', '词', '意义', '无关紧要', '词']
-
直接处理文本中的停用词
在过滤敏感词时,需要将敏感词替换为特定的字符串,例如**,这时使用不分词,直接替换的方法效率更高。这里使用HanLP的双数组字典树的
getLongestSearcher
方法实现。def replace_stropwords_text(text, replacement, trie): searcher = trie.getLongestSearcher(JString(text), 0) offset = 0 result = '' while searcher.next(): begin = searcher.begin end = begin + searcher.length if begin > offset: result += text[offset: begin] result += replacement offset = end if offset < len(text): result += text[offset:] return result if __name__ == '__main__': text = "停用词的意义相对而言无关紧要的词吧" trie = load_from_words("的", "相对而言", "吧") print("不分词去掉停用词:", replace_stropwords_text(text, "**", trie))
运行结果
不分词去掉停用词: 停用词**意义**无关紧要**词**
学习使用教材:《自然语言处理入门》(何晗):2.10
原作者词典说明
本章详细介绍HanLP中的词典格式,满足用户自定义的需要。HanLP中有许多词典,它们的格式都是相似的,形式都是文本文档,随时可以修改。
基本格式
词典分为词频词性词典和词频词典。
词频词性词典(如CoreNatureDictionary.txt)
- 每一行代表一个单词,格式遵从[单词] [词性A] [A的频次] [词性B] [B的频次] ...。
- 支持省略词性和频次,直接一行一个单词。
- .txt词典文件的分隔符为空格或制表符,所以不支持含有空格的词语。如果需要支持空格,请使用英文逗号,分割的纯文本.csv文件。在使用Excel等富文本编辑器时,则请注意保存为纯文本形式。
词频词典(如CoreNatureDictionary.ngram.txt)
- 每一行代表一个单词或条目,格式遵从[单词] [单词的频次]。
- 每一行的分隔符为空格或制表符。
少数词典有自己的专用格式,比如同义词词典兼容《同义词词林扩展版》的文本格式,而转移矩阵词典则是一个csv表格。
下文主要介绍通用词典,如不注明,词典特指通用词典。
数据结构
Trie树(字典树)是HanLP中使用最多的数据结构,为此,我实现了通用的Trie树,支持泛型、遍历、储存、载入。
用户自定义词典采用AhoCorasickDoubleArrayTrie和二分Trie树储存,其他词典采用基于双数组Trie树(DoubleArrayTrie)实现的AC自动机AhoCorasickDoubleArrayTrie。关于一些常用数据结构的性能评估,请参考wiki。
储存形式
词典有两个形态:文本文件(filename.txt)和缓存文件(filename.txt.bin或filename.txt.trie.dat和filename.txt.trie.value)。
文本文件
- 采用明文储存,UTF-8编码,CRLF换行符。
缓存文件
- 就是一些二进制文件,通常在文本文件的文件名后面加上.bin表示。有时候是.trie.dat和.trie.value。后者是历史遗留产物,分别代表trie树的数组和值。
- 如果你修改了任何词典,只有删除缓存才能生效。
修改方法
HanLP的核心词典训练自人民日报2014语料,语料不是完美的,总会存在一些错误。这些错误可能会导致分词出现奇怪的结果,这时请打开调试模式排查问题:
HanLP.Config.enableDebug();
注:原词典后来又有变动,但是词典说明中原作者没改
核心词性词频词典
- 比如你在data/dictionary/CoreNatureDictionary.txt中发现了一个不是词的词,或者词性标注得明显不对,那么你可以修改它,然后删除缓存文件使其生效。
- 目前CoreNatureDictionary.ngram.txt的缓存依赖于CoreNatureDictionary.txt的缓存,修改了后者之后必须同步删除前者的缓存,否则可能出错
核心二元文法词典
- 二元文法词典data/dictionary/CoreNatureDictionary.ngram.txt储存的是两个词的接续,如果你发现不可能存在这种接续时,删掉即可。
- 你也可以添加你认为合理的接续,但是这两个词必须同时在核心词典中才会生效。
命名实体识别词典
- 基于角色标注的命名实体识别比较依赖词典,所以词典的质量大幅影响识别质量。
- 这些词典的格式与原理都是类似的,请阅读相应的文章或代码修改它。