中文手语识别:结合序列标注和深度学习的方法

作者:禅与计算机程序设计艺术

1.简介

中文手语识别(Chinese Speech Recognition)主要包括了汉语普通话和粤语方言之间的文本转写、语音合成以及语音识别三种任务,其研究和应用的研究范畴都十分广泛。近年来,基于深度学习(Deep Learning)和强化学习(Reinforcement Learning)的最新模型方法取得了很大的成功,特别是在文本转写和语音识别方面,取得了前所未有的效果。本文将介绍基于LSTM(长短时记忆神经网络)和CRF(条件随机场)的中文手语识别模型,并从各个角度对模型进行阐述,希望能够为中文手语识别领域的研究者提供一些借鉴参考。

2.相关背景

2.1 中文手语识别的定义

中文手语识别,是指通过自动化的技术或手工的方法,将不规范的中文语言文本转换为标准的英文字母数字字符串,或者将人类的普通话声音转换成相应的文字输入形式。中文手语识别属于信息技术(IT)技术的重要分支之一,因为在线中文新闻、论坛、微博等社交媒体、电商、客服机器人的应用中,手语转换功能必不可少。

2.2 深度学习与机器学习的关系

机器学习是人工智能领域的一个子领域,它旨在利用数据训练计算机模型,使得模型可以从数据中学到知识、解决问题,得到预测能力。机器学习分为监督学习、无监督学习、半监督学习、强化学习四类,其中深度学习与机器学习密切相关。深度学习是机器学习中的一个子集,它是利用多层次的神经网络来进行数据的学习和推理。通过学习特征表示和权重共享,深度学习可以自动发现数据中隐含的模式,在图像、语音、文本等不同类型的数据上,都可以获得显著的性能提升。

2.3 语音技术

声音是人类最基础的感知系统之一,也是理解语音并进行传达的最直接的途径。在语音识别领域,一般采用麦克风采集到的原始信号作为输入,经过预加重、噪声抑制、加窗以及其他处理之后,还原出声波的振幅值、频率等特征,然后根据不同的应用场景,对声波的特征进行特征选择,以获得关键的特征向量用于分类或聚类。常用的语音编码技术包括三种:线性预测,离散余弦变换(DCT),正弦二项回归系数法。

3.概述

3.1 模型结构

中文手语识别模型主要由三个主要模块组成:前端(Front-End)、序列建模器(Sequence Modeler)、混合模型(Hybrid Model)。

3.1.1 前端

中文手语识别通常需要进行前端处理,例如去除音素边界、音节与字之间的时间差异以及将音素转化为模板的映射。前端主要工作包括:

  1. 分割: 将句子或音频按一定时间长度划分成多个片段,每个片段只包含一个音素;
  2. 拼接: 对分割后的音频片段拼接,使每一个音素单独占用一个片段;
  3. 重采样: 将重采样后的音频数据采样率统一为16kHz,降低计算复杂度;
  4. 提取特征: 使用短时傅里叶变换(STFT)提取声道信号的时频特征;
  5. 去除噪声: 通过高通滤波器或最大似然估计(MLE)消除噪声;
  6. 滤波器后处理: 按要求进行预加重、消除冗余、去除随机漫步等处理。

3.1.2 序列建模器

序列建模器包括两种主要模型,一种是长短时记忆网络LSTM,另一种是条件随机场CRF。LSTM用于预测音素的上下文信息,而CRF用于对音素的结果进行概率估计。

LSTM

LSTM是一种循环神经网络模型,它能够对序列数据进行建模,能够捕获长期依赖关系。LSTM模型由两个门的堆积组成,分别是输入门、遗忘门和输出门。这些门的作用如下:

  1. 输入门:决定哪些数据会进入到下一个时间步的单元状态中,也就是保留哪些数据,并将剩下的一些数据丢弃;
  2. 遗忘门:决定那些之前的单元状态要被遗忘掉;
  3. 输出门:决定那些数据应该被放入到输出序列中,同时决定每个时间步上的输出值。
