0 项目说明
汉语多音字注音研究
提示:适合用于课程设计或毕业设计,工作量达标,源码开放
1 准确率
计算准确率的方法:
某个数据集上的准确率 = 该数据集中所有标注正确的多音字数 / 数据集中含有的多音字总数
之前的准确率计算有些问题,将<unk>
和<pad>
也算作多音字了,改正之后准确率变为85% - 88%。
我们试着增加了LSTM的层数,发现在当前情况下,随着层数增加,准确率有所降低。
2 调研阶段
查了一些国内外的论文,总结了一下其中用到的方法,以下是对这个问题的一些认识。
1.多音字个数是有限的,要根据其在不同情况下的读音来建立规则,以后再遇到新词时就去匹配这些规则,无法匹配时就更新规则。应该是通过机器学习实现的。
2.对这个问题的研究经历了几个过程
- 早期是对多音字统计不同读音下的使用频率,以频率最高的读音来标注新词。准确率低
- 统计法。通过构建词库,建立规则来进行注音,是以词语为单位,准确率有所提升,但仍无法解决多音字前后的字一样但读音不同的情况。
- 规则法。语言学家建立规则,再输入到计算机,但无法指定一个很完备的规则集
3.论文中的解决方案
《中文文语转换系统中多音字问题研究》
[1]李清. 中文文—语转换系统中多音字问题研究[D].河北大学,2010.
对于低频多音字,即某个出现频率很低的多音字,它在绝大多数情况下都读一个音,只在少部分情况下读另一个音时,通过对每个字建立一个读音表,将相应词条和读音输入,注音时进行匹配即可。如果遇到没有收入的词语,就按高频音来注音。
对于中高频多音字又有三种情况
某个音只出现在特定的词语或者语境中,则通过分词之后进行匹配。
某个音覆盖率百分之99以上,采用和低频多音字一样的解决方法。
某个字低频读音和高频读音的频率差不多一样时,需要人工建立规则
《一种新的基于规则的多音字自动注音方法》
[1]郑敏. 一种新的基于规则的多音字自动注音方法[A]. 中国中文信息学会.第二届全国学生计算语言学研讨会论文集[C].中国中文信息学会:中国中文信息学会,2004:6.
通过对多音字进行特征提取,总结出八条特征,对一些高频常见的多音字根据其不同音的使用场景利用总结出的特征建立规则。
在统计时首先对句子进行分词,得到含有多音字的词语,根据该字所在的词语以及上下文的特征去检索规则,得到读音。
《统计与规则相结合的多音字自动注音方法研究与实现》
利用决策树对多音字中高频音和低频音相差不多的情况进行判别。
《汉语字音转换中的多层面多音字读音消歧》
采用决策树和人工建立规则相结合的方法。
《基于规则及SVM权值训练的汉语多音字自动消歧研究》
胡国平. 基于规则及SVM权值训练的汉语多音字自动消歧研究[A]. Northeastern University、Tsinghua University、Chinese Information Processing Society of China、Chinese Languages Computer Society, USA.Advances in Computation of Oriental Languages--Proceedings of the 20th International Conference on Computer Processing of Oriental Languages[C].Northeastern University、Tsinghua University、Chinese Information Processing Society of China、Chinese Languages Computer Society, USA:中国中文信息学会,2003:7.
《解决多音字字-音转换的一种统计学习方法》
1]张子荣,初敏.解决多音字字-音转换的一种统计学习方法[J].中文信息学报,2002(03):39-45.
基于扩展的随机复杂度的随机决策列表方法
《基于最大熵模型的多音字消歧》
[1]刘方舟. 基于最大熵模型的多音字消歧[A]. 中文信息学会语音信息专业委员会、中国声学学会语言、听觉和音乐声学分会、中国语言学会语音学分会.第九届全国人机语音通讯学术会议论文集[C].中文信息学会语音信息专业委员会、中国声学学会语言、听觉和音乐声学分会、中国语言学会语音学分会:清华信息科学与技术国家实验室(筹),2007:6.
使用最大熵增模型和决策树的方法。
《A Comparative Study of Diverse Knowledge Sources and Smoothing Techniques via Maximum Entropy for Polyphone Disambiguation in Mandarin TTS Systems 》
采用多种算法比较。最大熵增模型
《A Method of Part of Speech Based on the Algorithm of C4.5 for Polyphone》
使用C4.5算法,一种产生决策树的算法
《A Bi-directional LSTM Approach for Polyphone Disambiguation in Mandarin Chinese》
采用LSTM(长短期记忆),一种神经网络
3 数据统计
用到的数据见Code里的data目录。
pinyindata.txt :汉字拼音库,使用《现代汉语词典》中的拼音数据,汉字数:11017,多音字个数:1217,来自pinyin-data
polyphones.txt:将汉字拼音库中的多音字全部提取出来,存入该文件中
统计了一下多音字的读音个数。
polyphones.json:将所有多音字和读音存储成json文件
198801.txt:人民日报1988年一月的新闻语料,一共有19374条新闻,包含5593个多音字,其中有85种不同的多音字,来自pkuopendata
198801output.txt:存储每个多音字出现的次数,按出现频次从多到少排序
news.txt:将所有包含多音字的新闻全部提取出来,存入该文件
phrase.txt:带注音的中文短语,一共有96809条,来自ChineseTone
poly_phrase.txt:将所有含多音字的短语输出到该文件中,共有47289条含多音字的短语。每条短语前面的数字是多音字在短语中的位置(从0开始计算)。
对该文件中的所有句子按多音字出现次数的多少排序,并对每个句子中多音字的个数进行了统计。结果如下。
句子中包含的多音字个数 句子数量
phrase_frequency.txt :输出包含多音字的短语中所有多音字出现的次数,按出现次数从多到少排序。
phrase.csv:将短语和读音转换成特定格式(text, label),text为句子,label为读音,非多音字编码为’NA’,多音字编码为其读音。
correct.csv:标注正确的语句(只包含一句话中所有多音字均标注正确的句子)
wrong.csv:标注错误的语句(一句话中只要有一个多音字标注错误,就输出到该文件中)
4 语料处理
主要是将语料划分为训练集、验证集与测试集,并将句子和读音做tokenize,建立词典,设定batchsize与生成迭代器,使数据能送入模型进行训练。
在进行处理时使用了torchtext库,能方便的对文本进行处理,并且支持csv、tsv等格式的读取。
最终生成的数据集:
train.csv:训练集,有29928条短语,包含30041个多音字。
valid.csv:验证集,有8723条短语,包含12696个多音字。
test.csv:有8638条短语,包含19198个多音字。
5 模型搭建
目前想到用两种方法搭建模型。
两种方法模型大体一致,但在将lstm的输出喂进全连接层时的输入不一样。
说明:代码中的各种参数并不是最优的参数,只是一个示例。
6 按照多音字的读音的分类方法
这种方法将训练数据按照其中某个多音字的读音进行分类,如训练数据中含有多音字“都”的句子将分为以下两类:
- 句子中“都”的读音为dū的数据
- 句子中“都”的读音为dōu的数据
此方法最大的弊端就是需要对每个多音字单独训练一个模型并测试,另外当一句话中含有两个及以上的多音字时,该方法并不能完成所有多音字的注音。
7 仿照词性标注问题的分类方法
代码:code/PolyphoneDisambiguation/disambiguation_pos.py
这种方法仿照了词性标注的处理思路,将训练数据编码为如下格式:
(‘在古都西安’, ‘都’, [‘NA’, ‘NA’, ‘dū’, ‘NA’, ‘NA’])
其中“NA”代表非多音字,多音字则使用读音来编码。
目前主要使用该方法进行注音,已将短语数据送入模型并进行训练,然后使用测试集计算准确率,将标注正确的语句和标注错误的语句输出到相应文件中。
8 项目源码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
@Author: hhzjj
@Description:统计分析数据中的多音字个数等特征
"""
from collections import defaultdict
import json
from DataProcessing.poly_dic import load_dict
# 计算多音字库中多音字的个数
def count_polyphone():
with open("../data/pinyindata.txt", 'r', encoding='utf-8') as f1:
f1_data = f1.readlines()
count = 0
# f1_data.pop(0)
# f1_data.pop(0)
print("数据总长度 %d" % len(f1_data))
with open('../data/polyphones.txt', 'w', encoding='utf-8') as outputfile:
for line in f1_data:
if ',' in line: # 多音字的每个读音会用','分割
outputfile.write(line)
outputfile.write('\n')
count += 1
print("多音字个数 %d" % count)
# 统计人民日报语料中多音字的种类及出现次数
def polyphone_frequency():
with open("../data/198801.txt", 'r', encoding='gbk') as f:
f_data = f.readlines()
f_data = [x for x in f_data if x != '\n'] # 去掉空行
print('新闻条数 %d' % len(f_data))
count = 0
category = 0
polyphones = {} # 记录多音字和出现次数
# 将含有多音字的语料筛选出来
with open('../data/news.txt', 'w', encoding='utf-8') as nf:
for line in f_data:
flag = False
words = line.split()
for w in words:
if '{' in w: # 多音字的注音会用'{}'标识
pos = w.index('{')
if pos == 1:
# 和已有多音字库做对比,删去字库中不存在的多音字
if w[0] in load_dict:
flag = True
if w[0] in polyphones:
polyphones[w[0]] += 1
else:
polyphones[w[0]] = 1
# 可能一个词中有多个多音字
else:
# 和已有多音字库做对比,删去字库中不存在的多音字
if w[0] in load_dict:
flag = True
for i in range(pos):
if w[0] in polyphones:
polyphones[w[0]] += 1
else:
polyphones[w[0]] = 1
if flag:
count += 1
nf.write(line)
nf.write('\n')
print("包含多音字的新闻数 %d" % count)
# 将多音字按出现频次从多到少排序
ordered_list = sorted(polyphones.items(), key=lambda item: item[1], reverse= True)
with open('../data/198801output.txt', 'w', encoding='utf-8') as f2:
for l in ordered_list:
category += 1
f2.write('%s : %d' % (l[0], l[1]))
f2.write('\n')
print("多音字种类数 %d" % category)
# 建立多音字字典,包含多音字其读音
def create_poly_dic():
poly_dic = defaultdict(list)
with open("../data/polyphones.txt", 'r', encoding='utf-8') as f:
lines = f.readlines()
lines = [x for x in lines if x != '\n']
for l in lines:
words = l.split()
pron = words[1].split(',')
for p in pron:
poly_dic[words[-1]].append(p)
# 存储成json文件
with open("../data/polyphones.json", 'w', encoding='utf-8') as f1:
json.dump(poly_dic, f1, ensure_ascii=False)
# 提取带注音的短语中所有含多音字的短语
def get_poly_phrase():
with open('../data/phrase.txt', 'r', encoding='utf-8') as f:
phrase_list = f.readlines()
print("短语总数 %d" % len(phrase_list))
count = 0
ch_dic = defaultdict()
with open("../data/poly_phrase.txt", 'w', encoding='utf-8') as of:
for p in phrase_list:
words = p.split('=')
is_poly = False
for i in range(len(words[0])):
ch = words[0][i]
if ch in load_dict:
if ch in ch_dic:
ch_dic[ch] += 1
else:
ch_dic[ch] = 1
is_poly = True
of.write("%d " % i) # 在短语前输出多音字在短语中的位置
if is_poly:
count += 1
of.write("%s %s" % (words[0], words[-1])) # 输出含多音字的短语及其读音
print("含多音字的短语总数 %d" % count)
ordered_chlist = sorted(ch_dic.items(), key=lambda item: item[1], reverse=True)
with open("../data/phrase_frequency.txt", 'w', encoding='utf-8') as pf:
# 将多音字按出现频次从多到少排序
for l in ordered_chlist:
pf.write('%s : %d\n' % (l[0], l[1]))
# 按注音短语中出现的多音字个数排序
def pron_len(s):
return len(s.split())
def sort_phrase():
with open('../data/poly_phrase.txt', 'r', encoding='utf-8') as f:
phrase_list = f.readlines()
phrase_list = [x for x in phrase_list if x != '\n']
num_dic = defaultdict()
sorted_phrase = sorted(phrase_list, key=pron_len)
with open('../data/poly_phrase.txt', 'w', encoding='utf-8') as of:
for p in sorted_phrase:
if len(p.split()) - 2 in num_dic:
num_dic[len(p.split()) - 2] += 1
else:
num_dic[len(p.split()) - 2] = 1
of.write(p)
of.write('\n')
for k, v in num_dic.items():
print('多音字个数:%d \t 句子数量:%d\n' %(k, v))
# 将所有多音字按读音个数排序
def num_pron(s):
prons = s.split()[1]
prons_list = prons.split(',')
return len(prons_list)
def sort_polyphone():
with open('../data/polyphones.txt', 'r', encoding='utf-8') as f:
polyphone_list = f.readlines()
polyphone_list = [x for x in polyphone_list if x != '\n']
sum_dic = defaultdict()
sorted_polyphone = sorted(polyphone_list, key=num_pron)
with open('../data/polyphones.txt', 'w', encoding='utf-8') as of:
for p in sorted_polyphone:
pron_list = p.split()[1]
pron_list = pron_list.split(',')
if len(pron_list) in sum_dic:
sum_dic[len(pron_list)] += 1
else:
sum_dic[len(pron_list)] = 1
of.write(p)
of.write('\n')
for k, v in sum_dic.items():
print('读音个数:%d \t 多音字数量:%d\n' % (k, v))
if __name__ == '__main__':
# count_polyphone()
# polyphone_frequency()
# create_poly_dic()
# get_poly_phrase()
# sort_phrase()
sort_polyphone()