机器学习——基于HMM的中文分词

机器学习——基于HMM的中文分词

1、代码展示

#encoding=utf-8

'''
B表示词汇的开始
M表示词汇的中间
E表示词汇的尾部
S表示词汇单独成词
'''
class HMM(object):
   def __init__(self):
       self.states = ['B','M','E','S']
       self.load_para = False
       self.A_dic ={}
       self.B_dic = {}
       self.Pai_dic = {}
   def generatePropbility(self,path):
       '''
       统计概率,生成π,A,B
       '''
       count_dic = {} #统计各个状态的次数,用于后面的归一化使用
       
       def init_parameters():
           '''
           初始化pai,A,B三个权重矩阵
           '''
           for state in self.states:
               self.A_dic[state] = {s:0.0 for s in self.states}
               self.Pai_dic[state] = 0.0
               self.B_dic[state] = {}
               count_dic[state] = 0
       def markLaebl(word):
           '''
           为训练数据中的每一个字,根据其所处的位置,打标签
           text是传入的一个词
           '''
           out_text = []
           if(len(word) == 1):
               out_text.append('S')
           else:
               #将输入的文本转换成标签序列的表示
               out_text += ['B'] + ['M'] * (len(word)-2)+['E']
           return out_text
       
       init_parameters()
       
       line_num = -1
       words = set()
       
       with open(path,'r',encoding='utf-8') as f:
           data_lines = f.readlines() #获取训练数据
       
       #对于数据中的每一行进行处理
       for line in data_lines:
           line_num += 1
           line = line.strip()
           if not line:
               continue
           word_list = [i for i in line if i != " "]
           words |= set(word_list)  #words中去除了重复的词汇
           linelist = line.split()
           line_state = []
           for w in linelist:
               line_state.extend(markLaebl(w)) #将每一个词汇转换成标签 此时的line_state是一个list,表示一个句子所有的标签
           assert len(word_list) == len(line_state)
           for k,v in enumerate(line_state):
               count_dic[v] += 1 #计算各个标签出现的总数
               if k == 0:
                   self.Pai_dic[v] += 1 #计算各个标签出现在第一个位置概率
               else:
                   self.A_dic[line_state[k-1]][v] += 1 #A_dic词典的第一个索引是前一个状态,v表示当前状态
                   #B_dic词典的第一个索引是当前的状态,第二个所有是当前状态对应的字
                   self.B_dic[line_state[k]][word_list[k]] = \
                       self.B_dic[line_state[k]].get(word_list[k],0) + 1.0
       
       #转换成概率
       self.Pai_dic = {k:v*10/line_num for k,v in self.Pai_dic.items()}
       self.A_dic = {k:{k1:v1/count_dic[k] for k1,v1 in v.items()} for k,v in self.A_dic.items()}
       #这里使用到了加1平滑
       self.B_dic = {k:{k1:(v1+1.0)/count_dic[k] for k1,v1 in v.items()} for k,v in self.B_dic.items()}
   
   def viterbi(self,text,states,pai,A,B):
       '''
       维特比算法过程
       '''
       V = [{}] 
       path = {}
       for y in states: #第一步
           V[0][y] = pai[y] * B[y].get(text[0],0) #第一个参数是时间,第二个参数是对应的状态
           path[y] = [y]
       for t in range(1,len(text)): #从第二个字的标签开始迭代
           V.append({})
           newpath = {}
       # 检查B中是否有产生该字的概率
           neverSeen = text[t] not in B['S'].keys() and \
             text[t] not in B['M'].keys() and \
             text[t] not in B['B'].keys() and \
             text[t] not in B['E'].keys()
           #对于每一个可能的状态进行取最大值的操作
           for y in states:
               #这里先计算每一个状态生成该字的概率PB
               PB = B[y].get(text[t],0) if not neverSeen else 1.0
               # y0是前一个时刻的每一个状态,取概率的最大值和对应的前一个时刻的状态
               (prob,state) = max([(V[t-1][y0]*A[y0].get(y,0)*PB,y0) for y0 in states if V[t-1][y0]>0])
               V[t][y] = prob
               #这里更新路径,state是前一个时刻的状态,y是当前时刻的状态
               newpath[y] = path[state]+[y]
           #更新总的路径
           path = newpath
       #判断最后一个字是单独成词还是属于一个词的一部分
       if B['M'].get(text[-1],0) > B['S'].get(text[-1],0):
           (prob,state) = max([(V[len(text)-1][y],y) for y in ('E','M')])
       else:
           (prob,state) = max([(V[len(text)-1][y],y) for y in states])
       return (prob,path[state])
   def cut(self,text):
       '''
       切分过程
       '''
       #获取最大最大概率和状态列表
       prob,pos_list = self.viterbi(text,self.states,self.Pai_dic,self.A_dic,self.B_dic)
       begin,next = 0,0
       for i,char in enumerate(text):
           pos = pos_list[i]
           if pos == "B":
               begin = i
           elif pos == "E":
               yield text[begin:i+1]
               next = i+1
           elif pos == 'S':
               yield char
               next = i+1
       if next < len(text):
           yield text[next:]

2 训练数据格式

机器学习——基于HMM的中文分词_第1张图片

你可能感兴趣的:(机器学习,NLP学习)