Пример #1
0
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
Пример #2
0
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
Пример #3
0
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