diff options
| author | ziejd2 | 2017-09-28 15:04:40 -0500 |
|---|---|---|
| committer | ziejd2 | 2017-09-28 15:04:40 -0500 |
| commit | 8070dc963753142bb86c4ed698d91fd623ed28e7 (patch) | |
| tree | d0f6dd8fc46a49b819aa55c1a90faa14d8448883 /sourcecodes/bnt-master/HMM | |
| parent | 7cc31810d53176e805532b2789955f4eedbce6bb (diff) | |
| download | BNW-8070dc963753142bb86c4ed698d91fd623ed28e7.tar.gz | |
BNW using Octave instead of Matlab.
This version of BNW should perform the same as the original version. The only difference is that it uses Octave instead of Matlab when running BayesNet Toolbox during parameter learning. I am calling this BNW_1.02. It can be accessed at: compbio.uthsc.edu/BNW_1.02
Diffstat (limited to 'sourcecodes/bnt-master/HMM')
31 files changed, 1318 insertions, 0 deletions
diff --git a/sourcecodes/bnt-master/HMM/CVS/Entries b/sourcecodes/bnt-master/HMM/CVS/Entries new file mode 100644 index 00000000..b9105b50 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/CVS/Entries @@ -0,0 +1,29 @@ +/README.txt/1.1.1.1/Thu Jun 9 01:22:48 2005// +/dhmm_em.m/1.1.1.1/Thu Jun 9 01:25:04 2005// +/dhmm_em_demo.m/1.1.1.1/Sun May 4 22:01:12 2003// +/dhmm_em_online.m/1.1.1.1/Sun May 4 22:02:58 2003// +/dhmm_em_online_demo.m/1.1.1.1/Sun May 4 22:04:10 2003// +/dhmm_logprob.m/1.1.1.1/Sun May 4 22:01:34 2003// +/dhmm_logprob_brute_force.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dhmm_logprob_path.m/1.1.1.1/Wed May 29 15:59:56 2002// +/dhmm_sample.m/1.1.1.1/Mon May 31 22:19:50 2004// +/dhmm_sample_endstate.m/1.1.1.1/Sun May 4 22:00:34 2003// +/fixed_lag_smoother.m/1.1.1.1/Wed Jan 22 17:56:04 2003// +/fixed_lag_smoother_demo.m/1.1.1.1/Thu Jun 9 01:27:20 2005// +/fwdback.m/1.1.1.1/Thu Jun 9 01:17:50 2005// +/gausshmm_train_observed.m/1.1.1.1/Thu Feb 12 23:08:22 2004// +/mc_sample.m/1.1.1.1/Mon May 24 22:26:34 2004// +/mc_sample_endstate.m/1.1.1.1/Wed Jan 22 20:32:28 2003// +/mdp_sample.m/1.1.1.1/Wed May 29 15:59:56 2002// +/mhmmParzen_train_observed.m/1.1.1.1/Sat Feb 14 02:06:30 2004// +/mhmm_em.m/1.1.1.1/Sun Feb 8 04:52:42 2004// +/mhmm_em_demo.m/1.1.1.1/Tue May 13 16:11:22 2003// +/mhmm_logprob.m/1.1.1.1/Sun May 4 22:11:54 2003// +/mhmm_sample.m/1.1.1.1/Wed May 26 00:32:28 2004// +/mk_leftright_transmat.m/1.1.1.1/Wed May 29 15:59:58 2002// +/mk_rightleft_transmat.m/1.1.1.1/Fri Nov 22 21:45:52 2002// +/pomdp_sample.m/1.1.1.1/Sun May 4 21:58:20 2003// +/testHMM.m/1.1.1.1/Thu Jun 9 01:25:50 2005// +/transmat_train_observed.m/1.1.1.1/Sun Aug 29 12:41:52 2004// +/viterbi_path.m/1.1.1.1/Sat Oct 23 01:18:22 2004// +D diff --git a/sourcecodes/bnt-master/HMM/CVS/Repository b/sourcecodes/bnt-master/HMM/CVS/Repository new file mode 100644 index 00000000..5f13f5c3 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/CVS/Repository @@ -0,0 +1 @@ +FullBNT/HMM diff --git a/sourcecodes/bnt-master/HMM/CVS/Root b/sourcecodes/bnt-master/HMM/CVS/Root new file mode 100644 index 00000000..f3bd14a6 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/CVS/Root @@ -0,0 +1 @@ +:ext:nsaunier@bnt.cvs.sourceforge.net:/cvsroot/bnt diff --git a/sourcecodes/bnt-master/HMM/README.txt b/sourcecodes/bnt-master/HMM/README.txt new file mode 100644 index 00000000..2e6ab78f --- /dev/null +++ b/sourcecodes/bnt-master/HMM/README.txt @@ -0,0 +1,23 @@ +Hidden Markov Model (HMM) Toolbox written by Kevin Murphy (1998). +See http://www.ai.mit.edu/~murphyk/Software/hmm.html for details. + +Models +------ + +dhmm = HMM with discrete output +mhmm = HMM with mixture of Gaussians output; + Use mhmm with M=1 components to simulate an HMM with a single Gaussian output. + +Demos +----- + +mhmm_em_demo +dhmm_em_demo +dhmm_em_online_demo +fixed_lag_smoother_demo + +References +----------- + +See "A tutorial on Hidden Markov Models and selected applications in speech recognition", + L. Rabiner, 1989, Proc. IEEE 77(2):257--286. diff --git a/sourcecodes/bnt-master/HMM/dhmm_em.m b/sourcecodes/bnt-master/HMM/dhmm_em.m new file mode 100644 index 00000000..34ac1c6d --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_em.m @@ -0,0 +1,124 @@ +function [LL, prior, transmat, obsmat, nrIterations] = ... + dhmm_em(data, prior, transmat, obsmat, varargin) +% LEARN_DHMM Find the ML/MAP parameters of an HMM with discrete outputs using EM. +% [ll_trace, prior, transmat, obsmat, iterNr] = learn_dhmm(data, prior0, transmat0, obsmat0, ...) +% +% Notation: Q(t) = hidden state, Y(t) = observation +% +% INPUTS: +% data{ex} or data(ex,:) if all sequences have the same length +% prior(i) +% transmat(i,j) +% obsmat(i,o) +% +% Optional parameters may be passed as 'param_name', param_value pairs. +% Parameter names are shown below; default values in [] - if none, argument is mandatory. +% +% 'max_iter' - max number of EM iterations [10] +% 'thresh' - convergence threshold [1e-4] +% 'verbose' - if 1, print out loglik at every iteration [1] +% 'obs_prior_weight' - weight to apply to uniform dirichlet prior on observation matrix [0] +% +% To clamp some of the parameters, so learning does not change them: +% 'adj_prior' - if 0, do not change prior [1] +% 'adj_trans' - if 0, do not change transmat [1] +% 'adj_obs' - if 0, do not change obsmat [1] +% +% Modified by Herbert Jaeger so xi are not computed individually +% but only their sum (over time) as xi_summed; this is the only way how they are used +% and it saves a lot of memory. + +[max_iter, thresh, verbose, obs_prior_weight, adj_prior, adj_trans, adj_obs] = ... + process_options(varargin, 'max_iter', 10, 'thresh', 1e-4, 'verbose', 1, ... + 'obs_prior_weight', 0, 'adj_prior', 1, 'adj_trans', 1, 'adj_obs', 1); + +previous_loglik = -inf; +loglik = 0; +converged = 0; +num_iter = 1; +LL = []; + +if ~iscell(data) + data = num2cell(data, 2); % each row gets its own cell +end + +while (num_iter <= max_iter) & ~converged + % E step + [loglik, exp_num_trans, exp_num_visits1, exp_num_emit] = ... + compute_ess_dhmm(prior, transmat, obsmat, data, obs_prior_weight); + + % M step + if adj_prior + prior = normalise(exp_num_visits1); + end + if adj_trans & ~isempty(exp_num_trans) + transmat = mk_stochastic(exp_num_trans); + end + if adj_obs + obsmat = mk_stochastic(exp_num_emit); + end + + if verbose, fprintf(1, 'iteration %d, loglik = %f\n', num_iter, loglik); end + num_iter = num_iter + 1; + converged = em_converged(loglik, previous_loglik, thresh); + previous_loglik = loglik; + LL = [LL loglik]; +end +nrIterations = num_iter - 1; + +%%%%%%%%%%%%%%%%%%%%%%% + +function [loglik, exp_num_trans, exp_num_visits1, exp_num_emit, exp_num_visitsT] = ... + compute_ess_dhmm(startprob, transmat, obsmat, data, dirichlet) +% COMPUTE_ESS_DHMM Compute the Expected Sufficient Statistics for an HMM with discrete outputs +% function [loglik, exp_num_trans, exp_num_visits1, exp_num_emit, exp_num_visitsT] = ... +% compute_ess_dhmm(startprob, transmat, obsmat, data, dirichlet) +% +% INPUTS: +% startprob(i) +% transmat(i,j) +% obsmat(i,o) +% data{seq}(t) +% dirichlet - weighting term for uniform dirichlet prior on expected emissions +% +% OUTPUTS: +% exp_num_trans(i,j) = sum_l sum_{t=2}^T Pr(X(t-1) = i, X(t) = j| Obs(l)) +% exp_num_visits1(i) = sum_l Pr(X(1)=i | Obs(l)) +% exp_num_visitsT(i) = sum_l Pr(X(T)=i | Obs(l)) +% exp_num_emit(i,o) = sum_l sum_{t=1}^T Pr(X(t) = i, O(t)=o| Obs(l)) +% where Obs(l) = O_1 .. O_T for sequence l. + +numex = length(data); +[S O] = size(obsmat); +exp_num_trans = zeros(S,S); +exp_num_visits1 = zeros(S,1); +exp_num_visitsT = zeros(S,1); +exp_num_emit = dirichlet*ones(S,O); +loglik = 0; + +for ex=1:numex + obs = data{ex}; + T = length(obs); + %obslik = eval_pdf_cond_multinomial(obs, obsmat); + obslik = multinomial_prob(obs, obsmat); + [alpha, beta, gamma, current_ll, xi_summed] = fwdback(startprob, transmat, obslik); + + loglik = loglik + current_ll; + exp_num_trans = exp_num_trans + xi_summed; + exp_num_visits1 = exp_num_visits1 + gamma(:,1); + exp_num_visitsT = exp_num_visitsT + gamma(:,T); + % loop over whichever is shorter + if T < O + for t=1:T + o = obs(t); + exp_num_emit(:,o) = exp_num_emit(:,o) + gamma(:,t); + end + else + for o=1:O + ndx = find(obs==o); + if ~isempty(ndx) + exp_num_emit(:,o) = exp_num_emit(:,o) + sum(gamma(:, ndx), 2); + end + end + end +end diff --git a/sourcecodes/bnt-master/HMM/dhmm_em_demo.m b/sourcecodes/bnt-master/HMM/dhmm_em_demo.m new file mode 100644 index 00000000..c6ffa1dd --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_em_demo.m @@ -0,0 +1,25 @@ +O = 3; +Q = 2; + +% "true" parameters +prior0 = normalise(rand(Q,1)); +transmat0 = mk_stochastic(rand(Q,Q)); +obsmat0 = mk_stochastic(rand(Q,O)); + +% training data +T = 5; +nex = 10; +data = dhmm_sample(prior0, transmat0, obsmat0, T, nex); + +% initial guess of parameters +prior1 = normalise(rand(Q,1)); +transmat1 = mk_stochastic(rand(Q,Q)); +obsmat1 = mk_stochastic(rand(Q,O)); + +% improve guess of parameters using EM +[LL, prior2, transmat2, obsmat2] = dhmm_em(data, prior1, transmat1, obsmat1, 'max_iter', 5); +LL + +% use model to compute log likelihood +loglik = dhmm_logprob(data, prior2, transmat2, obsmat2) +% log lik is slightly different than LL(end), since it is computed after the final M step diff --git a/sourcecodes/bnt-master/HMM/dhmm_em_online.m b/sourcecodes/bnt-master/HMM/dhmm_em_online.m new file mode 100644 index 00000000..605a1f5b --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_em_online.m @@ -0,0 +1,80 @@ +function [transmat, obsmat, exp_num_trans, exp_num_emit, gamma, ll] = dhmm_em_online(... + prior, transmat, obsmat, exp_num_trans, exp_num_emit, decay, data, ... + act, adj_trans, adj_obs, dirichlet, filter_only) +% ONLINE_EM Adjust the parameters using a weighted combination of the old and new expected statistics +% +% [transmat, obsmat, exp_num_trans, exp_num_emit, gamma, ll] = online_em(... +% prior, transmat, obsmat, exp_num_trans, exp_num_emit, decay, data, act, ... +% adj_trans, adj_obs, dirichlet, filter_only) +% +% 0 < decay < 1, with smaller values meaning the past is forgotten more quickly. +% (We need to decay the old ess, since they were based on out-of-date parameters.) +% The other params are as in learn_hmm. +% We do a single forwards-backwards pass on the provided data, initializing with the specified prior. +% (If filter_only = 1, we only do a forwards pass.) + +if ~exist('act'), act = []; end +if ~exist('adj_trans'), adj_trans = 1; end +if ~exist('adj_obs'), adj_obs = 1; end +if ~exist('dirichlet'), dirichlet = 0; end +if ~exist('filter_only'), filter_only = 0; end + +% E step +olikseq = multinomial_prob(data, obsmat); +if isempty(act) + [alpha, beta, gamma, ll, xi] = fwdback(prior, transmat, olikseq, 'fwd_only', filter_only); +else + [alpha, beta, gamma, ll, xi] = fwdback(prior, transmat, olikseq, 'fwd_only', filter_only, ... + 'act', act); +end + +% Increment ESS +[S O] = size(obsmat); +if adj_obs + exp_num_emit = decay*exp_num_emit + dirichlet*ones(S,O); + T = length(data); + if T < O + for t=1:T + o = data(t); + exp_num_emit(:,o) = exp_num_emit(:,o) + gamma(:,t); + end + else + for o=1:O + ndx = find(data==o); + if ~isempty(ndx) + exp_num_emit(:,o) = exp_num_emit(:,o) + sum(gamma(:, ndx), 2); + end + end + end +end + +if adj_trans & (T > 1) + if isempty(act) + exp_num_trans = decay*exp_num_trans + sum(xi,3); + else + % act(2) determines Q(2), xi(:,:,1) holds P(Q(1), Q(2)) + A = length(transmat); + for a=1:A + ndx = find(act(2:end)==a); + if ~isempty(ndx) + exp_num_trans{a} = decay*exp_num_trans{a} + sum(xi(:,:,ndx), 3); + end + end + end +end + + +% M step + +if adj_obs + obsmat = mk_stochastic(exp_num_emit); +end +if adj_trans & (T>1) + if isempty(act) + transmat = mk_stochastic(exp_num_trans); + else + for a=1:A + transmat{a} = mk_stochastic(exp_num_trans{a}); + end + end +end diff --git a/sourcecodes/bnt-master/HMM/dhmm_em_online_demo.m b/sourcecodes/bnt-master/HMM/dhmm_em_online_demo.m new file mode 100644 index 00000000..579d89d5 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_em_online_demo.m @@ -0,0 +1,93 @@ +% Example of online EM applied to a simple POMDP with fixed action seq + +clear all + +% Create a really easy model to learn +rand('state', 1); +O = 2; +S = 2; +A = 2; +prior0 = [1 0]'; +transmat0 = cell(1,A); +transmat0{1} = [0.9 0.1; 0.1 0.9]; % long runs of 1s and 2s +transmat0{2} = [0.1 0.9; 0.9 0.1]; % short runs +obsmat0 = eye(2); + +%prior0 = normalise(rand(S,1)); +%transmat0 = mk_stochastic(rand(S,S)); +%obsmat0 = mk_stochastic(rand(S,O)); + +T = 10; +act = [1*ones(1,25) 2*ones(1,25) 1*ones(1,25) 2*ones(1,25)]; +data = pomdp_sample(prior0, transmat0, obsmat0, act); +%data = sample_dhmm(prior0, transmat0, obsmat0, T, 1); + +% Initial guess of params +rand('state', 2); % different seed! +transmat1 = cell(1,A); +for a=1:A + transmat1{a} = mk_stochastic(rand(S,S)); +end +obsmat1 = mk_stochastic(rand(S,O)); +prior1 = prior0; % so it labels states the same way + +% Uniformative Dirichlet prior (expected sufficient statistics / pseudo counts) +e = 0.001; +ess_trans = cell(1,A); +for a=1:A + ess_trans{a} = repmat(e, S, S); +end +ess_emit = repmat(e, S, O); + +% Params +w = 2; +decay_sched = [0.1:0.1:0.9]; + +% Initialize +LL1 = zeros(1,T); +t = 1; +y = data(t); +data_win = y; +act_win = [1]; % arbitrary initial value +[prior1, LL1(1)] = normalise(prior1 .* obsmat1(:,y)); + +% Iterate +for t=2:T + y = data(t); + a = act(t); + if t <= w + data_win = [data_win y]; + act_win = [act_win a]; + else + data_win = [data_win(2:end) y]; + act_win = [act_win(2:end) a]; + prior1 = gamma(:, 2); + end + d = decay_sched(min(t, length(decay_sched))); + [transmat1, obsmat1, ess_trans, ess_emit, gamma, ll] = dhmm_em_online(... + prior1, transmat1, obsmat1, ess_trans, ess_emit, d, data_win, act_win); + bel = gamma(:, end); + LL1(t) = ll/length(data_win); + %fprintf('t=%d, ll=%f\n', t, ll); +end + +LL1(1) = LL1(2); % since initial likelihood is for 1 slice +plot(1:T, LL1, 'rx-'); + + +% compare with offline learning + +if 0 +rand('state', 2); % same seed as online learner +transmat2 = cell(1,A); +for a=1:A + transmat2{a} = mk_stochastic(rand(S,S)); +end +obsmat2 = mk_stochastic(rand(S,O)); +prior2 = prior0; +[LL2, prior2, transmat2, obsmat2] = dhmm_em(data, prior2, transmat2, obsmat2, .... + 'max_iter', 10, 'thresh', 1e-3, 'verbose', 1, 'act', act); + +LL2 = LL2 / T + +end diff --git a/sourcecodes/bnt-master/HMM/dhmm_logprob.m b/sourcecodes/bnt-master/HMM/dhmm_logprob.m new file mode 100644 index 00000000..f8afe835 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_logprob.m @@ -0,0 +1,22 @@ +function [loglik, errors] = dhmm_logprob(data, prior, transmat, obsmat) +% LOG_LIK_DHMM Compute the log-likelihood of a dataset using a discrete HMM +% [loglik, errors] = log_lik_dhmm(data, prior, transmat, obsmat) +% +% data{m} or data(m,:) is the m'th sequence +% errors is a list of the cases which received a loglik of -infinity + +if ~iscell(data) + data = num2cell(data, 2); +end +ncases = length(data); + +loglik = 0; +errors = []; +for m=1:ncases + obslik = multinomial_prob(data{m}, obsmat); + [alpha, beta, gamma, ll] = fwdback(prior, transmat, obslik, 'fwd_only', 1); + if ll==-inf + errors = [errors m]; + end + loglik = loglik + ll; +end diff --git a/sourcecodes/bnt-master/HMM/dhmm_logprob_brute_force.m b/sourcecodes/bnt-master/HMM/dhmm_logprob_brute_force.m new file mode 100644 index 00000000..abffd0c9 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_logprob_brute_force.m @@ -0,0 +1,21 @@ +function logp = enumerate_HMM_loglik(prior, transmat, obsmat) +% ENUMERATE_HMM_LOGLIK Compute the log likelihood of a sequence by exhaustive (O(Q^T)) enumeration. +% logp = enumerate_HMM_loglik(prior, transmat, obsmat) +% +% Inputs: +% prior(i) = Pr(Q(1) = i) +% transmat(i,j) = Pr(Q(t+1)=j | Q(t)=i) +% obsmat(i,t) = Pr(y(t) | Q(t)=i) + +Q = length(prior); +T = size(obsmat, 2); +sizes = repmat(Q, 1, T); + +psum = 0; +for i=1:Q^T + qs = ind2subv(sizes, i); % make the state sequence + psum = psum + prob_path(prior, transmat, obsmat, qs); +end +logp = log(psum) + + diff --git a/sourcecodes/bnt-master/HMM/dhmm_logprob_path.m b/sourcecodes/bnt-master/HMM/dhmm_logprob_path.m new file mode 100644 index 00000000..11a1e3af --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_logprob_path.m @@ -0,0 +1,15 @@ +function [ll, p] = prob_path(prior, transmat, obsmat, qs) +% PROB_PATH Compute the prob. of a specific path (state sequence) through an HMM. +% [ll, p] = prob_path(prior, transmat, obsmat, states) +% +% ll = log prob path +% p(t) = Pr(O(t)) * Pr(Q(t) -> Q(t+1)) for 1<=t<T, p(T) = Pr(O(T)) + +T = size(obsmat, 2); +p = zeros(1,T); +p(1) = prior(qs(1)) * obsmat(qs(1),1); +for t=2:T + p(t) = transmat(qs(t-1), qs(t)) * obsmat(qs(t),t); +end + +ll = sum(log(p)); diff --git a/sourcecodes/bnt-master/HMM/dhmm_sample.m b/sourcecodes/bnt-master/HMM/dhmm_sample.m new file mode 100644 index 00000000..5122c81b --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_sample.m @@ -0,0 +1,10 @@ +function [obs, hidden] = dhmm_sample(initial_prob, transmat, obsmat, numex, len) +% SAMPLE_DHMM Generate random sequences from a Hidden Markov Model with discrete outputs. +% +% [obs, hidden] = sample_dhmm(initial_prob, transmat, obsmat, numex, len) +% Each row of obs is an observation sequence of length len. + +hidden = mc_sample(initial_prob, transmat, len, numex); +obs = multinomial_sample(hidden, obsmat); + + diff --git a/sourcecodes/bnt-master/HMM/dhmm_sample_endstate.m b/sourcecodes/bnt-master/HMM/dhmm_sample_endstate.m new file mode 100644 index 00000000..1972ebd2 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/dhmm_sample_endstate.m @@ -0,0 +1,19 @@ +function [obs, hidden] = dhmm_sample_endstate(startprob, transmat, obsmat, endprob, numex) +% SAMPLE_DHMM Generate random sequences from an HMM with discrete outputs. +% function [obs, hidden] = sample_dhmm_endstate(startprob, transmat, obsmat, endprob, numex) +% +% We sample until we have have entered the end state +% obs{m} and hidden{m} are the m'th sequence + +hidden = cell(1,numex); +obs = cell(1,numex); + +for m=1:numex + hidden{m} = mc_sample_endstate(startprob, transmat, endprob); + T = length(hidden{m}); + obs{m} = zeros(1,T); + for t=1:T + h = hidden{m}(t); + obs{m}(t) = sample_discrete(obsmat(h,:)); + end +end diff --git a/sourcecodes/bnt-master/HMM/fixed_lag_smoother.m b/sourcecodes/bnt-master/HMM/fixed_lag_smoother.m new file mode 100644 index 00000000..9e19df98 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/fixed_lag_smoother.m @@ -0,0 +1,65 @@ +function [alpha, obslik, gamma, xi] = fixed_lag_smoother(d, alpha, obslik, obsvec, transmat, act) +% FIXED_LAG_SMOOTHER Computed smoothed posterior estimates within a window given previous filtered window. +% [alpha, obslik, gamma, xi] = fixed_lag_smoother(d, alpha, obslik, obsvec, transmat, act) +% +% d >= 2 is the desired window width. +% Actually, we use d=min(d, t0), where t0 is the current time. +% +% alpha(:, t0-d:t0-1) - length d window, excluding t0 (Columns indexed 1..d) +% obslik(:, t0-d:t0-1) - length d window +% obsvec - likelihood vector for current observation +% transmat - transition matrix +% If we specify the optional 'act' argument, transmat{a} should be a cell array, and +% act(t0-d:t0) - length d window, last column = current action +% +% Output: +% alpha(:, t0-d+1:t0) - last column = new filtered estimate +% obslik(:, t0-d+1:t0) - last column = obsvec +% xi(:, :, t0-d+1:t0-1) - 2 slice smoothed window +% gamma(:, t0-d+1:t0) - smoothed window +% +% As usual, we define (using T=d) +% alpha(i,t) = Pr(Q(t)=i | Y(1:t)) +% gamma(i,t) = Pr(Q(t)=i | Y(1:T)) +% xi(i,j,t) = Pr(Q(t)=i, Q(t+1)=j | Y(1:T)) +% +% obslik(i,t) = Pr(Y(t) | Q(t)=i) +% transmat{a}(i,j) = Pr(Q(t)=j | Q(t-1)=i, A(t)=a) + +[S n] = size(alpha); +d = min(d, n+1); +if d < 2 + error('must keep a window of length at least 2'); +end + +if ~exist('act') + act = ones(1, n+1); + transmat = { transmat }; +end + +% pluck out last d-1 components from the history +alpha = alpha(:, n-d+2:n); +obslik = obslik(:, n-d+2:n); + +% Extend window by 1 +t = d; +obslik(:,t) = obsvec; +xi = zeros(S, S, d-1); +xi(:,:,t-1) = normalise((alpha(:,t-1) * obslik(:,t)') .* transmat{act(t)}); +alpha(:,t) = sum(xi(:,:,t-1), 1)'; + +% Now smooth backwards inside the window +beta = ones(S, d); +T = d; +%fprintf('smooth from %d to 1, i.e., %d to %d\n', d, t0, t0-d+1); +gamma(:,T) = alpha(:,T); +for t=T-1:-1:1 + b = beta(:,t+1) .* obslik(:,t+1); + beta(:,t) = normalise(transmat{act(t)} * b); + gamma(:,t) = normalise(alpha(:,t) .* beta(:,t)); + xi(:,:,t) = normalise((transmat{act(t)} .* (alpha(:,t) * b'))); +end + + + + diff --git a/sourcecodes/bnt-master/HMM/fixed_lag_smoother_demo.m b/sourcecodes/bnt-master/HMM/fixed_lag_smoother_demo.m new file mode 100644 index 00000000..9cf19f90 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/fixed_lag_smoother_demo.m @@ -0,0 +1,38 @@ +% Example of fixed lag smoothing + +rand('state', 1); +S = 2; +O = 2; +T = 7; +data = sample_discrete([0.5 0.5], 1, T); +transmat = mk_stochastic(rand(S,S)); +obsmat = mk_stochastic(rand(S,O)); +obslik = multinomial_prob(data, obsmat); +prior = [0.5 0.5]'; + + +[alpha0, beta0, gamma0, ll0, xi0] = fwdback(prior, transmat, obslik); + +w = 3; +alpha1 = zeros(S, T); +gamma1 = zeros(S, T); +xi1 = zeros(S, S, T-1); +t = 1; +b = obsmat(:, data(t)); +olik_win = b; % window of conditional observation likelihoods +alpha_win = normalise(prior .* b); +alpha1(:,t) = alpha_win; +for t=2:T + [alpha_win, olik_win, gamma_win, xi_win] = ... + fixed_lag_smoother(w, alpha_win, olik_win, obsmat(:, data(t)), transmat); + alpha1(:,max(1,t-w+1):t) = alpha_win; + gamma1(:,max(1,t-w+1):t) = gamma_win; + xi1(:,:,max(1,t-w+1):t-1) = xi_win; +end + +e = 1e-1; +%assert(approxeq(alpha0, alpha1, e)); +assert(approxeq(gamma0(:, T-w+1:end), gamma1(:, T-w+1:end), e)); +%assert(approxeq(xi0(:,:,T-w+1:end), xi1(:,:,T-w+1:end), e)); + + diff --git a/sourcecodes/bnt-master/HMM/fwdback.m b/sourcecodes/bnt-master/HMM/fwdback.m new file mode 100644 index 00000000..d74d5490 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/fwdback.m @@ -0,0 +1,197 @@ +function [alpha, beta, gamma, loglik, xi_summed, gamma2] = fwdback(init_state_distrib, ... + transmat, obslik, varargin) +% FWDBACK Compute the posterior probs. in an HMM using the forwards backwards algo. +% +% [alpha, beta, gamma, loglik, xi, gamma2] = fwdback(init_state_distrib, transmat, obslik, ...) +% +% Notation: +% Y(t) = observation, Q(t) = hidden state, M(t) = mixture variable (for MOG outputs) +% A(t) = discrete input (action) (for POMDP models) +% +% INPUT: +% init_state_distrib(i) = Pr(Q(1) = i) +% transmat(i,j) = Pr(Q(t) = j | Q(t-1)=i) +% or transmat{a}(i,j) = Pr(Q(t) = j | Q(t-1)=i, A(t-1)=a) if there are discrete inputs +% obslik(i,t) = Pr(Y(t)| Q(t)=i) +% (Compute obslik using eval_pdf_xxx on your data sequence first.) +% +% Optional parameters may be passed as 'param_name', param_value pairs. +% Parameter names are shown below; default values in [] - if none, argument is mandatory. +% +% For HMMs with MOG outputs: if you want to compute gamma2, you must specify +% 'obslik2' - obslik(i,j,t) = Pr(Y(t)| Q(t)=i,M(t)=j) [] +% 'mixmat' - mixmat(i,j) = Pr(M(t) = j | Q(t)=i) [] +% +% For HMMs with discrete inputs: +% 'act' - act(t) = action performed at step t +% +% Optional arguments: +% 'fwd_only' - if 1, only do a forwards pass and set beta=[], gamma2=[] [0] +% 'scaled' - if 1, normalize alphas and betas to prevent underflow [1] +% 'maximize' - if 1, use max-product instead of sum-product [0] +% +% OUTPUTS: +% alpha(i,t) = p(Q(t)=i | y(1:t)) (or p(Q(t)=i, y(1:t)) if scaled=0) +% beta(i,t) = p(y(t+1:T) | Q(t)=i)*p(y(t+1:T)|y(1:t)) (or p(y(t+1:T) | Q(t)=i) if scaled=0) +% gamma(i,t) = p(Q(t)=i | y(1:T)) +% loglik = log p(y(1:T)) +% xi(i,j,t-1) = p(Q(t-1)=i, Q(t)=j | y(1:T)) - NO LONGER COMPUTED +% xi_summed(i,j) = sum_{t=}^{T-1} xi(i,j,t) - changed made by Herbert Jaeger +% gamma2(j,k,t) = p(Q(t)=j, M(t)=k | y(1:T)) (only for MOG outputs) +% +% If fwd_only = 1, these become +% alpha(i,t) = p(Q(t)=i | y(1:t)) +% beta = [] +% gamma(i,t) = p(Q(t)=i | y(1:t)) +% xi(i,j,t-1) = p(Q(t-1)=i, Q(t)=j | y(1:t)) +% gamma2 = [] +% +% Note: we only compute xi if it is requested as a return argument, since it can be very large. +% Similarly, we only compute gamma2 on request (and if using MOG outputs). +% +% Examples: +% +% [alpha, beta, gamma, loglik] = fwdback(pi, A, multinomial_prob(sequence, B)); +% +% [B, B2] = mixgauss_prob(data, mu, Sigma, mixmat); +% [alpha, beta, gamma, loglik, xi, gamma2] = fwdback(pi, A, B, 'obslik2', B2, 'mixmat', mixmat); + +if nargout >= 5, compute_xi = 1; else compute_xi = 0; end +if nargout >= 6, compute_gamma2 = 1; else compute_gamma2 = 0; end + +[obslik2, mixmat, fwd_only, scaled, act, maximize, compute_xi, compute_gamma2] = ... + process_options(varargin, ... + 'obslik2', [], 'mixmat', [], ... + 'fwd_only', 0, 'scaled', 1, 'act', [], 'maximize', 0, ... + 'compute_xi', compute_xi, 'compute_gamma2', compute_gamma2); + +[Q T] = size(obslik); + +if isempty(obslik2) + compute_gamma2 = 0; +end + +if isempty(act) + act = ones(1,T); + transmat = { transmat } ; +end + +scale = ones(1,T); + +% scale(t) = Pr(O(t) | O(1:t-1)) = 1/c(t) as defined by Rabiner (1989). +% Hence prod_t scale(t) = Pr(O(1)) Pr(O(2)|O(1)) Pr(O(3) | O(1:2)) ... = Pr(O(1), ... ,O(T)) +% or log P = sum_t log scale(t). +% Rabiner suggests multiplying beta(t) by scale(t), but we can instead +% normalise beta(t) - the constants will cancel when we compute gamma. + +loglik = 0; + +alpha = zeros(Q,T); +gamma = zeros(Q,T); +if compute_xi + xi_summed = zeros(Q,Q); +else + xi_summed = []; +end + +%%%%%%%%% Forwards %%%%%%%%%% + +t = 1; +alpha(:,1) = init_state_distrib(:) .* obslik(:,t); +if scaled + %[alpha(:,t), scale(t)] = normaliseC(alpha(:,t)); + [alpha(:,t), scale(t)] = normalise(alpha(:,t)); +end +assert(approxeq(sum(alpha(:,t)),1)) +for t=2:T + %trans = transmat(:,:,act(t-1))'; + trans = transmat{act(t-1)}; + if maximize + m = max_mult(trans', alpha(:,t-1)); + %A = repmat(alpha(:,t-1), [1 Q]); + %m = max(trans .* A, [], 1); + else + m = trans' * alpha(:,t-1); + end + alpha(:,t) = m(:) .* obslik(:,t); + if scaled + %[alpha(:,t), scale(t)] = normaliseC(alpha(:,t)); + [alpha(:,t), scale(t)] = normalise(alpha(:,t)); + end + if compute_xi & fwd_only % useful for online EM + %xi(:,:,t-1) = normaliseC((alpha(:,t-1) * obslik(:,t)') .* trans); + xi_summed = xi_summed + normalise((alpha(:,t-1) * obslik(:,t)') .* trans); + end + assert(approxeq(sum(alpha(:,t)),1)) +end +if scaled + if any(scale==0) + loglik = -inf; + else + loglik = sum(log(scale)); + end +else + loglik = log(sum(alpha(:,T))); +end + +if fwd_only + gamma = alpha; + beta = []; + gamma2 = []; + return; +end + +%%%%%%%%% Backwards %%%%%%%%%% + +beta = zeros(Q,T); +if compute_gamma2 + M = size(mixmat, 2); + gamma2 = zeros(Q,M,T); +else + gamma2 = []; +end + +beta(:,T) = ones(Q,1); +%gamma(:,T) = normaliseC(alpha(:,T) .* beta(:,T)); +gamma(:,T) = normalise(alpha(:,T) .* beta(:,T)); +t=T; +if compute_gamma2 + denom = obslik(:,t) + (obslik(:,t)==0); % replace 0s with 1s before dividing + gamma2(:,:,t) = obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]) ./ repmat(denom, [1 M]); + %gamma2(:,:,t) = normaliseC(obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M])); % wrong! +end +for t=T-1:-1:1 + b = beta(:,t+1) .* obslik(:,t+1); + %trans = transmat(:,:,act(t)); + trans = transmat{act(t)}; + if maximize + B = repmat(b(:)', Q, 1); + beta(:,t) = max(trans .* B, [], 2); + else + beta(:,t) = trans * b; + end + if scaled + %beta(:,t) = normaliseC(beta(:,t)); + beta(:,t) = normalise(beta(:,t)); + end + %gamma(:,t) = normaliseC(alpha(:,t) .* beta(:,t)); + gamma(:,t) = normalise(alpha(:,t) .* beta(:,t)); + if compute_xi + %xi(:,:,t) = normaliseC((trans .* (alpha(:,t) * b'))); + xi_summed = xi_summed + normalise((trans .* (alpha(:,t) * b'))); + end + if compute_gamma2 + denom = obslik(:,t) + (obslik(:,t)==0); % replace 0s with 1s before dividing + gamma2(:,:,t) = obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M]) ./ repmat(denom, [1 M]); + %gamma2(:,:,t) = normaliseC(obslik2(:,:,t) .* mixmat .* repmat(gamma(:,t), [1 M])); + end +end + +% We now explain the equation for gamma2 +% Let zt=y(1:t-1,t+1:T) be all observations except y(t) +% gamma2(Q,M,t) = P(Qt,Mt|yt,zt) = P(yt|Qt,Mt,zt) P(Qt,Mt|zt) / P(yt|zt) +% = P(yt|Qt,Mt) P(Mt|Qt) P(Qt|zt) / P(yt|zt) +% Now gamma(Q,t) = P(Qt|yt,zt) = P(yt|Qt) P(Qt|zt) / P(yt|zt) +% hence +% P(Qt,Mt|yt,zt) = P(yt|Qt,Mt) P(Mt|Qt) [P(Qt|yt,zt) P(yt|zt) / P(yt|Qt)] / P(yt|zt) +% = P(yt|Qt,Mt) P(Mt|Qt) P(Qt|yt,zt) / P(yt|Qt) diff --git a/sourcecodes/bnt-master/HMM/gausshmm_train_observed.m b/sourcecodes/bnt-master/HMM/gausshmm_train_observed.m new file mode 100644 index 00000000..0e96b365 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/gausshmm_train_observed.m @@ -0,0 +1,44 @@ +function [initState, transmat, mu, Sigma] = gausshmm_train_observed(obsData, hiddenData, ... + nstates, varargin) +% GAUSSHMM_TRAIN_OBSERVED Estimate params of HMM with Gaussian output from fully observed sequences +% [initState, transmat, mu, Sigma] = gausshmm_train_observed(obsData, hiddenData, nstates,...) +% +% INPUT +% If all sequences have the same length +% obsData(:,t,ex) +% hiddenData(ex,t) - must be ROW vector if only one sequence +% If sequences have different lengths, we use cell arrays +% obsData{ex}(:,t) +% hiddenData{ex}(t) +% +% Optional argumnets +% dirichletPriorWeight - for smoothing transition matrix counts +% +% Optional parameters from mixgauss_Mstep: +% 'cov_type' - 'full', 'diag' or 'spherical' ['full'] +% 'tied_cov' - 1 (Sigma) or 0 (Sigma_i) [0] +% 'clamped_cov' - pass in clamped value, or [] if unclamped [ [] ] +% 'clamped_mean' - pass in clamped value, or [] if unclamped [ [] ] +% 'cov_prior' - Lambda_i, added to YY(:,:,i) [0.01*eye(d,d,Q)] +% +% Output +% mu(:,q) +% Sigma(:,:,q) + +[dirichletPriorWeight, other] = process_options(... + varargin, 'dirichletPriorWeight', 0); + +[transmat, initState] = transmat_train_observed(hiddenData, nstates, ... + 'dirichletPriorWeight', dirichletPriorWeight); + +% convert to obsData(:,t*nex) +if ~iscell(obsData) + [D T Nex] = size(obsData); + obsData = reshape(obsData, D, T*Nex); +else + obsData = cat(2, obsData{:}); + hiddenData = cat(2,hiddenData{:}); +end +[mu, Sigma] = condgaussTrainObserved(obsData, hiddenData(:), nstates, varargin{:}); + + diff --git a/sourcecodes/bnt-master/HMM/mc_sample.m b/sourcecodes/bnt-master/HMM/mc_sample.m new file mode 100644 index 00000000..87488f44 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mc_sample.m @@ -0,0 +1,17 @@ +function S = mc_sample(prior, trans, len, numex) +% SAMPLE_MC Generate random sequences from a Markov chain. +% STATE = SAMPLE_MC(PRIOR, TRANS, LEN) generates a sequence of length LEN. +% +% STATE = SAMPLE_MC(PRIOR, TRANS, LEN, N) generates N rows each of length LEN. + +if nargin==3 + numex = 1; +end + +S = zeros(numex,len); +for i=1:numex + S(i, 1) = sample_discrete(prior); + for t=2:len + S(i, t) = sample_discrete(trans(S(i,t-1),:)); + end +end diff --git a/sourcecodes/bnt-master/HMM/mc_sample_endstate.m b/sourcecodes/bnt-master/HMM/mc_sample_endstate.m new file mode 100644 index 00000000..8dc9e0ba --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mc_sample_endstate.m @@ -0,0 +1,27 @@ +function S = sample_mc_endstate(startprob, trans, endprob) +% SAMPLE_MC_ENDSTATE Generate a random sequence from a Markov chain until enter the endstate. +% seq = sample_mc(startprob, trans, endprob) + +% add an end state +Q = size(trans,1); +transprob = zeros(Q,Q+1); +end_state = Q+1; +for i=1:Q + for j=1:Q + transprob(i,j) = (1-endprob(i)) * trans(i,j); + end + transprob(i,end_state) = endprob(i); + %assert(approxeq(sum(transprob(i,:)), 1)) +end + +S = []; +S(1) = sample_discrete(startprob); +t = 1; +p = endprob(S(t)); +stop = (S(1) == end_state); +while ~stop + S(t+1) = sample_discrete(transprob(S(t),:)); + stop = (S(t+1) == end_state); + t = t + 1; +end +S = S(1:end-1); % don't include end state diff --git a/sourcecodes/bnt-master/HMM/mdp_sample.m b/sourcecodes/bnt-master/HMM/mdp_sample.m new file mode 100644 index 00000000..63923c9c --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mdp_sample.m @@ -0,0 +1,18 @@ +function state = sample_mdp(prior, trans, act) +% SAMPLE_MDP Sample a sequence of states from a Markov Decision Process. +% state = sample_mdp(prior, trans, act) +% +% Inputs: +% prior(i) = Pr(Q(1)=i) +% trans{a}(i,j) = Pr(Q(t)=j | Q(t-1)=i, A(t)=a) +% act(a) = A(t), so act(1) is ignored +% +% Output: +% state is a vector of length T=length(act) + +len = length(act); +state = zeros(1,len); +state(1) = sample_discrete(prior); +for t=2:len + state(t) = sample_discrete(trans{act(t)}(state(t-1),:)); +end diff --git a/sourcecodes/bnt-master/HMM/mhmmParzen_train_observed.m b/sourcecodes/bnt-master/HMM/mhmmParzen_train_observed.m new file mode 100644 index 00000000..1786e9a2 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mhmmParzen_train_observed.m @@ -0,0 +1,37 @@ +function [initState, transmat, mu, Nproto, pick] = mhmmParzen_train_observed(obsData, hiddenData, ... + nstates, maxNproto, varargin) +% mhmmParzentrain_observed with mixture of Gaussian outputs from fully observed sequences +% function [initState, transmat, mu, Nproto] = mhmm_train_observed_parzen(obsData, hiddenData, ... +% nstates, maxNproto) +% +% +% INPUT +% If all sequences have the same length +% obsData(:,t,ex) +% hiddenData(ex,t) - must be ROW vector if only one sequence +% If sequences have different lengths, we use cell arrays +% obsData{ex}(:,t) +% hiddenData{ex}(t) +% +% Optional argumnets +% dirichletPriorWeight - for smoothing transition matrix counts +% mkSymmetric +% +% Output +% mu(:,q) +% Nproto(q) is the number of prototypes (mixture components) chosen for state q + +[transmat, initState] = transmat_train_observed(... + hiddenData, nstates, varargin{:}); + +% convert to obsData(:,t*nex) +if ~iscell(obsData) + [D T Nex] = size(obsData); + obsData = reshape(obsData, D, T*Nex); +else + obsData = cat(2, obsData{:}); + hiddenData = cat(2, hiddenData{:}); +end +[mu, Nproto, pick] = parzen_fit_select_unif(obsData, hiddenData(:), maxNproto); + + diff --git a/sourcecodes/bnt-master/HMM/mhmm_em.m b/sourcecodes/bnt-master/HMM/mhmm_em.m new file mode 100644 index 00000000..036c5222 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mhmm_em.m @@ -0,0 +1,173 @@ +function [LL, prior, transmat, mu, Sigma, mixmat] = ... + mhmm_em(data, prior, transmat, mu, Sigma, mixmat, varargin); +% LEARN_MHMM Compute the ML parameters of an HMM with (mixtures of) Gaussians output using EM. +% [ll_trace, prior, transmat, mu, sigma, mixmat] = learn_mhmm(data, ... +% prior0, transmat0, mu0, sigma0, mixmat0, ...) +% +% Notation: Q(t) = hidden state, Y(t) = observation, M(t) = mixture variable +% +% INPUTS: +% data{ex}(:,t) or data(:,t,ex) if all sequences have the same length +% prior(i) = Pr(Q(1) = i), +% transmat(i,j) = Pr(Q(t+1)=j | Q(t)=i) +% mu(:,j,k) = E[Y(t) | Q(t)=j, M(t)=k ] +% Sigma(:,:,j,k) = Cov[Y(t) | Q(t)=j, M(t)=k] +% mixmat(j,k) = Pr(M(t)=k | Q(t)=j) : set to [] or ones(Q,1) if only one mixture component +% +% Optional parameters may be passed as 'param_name', param_value pairs. +% Parameter names are shown below; default values in [] - if none, argument is mandatory. +% +% 'max_iter' - max number of EM iterations [10] +% 'thresh' - convergence threshold [1e-4] +% 'verbose' - if 1, print out loglik at every iteration [1] +% 'cov_type' - 'full', 'diag' or 'spherical' ['full'] +% +% To clamp some of the parameters, so learning does not change them: +% 'adj_prior' - if 0, do not change prior [1] +% 'adj_trans' - if 0, do not change transmat [1] +% 'adj_mix' - if 0, do not change mixmat [1] +% 'adj_mu' - if 0, do not change mu [1] +% 'adj_Sigma' - if 0, do not change Sigma [1] +% +% If the number of mixture components differs depending on Q, just set the trailing +% entries of mixmat to 0, e.g., 2 components if Q=1, 3 components if Q=2, +% then set mixmat(1,3)=0. In this case, B2(1,3,:)=1.0. + +if ~isstr(varargin{1}) % catch old syntax + error('optional arguments should be passed as string/value pairs') +end + +[max_iter, thresh, verbose, cov_type, adj_prior, adj_trans, adj_mix, adj_mu, adj_Sigma] = ... + process_options(varargin, 'max_iter', 10, 'thresh', 1e-4, 'verbose', 1, ... + 'cov_type', 'full', 'adj_prior', 1, 'adj_trans', 1, 'adj_mix', 1, ... + 'adj_mu', 1, 'adj_Sigma', 1); + +previous_loglik = -inf; +loglik = 0; +converged = 0; +num_iter = 1; +LL = []; + +if ~iscell(data) + data = num2cell(data, [1 2]); % each elt of the 3rd dim gets its own cell +end +numex = length(data); + + +O = size(data{1},1); +Q = length(prior); +if isempty(mixmat) + mixmat = ones(Q,1); +end +M = size(mixmat,2); +if M == 1 + adj_mix = 0; +end + +while (num_iter <= max_iter) & ~converged + % E step + [loglik, exp_num_trans, exp_num_visits1, postmix, m, ip, op] = ... + ess_mhmm(prior, transmat, mixmat, mu, Sigma, data); + + + % M step + if adj_prior + prior = normalise(exp_num_visits1); + end + if adj_trans + transmat = mk_stochastic(exp_num_trans); + end + if adj_mix + mixmat = mk_stochastic(postmix); + end + if adj_mu | adj_Sigma + [mu2, Sigma2] = mixgauss_Mstep(postmix, m, op, ip, 'cov_type', cov_type); + if adj_mu + mu = reshape(mu2, [O Q M]); + end + if adj_Sigma + Sigma = reshape(Sigma2, [O O Q M]); + end + end + + if verbose, fprintf(1, 'iteration %d, loglik = %f\n', num_iter, loglik); end + num_iter = num_iter + 1; + converged = em_converged(loglik, previous_loglik, thresh); + previous_loglik = loglik; + LL = [LL loglik]; +end + + +%%%%%%%%% + +function [loglik, exp_num_trans, exp_num_visits1, postmix, m, ip, op] = ... + ess_mhmm(prior, transmat, mixmat, mu, Sigma, data) +% ESS_MHMM Compute the Expected Sufficient Statistics for a MOG Hidden Markov Model. +% +% Outputs: +% exp_num_trans(i,j) = sum_l sum_{t=2}^T Pr(Q(t-1) = i, Q(t) = j| Obs(l)) +% exp_num_visits1(i) = sum_l Pr(Q(1)=i | Obs(l)) +% +% Let w(i,k,t,l) = P(Q(t)=i, M(t)=k | Obs(l)) +% where Obs(l) = Obs(:,:,l) = O_1 .. O_T for sequence l +% Then +% postmix(i,k) = sum_l sum_t w(i,k,t,l) (posterior mixing weights/ responsibilities) +% m(:,i,k) = sum_l sum_t w(i,k,t,l) * Obs(:,t,l) +% ip(i,k) = sum_l sum_t w(i,k,t,l) * Obs(:,t,l)' * Obs(:,t,l) +% op(:,:,i,k) = sum_l sum_t w(i,k,t,l) * Obs(:,t,l) * Obs(:,t,l)' + + +verbose = 0; + +%[O T numex] = size(data); +numex = length(data); +O = size(data{1},1); +Q = length(prior); +M = size(mixmat,2); +exp_num_trans = zeros(Q,Q); +exp_num_visits1 = zeros(Q,1); +postmix = zeros(Q,M); +m = zeros(O,Q,M); +op = zeros(O,O,Q,M); +ip = zeros(Q,M); + +mix = (M>1); + +loglik = 0; +if verbose, fprintf(1, 'forwards-backwards example # '); end +for ex=1:numex + if verbose, fprintf(1, '%d ', ex); end + %obs = data(:,:,ex); + obs = data{ex}; + T = size(obs,2); + if mix + [B, B2] = mixgauss_prob(obs, mu, Sigma, mixmat); + [alpha, beta, gamma, current_loglik, xi, gamma2] = ... + fwdback(prior, transmat, B, 'obslik2', B2, 'mixmat', mixmat); + else + B = mixgauss_prob(obs, mu, Sigma); + [alpha, beta, gamma, current_loglik, xi] = fwdback(prior, transmat, B); + end + loglik = loglik + current_loglik; + if verbose, fprintf(1, 'll at ex %d = %f\n', ex, loglik); end + + exp_num_trans = exp_num_trans + sum(xi,3); + exp_num_visits1 = exp_num_visits1 + gamma(:,1); + + if mix + postmix = postmix + sum(gamma2,3); + else + postmix = postmix + sum(gamma,2); + gamma2 = reshape(gamma, [Q 1 T]); % gamma2(i,m,t) = gamma(i,t) + end + for i=1:Q + for k=1:M + w = reshape(gamma2(i,k,:), [1 T]); % w(t) = w(i,k,t,l) + wobs = obs .* repmat(w, [O 1]); % wobs(:,t) = w(t) * obs(:,t) + m(:,i,k) = m(:,i,k) + sum(wobs, 2); % m(:) = sum_t w(t) obs(:,t) + op(:,:,i,k) = op(:,:,i,k) + wobs * obs'; % op(:,:) = sum_t w(t) * obs(:,t) * obs(:,t)' + ip(i,k) = ip(i,k) + sum(sum(wobs .* obs, 2)); % ip = sum_t w(t) * obs(:,t)' * obs(:,t) + end + end +end +if verbose, fprintf(1, '\n'); end diff --git a/sourcecodes/bnt-master/HMM/mhmm_em_demo.m b/sourcecodes/bnt-master/HMM/mhmm_em_demo.m new file mode 100644 index 00000000..3e361545 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mhmm_em_demo.m @@ -0,0 +1,40 @@ +if 1 + O = 4; + T = 10; + nex = 50; + M = 2; + Q = 3; +else + O = 8; %Number of coefficients in a vector + T = 420; %Number of vectors in a sequence + nex = 1; %Number of sequences + M = 1; %Number of mixtures + Q = 6; %Number of states +end +cov_type = 'full'; + +data = randn(O,T,nex); + +% initial guess of parameters +prior0 = normalise(rand(Q,1)); +transmat0 = mk_stochastic(rand(Q,Q)); + +if 0 + Sigma0 = repmat(eye(O), [1 1 Q M]); + % Initialize each mean to a random data point + indices = randperm(T*nex); + mu0 = reshape(data(:,indices(1:(Q*M))), [O Q M]); + mixmat0 = mk_stochastic(rand(Q,M)); +else + [mu0, Sigma0] = mixgauss_init(Q*M, data, cov_type); + mu0 = reshape(mu0, [O Q M]); + Sigma0 = reshape(Sigma0, [O O Q M]); + mixmat0 = mk_stochastic(rand(Q,M)); +end + +[LL, prior1, transmat1, mu1, Sigma1, mixmat1] = ... + mhmm_em(data, prior0, transmat0, mu0, Sigma0, mixmat0, 'max_iter', 5); + + +loglik = mhmm_logprob(data, prior1, transmat1, mu1, Sigma1, mixmat1); + diff --git a/sourcecodes/bnt-master/HMM/mhmm_logprob.m b/sourcecodes/bnt-master/HMM/mhmm_logprob.m new file mode 100644 index 00000000..e27a4eb9 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mhmm_logprob.m @@ -0,0 +1,30 @@ +function [loglik, errors] = mhmm_logprob(data, prior, transmat, mu, Sigma, mixmat) +% LOG_LIK_MHMM Compute the log-likelihood of a dataset using a (mixture of) Gaussians HMM +% [loglik, errors] = log_lik_mhmm(data, prior, transmat, mu, sigma, mixmat) +% +% data{m}(:,t) or data(:,t,m) if all cases have same length +% errors is a list of the cases which received a loglik of -infinity +% +% Set mixmat to ones(Q,1) or omit it if there is only 1 mixture component + +Q = length(prior); +if size(mixmat,1) ~= Q % trap old syntax + error('mixmat should be QxM') +end +if nargin < 6, mixmat = ones(Q,1); end + +if ~iscell(data) + data = num2cell(data, [1 2]); % each elt of the 3rd dim gets its own cell +end +ncases = length(data); + +loglik = 0; +errors = []; +for m=1:ncases + obslik = mixgauss_prob(data{m}, mu, Sigma, mixmat); + [alpha, beta, gamma, ll] = fwdback(prior, transmat, obslik, 'fwd_only', 1); + if ll==-inf + errors = [errors m]; + end + loglik = loglik + ll; +end diff --git a/sourcecodes/bnt-master/HMM/mhmm_sample.m b/sourcecodes/bnt-master/HMM/mhmm_sample.m new file mode 100644 index 00000000..83034cd4 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mhmm_sample.m @@ -0,0 +1,31 @@ +function [obs, hidden] = mhmm_sample(T, numex, initial_prob, transmat, mu, Sigma, mixmat) +% SAMPLE_MHMM Generate random sequences from an HMM with (mixtures of) Gaussian output. +% [obs, hidden] = sample_mhmm(T, numex, initial_prob, transmat, mu, Sigma, mixmat) +% +% INPUTS: +% T - length of each sequence +% numex - num. sequences +% init_state_prob(i) = Pr(Q(1) = i) +% transmat(i,j) = Pr(Q(t+1)=j | Q(t)=i) +% mu(:,j,k) = mean of Y(t) given Q(t)=j, M(t)=k +% Sigma(:,:,j,k) = cov. of Y(t) given Q(t)=j, M(t)=k +% mixmat(j,k) = Pr(M(t)=k | Q(t)=j) : set to ones(Q,1) or omit if single mixture +% +% OUTPUT: +% obs(:,t,l) = observation vector at time t for sequence l +% hidden(t,l) = the hidden state at time t for sequence l + +Q = length(initial_prob); +if nargin < 7, mixmat = ones(Q,1); end +O = size(mu,1); +hidden = zeros(T, numex); +obs = zeros(O, T, numex); + +hidden = mc_sample(initial_prob, transmat, T, numex)'; +for i=1:numex + for t=1:T + q = hidden(t,i); + m = sample_discrete(mixmat(q,:), 1, 1); + obs(:,t,i) = gaussian_sample(mu(:,q,m), Sigma(:,:,q,m), 1); + end +end diff --git a/sourcecodes/bnt-master/HMM/mk_leftright_transmat.m b/sourcecodes/bnt-master/HMM/mk_leftright_transmat.m new file mode 100644 index 00000000..8a36e1a6 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mk_leftright_transmat.m @@ -0,0 +1,6 @@ +function transmat = mk_leftright_transmat(Q, p) +% MK_LEFTRIGHT_TRANSMAT Q = num states, p = prob on (i,i), 1-p on (i,i+1) +% function transmat = mk_leftright_transmat(Q, p) + +transmat = p*diag(ones(Q,1)) + (1-p)*diag(ones(Q-1,1),1); +transmat(Q,Q)=1; diff --git a/sourcecodes/bnt-master/HMM/mk_rightleft_transmat.m b/sourcecodes/bnt-master/HMM/mk_rightleft_transmat.m new file mode 100644 index 00000000..ce7cf62b --- /dev/null +++ b/sourcecodes/bnt-master/HMM/mk_rightleft_transmat.m @@ -0,0 +1,6 @@ +function transmat = mk_rightleft_transmat(Q, p) +% MK_RIGHTLEFT_TRANSMAT Q = num states, p = prob on (i,i), 1-p on (i,i+1) +% function transmat = mk_rightleft_transmat(Q, p) + +transmat = p*diag(ones(Q,1)) + (1-p)*diag(ones(Q-1,1),-1); +transmat(1,1)=1; diff --git a/sourcecodes/bnt-master/HMM/pomdp_sample.m b/sourcecodes/bnt-master/HMM/pomdp_sample.m new file mode 100644 index 00000000..836ce39f --- /dev/null +++ b/sourcecodes/bnt-master/HMM/pomdp_sample.m @@ -0,0 +1,20 @@ +function [obs, hidden] = pomdp_sample(initial_prob, transmat, obsmat, act) +% SAMPLE_POMDP Generate a random sequence from a Partially Observed Markov Decision Process. +% [obs, hidden] = sample_pomdp(prior, transmat, obsmat, act) +% +% Inputs: +% prior(i) = Pr(Q(1)=i) +% transmat{a}(i,j) = Pr(Q(t)=j | Q(t-1)=i, A(t)=a) +% obsmat(i,k) = Pr(Y(t)=k | Q(t)=i) +% act(a) = A(t), so act(1) is ignored +% +% Output: +% obs and hidden are vectors of length T=length(act) + + +len = length(act); +hidden = mdp_sample(initial_prob, transmat, act); +obs = zeros(1, len); +for t=1:len + obs(t) = sample_discrete(obsmat(hidden(t),:)); +end diff --git a/sourcecodes/bnt-master/HMM/testHMM.m b/sourcecodes/bnt-master/HMM/testHMM.m new file mode 100644 index 00000000..b3ced724 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/testHMM.m @@ -0,0 +1,5 @@ +% Run all the demos, to check everything is "syntactically correct" +mhmm_em_demo +dhmm_em_demo +dhmm_em_online_demo +fixed_lag_smoother_demo diff --git a/sourcecodes/bnt-master/HMM/transmat_train_observed.m b/sourcecodes/bnt-master/HMM/transmat_train_observed.m new file mode 100644 index 00000000..06918429 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/transmat_train_observed.m @@ -0,0 +1,39 @@ +function [transmat, initState] = transmat_train_observed(labels, nstates, varargin) +% transmat_train_observed ML estimation from fully observed data +% function [transmat, initState] = transmat_train_observed(labels, nstates, varargin) +% +% If all sequences have the same length +% labels(ex,t) +% If sequences have different lengths, we use cell arrays +% labels{ex}(t) + +[dirichletPriorWeight, mkSymmetric, other] = process_options(... + varargin, 'dirichletPriorWeight', 0, 'mkSymmetric', 0); + +if ~iscell(labels) + [numex T] = size(labels); + if T==1 + labels = labels'; + end + %fprintf('T=%d, numex=%d\n', T, numex); + labels = num2cell(labels,2); % each row gets its own cell +end +numex = length(labels); + +counts = zeros(nstates, nstates); +counts1 = zeros(nstates,1); +for s=1:numex + labs = labels{s}; labs = labs(:)'; + dat = [labs(1:end-1); labs(2:end)]; + counts = counts + compute_counts(dat, [nstates nstates]); + q = labs(1); + counts1(q) = counts1(q) + 1; +end +pseudo_counts = dirichletPriorWeight*ones(nstates, nstates); +if mkSymmetric + counts = counts + counts'; +end +transmat = mk_stochastic(counts + pseudo_counts); +initState = normalize(counts1 + dirichletPriorWeight*ones(nstates,1)); + + diff --git a/sourcecodes/bnt-master/HMM/viterbi_path.m b/sourcecodes/bnt-master/HMM/viterbi_path.m new file mode 100644 index 00000000..88010277 --- /dev/null +++ b/sourcecodes/bnt-master/HMM/viterbi_path.m @@ -0,0 +1,62 @@ +function path = viterbi_path(prior, transmat, obslik) +% VITERBI Find the most-probable (Viterbi) path through the HMM state trellis. +% path = viterbi(prior, transmat, obslik) +% +% Inputs: +% prior(i) = Pr(Q(1) = i) +% transmat(i,j) = Pr(Q(t+1)=j | Q(t)=i) +% obslik(i,t) = Pr(y(t) | Q(t)=i) +% +% Outputs: +% path(t) = q(t), where q1 ... qT is the argmax of the above expression. + + +% delta(j,t) = prob. of the best sequence of length t-1 and then going to state j, and O(1:t) +% psi(j,t) = the best predecessor state, given that we ended up in state j at t + +scaled = 1; + +T = size(obslik, 2); +prior = prior(:); +Q = length(prior); + +delta = zeros(Q,T); +psi = zeros(Q,T); +path = zeros(1,T); +scale = ones(1,T); + + +t=1; +delta(:,t) = prior .* obslik(:,t); +if scaled + [delta(:,t), n] = normalise(delta(:,t)); + scale(t) = 1/n; +end +psi(:,t) = 0; % arbitrary value, since there is no predecessor to t=1 +for t=2:T + for j=1:Q + [delta(j,t), psi(j,t)] = max(delta(:,t-1) .* transmat(:,j)); + delta(j,t) = delta(j,t) * obslik(j,t); + end + if scaled + [delta(:,t), n] = normalise(delta(:,t)); + scale(t) = 1/n; + end +end +[p, path(T)] = max(delta(:,T)); +for t=T-1:-1:1 + path(t) = psi(path(t+1),t+1); +end + +% If scaled==0, p = prob_path(best_path) +% If scaled==1, p = Pr(replace sum with max and proceed as in the scaled forwards algo) +% Both are different from p(data) as computed using the sum-product (forwards) algorithm + +if 0 +if scaled + loglik = -sum(log(scale)); + %loglik = prob_path(prior, transmat, obslik, path); +else + loglik = log(p); +end +end |
