论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks

文章目录

  • 1 概述
  • 2 信号预处理
  • 3 分类神经网络
  • 4 动态贝叶斯网络(HMM)
    • 4.1 原始的bar pointer model
    • 4.2 原始的bar pointer model的缺点
    • 4.3 改进后的模型
  • 5 预测
  • 参考资料

1 概述

最近在做音乐卡点相关的项目,需要对音乐的基本特征进行理解,比如beats和downbeats就是最基本的特征。madmom是我找到的一个对beats和downbeats的检测都有实现的第三方库,于是就认真学习了一下,把其中用到的方法和自己的理解记录下来。

madmom中的beats和downbeats检测就是复现了Joint Beat and Downbeat Tracking with Recurrent Neural Networks这篇论文,其核心思想就是HMM,如果对HMM没有扎实的理解的话,建议先看我写的另一篇搞懂HMM。本文不会对HMM的基本概念做详细的说明。

在说论文之前,我先来说明一些音乐上的专用术语,对齐一些概念,便于后文的叙述。有些术语在本文中可能不会再次出现,但有助于理解音乐,就一并写上了。

名词 名词解释
拍子(beat) 在音乐中,时间被分成均等的基本单位,每个单位叫做一个“拍子”或称一拍。
强拍(downbeat) 音乐中的强拍。是beats的子集。一般是每个小节的起始beat
小节(measure/bar) 音乐是由强拍和弱拍交替进行的,是按照一定的规律构成最小的节拍组织一小节,然后以此为基础循环往复。一个小节一般有2拍,3拍,4拍或者6拍等等
节拍(meter) beats per measure,一首歌的节拍,一般表示为1/4拍或者2/4拍或者3/8拍等等。比如3/4拍表示4分音符为1拍,每小节有3拍,节拍强度为强、弱、弱。
全局节奏(tempo) 一般用bpm(beats per minute)来做单位,表示每分钟有多少个beats,用来衡量音乐的速度,一首音乐可以由不同的tempo演奏。
局部节奏(tempi) 每两个相邻的beats之间可以是有不同的1/16音符,或是1/8音符,或是1/4等组成的,这个就是局部节奏
onset 一个音符被乐器或者人发出声音的那个时刻点。
峰值(peak) onset包络图的峰值,具体可见peak_pick。

再说回这篇论文。这篇论文概括地说就是把信号切成多个frames,每个frame会在RNN网络之后对应一个概率输出,表示该frame是beat还是downbeat还是都不是。HMM会在发射概率的计算中借助于这个RNN结果,把frame在measure当中的相对位置和tempi作为隐变量,得到最佳的隐变量路径,并把该路径解码为beats和downbeats。

下面会分步骤说明一下每个步骤,着重是HMM和预测部分。这两部分理解起来有点绕。

2 信号预处理

这部分其实就是一个类似于MFCC提取语音信号特征的过程,作为RNN的输入,这里简单说下。先是用汉宁窗对信号做了有交叠的分割,分成了100fps(frame per second),并基于此做了短时傅里叶变换(STFT)。丢弃了相位特征,只保留幅值特征,因为人耳是听不出相位的。为了保证时域和频域的精度,做STFT时,分别用了1024,2048和4096三种窗口大小。把频段限制在了[30, 17000]Hz的的范围,并给每个八度分了3,6和12个频段,分别对应于1024,2048和4096三种窗口大小。同时也对频谱进行了一阶差分,也作为特征concat进去。最终,输入网络的特征维度为314。

3 分类神经网络

这部分用了由LSTM搭建而成的网络,目的是给每个frame进行分类,输出两个概率值,(是beat的概率,是downbeat的概率),取较大的作为该frame的分类。为了避免混淆,downbeat不属于beat。

同时,也设置了 θ = 0.05 \theta=0.05 θ=0.05的阈值,只有大于该阈值,才会被判为是beat或者downbeat。这是为了减小音乐头尾有空白的干扰。

既然每个frame是beat还是downbeat的概率都知道了,那我直接取每个frame最大概率的类别,两个概率都比较小的就认为既不是beat也不是downbeat,这事不就成了?

理想很美好,现实并非如此,不要被论文中的图片给误导了,来看下模型的实际输出是长啥样的。
论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks_第1张图片

