HMM Procedure
Gaussian Hidden Markov Model
Let
, denote a p-dimensional time series vector of random variables. Conditional on a latent variable
,
follows a normal distribution,
where and
are mean and covariance parameters whose values depend on the variable
. The variable
, the so-called state, follows the first-order Markov chain; that is,
where denotes the conditional probability. The range of
is a finite set,
. The transition probability from state i to state j is expressed as
The matrix
is called the transition probability matrix (TPM). The last element in the model is the initial state probability vector (ISPV),
, of the first state
:
Because the variable is unobservable and follows a Markov chain and because
follows a Gaussian distribution conditional on
, the model is called the Gaussian hidden Markov model (Gaussian HMM). The Gaussian HMM is sometimes described as
, where
, consisting of parameters that define the state-dependent distribution of observable variables.
Consider a bivariate Gaussian HMM that has the following parameter values:
The following statements simulate the bivariate vector time series from the previous model to provide test data for the HMM procedure:
%let pi1 = 0.5;
%let a11 = 0.001;
%let a22 = 0.001;
%let mu1_1 = 1;
%let mu1_2 = 1;
%let sigma1_11 = 1;
%let sigma1_21 = 0;
%let sigma1_22 = 1;
%let mu2_1 = -1;
%let mu2_2 = -1;
%let sigma2_11 = 1;
%let sigma2_21 = 0;
%let sigma2_22 = 1;
%let T = 10000;
%let seed = 1234;
data DGPone;
retain cd1_11 cd1_21 cd1_22 cd2_11 cd2_21 cd2_22;
do t = 1 to &T.;
if(t=1) then do;
/* initial probability distribution */
p = &pi1.;
/* Cholesky decomposition of sigma1 */
cd1_11 = sqrt(&sigma1_11.);
cd1_21 = &sigma1_21./sqrt(&sigma1_11.);
cd1_22 = sqrt(&sigma1_22.-&sigma1_21.*&sigma1_21./&sigma1_11.);
/* Cholesky decomposition of sigma2 */
cd2_11 = sqrt(&sigma2_11.);
cd2_21 = &sigma2_21./sqrt(&sigma2_11.);
cd2_22 = sqrt(&sigma2_22.-&sigma2_21.*&sigma2_21./&sigma2_11.);
end;
else do;
/* transition probability matrix */
if(lags=1) then p = &a11.;
else p = 1-&a22.;
end;
u = uniform(&seed.);
if(u<=p) then s=1;
else s = 2;
e1 = normal(&seed.); e2 = normal(&seed.);
if(s=1) then do;
/* (x,y) ~ N(mu1, Sigma1) at state 1 */
x = &mu1_1. + cd1_11*e1;
y = &mu1_2. + cd1_21*e1+cd1_22*e2;
end;
else do;
/* (x,y) ~ N(mu2, Sigma2) at state 2 */
x = &mu2_1. + cd2_11*e1;
y = &mu2_2. + cd2_21*e1+cd2_22*e2;
end;
output;
lags = s;
end;
run;
data One;
set DGPone;
keep t x y;
run;
In general, the data generating process is unknown. What can be seen is the data table One, but not the data table DGPone.
The following statements plot the simulated series x:
proc sgplot data=One;
series x=t y=x;
run;
Figure 1 shows the plot of x, whose points seem to fall randomly around 0.
Figure 1: Plot of Time Series x

The following statements plot the simulated series y:
proc sgplot data=One;
series x=t y=y;
run;
Figure 2 shows the plot of y, whose points also seem to fall randomly around 0.
Figure 2: Plot of Time Series y

Ignoring the sequential information, the following statements plot the observed points :
proc sgplot data=One;
scatter y=y x=x;
run;
As shown in Figure 3, it seems that the points come from two different cluster centers, and many of them lie between two cluster centers.
Figure 3: Unclassified Observations

