目录

  • 分步实现
    • 初始化函数:
    • 训练初始概率、转移概率函数:
    • 汉字转拼音函数(将汉字转化为对应的拼音,训练发射概率时使用):
    • 训练发射概率函数:
    • 获得拼音字典函数:
    • 维特比算法:
    • 输入测试集函数:
  • 全代码
  • 测试结果
    • 分析

分步实现

主要就是训练HMM模型,实现维特比算法。我的HMM模型包含以下7个函数:

初始化函数:

 '''初始化函数'''
def __init__(self,): self.init_pro = {} #初始概率 self.emiss_pro = {} #发射概率 self.trans_pro = {} #转移概率 self.pinyin_to_chinese= {} #拼音字典

主要包含HMM模型必备的三大概率(初始概率、发射概率、转移概率),以及为了实现拼音转汉字功能而定义的拼音字典。

训练初始概率、转移概率函数:

 '''训练初始概率、转移概率函数'''
def train_init_trans_pro(self,taotiao_path,): f = open(taotiao_path,encoding='utf-8') single_word = {} #存放单个词的频数 double_word = {} #存放两个词的频数 num = 0 for line in f.readlines(): temp =re.findall('[\u4e00-\u9fa5]+',line) for words in temp: pre = ' ' for word in words: #计算单个词的频数 if word in single_word: single_word[word] += 1 else: single_word[word]=1 #计算两个词的频数 if pre != ' ': if pre+word in double_word: double_word[pre+word] += 1 else: double_word[pre+word] = 1 pre = word num += 1 f.close() #求初始概率 for i in single_word.keys(): #取对数运算,避免出现概率过小 #     init_pro[i] = np.log(single_word[i]/num) self.init_pro[i] = single_word[i]/num #求转移概率 for i in double_word: #     trans_pro[i] = np.log(double_word[i]/single_word[i[0]]) self.trans_pro[i] = double_word[i]/single_word[i[0]]

传入的是头条的一个语料库进行训练,格式如下图:

初始概率统计的是每个字占总体字数的比例(字典格式:{字:概率,…}),转移概率统计的是 由前一个字转换为后一个字的概率(字典格式:{词组:概率,…})。

汉字转拼音函数(将汉字转化为对应的拼音,训练发射概率时使用):

def word2pinyin(self,text): py = pypinyin.lazy_pinyin(text) return py

训练发射概率函数:

'''训练发射概率函数''' def train_emiss_pro(self,taotiao_path): f = open(taotiao_path,encoding='utf-8') for line in f.readlines(): temp =re.findall('[\u4e00-\u9fa5]+',line) for words in temp: ans = self.word2pinyin(words) for i in range(len(ans)): if ans[i] not in self.emiss_pro: self.emiss_pro[ans[i]] = {} self.emiss_pro[ans[i]][words[i]] = 1 else: if words[i] not in self.emiss_pro[ans[i]]: self.emiss_pro[ans[i]][words[i]] = 1 else: self.emiss_pro[ans[i]][words[i]] += 1 f.close() for key in self.emiss_pro: s = sum(self.emiss_pro[key].values()) for key2 in self.emiss_pro[key]: #         emiss_pro[key][key2] = np.log(emiss_pro[key][key2]/s) self.emiss_pro[key][key2] = self.emiss_pro[key][key2]/s

发射概率指的时一个字在它这个读音下占的比例(因为同一个读音会有许多不同的字,这里不考虑音调),字典格式为;{拼音:{字1:概率1,字2:概率2,…},…}。

获得拼音字典函数:

'''获得拼音字典函数'''
def get_pinyin_dict(self,pinyin2hanzi_path,): f = open(pinyin2hanzi_path,encoding='utf-8') #按行读取 for line in f.readlines(): #将每行拼音与汉字之间的零宽不换行空格换为普通空格 line = re.sub(r'[\ufeff]','',line) #将每行按空格切分并放入line列表中,一共有两个部分,其中line[0]为拼音,line[1]为对应的汉字(一堆) line = line.strip().split() #存入拼音字典中 self.pinyin_to_chinese[line[0]] = line[1] f.close()

将每个读音对应的字分别存储起来,在最后拼音转汉字的时候用来生成隐藏序列。

