精华内容
下载资源
问答
  • 在孤立词语音识别(Isolated Word Speech Recognition) 中,DTW,GMMHMM 是三种典型的方法: 动态时间规整(DTW, Dyanmic Time Warping) 高斯混合模型(GMM, Gaussian Mixed Model) 隐马尔可夫模型(HMM, Hidden ...

    前言


    Github 项目地址:https://github.com/Sherry-XLL/Digital-Recognition-DTW_HMM_GMM

    在孤立词语音识别(Isolated Word Speech Recognition) 中,DTWGMMHMM 是三种典型的方法:

    • 动态时间规整(DTW, Dyanmic Time Warping)
    • 高斯混合模型(GMM, Gaussian Mixed Model)
    • 隐马尔可夫模型(HMM, Hidden Markov Model)

    本文并不介绍这三种方法的基本原理,而是侧重于 Python 版代码的实现,针对一个具体的语音识别任务——10 digits recognition system,分别使用 DTW、GMM 和 HMM 建立对 0~9 十个数字的孤立词语音分类识别模型。

    一、音频数据处理


    1. 数据集

    本文中的语音识别任务是对 0 ~ 9 这十个建立孤立词语言识别模型,所使用的数据集类似于语音版的 MNIST 手写数字数据库。原始数据集是 20 个人录制的数字 0 ~ 9 的音频文件,共 200 条 .wav 格式的录音,同样上传到了 Github 项目 中,分支情况如下:

    records
    digit_0
    digit_1
    digit_2
    digit_3
    digit_4
    digit_5
    digit_6
    digit_7
    digit_8
    digit_9
    1_0.wav
    ......
    20_0.wav

    records 文件夹下包括 digit_0 ~ digit_9 十个子文件夹。每个子文件夹代表一个数字,里面有 20 个人对该数字的录音音频,1_0.wav 就代表第 1 个人录数字 0 的音频文件。

    为了比较不同方法的性能,我们将数据集按照 4:1 的比例分为训练集和测试集。我们更想了解的是模型在未知数据上的表现,于是前16个人的数据都被划分为了训练集,共 160 条音频文件;17-20 这四个人的音频为测试集,共 40 条 .wav 格式的录音。

    2. 音频预处理

    preprocess.py referencing
    https://github.com/rocketeerli/Computer-VisionandAudio-Lab/tree/master/lab1

    当录数字的音频时,录音前后会有微小时间的空隙,并且每段音频的空白片段长度不一。如果忽略这个差异,直接对原音频进行建模运算,结果难免会受到影响,因此本文选择基于双阈值的语音端点检测对音频进行预处理。

    对于每段 .wav 音频,首先用 wave 读取,并通过 np.frombufferreadframes 得到二进制数组。然后,编写计算能量 (energy) 的函数 calEnergy 和计算过零率 (zero-crossing rate,ZCR) 的函数 calZeroCrossingRate,帧长 framesize 设置为常用的 256:

    def calEnergy(wave_data):
        """
        :param wave_data: binary data of audio file
        :return: energy
        """
        energy = []
        sum = 0
        for i in range(len(wave_data)):
            sum = sum + (int(wave_data[i]) * int(wave_data[i]))
            if (i + 1) % 256 == 0:
                energy.append(sum)
                sum = 0
            elif i == len(wave_data) - 1:
                energy.append(sum)
        return energy
    
    
    def calZeroCrossingRate(wave_data):
        """
        :param wave_data: binary data of audio file
        :return: ZeroCrossingRate
        """
        zeroCrossingRate = []
        sum = 0
        for i in range(len(wave_data)):
            sum = sum + np.abs(int(wave_data[i] >= 0) - int(wave_data[i - 1] >= 0))
            if (i + 1) % 256 == 0:
                zeroCrossingRate.append(float(sum) / 255)
                sum = 0
            elif i == len(wave_data) - 1:
                zeroCrossingRate.append(float(sum) / 255)
        return zeroCrossingRate
    

    代入音频的二进制数据得到能量和过零率数组之后,通过语音端点检测得到处理之后的音频数据,端点检测函数 endPointDetect 主要为三个步骤:

    • 先根据 energy 和 zeroCrossingRate 计算高能阈值 (high energy threshold, MH)
    • 第一步是计算较高的短时能量作为高能阈值 (high energy threshold, MH),可用于识别语音的浊音部分,得到初步检测数据 A。
    • 第二步是计算较低的能量阈值 (low energy threshold, ML),使用该阈值搜索两端,第二次检测从 A 得到 B。
    • 第三步是计算过零率阈值 (zero crossing rate threshold, Zs),在考虑过零率的基础上进一步处理第二步的结果 B,返回处理好的数据 C。

    最后,将编号 1-16 被试者的音频文件存入 processed_train_records ,编号 17-20 存入 processed_test_records

    3. MFCC 特征提取

    音频文件无法直接进行语音识别,需要对其进行特征提取。在语音识别(Speech Recognition)领域,最常用到的语音特征就是梅尔倒谱系数(Mel-scale Frequency Cepstral Coefficients,MFCC)。

    在 Python 中,可以用封装好的工具包 librosapython_speech_features 实现对 MFCC 特征的提取。本文使用 librosa 提取给定音频的 MFCC 特征,每帧提取 39 维 MFCC 特征:

    import librosa
    def mfcc(wav_path, delta = 2):
        """
        Read .wav files and calculate MFCC
        :param wav_path: path of audio file
        :param delta: derivative order, default order is 2
        :return: mfcc
        """
        y, sr = librosa.load(wav_path)
        # MEL frequency cepstrum coefficient
        mfcc_feat = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)
        ans = [mfcc_feat]
        
        # Calculate the 1st derivative
        if delta >= 1:
            mfcc_delta1 = librosa.feature.delta(mfcc_feat, order=1, mode ='nearest')
            ans.append(mfcc_delta1)
            
        # Calculate the 2nd derivative
        if delta >= 2:
            mfcc_delta2 = librosa.feature.delta(mfcc_feat, order=2, mode ='nearest')
            ans.append(mfcc_delta2)
            
        return np.transpose(np.concatenate(ans, axis=0), [1,0])
    

    可以参考官网 librosapython-speech-features 了解更多音频特征提取工具。

    二、Isolated Speech Recognition - DTW


    1. DTW 算法步骤

    动态时间规整(DTW, Dyanmic Time Warping) 本质上是一种简单的动态规划算法,它可以分为两个步骤,一个是计算两种模式的帧之间的距离,即得到帧匹配距离矩阵;另一个是在帧匹配距离矩阵中找到最优路径。算法步骤如下:

    • 首先,使用欧几里德距离 (Euclidean distance) 初始化成本矩阵 (cost matrix)。
    • 其次,使用动态规划计算成本矩阵,将每个数据的向量与模板向量进行比较,动态转移方程为
      c o s t [ i , j ] + = m i n ( [ c o s t [ i , j ] , c o s t [ i + 1 , j ] , c o s t [ i , j + 1 ] ] ) . cost[i, j] += min([cost[i, j], cost[i+1, j], cost[i, j+1]]). cost[i,j]+=min([cost[i,j],cost[i+1,j],cost[i,j+1]]).
    • 第三,计算规整路径 (warp path),最小距离为标准化距离。将第一个训练序列设置为标准 (template),合并所有训练序列并求其平均值,返回 0-9 位数字的模型。
    • 测试过程中,使用 DTW 衡量每条音频到 0-9 这十个模板的”距离“,并选择模板距离最小的数字作为音频的预测值。

    2. DTW 函数编写

    编写 dtw 函数的关键在于两段 MFCC 序列的动态规划矩阵和规整路径的计算。令输入的两段 MFCC 序列分别为 M1 和 M2,M1_len 和 M2_len 表示各自的长度,则 cost matrix 的大小为 M1_len * M2_len,先用欧式距离对其进行初始化,再根据转移式计算 cost matrix:

    def dtw(M1, M2):
        """
        Compute Dynamic Time Warping(DTW) of two mfcc sequences.
        :param M1, M2: two mfcc sequences
        :return: the minimum distance and the wrap path
        """
        # length of two sequences
        M1_len = len(M1)
        M2_len = len(M2)
        cost_0 = np.zeros((M1_len + 1, M2_len + 1))
        cost_0[0, 1:] = np.inf
        cost_0[1:, 0] = np.inf
        
        # Initialize the array size to M1_len * M2_len
        cost = cost_0[1:, 1:]
        for i in range(M1_len):
            for j in range(M2_len):
                cost[i, j] = calEuclidDist(M1[i], M2[j])
                
        # dynamic programming to calculate cost matrix
        for i in range(M1_len):
            for j in range(M2_len):
                cost[i, j] += min([cost_0[i, j], \
                                   cost_0[min(i + 1, M1_len - 1), j], \
                                   cost_0[i, min(j + 1, M2_len - 1)]])
    

    欧式距离的计算函数 calEuclidDist 可以用一行代码完成:

    def calEuclidDist(A, B):
        """
        :param A, B: two vectors
        :return: the Euclidean distance of A and B
        """
        return sqrt(sum([(a - b) ** 2 for (a, b) in zip(A, B)]))
    

    cost matrix 计算完成后还需计算 warp path,MFCC 序列长度为 1 时的路径可以单独分情况讨论,最小代价的 path_1 和 path_2 即为所求,最后返回数组 path:

    # calculate the warp path
       if len(M1) == 1:
           path = np.zeros(len(M2)), range(len(M2))
       elif len(M2) == 1:
           path = range(len(M1)), np.zeros(len(M1))
       else:
           i, j = np.array(cost_0.shape) - 2
           path_1, path_2 = [i], [j]
           # path_1, path_2 with the minimum cost is what we want
           while (i > 0) or (j > 0):
               arg_min = np.argmin((cost_0[i, j], cost_0[i, j + 1], cost_0[i + 1, j]))
               if arg_min == 0:
                   i -= 1
                   j -= 1
               elif arg_min == 1:
                   i -= 1
               else:
                   j -= 1
               path_1.insert(0, i)
               path_2.insert(0, j)
           # convert to array
           path = np.array(path_1), np.array(path_2)
    

    3. 模型训练与预测

    在模型训练阶段,对于每个数字的 16 条音频文件,现计算 MFCC 序列到 mfcc_list 列表中,将第一个音频文件的 MFCC 序列设置为标准,计算标准模板和每条模板之间的动态时间规整路径,再对其求平均进行归一化,将结果 appendmodel 列表中。返回的 model 包含 0-9 这 10 个数字的 DTW 模型:

     # set the first sequence as standard, merge all training sequences
     mfcc_count = np.zeros(len(mfcc_list[0]))
     mfcc_all = np.zeros(mfcc_list[0].shape)
     for i in range(len(mfcc_list)):
         # calculate the wrap path between standard and each template
         _, path = dtw(mfcc_list[0], mfcc_list[i])
         for j in range(len(path[0])):
             mfcc_count[int(path[0][j])] += 1
             mfcc_all[int(path[0][j])] += mfcc_list[i][path[1][j]]
    
     # Generalization by averaging the templates
     model_digit = np.zeros(mfcc_all.shape)
     for i in range(len(mfcc_count)):
         for j in range(len(mfcc_all[i])):
             model_digit[i][j] = mfcc_all[i][j] / mfcc_count[i]
             
     # return model with models of 0-9 digits
     model.append(model_digit)
    

    测试阶段比较简单,将训练得到的 model 和需要预测音频的路径输入到函数 predict_dtw 中,分别计算 model 中每位数字到音频 MFCC 序列的最短距离 min_distmin_dist 所对应的数字即为该音频的预测标签。

    def predict_dtw(model, file_path):
        """
        :param model: trained model
        :param file_path: path of .wav file
        :return: digit
        """
        # Iterate, find the digit with the minimum distance
        digit = 0
        min_dist, _ = dtw(model[0], mfcc_feat)
        for i in range(1, len(model)):
            dist, _ = dtw(model[i], mfcc_feat)
            if dist < min_dist:
                digit = i
                min_dist = dist
        return str(digit)
    

    三、Isolated Speech Recognition - GMM


    1. GMM 模型训练流程

    参考 sklearn 高斯混合模型中文文档:https://www.scikitlearn.com.cn/0.21.3/20/#211

    高斯混合模型(GMM, Gaussian Mixed Model) 是一个假设所有的数据点都是生成于有限个带有未知参数的高斯分布所混合的概率模型,包含了数据的协方差结构以及隐高斯模型的中心信息,同样可以用于解决本文的数字识别任务。

    scikit-learn 是基于 Python 语言的机器学习工具,sklearn.mixture 是其中应用高斯混合模型进行非监督学习的包,GaussianMixture 对象实现了用来拟合高斯混合模型的期望最大化 (EM, Expectation-Maximum) 算法。

    如果想要直接调用封装好的库,只需在代码中加入 from sklearn.mixture import GaussianMixtureGaussianMixture.fit 便可以从训练数据中拟合出一个高斯混合模型,并用 predict 进行预测,详情参见官方文档

    GMM 模型的训练和预测过程与 DTW 类似,此处不再赘述,流程图如下所示:
    GMM 训练流程图

    2. EM 算法步骤

    参考文档:https://blog.csdn.net/nsh119/article/details/79584629?spm=1001.2014.3001.5501

    GMM 模型的核心在于期望最大化 (EM, Expectation-Maximum) 算法,模型参数的训练步骤主要为 Expectation 的 E 步和 Maximum 的 M 步:

    (1) 取参数的初始值开始迭代。
    (2) E 步:依据当前模型参数,计算分模型 k k k 对观测数据 y j y_j yj 的相应度
    γ ^ j k = α j ϕ ( y j ∣ θ k ) Σ k = 1 K α k ϕ ( y j ∣ θ k ) , j = 1 , 2 , . . . , N ; k = 1 , 2 , . . . , K \hat{\gamma}_{jk}=\dfrac{\alpha_j\phi(y_j|\theta_k)}{\Sigma^K_{k=1}\alpha_k\phi(y_j|\theta_k)},j=1,2,...,N; k=1,2,...,K γ^jk=Σk=1Kαkϕ(yjθk)αjϕ(yjθk)j=1,2,...,N;k=1,2,...,K
    (3) M 步:计算新一轮迭代的模型参数
    μ ^ k = Σ j = 1 N γ ^ j k y j Σ j = 1 N γ ^ j k , k = 1 , 2 , . . . , K σ ^ k 2 = Σ j = 1 N γ ^ j k ( y j − μ k ) 2 Σ j = 1 N γ ^ j k , k = 1 , 2 , . . . , K α ^ k = Σ j = 1 N γ ^ j k N , k = 1 , 2 , . . . , K \begin{aligned} \hat{\mu}_k&=\dfrac{\Sigma^N_{j=1}\hat{\gamma}_{jk}y_j}{\Sigma^N_{j=1}\hat{\gamma}_{jk}},k=1,2,...,K \\ \hat{\sigma}^2_k&=\dfrac{\Sigma^N_{j=1}\hat{\gamma}_{jk}(y_j-\mu_k)^2}{\Sigma^N_{j=1}\hat{\gamma}_{jk}},k=1,2,...,K \\ \hat{\alpha}_k&=\dfrac{\Sigma^N_{j=1}\hat{\gamma}_{jk}}{N},k=1,2,...,K \end{aligned} μ^kσ^k2α^k=Σj=1Nγ^jkΣj=1Nγ^jkyjk=1,2,...,K=Σj=1Nγ^jkΣj=1Nγ^jk(yjμk)2k=1,2,...,K=NΣj=1Nγ^jkk=1,2,...,K

    3. GMM 实现代码

    sklearn.mixture/_gaussian_mixture.py:https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/mixture/_gaussian_mixture.py

    GMM 模型实现可以参考 GaussianMixture源码,本文对 sklearn 的实现方式进行了简化,关键在于 GMM 这个类的函数实现。

    我们实现的 GMM 类包含三个参数 (parameter) 和三个属性 (attribute):n_components 表示模型中状态的数目;mfcc_data 为音频提取的 MFCC 数据;random_state 是控制随机数的参数;means 表示在每个状态中 mixture component 的平均值;var 为方差;weights 是每个 component 的参数矩阵。

    class GMM:
        """Gaussian Mixture Model.
        Parameters
        ----------
        n_components : int
            Number of states in the model.
        mfcc_data : array, shape (, 39)
            Mfcc data of training wavs.
        random_state: int
            RandomState instance or None, optional (default=0)
        Attributes
        ----------
        means : array, shape (n_components, 1)
            Mean parameters for each mixture component in each state.
        var : array, shape (n_components, 1)
            Variance parameters for each mixture component in each state.
        weights : array, shape (1, n_components)
            Weights matrix of each component.
        """
    

    根据均值和方差可以计算对数高斯概率:

    def log_gaussian_prob(x, means, var):
        """
        Estimate the log Gaussian probability
        :param x: input array
        :param means: The mean of each mixture component.
        :param var: The variance of each mixture component.
        :return: the log Gaussian probability
        """
        return (-0.5 * np.log(var) - np.divide(np.square(x - means), 2 * var) - 0.5 * np.log(2 * np.pi)).sum()
    

    GMM 类中包含五个部分:__init__ 用于初始化;e_step 是 Expectation 步骤;m_step 是 Maximization 步骤;train 用于调用 E 步和 M 步进行参数训练;log_prob 计算每个高斯模型的对数高斯概率。

    def __init__(self, mfcc_data, n_components, random_state=0):
        # Initialization
        self.mfcc_data = mfcc_data
        self.means = np.tile(np.mean(self.mfcc_data, axis=0), (n_components, 1))
        # randomization
        np.random.seed(random_state)
        for k in range(n_components):
            randn_k = np.random.randn()
            self.means[k] += 0.01 * randn_k * np.sqrt(np.var(self.mfcc_data, axis=0))
        self.var = np.tile(np.var(self.mfcc_data, axis=0), (n_components, 1))
        self.weights = np.ones(n_components) / n_components
        self.n_components = n_components
    
    def e_step(self, x):
        """
        Expectation-step.
        :param x: input array-like data
        :return: Logarithm of the posterior probabilities (or responsibilities) of
                 the point of each sample in x.
        """
        log_resp = np.zeros((x.shape[0], self.n_components))
        for i in range(x.shape[0]):
            log_resp_i = np.log(self.weights)
            for j in range(self.n_components):
                log_resp_i[j] += log_gaussian_prob(x[i], self.means[j], self.var[j])
            y = np.exp(log_resp_i - log_resp_i.max())
            log_resp[i] = y / y.sum()
        return log_resp
    
    def m_step(self, x, log_resp):
        """
        Maximization step.
        :param x: input array-like data
        :param log_resp: Logarithm of the posterior probabilities (or responsibilities) of
                      the point of each sample in data
        """
        self.weights = np.sum(log_resp, axis=0) / np.sum(log_resp)
        denominator = np.sum(log_resp, axis=0, keepdims=True).T
        means_num = np.zeros_like(self.means)
        for k in range(self.n_components):
            means_num[k] = np.sum(np.multiply(x, np.expand_dims(log_resp[:, k], axis=1)), axis=0)
        self.means = np.divide(means_num, denominator)
        var_num = np.zeros_like(self.var)
        for k in range(self.n_components):
            var_num[k] = np.sum(np.multiply(np.square(np.subtract(x, self.means[k])),
                                            np.expand_dims(log_resp[:, k], axis=1)), axis=0)
        self.var = np.divide(var_num, denominator)
    
    def train(self, x):
        """
        :param x: input array-like data
        """
        log_resp = self.e_step(x)
        self.m_step(x, log_resp)
    
    def log_prob(self, x):
        """
        :param x: input array-like data
        :return: calculated log gaussian probability of single modal Gaussian
        """
        sum_prob = 0
        for i in range(x.shape[0]):
            prob_i = np.array([np.log(self.weights[j]) + log_gaussian_prob(x[i], self.means[j], self.var[j])
                               for j in range(self.n_components)])
            sum_prob += np.max(prob_i)
        return sum_prob
    

    四、Isolated Speech Recognition - HMM


    1. 隐马尔可夫模型

    参考 hmmlearn 文档:https://hmmlearn.readthedocs.io/en/stable

    隐马尔可夫模型(HMM, Hidden Markov Model) 是一种生成概率模型,其中可观测的 X X X 变量序列由内部隐状态序列 Z Z Z 生成。隐状态 (hidden states) 之间的转换假定为(一阶)马尔可夫链 (Markov chain) 的形式,可以由起始概率向量 π π π 和转移概率矩阵 A A A 指定。可观测变量的发射概率 (emission probability) 可以是以当前隐藏状态为条件的任何分布,参数为 θ \theta θ 。HMM 完全由 π π π A A A θ \theta θ 决定。

    HMM 有三个基本问题:

    • 给定模型参数和观测数据,估计最优隐状态序列 (estimate the optimal sequence of hidden states)
    • 给定模型参数和观测数据,计算模型似然 (calculate the model likelihood)
    • 仅给出观测数据,估计模型参数 (estimate the model parameters)

    第一个和第二个问题可以通过动态规划中的维特比算法 (Viterbi algorithm) 和前向-后向算法 (Forward-Backward algorithm) 来解决,最后一个问题可以通过迭代期望最大化 (EM, Expectation-Maximization) 算法求解,也称为 Baum-Welch 算法。

    如果想要直接调用封装好的库,只需在代码中加入 from hmmlearn import hmm,详情参见官方文档源码

    2. 代码实现

    对于本文的数字语音识别任务,HMM 模型的训练预测过程与 DTW 和 GMM 类似,不再重复说明。HMM 模型实现可以参考 hmmlearn源码,本文的实现关键在于 HMM 这个类中的函数。与 GMM 的 EM 算法不同的是,在 HMM 的实现中我们使用的是维特比算法 (Viterbi algorithm) 和前向-后向算法 (Forward-Backward algorithm)。

    HMM 类包含两个参数 (parameter) 和四个属性 (attribute):n_components 表示模型中状态的数目;mfcc_data 为音频提取的 MFCC 数据;means 表示在每个状态中 mixture component 的平均值;var 为方差;trans_mat 是状态之间的转移矩阵。

    class HMM():
        """Hidden Markov Model.
        Parameters
        ----------
        n_components : int
            Number of states in the model.
        mfcc_data : array, shape (, 39)
            Mfcc data of training wavs.
        Attributes
        ----------
        init_prob : array, shape (n_components, )
            Initial probability over states.
        means : array, shape (n_components, 1)
            Mean parameters for each mixture component in each state.
        var : array, shape (n_components, 1)
            Variance parameters for each mixture component in each state.
        trans_mat : array, shape (n_components, n_components)
            Matrix of transition probabilities between states.
    

    HMM 类中包含五个部分:

    • __init__ 对参数进行初始化 (Initialization);
    • viterbi Viterbi 算法用于解决 HMM 的解码问题,即找到观测序列中最可能的标记序列,本质上是一种动态规划算法 (dynamic programming);
    • forward 计算所有时间步中所有状态的对数概率 (log probabilities);
    • forward_backward 是参数估计的前向-后向算法 (Forward-Backward algorithm);
    • train 用于调用 forward_backwardviterbi 进行参数训练;
    • log_prob 计算每个模型的对数概率 (log likelihood)。
    def viterbi(self, data):
        # Viterbi algorithm is used to solve decoding problem of HMM
        # That is to find the most possible labeled sequence of the observation sequence
        for index, single_wav in enumerate(data):
            n = single_wav.shape[0]
            labeled_seq = np.zeros(n, dtype=int)
            log_delta = np.zeros((n, self.n_components))
            psi = np.zeros((n, self.n_components))
            log_delta[0] = np.log(self.init_prob)
            for i in range(self.n_components):
                log_delta[0, i] += log_gaussian_prob(single_wav[0], self.means[i], self.var[i])
            log_trans_mat = np.log(self.trans_mat)
            for t in range(1, n):
                for j in range(self.n_components):
                    temp = np.zeros(self.n_components)
                    for i in range(self.n_components):
                        temp[i] = log_delta[t - 1, i] + log_trans_mat[i, j] + log_gaussian_prob(single_wav[t], self.means[j], self.var[j])
                    log_delta[t, j] = np.max(temp)
                    psi[t, j] = np.argmax(log_delta[t - 1] + log_trans_mat[:, j])
            labeled_seq[n - 1] = np.argmax(log_delta[n - 1])
            for i in reversed(range(n - 1)):
                labeled_seq[i] = psi[i + 1, labeled_seq[i + 1]]
            self.states[index] = labeled_seq
    
    def forward(self, data):
        # Computes forward log-probabilities of all states at all time steps.
        n = data.shape[0]
        m = self.means.shape[0]
        alpha = np.zeros((n, m))
        alpha[0] = np.log(self.init_prob) + np.array([log_gaussian_prob(data[0], self.means[j], self.var[j]) for j in range(m)])
        for i in range(1, n):
            for j in range(m):
                alpha[i, j] = log_gaussian_prob(data[i], self.means[j], self.var[j]) + np.max(
                    np.log(self.trans_mat[:, j].T) + alpha[i - 1])
        return alpha
    
    def forward_backward(self, data):
        # forward backward algorithm to estimate parameters
        gamma_0 = np.zeros(self.n_components)
        gamma_1 = np.zeros((self.n_components, data[0].shape[1]))
        gamma_2 = np.zeros((self.n_components, data[0].shape[1]))
        for index, single_wav in enumerate(data):
            n = single_wav.shape[0]
            labeled_seq = self.states[index]
            gamma = np.zeros((n, self.n_components))
            for t, j in enumerate(labeled_seq[:-1]):
                self.trans_mat[j, labeled_seq[t + 1]] += 1
                gamma[t, j] = 1
            gamma[n - 1, self.n_components - 1] = 1
            gamma_0 += np.sum(gamma, axis=0)
            for t in range(n):
                gamma_1[labeled_seq[t]] += single_wav[t]
                gamma_2[labeled_seq[t]] += np.square(single_wav[t])
        gamma_0 = np.expand_dims(gamma_0, axis=1)
        self.means = gamma_1 / gamma_0
        self.var = (gamma_2 - np.multiply(gamma_0, self.means ** 2)) / gamma_0
        for j in range(self.n_components):
            self.trans_mat[j] /= np.sum(self.trans_mat[j])
    

    五、实验结果


    Github 项目地址:https://github.com/Sherry-XLL/Digital-Recognition-DTW_HMM_GMM

    1. 实验环境

      macOS Catalina Version 10.15.6, Python 3.8, PyCharm 2020.2 (Professional Edition).
    

    2. 文件简介

      dtw.py: Implementation of Dynamic Time Warping (DTW)
      gmm.py: Implementation of Gaussian Mixture Model (GMM)
      hmm.py: Implementation of Hidden Markov Model (HMM)
    
      gmm_from_sklearn.py: Train gmm model with GaussianMixture from sklearn
      hmm_from_hmmlearn.py: Train hmm model with hmm from hmmlearn
    
      preprocess.py: preprocess audios and split data
      processed_test_records: records with test audios
      processed_train_records: records with train audios
      records: original audios
      utils.py: utils function
    

    若想运行代码,只需在命令行输入如下指令,以 DTW 为例:

      eg:
      python preprocess.py (mkdir processed records)
      python dtw.py 
    

    3. 结果对比

    各个方法的数据集和预处理部分完全相同,下面是运行不同文件的结果:

    python dtw.py
    ----------Dynamic Time Warping (DTW)----------
    Train num: 160, Test num: 40, Predict true num: 31
    Accuracy: 0.78
    
    python gmm_from_sklearn.py:
    ---------- GMM (GaussianMixture) ----------
    Train num: 160, Test num: 40, Predict true num: 34
    Accuracy: 0.85
    
    python gmm.py
    ---------- Gaussian Mixture Model (GMM) ----------
    confusion_matrix: 
     [[4 0 0 0 0 0 0 0 0 0]
     [0 4 0 0 0 0 0 0 0 0]
     [0 0 4 0 0 0 0 0 0 0]
     [0 0 0 4 0 0 0 0 0 0]
     [0 0 0 0 4 0 0 0 0 0]
     [0 0 0 0 0 4 0 0 0 0]
     [0 0 0 0 0 0 4 0 0 0]
     [0 0 0 0 0 0 0 4 0 0]
     [0 0 0 0 0 0 0 0 4 0]
     [0 0 0 0 0 0 0 0 0 4]]
    Train num: 160, Test num: 40, Predict true num: 40
    Accuracy: 1.00
    
    python hmm_from_hmmlearn.py
    ---------- HMM (GaussianHMM) ----------
    Train num: 160, Test num: 40, Predict true num: 36
    Accuracy: 0.90
    
    python hmm.py
    ---------- HMM (Hidden Markov Model) ----------
    confusion_matrix: 
     [[4 0 0 0 0 0 0 0 0 0]
     [0 4 0 0 0 0 0 0 0 0]
     [0 0 3 0 1 0 0 0 0 0]
     [0 0 0 4 0 0 0 0 0 0]
     [0 0 0 0 4 0 0 0 0 0]
     [0 0 0 0 1 3 0 0 0 0]
     [0 0 0 0 0 0 3 0 1 0]
     [0 0 0 0 0 0 0 4 0 0]
     [0 0 0 1 0 0 1 0 2 0]
     [0 0 0 0 0 0 0 0 0 4]]
    Train num: 160, Test num: 40, Predict true num: 35
    Accuracy: 0.875
    
    MethodDTWGMM from sklearnOur GMMHMM from hmmlearnOur HMM
    Accuracy0.780.851.000.900.875

    值得注意的是,上面所得正确率仅供参考。我们阅读源码就会发现,不同文件中 n_components 的数目并不相同,最大迭代次数 max_iter 也会影响结果。设置不同的超参数 (hyper parameter) 可以得到不同的正确率,上表并不是各种方法的客观对比。事实上 scikit-learn 的实现更完整详细,效果也更好,文中三种方法的实现仅为基础版本。

    完整代码详见笔者的 Github 项目,如有问题欢迎在评论区留言指出。

    六、参考链接


    1. https://github.com/Sherry-XLL/Digital-Recognition-DTW_HMM_GMM
    2. https://github.com/rocketeerli/Computer-VisionandAudio-Lab/tree/master/lab1
    3. http://librosa.github.io/librosa/core.html
    4. https://python-speech-features.readthedocs.io/
    5. http://yann.lecun.com/exdb/mnist/
    6. https://www.scikitlearn.com.cn/0.21.3/20/#211
    7. https://scikit-learn.org/stable/developers/advanced_installation.html#install-bleeding-edge
    8. https://blog.csdn.net/nsh119/article/details/79584629?spm=1001.2014.3001.5501
    9. https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/mixture/_gaussian_mixture.py
    10. https://github.com/hmmlearn/hmmlearn
    11. https://hmmlearn.readthedocs.io/en/stable
    12. https://hmmlearn.readthedocs.io/en/stable/api.html#hmmlearn-hmm
    展开全文
  • gmmhmm 原理

    2019-06-03 13:40:14
    GMM-HMM模型之后,如何对语音信号进行识别,那么GMM-HMM模型的训练过程又是怎么样的呢?直接摘录abcjennifer的博客,首先,GMM的训练采用的是EM算法: E(estimate)-step: 根据当前参数(means,variances, mixing ...

    GMM-HMM模型之后,如何对语音信号进行识别,那么GMM-HMM模型的训练过程又是怎么样的呢?直接摘录abcjennifer的博客,首先,GMM的训练采用的是EM算法:
    E(estimate)-step: 根据当前参数 (means, variances, mixing parameters)估计P(j|x)

    M(maximization)-step: 根据当前P(j|x) 计算GMM参数

    而HMM也是采用的类似于EM算法的前向后向算法(Baum-Welch算法),过程为:

    E(estimate)-step: 给定observation序列,估计时刻t处于状态sj的概率

    M(maximization)-step: 根据该概率重新估计HMM参数aij.
     

    展开全文
  • 用于隔离数字识别的简单GMM-HMM模型 简单的GMMHMM模型的Python实现,用于隔离数字识别。 此实现包含3个模型: 单一高斯:使用具有对角协方差的单一高斯对每个数字进行建模。 高斯混合模型(GMM):每个数字都是...
  • score()函数计算的是ln(x), x&lt;=1,那这样的话结果不应该是&lt;=0的吗?不太明白原因,是模型参数设置出现问题了吗?求大神解答,谢谢

    score()函数计算的是ln(x), x<=1,那这样的话结果不应该是<=0的吗?

    不太明白原因,是模型参数设置出现问题了吗?

    求大神解答,谢谢

    展开全文
  • GMM-HMM 详解

    千次阅读 2019-03-20 20:50:53
    本文简明讲述GMM-HMM在语音识别上的原理,建模和测试过程。这篇blog只回答三个问题: 1.什么是Hidden Markov Model? HMM要解决的三个问题: 1) Likelihood 2) Decoding 3) Training 2. GMM是神马?怎样用GMM求...

    本文简明讲述GMM-HMM在语音识别上的原理,建模和测试过程。这篇blog只回答三个问题:

    1. 什么是Hidden Markov Model

    HMM要解决的三个问题:

    1) Likelihood

    2) Decoding

    3) Training

    2. GMM是神马?怎样用GMM求某一音素(phoneme)的概率?

     

    3. GMM+HMM大法解决语音识别

    3.1 识别

    3.2 训练

    3.2.1 Training the params of GMM

    3.2.2 Training the params of HMM

     

     

     

    首先声明我是做视觉的不是做语音的,迫于**需要24小时速成语音。上网查GMM-HMM资料中文几乎为零,英文也大多是paper。苦苦追寻终于貌似搞懂了GMM-HMM,感谢语音组老夏(http://weibo.com/ibillxia)提供资料给予指导。本文结合最简明的概括还有自己一些理解应运而生,如有错误望批评指正。

     

    ====================================================================

     

     

    1. 什么是Hidden Markov Model

    ANS:一个有隐节点(unobservable)和可见节点(visible)的马尔科夫过程(见详解)。

    隐节点表示状态,可见节点表示我们听到的语音或者看到的时序信号。

    最开始时,我们指定这个HMM的结构,训练HMM模型时:给定n个时序信号y1...yT(训练样本), 用MLE(typically implemented in EM) 估计参数:

    1. N个状态的初始概率

    2. 状态转移概率a

    3. 输出概率b

    --------------

     

    • 在语音处理中,一个word由若干phoneme(音素)组成;
    • 每个HMM对应于一个word或者音素(phoneme)
    • 一个word表示成若干states,每个state表示为一个音素

     

    用HMM需要解决3个问题:

    1). Likelihood: 一个HMM生成一串observation序列x的概率< the Forward algorithm>

    其中,αt(sj)表示HMM在时刻t处于状态j,且observation = {x1,...,xt}的概率

    aij是状态i到状态j的转移概率,

    bj(xt)表示在状态j的时候生成xt的概率,

     

     

     

     

     

     

    2). Decoding: 给定一串observation序列x,找出最可能从属的HMM状态序列< the Viterbi algorithm>

     

    在实际计算中会做剪枝,不是计算每个可能state序列的probability,而是用Viterbi approximation:

    从时刻1:t,只记录转移概率最大的state和概率。

    记Vt(si)为从时刻t-1的所有状态转移到时刻t时状态为j的最大概率:

    为:从时刻t-1的哪个状态转移到时刻t时状态为j的概率最大;

    进行Viterbi approximation过程如下:

     

    然后根据记录的最可能转移状态序列进行回溯:

     

     

     

    3). Training: 给定一个observation序列x,训练出HMM参数λ = {aij, bij}  the EM (Forward-Backward) algorithm

    这部分我们放到“3. GMM+HMM大法解决语音识别”中和GMM的training一起讲

     

     

     

     

     

     

     

    ---------------------------------------------------------------------

     

    2. GMM是神马?怎样用GMM求某一音素(phoneme)的概率?

    2.1 简单理解混合高斯模型就是几个高斯的叠加。。。e.g. k=3

    fig2. GMM illustration and the probability of x

     

     

    2.2 GMM for state sequence 

    每个state有一个GMM,包含k个高斯模型参数。如”hi“(k=3):

    PS:sil表示silence(静音)

    fig3. use GMM to estimate the probability of a state sequence given observation {o1, o2, o3}

     

    其中,每个GMM有一些参数,就是我们要train的输出概率参数

    fig4. parameters of a GMM

    怎么求呢?和KMeans类似,如果已知每个点x^n属于某每类 j 的概率p(j|x^n),则可以估计其参数:

     , 其中 

     

    只要已知了这些参数,我们就可以在predict(识别)时在给定input sequence的情况下,计算出一串状态转移的概率。如上图要计算的state sequence 1->2->2概率:

    fig5. probability of S1->S2->S3 given o1->o2->o3

     

     

     

     

     

     


    ---------------------------------------------------------------------

    3. GMM+HMM大法解决语音识别

    <!--识别-->

    我们获得observation是语音waveform, 以下是一个词识别全过程:

    1). 将waveform切成等长frames,对每个frame提取特征(e.g. MFCC), 

    2).对每个frame的特征跑GMM,得到每个frame(o_i)属于每个状态的概率b_state(o_i)

     

    fig6. complete process from speech frames to a state sequence

     

    3). 根据每个单词的HMM状态转移概率a计算每个状态sequence生成该frame的概率; 哪个词的HMM 序列跑出来概率最大,就判断这段语音属于该词

     

    宏观图:

    fig7. Speech recognition, a big framework

    (from Encyclopedia of Information Systems, 2002)

     

     

     

    <!--训练-->

    好了,上面说了怎么做识别。那么我们怎样训练这个模型以得到每个GMM的参数和HMM的转移概率什么的呢?

     

     

     

    ①Training the params of GMM

    GMM参数:高斯分布参数:

    从上面fig4下面的公式我们已经可以看出来想求参数必须要知道P(j|x),即,x属于第j个高斯的概率。怎么求捏?

    fig8. bayesian formula of P( j | x )

    根据上图 P(j | x), 我们需要求P(x|j)和P(j)去估计P(j|x). 

    这里由于P(x|j)和P(j)都不知道,需要用EM算法迭代估计以最大化P(x) = P(x1)*p(x2)*...*P(xn):

    A. 初始化(可以用kmeans)得到P(j)

    B. 迭代

        E(estimate)-step: 根据当前参数 (means, variances, mixing parameters)估计P(j|x)

        M(maximization)-step: 根据当前P(j|x) 计算GMM参数(根据fig4 下面的公式:)

     , 其中 

     

     

     

     

    ②Training the params of HMM

    前面已经有了GMM的training过程。在这一步,我们的目标是:从observation序列中估计HMM参数λ;

    假设状态->observation服从单核高斯概率分布:,则λ由两部分组成:

     

     

    HMM训练过程:迭代

     

        E(estimate)-step: 给定observation序列,估计时刻t处于状态sj的概率 

        M(maximization)-step: 根据重新估计HMM参数aij. 

    其中,

     

    E-step: 给定observation序列,估计时刻t处于状态sj的概率 

    为了估计, 定义: t时刻处于状态sj的话,t时刻未来observation的概率。即

    这个可以递归计算:β_t(si)=从状态 si 转移到其他状态 sj 的概率aij * 状态 i 下观测到x_{t+1}的概率bi(x_{t+1}) * t时刻处于状态sj的话{t+1}后observation概率β_{t+1}(sj)

    即:

    定义刚才的为state occupation probability,表示给定observation序列,时刻t处于状态sj的概率P(S(t)=sj | X,λ) 。根据贝叶斯公式p(A|B,C) = P(A,B|C)/P(B|C),有:

     

     

    由于分子p(A,B|C)为

    其中,αt(sj)表示HMM在时刻t处于状态j,且observation = {x1,...,xt}的概率

    : t时刻处于状态sj的话,t时刻未来observation的概率;

    finally, 带入的定义式有:

     

     

    好,终于搞定!对应上面的E-step目标,只要给定了observation和当前HMM参数 λ,我们就可以估计了对吧 (*^__^*) 

     

     

     

     

     

    M-step:根据重新估计HMM参数λ:

    对于λ中高斯参数部分,和GMM的M-step是一样一样的(只不过这里写成向量形式):

    对于λ中的状态转移概率aij, 定义C(Si->Sj)为从状态Si转到Sj的次数,有

    实际计算时,定义每一时刻的转移概率为时刻t从si->sj的概率:

    那么就有:

    把HMM的EM迭代过程和要求的参数写专业点,就是这样的:

    PS:这个训练HMM的算法叫 Forward-Backward algorithm。

     

     

     

     

    一个很好的reference:点击打开链接

     

    from: http://blog.csdn.net/abcjennifer/article/details/27346787

    展开全文
  • hmm模型matlab代码HMM-GMM 这是我个人实现的隐马尔可夫模型和高斯混合模型,这是统计机器学习中的两个经典生成模型。 HMM是在无监督的情况下进行训练的,代码实现了前向后退算法,以在给出部分/全部观测值的任何时间...
  • GMM-HMM kaldi 详解

    千次阅读 2019-04-09 09:00:34
    而且掌握HMM-GMM结构,对于深入理解语音识别过程是由有一定好处的。 但对于外行(比如像我这种从机械行业转行到语音识别领域的人)或者刚接触语音识别的人来说,要弄懂HMM-GMM结构还是要花不少时间的,因为语音识别...
  • GMM-HMM理解

    千次阅读 2017-12-03 12:13:47
    本文简明讲述GMM-HMM在语音识别上的原理,建模和测试过程。这篇blog只回答三个问题: 1. 什么是Hidden Markov Model? HMM要解决的三个问题: 1) Likelihood 2) Decoding 3) Training 2. GMM是...
  • GMM-HMM学习笔记

    万次阅读 多人点赞 2015-07-17 16:31:23
    最近几天钻研了语音处理中的GMM-HMM模型,阅读了一些技术博客和学术论文,总算是对这个框架模型和其中的算法摸清了皮毛。在这里梳理一下思路,总结一下这几天学习的成果,也是为以后回顾时提高效率。 本文主要结合...
  • 上一专题GMM-HMM声学模型中讲述了其理论知识,这一章利用理论搭建一套GMM-HMM系统,来识别连续0123456789的英文语音。 本系统是单音素,未涉及后面三音子的训练以及决策树的内容。 在GMM专题和HMM专题中分别讲述...
  • 基于GMM的0-9孤立词识别系统以词为训练单位,添加新词汇需要重新进行训练,若要涵盖所以词,差不多6万个词,训练量极大,预测时也要计算6万个模型的似然,哪个大预测出哪个,在实际应用中有局限性,只能应用于小词汇...
  • 该压缩包中包含一个完整的语音识别程序,代码使用matlab实现,使用了经典的GMMHMM模型。附件中还包含完整的说明文档,介绍了一些基本原理,和该源码的使用方法。是语音识别入门必读源码之一。该源码只需要很小的...
  • GMM HMM语音识别模型 原理篇
  • GMM-HMM总结

    千次阅读 2015-12-19 22:42:11
    GMM-HMM是两个算法,GMMHMM。 其中GMM是混合高斯模型(Gauss of mixture models),什么意思呢。意思是说,给出一堆观察序列(可以是多维的可以是一维的),用几个高斯函数模拟一个模型,这个模型产生这些观察...
  • GMM-HMM语音识别模型 原理篇

    万次阅读 多人点赞 2014-05-28 20:52:33
    本文简明讲述GMM-HMM在语音识别上的原理,建模和测试过程。这篇blog只回答三个问题: 1. 什么是Hidden Markov Model? 2. GMM是神马?怎样用GMM求某一音素(phoneme)的概率? 3. 用GMM+HMM大法解决语音识别
  • 很详细的EM算法,GMMHMM训练用

    热门讨论 2010-01-22 17:54:45
    详细介绍了训练hmmgmm的EM算法,以及其应用,对利用这些模型的朋友,想了解此算法的最好的资料。
  • GMM-HMM孤立词识别

    2019-09-19 16:45:34
    之前做过的GMM-HMM孤立词识别,现在整理一下。 这里我们把输入的语音当做语音识别中的一个音素来建模,假定建模的HMM状态链是7状态的,因此,孤立词识别就是只用到了声学模型部分,没有涉及语言模型这些。 1、将...
  • gmm-hmm到dnn-hmm

    2019-11-11 16:37:46
    gmm-hmm就是把我们的特征用混合高斯模型区模拟,然后把均值和方差输入到hmm的模型里。 (2)dnn-hmm的模型图 (3)dbn-hmm的模型图 3、语言模型 对训练文本数据库进行语法、语义分析,经过基于统计模型训练...
  • 声学模型GMM-HMM训练

    千次阅读 2017-10-28 16:50:34
    语音识别中声学模型是重中之重,常用到的声学模型是GMM-HMM,本文记录下声学模型训练的细节。下图是识别某个孤立字的GMM-HMM示意图。HMM部分是文字所占的音素以及其转移概率。语音帧通过已经训练好的GMM得属于某个...
  • 本文介绍基于GMM-HMM的语音识别系统,包括:孤立词、单音素、三音素识别系统。
  • GMM-hmm算法学习笔记

    千次阅读 2014-12-22 20:31:33
    一、基础 设X是一串已经观察的听觉特征向量,W...HMM模型就是一种声学模型。 ‘下面举个例子: “NO right”分为两个词,每个词又分为几个音素。对于每个音素跑HMM模型。 HMM模型如下: 参数 入 : 转移概率是akj,表
  • Kaldi单音素GMM学习笔记。从原理、脚本、程序和类四个方面介绍单音素GMM和Kaldi代码。Kaldi单音素GMM学习笔记。从原理、脚本、程序和类四个方面介绍单音素GMM和Kaldi代码。Kaldi单音素GMM学习笔记。从原理、脚本、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,386
精华内容 1,354
关键字:

gmmhmm

友情链接: code.rar