马尔可夫模型和隐马尔可夫_使用Perl的隐马尔可夫模型入门

马尔可夫模型和隐马尔可夫

使用Markov模型(以数学家Andrey Markov的名字命名)用于随机变化系统的预测。 马尔可夫的见解是,在这种情况下,仅可以从事件的最新发生来做出良好的预测,而忽略当前事件之前的任何发生。 该方法可以描述为无记忆或与历史无关的预测。

马尔可夫的第一个例子(1913年)预言了普希金的诗作《尤金·奥涅金》中的元音出现。 今天的挑战是要找到一个研究领域,其中马尔可夫模型不起作用。 这些模型用于研究热力学和统计力学。 生物信息学,酶活性和种群动态; 太阳辐照度和风力; 价格趋势; 语音识别和生成; 数据压缩和模式识别; 强化学习和手势识别。 这个清单不胜枚举

本文介绍了隐马尔可夫模型(HMM),它是原始模型的改进。 HMM和非常适合HMM的Viterbi算法的介绍在Perl中设置了完整的代码示例。

1.利用马尔可夫属性随机行走

想象一只狐狸正在觅食,目前在位置C(例如,在溪流旁的灌木丛中)觅食。 狐狸搜索路径上的先前位置是P1,P2,P3等。 问题是如何预测狐狸的下一个位置。

一种方法是使用整个搜索历史记录P1,P2,…,C来预测下一个位置。 例如,由于狐狸足够聪明,不会重复失败的搜索位置,因此在狐狸搜索中已经访问过的位置可能会成为下一个位置的可能性很小。 相比之下,附近但未到访的位置(例如,溪流露头)的概率较高。

一种更简单的方法是假设仅从当前位置C就能从整个搜索历史记录P1,P2,...,C准确预测狐狸的下一个位置N。如果是这样,则可以将狐狸的搜索建模为以下过程: Markov属性。 在此示例中,该属性表示狐狸下一个搜索位置的概率分布仅取决于狐狸的当前位置,而不取决于狐狸之前的位置。 因此,马尔可夫过程可以描述为具有马尔可夫特性的随机游动。

马尔可夫属性可以通过使用条件概率的表示来简洁地表示:P(A | B)是A给定B的概率。在fox示例中,马尔可夫属性支持等式:P( N | P1P2 ... , C )= P( N | C )其中CN分别是当前位置和下一个位置。

2. HMM和Viterbi算法

在fox示例中,通过假设,观察者可以看到过程中每个感兴趣的状态(在特定位置觅食):没有任何状态被隐藏。 在HMM改进中,状态是隐藏的,但是每个状态都有一个或多个关联的输出令牌(也称为观察值),这些令牌没有被隐藏。 目的是根据观察结果推断状态顺序。 一个典型的例子是骨灰盒问题

拾取的在此过程中属于隐藏状态,并且放置在传送带上的球形标签的副本是输出令牌。 机器人有一个食谱(尽管是秘密的)来捡起每个。 因此,这种拾取可以建模为具有隐藏状态的HMM马尔可夫过程。

从1960年代后期开始,维特比算法可用于从观察到的事件(在这种情况下,是传送带上的标签)推断出隐藏状态的可能顺序(在此示例中,是拾取的顺序)。 以维特比(Viterbi)的名义,推断出的序列称为维特比路径。

作为示例,请考虑以下这些输出令牌:

 B9 , B4 , B13 , B27 , B6 , B13 , B29 ## sequence of 7 observations 

基本的维特比路径可能是:


   
   
  B9     B4    B13     B27    B6     B13    B29   ## observations
  /      /       /      /       /      /       /
UrnD --> UrnA --> UrnF --> UrnA --> UrnG --> UrnF --> UrnN   ## hidden states

维特比算法从传送带上观察到的标签推断出机器人的隐藏状态转换(n拾取)。 当然,推论本质上是统计的。 有猜测,但是维特比算法应该使这具有教育意义。

3.详细的维特比算法