维特比算法:

'''维特比算法''' def viterbi(self,word_list, pinyin_list, n, id2word,label_list,): """ 维特比算法求解最大路径问题 :param word_list:   每个拼音对应的隐藏状态矩阵 :param n:   可能观察到的状态数, 对应为汉字数量 :param id2word:    id到汉字的映射 :label_list:    原句 :return: """ T = len(word_list)  # 观察状态的长度 delta = np.zeros((T, n)) # 保存转移下标值 psi = np.zeros((T, n), dtype=int) # 初始化第一个字符的转移概率, 设置为每个词在词典中的单独出现的概率 words = word_list[0] for w in words: if id2word[w] not in self.init_pro: delta[0][w] = 0 else: delta[0][w] = self.init_pro[id2word[w]] # 动态规划计算 for idx in range(1, T): words = word_list[idx] for i in range(len(words)): max_value = 0 pre_words = word_list[idx-1] index = 0 for j in range(len(pre_words)): tmp_key = id2word[pre_words[j]] + id2word[words[i]] # 获得转移概率,如果不存在,转移概率则为0 if tmp_key in self.trans_pro: prob = self.trans_pro[tmp_key] else: prob = 0 tmp_value = delta[idx-1][pre_words[j]] * prob if max_value < tmp_value: max_value = tmp_value index = j # 计算观察状态到隐藏状态的概率 #             tmp_key = id2word[words[i]] + pinyin_list[idx]  if pinyin_list[idx] not in self.emiss_pro: emit_prob=0 elif id2word[words[i]] not in self.emiss_pro[pinyin_list[idx]]: emit_prob=0 else: #                 emit_prob = emiss_pro[pinyin_list[idx]][id2word[words[i]]] * max_value emit_prob = self.emiss_pro[pinyin_list[idx]][id2word[words[i]]] * max_value delta[idx][words[i]] = emit_prob psi[idx][words[i]] = pre_words[index] prob = 0 path = np.zeros(T, dtype=int) path[T - 1] = 1 # 获取最大的转移值 for i in range(n): if prob < delta[T - 1][i]: prob = delta[T - 1][i] path[T - 1] = i # 最优路径回溯 for t in range(T - 2, -1, -1): path[t] = psi[t+1][path[t+1]] # 生成解析结果 final_word = "" a=0 for i in range(T): final_word += id2word[path[i]] if id2word[path[i]]==label_list[i]: a+=1 print('转换:',final_word) print('准确率:',a*1.0/T)

使用了动态规划的思想,生成概率最大的序列作为输出序列。

输入测试集函数:

 def accury(self,test_path,): f = open(test_path,encoding='gb2312') lines = f.readlines() flag = 1 pinyin = [] label = [] for line in lines: if flag == 1: line = line.lower() line = line.split() pinyin.append(line) flag = 0 else: label.append(line) flag = 1 for i in range(len(pinyin)): pinyin_list = pinyin[i] label_list = label[i] word_list =  [] for i in pinyin_list: temp = re.findall(r'[\u4e00-\u9fa5]',self.pinyin_to_chinese[i]) word_list.append(temp) words = set() for wl in word_list: for w in wl: words.add(w) word2idx = dict() id2word = dict() idx = 0 for w in words: word2idx[w] = idx id2word[idx] = w idx += 1 # 将各个汉字转换为id表示 word_id_list = [None] * len(word_list) for i, wl in enumerate(word_list): word_id_list[i] = [None] * len(wl) for j, w in enumerate(wl): word_id_list[i][j] = (word2idx[w]) print('原句:',label_list) self.viterbi(word_id_list, pinyin_list, len(words), id2word,label_list, )

测试集格式如下(一行拼音,一行汉字):


全代码

