def incremental_em_hmm(X, init_pi, init_obs_distr, t_min=100, step=None, m_step_delta=1, Xtest=None, monitor=None): pi = init_pi.copy() obs_distr = copy.deepcopy(init_obs_distr) if step is None: step = lambda t: 1. / t T = X.shape[0] K = len(obs_distr) A = 1. / K * np.ones((K, K)) seq = np.zeros(T, dtype=int) # online filtering sequence tau = np.zeros((T, K)) # filter emissions = np.array([d.pdf(X[0]) for d in obs_distr]).flatten() phi = pi * emissions # current marginal in q-distribution phi /= phi.sum() tau[0] = phi seq[0] = np.argmax(phi) s_pairs = distributions.TransitionISufficientStatistics(K) s_obs = [ d.new_incremental_sufficient_statistics(X[0], phi, i) for i, d in enumerate(obs_distr) ] ll_test = [] if Xtest is not None: lalpha_test, lbeta_test = alpha_beta(Xtest, pi, A, obs_distr) ll_test.append(log_likelihood(lalpha_test, lbeta_test)) monitor_vals = [] if monitor: monitor_vals.append(monitor(A, obs_distr)) for t in range(1, T): # E-step emissions = np.array([d.pdf(X[t]) for d in obs_distr]).flatten() q = A * emissions # new q_t transition q /= q.sum(axis=1)[:, nax] phi_q = phi[:, nax] * q # for transition statistics phi = phi_q.sum(axis=0) # compute filter tau[t] = emissions * A.T.dot(tau[t - 1]) tau[t] /= tau[t].sum() seq[t] = np.argmax(tau[t]) # sufficient statistics updates s = step(t) s_pairs.online_update(phi_q, s) for k in range(K): s_obs[k].online_update(X[t], phi, s) # M-step if t < t_min or t % m_step_delta != 0: continue # Tracer()() A = s_pairs.get_statistics() A /= A.sum(axis=1)[:, nax] for k in range(K): obs_distr[k].online_max_likelihood(s_obs[k], t=t) if Xtest is not None and t % 100 == 0: lalpha_test, lbeta_test = alpha_beta(Xtest, pi, A, obs_distr) ll_test.append(log_likelihood(lalpha_test, lbeta_test)) if monitor: monitor_vals.append(monitor(A, obs_distr)) return seq, tau, A, obs_distr, ll_test, monitor_vals
def incremental_em_hmm_add(X, lmbda, alpha=1.5, Kmax=30, init_pi=None, init_obs_distr=None, dist_cls=distributions.KL, dist_params=None, t_min=100, step=None, Xtest=None, monitor=None): if dist_params is None: dist_params = {} if init_obs_distr is None: obs_distr = [dist_cls(X[0], **dist_params)] pi = 1. else: obs_distr = copy.deepcopy(init_obs_distr) pi = copy.deepcopy(init_pi) if step is None: step = lambda t: 1. / t T = X.shape[0] K = 1 A = np.zeros((K, K)) A[0, 0] = 1. seq = np.zeros(T, dtype=int) emissions = np.array([d.pdf(X[0]) for d in obs_distr]).flatten() phi = pi * emissions phi /= phi.sum() tau = phi seq[0] = np.argmax(phi) s_pairs = distributions.TransitionISufficientStatistics(Kmax) s_obs = [ d.new_incremental_sufficient_statistics(X[0], phi, i) for i, d in enumerate(obs_distr) ] t_init = [0 for _ in range(len(obs_distr))] ll_test = [] if Xtest is not None: lalpha_test, lbeta_test = alpha_beta(Xtest, np.ones(K) / K, A, obs_distr) ll_test.append(log_likelihood(lalpha_test, lbeta_test)) monitor_vals = [] if monitor: monitor_vals.append(monitor(A, obs_distr)) for t in range(1, T): # E-step new_distr = dist_cls(X[0], **dist_params) new_distr.max_likelihood(X[t][nax, :], np.ones(1)) emissions = np.array([d.pdf(X[t]) for d in obs_distr + [new_distr]]).flatten() q = A * emissions[:K] Z = q.sum(axis=1) q /= Z[:, nax] phi_q = phi[:, nax] * q added_state = False if K < Kmax: # test new model with K+1 states Anew = alpha - 1 + s_pairs.get_statistics()[:K, :K + 1] Anew[np.eye(K) == 1] += 0.5 * alpha Anew /= Anew.sum(axis=1)[:, nax] q_new = Anew * emissions Z_new = q_new.sum(axis=1) q_new /= Z_new[:, nax] phi_q_new = phi[:, nax] * q_new loglik = phi.dot(np.log(Z)) loglik_new = phi.dot(np.log(Z_new)) if loglik_new - lmbda > loglik: A = Anew q = q_new phi_q = phi_q_new added_state = True K = K + 1 obs_distr.append(new_distr) t_init.append(t) phi = phi_q.sum(axis=0) # compute filter tau = emissions[:K] * A.T.dot(tau) tau /= tau.sum() seq[t] = np.argmax(tau) # sufficient statistics updates s = step(t) phq = np.zeros((Kmax, Kmax)) phq[:phi_q.shape[0], :phi_q.shape[1]] = phi_q s_pairs.online_update(phq, s) for k in range(len(s_obs)): s_obs[k].online_update(X[t], phi, step(t - t_init[k] + 1)) if added_state: s_obs.append( new_distr.new_incremental_sufficient_statistics( X[t], phi, K - 1)) # M-step # if t < t_min: # continue # Tracer()() A = alpha - 1 + s_pairs.get_statistics()[:K, :K] A[np.eye(K) == 1] += 0.5 * alpha A /= A.sum(axis=1)[:, nax] for k in range(K): obs_distr[k].online_max_likelihood(s_obs[k], t=t - t_init[k] + 1) if Xtest is not None and t % 100 == 0: lalpha_test, lbeta_test = alpha_beta(Xtest, np.ones(K) / K, A, obs_distr) ll_test.append(log_likelihood(lalpha_test, lbeta_test)) if monitor: monitor_vals.append(monitor(A, obs_distr)) Tracer()() return seq, A, obs_distr, ll_test, monitor_vals
def incremental_em_hsmm(X, init_pi, init_obs_distr, init_dur_distr, t_min=100, step=None, fit_durations=False): pi = init_pi.copy() obs_distr = copy.deepcopy(init_obs_distr) if fit_durations: dur_distr = copy.deepcopy(init_dur_distr) else: dur_distr = init_dur_distr D = dur_distr[0].D if step is None: step = lambda t: 1. / t T = X.shape[0] K = len(obs_distr) A = 1. / K * np.ones((K,K)) seq = np.zeros(T, dtype=int) phi = np.zeros((K, D)) tau = np.zeros(K) # tau[i] = sum_d phi(i,d) emissions = np.array([d.pdf(X[0]) for d in obs_distr]).flatten() phi[:,0] = pi * emissions phi[:,0] /= phi[:,0].sum() tau = phi[:,0] seq[0] = np.argmax(tau) # q[i,d,j] = q_t(j,1|i,d) if j > 0 # q[i,d,0] = q_t(i,d+1|i,d) q = np.zeros((K,D,K+1)) s_pairs = distributions.TransitionISufficientStatistics(K) s_obs = [d.new_incremental_sufficient_statistics(X[0], tau, i) for i, d in enumerate(obs_distr)] if fit_durations: pass # TODO # s_dur = [d.new_incremental_sufficient_statistics(i, K, D) # for i, d in enumerate(dur_distr)] for t in range(1,T): ## E-step # d_frac[i,d] = lambda_i(d) = D_i(d+1)/D_i(d) d_frac = np.vstack(d.d_frac() for d in dur_distr) emissions = np.array([d.pdf(X[t]) for d in obs_distr]).flatten() # new q_t[i,j,d] transition q[:,:,1:] = (1 - d_frac)[:,:,nax] * A[:,nax,:] * emissions[nax,nax,:] q[:,:,0] = d_frac * emissions[:,nax] q /= q.sum(2)[:,:,nax] # phi_q[i,j] = sum_d phi_{t-1}(i,d) q_t(j,1|i,d) (for transitions) phi_q = np.sum(phi[:,:,nax] * q[:,:,1:], axis=1) # update phi # phi_t(j,1) phi_1 = np.tensordot(phi, q[:,:,1:], axes=2) phi[:,1:] = phi[:,:-1] * q[:,:-1,0] phi[:,0] = phi_1 tau = phi.sum(1) # online sequence estimate (could improve using a filter) seq[t] = np.argmax(tau) # sufficient statistics updates s = step(t) s_pairs.online_update(phi_q, s) for k in range(K): s_obs[k].online_update(X[t], tau, s) if fit_durations: pass # TODO # s_dur[k].online_update(r, r_marginal, s) ## M-step if t < t_min: continue A = s_pairs.get_statistics() A /= A.sum(axis=1)[:,nax] for k in range(K): obs_distr[k].online_max_likelihood(s_obs[k], t=t) if fit_durations: pass # TODO # dur_distr[k].online_max_likelihood(s_dur[k], t=t) return seq, A, obs_distr, dur_distr