CRF

CRF是一种带有局部和全局约束的图模型,能够有效地解决序列标签问题。CRF模型中,每个位置有对应的一组可能的标签集合,称作发射矩阵。发射矩阵是一个n*m的矩阵,其中n表示序列的长度,m表示标签的数量。对于给定的一组观察变量和标记序列,CRF模型通过局部转移概率和全局概率来计算该序列的似然函数,并求解最优路径,从而得到序列中每个位置的标签。

3.1.3 混合模型

混合模型将前端和序列建模器的输出整合起来,作为最终的预测结果。例如,将LSTM的输出与CRF的输出联合进行融合,构造联合模型。

4.算法原理

4.1 LSTM

LSTM是一种递归神经网络模型,由输入门、输出门、遗忘门以及记忆单元4个门组成。其计算方式如下:

  1. t时刻的输入向量 xt 和上一时间步的隐藏状态 ht−1 相乘生成输入候选值;
  2. 输入候选值通过sigmoid激活函数生成输入值 it;
  3. it 与 xt 的和与上一时间步的 cell state ct−1 相加生成候选记忆单元值;
  4. 候选记忆单元值通过tanh激活函数生成记忆单元值 ct;
  5. 如果该时间步输入门打开,则更新 ct=ct+it;
  6. 如果该时间步输出门打开,则生成 ht;
  7. 如果该时间步遗忘门打开,则更新 ct=ct−ht−1;
  8. 返回 ht 及 ct 作为当前时间步的隐藏状态和 cell state。

4.2 CRF

CRF是一种图模型,用于表示序列中各个元素之间的可能联系,包括局部依赖关系和全局依赖关系。其计算方式如下:

  1. 用权重 θ 与特征向量 x 相乘生成状态序列 score;
  2. 根据发射矩阵 E 生成 score;
  3. 以状态序列作为图节点,节点间的边代表状态转移概率,边的权重等于特征函数的评分值,最后求解最优路径,得到最优状态序列 labeling;
  4. 根据 labeling 计算给定观察变量和标记序列的似然函数,即给定模型参数θ,观察变量序列x和标记序列y,计算P(y|x;θ)。

5.实验及代码实现

5.1 数据集

本文使用了清华大学提供的北京市2000人大词典(BCC)数据集,共计140小时的中文语音数据。数据集中包含的音素共有7911个,包括标点符号、音节、音调、韵律等。数据集中共有42万条语音,均为未知词汇的语音数据。

5.2 模型结构

本文使用的中文手语识别模型包括:前端(Front-End)、LSTM序列建模器(Sequence Modeler)、混合模型(Hybrid Model)。

5.2.1 前端

由于本文目标只是做中文手语识别,所以本文只需进行端到端的中文手语识别,不需要进行前端分割处理。

5.2.2 LSTM序列建模器

本文使用的LSTM序列建模器由三个层次结构组成,输入层、隐层、输出层,以及记忆元层。输入层输入MFCC特征,隐层和输出层由全连接神经网络构建,记忆元层使用LSTM记忆元。

5.2.3 混合模型

本文使用的混合模型是将前端和序列建模器的输出整合起来,作为最终的预测结果。例如,将LSTM的输出与CRF的输出联合进行融合,构造联合模型。

5.3 实验环境

5.3.1 硬件配置

本文实验在笔记本电脑上运行,配置为 Intel i7 8700K CPU、NVIDIA GTX TITAN X GPU 和 32GB内存。

5.3.2 软件配置

本文实验使用 Python 3.5 + Keras 2.1.5 + Tensorflow 1.8.0 + OpenFST 1.6.2 在 Linux 操作系统下完成。

5.4 数据处理

5.4.1 MFCC特征提取

首先,本文实验使用 MFCC 特征提取器对语音信号进行特征提取。这里,我们使用 Kaldi 提供的脚本对语音信号进行 MFCC 特征提取。

