Python實現的HMM類

來源:互聯網
上載者:User

#!/usr/bin/env python

"""
HMM module

This module implements simple Hidden Markov Model class. It follows the description in
Chapter 6 of Jurafsky and Martin (2008) fairly closely, with one exception: in this
implementation, we assume that all states are initial states.

@author: Rob Malouf
@organization: Dept. of Linguistics, San Diego State University
@contact: rmalouf@mail.sdsu.edu
@version: 2
@since: 24-March-2008
"""

from copy import copy

class HMM(object):

    """
    Class for Hidden Markov Models
    An HMM is a weighted FSA which consists of:

        - a set of states (0...C{self.states})
        - an output alphabet (C{self.alphabet})
        - a table of state transition probabilities (C{self.A})
        - a table of symbol emission probabilities (C{self.B})          
        - a list of initial probabilies (C{self.initial})

        We assume that the HMM is complete, and that all states are both initial and final
        states. 
    """
    def __init__(self,states,alphabet,A,B,initial):
        """
        Create a new FSA object
        @param states: states
        @type states: C{list}
        @param alphabet: output alphabet
        @type finals: C{list}
        @param A: transition probabilities
        @type finals: C{list} of C{list}s
        @param B: emission probabilities
        @type finals: C{list} of C{list}s
        @param initial: initial state probabilities
        @type initial: C{list} of C{int}s

        @raise ValueError: the HMM is mis-specified somehow
        """

        # basic configuration
        self.states = states
        self.N = len(self.states)
        self.alphabet = alphabet

        # initial probabilities
        self.initial = initial
        if len(self.initial) != self.N:
            raise ValueError,'only found %d initial probabilities'%len(self.initial)
        if abs(sum(self.initial)-1.0) > 1e-8:
            raise ValueError,'improper initial probabilities'
        # transition probabilities
        self.A = A
        if len(self.A) != self.N:
            raise ValueError,'only found %d transition probability distributions'%len(self.A)
        for i in xrange(self.N):
            if len(self.A[i]) != self.N:
                raise ValueError,'only found %d transition probabilities for state %d'%(len(self.A[i]),i)
            if abs(sum(self.A[i])-1.0) > 1e-8:
                raise ValueError,'improper transition probabilities for state %d'%i

        # emission probabilities       
        self.B = B
        if len(self.B) != self.N:
            raise ValueError,'only found %d emission probability distributions'%len(self.B)
        for i in xrange(self.N):
            if i != self.initial:  # no output from initial state
                if len(self.B[i]) != len(self.alphabet):
                    raise ValueError,'only found %d emission probabilities for state %d'%(len(self.B[i]),i)
                if i != abs(sum(self.B[i])-1.0) > 1e-8:
                    raise ValueError,'improper emission probabilities for state %d'%i

    def allseqs(self,n):
        """Generate all possible state sequences of length n"""

        if n == 0:
            yield [ ]
        else:
            for first in self.states:
                for rest in self.allseqs(n-1):
                    yield [first] + rest

    def prob(self,S,O):
        """Calculate P(O,S|M)"""

        assert len(O) == len(S)
        assert len(O) > 0

        # convert state and output names to indices
        try:
            S = [self.states.index(t) for t in S]
        except ValueError:
            raise ValueError,'unknown state: %s'%t
        try:
            O = [self.alphabet.index(t) for t in O]
        except ValueError:
            raise ValueError,'unknown output: %s'%t

        prob = self.initial[S[0]] * self.B[S[0]][O[0]]
        for t in xrange(1,len(S)):
            prob *= self.A[S[t-1]][S[t]] * self.B[S[t]][O[t]]
        return prob

    def probseq(self,O):
        """Calculate P(O|M) the hard way"""

        # sum probs
        prob = sum(self.prob(S,O) for S in self.allseqs(len(O)))

        return prob

    def forward(self,O):
        """Calculate P(O|M) using the Forward Algorithm"""

        # convert output names to indices
        try:
            O = [self.alphabet.index(t) for t in O]
        except ValueError:
            raise ValueError,'unknown output: %s'%t

        # initialize trellis
        trellis = [ [self.initial[t]*self.B[t][O[0]] for t in xrange(self.N)] ]
        # fill in the rest of the trellis
        for t in xrange(1,len(O)):
            trellis.append([0.0] * self.N)
            for s2 in xrange(self.N):
                for s1 in xrange(self.N):
                    trellis[-1][s2] += self.A[s1][s2] * self.B[s2][O[t]] * trellis[-2][s1]
        # find total probability
        return sum(trellis[-1])

def main():

    ## set up Eisner's HMM

    states = [ 'HOT', 'COLD' ]
    output = [ 1, 2, 3]
    transition = [ [ 0.7, 0.3 ],
                   [ 0.4, 0.6 ] ]            
    emission = [ [ 0.2, 0.4, 0.4 ],
                 [ 0.5, 0.4, 0.1 ] ]
    initial = [ 0.8, 0.2 ]            
    m = HMM(states,output,transition,emission,initial)

    print 'P([HOT,HOT,COLD],[3,1,3]|M) =',
    print m.prob(['HOT','HOT','COLD'],[3,1,3])
    print 'P([3,1,3]|M) =',
    print m.probseq([3,1,3]),'(by the naive method)'
    print 'P([3,1,3]|M) =',
    print m.forward([3,1,3]),'(by the Forward Algorithm)'

main()

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.