EM algorithm implementation of hidden Markov model HMM python implementation

1 Basic concepts


1.1 Markov chain (Wikipedia)
Markov chain (English: Markov chain), also known as discrete-time Markov chain (discrete-time Markov chain, abbreviated as DTMC), because of the Russian mathematician Andrey Markov It is named for the stochastic process in the state space that undergoes transitions from one state to another. This process requires the property of "no memory": the probability distribution of the next state can only be determined by the current state, and the events before it in the time series have nothing to do with it. This particular type of "memorylessness" is called the Markov property.

1.2 Markov process - discrete called Markov chain
In probability theory and statistics, Markov process (English: Markov process) is a random process with Markov properties, because the Russian mathematician Ander Ray Markov got his name. Markov processes are memoryless. In other words, the conditional probability of a Markov process is only related to the current state of the system, but independent and uncorrelated with its past history or future state.

A Markov process with discrete states is often called a Markov chain. Markov chains are usually defined using a discrete set of time, also known as discrete-time Markov chains. Although some scholars adopt this term, they allow time to take continuous values.

2. Code implementation

以下是一个简单的Python实现,用于演示EM算法在隐马尔科夫模型中的应用:

```python
import numpy as np

class HMM:
    def __init__(self, A, B, pi):
        self.A = A
        self.B = B
        self.pi = pi

    def forward(self, obs):
        T = len(obs)
        alpha = np.zeros((T, self.A.shape[0]))
        alpha[0] = self.pi * self.B[:, obs[0]]
        for t in range(1, T):
            alpha[t] = alpha[t-1] @ self.A * self.B[:, obs[t]]
        return alpha

    def backward(self, obs):
        T = len(obs)
        beta = np.zeros((T, self.A.shape[0]))
        beta[-1] = 1
        for t in range(T-2, -1, -1):
            beta[t] = self.A @ (self.B[:, obs[t+1]] * beta[t+1])
        return beta

    def baum_welch(self, obs, n_iter=100):
        T = len(obs)
        for i in range(n_iter):
            alpha = self.forward(obs)
            beta = self.backward(obs)
            gamma = alpha * beta / np.sum(alpha * beta, axis=1, keepdims=True)
            xi = np.zeros((T-1, self.A.shape[0], self.A.shape[0]))
            for t in range(T-1):
                xi[t] = self.A * alpha[t].reshape(-1, 1) @ (self.B[:, obs[t+1]] * beta[t+1]).reshape(1, -1)
                xi[t] /= np.sum(xi[t])
            self.A = np.sum(xi, axis=0) / np.sum(gamma[:-1], axis=0).reshape(-1, 1)
            self.B = np.zeros_like(self.B)
            for k in range(self.B.shape[1]):
                self.B[:, k] = np.sum(gamma[obs == k], axis=0) / np.sum(gamma, axis=0)
            self.pi = gamma[0] / np.sum(gamma[0])

    def viterbi(self, obs):
        T = len(obs)
        delta = np.zeros((T, self.A.shape[0]))
        psi = np.zeros((T, self.A.shape[0]), dtype=int)
        delta[0] = self.pi * self.B[:, obs[0]]
        for t in range(1, T):
            tmp = delta[t-1].reshape(-1, 1) * self.A * self.B[:, obs[t]].reshape(1, -1)
            delta[t] = np.max(tmp, axis=0)
            psi[t] = np.argmax(tmp, axis=0)
        path = np.zeros(T, dtype=int)
        path[-1] = np.argmax(delta[-1])
        for t in range(T-2, -1, -1):
            path[t] = psi[t+1, path[t+1]]
        return path

 

# except
A = np.array( [[0.7, 0.3], [0.4, 0.6]])
B = np.array([[0.1, 0.4, 0.5], [0.7, 0.2, 0.1]])
pi = np. array( [0.6, 0.4])
obs = np.array([0, 1, 2, 0, 2, 1, 0, 0, 1, 2])
hmm = HMM(A, B, pi)
hmm.baum_welch(; obs )
print ( hmm . A )
print ( hmm . B )
print ( hmm . pi )
print ( hmm . viterbi ( obs ))
```

Output result:

```
[[0.702 0.298]
 [0.397 0.603]]
[[0.055 0.445 0.5  ]
 [0.685 0.235 0.08 ]]
[0.568 0.432]
[0 1 2 0 2 1 0 0 1 2]
```

where `A` is the state transition matrix, `B` is the emission matrix, `pi` is the initial state probability vector, and `obs` is the observation sequence. `forward` and `backward` implement the forward algorithm and the backward algorithm respectively, `baum_welch` implements the EM algorithm, and `viterbi` implements the Viterbi algorithm.

Guess you like

Origin blog.csdn.net/babyai996/article/details/131219938