语音合成学习(六)学习笔记

语音合成学习(六)学习笔记


文本归一化

一、介绍

简介

文本正则化是将文本数据转化为标准形式的过程,以便于进行自然语言处理和文本分析。文本归一化通常包括以下步骤:

  • 文本清理:删除文本中的无用信息,如HTML标记,特殊字符,URL和标点符号等。
  • 大小写统一:将文本中的所有字符转换为小写或大写字母。
  • 停用词过滤:删除指定的停用词,如“的”、“在”、“是”等常用词语。
  • 词干提取或词形还原:将不同形式的词汇转换为其原始形式,如将“running”转换为“run”或将“cats”转换为“cat”。
  • 拼写纠正:修正文本中的拼写错误。

文本归一化的目的是消除文本中的噪声和不规则性,提高自然语言处理和文本分析的准确性和效率。


TN全称Text Normalization,意思是文本规整、文本正则化 。

TN是 TTS (Text-to-speech,文本转语音) 系统中的重要组成部分,主要功能是将文本中的数字、符号、缩写等转换成语言文字。如:

# delete english characters
# e.g. "你好aBC" -> "你 好"

# fractionation
# e.g. "现场有7/12的观众投出了赞成票"  -> "现场有十二分之七的观众投出了赞成票"

# time
# e.g. "2:10pm出门" -> "下午两点十分出门"
  • 使用归一化的好处:

1.提升模型的收敛速度(即加快梯度下降求最优解的速度

2.提升模型的精度
在涉及到一些距离计算的算法时效果显著,比如算法要计算欧氏距离,归一化可以让可以让各个特征对结果做出的贡献相同,未归一化就会造成精度的损失。

3.提高准确性:文本归一化可以消除文本中的噪声和不规则性,如大小写差异、拼写错误、同义词等,从而提高文本分析的准确性。

4.降低复杂性:文本归一化可以将文本转换为标准格式或标准形式,从而降低文本分析的复杂性和难度。

5.提高效率:文本归一化可以将不同形式的词汇转换为其原始形式,并删除停用词等无用信息,从而提高自然语言处理和文本分析的效率。

6.统一数据:文本归一化可以统一不同来源的文本数据,使其格式和表达方式相同,从而方便进行后续的文本分析和处理。

二、常见的归一化转换规则与表达

# 过滤掉特殊字符
text = re.sub(r'[——《》【】<=>{}()()#&@“”^_|…\\]', '', text)
# 日期表达式
用 / 或者 - 分隔的 YY/MM/DD 或者 YY-MM-DD 日期
RE_DATE2 = re.compile(r'(\d{4})([- /.])(0[1-9]|1[012])\2(0[1-9]|[12][0-9]|3[01])')
RE_DATE21 = re.compile(r'(\d{4}|\d{2})年'
                     r'((0?[1-9]|1[0-2])月)?'
                     r'(((0?[1-9])|((1|2)[0-9])|30|31)([日号]))?')
# 时刻表达式
RE_TIME = re.compile(r'([0-1]?[0-9]|2[0-3])'
                     r':([0-5][0-9])'
                     r'(:([0-5][0-9]))?')
# 时间范围,如8:30-12:30
RE_TIME_RANGE = re.compile(r'([0-1]?[0-9]|2[0-3])'
                           r':([0-5][0-9])'
                           r'(:([0-5][0-9]))?'
                           r'(~|-)'
                           r'([0-1]?[0-9]|2[0-3])'
                           r':([0-5][0-9])'
                           r'(:([0-5][0-9]))?')
# 分数表达式
RE_FRAC = re.compile(r'(-?)(\d+)/(\d+)')

# 百分数表达式
RE_PERCENTAGE = re.compile(r'(-?)(\d+(\.\d+)?)%')
# 整数表达式
# 带负号的整数 -10
RE_INTEGER = re.compile(r'(-)' r'(\d+)')
# 编号-无符号整形
# 00078
RE_DEFAULT_NUM = re.compile(r'\d{3}\d*')
# 数字表达式
# 纯小数
RE_DECIMAL_NUM = re.compile(r'(-?)((\d+)(\.\d+))' r'|(\.(\d+))')
# 正整数 + 量词
RE_POSITIVE_QUANTIFIERS = re.compile(r"(\d+)([多余几\+])?" + COM_QUANTIFIERS)
RE_NUMBER = re.compile(r'(-?)((\d+)(\.\d+)?)' r'|(\.(\d+))')
# 范围表达式
RE_RANGE = re.compile(r'((-?)((\d+)(\.\d+)?)|(\.(\d+)))[-~]((-?)((\d+)(\.\d+)?)|(\.(\d+)))')
# 规范化固话/手机号码
# 手机
# 移动:139、138、137、136、135、134、159、158、157、150、151、152、188、187、182、183、184、178、198
# 联通:130、131、132、156、155、186、185、176
# 电信:133、153、189、180、181、177
RE_MOBILE_PHONE = re.compile(r"(?<!\d)((\+?86 ?)?1([38]\d|5[0-35-9]|7[678]|9[89])\d{8})(?!\d)")
RE_TELEPHONE = re.compile(r"(?<!\d)((0(10|2[1-3]|[3-9]\d{2})-?)?[1-9]\d{7,8})(?!\d)")

# 全国统一的号码400开头
RE_NATIONAL_UNIFORM_NUMBER = re.compile(r"(400)(-)?\d{3}(-)?\d{4}")

# 温度表达式,温度会影响负号的读法
# -3°C 零下三度
RE_TEMPERATURE = re.compile(r'(-?)(\d+(\.\d+)?)(°C|℃|度|摄氏度)')
## Normalize unicode characters  // 规范化unicode 字符
def remove_weird_chars(text):
    text = unicodedata.normalize('NFKD', text).encode('utf-8', 'ignore').decode(
        'utf-8', 'ignore')
    return text
    
## Remove extra linebreaks   //删除额外换行符
def remove_extra_linebreaks(text):
    lines = text.split(r'\n+')
    return '\n'.join(
        [re.sub(r'[\s]+', ' ', l).strip() for l in lines if len(l) != 0])
        
 
## Remove extra medial/trailing/leading spaces  // 删除额外的中间/后面/前面的空间
def remove_extra_spaces(text):
    return re.sub("\\s+", " ", text).strip()
    
## Seg the text into words   // 分词
def seg(text):
    text_seg = jieba.cut(text)
    out = ' '.join(text_seg)
    return out
    
## Remove punctuation/symbols  // 除去标点符号
def remove_symbols(text):
text = ''.join(
        ch for ch in text if unicodedata.category(ch)[0] not in ['P', 'S'])
    return text
    
## Remove numbers  // 除去数字
def remove_numbers(text):
    return re.sub('\\d+', "", text)
    
## Remove alphabets   //除去字母
def remove_alphabets(text):
    return re.sub('[a-zA-Z]+', '', text)
  
## Unify upper/lower cases   //转换大小写
    if args.to_upper:
        text = text.upper()
    if args.to_lower:
        text = text.lower()
        
## export  const  verbalizeCardinal = (num)  => {
    // 将基数词转换为中文汉字表示
    let result = '';
    for (let digit of String(num)) {
        result += CARDINALS[digit];
    }
    return result;
 }