import re
import numpy as np
import pypinyin '''HMM模型'''
class HMM(object): '''初始化函数''' def __init__(self,): self.init_pro = {} #初始概率 self.emiss_pro = {} #发射概率 self.trans_pro = {} #转移概率 self.pinyin_to_chinese= {} #拼音字典 '''获得拼音字典函数''' def get_pinyin_dict(self,pinyin2hanzi_path,): f = open(pinyin2hanzi_path,encoding='utf-8') #按行读取 for line in f.readlines(): #将每行拼音与汉字之间的零宽不换行空格换为普通空格 line = re.sub(r'[\ufeff]','',line) #将每行按空格切分并放入line列表中,一共有两个部分,其中line[0]为拼音,line[1]为对应的汉字(一堆) line = line.strip().split() #存入拼音字典中 self.pinyin_to_chinese[line[0]] = line[1] f.close() '''训练初始概率、转移概率函数''' def train_init_trans_pro(self,taotiao_path,): f = open(taotiao_path,encoding='utf-8') single_word = {} #存放单个词的频数 double_word = {} #存放两个词的频数 num = 0 for line in f.readlines(): temp =re.findall('[\u4e00-\u9fa5]+',line) for words in temp: pre = ' ' for word in words: #计算单个词的频数 if word in single_word: single_word[word] += 1 else: single_word[word]=1 #计算两个词的频数 if pre != ' ': if pre+word in double_word: double_word[pre+word] += 1 else: double_word[pre+word] = 1 pre = word num += 1 f.close() #求初始概率 for i in single_word.keys(): #取对数运算,避免出现概率过小 #     init_pro[i] = np.log(single_word[i]/num) self.init_pro[i] = single_word[i]/num #求转移概率 for i in double_word: #     trans_pro[i] = np.log(double_word[i]/single_word[i[0]]) self.trans_pro[i] = double_word[i]/single_word[i[0]] '''汉字转拼音函数'''     def word2pinyin(self,text): py = pypinyin.lazy_pinyin(text) return py '''训练发射概率函数''' def train_emiss_pro(self,taotiao_path): f = open(taotiao_path,encoding='utf-8') for line in f.readlines(): temp =re.findall('[\u4e00-\u9fa5]+',line) for words in temp: ans = self.word2pinyin(words) for i in range(len(ans)): if ans[i] not in self.emiss_pro: self.emiss_pro[ans[i]] = {} self.emiss_pro[ans[i]][words[i]] = 1 else: if words[i] not in self.emiss_pro[ans[i]]: self.emiss_pro[ans[i]][words[i]] = 1 else: self.emiss_pro[ans[i]][words[i]] += 1 f.close() for key in self.emiss_pro: s = sum(self.emiss_pro[key].values()) for key2 in self.emiss_pro[key]: #         emiss_pro[key][key2] = np.log(emiss_pro[key][key2]/s) self.emiss_pro[key][key2] = self.emiss_pro[key][key2]/s '''维特比算法''' def viterbi(self,word_list, pinyin_list, n, id2word,label_list,): """ 维特比算法求解最大路径问题 :param word_list:   每个拼音对应的隐藏状态矩阵 :param n:   可能观察到的状态数, 对应为汉字数量 :param id2word:    id到汉字的映射 :return: """ T = len(word_list)  # 观察状态的长度 delta = np.zeros((T, n)) # 保存转移下标值 psi = np.zeros((T, n), dtype=int) # 初始化第一个字符的转移概率, 设置为每个词在词典中的单独出现的概率 words = word_list[0] for w in words: if id2word[w] not in self.init_pro: delta[0][w] = 0 else: delta[0][w] = self.init_pro[id2word[w]] # 动态规划计算 for idx in range(1, T): words = word_list[idx] for i in range(len(words)): max_value = 0 pre_words = word_list[idx-1] index = 0 for j in range(len(pre_words)): tmp_key = id2word[pre_words[j]] + id2word[words[i]] # 获得转移概率,如果不存在,转移概率则为0 if tmp_key in self.trans_pro: prob = self.trans_pro[tmp_key] else: prob = 0 tmp_value = delta[idx-1][pre_words[j]] * prob if max_value < tmp_value: max_value = tmp_value index = j # 计算观察状态到隐藏状态的概率 #             tmp_key = id2word[words[i]] + pinyin_list[idx]  if pinyin_list[idx] not in self.emiss_pro: emit_prob=0 elif id2word[words[i]] not in self.emiss_pro[pinyin_list[idx]]: emit_prob=0 else: #                 emit_prob = emiss_pro[pinyin_list[idx]][id2word[words[i]]] * max_value emit_prob = self.emiss_pro[pinyin_list[idx]][id2word[words[i]]] * max_value delta[idx][words[i]] = emit_prob psi[idx][words[i]] = pre_words[index] prob = 0 path = np.zeros(T, dtype=int) path[T - 1] = 1 # 获取最大的转移值 for i in range(n): if prob < delta[T - 1][i]: prob = delta[T - 1][i] path[T - 1] = i # 最优路径回溯 for t in range(T - 2, -1, -1): path[t] = psi[t+1][path[t+1]] # 生成解析结果 final_word = "" a=0 for i in range(T): final_word += id2word[path[i]] if id2word[path[i]]==label_list[i]: a+=1 print('转换:',final_word) print('准确率:',a*1.0/T) def accury(self,test_path,): f = open(test_path,encoding='gb2312') lines = f.readlines() flag = 1 pinyin = [] label = [] for line in lines: if flag == 1: line = line.lower() line = line.split() pinyin.append(line) flag = 0 else: label.append(line) flag = 1 for i in range(len(pinyin)): pinyin_list = pinyin[i] label_list = label[i] word_list =  [] for i in pinyin_list: temp = re.findall(r'[\u4e00-\u9fa5]',self.pinyin_to_chinese[i]) word_list.append(temp) words = set() for wl in word_list: for w in wl: words.add(w) word2idx = dict() id2word = dict() idx = 0 for w in words: word2idx[w] = idx id2word[idx] = w idx += 1 # 将各个汉字转换为id表示 word_id_list = [None] * len(word_list) for i, wl in enumerate(word_list): word_id_list[i] = [None] * len(wl) for j, w in enumerate(wl): word_id_list[i][j] = (word2idx[w]) print('原句:',label_list) self.viterbi(word_id_list, pinyin_list, len(words), id2word,label_list, ) #主函数
if __name__=='__main__': pinyin2hanzi_path = 'E://大三上//自然语言//实验//实验二//pinyin2hanzi.txt' taotiao_path = 'E://大三上//自然语言//实验//实验二//toutiao_cat_data.txt' test_path = 'E://大三上//自然语言//实验//实验二//测试集.txt' hmm = HMM() hmm.get_pinyin_dict(pinyin2hanzi_path) hmm.train_init_trans_pro(taotiao_path) hmm.train_emiss_pro(taotiao_path) hmm.accury(test_path)