图3-1 RNN网络输出示意图

模型的输出如上图3-1所示,这个是一条30s左右的实际样例。上半个图是每个frame为beat的概率,下半个图是每个frame为downbeat的概率。只看downbeat这部分的话,不难看出概率比较高的frame之间的间距有长有短,而在一个小节(bar)当中,强拍(downbeat)应该只会出现一次,次强拍的出现会对其产生干扰。RNN不知道强拍的出现是有周期性的,所以需要HMM来判断究竟哪些位置是强拍,哪些位置是弱拍,哪些位置什么都不是。

从另一个角度来思考,RNN这里并不知道音乐的meter是几几拍,没有这个信息,要RNN直接判断强拍和弱拍的难度是很大的。下一节的HMM的作用就是根据RNN的结果,去选一组最优的beats和downbeats。

4 动态贝叶斯网络(HMM)

这是重点部分,我们来详细讲一下。Joint Beat and Downbeat Tracking with Recurrent Neural Networks对这部分的说明很不清晰,我们直接看它沿用的An Efficient State-Space Model for Joint Tempo and Meter Tracking中的说明即可。

4.1 原始的bar pointer model

早在2006年的时候,Bayesian Modelling of Temporal Structure in Musical Audio就提出过用HMM解决beat tracking问题的方法。现在的方法,就是在它的基础上优化的,我们先来看看最早版本的HMM是怎么设计的。

我们令第 k k k个frame的隐变量为 x k = [ Φ k , Φ ˙ k ] \bold{x}_k=[\Phi_k, \dot{\Phi}_k] xk=[Φk,Φ˙k] Φ k ∈ { 1 , 2 , . . . , M } \Phi_k \in \{1,2,...,M\} Φk{1,2,...,M}表示第 k k k个frame在某个小节(bar)中的相对位置,1表示起始位置, M M M表示结束位置,这个bar被分成了M个相对位置,一般是均分的。 Φ ˙ k ∈ { Φ ˙ m i n , Φ ˙ m i n + 1 , . . . , Φ ˙ m a x } \dot{\Phi}_k \in \{\dot{\Phi}_{min}, \dot{\Phi}_{min} + 1, ..., \dot{\Phi}_{max}\} Φ˙k{Φ˙min,Φ˙min+1,...,Φ˙max}表示第 k k k个frame的tempi, Φ ˙ m i n \dot{\Phi}_{min} Φ˙min Φ ˙ m a x \dot{\Phi}_{max} Φ˙max是人为设置的上下界。说的通俗一点,把 Φ k \Phi_k Φk看成位移的话, Φ ˙ k \dot{\Phi}_k Φ˙k就是速度, k + 1 k+1 k+1个frame的位置 Φ k + 1 \Phi_{k + 1} Φk+1就是 Φ k + Φ ˙ k \Phi_k + \dot{\Phi}_k Φk+Φ˙k。说的音乐一点,就是 Φ ˙ k \dot{\Phi}_k Φ˙k表示了第 k k k个frame是一个几分之几的音符,比如1/8音符,如果知道这首歌是4/4拍的话,第 k k k个frame就走了 ( 1 / 8 ) / ( 4 ∗ 1 / 4 ) = 1 / 8 (1/8) / (4*1/4) = 1/8 (1/8)/(41/4)=1/8个小节(bar),这里使用离散的 M M M个数值来表示了。

观测变量就是我们的frames的特征序列,记为 { y 1 , y 2 , . . . , y K } \{\bold{y}_1, \bold{y}_2, ..., \bold{y}_K\} {y1,y2,...,yK}。我们想要找到一串隐变量的序列 x 1 : K ∗ = { x 1 ∗ , x 2 ∗ , . . . , x K ∗ } \bold{x}_{1:K}^*=\{\bold{x}_1^*, \bold{x}_2^*, ..., \bold{x}_K^*\} x1:K={x1,x2,...,xK}使得

x 1 : K ∗ = a r g max ⁡ x 1 : K P ( x 1 : K ∣ y 1 : K ) (4-1) \bold{x}_{1:K}^* = arg\max_{\bold{x}_{1:K}} P(\bold{x}_{1:K} | \bold{y}_{1:K}) \tag{4-1} x1:K=argx1:KmaxP(x1:Ky1:K)(4-1)