It is very difficult for any cluster analysis methods that cannot use the sequential information to find the correct states for the points that lie between two cluster centers. The simplest cluster analysis method to classify the points, because of the symmetry shown in Figure 3, is to draw a line , classify all points above the line to one state, and classify all points below the line to another state.[6] The following statements implement this simplest cluster analysis method and calculate the accuracy, based on the data table
DGPone:
/* classify the data points by the line y = -x */
data Cluster;
set One;
if(y>-x) then state = 1;
else state = 2;
keep t state;
run;
data clusterCheck;
merge DGPone(in=a) Cluster(in=b);
by t;
if(s=state) then correct = 1;
else correct = 0;
if(a=b);
keep correct;
run;
data clusterAccuracy;
set clusterCheck;
retain count 0 correctCount 0;
count = count + 1;
correctCount = correctCount + correct;
if(count>&T.-0.5) then do;
accuracy = correctCount / count;
if(accuracy<0.5) then accuracy = 1 - accuracy;
output;
end;
keep accuracy;
run;
proc print data = clusterAccuracy noobs; run;
The accuracy of the cluster analysis, which is shown in Figure 4, is 92.02%. This implies that the cluster analysis cannot choose the correct states for about 800 data points!
Figure 4: Accuracy of Cluster Analysis
| accuracy |
|---|
| 0.9202 |
What is the big difference that you get by using the sequential information through HMMs? Before estimating any model, make a copy of the data in the library:
data mylib.One; set One; run;
The following statements estimate the two-state Gaussian HMM and then classify the data points through decoding; here decoding means to find the best possible state path for the observed data. The SORT=DESC(MU) option requests that the states be labeled in descending order of the mean parameter values; for more information about the label switching problem, see the section Label Switching Problem.
proc hmm data=mylib.One labelSwitch=(sort=desc(mu));
id time=t;
model x y / type=gaussian nstate=2;
optimize printLevel=3 printIterFreq=1;
decode out=mylib.oneDecode;
run;
The number of observations and model information are shown in Figure 5.
Figure 5: Number of Observations and Model Information
The default method, which is the maximum likelihood method, is applied; this is a nonlinear optimization problem. The initial parameter values and the objective function value at the start of the optimization are shown in Figure 6.
Figure 6: Initial Parameter Values and Objective Function Value
| Optimization Start | |
|---|---|
| Parameter | Estimate |
| TPM1_1 | 0.500000 |
| TPM1_2 | 0.500000 |
| TPM2_1 | 0.500000 |
| TPM2_2 | 0.500000 |
| MU1_1 | 0.642059 |
| MU1_2 | 0.513308 |
| MU2_1 | -1.609545 |
| MU2_2 | -1.329369 |
| SIGMA1_1_1 | 1.152038 |
| SIGMA1_2_1 | 0.320856 |
| SIGMA1_2_2 | 1.478814 |
| SIGMA2_1_1 | 0.525065 |
| SIGMA2_2_1 | -0.243910 |
| SIGMA2_2_2 | 0.892018 |
| Initial Value of Objective Function | -34510.54106 |
|---|
The details of the optimization algorithm are shown in Figure 7.
Figure 7: Optimization Algorithm Details
| Algorithm Settings | |
|---|---|
| Maximum Number of Iterations | 128 |
| Tolerance for Optimality Error | 1E-6 |
| Tolerance for Infeasibility | 1E-6 |
| Maximum Allowed Time (seconds) | 1.797693E308 |
| Maximum Magnitude of Objective Function Value | 1E20 |
| Random Seed | 1 |
| Solution Type | 1 |
| Multi-Start Globalization Scheme | 0 |
| Multi-Start Globalization Bound Range | 2 |
The iterations of the optimization process are shown in Figure 8.
Figure 8: Iterations of the Optimization Process
| Iteration History | ||||||
|---|---|---|---|---|---|---|
| Iteration | Objective Function | Optimality Error | Infeasibility | Function Calls | Gradient Calls | Hessian Calls |
| 1 | -34510.54106 | 610.24392 | 0.00000 | 2 | 1 | 0 |
| 2 | -29343.35393 | 350.54614 | 0.00000 | 11 | 10 | 1 |
| 3 | -28606.45004 | 78.05203 | 0.00000 | 19 | 18 | 2 |
| 4 | -28467.87680 | 1.42097 | 0.00000 | 28 | 27 | 3 |
| 5 | -28460.41282 | 0.34998 | 0.00000 | 37 | 36 | 4 |
| 6 | -28459.08728 | 0.10030 | 0.00000 | 46 | 45 | 5 |
| 7 | -28458.80039 | 0.02968 | 0.00000 | 55 | 54 | 6 |
| 8 | -28458.73576 | 0.00812 | 0.00000 | 64 | 63 | 7 |
| 9 | -28458.72552 | 0.00167 | 0.00000 | 73 | 72 | 8 |
| 10 | -28458.72484 | 0.00018 | 0.00000 | 82 | 81 | 9 |
| 11 | -28458.72483 | 0.00000 | 0.00000 | 91 | 90 | 10 |
The final parameter values and the objective function value at the end of the optimization are shown in Figure 9.
Figure 9: Final Parameter Values and Objective Function Value
| Optimization Results | |
|---|---|
| Parameter | Estimate |
| TPM1_1 | 0.002071 |
| TPM1_2 | 0.997929 |
| TPM2_1 | 0.999863 |
| TPM2_2 | 0.000137 |
| MU1_1 | 1.006466 |
| MU1_2 | 0.994184 |
| MU2_1 | -0.993036 |
| MU2_2 | -1.007869 |
| SIGMA1_1_1 | 1.011152 |
| SIGMA1_2_1 | 0.006387 |
| SIGMA1_2_2 | 0.988329 |
| SIGMA2_1_1 | 0.992333 |
| SIGMA2_2_1 | -0.005378 |
| SIGMA2_2_2 | 1.008722 |
| Final Value of Objective Function | -28458.72483 |
|---|
The estimates of the initial state probability vector (ISPV) are shown in Figure 10.
Figure 10: Estimates of Initial State Probability Vector
| Initial State Probabilities | |
|---|---|
| State | Estimation |
| 1 | 0.50048 |
| 2 | 0.49952 |
The estimates of the transition probability matrix (TPM) are shown in Figure 11.
Figure 11: Estimates of Transition Probability Matrix
| Estimated Transition Probability Matrix | ||
|---|---|---|
| State | 1 | 2 |
| 1 | 0.00207 | 0.99793 |
| 2 | 0.99986 | 0.00014 |
The estimates of the mean vector of the Gaussian distribution for each state are shown in Figure 12.
Figure 12: Estimate of Mean Vector for Each State
| Mu | ||
|---|---|---|
| State | x | y |
| 1 | 1.00647 | 0.99418 |
| 2 | -0.99304 | -1.00787 |
The estimates of the covariance matrix of the Gaussian distribution for each state are shown in Figure 13.
Figure 13: Estimate of Covariance Matrix for Each State
| Sigma | |||
|---|---|---|---|
| State | Variable | x | y |
| 1 | x | 1.01115 | 0.00639 |
| y | 0.00639 | 0.98833 | |
| 2 | x | 0.99233 | -0.00538 |
| y | -0.00538 | 1.00872 | |
The estimates of all parameters are shown in Figure 14, which displays columns for the parameter name, estimate value, standard error, t-value, and p-value.
Figure 14: Parameter Estimates
| Parameter Estimates | ||||
|---|---|---|---|---|
| Parameter | Estimate | Standard Error | t Value | Pr > |t| |
| TPM1_1 | 0.002071 | 0.000711 | 2.91 | 0.0036 |
| TPM1_2 | 0.997929 | 0.000711 | 1403.15 | <.0001 |
| TPM2_1 | 0.999863 | 0.000342 | 2926.28 | <.0001 |
| TPM2_2 | 0.000137 | 0.000342 | 0.40 | 0.6888 |
| MU1_1 | 1.006466 | 0.014228 | 70.74 | <.0001 |
| MU1_2 | 0.994184 | 0.014054 | 70.74 | <.0001 |
| MU2_1 | -0.993036 | 0.014096 | -70.45 | <.0001 |
| MU2_2 | -1.007869 | 0.014217 | -70.89 | <.0001 |
| SIGMA1_1_1 | 1.011152 | 0.020252 | 49.93 | <.0001 |
| SIGMA1_2_1 | 0.006387 | 0.014139 | 0.45 | 0.6514 |
| SIGMA1_2_2 | 0.988329 | 0.019757 | 50.02 | <.0001 |
| SIGMA2_1_1 | 0.992333 | 0.019859 | 49.97 | <.0001 |
| SIGMA2_2_1 | -0.005378 | 0.014159 | -0.38 | 0.7041 |
| SIGMA2_2_2 | 1.008722 | 0.020194 | 49.95 | <.0001 |
The log likelihood and information criteria are shown in Figure 15.
Figure 15: Log Likelihood and Information Criteria
| Fit Statistics | |
|---|---|
| Log Likelihood | -28458.72483 |
| AIC | 56941.449663 |
| AICC | 56941.480904 |
| BIC | 57027.973748 |
| HQC | 56970.737507 |
The accuracy of classification through the Gaussian HMM is calculated by the following statements:
data gHmmCheck;
merge DGPone(in=a) mylib.oneDecode(in=b);
by t;
if(s=state) then do; correct=1; ds = s; end;
else do; correct=0; ds = -s; end;
if(a=b);
keep t x y s correct ds;
run;
%let qFlipState = -1;
data gHmmAccuracy;
set gHmmCheck;
retain count 0 correctCount 0;
count = count + 1;
correctCount = correctCount + correct;
if(count>&T.-0.5) then do;
accuracy = correctCount / count;
if(accuracy<0.5) then call symputx("qFlipState",1,'G');
else call symputx("qFlipState",0,'G');
if(accuracy<0.5) then accuracy = 1 - accuracy;
output;
end;
keep accuracy;
run;
data gHmmCheck;
set gHmmCheck;
if(&qFlipState.=1) then do;
ds = -ds;
correct = 1 - correct;
end;
run;
proc print data = gHmmAccuracy noobs; run;
The accuracy of the Gaussian HMM, which is shown in Figure 16, is 99.99%. This means that the Gaussian HMM successfully takes advantage of the sequential information: only 1 data point cannot be correctly classified, compared to about 800 points that cannot be correctly classified by the cluster analysis method without the sequential information.
Figure 16: Accuracy of Decoding by the Gaussian HMM
| accuracy |
|---|
| 0.9999 |
To show how well the data are classified, the previous statements label any incorrectly classified data points with negative state values. In this example, only 1 point is not correctly classified. You plot the classified data points by using the following statements. The plot is shown in Figure 17.
proc sgplot data=gHmmCheck;
scatter y=y x=x / group=ds;
run;
Figure 17: Classified Data Points by Gaussian HMM

[6] In theory, no cluster analysis method, without using the sequential information, could do a better job on this data table than this simplest cluster analysis method.