【一】词性标注
词性标注分为2部分,首先是分词,然后基于分词结果做词性标注。
【二】jieba的词性标注代码流程详解
1. 代码位置
jieba/posseg/_init_.py
2. 流程分析
def cut(sentence, HMM=True): """ Global `cut` function that supports parallel processing. Note that this only works using dt, custom POSTokenizer instances are not supported. """ global dt # 该pool 默认为None if jieba.pool is None: # 调用POSTokenizer的cut接口进行词性标注 for w in dt.cut(sentence, HMM=HMM): yield w else: parts = strdecode(sentence).splitlines(True) if HMM: result = jieba.pool.map(_lcut_internal, parts) else: result = jieba.pool.map(_lcut_internal_no_hmm, parts) for r in result: for w in r: yield w
可以看出,对于未登录词,采用HMM模型进行分词与词性标注。
def __cut_internal(self, sentence, HMM=True): # 词典加载 self.makesure_userdict_loaded() sentence = strdecode(sentence) # 中文正则表达式匹配 blocks = re_han_internal.split(sentence) # 设置分词函数 if HMM: cut_blk = self.__cut_DAG else: cut_blk = self.__cut_DAG_NO_HMM for blk in blocks: # 如果是中文,则调用分词接口进行分词与词性标注 if re_han_internal.match(blk): for word in cut_blk(blk): yield word else: tmp = re_skip_internal.split(blk) for x in tmp: if re_skip_internal.match(x): yield pair(x, 'x') else: for xx in x: # 如果是数字,则使用m标注词性,由于number的n和u已经用于其他词性,因此使用m if re_num.match(xx): yield pair(xx, 'm') # 如果是英文,标注为eng elif re_eng.match(x): yield pair(xx, 'eng') # 否则,一律标注为x else: yield pair(xx, 'x')
# 对于未位登录词,采用HMM模型进行词性标注 def __cut_detail(self, sentence): blocks = re_han_detail.split(sentence) for blk in blocks: if re_han_detail.match(blk): # 使用HMM模型进行词性标注 for word in self.__cut(blk): yield word else: tmp = re_skip_detail.split(blk) for x in tmp: if x: if re_num.match(x): yield pair(x, 'm') elif re_eng.match(x): yield pair(x, 'eng') else: yield pair(x, 'x')
def __cut(self, sentence): # 使用viterbi算法进行状态序列求解,这里的状态序列包含2部分 # 一是:词的位置,而是词性。由于一词多性,因此需要计算出该词概率最大的词性 prob, pos_list = viterbi( sentence, char_state_tab_P, start_P, trans_P, emit_P) begin, nexti = 0, 0 for i, char in enumerate(sentence): pos = pos_list[i][0] if pos == 'B': begin = i elif pos == 'E': yield pair(sentence[begin:i + 1], pos_list[i][1]) nexti = i + 1 elif pos == 'S': yield pair(char, pos_list[i][1]) nexti = i + 1 if nexti < len(sentence): yield pair(sentence[nexti:], pos_list[nexti][1])
这里,依旧使用viterbi算法进行状态序列求解。这里就不分析了,算法流程和前面的未登录词的分词一致。只是强调一点,这里的状态序列包含两部分:一是:字的位置,即BMES,而是词对应的词性,如 :n,a等
【三】总结
jieba总体而言,包含如下三个功能:分词、词性标注、关键字提取。使用的都是传统的方法,如基于词典前缀的匹配、基于HMM模型对未登录词进行分割与词性标注、基于TF-IDF和TextRank进行关键字提取。这三大功能,全都离不开jieba基于语料库统计出来的词典,包括词频、词性、HMM模型参数(状态转移概率矩阵、发射概率矩阵、初始状态概率向量)。
要想使用jieba得到好的分词效果,需要替换自己的词典,训练自己的HMM参数,而这些,基本都是基于语料库统计得到的。