./mfcc.sh data_path exp_dir feat_name num_jobs num_ceps delta_order window_size 
  • data_path 是源文件所在目录;
  • exp_dir 是存放中间结果的文件夹;
  • feat_name 是特征名称;
  • num_jobs 是进程数目;
  • num_ceps 是cepstral coefficients(倒谱系数)的个数;
  • delta_order 表示阶数;
  • window_size 是窗大小。

5.4.2 获取拼音字母字典

我们还需要获取拼音字母字典,用于将 MFCC 特征转化为文本形式。这里,我们使用 openfst 来生成拼音字母字典。

farcompilestrings --symbols="data/chars" --keep_symbols=false \"data/pny_dict > data/pny_dict.far"
  • symbols 是拼音字符表文件;
  • keep_symbols 表示是否保留符号;
  • pny_dict 是拼音字典文件,包含每个汉字对应的拼音符号串。

5.4.3 使用 FST 对特征进行转换

接着,我们可以使用 FST 文件对 MFCC 特征进行转换,将其转化为文本形式。

farextract --package_type=compact "data/lm_bg.arpa.gz" "data/wordlist" "data/G.carpa"
build_binary_tree "data/G.carpa" | fstprint > "data/G.fst"fsttablecompose "data/pny_dict.far" "data/feat_mfcc.scp" |\fstarcsort --sort_type=ilabel > "data/feats_sorted.ark"fstcomposecontext "data/G.fst" "data/feats_sorted.ark" |\fstdeterminizestar --use_log=true --star_weight=1.0 >> "data/output.fst"fstrmepsilon "data/output.fst" >/dev/null
fstminimizeencoded "data/output.fst" | fstshortestpath "data/decode.fst"for w in `cat $data_dir/wav.scp`; do name=$(echo "$w" | awk '{split($1,arr,"/"); print arr[(NF-1)]}' | sed's/.wav//g');echo "Decoding for file $name";fstcomposecontext "data/pny_dict.far" " " "\$name " "data/output.fst" |\fstdeterminizestar --use_log=true --star_weight=1.0 |\fstrmepsilon | fstshortestpath | fstprint; donefstisstochastic "data/decode.fst" &&\fstprint "data/decode.fst" | awk -v "lnorm=$labelnorm" '$2=="final"{printf("%s %.5f ", $1, exp(-lnorm*$3))}' |\sort -t''-nk2 > "data/lat_probs_$i.txt" ||\echo "WARNING: Output of network is not stochastic!"; exit 1; phonetisaurus-align --input="data/$file_id.txt" --ofile="data/$file_id.phn" --lexicon="data/pny_dict" --lm="data/lm_bg.arpa.gz" || exit 1;
  • package_type 指定包的类型;
  • lm_bg.arpa.gz 是语言模型文件;
  • wordlist 是词列表文件,它记录了词汇出现的次数;
  • G.carpa 是词性组合规则文件;
  • feat_mfcc.scp 是 MFCC 特征文件;
  • feats_sorted.ark 是排序后的特征文件;
  • output.fst 是归一化的 FST 文件;
  • decode.fst 是解码后得到的最佳路径;
  • lat_probs_$i.txt 是 lattice probabilities 文件;
  • phonetisaurus-align 命令行工具用于将输入文本转换为音素序列。

5.5 模型训练

5.5.1 数据准备

为了训练 LSTM 序列建模器,我们需要准备训练集、验证集以及测试集。这里,我们使用 VoxForge 中的小语料库 CSLU Spheres 数据集。