( 4 − 1 ) (4-1) (41)可以用viterbi算法来解,不清楚的可以参看我的搞懂HMM。求解式 ( 4 − 1 ) (4-1) (41)需要知道三个模型,一个是初始概率模型 P ( x 1 ) P(\bold{x}_1) P(x1),第二个是状态转移模型 P ( x k ∣ x k − 1 ) P(\bold{x}_k|\bold{x}_{k-1}) P(xkxk1),第三个是发射概率 P ( y k ∣ x k ) P(\bold{y}_k|\bold{x}_k) P(ykxk)

(1)初始概率
初始概率 P ( x 1 ) P(\bold{x}_1) P(x1),作者用了均匀分布初始化,后面用数据学就行,没啥说的。

(2)转移概率
转移概率是个比较关键的概率,我们来仔细看下

P ( x k ∣ x k − 1 ) = P ( Φ k , Φ ˙ k ∣ Φ k − 1 , Φ ˙ k − 1 ) = P ( Φ k ∣ Φ ˙ k , Φ k − 1 , Φ ˙ k − 1 ) P ( Φ ˙ k ∣ Φ k − 1 , Φ ˙ k − 1 ) \begin{aligned} P(\bold{x}_k | \bold{x}_{k-1}) &= P(\Phi_k, \dot{\Phi}_k | \Phi_{k-1}, \dot{\Phi}_{k-1}) \\ &=P(\Phi_k | \dot{\Phi}_k, \Phi_{k-1}, \dot{\Phi}_{k-1})P(\dot{\Phi}_k | \Phi_{k-1}, \dot{\Phi}_{k-1}) \end{aligned} P(xkxk1)=P(Φk,Φ˙kΦk1,Φ˙k1)=P(ΦkΦ˙k,Φk1,Φ˙k1)P(Φ˙kΦk1,Φ˙k1)

我们把 Φ k \Phi_k Φk理解成位移, Φ ˙ k \dot{\Phi}_k Φ˙k理解成速度,这应该也是为啥这两个变量的符号只差了一个一阶导的符号的原因。

Φ k \Phi_k Φk k k k时刻的位置,它由上一刻的位置 Φ k − 1 \Phi_{k-1} Φk1和上一刻的速度 Φ ˙ k − 1 \dot{\Phi}_{k-1} Φ˙k1决定,与 k k k时刻的速度无关,故有

P ( Φ k ∣ Φ ˙ k , Φ k − 1 , Φ ˙ k − 1 ) = P ( Φ k ∣ Φ k − 1 , Φ ˙ k − 1 ) P(\Phi_k | \dot{\Phi}_k, \Phi_{k-1}, \dot{\Phi}_{k-1}) = P(\Phi_k | \Phi_{k-1}, \dot{\Phi}_{k-1}) P(ΦkΦ˙k,Φk1,Φ˙k1)=P(ΦkΦk1,Φ˙k1)

Φ ˙ k \dot{\Phi}_k Φ˙k k k k时刻的速度,它只与 k − 1 k-1 k1时刻的速度有关,速度不太会突变,与位置无关,故有

P ( Φ ˙ k ∣ Φ k − 1 , Φ ˙ k − 1 ) = P ( Φ ˙ k ∣ Φ ˙ k − 1 ) P(\dot{\Phi}_k | \Phi_{k-1}, \dot{\Phi}_{k-1}) = P(\dot{\Phi}_k | \dot{\Phi}_{k-1}) P(Φ˙kΦk1,Φ˙k1)=P(Φ˙kΦ˙k1)

于是有

P ( x k ∣ x k − 1 ) = P ( Φ k ∣ Φ k − 1 , Φ ˙ k − 1 ) P ( Φ ˙ k ∣ Φ ˙ k − 1 ) (4-2) P(\bold{x}_k | \bold{x}_{k-1}) = P(\Phi_k | \Phi_{k-1}, \dot{\Phi}_{k-1})P(\dot{\Phi}_k | \dot{\Phi}_{k-1}) \tag{4-2} P(xkxk1)=P(ΦkΦk1,Φ˙k1)P(Φ˙kΦ˙k1)(4-2)

