小发猫 2020-02-02
jieba库是一个简单实用的中文自然语言处理分词库。
jieba分词属于概率语言模型分词。概率语言模型分词的任务是:在全切分所得的所有结果中求某个切分方案S,使得P(S)最大。
jieba支持三种分词模式:
全模式,把句子中所有的可以成词的词语都扫描出来, 速度非常快,但是不能解决歧义;
精确模式,试图将句子最精确地切开,适合文本分析;
搜索引擎模式,在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。
1. 根据dict.txt生成trie树,字典在生成trie树的同时, 把每个词的出现次数转换为频率(jieba自带一个dict.txt的词典, 里面有2万多条词, 包含了词条出现的次数和词性(作者基于人民日报语料等资源训练得出来)。trie树结构的词图扫描, 说的就是把这2万多条词语, 放到一个trie树中, trie树是有名的前缀树, 也就是说一个词语的前面几个字一样, 就表示他们具有相同的前缀, 就可以使用trie树来存储, 具有查找速度快的优势)。
2.对待分词句子, 根据dict.txt生成的trie树, 生成DAG, 通俗的讲, 就是将句子根据给定的词典进行查词典操作, 生成所有可能的句子切分。jieba在DAG中记录的是句子中某个词的开始位置, 从0到n-1(n为句子的长度), 每个开始位置作为字典的键, value是个list, 其中保存了可能的词语的结束位置(通过查字典得到词, 开始位置+词语的长度得到结束位置)
1.查找待分词句子中已经切分好的词语(全模式下的分词list), 得出查找该词语出现的频率(次数/总数), 如果没有该词(基于词典一般都是有的), 就把词典中出现频率最小的那个词语的频率作为该词的频率。
2.根据动态规划查找最大概率路径的方法, 对句子从右往左反向计算最大概率(这里反向是因为汉语句子的重心经常落在后面(右边), 因为通常情况下形容词太多, 后面的才是主干。因此, 从右往左计算, 正确率要高于从左往右计算, 这里类似于逆向最大匹配), P(NodeN)=1.0, P(NodeN-1)=P(NodeN)*Max(P(倒数第一个词))…依次类推, 最后得到最大概率路径, 得到最大概率的切分组合。
1.利用HMM模型将中文词汇按照BEMS四个状态来标记, B是开始begin位置, E是end结束位置, M是middle中间位置, S是singgle单独成词的位置。jieba采用(B,E,M,S)这四种状态来标记中文词语, 比如北京可以标注为 BE, 即 北/B 京/E, 表示北是开始位置, 京是结束位置, 中华民族可以标注为BMME, 就是开始, 中间, 中间, 结束.
2.作者利用大量语料进行训练, 得到了三个概率表。分别是1)位置转换概率,即B(开头),M(中间),E(结尾),S(独立成词)四种状态的转移概率,P(E|B) = 0.851, P(M|B) = 0.149,说明当我们处于一个词的开头时,下一个字是结尾的概率要远高于下一个字是中间字的概率,符合我们的直觉,因为二个字的词比多个字的词更常见。2)位置到单字的发射概率,比如P(“和”|M)表示一个词的中间出现”和”这个字的概率;3) 词语以某种状态开头的概率,其实只有两种,要么是B,要么是S。这个就是起始向量, 就是HMM系统的最初模型状态。实际上, BEMS之间的转换有点类似于2元模型, 就是2个词之间的转移。二元模型考虑一个单词后出现另外一个单词的概率,是N元模型中的一种。
给定一个待分词的句子, 就是观察序列, 对HMM(BEMS)四种状态的模型来说, 就是为了找到一个最佳的BEMS序列, 这个就需要使用viterbi算法来得到这个最佳的隐藏状态序列。通过训练得到的概率表和viterbi算法, 就可以得到一个概率最大的BEMS序列, 按照B打头, E结尾的方式, 对待分词的句子重新组合, 就得到了分词结果. 比如 对待分词的句子 ‘全世界都在学中国话’ 得到一个BEMS序列 [S,B,E,S,S,S,B,E,S], 通过把连续的BE凑合到一起得到一个词, 单独的S放单, 就得到一个分词结果了。
1. 加载字典, 生成trie树。
2. 给定待分词的句子, 使用正则获取连续的 中文字符和英文字符, 切分成 短语列表, 对每个短语使用DAG(查字典)和动态规划, 得到最大概率路径, 对DAG中那些没有在字典中查到的字, 组合成一个新的片段短语, 使用HMM模型进行分词, 也就是作者说的识别未登录词。
3. 使用python的yield 语法生成一个词语生成器, 逐词语返回。
1.dict.txt字典占用的内存为140多M, 占用内存过多。jieba中词典的使用是为了弥补HMM在识别多字词方面能力欠佳的问题, 所以词典中保存的是3 ,4 个字的词语。专业化的词典生成不方便,怎么训练自己的专用概率表没有提供工具。
2.HMM识别新词在时效性上是不足的, 并且只能识别2个字的词, 对于3个字的新词, 相对能力有限。
3.词性标注效果不够好,句法分析, 语义分析也都是没有的。
4.命名实体识别效果不够好。
1).切词
2).过滤掉单个字的词和停用词
3).时使用TF-IDF计算,TF为 (词频* 1.0),IDF从外部文件的词表中获得如果不存在就赋为平均的IDF
## 分词
### jieba.dict.utf8/gbk
作为最大概率法(MPSegment: Max Probability)分词所使用的词典。
### hmm_model.utf8/gbk
作为隐式马尔科夫模型(HMMSegment: Hidden Markov Model)分词所使用的词典。
__对于MixSegment(混合MPSegment和HMMSegment两者)则同时使用以上两个词典__
## 关键词抽取
### idf.utf8
IDF(Inverse Document Frequency)
在KeywordExtractor中,使用的是经典的TF-IDF算法,所以需要这么一个词典提供IDF信息。
### stop_words.utf8
停用词词典
#include <iostream> #include "cppjieba/Jieba.hpp" using namespace std; int main(int argc, char const *argv[]) { char ch0[]={"金古江湖战旗春秋"}; const char* const DICT_PATH = "cppjieba/dict/jieba.dict.utf8"; const char* const HMM_PATH = "cppjieba/dict/hmm_model.utf8"; const char* const USER_DICT_PATH = "cppjieba/dict/user.dict.utf8"; const char* const IDF_PATH = "cppjieba/dict/idf.utf8"; const char* const STOP_WORD_PATH = "cppjieba/dict/stop_words.utf8"; cppjieba::Jieba jieba(DICT_PATH, HMM_PATH, USER_DICT_PATH, IDF_PATH, STOP_WORD_PATH); vector<string> words; vector<cppjieba::Word> jiebawords; string s=ch0; string result; jieba.Cut(s, words, true); cout << limonp::Join(words.begin(), words.end(), "/") << endl; return 0; }
from NLP之jieba分词原理简析