#!/usr/bin/env python # coding: utf-8 # ### 一.分词介绍 # 这一节介绍下中文分词,以便将前面的内容串起来,中文分词可以看做给定一个可观测状态(即中文字),预测其最有可能的隐状态(是否切分)的过程,比如如下例子(来自于《自然语言处理入门》): # # ![avatar](./source/12_HMM_中文分词1.png) # # 可以将每个字对应的状态看做“过”或者“切”,比如上面的结果经过分词后为:“参观”,“了”,“北京”,“天安门”,但是呢,这样的风格比较粗糙,为了捕捉汉字的不同构成概率信息,通常使用{B,M,E,S}标注集,包括词语首尾(Begin,End),词中(Middle)以及单字成词(Single),这样上面的过程可以进一步细化如下: # ![avatar](./source/12_HMM_中文分词2.png) # ### 二.实践 # 了解了原理后,接下来在《人民日报》1988的语料上做训练,使用BMES的方式进行标注 # In[1]: visible_seqs=[] hidden_seqs=[] char2idx={} idx2hidden={0:"B",1:"M",2:"E",3:"S"} # In[2]: count=0 for line in open("./data/people_daily_mini.txt",encoding="utf8"): visible_seq=[] hidden_seq=[] arrs=line.strip().split(" ") for item in arrs: if len(item)==1: hidden_seq.append(3) elif len(item)==2: hidden_seq.extend([0,2]) else: hidden_seq.extend([0]+[1]*(len(item)-2)+[2]) for c in item: if c in char2idx: visible_seq.append(char2idx[c]) else: char2idx[c]=count visible_seq.append(count) count+=1 visible_seqs.append(visible_seq) hidden_seqs.append(hidden_seq) # In[3]: len(visible_seqs),len(hidden_seqs),len(char2idx) # In[4]: #训练模型 import os os.chdir('../') from ml_models.pgm import HMM hmm=HMM(hidden_status_num=4,visible_status_num=len(char2idx)) hmm.fit_with_hidden_status(visible_seqs,hidden_seqs) # 看看分词效果 # In[5]: def seg(vis,hid): rst=[] for i in range(0,len(hid)): if hid[i] in [2,3]: rst.append(vis[i]) rst.append(" ") else: rst.append(vis[i]) return "".join(rst) # In[6]: seq="我和我的祖国,一刻也不能分离" seg(seq,hmm.predict_hidden_status([char2idx[c] for c in seq])) # In[7]: seq="小龙女说,我也想过过过过过过过的生活" seg(seq,hmm.predict_hidden_status([char2idx[c] for c in seq])) # In[8]: seq="我爱马云爸爸" seg(seq,hmm.predict_hidden_status([char2idx[c] for c in seq])) # 在看看观测状态的概率呢?差距没那么大了... # In[9]: import numpy as np print(np.log(hmm.predict_joint_visible_prob([char2idx[c] for c in "我爱马云爸爸"]))) print(np.log(hmm.predict_joint_visible_prob([char2idx[c] for c in "马云爸爸爱我"]))) # ### 三.扩展 # # 只要是序列标注的问题其实都可以用到HMM,比如NLP中的词性标注(POS),命名实体识别(NER)等,只是换个不一样的隐状态标注即可,比如最上面的举例,如果是作NER的话,“北京天安门”就不该切分为“北京”和“天安门”,而是应该作为一个整体,所以训练集大概需要这样标注: # # # ![avatar](./source/12_HMM_NER.png) # # # 这里,同样采用了{B,M,E,S}标注集,只是面向的是实体,而不是分词,由于实体类型很多,所以{B,M,E}其实通常有多类,比如上面是针对地名的,那可能还有针对人名,机构名等,另外常用的实体标注集还有: # # (1){B,I,O}:B-X 代表实体X的开头, I-X代表实体的结尾 O代表不属于任何类型; # # (2){B,I,O,E,S}:B-X 表示开始,I-X表示内部, O表示非实体 ,E-X实体尾部,S-X表示该词本身就是一个实体 # In[ ]: