HMM用于中文分词

一、在分词、分句前用规则处理句子

#在分词前用规则处理句子
def preprocess(oriSentence):
    #删除后缀
   
oriSentence = deleteTMword(oriSentence,'\r')
    oriSentence = deleteTMword(oriSentence,'\n')
    oriSentence = deleteTMword(oriSentence,'\t')
    #load the rules
    #
加载规则
   
deletes = Rule.Rules["DELETE"]
    updates = Rule.Rules["CHANGE"].keys()
    #we must delete the whitespaces.
    #
对于空格进行删除
   
deletes.append(' ')
    deletes.append(' ')
    sentence = ''
   
for char in oriSentence:
        #对于词组进行删除操作
       
if(char in deletes):
            sentence += ''
       
#对词组进行替换操作
       
elif(char in updates):
            sentence += Rule.Rules["CHANGE"][char]
        #不进行操作
       
else:
            sentence += char
    return sentence

二、获取文本中所有的句子

##将一个段落切割成几个最简单的句子
def segmentSentence(text,isForSegmentWord=False):
    #载入需要进行切割的符号
   
lst = [',','!','?','…','','','','','"','“','”','\'','‘','’',':','',';','','—','(',')','','']
    #是否需要进一步切分
   
if(isForSegmentWord):
        #these punctuations shouldn't cut the sentence.
        #
这些标点不应该删减句子。
       
lst += ['','','','<','>']
    #这里是对于输出格式的处理
   
assert isinstance(text, str)
    text_seg = ""
   
for char in text:
        text_seg += char in lst and '|' or char
    sentence_lst = text_seg.split('|')
    while('' in sentence_lst):
        sentence_lst.remove('')
    return sentence_lst

三、获取文本中所有的词组

(1)解析离线统计词典文本文件,将词和词频提取出来,获取每个单词所有的前缀词,如果某前缀词不在前缀词典中,则将对应词频设置为0。

#Dict中的每个单词切成作为前缀,基于前缀词典,对输入文本进行切分,便于之后构造无环图
def generatePrefixDict():
    global Possibility,PrefixDict,total,Dictionary_URL
    fo = Lexicon.loadDict(Dictionary_URL)
    PrefixDict = set()
    FREQ = {}
    for line in fo.read().rstrip().split("\n"):
        word,freq = line.split(' ')[:2]
        FREQ[word] = float(freq)
        total += float(freq) #calculate the total number of words
       
for idx in range(len(word)): #generate the prefix dictionary
           
prefix = word[0:idx+1]
            PrefixDict.add(prefix)
    fo.close()
    #将频率转化为可能性                                 
   
Possibility = dict((key,log(value/total)) for key,value in FREQ.items())

(2)构造有向无环图,对于每一种划分,都将相应的位置相连:

从前往后依次遍历文本的每个位置,对于位置k,首先形成一个片段,这个片段只包含位置k的字,然后就判断该片段是否在前缀词典中,如果这个片段在前缀词典中,

1.如果词频大于0,就将这个位置i追加到以k为key的一个列表中;

2.如果词频等于0,则表明前缀词典存在这个前缀,但是统计词典并没有这个词,继续循环;

如果这个片段不在前缀词典中,则表明这个片段已经超出统计词典中该词的范围,则终止循环;然后该位置加1,然后就形成一个新的片段,该片段在文本的索引为[k:i+1],继续判断这个片段是否在前缀词典中。

#对于每一种划分,都将相应的位置相连,构造有向无环图
def getDAG(sentence:str):
    global PrefixDict,Possibility
    DAG = {}
    N = len(sentence)
    for i in range(N):
        lst_pos = []
        #这个列表是保存那些可以用第i个字符组成单词的字符的索引。
       
j = i
        word = sentence[i]
        lst_pos.append(i)
        while(j<N and word in PrefixDict):
            if(word in Possibility):
                if(lst_pos[0]!=j):
                    lst_pos.append(j)
            j+=1
            word = sentence[i:j+1]
        #把这个第i个列表放入DAG[i]
       
DAG[i]=lst_pos
    return  DAG

(3)函数根据已经构建好的有向无环图计算最大概率路径,动态规划查找最大概率路径,计算每个开始字符的最可能路线,找出基于词频的最大切分组合

#动态规划查找最大概率路径,计算每个开始字符的最可能路线,找出基于词频的最大切分组合
def calculateRoute(DAG:dict,route:dict,sentence:str):
    #from right to left
   
global Possibility , min_Possibility
    N = len(sentence)
    route[N]=(0.0,'')
    #In fact, it is a recurse procedure.
   
for i in range(N-1,-1,-1):
        max_psblty = None
        max_cur = i
        for j in DAG[i]:
            #to consider every possible word
           
word = sentence[i:j+1]
            posb1 = Possibility.get(word,min_Possibility)
            posb2 = route[j+1][0]
            posb = posb1 + posb2
            if max_psblty is None:
                max_psblty = posb
            if(posb>=max_psblty):
                max_psblty = posb
                max_cur = j
        route[i] = (max_psblty,max_cur)

(4)对于不在词典中的词,使用HMM的方法,对这些词进行标注

__init__.py 实现了HMM模型识别未登录词;

prob_start.py 存储了已经训练好的HMM模型的状态初始概率表;

prob_trans.py 存储了已经训练好的HMM模型的状态转移概率表;

prob_emit.py 存储了已经训练好的HMM模型的状态发射概率表;

1)获取无法识别单词的索引,作为标志来分割句子

#删去不认识的句子,这句话可以用engchs处理
def cutUnrecognized(sentence):
    #first we need to find all indexes of abnormal char
    #
首先我们需要找到异常单词的所有索引
   
eng_dgt_start,eng_dgt_end = findEngAndDigits(sentence)
    cur = 0
    N = len(eng_dgt_start)
    L = len(sentence)
    words = []
    #And then we need to ues them as the flag to segment the sentence
    #
然后我们需要将它们作为标志来分割句子
   
if(N!=0):
        for t in range(N):
            begin = eng_dgt_start[t]
            end = eng_dgt_end[t]
            if(t==0 and begin!=0):
                buf = sentence[0:begin]
                words += cutWithHMM(buf)
            if(t!=N-1):
                next_start = eng_dgt_start[t+1]
                buf = sentence[begin:end+1]
                words.append(buf)
                buf = sentence[end+1:next_start]
                words += cutWithHMM(buf)
            else:
                buf = sentence[begin:end+1]
                words.append(buf)
                if(end!=L-1):
                    buf = sentence[end+1:L]
                    words += cutWithHMM(buf)
    else:
        words += cutWithHMM(sentence)
    return words

2)调用viterbi算法,求出输入句子的隐藏状态,然后基于隐藏状态进行分词。
def cutWithHMM(sentence):
    #possess the english and digits
    #find their indexes
   
init_P()
    words = []
    #Viterbi是隐马尔科夫模型中用于确定(搜索)已知观察序列在HMM;下最可能的隐藏序列
    #通过viterbi算法求出隐藏状态序列
   
prob,path = viterbi(sentence)
    buf = "" # temporary variable

#基于隐藏状态序列进行分词
    for i in range(len(sentence)):
        flag = path[i]
        if(flag=='S'):
            buf = sentence[i]
            words.append(buf)
            buf=""
       
elif(flag=='B'):
            buf = sentence[i]
        elif(flag=='M'):
            buf = buf + sentence[i]
        elif(flag=='E'):
            buf = buf+sentence[i]
            words.append(buf)
            buf=""
   
return words

3)viterbi算法的实现

MIN_FLOAT = -3.14e100
#states
States = ('B','M','E','S')
#every possible previous of each state
PrevStatus = {
    'B':('E','S'),
    'M':('M','B'),
    'S':('S','E'),
    'E':('B','M')
}

def viterbi(obs):
    V = [{}]
    path={}
    #possess the first char
   
for s in States:
        #this state's possibility is the start * emit
       
V[0][s] = start_P[s] + emit_P[s].get(obs[0],MIN_FLOAT)
        #record this path
       
path[s] = [s]
    #poseess the other chars
   
for i in range(1,len(obs)):
        char = obs[i]
        V.append({})
        newPath = {}
        #check every state it can be
       
for s in States:
            #assume that in this case and calculate what the possibility is
           
emit =  emit_P[s].get(char, MIN_FLOAT) #emit it
           
prob_max = MIN_FLOAT
            state_max = PrevStatus[s][0]
            for preState in PrevStatus[s]:
                #calculate the previous state
                
prob = V[i-1][preState] + trans_P[preState][s] + emit
                if(prob>prob_max):
                    prob_max = prob
                    state_max = preState
            V[i][s] = prob_max
            newPath[s] = path[state_max] + [s]
        path = newPath
    finalProb = MIN_FLOAT
    finalState = ""
   
for fs in ('E','S'):
        p = V[len(obs)-1][fs]
        if(p>finalProb):
            finalProb = p
            finalState = fs
    return (finalProb,path[finalState])
#judge if the char is a digit or a english char or a punctuation
def isDigitOrEng(char):
    return char in string.ascii_letters or char in string.digits or char in string.punctuation
#this is to find the indexes of non-Chinese chars of this sentence
def findEngAndDigits(sentence):
    ind_start = []
    ind_end = []
    for i in range(len(sentence)):
        if(i==0 and isDigitOrEng(sentence[i])):
            ind_start.append(i)
            if(not isDigitOrEng(sentence[i+1])):
                ind_end.append(i)
        elif(i==len(sentence)-1 and isDigitOrEng(sentence[i])):
            ind_end.append(i)
            if(not isDigitOrEng(sentence[i-1])):
                ind_start.append(i)
        else:
            if(isDigitOrEng(sentence[i]) and (not isDigitOrEng(sentence[i-1]))):
                ind_start.append(i)
            if(isDigitOrEng(sentence[i]) and (not isDigitOrEng(sentence[i+1]))):
                ind_end.append(i)
    return ind_start,ind_end

(5)对于词组进行切分的主函数

#对于词组进行切分
def segmentWord(oriSentence,useRules=True):
    #用规则进行处理                                                             
   
sentence = preprocess(oriSentence)
    if(not isInitialized):
        initialize()

#构造无向图
    DAG = getDAG(sentence)
    route = {}
    #动态规划查找最大概率路径,计算每个开始字符的最可能路线
   
calculateRoute(DAG,route,sentence)
    words = []
    cur = 0
    buf = ""
   
N = len(sentence)
    #cur is the current curse of state
    #cur
是当前状态的几率
   
while(cur<N):
        next_cur = route[cur][1]
        word = sentence[cur:next_cur+1]
        #如果单词只是一个字符,那么它可能是未登录的单词
       
#buf 是临时部分。
       
if(len(word)==1):
            buf+=word
        else:
            #until we have next word which is not single.
           
if(len(buf)!=0):
                #we have buf
                #if this word is in dictionary, usually not
                #or if it is a single word
               
if(buf in Possibility or len(buf)==1):
                    words.append(buf)
                else:
                #if not , it is an un-login word.
                #we must use HMM to cut them
                #if not
,它是一个未登录词。
#我们必须使用 HMM 来切割它们,采用的HMM模型了,中文词汇按照BEMS四个状态来标记.
#
在这个函数中,就是使用HMM的方法,对这些未识别成功的词进行标注
                   
words += HMM.cutUnrecognized(buf)
                #clear the buf
               
buf=""
           
words.append(word)
        cur = next_cur+1
    if(buf):
        words += HMM.cutUnrecognized(buf)
    return words

基于隐马科夫模型,HMM用于中文分词相关推荐

  1. 人脑与计算机类比文献,人脑计算机接口基于隐马科夫模型的思维运动异步分类...

    摘要: 近年来,人脑计算机接口(Brain-Computer Interface)技术在生理学和信息学研究的基础上不断取得进展.BCI技术的发展以及成功运用将会给人类社会带来巨大的便利.本文通过对人脑 ...

  2. 转:从头开始编写基于隐含马尔可夫模型HMM的中文分词器

    http://blog.csdn.net/guixunlong/article/details/8925990 从头开始编写基于隐含马尔可夫模型HMM的中文分词器之一 - 资源篇 首先感谢52nlp的 ...

  3. 从头开始编写基于隐含马尔可夫模型HMM的中文分词器之一 - 资源篇

    首先感谢52nlp的系列博文(http://www.52nlp.cn/),提供了自然语言处理的系列学习文章,让我学习到了如何实现一个基于隐含马尔可夫模型HMM的中文分词器. 在编写一个中文分词器前,第 ...

  4. 隐含马尔可夫模型HMM的中文分词器 入门-1

    <pre name="code" class="sql">http://sighan.cs.uchicago.edu/bakeoff2005/ ht ...

  5. 【NLP】用于语音识别、分词的隐马尔科夫模型HMM

    大家好,今天介绍自然语言处理中经典的隐马尔科夫模型(HMM).HMM早期在语音识别.分词等序列标注问题中有着广泛的应用. 了解HMM的基础原理以及应用,对于了解NLP处理问题的基本思想和技术发展脉络有 ...

  6. m基于隐马尔科夫模型(HMM)的手机用户行为预测(MMUB)算法matlab仿真

    目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 隐马尔可夫模型(Hidden Markov Model,HMM)是一种统计模型,广泛应用在语音识别, ...

  7. python地图匹配_基于隐马尔科夫模型(HMM)的地图匹配(Map-Matching)算法

    1. 摘要 本篇博客简单介绍下用隐马尔科夫模型(Hidden Markov Model, HMM)来解决地图匹配(Map-Matching)问题.转载请注明网址. 2. Map-Matching(MM ...

  8. 隐马尔科夫模型HMM(三)鲍姆-韦尔奇算法求解HMM参数

    在本篇我们会讨论HMM模型参数求解的问题,这个问题在HMM三个问题里算是最复杂的.在研究这个问题之前,建议先阅读这个系列的前两篇以熟悉HMM模型和HMM的前向后向算法,以及EM算法原理总结,这些在本篇 ...

  9. 一、隐马尔科夫模型HMM

    隐马尔科夫模型HMM(一)HMM模型基础 隐马尔科夫模型(Hidden Markov Model,以下简称HMM)是比较经典的机器学习模型了,它在语言识别,自然语言处理,模式识别等领域得到广泛的应用. ...

最新文章

  1. CRM User Status profile中Business Transaction字段的用途
  2. 自学python需要多长时间-零基础自学python要多久?
  3. 前端登陆加密和后端解密
  4. java list有序还是无序_最详细的Java学习点知识脑图,从基础到进阶,看完还有啥你不懂的...
  5. mysql alter怎么用,mysql alter话语用法
  6. 【Flink】Flink 或者 Spark the client is stop
  7. attrib批量显示文件夹_Windows 下彻底隐藏文件和文件夹的方法
  8. [Java] 蓝桥杯ADV-233 算法提高 队列操作
  9. 高效开发Android App的10个建议
  10. 每日算法系列【LeetCode 943】最短超级串
  11. 微信后台红包系统架构设计与最佳实践
  12. css中怎么把数字改成罗马数字,罗马数字和阿拉伯数字相互转换
  13. 重装系统后计算机无法联网,韩博士重装系统后电脑无法上网怎么办?
  14. 关于诡辩--偷换概念
  15. 绕圈圈面试题(Python经典编程案例)
  16. Solidworks如何为装配体绘制剖面视图
  17. js中特殊字符以及转义
  18. 6.27软件园与血站见习报告
  19. Unity Navigation
  20. 突破路缘石建设桎梏中利用沥青拦水带成型机实现

热门文章

  1. 量化投资回测教学之掌握矢量化回测
  2. vue跳转下个页面关闭当前页面_vue页面跳转 vue生命周期
  3. 半导体材料 MOOC学习记录 第十四章 新型半导体材料SiC
  4. Spark sql之集成Hbase-connectors
  5. ios h5 长按放大镜效果关闭
  6. verdi仿真中看波形经常犯的错误
  7. MIT-Adobe FiveK Dataset 图片自动下载
  8. 逆向入门分析实战(一)
  9. mysql提示python没有安装_MySQL-Python安装问题小记
  10. windows微信如何双开