维特比算法使用六个输入来生成维特比路径:

  • 观察空间由可能的输出标记或观察(例如B1,​​B2,B3等)组成。
  • 顺序观察是观察标记的顺序(例如B9,B4,B13等)。
  • 状态空间由可能的隐藏状态(例如UrnA,UrnB等)组成。
  • 初始概率是特定代表机器人选择过程中开始状态的可能性。
  • 过渡概率是从一个过渡到另一个的可能性,包括返回本身。
  • 发射概率是观察到的球来自特定的可能性。

输出为:

  • 推断的隐藏状态序列(在此示例中为选定的urn序列),即维特比路径。

这些输入可以从示例中学习,而不是任意规定。 因此,下面的短代码示例中有两个部分。 第一部分从示例中模拟机器学习,第二部分使用从学习中获得的结果来推断HMM,该HMM是观察到的球标签序列的基础。

4. hmm程序

下面的hmm程序可从我的网站上获得 。 Unix类型的系统安装了Perl,但是此程序需要CPAN和其他Perl存储库中提供的Algorithm :: Viterbi软件包。 可选软件包Data :: Dumper可以用于完整查看Viterbi输入。 为此,请取消注释该程序的最后一行。

例子1. hmm程序


   
   
use strict ;
use warnings ;
use Algorithm :: Viterbi ;
use Data :: Dumper ; ## to view the inputs in full

use constant HowMany     => 64 _000 ; # 64K
use constant UrnCount   =>       5 ; # five urns
use constant TokenCount =>     64 ; # observations

my @urns   =   ( 'UrnA' , 'UrnB' , 'UrnC' , 'UrnD' , 'UrnE' ) ;

my %balls = ( 'UrnA' => [ 'B1' , 'B2' , 'B4' , 'B5' , 'B7' , 'B8' ] ,
              'UrnB' => [ 'B1' , 'B3' , 'B5' , 'B7' ] ,
              'UrnC' => [ 'B2' , 'B3' , 'B4' , 'B6' , 'B7' ] ,
              'UrnD' => [ 'B1' , 'B2' , 'B3' , 'B5' , 'B7' ] ,
              'UrnE' => [ 'B2' , 'B3' , 'B5' , 'B8' ] ) ;

my @data = ( ) ; # list of ball-urn pairs for training

sub get_ball_urn_pair { ## return a ball-urn pair or just a ball
    my $both = shift ;
    my $urn = $urns [ int ( rand ( UrnCount ) ) ] ;
    my $list = $balls { $urn } ;
    my $i = int ( rand ( scalar @$list ) ) ;
    return $both ? ( $list -> [ $i ] , $urn ) : $list -> [ $i ] ;
}

#### execute
## randomly pick an urn and a ball from it
srand ( time ( ) ) ;
for ( my $i = 0 ; $i < HowMany ; $i ++ ) {
    my @add = get_ball_urn_pair ( 1 ) ; # random pick
    push ( @data , \@add ) ;
}

## train the algorithm
my $viterbi = Algorithm :: Viterbi -> new ( ) ;
$viterbi -> train ( \@data ) ;

## generate observations
my @tokens = ( ) ; ## observed labels
for ( my $i = 0 ; $i < TokenCount ; $i ++ ) {
    my $ball = get_ball_urn_pair ( 0 ) ; # random pick
    push ( @tokens , $ball ) ;
}

## output Viterbi path
my $v_path = ( $viterbi -> forward_viterbi ( \@tokens ) ) [ 1 ] ; ## Viterbi path
print join ( '->' , @$v_path ) , " \n " ;

# print Dumper($viterbi);  ## uncomment to see the probabilities in full

hmm程序使用五个骨灰盒(UrnA至UrnE),但每个骨灰盒中的球不同:


   
   
