253 lines
7.3 KiB
Markdown
253 lines
7.3 KiB
Markdown
# Hidden Markov Model (HMM) API
|
|
|
|
## Overview
|
|
|
|
The Hidden Markov Model (HMM) module provides efficient implementations of the Baum-Welch algorithm for parameter estimation and the Viterbi algorithm for state sequence decoding. This is particularly useful for regime detection in time series, speech recognition, biological sequence analysis, and financial market state identification.
|
|
|
|
## Class: `HMM`
|
|
|
|
```python
|
|
from optimizr import HMM
|
|
```
|
|
|
|
### Constructor
|
|
|
|
```python
|
|
HMM(n_states: int = 2)
|
|
```
|
|
|
|
**Parameters:**
|
|
- `n_states` (int): Number of hidden states. Must be at least 2. Default is 2.
|
|
|
|
**Raises:**
|
|
- `ValueError`: If `n_states < 2`
|
|
|
|
### Attributes
|
|
|
|
After fitting, the following attributes are populated:
|
|
|
|
- **`transition_matrix_`** (np.ndarray): State transition probabilities matrix of shape `(n_states, n_states)`. Entry `[i, j]` represents the probability of transitioning from state `i` to state `j`.
|
|
|
|
- **`emission_means_`** (np.ndarray): Mean parameters of Gaussian emissions for each state. Array of shape `(n_states,)`.
|
|
|
|
- **`emission_stds_`** (np.ndarray): Standard deviation parameters of Gaussian emissions for each state. Array of shape `(n_states,)`.
|
|
|
|
### Methods
|
|
|
|
#### `fit(X, n_iterations=100, tolerance=1e-6)`
|
|
|
|
Fit HMM parameters using the Baum-Welch (Expectation-Maximization) algorithm.
|
|
|
|
**Parameters:**
|
|
- `X` (np.ndarray): Time series observations as a 1D array.
|
|
- `n_iterations` (int, optional): Maximum number of EM iterations. Default is 100.
|
|
- `tolerance` (float, optional): Convergence threshold for log-likelihood change. Default is 1e-6.
|
|
|
|
**Returns:**
|
|
- `self` (HMM): The fitted model instance.
|
|
|
|
**Raises:**
|
|
- `ValueError`: If `X` is empty.
|
|
|
|
**Example:**
|
|
```python
|
|
import numpy as np
|
|
from optimizr import HMM
|
|
|
|
# Generate sample data with regime changes
|
|
returns = np.concatenate([
|
|
np.random.normal(0.01, 0.02, 500), # Bull market
|
|
np.random.normal(-0.01, 0.03, 500), # Bear market
|
|
])
|
|
|
|
# Create and fit HMM
|
|
hmm = HMM(n_states=2)
|
|
hmm.fit(returns, n_iterations=100)
|
|
|
|
print("Transition Matrix:")
|
|
print(hmm.transition_matrix_)
|
|
print("\nEmission Means:", hmm.emission_means_)
|
|
print("Emission Stds:", hmm.emission_stds_)
|
|
```
|
|
|
|
#### `predict(X)`
|
|
|
|
Predict the most likely state sequence using the Viterbi algorithm.
|
|
|
|
**Parameters:**
|
|
- `X` (np.ndarray): Time series observations as a 1D array.
|
|
|
|
**Returns:**
|
|
- `states` (np.ndarray): Array of integers representing the most likely state at each time step. Same length as `X`.
|
|
|
|
**Raises:**
|
|
- `ValueError`: If the model has not been fitted yet.
|
|
|
|
**Example:**
|
|
```python
|
|
# Decode most likely state sequence
|
|
states = hmm.predict(returns)
|
|
|
|
print(f"Detected states: {np.unique(states)}")
|
|
print(f"State distribution: {np.bincount(states)}")
|
|
|
|
# Visualize regime changes
|
|
import matplotlib.pyplot as plt
|
|
|
|
plt.figure(figsize=(12, 6))
|
|
plt.plot(returns, alpha=0.6, label='Returns')
|
|
plt.scatter(range(len(returns)), returns, c=states, cmap='viridis',
|
|
alpha=0.3, s=1, label='States')
|
|
plt.legend()
|
|
plt.title('HMM Regime Detection')
|
|
plt.show()
|
|
```
|
|
|
|
#### `score(X)`
|
|
|
|
Compute the log-likelihood of observations given the model.
|
|
|
|
**Parameters:**
|
|
- `X` (np.ndarray): Time series observations as a 1D array.
|
|
|
|
**Returns:**
|
|
- `log_likelihood` (float): The log probability of the observations given the model parameters.
|
|
|
|
**Raises:**
|
|
- `ValueError`: If the model has not been fitted yet.
|
|
|
|
**Example:**
|
|
```python
|
|
# Calculate model fit quality
|
|
ll = hmm.score(returns)
|
|
print(f"Log-likelihood: {ll:.2f}")
|
|
|
|
# Compare different numbers of states
|
|
for n in [2, 3, 4]:
|
|
hmm_temp = HMM(n_states=n)
|
|
hmm_temp.fit(returns)
|
|
ll = hmm_temp.score(returns)
|
|
print(f"States: {n}, Log-likelihood: {ll:.2f}")
|
|
```
|
|
|
|
## Complete Example
|
|
|
|
```python
|
|
import numpy as np
|
|
from optimizr import HMM
|
|
|
|
# Simulate financial returns with regime switching
|
|
np.random.seed(42)
|
|
|
|
# Create synthetic data with 3 regimes
|
|
n_samples = 1000
|
|
regime_1 = np.random.normal(0.02, 0.01, 300) # High return, low vol
|
|
regime_2 = np.random.normal(0.00, 0.02, 400) # Neutral return, medium vol
|
|
regime_3 = np.random.normal(-0.01, 0.03, 300) # Negative return, high vol
|
|
|
|
returns = np.concatenate([regime_1, regime_2, regime_3])
|
|
|
|
# Fit HMM
|
|
hmm = HMM(n_states=3)
|
|
hmm.fit(returns, n_iterations=100, tolerance=1e-6)
|
|
|
|
# Decode states
|
|
states = hmm.predict(returns)
|
|
|
|
# Analyze results
|
|
print("Transition Matrix:")
|
|
print(hmm.transition_matrix_)
|
|
print("\nState Statistics:")
|
|
for i in range(3):
|
|
mask = states == i
|
|
print(f"State {i}:")
|
|
print(f" Mean: {hmm.emission_means_[i]:.4f}")
|
|
print(f" Std: {hmm.emission_stds_[i]:.4f}")
|
|
print(f" Count: {np.sum(mask)} ({100*np.sum(mask)/len(states):.1f}%)")
|
|
|
|
# Calculate model quality
|
|
ll = hmm.score(returns)
|
|
print(f"\nLog-likelihood: {ll:.2f}")
|
|
```
|
|
|
|
## Performance Notes
|
|
|
|
- **Rust Backend**: When the Rust backend is available, HMM operations are 50-100x faster than pure Python implementations.
|
|
|
|
- **Python Fallback**: If the Rust backend is not available, a pure Python implementation using NumPy is automatically used. A warning will be issued.
|
|
|
|
- **Memory Efficiency**: The implementation uses log-space computations to prevent numerical underflow for long sequences.
|
|
|
|
- **Numerical Stability**: Forward-backward probabilities are normalized at each time step to maintain numerical stability.
|
|
|
|
## Algorithm Details
|
|
|
|
### Baum-Welch (EM) Algorithm
|
|
|
|
The Baum-Welch algorithm iteratively refines HMM parameters:
|
|
|
|
1. **E-step**: Compute expected state occupancies using the Forward-Backward algorithm
|
|
2. **M-step**: Update transition and emission parameters to maximize expected log-likelihood
|
|
3. **Convergence**: Repeat until log-likelihood change is below tolerance
|
|
|
|
### Viterbi Algorithm
|
|
|
|
The Viterbi algorithm finds the most likely state sequence:
|
|
|
|
1. **Initialization**: Set initial state probabilities
|
|
2. **Recursion**: For each time step, find the most likely path to each state
|
|
3. **Backtracking**: Trace back the optimal path from the final state
|
|
|
|
## Common Use Cases
|
|
|
|
### 1. Financial Regime Detection
|
|
|
|
```python
|
|
# Detect bull/bear markets in stock returns
|
|
hmm = HMM(n_states=2)
|
|
hmm.fit(stock_returns)
|
|
market_regimes = hmm.predict(stock_returns)
|
|
```
|
|
|
|
### 2. Volatility Clustering
|
|
|
|
```python
|
|
# Identify high/low volatility periods
|
|
abs_returns = np.abs(returns)
|
|
hmm = HMM(n_states=2)
|
|
hmm.fit(abs_returns)
|
|
volatility_regimes = hmm.predict(abs_returns)
|
|
```
|
|
|
|
### 3. Multi-State Analysis
|
|
|
|
```python
|
|
# Analyze complex market dynamics
|
|
hmm = HMM(n_states=4)
|
|
hmm.fit(returns)
|
|
states = hmm.predict(returns)
|
|
|
|
# States might represent: crash, bear, normal, bull
|
|
```
|
|
|
|
## Tips and Best Practices
|
|
|
|
1. **Choosing n_states**: Start with 2-3 states. Use cross-validation or information criteria (AIC/BIC) to select the optimal number.
|
|
|
|
2. **Data Preprocessing**: Standardize or normalize data before fitting, especially when combining multiple time series.
|
|
|
|
3. **Initialization**: The algorithm initializes parameters based on data quantiles. For better results, you can manually initialize parameters.
|
|
|
|
4. **Convergence**: If the algorithm doesn't converge, try:
|
|
- Increasing `n_iterations`
|
|
- Adjusting `tolerance`
|
|
- Preprocessing the data to remove outliers
|
|
|
|
5. **Overfitting**: Too many states can lead to overfitting. Use a validation set to assess generalization.
|
|
|
|
## See Also
|
|
|
|
- [MCMC API](mcmc.md) - For Bayesian parameter estimation
|
|
- [HMM Theory](theory/hmm.md) - Mathematical background and references
|
|
- [Examples](../examples/) - Complete working examples and tutorials
|