测试结果


分析

(1)开始的时候,对于三大概率的计算结果使用取对数的方法,这样使得概率不会太小,但是在后面维特比算法计算的时候就不能使用概率相乘的方法计算最长路径,改用加法效果也不好,最后还是使用的原始概率。
(2)对于发射概率与转移概率的存储方式有不小的纠结,最后还是由于字典检索比矩阵更加方便而使用了字典进行存储。

实验一:基于HMM的拼音转汉字程序|自然语言相关推荐

  1. 基于HMM的拼音转汉字程序

    本文将讲述怎样利用HMM进行拼音转汉字. 准备阶段 python 2.7: 安装 python 工具包 ChineseTone,直接使用 pip install 安装: 运行程序的过程中,可能还会用到 ...

  2. pyqt tcp通信_实验十 基于PyQt界面的TCP通信程序(一).doc_学小易找答案

    [简答题]请同学们找一个目前已学的知识点,出一个题目上传,不能是上课讲的程序和上机的题目 [简答题]请拍照模块五完成情况 [简答题]请拍照模块六完成情况 [计算题]书本80页,3-7;3-8;3-9; ...

  3. 如何实现拼音与汉字的互相转换

    如何实现拼音与汉字的互相转换 发表于2个月前(2016-02-16 15:01)   阅读(58) | 评论(0) 6人收藏此文章, 我要收藏 赞0 4月23日,武汉源创会火热报名中,期待您的参与&g ...

  4. 在PostgreSQL中实现按拼音、汉字、拼音首字母搜索的例子

    在PostgreSQL中实现按拼音.汉字.拼音首字母搜索的例子 作者 digoal 日期 2016-11-09 标签 PostgreSQL , 拼音 , 中文分词 , tsvector , 拼音首字母 ...

  5. 计算机网络课程设计之基于 IP 多播的网络会议程序

    前言 本实验难点在于环境的配置,尤其是多网卡配置,经过查阅资料和多次小伙伴们测试,最后终于找到问题的根源 问题分析和配置主要放在实验结果与分析栏中 结尾附上指导书的IP多播源码 白嫖容易,创作不易,本 ...

  6. 基于C++的Qt网络编程——基于 IP 多播的网络会议程序

    目录 一.实验题目 二.实验目的 三.总体设计 1.实验原理 2.设计步骤 四.详细设计 1.程序流程图 2.实验代码(部分) 五.实验结果与分析 六.小结与心得体会 一.实验题目 基于 IP 多播的 ...

  7. 基于马尔可夫HMM的拼音输入法自然语言处理实现

    两个py文件,一个是利用文本训练并保存,另外一个是拼音转汉字,基于隐马尔可夫模型HMM,拼音输入法可以按注音符号与汉语拼音两种汉字拼音方案分成两大类.汉语拼音输入法的编码是依据汉语拼音方案(汉字的读音 ...

  8. python编程输出汉字_课内资源 - 基于Python的拼音汉字转换程序

    1.实验内容利用统计语言模型实现拼音汉字转换 输入:拼音串,输出:对应的汉字串 给定10000字的测试语料,测试音字转换的准确率 针对音字转换结果中存在的问题给出具体分析 以图表的形式表示上述结果 2 ...

  9. 《数字语音处理》- 实验4. 基于MATLAB与VQ的特定人孤立词语音识别研究(附代码)

    声明 本文仅在CSDN发布,未经允许请勿转载或引用! 正版链接: https://blog.csdn.net/meenr/article/details/117629850 MATLAB基于VQ的特定 ...

  10. 通过拼音模糊搜索汉字的功能实现

    一.原由 前一段时间用php实现通讯录系统,需要用到拼音查找汉字功能,匹配通讯录的姓名字段,于是在网上搜索已有的开源代码,下面总结和分析一下思路和具体实现. 二.思路 查找了网上的多种解决方案,大致分 ...

最新文章

  1. Python的控制语句2
  2. LocalDateTime、OffsetDateTime、ZonedDateTime互转,这一篇绝对喂饱你
  3. java代码读取dbsequence的值_MongoDB自增序列实现 - Java多线程同步 synchronized 用法
  4. Uncaught TypeError: Object [object Object] has no method 'xxx'
  5. HTML5调用手机前置摄像头或后置摄像头拍照,canvas显示,经过Android测试
  6. CPU并行与GPU并行联系及区别
  7. mysql中当前时间九点_MySQL 获得当前日期时间(以及时间的转换)
  8. 【Elastischearch】7.6 版本 update 后 refresh 慢,性能问题导致稳定性问题
  9. python百万并发压测_100W高并发(转载) - 橙子柠檬's Blog
  10. 永中office java_永中office怎么样?使用过的说一下感受如何?
  11. 面向对象实现气缸吹气类的PLC逻辑
  12. 无锡旅游景点古文化的调研报告
  13. 基于python+django框架+Mysql数据库的校园运动场地预约系设计与实现
  14. sleep、yield、join方法简介与用法 sleep与wait区别 多线程中篇(十五)
  15. 基于jsp+mysql+Spring+mybatis java的SSM健身房管理系统
  16. 小学计算机课 标语,小学教室的标语
  17. cad移动时捕捉不到基点_CAD中捕捉基点不能用怎么办?就是用fro不行?
  18. 共模电感磁芯材质你知道哪几种
  19. MyEclipse创建jsp项目
  20. 10个月时间,CMO如何挽救这家破产的电商巨头?

热门文章

  1. Python 实现大量图片裁剪拼接并生成PDF
  2. check异常和uncheck异常的区别b
  3. QT操作word表格——垂直居中、水平居中
  4. Ubuntu 16.04 单显卡安装Nvidia驱动+GTX750显卡安装CUDA 9.1+cuDNN 7.1.3
  5. 各地前端工资是多少?三线城市的前端有多少
  6. 《赖氏经典英语语法》第五集
  7. 差分 线宽 线距_线宽、线距规则设置到底怎样最合适?
  8. “萌新”商家应该如何选择电商直播平台呢?
  9. 【EasyUI】如何根据条件控制可编辑表格某字段是否可以编辑;
  10. Unity 代码实现锁定手机横屏