my %balls = ( 'UrnA' => [ 'B1' , 'B2' , 'B4' , 'B5' , 'B7' , 'B8' ] ,
              'UrnB' => [ 'B1' , 'B3' , 'B5' , 'B7' ] ,
              'UrnC' => [ 'B2' , 'B3' , 'B4' , 'B6' , 'B7' ] ,
              'UrnD' => [ 'B1' , 'B2' , 'B3' , 'B5' , 'B7' ] ,
              'UrnE' => [ 'B2' , 'B3' , 'B5' , 'B8' ] ) ;

这些球以urn作为键并包含一个球列表作为该键的值存储在地图中。

hmm程序中的学习部分使用随机生成的测试数据,其中包括64,000个球-对,例如:

 B5 , UrnD ## evidence 

这对确认UrnD包含一个标记为B5的球,但其他对则确认一个B5球可能来自其他一些:

 B5 , UrnA ## more evidence 

这两个例子一起表明B5球可能来自UrnA或UrnD。 学习对不提供有关机器人的任何信息,除非它可以从特定的中拾取特定的球。

来自学习部分的测试数据存储在一个列表中,该列表传递给Algorithm :: Viterbi实例的train方法:


   
   
my $viterbi = Algorithm :: Viterbi -> new ( ) ; ## instantiate
$viterbi -> train ( \@data ) ;                 ## pass training data

接下来将澄清此培训电话的结果。

5.培训结果

训练方法在基础HMM中检测到五个可能的状态(ur),因为每个在随机生成的测试数据中至少发生一次。 以下是按检测顺序排列的状态:

 states => [ UrnA , UrnD , UrnE , UrnB , UrnC ] 

回想一下,维特比算法所需的三个概率是初始概率(对于初始urn); 过渡概率(机器人从当前的ur移动到下一个); 以及发射概率(观察到的标签识别出特定中的球的可能性)。 训练针对初始概率得出以下结果:


   
   
start => { UrnA => 0.199578125 ,
          UrnD => 0.201703125 ,
          UrnE => 0.198546875 ,
          UrnB => 0.200343750 ,
          UrnC => 0.199828125 }

该值总计为1,这使五个缸中的每个缸都有大约五分之一的机会成为机器人的初始选择。 UrnD比UrnB略有优势,但结果基本上是值得一提的。

以下是从测试得出的转换概率,以UrnC为例:


   
   
UrnC => { UrnA => 0.198230642762076 ,
         UrnE => 0.192885810970331 ,
         UrnB => 0.204414287942599 ,
         UrnD => 0.205128205128205 ,
         UrnC => 0.198373602314489 }

以UrnC作为当前状态,转换到任何urn(再次包括UrnC)的可能性约为20%。

发射概率很有趣,因为并非每个都有每个球。 例如,B6仅出现在UrnC中。 但是,根据训练示例,维特比算法不能排除其他some也可能包含此球。 训练的结果仅给出保守的20%可能性,即发出的B6标签源自UrnC中的球:

 B6 => { UrnC => 0.201110329189147 } 

相比之下,B7出现在四个骨灰盒中,并且训练数据包括这样的情况,因此存在B7起源于四个骨灰盒中每个的概率:


   
   
B7 => { UrnA => 0.160259923275660 ,
       UrnB => 0.244657619716113 ,
       UrnD => 0.197923929041754 ,
       UrnC => 0.201266713581985 }

根据训练数据,UrnB是B7球的最可能来源,而UrnA是此球的最不可能的来源。 (UrnA和UrnB都包含一个B7球。)

在回顾中,培训数据支持对三个关键事项的统计推断:

  • 机器人最初可能会拾取的。 在这种情况下,每个的采摘机会大致相同。
  • 给定拾取的骨灰盒,机器人接下来可能会拾取的骨灰盒,这又可能是相同的骨灰盒。 训练数据表明,所有骨灰间转换都同样可能。
  • 从中拾取特定球(例如B7)的。 在这里,可能性差异很大,因为并非每个都持有相同的球。

维特比算法使用三个输入概率,尤其是排放的最后一个概率,试图从一系列观察到的标记中确定从中拾取带有观察到的标记的球的序列。

6.观察

维特比算法的最终输入包括观察值,即在传送带上可见的标签。 hmm程序也会随机生成这些值:


   
   
