def compute_min_backward_messages(phi, observations): num_time_steps = len(observations) reverse_transition_model = compute_reverse_transition_model() backward_messages = [None] * (num_time_steps - 1) backward_traceback_messages = [None] * (num_time_steps - 1) for time_step in range(num_time_steps - 2, -1, -1): backward_messages[time_step] = robot.Distribution() backward_traceback_messages[time_step] = robot.Distribution() for goal_state in all_possible_hidden_states: min_val = np.inf argmin = None for current_state in phi[time_step + 1]: current_val = \ phi[time_step + 1][current_state] + \ -careful_log(reverse_transition_model[current_state][goal_state]) if time_step < num_time_steps - 2: current_val += backward_messages[time_step + 1][current_state] if current_val < min_val: min_val = current_val argmin = current_state backward_messages[time_step][goal_state] = min_val backward_traceback_messages[time_step][goal_state] = argmin return backward_messages, backward_traceback_messages
def compute_min_forward_messages(phi, observations): num_time_steps = len(observations) forward_messages = [None] * (num_time_steps - 1) traceback_messages = [None] * (num_time_steps - 1) for time_step in range(num_time_steps - 1): forward_messages[time_step] = robot.Distribution() traceback_messages[time_step] = robot.Distribution() for goal_state in all_possible_hidden_states: min_val = np.inf argmin = None for current_state in phi[time_step]: current_val = \ phi[time_step][current_state] + \ -careful_log(transition_model(current_state)[goal_state]) if time_step > 0: current_val += forward_messages[time_step - 1][current_state] if current_val < min_val: min_val = current_val argmin = current_state forward_messages[time_step][goal_state] = min_val traceback_messages[time_step][goal_state] = argmin return forward_messages, traceback_messages
def particle_filter(prior_distribution, transition_model, observation_model, observations, num_particles=500): """ Inputs ------ prior_distribution: a distribution over states transition_model: a function that takes a hidden state and returns a Distribution for the next state observation_model: a function that takes a hidden state and returns a Distribution for the observation from that hidden state observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ This function is a Python generator! It calculates outputs "on demand"; the i-th output should be the marginal distribution for time step i. Please see these two pages on Python generators for more information: http://wiki.python.org/moin/Generators http://docs.python.org/2/tutorial/classes.html#generators For an example of a Python generator function, check out the function forward (for the forward algorithm) below. Key point: The output is *not* a list, so you can't use brackets [] to access the i-th entry. We've provided skeletal code so that it should be clear what you need to fill in. """ num_time_steps = len(observations) # time step 0 initial_marginal = robot.Distribution() # TODO: Your code here raise NotImplementedError yield initial_marginal # do not modify this line # remaining time steps for n in range(1, num_time_steps): marginal = robot.Distribution() # TODO: Your code here raise NotImplementedError yield marginal # do not modify this line
def generate_uniform_message(): message = robot.Distribution() for state in all_possible_hidden_states: message[state] = 1 / len(all_possible_hidden_states) return message
def precompute_singleton_potentials(observations): num_time_steps = len(observations) phi = [None] * num_time_steps reverse_observation_model = compute_reverse_observation_model() for time_step in range(num_time_steps): phi[time_step] = robot.Distribution() observation = observations[time_step] if not observation: phi[time_step] = uniform_potential() continue for state in reverse_observation_model[observation]: potential = reverse_observation_model[observation][state] # time step 0 takes into account the prior dist. if time_step == 0: potential *= prior_distribution[state] if potential > 0: phi[time_step][state] = -careful_log(potential) return phi
def uniform_potential(): potential = robot.Distribution() for state in all_possible_hidden_states: potential[state] = 0.1 return potential
def initial_distribution(): # returns a Distribution for the initial hidden state prior = robot.Distribution() prior['F'] = 0.50 prior['B'] = 0.50 prior.renormalize() return prior
def rev_transition_model(curState): # given a hidden state, return the Distribution for the prev hidden state revModel = robot.Distribution() for x in all_possible_hidden_states: tmp = transition_model(x) revModel[x] = tmp[curState] #revModel.renormalize() return revModel
def buildPhi(y): phi_X = robot.Distribution() for x in all_possible_hidden_states: if y is None: phi_X[x] = 1 else: yPoss = observation_model(x) phi_X[x] = yPoss[y] return phi_X
def backward(alphaIn, phi_x, y): """compute the next forward message""" alphaPhi_X = robot.Distribution() for x, alphaX in alphaIn.items(): yProb = phi_x[x] tmpProd = yProb * alphaX if tmpProd > 0: alphaPhi_X[x] = tmpProd # compute alpha out alphaOut = robot.Distribution() for x, alphaPhi in alphaPhi_X.items(): x2Poss = rev_transition_model(x) # multiply and add x2Poss to o/p for x2Key, x2pVal in x2Poss.items(): alphaOut[x2Key] += x2pVal * alphaPhi #print(alphaOut) return alphaOut
def observation_model(state): observed_states = robot.Distribution() if state == 'F': observed_states['H'] = 0.5 observed_states['T'] = 0.5 elif state == 'B': observed_states['H'] = 0.25 observed_states['T'] = 0.75 observed_states.renormalize() return observed_states
def mostLikely(phiLast, msgHat): finNode = robot.Distribution() for key in phiLast.keys(): val2 = msgHat[key] if val2 == 0: val2 = np.inf finNode[key] = neglog(phiLast[key]) + val2 minVal, minKey = myDictMin(finNode) mHat = minVal tBack = minKey return mHat, tBack
def transition_model(state): # given a hidden state, return the Distribution for the next hidden state next_states = robot.Distribution() # we can always stay where we are if state == 'F': next_states['F'] = .75 next_states['B'] = .25 elif state == 'B': next_states['F'] = .25 next_states['B'] = .75 next_states.renormalize() return next_states
def compute_reverse_transition_model(): # initialize final distribution reverse_transitions = {} for state in all_possible_hidden_states: reverse_transitions[state] = robot.Distribution() # collect (unnormalized) reverse transition probabilities for start_state in all_possible_hidden_states: possible_goal_states = transition_model(start_state) for goal_state in possible_goal_states: reverse_transitions[goal_state][start_state] = \ possible_goal_states[goal_state] return reverse_transitions
def compute_log_reverse_observation_model(): # initialize final distribution reverse_observations = {} for observation in all_possible_observed_states: reverse_observations[observation] = robot.Distribution() # collect (unnormalized) observation-probabilities for state in all_possible_hidden_states: possible_observations = observation_model(state) for observation in possible_observations: reverse_observations[observation][state] = \ careful_log(possible_observations[observation]) return reverse_observations
def forward_backward(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ num_time_steps = len(observations) forward_messages = [None] * num_time_steps forward_messages[0] = prior_distribution reverse_observation_model = compute_reverse_observation_model() # Compute the forward messages #print(" - compute forward messages...") # apply for every timestep for time_step in range(1, num_time_steps): forward_messages[time_step] = robot.Distribution() observation = observations[time_step - 1] # loop through all possible values of next state for goal_state in all_possible_hidden_states: probability_sum = 0 if observation: # loop through all non-zero values of previous state for current_state in reverse_observation_model[observation]: # multiply obs-model, trans-model, message probability_sum += \ reverse_observation_model[observation][current_state] * \ transition_model(current_state)[goal_state] * \ forward_messages[time_step - 1][current_state] else: for current_state in forward_messages[time_step - 1]: probability_sum += \ transition_model(current_state)[goal_state] * \ forward_messages[time_step - 1][current_state] if probability_sum > 0: forward_messages[time_step][goal_state] = probability_sum # Compute the backward messages # note: order will be reverse! backward_messages[0] will hold the message # coming in to the *last* hidden state #print(" - compute backward messages") backward_messages = [None] * num_time_steps backward_messages[0] = generate_uniform_message() reverse_transition_model = compute_reverse_transition_model() # apply for every time step for time_step in range(1, num_time_steps): backward_messages[time_step] = robot.Distribution() observation = observations[num_time_steps - time_step] # create a table-entry for every possible value for goal_state in all_possible_hidden_states: probability_sum = 0 if observation: # look only at non-zero values allowed by the observation for current_state in reverse_observation_model[observation]: # multiply obs-model, reverse-trans-model, back-message probability_sum += \ reverse_observation_model[observation][current_state] * \ reverse_transition_model[current_state][goal_state] * \ backward_messages[time_step - 1][current_state] else: for current_state in backward_messages[time_step - 1]: probability_sum += \ reverse_transition_model[current_state][goal_state] * \ backward_messages[time_step - 1][current_state] if probability_sum > 0: backward_messages[time_step][goal_state] = probability_sum # Compute the marginals #print(" - compute marginals") marginals = [None] * num_time_steps # remove this for time_step in range(0, num_time_steps): marginals[time_step] = robot.Distribution() observation = observations[time_step] for state in all_possible_hidden_states: if observation: probability = \ reverse_observation_model[observation][state] * \ forward_messages[time_step][state] * \ backward_messages[num_time_steps - 1 - time_step][state] else: probability = \ forward_messages[time_step][state] * \ backward_messages[num_time_steps - 1 - time_step][state] if probability > 0: marginals[time_step][state] = probability for px in marginals: px.renormalize() return marginals
## load wiki examples #import dnaExample as dna #all_possible_hidden_states = dna.get_all_hidden_states() #all_possible_observed_states = dna.get_all_observed_states() #prior_distribution = dna.initial_distribution() #transition_model = dna.transition_model #observation_model = dna.observation_model #observationsStr = 'GGCACTGAA'.lower() #observations = [x for x in observationsStr] # %% computing m12 num_time_steps = len(observations) phi1 = robot.Distribution() obs1 = buildPhi(observations[0]) for x in obs1.keys(): tmpProd= obs1[x] * prior_distribution[x] if tmpProd > 0: phi1[x] = tmpProd # compute message 1 to 2 m12 = robot.Distribution() tBack12 = {} phi_use = phi1 for x2_state in all_possible_hidden_states: x1_collect = {} for x1_state, x1_value in phi_use.items(): x2_x1_trans = transition_model(x1_state) trans_value = x2_x1_trans[x2_state]
def baum_welch(all_possible_hidden_states, all_possible_observed_states, observations): """ Inputs ------ all_possible_hidden_states: a list of possible hidden states all_possible_observed_states: a list of possible observed states observations: a list of observations, one per hidden state (in this problem, we don't have any missing observations) Output ------ A transition model and an observation model """ ### Initialize transition_model = robot.uniform_transition_model observation_model = robot.spread_observation_model initial_state_distribution = robot.initial_distribution() num_time_steps = len(observations) convergence_reached = False while not convergence_reached: # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv ### YOUR CODE HERE: Estimate marginals & pairwise marginals pairwise_marginals = [None] * num_time_steps marginals = [None] * num_time_steps # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ### Learning: estimate model parameters # maps states to distributions over possible next states transition_model_dict = {} # maps states to distributions over possible observations observation_model_dict = {} # initial initial_state_distribution = robot.Distribution() # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv # YOUR CODE HERE: Use the estimated marginals & pairwise marginals to # estimate the parameters. # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ new_transition_model = robot.make_transition_model(transition_model_dict) new_obs_model = robot.make_observation_model(observation_model_dict) ### Check for convergence if check_convergence(all_possible_hidden_states, transition_model, new_transition_model): convergence_reached = True transition_model = new_transition_model observation_model = new_obs_model return (transition_model, observation_model, initial_state_distribution, marginals)
def forward_backward(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ # ------------------------------------------------------------------------- # YOUR CODE GOES HERE # num_time_steps = len(observations) num_hidden_states = len(all_possible_hidden_states) # TODO: Compute the forward messages forward_messages = np.zeros((num_time_steps, num_hidden_states)) for i in range(num_hidden_states): forward_messages[0, i] = prior_distribution[all_possible_hidden_states[i]] # Create a dictionary to index observed states observation_index_dict = { all_possible_observed_states[i]: i for i in range(len(all_possible_observed_states)) } # Create a transition dictionary based on all possible hidden states transition_dict = {} for i in all_possible_hidden_states: transition_dict[i] = {} transitions = transition_model(i) for j in all_possible_hidden_states: if j in transitions: transition_dict[i][j] = transitions[j] else: transition_dict[i][j] = 0 # Convert transition dictionary to a numpy array A = np.array([[transition_dict[i][j] for j in all_possible_hidden_states] for i in all_possible_hidden_states]) # Create an emission dictionary based on all possible hidden states emission_dict = {} for i in all_possible_hidden_states: emission_dict[i] = {} obs = observation_model(i) for j in all_possible_observed_states: if j in obs: emission_dict[i][j] = obs[j] else: emission_dict[i][j] = 0 # Convert emission dictionary to a numpy array B = np.array([[emission_dict[i][j] for j in all_possible_observed_states] for i in all_possible_hidden_states]) # Iterate through observations and calculate forward messages for o in range(num_time_steps - 1): if observations[o] == None: forward_messages[o + 1, :] = (forward_messages[o]) @ A else: obs = observation_index_dict[observations[o]] forward_messages[o + 1, :] = (B[:, obs] * forward_messages[o]) @ A # TODO: Compute the backward messages backward_messages = np.zeros( (num_time_steps, len(all_possible_hidden_states))) backward_messages[num_time_steps - 1, :] = 1 / num_hidden_states # Iterate through observations in reverse order and calculate backwards messages for o in reversed(range(1, num_time_steps)): if observations[o] == None: backward_messages[o - 1, :] = (backward_messages[o]) @ A.T else: obs = observation_index_dict[observations[o]] backward_messages[o - 1, :] = (B[:, obs] * backward_messages[o]) @ A.T # TODO: Compute the marginals marginals = [None] * num_time_steps # remove this marginal_matrix = np.zeros((num_time_steps, num_hidden_states)) for o in range(num_time_steps): if observations[o] == None: marginal = forward_messages[o, :] * backward_messages[o, :] else: obs = observation_index_dict[observations[o]] marginal = forward_messages[o, :] * backward_messages[ o, :] * B[:, obs] marginal_dist = robot.Distribution() for m in range(num_hidden_states): marginal_dist[all_possible_hidden_states[m]] = marginal[m] marginal_dist.renormalize() marginals[o] = marginal_dist return marginals
def forward_backward(all_possible_hidden_states, all_possible_observed_states, prior_distribution, transition_model, observation_model, observations): """ Inputs ------ all_possible_hidden_states: a list of possible hidden states all_possible_observed_states: a list of possible observed states prior_distribution: a distribution over states transition_model: a function that takes a hidden state and returns a Distribution for the next state observation_model: a function that takes a hidden state and returns a Distribution for the observation from that hidden state observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ num_time_steps = len(observations) marginals = [None] * num_time_steps alphas = [] print "Calculating forward messages..." for i in range(num_time_steps - 1): X = robot.Distribution() for a in all_possible_hidden_states: msum = 0 if observations[i] == None: for b in all_possible_hidden_states: if i == 0: msum += prior_distribution[b] * transition_model(b)[a] else: msum += alphas[i - 1][b] * transition_model(b)[a] else: for b in possible_states(observations[i]): if i == 0: msum += prior_distribution[b] * observation_model(b)[ observations[i]] * transition_model(b)[a] else: msum += alphas[i - 1][b] * observation_model(b)[ observations[i]] * transition_model(b)[a] X[a] = msum alphas.append(X) betas = [] print "Calculating backward messages..." for i in range(num_time_steps - 1): X = robot.Distribution() for a in all_possible_hidden_states: msum = 0 if observations[num_time_steps - 1 - i] is None: for b in all_possible_hidden_states: if i == 0: msum += transition_model(a)[b] else: msum += betas[i - 1][b] * transition_model(a)[b] else: for b in possible_states(observations[num_time_steps - 1 - i]): if i == 0: msum += observation_model(b)[observations[ num_time_steps - 1 - i]] * transition_model(a)[b] else: msum += betas[i - 1][b] * observation_model(b)[ observations[num_time_steps - 1 - i]] * transition_model(a)[b] X[a] = msum betas.append(X) betas = betas[::-1] for i in range(num_time_steps): X = robot.Distribution() for a in all_possible_hidden_states: if observations[i] is None: if i == 0: X[a] = prior_distribution[a] * betas[i][a] elif i == num_time_steps - 1: X[a] = alphas[i - 1][a] else: X[a] = alphas[i][a] * betas[i][a] else: if i == 0: X[a] = prior_distribution[a] * observation_model(a)[ observations[i]] * betas[i][a] elif i == num_time_steps - 1: X[a] = alphas[i - 1][a] * observation_model(a)[observations[i]] else: X[a] = alphas[i][a] * observation_model(a)[ observations[i]] * betas[i][a] X.renormalize() marginals[i] = X return marginals
def Viterbi(all_possible_hidden_states, all_possible_observed_states, prior_distribution, transition_model, observation_model, observations): """ Inputs ------ See the list inputs for the function forward_backward() above. Output ------ A list of esimated hidden states, each encoded as a tuple (<x>, <y>, <action>) """ num_time_steps = len(observations) estimated_hidden_states = [None] * num_time_steps # remove this messages = [] tracebacks = [] for i in range(num_time_steps - 1): X = robot.Distribution() Y = robot.Distribution() for a in all_possible_hidden_states: m_max = 0 max_state = None if observations[i] is not None: for b in possible_states(observations[i]): if i == 0: temp = prior_distribution[b] * observation_model(b)[ observations[i]] * transition_model(b)[a] else: temp = messages[i - 1][b] * observation_model(b)[ observations[i]] * transition_model(b)[a] if temp >= m_max: m_max = temp max_state = b else: for b in all_possible_hidden_states: if i == 0: temp = prior_distribution[b] * transition_model(b)[a] else: temp = messages[i - 1][b] * transition_model(b)[a] if temp >= m_max: m_max = temp max_state = b X[a] = m_max Y[a] = max_state messages.append(X) tracebacks.append(Y) x_hat = None m_max = 0 for a in all_possible_hidden_states: temp = messages[98][a] * observation_model(a)[observations[99]] if temp > m_max: m_max = temp x_hat = a estimated_hidden_states[99] = x_hat for i in range(num_time_steps - 2, -1, -1): x_hat = tracebacks[i][x_hat] estimated_hidden_states[i] = x_hat return estimated_hidden_states
def second_best(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of esimated hidden states, each encoded as a tuple (<x>, <y>, <action>) """ # ------------------------------------------------------------------------- # YOUR CODE GOES HERE # num_time_steps = len(observations) # Basically for each (possible) hidden state at time step i, we need to # keep track of the best previous hidden state AND the second best # previous hidden state--where we need to keep track of TWO back pointers # per (possible) hidden state at each time step! messages = [] # best values so far messages2 = [] # second-best values so far back_pointers = [] # per time step per hidden state, we now need # *two* back-pointers # ------------------------------------------------------------------------- # Fold observations into singleton potentials # phis = [] # phis[n] is the singleton potential for node n for n in range(num_time_steps): potential = robot.Distribution() observed_state = observations[n] if n == 0: for hidden_state in prior_distribution: value = prior_distribution[hidden_state] if observed_state is not None: value *= observation_model(hidden_state)[observed_state] if value > 0: # only store entries with nonzero prob. potential[hidden_state] = value else: for hidden_state in all_possible_hidden_states: if observed_state is None: # singleton potential should be identically 1 potential[hidden_state] = 1. else: value = observation_model(hidden_state)[observed_state] if value > 0: # only store entries with nonzero prob. potential[hidden_state] = value phis.append(potential) # ------------------------------------------------------------------------- # Forward pass # # handle initial time step differently initial_message = {} for hidden_state in prior_distribution: value = -careful_log(phis[0][hidden_state]) if value < np.inf: # only store entries with nonzero prob. initial_message[hidden_state] = value messages.append(initial_message) initial_message2 = {} # there is no second-best option messages2.append(initial_message2) # rest of the time steps for n in range(1, num_time_steps): prev_message = messages[-1] prev_message2 = messages2[-1] new_message = {} new_message2 = {} new_back_pointers = {} # need to store 2 per possible hidden state # only look at possible hidden states given observation for hidden_state in phis[n]: values = [] # each entry in values will be a tuple of the form: # (<value>, <previous hidden state>, # <which back pointer we followed>), # where <which back pointer we followed> is 0 (best back pointer) # or 1 (second-best back pointer) # iterate through best previous values for prev_hidden_state in prev_message: value = prev_message[prev_hidden_state] - \ careful_log(transition_model(prev_hidden_state)[ hidden_state]) - \ careful_log(phis[n][hidden_state]) if value < np.inf: # only store entries with nonzero prob. values.append((value, prev_hidden_state, 0)) # also iterate through second-best previous values for prev_hidden_state in prev_message2: value = prev_message2[prev_hidden_state] - \ careful_log(transition_model(prev_hidden_state)[ hidden_state]) - \ careful_log(phis[n][hidden_state]) if value < np.inf: # only store entries with nonzero prob. values.append((value, prev_hidden_state, 1)) if len(values) > 0: # this part could actually be sped up by not using a sorting # algorithm... sorted_values = sorted(values, key=lambda x: x[0]) best_value, best_prev_hidden_state, which_back_pointer = \ sorted_values[0] # for the best value, the back pointer should *always* be 0, # meaning that we follow the best back pointer and not the # second best if len(values) > 1: best_value2, best_prev_hidden_state2, which_back_pointer2\ = sorted_values[1] else: best_value2 = np.inf best_prev_hidden_state2 = None which_back_pointer2 = None new_message[hidden_state] = best_value new_message2[hidden_state] = best_value2 new_back_pointers[hidden_state] = \ ((best_prev_hidden_state, which_back_pointer), (best_prev_hidden_state2, which_back_pointer2)) messages.append(new_message) messages2.append(new_message2) back_pointers.append(new_back_pointers) # ------------------------------------------------------------------------- # Backward pass (follow back-pointers) # # handle last time step differently values = [] for hidden_state, value in messages[-1].items(): values.append((value, hidden_state, 0)) for hidden_state, value in messages2[-1].items(): values.append((value, hidden_state, 1)) divergence_time_step = -1 if len(values) > 1: # this part could actually be sped up by not using a sorting # algorithm... sorted_values = sorted(values, key=lambda x: x[0]) second_best_value, hidden_state, which_back_pointer = sorted_values[1] estimated_hidden_states = [hidden_state] # rest of the time steps for t in range(num_time_steps - 2, -1, -1): hidden_state, which_back_pointer = \ back_pointers[t][hidden_state][which_back_pointer] estimated_hidden_states.insert(0, hidden_state) else: # this happens if there isn't a second best option, which should mean # that the only possible option (the MAP estimate) is the only # solution with 0 error estimated_hidden_states = [None] * num_time_steps return estimated_hidden_states
def forward_backward(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ # ------------------------------------------------------------------------- # YOUR CODE GOES HERE # num_time_steps = len(observations) # forward_messages = [None] * num_time_steps # forward_messages[0] = prior_distribution initial_dist = np.full(440, 1. / 440) transition = np.zeros((440, 440)) obs_matrix = np.zeros((440, 96)) forward_messages = np.zeros((num_time_steps, 440)) backward_messages = np.zeros((num_time_steps, 440)) backward_messages[num_time_steps - 1, :] = np.full(440, 1. / 440) for i, x in enumerate(all_possible_hidden_states): forward_messages[0, i] = prior_distribution[all_possible_hidden_states[i]] for k, v in transition_model(x).items(): transition[i, hidden_dict[k]] = v for k, v in observation_model(x).items(): obs_matrix[i, obs_dict[k]] = v global trans_mat, emit_mat, observes trans_mat = transition emit_mat = obs_matrix observes = observations emission = [] for i, obs in enumerate(observations[:-1]): if obs: emission = emit_mat[:, obs_dict[obs]] else: emission = np.ones(emit_mat[:, 1].shape) forward_messages[i + 1, :] = (emission * forward_messages[i]) @ trans_mat forward_messages[i + 1, :] = forward_messages[ i + 1, :] / forward_messages[i + 1, :].sum() for i, obs in enumerate(observations[:0:-1]): if obs: emission = emit_mat[:, obs_dict[obs]] else: emission = np.ones(emit_mat[:, 1].shape) backward_messages[-(i + 2), :] = ( emission * backward_messages[-(i + 1)]) @ trans_mat.T backward_messages[-(i + 2), :] = backward_messages[ -(i + 2), :] / backward_messages[-(i + 2), :].sum() global alphas, betas alphas = forward_messages betas = backward_messages dist_list = [] for i, obs in enumerate(observations): if obs: emission = emit_mat[:, obs_dict[obs]] else: emission = np.ones(emit_mat[:, 1].shape) marginal = alphas[i] * betas[i] * emission marginal = marginal / marginal.sum() temp_dist = robot.Distribution() for ind, val in enumerate(marginal): temp_dist[all_possible_hidden_states[ind]] = val dist_list.append(temp_dist) return dist_list
def forward_backward(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ num_time_steps = len(observations) forward_messages = [None] * num_time_steps B = {} state_from_observation = {} #dictionary {observation: [state1, state2, ...]} - for each observation all states from which it can be observed for state in all_possible_hidden_states: model = observation_model(state) for obs in model.keys(): if obs not in state_from_observation: state_from_observation[obs] = [] state_from_observation[obs].append(state) B[(state, obs)] = model[obs] #previous_states = {} next_states = {} A = {} for state in all_possible_hidden_states: transition_matrix = transition_model(state) if state not in next_states: next_states[state] = [] for state_next in transition_matrix.keys(): #if state_next not in previous_states: # previous_states[state_next] = [] #previous_states[state_next].append(state) next_states[state].append(state_next) A[(state, state_next)] = transition_matrix[state_next] #computing forward messages states_amount = len(all_possible_hidden_states) #forward_messages[0] = prior_distribution #forward_messages[0] = robot.Distribution() forward_messages[0] = prior_distribution for i in range(1, num_time_steps, 1): observation = observations[i-1] forward_messages[i] = robot.Distribution() if observation: for state_next in all_possible_hidden_states: for state_current in state_from_observation[observation]: m = forward_messages[i-1].get(state_current,0) * A.get((state_current,state_next),0) * B.get((state_current, observation), 0) if m > 0: forward_messages[i][state_next] += m else: for state_next in all_possible_hidden_states: for state_current in forward_messages[i-1].keys(): m = forward_messages[i-1].get(state_current,0) * A.get((state_current,state_next),0) if m > 0: forward_messages[i][state_next] += m forward_messages[i].renormalize() #print i, forward_messages[i] backward_messages = [None] * num_time_steps backward_messages[num_time_steps-1] = robot.Distribution() for state in all_possible_hidden_states: backward_messages[num_time_steps-1][state] = 1./states_amount for i in range(num_time_steps-2, -1, -1): observation = observations[i+1] backward_messages[i] = robot.Distribution() if observation: for state_current in all_possible_hidden_states: for state_next in state_from_observation[observation]: m = B.get((state_next, observation), 0) * A.get((state_current, state_next), 0) * backward_messages[i+1].get(state_next, 0) if m > 0: backward_messages[i][state_current] += m else: for state_current in all_possible_hidden_states: for state_next in backward_messages[i+1].keys(): m = A.get((state_current, state_next), 0) * backward_messages[i+1].get(state_next, 0) if m > 0: backward_messages[i][state_current] += m backward_messages[i].renormalize() #print observation, i, backward_messages[i] marginals = [None] * num_time_steps # remove this marginals[0] = robot.Distribution() for state in backward_messages[0]: m = backward_messages[0].get(state,0) * B.get((state, observations[0]), 0) * prior_distribution.get(state, 0) if m > 0: marginals[0][state] = m marginals[0].renormalize() marginals[num_time_steps-1] = robot.Distribution() observation = observations[num_time_steps-1] for state in forward_messages[num_time_steps-1]: if observation: m = forward_messages[num_time_steps-1].get(state,0) * B.get((state, observation), 0) else: m = forward_messages[num_time_steps-1].get(state,0) if m > 0: marginals[num_time_steps-1][state] = m marginals[num_time_steps-1].renormalize() for i in range(1, num_time_steps-1, 1): observation = observations[i] marginals[i] = robot.Distribution() if observation: for state in state_from_observation[observation]: m = B.get((state, observation), 0) * forward_messages[i].get(state,0) * backward_messages[i].get(state,0) if m > 0: marginals[i][state] = m else: for state in backward_messages[i]: m = forward_messages[i].get(state,0) * backward_messages[i].get(state,0) if m > 0: marginals[i][state] = m marginals[i].renormalize() #print observation_model((3,3,'stay')) #print transition_model((3,3,'stay')) #print observation_model((3,3,'up')) #print transition_model((3,3,'up')) return marginals
def forward_backward(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ # ------------------------------------------------------------------------- # YOUR CODE GOES HERE # num_time_steps = len(observations) forward_messages = [None] * num_time_steps forward_messages[0] = prior_distribution # TODO: Compute the forward messages for i in range(num_time_steps - 1): forward_messages[i+1] = robot.Distribution() if (observations[i] != None): current_obs = posterior_observed_matrix[observations[i]] dist_to_add = robot.Distribution() for tup in current_obs: key_a = [a for a, b in forward_messages[i].items() if a[0:2] == tup] if key_a != []: for item in key_a: new_prob = forward_messages[i][item] * current_obs[tup] temp_dist = transition_model(item) for k in temp_dist: new_val = temp_dist[k] * new_prob dist_to_add[k] += new_val else: dist_to_add = robot.Distribution() for tup in forward_messages[i]: temp_dist = transition_model(tup) for key in temp_dist: dist_to_add[key] += temp_dist[key] * forward_messages[i][tup] forward_messages[i+1].update(dist_to_add) forward_messages[i+1].renormalize() #print ('complete') #print (forward_messages[3]) backward_messages = [None] * num_time_steps backward_messages[-1] = robot.Distribution() for state in all_possible_hidden_states: backward_messages[-1][state] = 1.0 / len(all_possible_hidden_states) # Because there is no posterior_transition_model, I'm forced to # either a) make one myself or b) fill out an entire transition # matrix just so I can transpose it. B is a tremendous waste of # space but much faster to code, so I'm trying it. for i in reversed(range(num_time_steps - 1)): backward_messages[i] = robot.Distribution() if (observations[i+1] != None): next_obs = posterior_observed_matrix[observations[i+1]] dist_to_add = robot.Distribution() for tup in next_obs: key_a = [a for a, b in backward_messages[i+1].items() if a[0:2] == tup] if key_a != []: for item in key_a: new_prob = backward_messages[i+1][item] * next_obs[tup] temp_dist = posterior_transition_matrix[item] for k in temp_dist: new_val = temp_dist[k] * new_prob dist_to_add[k] += new_val else: dist_to_add = robot.Distribution() for tup in backward_messages[i+1]: temp_dist = posterior_transition_matrix[tup] for key in temp_dist: dist_to_add[key] += temp_dist[key] * backward_messages[i+1][tup] backward_messages[i].update(dist_to_add) backward_messages[i].renormalize() #print (backward_messages[3]) marginals = [None] * num_time_steps # remove this # TODO: Compute the marginals for i in range(num_time_steps): marginals[i] = robot.Distribution() shared_keys = robot.Distribution() for key in forward_messages[i]: if (key in backward_messages[i]): shared_keys[key] = forward_messages[i][key] * backward_messages[i][key] if (observations[i] != None): current_obs = posterior_observed_matrix[observations[i]] for tup in current_obs: key_a = [a for a, b in shared_keys.items() if a[0:2] == tup] if key_a != []: for item in key_a: new_prob = current_obs[tup] * shared_keys[item] marginals[i][item] = new_prob else: marginals[i].update(shared_keys) marginals[i].renormalize() return marginals
# - transition_model: a function that takes a hidden state and returns a # Distribution for the next state # - observation_model: a function that takes a hidden state and returns a # Distribution for the observation from that hidden state all_possible_hidden_states = robot.get_all_hidden_states() all_possible_observed_states = robot.get_all_observed_states() prior_distribution = robot.initial_distribution() transition_model = robot.transition_model observation_model = robot.observation_model observed_matrix = {} for state in all_possible_observed_states: x,y = state new_tuple = (x,y,'stay') observed_matrix[state] = robot.Distribution() observed_matrix[state] = observation_model(new_tuple) posterior_observed_matrix = {} for i in observed_matrix: for j in observed_matrix[i]: if (j not in posterior_observed_matrix): posterior_observed_matrix[j] = robot.Distribution() posterior_observed_matrix[j][i] = observed_matrix[i][j] transition_matrix = {} for state in all_possible_hidden_states: transition_matrix[state] = robot.Distribution() transition_matrix[state] = transition_model(state)
def Viterbi(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of esimated hidden states, each encoded as a tuple (<x>, <y>, <action>) """ num_time_steps = len(observations) if num_time_steps == 1: return MAP_estimate(observations) estimated_hidden_states = [None] * num_time_steps # fold in observations phi = precompute_singleton_potentials(observations) # compute forward-messages forward_messages = [None] * (num_time_steps - 1) traceback_messages = [None] * (num_time_steps - 1) for time_step in range(num_time_steps - 1): forward_messages[time_step] = robot.Distribution() traceback_messages[time_step] = robot.Distribution() for goal_state in all_possible_hidden_states: min_val = np.inf argmin = None for current_state in phi[time_step]: current_val = \ phi[time_step][current_state] + \ -careful_log(transition_model(current_state)[goal_state]) if time_step > 0: current_val += forward_messages[time_step - 1][current_state] if current_val < min_val: min_val = current_val argmin = current_state forward_messages[time_step][goal_state] = min_val traceback_messages[time_step][goal_state] = argmin # compute maximum value at the root min_val = np.inf argmin = None for state in phi[num_time_steps - 1]: current_val = \ phi[num_time_steps - 1][state] + \ forward_messages[num_time_steps - 2][state] if current_val < min_val: min_val = current_val argmin = state estimated_hidden_states[num_time_steps - 1] = argmin # follow the traceback for time_step in range(num_time_steps - 2, -1, -1): estimated_hidden_states[time_step] = \ traceback_messages[time_step][estimated_hidden_states[time_step + 1]] return estimated_hidden_states
def myneglog(pDist): pDist = pDist.copy() pOut = robot.Distribution() for key, val in pDist.items(): pOut[key] = -1*careful_log(val) return pOut
def Viterbi(all_possible_hidden_states, all_possible_observed_states, prior_distribution, transition_model, observation_model, observations): num_time_steps = len(observations) noinfo = robot.Distribution() for k in all_possible_hidden_states: noinfo[k] = 1.0 epyx = {} for y in all_possible_observed_states: epyx[y] = robot.Distribution() for x in all_possible_hidden_states: v = observation_model(x)[y] if v != 0: epyx[y][x] = v epyx[y].renormalize() phiprime = [None] * len(observations) phiprime[0] = robot.Distribution(prior_distribution.copy()) for i in range(1, len(observations)): phiprime[i] = robot.Distribution(noinfo.copy()) for i in range(len(observations)): if observations[i] != None: for k in noinfo: phiprime[i][k] = phiprime[i][k] * epyx[observations[i]][k] phiprime[i].renormalize() for i in range(len(observations)): for k in noinfo: if phiprime[i][k] == 0: del phiprime[i][k] num_time_steps = len(observations) forward_messages = [None] * num_time_steps table = [None] * num_time_steps for i in range(num_time_steps): forward_messages[i] = robot.Distribution() table[i] = robot.Distribution() forward_messages[0] = noinfo.copy() for i in range(1, num_time_steps): mf = robot.Distribution(forward_messages[i - 1].copy()) for k in mf: mf[k] = mf[k] * phiprime[i - 1][k] #fold for k in mf.keys(): if mf[k] == 0: del mf[k] #fold mf.renormalize() for k in mf: nexf = transition_model(k) for kn in nexf: if forward_messages[i].get(kn, 0) < nexf[kn] * mf[k]: forward_messages[i][kn] = nexf[kn] * mf[k] table[i][kn] = k forward_messages[i].renormalize() xt = robot.Distribution() for k in forward_messages[-1]: xt[k] = forward_messages[-1][k] * phiprime[-1].get(k, 0) xmap = [] maxval = max(xt.values()) for k in xt: if xt[k] == maxval: x = k xmap.append(x) for i in range(1, num_time_steps): j = num_time_steps - i xpre = table[j][xmap[-1]] xmap.append(xpre) xmap.reverse() estimated_hidden_states = xmap return estimated_hidden_states
def forward_backward(observations): """ Input ----- observations: a list of observations, one per hidden state (a missing observation is encoded as None) Output ------ A list of marginal distributions at each time step; each distribution should be encoded as a Distribution (see the Distribution class in robot.py and see how it is used in both robot.py and the function generate_data() above, and the i-th Distribution should correspond to time step i """ # ------------------------------------------------------------------------- # YOUR CODE GOES HERE # num_time_steps = len(observations) # forward_messages[i] is P([state at i] and y's before i) forward_messages = [prior_distribution] for t in range(num_time_steps - 1): # Incorporate the t-th observation. if observations[t] is not None: prob_given_y = robot.Distribution() for state, prob in forward_messages[t].items(): prob_given_y[state] \ = prob * observation_model(state)[observations[t]] prob_given_y.renormalize() else: prob_given_y = forward_messages[t] # Take a step forward in time, using the transition model. next_dist = robot.Distribution() for state, prob in prob_given_y.items(): for new_state, trans_prob in transition_model(state).items(): next_dist[new_state] += prob * trans_prob next_dist.renormalize() forward_messages.append(next_dist) # Make the reverse transition dictionary, to make backward messages # easier. backwards_transitions = collections.defaultdict(robot.Distribution) for state_1 in all_possible_hidden_states: for state_2, prob in transition_model(state_1).items(): backwards_transitions[state_2][state_1] += prob # backward messages[i] is P(y's after i | state at i) backward_messages = [None] * num_time_steps uniform = robot.Distribution() for s in all_possible_hidden_states: uniform[s] = 1 uniform.renormalize() backward_messages[-1] = uniform for t in range(num_time_steps - 1, 0, -1): if observations[t] is not None: prob_given_y = robot.Distribution() for state, prob in backward_messages[t].items(): prob_given_y[state] = prob * \ observation_model(state)[observations[t]] prob_given_y.renormalize() else: prob_given_y = backward_messages[t] prev_dist = robot.Distribution() for state, prob in prob_given_y.items(): for past_state, trans_prob in backwards_transitions[state].items(): prev_dist[past_state] += prob * trans_prob prev_dist.renormalize() backward_messages[t - 1] = prev_dist # Finally, compute marginals. marginals = [] for t in range(num_time_steps): marginal_t = robot.Distribution() for state in all_possible_hidden_states: product = forward_messages[t][state] * backward_messages[t][state] if observations[t] is not None: product *= observation_model(state)[observations[t]] if product > 0: marginal_t[state] = product marginal_t.renormalize() marginals.append(marginal_t) return marginals