P ( Φ k ∣ Φ k − 1 , Φ ˙ k − 1 ) P(\Phi_k | \Phi_{k-1}, \dot{\Phi}_{k-1}) P(ΦkΦk1,Φ˙k1)就是一个位移的计算,可以定义为

P ( Φ k ∣ Φ k − 1 , Φ ˙ k − 1 ) = { 1 , i f   ( Φ k − 1 + Φ ˙ k − 1 − 1 ) %   M + 1 0 , o t h e r w i s e (4-3) P(\Phi_k | \Phi_{k-1}, \dot{\Phi}_{k-1}) = \begin{cases} 1, &if \ (\Phi_{k-1} + \dot{\Phi}_{k-1} - 1) \% \ M + 1\\ 0, &otherwise \end{cases} \tag{4-3} P(ΦkΦk1,Φ˙k1)={1,0,if (Φk1+Φ˙k11)% M+1otherwise(4-3)

也就是 Φ k = ( Φ k − 1 + Φ ˙ k − 1 − 1 ) %   M + 1 \Phi_k = (\Phi_{k-1} + \dot{\Phi}_{k-1} - 1) \% \ M + 1 Φk=(Φk1+Φ˙k11)% M+1的意思。这里取余是为了在下一个bar中,位置重新计数。为啥要先减1再取余再加1?我猜测是为了避免产生 Φ k = 0 \Phi_k=0 Φk=0的情况,使得 Φ k \Phi_k Φk的取值在 { 1 , 2 , . . . , M } \{1,2,...,M\} {1,2,...,M},只是为了符号的统一。

P ( Φ ˙ k ∣ Φ ˙ k − 1 ) P(\dot{\Phi}_k | \dot{\Phi}_{k-1}) P(Φ˙kΦ˙k1)是一个速度变化的概率,定义为

P ( Φ ˙ k ∣ Φ ˙ k − 1 ) = { 1 − p , Φ ˙ k = Φ ˙ k − 1 p 2 , Φ ˙ k = Φ ˙ k − 1 + 1 p 2 , Φ ˙ k = Φ ˙ k − 1 − 1 (4-4) P(\dot{\Phi}_k | \dot{\Phi}_{k-1}) = \begin{cases} 1 - p, & \dot{\Phi}_k=\dot{\Phi}_{k-1} \\ \frac{p}{2}, & \dot{\Phi}_k=\dot{\Phi}_{k-1} + 1 \\ \frac{p}{2}, & \dot{\Phi}_k=\dot{\Phi}_{k-1} - 1 \end{cases} \tag{4-4} P(Φ˙kΦ˙k1)=1p,2p,2p,Φ˙k=Φ˙k1Φ˙k=Φ˙k1+1Φ˙k=Φ˙k11(4-4)

p p p是速度发生变化的一个概率,我们人为限制了速度只能在距离为1的速度上转移或者保持不变。在边界 Φ ˙ m i n \dot{\Phi}_{min} Φ˙min Φ ˙ m a x \dot{\Phi}_{max} Φ˙max上,略有不同,保持速度不超界即可。 p p p是需要学习得到的。

论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks_第2张图片

图4-1 原始状态转移示意图

原始状态转移示意图如图4-1所示,可以很明显滴看出位置和速度的关系。

(3)发射概率
这里是结合RNN的结果的地方。我们假设第 k k k个frame为beat的概率是 b k b_k bk,为downbeat的概率是 d k d_k dk,既不是beat也不是downbeat的概率为 n k n_k nk。那么发射概率就为

P ( y k ∣ x k ) = { b k , s k ∈ B d k , b k ∈ D n k λ 0 − 1 , o t h e r w i s e (4-5) P(\bold{y}_k | \bold{x}_k) = \begin{cases} b_k, &s_k \in B \\ d_k, &b_k \in D \\ \frac{n_k}{\lambda_0 - 1}, & otherwise \end{cases} \tag{4-5} P(ykxk)=bk,dk,λ01nk,skBbkDotherwise(4-5)

B B B表示 x k x_k xk被认为是beat的集合, D D D表示 x k x_k xk被认为是downbeat的集合。怎么样的 x k x_k xk会被认为是beat或是downbeat呢?这点论文中没有细说,我看了A Multi-model Approach to Beat Tracking Considering Heterogeneous Music Styles中的做法,大概猜测了一下,是将位置在小节等分点附近的states作为beat或者downbeat,每个小节的第一个等分点附近为downbeat(每小节的第一拍为强拍),其他的等分点附近为beat。这个附近有多近,其实是个可以人为设置的范围。按几等份算,则是一个用户需要提前输入的参数,比如一首歌是3/4拍的,那每个小节就有3拍,就三等分,又比如一首歌是6/8拍的,那每个小节就有6拍,就六等份。如果不知道是几几拍的音乐的话,就一个个算过去,取概率最大的,这个在下一节会讲。

λ 0 \lambda_0 λ0是个超参数,论文中取 λ 0 = 16 \lambda_0=16 λ0=16得到了最好的实验结果。 B B B D D D的范围和 λ 0 \lambda_0 λ0相关。

4.2 原始的bar pointer model的缺点

(1)位置(时间)分辨率
原文中说是时间分辨率,我这里直接说成位置分辨率了,这样比较方便理解。作者认为不同的速度下,需要的位置分辨率是不同的。速度快的,每次都大步大步跨,需要的位置分辨率很低;速度慢的,每次跨的步幅小,需要的位置分辨率就高了。一句话说,就是不同的速度条件下,要不同的位置分辨率。

(2)速度分辨率
原文中的tempo就是我这里的速度。作者认为人耳对于速度的变化感知,在不同的速度下是不一样。比如在速度为1的时候,速度加个1,变成2,听起来就变化很明显了。但是在速度为10的时候,速度价格1,变成11,听起来都没什么变化。也就是人耳的听觉不是linear的,而是log linear的。

(3)速度的稳定性
使用HMM时,有一个齐次马尔科夫假设,认为 Φ k \Phi_k Φk只依赖于 Φ ˙ k \dot{\Phi}_k Φ˙k,这可能会导致同一个beat里,速度经过几个frames之后就产生比较大的变化,也就是速度的稳定性无法保证。

4.3 改进后的模型

论文针对4.2中提出的三点对模型进行了改进,改进都是针对状态转移的计算的。改进后的状态转移示意图,如下图4-2所示。
论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks_第3张图片

图4-2 改进后的状态转移示意图

(1)位置分辨率的改进
这里说白了就是根据速度来调整一个bar要被分成几份。从图4-2中的横向可以看出,tempo越大的,bar position就被分的越稀疏,反之越密。划分方式我们从具体madmom的实现来看,不看论文里说的,论文里说的有些模糊。

"""
max_bpm:用户输入,表示最大的beats per minute,默认为215
min_bpm:用户输入,表示最小的beats per minute,默认为55
fps:用户输入,frame per second

min_interval:计算得到,最小的frame per beat
max_interval:计算得到,最大的frame per beat
"""
# convert timing information to construct a beat state space
min_interval = 60. * fps / max_bpm # second per beat * frame per second = frame per beat
max_interval = 60. * fps / min_bpm

上面代码片中的min_interval和max_interval就是由最大的bpm和最小的bpm确定的每个beat被分为多少个frames。一个bar有多少个beats也是用户输入的,所以一个bar的bar positions也就根据速度的不同确定了。

(2)速度分辨率的改进
速度在min_bpm和max_bpm的范围内,按log linear的方式进行了划分。具体的实现可以看这一段https://github.com/CPJKU/madmom/blob/master/madmom/features/beats_hmm.py#L66。应该说实现中,没有速度这个东西,都转变成了位置。不同bpm下有不同的位置点,log linear的对象是位置。

(3)速度稳定性
作者为了保证速度的稳定性,速度只能在每个beat的位置上发生改变,改变时依赖于一个分布,这个分布是在实验中试了几个得到的。

Φ k ∈ B \Phi_k \in B ΦkB

P ( Φ ˙ k ∣ Φ ˙ k − 1 ) = f ( Φ ˙ k , Φ ˙ k − 1 ) (4-6) P(\dot{\Phi}_k | \dot{\Phi}_{k-1}) = f(\dot{\Phi}_k, \dot{\Phi}_{k-1}) \tag{4-6} P(Φ˙kΦ˙k1)=f(Φ˙k,Φ˙k1)(4-6)

其中 f ( Φ ˙ k , Φ ˙ k − 1 ) f(\dot{\Phi}_k, \dot{\Phi}_{k-1}) f(Φ˙k,Φ˙k1)可以是各种各样的函数,效果比较好的是

f ( Φ ˙ k , Φ ˙ k − 1 ) = e x p ( − λ × ∣ Φ ˙ k Φ ˙ k − 1 − 1 ∣ ) (4-7) f(\dot{\Phi}_k, \dot{\Phi}_{k-1}) = exp(-\lambda \times |\frac{\dot{\Phi}_k}{\dot{\Phi}_{k-1}} - 1|) \tag{4-7} f(Φ˙k,Φ˙k1)=exp(λ×Φ˙k1Φ˙k1)(4-7)

不难看出,当 Φ ˙ k − 1 = Φ ˙ k \dot{\Phi}_{k-1} = \dot{\Phi}_k Φ˙k1=Φ˙k时概率最大。 λ ∈ [ 1 , 300 ] \lambda \in [1, 300] λ[1,300]是一个超参数,不同 λ \lambda λ f ( Φ ˙ k , Φ ˙ k − 1 ) f(\dot{\Phi}_k, \dot{\Phi}_{k-1}) f(Φ˙k,Φ˙k1)的结果如下图4-3所示。
论文阅读 - Joint Beat and Downbeat Tracking with Recurrent Neural Networks_第4张图片

图4-3 不同参数下f的结果图

Φ k ∉ B \Phi_k \notin B Φk/B
P ( Φ ˙ k ∣ Φ ˙ k − 1 ) = { 1 , Φ ˙ k = Φ ˙ k − 1 0 , o t h e r w i s e (4-8) P(\dot{\Phi}_k | \dot{\Phi}_{k-1}) = \begin{cases} 1, & \dot{\Phi}_k = \dot{\Phi}_{k-1} \\ 0, & otherwise \end{cases} \tag{4-8} P(Φ˙kΦ˙k1)={1,0,Φ˙k=Φ˙k1otherwise(4-8)

表示速度不变。

5 预测

madmom很清晰地把整个模型分成了两块,DBNDownBeatTrackingProcessor和RNNDownBeatProcessor。RNNDownBeatProcessor就是我们的RNN网络,DBNDownBeatTrackingProcessor是HMM的部分。从宏观上讲,beats和downbeats的位置是由RNN大致确定,然后由HMM根据周期性这个条件去决定最后的位置的。RNN可以理解为是针对局部的理解,HMM是针对全局的决策。

在预测的时候,我们会告诉模型这首歌每个bar的beat有几个,如果不确定的话,就把beats_per_bar=[2,3,4,6]全填上,让模型每个跑一边,然后取概率最大的就行了。

在每个beats_per_bar下,我们算一个最佳的隐变量路径

x 1 : K ∗ = a r g max ⁡ x 1 : K ( x 1 : K ∣ y 1 : K ) (5-1) \bold{x}_{1:K}^* = arg\max_{x_{1:K}}(\bold{x}_{1:K} | \bold{y}_{1:K}) \tag{5-1} x1:K=argx1:Kmax(x1:Ky1:K)(5-1)

解这个用viterbi算法就可以了。

最终的结果就是

B ∗ = { k : x k ∗ ∈ B } (5-2) B^* = \{k : \bold{x}_k^* \in B\} \tag{5-2} B={k:xkB}(5-2)

D ∗ = { k : x k ∗ ∈ D } (5-2) D^* = \{k : \bold{x}_k^* \in D\} \tag{5-2} D={k:xkD}(5-2)

确定了 B ∗ B^* B D ∗ D^* D之后,还会根据RNN的结果,把点修正到附近的概率峰值点上。

验证模型的时候,把误差在70ms以内的点都认为是正确的。madmom的效果还是很不错的。

参考资料

[1] madmom implementation
[2] Joint Beat and Downbeat Tracking with Recurrent Neural Networks
[3] An Efficient State-Space Model for Joint Tempo and Meter Tracking
[4] 百度百科-音乐节拍
[5] Bayesian Modelling of Temporal Structure in Musical Audio
[6] A Multi-model Approach to Beat Tracking Considering Heterogeneous Music Styles

你可能感兴趣的:(论文阅读,人工智能,MIR,beats,downbeats,HMM)