my @tokens = ( ) ; ## observed labels
for ( my $i = 0 ; $i < TokenCount ; $i ++ ) {
    my $ball = get_ball_urn_pair ( 0 ) ; ## random pick
    push ( @tokens , $ball ) ;
}

有了观察之后,最后一步是将它们作为参数传递给forward_viterbi方法,该方法推断基础HMM并将其作为维特比路径返回:

 my $v_path = ( $viterbi -> forward_viterbi ( \@tokens ) ) [ 1 ] ; ## Viterbi path 

然后程序将结果输出为一系列状态转换:

 UrnB -> UrnE -> UrnE -> UrnB -> UrnC ->...-> UrnB -> UrnE -> UrnB -> UrnA 

在此示例运行中,只有UrnD无法出现在Viterbi路径中。 UrnD中的每个球都至少发生在另一个中,并且转换概率不支持UrnD作为目标。 因此,该算法可以呈现出从HMM中排除UrnD的合理状态序列。

forward_viterbi方法还返回两个概率,它们代表推断的维特比路径中的置信度。 为简单起见,此处仅输出Viterbi路径。

7.分而治之与动态编程

hmm程序运行很快,这提示了维特比算法如何工作的问题。 该算法构建一个网格,该网格是一个图,其节点位于垂直切片中:较高的切片代表较早的时间,而较低的切片代表较晚的时间。 图1是Wikipedia的一般示例,最短的从左到右路径用红色显示:

图1.格子

Trellis graph

维特比算法从顶部切片的一个节点开始,计算通过网格的最短路径。 这是维特比路径,其节点表示从观测值推断出的HMM状态。 该算法使用经典的动态编程技术来提高效率。 一个更简单(并且可能更熟悉)的示例阐明了该技术,突出了Perl中如此简单的记忆。

考虑使用递归fib函数作为实现来计算斐波那契数的分治法:


   
   
sub fib { ## divide and conquer: exponential time
    my ( $n ) = shift ;                   # read argument
    return $n if $n < 2 ;               # base case
    return fib ( $n - 1 ) + fib ( $n - 2 ) ; # recursive case
}

要计算诸如fib(40)的值,该函数将问题分为fib(39)fib(38)的子问题。 划分继续进行,直到子问题小到足以直接解决为止。 在这种情况下,计算fib(0)fib(1)的值

fib(40)的值为102,334,155,但是递归计算需要对fib函数进行331,160,281次调用,这强调了这种分而治之设计的指数时间复杂性。 困难在于,由于使用n-1和n-2作为参数的递归调用,一次又一次地重新计算了太多的值。 例如, fib(40)调用fib(39)fib(38) ; 但是fib(39)再次调用fib(38) 。 后两个调用中的每一个都导致对fib(37)的调用,依此类推。

需要更改设计。 所写的fib函数可以被记忆,从而将设计从分而治之变为动态编程。 两种设计都将问题分解为更易于管理的子问题,直到可以直接解决子问题为止。 两种方法的不同之处在于,动态编程会缓存子问题解决方案,以便可以根据需要重用它们。 简而言之,缓存取代了重新计算。 结果可以大大提高效率。

可以在Perl中轻松获得fib的备忘版本,无需对原始源代码进行任何更改:


   
   
memoize ( 'fib' ) ; # dynamic-programming: linear time
fib ( 40 ) ;         # 41 calls

对于记忆版本, fib(40)仅需要41次调用即可计算该值。 在幕后memoize调用提供了一个哈希表,作为fib函数的附加参数,该哈希表存储已计算的斐波那契值; 因此,每个所需的斐波那契值仅计算一次。 分而治之的指数时间复杂度降低为线性时间复杂度。

维特比算法在计算通过网格的最短路径(代表HMM的路径)时使用相同的缓存方法。 hmm程序高效,因为Viterbi算法是如此。

翻译自: https://opensource.com/article/20/3/markov-models-perl

马尔可夫模型和隐马尔可夫

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值