def _update_trans(self, elngamma, elnxi, n_obs): for i in range(self.n_states): for j in range(self.n_states): numerator = -np.inf denominator = -np.inf for k in range(n_obs - 1): numerator = elnsum(numerator, elnxi[i, j, k]) denominator = elnsum(denominator, elngamma[i, k]) self.trans[i, j] = eexp(elnproduct(numerator, -denominator))
def _update_emis(self, elngamma, obs_seq): for e in range(self.emis.shape[0]): for i in range(self.n_states): numerator = -np.inf denominator = -np.inf for k in range(len(obs_seq)): if obs_seq[k] - 1 == e: numerator = elnsum(numerator, elngamma[i, k]) denominator = elnsum(denominator, elngamma[i, k]) self.emis[e, i] = eexp(elnproduct(numerator, -denominator))
def viterbi_path(self): init = np.array([eln(val) for val in np.nditer(self.init)]) trans = np.array([[eln(v) for v in np.nditer(axis)] for axis in np.nditer(self.trans)]).reshape(self.n_states, self.n_states) print(np.array([[eln(v) for v in np.nditer(axis)] for axis in np.nditer(self.emis)])) emis = np.array([[eln(v) for v in np.nditer(axis)] for axis in np.nditer(self.emis)]).reshape(self.emis.shape[1], self.emis.shape[0]) best_states = [] logprob = init for k in range(0, self.n_obs): trans_p = np.zeros([self.n_states, self.n_states]) for i in range(self.n_states): for j in range(self.n_states): trans_p[i, j] = elnsum(logprob[i], elnproduct(trans[i, j], emis[i, self.obs[k-1]])) # Get the indices of the max probs that give the best prior states. best_states.append(np.argmax(trans_p, axis=0)) logprob = np.max(trans_p, axis=0) print(len(best_states)) # Most likely final state. final_state = np.argmax(logprob) print(final_state) # Reconstruct path by backtracking through likeliest states. prior_state = final_state best_path = [prior_state + 1] for best in reversed(best_states): prior_state = best[prior_state] best_path.append(prior_state + 1) return list(reversed(best_path)), logprob[final_state]
def _update_emis_eln(self, elngammas): """ This function calculates the update of the transition probabilities as defined by the Berkeley notes references in the _m_step algorithm. The numerator is the eln_xi values summed across all sequences and across all time steps. The denominator is the eln_gamma values summed across all sequences and across all time steps. """ for e in range(self.emis.shape[0]): for i in range(self.n_states): numerator = -np.inf denominator = -np.inf for idx, obs_seq in enumerate(self.sequences): for k in range(0, len(obs_seq)): if obs_seq[k] == e: numerator = elnsum(numerator, elngammas[idx][i, k]) denominator = elnsum(denominator, elngammas[idx][i, k]) self.emis[e, i] = eexp(elnproduct(numerator, -denominator))
def _update_trans_eln(self, gammas, xis): """ This function calculates the update of the transition probabilities as defined by the Berkeley notes references in the _m_step algorithm. The numerator is the eln_gamma values summed across all sequences and across all time steps. The denominator is the eln_gamma values summed across all sequences and across all time steps. """ for i in range(self.n_states): for j in range(self.n_states): numerator = -np.inf denominator = -np.inf for idx, obs_seq in enumerate(self.sequences): for k in range(1, len(obs_seq)): numerator = elnsum(numerator, elnxis[idx][i, j, k - 1]) denominator = elnsum(denominator, elngammas[idx][i, k - 1]) self.trans[i, j] = eexp(elnproduct(numerator, -denominator))
def _update_init_eln(self, elngammas): """ This function calculate the update value of the initial probabilities as defined by the Berkeley notes references in the _m_step algorithm. It is the average of the sum of probabilities of x_1 over all sequences. i.e. the expected frequency of state Si at time t = 1 """ for i in range(self.n_states): numerator = -np.inf denominator = 0 for idx in range(len(self.sequences)): numerator = elnsum(numerator, elngammas[idx][i, 0]) denominator = denominator + 1 self.init[i] = eexp(numerator) / denominator
def _generate_gamma(self, elnalpha, elnbeta): elngamma = np.zeros((self.n_states, self.n_obs + 1)) gamma = np.zeros((self.n_states, self.n_obs + 1)) for k in range(0, self.n_obs + 1): norm = -np.inf for i in range(self.n_states): elngamma[i, k] = elnproduct(elnalpha[i, k], elnbeta[i, k]) norm = elnsum(norm, elngamma[i, k]) for i in range(self.n_states): elngamma[i, k] = elnproduct(elngamma[i, k], -norm) gamma[i, k] = eexp(elngamma[i, k]) return elngamma, gamma
def forward_backward_eln(self): logalphas = self._forward_iter_eln() logbetas = self._backward_iter_eln() print(logalphas.shape) print(logbetas.shape) probs = np.zeros((self.n_states, self.n_obs + 1)) for k in range(0, self.n_obs + 1): norm = -np.inf for i in range(self.n_states): probs[i, k] = elnproduct(logalphas[i, k], logbetas[i, k]) norm = elnsum(norm, probs[i, k]) for i in range(self.n_states): probs[i, k] = elnproduct(probs[i, k], -norm) # for i in range(self.n_obs): # print(sum([p for p in list(probs[:, i]) if p != -np.inf])) return probs, logalphas, logbetas
def _backward_iter_eln(self): elnbeta = np.zeros((self.n_states, self.n_obs + 1)) # base case elnbeta[:, -1] = 0 # recursive case for k in range(self.n_obs, 0, -1): for i in range(self.n_states): beta = -np.inf for j in range(self.n_states): beta = elnsum( beta, elnproduct( eln(self.trans.transpose()[i, j]), elnproduct(eln(self.emis[self.obs[k - 1], j]), elnbeta[j, k]))) elnbeta[i, k - 1] = beta return elnbeta
def _forward_iter_eln(self): elnalpha = np.zeros((self.n_states, self.n_obs + 1)) # base case elnalpha[:, 0] = [eln(x) for x in self.init] # recursive case for k in range(1, self.n_obs + 1): for j in range(self.n_states): logalpha = -np.inf for i in range(self.n_states): logalpha = elnsum( logalpha, elnproduct(elnalpha[i, k - 1], eln(self.trans.transpose()[i, j]))) elnalpha[j, k] = elnproduct(logalpha, eln(self.emis[self.obs[k - 1], j])) return elnalpha
def _backward_iter_eln(self): logbetas = np.zeros((self.n_states, self.n_obs + 1)) # base case logbetas[:, -1] = 0 print(logbetas.transpose) # recursive case for k in range(self.n_obs, 0, -1): for i in range(self.n_states): logbeta = -np.inf for j in range(self.n_states): logbeta = elnsum( logbeta, elnproduct( eln(self.trans.transpose()[i, j]), elnproduct(eln(self.emis[self.obs[k - 1], j]), logbetas[j, k]))) logbetas[i, k - 1] = logbeta return logbetas
def _eln_xi(self, elnalpha, elnbeta, obs_seq): elnxi = np.zeros((self.n_states, self.n_states, len(obs_seq))) for k in range(len(obs_seq) - 1, -1, -1): normalizer = -np.inf for i in range(self.n_states): for j in range(self.n_states): elnxi[i, j, k - 1] = elnproduct( elnalpha[i, k - 1], elnproduct( eln(self.trans.transpose()[i, j]), elnproduct(eln(self.emis[obs_seq[k] - 1, j]), elnbeta[j, k]))) normalizer = elnsum(normalizer, elnxi[i, j, k - 1]) for i in range(self.n_states): for j in range(self.n_states): elnxi[i, j, k - 1] = elnproduct(elnxi[i, j, k - 1], -normalizer) return elnxi
def _eln_xi(self, elnalpha, elnbeta, obs_seq): """ This function calculates P(S_i_t, S_j_t+1) i.e. the probability of being in state S_i at time t and state S_j at time t+1. """ elnxi = np.zeros((self.n_states, self.n_states, len(obs_seq) - 1)) xi = np.zeros((self.n_states, self.n_states, len(obs_seq) - 1)) for k in range(len(obs_seq) - 1): normalizer = -np.inf for i in range(self.n_states): for j in range(self.n_states): elnxi[i, j, k] = elnproduct( elnalpha[i, k], elnproduct( eln(self.trans.transpose()[i, j]), elnproduct(eln(self.emis[obs_seq[k + 1], j]), elnbeta[j, k + 1]))) normalizer = elnsum(normalizer, elnxi[i, j, k]) for i in range(self.n_states): for j in range(self.n_states): elnxi[i, j, k] = elnproduct(elnxi[i, j, k], -normalizer) xi[i, j, k] = eexp(elnxi[i, j, k]) return elnxi, xi