## export  const replaceDate = (match) =>  {
    // 将日期字符串中的数字转换为中文汉字表示
    const year = match[1];
    const month = match[3];
    const day = match[5];
    let result = '';
    if (year) {
        result += `${verbalizeDigit(year)}年`;
    }
    if (month) {
        result += `${verbalizeCardinal(month)}月`;
    }
    if (day) {
        result += `${verbalizeCardinal(day)}${match[9]}`;
    }
    return result;
    
  ## export const replaceDate2 = (match)  => {
    // 将日期字符串中的数字转换为中文汉字表示
    const year = match[1];
    const month = match[3];
    const day = match[4];
    let result = '';
    if (year) {
        result += `${verbalizeDigit(year)}年`;
    }
    if (month) {
        result += `${verbalizeCardinal(month)}月`;
    }
    if (day) {
        result += `${verbalizeCardinal(day)}日`;
    }
    return result;
## export  const replaceTime = (match)  => {
    const isRange = match[6] !== '-';
    const hour = parseInt(match[1]);
    const minute = parseInt(match[2]);
    const second = match[4] ? parseInt(match[4]) : 0;
    const hour2 = isRange ? parseInt(match[6]) : null;
    const minute2 = isRange ? parseInt(match[7]) : null;
    const second2 = isRange && match[9] ? parseInt(match[9]) : null;

    let result = `${num2str(hour)}点`;
    if (minute.toString().replace(/^0+/, '') !== '') {
        if (minute === 30) {
            result += '半';
        } else {
            result += `${_time_num2str(minute)}分`;
        }
    }
    if (second.toString().replace(/^0+/, '') !== '') {
        result += `${_time_num2str(second)}秒`;
    }

    if (isRange) {
        result += '至';
        result += `${num2str(hour2)}点`;
        if (minute2.toString().replace(/^0+/, '') !== '') {
            if (minute2 === 30) {
                result += '半';
            } else {
                result += `${_time_num2str(minute2)}分`;
            }
        }
        if (second2.toString().replace(/^0+/, '') !== '') {
            result += `${_time_num2str(second2)}秒`;
        }
    }

    return result;
}




以上主要是对归一化的简单介绍,也整理了一部分常用的规则。


相关阅读

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容