import os
from keras.utils import to_categoricaldef load_dataset():# 设置训练集、验证集和测试集比例train_ratio = 0.8valid_ratio = 0.1# 获取训练集、验证集和测试集路径train_path = "/media/home/zhou/Dataset/VoxForge/cslu_spheres/"dev_path = os.path.join(train_path, "validation")test_path = os.path.join(train_path, "test")# 初始化训练集、验证集和测试集train_set = []val_set = []test_set = []# 读取训练集和验证集中的 wav 文件名with open("data/train.txt", encoding='utf-8') as f:lines = f.readlines()train_files = [(line[:-1].split()[0], line[:-1].split()[1]) for line in lines if len(line)>1 and line[0]!='#']with open("data/valid.txt", encoding='utf-8') as f:lines = f.readlines()val_files = [(line[:-1].split()[0], line[:-1].split()[1]) for line in lines if len(line)>1 and line[0]!='#']# 从训练集、验证集和测试集中读取语音信号并生成 MFCC 特征for path, files in zip([train_path, dev_path, test_path],[train_files, val_files, test_files]):for fname, text in files:mfcc_path = os.path.join(path, "{}.mfc".format(fname))try:# 加载 MFCC 特征mfcc = np.load(mfcc_path)# 统计 MFCC 特征的维度n_features = mfcc.shape[-1]# 添加数据至训练集、验证集或者测试集set_index = int((random.random() < train_ratio)*len(lines))index = random.randint(0, mfcc.shape[0]-1)if set_index == 0:input_seq = mfcc[index,:n_features//3].reshape((1,-1))target_seq = [[phone_to_index[p]] for p in text.split()]train_set += list(zip(input_seq,target_seq))elif set_index == 1:input_seq = mfcc[index,:n_features//3].reshape((1,-1))target_seq = [[phone_to_index[p]] for p in text.split()]val_set += list(zip(input_seq,target_seq))else:input_seq = mfcc[:index,:n_features//3].reshape((-1,1,n_features//3))target_seq = [[phone_to_index[p] for p in text.split()]] * indextest_set += list(zip(input_seq,target_seq))except Exception as e:passreturn train_set, val_set, test_settrain_set, val_set, test_set = load_dataset()

5.5.2 LSTM 模型训练

训练 LSTM 序列建模器,我们使用 Keras API。

from keras.models import Sequential
from keras.layers import Dense, Dropout, LSTM# 定义 LSTM 模型
model = Sequential()
model.add(LSTM(64, input_shape=(None, n_features), return_sequences=True))
model.add(Dropout(0.5))
model.add(Dense(n_classes, activation='softmax'))# 编译模型
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])# 训练模型
history = model.fit(np.array([example[0] for example in train_set]), np.array([[example[1][i] for i in range(len(example[1]))] for example in train_set]),validation_data=[np.array([example[0] for example in val_set]), np.array([[example[1][i] for i in range(len(example[1]))] for example in val_set])], epochs=epochs, batch_size=batch_size)

5.5.3 预测测试集

使用训练好的模型对测试集进行预测。

pred_probas = model.predict(np.array([example[0] for example in test_set]))
pred_labels = np.argmax(pred_probas, axis=-1).tolist()
gold_labels = [example[1][:-1] for example in test_set]correct_count = sum([(pred==gold and pred!=silence_index) for pred, gold in zip(pred_labels,gold_labels)])
total_count = sum([int(not all(gold == silence_index for gold in example[1])) for example in test_set])accuracy = correct_count / total_count

5.6 模型测试

对模型的效果进行测试。

farcompilestrings --symbols="data/chars" --keep_symbols=false \"data/pny_dict > data/pny_dict.far"farextract --package_type=compact "data/lm_bg.arpa.gz" "data/wordlist" "data/G.carpa"build_binary_tree "data/G.carpa" | fstprint > "data/G.fst"for file in *.wav; do name=$(basename ${file}.wav);echo "Testing on file $name...";./generate.sh data/pny_dict.far "${name}.mfc" >"${name}.txt"; python postprocess.py "${name}.txt"; cat "${name}_postprocessed.txt" | tr '\r''' | paste -d''<(grep hyp $hyp_file) - | awk '{print $1, $2}' >"${name}.lbl"; 
done;local/score.sh --cmd "run.pl" --sclite_opts "-h $ref_file -r ${hyp_file}" --reffile "${ref_file}" --hypfile "${hyp_file}";


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部