Ejemplo n.º 1
0
 def backward_log_probabilities(self, sequence, normalize=True):
     """We override this to provide a faster implementation.
     
     @see: forward_log_probability
     
     Returns a numpy 2d array instead of a list of lists.
     
     """
     T = len(sequence)
     N = len(self.label_dom)
     beta = numpy.zeros((T, N), numpy.float64)
     
     # Initialize
     beta[T-1, :] = numpy.log2(1.0/N)
     
     # Iterate backwards over the other timesteps
     for t in range(T-2, -1, -1):
         for i,si in enumerate(self.label_dom):
             # Multiply each next state's prob by the transition prob 
             #  from this state to that and the emission prob in that next 
             #  state
             log_probs = [
                 beta[t+1, j] + self.transition_log_probability(sj, si) + \
                     self.emission_log_probability(sequence[t+1], sj) \
                     for j,sj in enumerate(self.label_dom)]
             beta[t, i] = sum_logs(log_probs)
         # Normalize by dividing all values by the total probability
         if normalize:
             total = sum_logs(beta[t,:])
             for j in range(N):
                 beta[t,j] -= total
     return beta
Ejemplo n.º 2
0
    def backward_log_probabilities(self, sequence, normalize=True):
        """We override this to provide a faster implementation.
        
        @see: forward_log_probability
        
        Returns a numpy 2d array instead of a list of lists.
        
        """
        T = len(sequence)
        N = len(self.label_dom)
        beta = numpy.zeros((T, N), numpy.float64)

        # Initialize
        beta[T - 1, :] = numpy.log2(1.0 / N)

        # Iterate backwards over the other timesteps
        for t in range(T - 2, -1, -1):
            for i, si in enumerate(self.label_dom):
                # Multiply each next state's prob by the transition prob
                #  from this state to that and the emission prob in that next
                #  state
                log_probs = [
                    beta[t+1, j] + self.transition_log_probability(sj, si) + \
                        self.emission_log_probability(sequence[t+1], sj) \
                        for j,sj in enumerate(self.label_dom)]
                beta[t, i] = sum_logs(log_probs)
            # Normalize by dividing all values by the total probability
            if normalize:
                total = sum_logs(beta[t, :])
                for j in range(N):
                    beta[t, j] -= total
        return beta
Ejemplo n.º 3
0
 def forward_log_probabilities(self, sequence, normalize=True, array=False):
     """We override this to provide a faster implementation.
     
     It might also be possible to speed up the superclass' implementation 
     using numpy, but it's easier here because we know we're using an 
     HMM, not a higher-order ngram.
     
     This is based on the fwd prob calculation in NLTK's HMM implementation.
     
     @type array: bool
     @param array: if True, returns a numpy 2d array instead of a list of 
         dicts.
     
     """
     T = len(sequence)
     N = len(self.label_dom)
     alpha = numpy.zeros((T, N), numpy.float64)
     
     # Prepare the first column of the matrix: probs of all states in the 
     #  first timestep
     for i,state in enumerate(self.label_dom):
         alpha[0,i] = self.transition_log_probability(state, None) + \
                         self.emission_log_probability(sequence[0], state)
     
     # Iterate over the other timesteps
     for t in range(1, T):
         for j,sj in enumerate(self.label_dom):
             # Multiply each previous state's prob by the transition prob 
             #  to this state and sum them all together
             log_probs = [
                 alpha[t-1, i] + self.transition_log_probability(sj, si) \
                     for i,si in enumerate(self.label_dom)]
             # Also multiply this by the emission probability
             alpha[t, j] = sum_logs(log_probs) + \
                             self.emission_log_probability(sequence[t], sj)
     # Normalize by dividing all values by the total probability
     if normalize:
         for t in range(T):
             total = sum_logs(alpha[t,:])
             for j in range(N):
                 alpha[t,j] -= total
                 
     if not array:
         # Convert this into a list of dicts
         matrix = []
         for t in range(T):
             timestep = {}
             for (i,label) in enumerate(self.label_dom):
                 timestep[label] = alpha[t,i]
             matrix.append(timestep)
         return matrix
     else:
         return alpha
Ejemplo n.º 4
0
 def emission_log_probability(self, emission, state):
     """
     Gives the probability P(emission | label). Returned as a base 2
     log.
     
     The emission should be a pair of (root,label), together defining a 
     chord.
     
     There's a special case of this. If the emission is a list, it's 
     assumed to be a I{distribution} over emissions. The list should 
     contain (prob,em) pairs, where I{em} is an emission, such as is 
     normally passed into this function, and I{prob} is the weight to 
     give to this possible emission. The probabilities of the possible 
     emissions are summed up, weighted by the I{prob} values.
     
     """
     if type(emission) is list:
         # Average probability over the possible emissions
         probs = []
         for (prob,em) in emission:
             probs.append(logprob(prob) + \
                          self.emission_log_probability(em, state))
         return sum_logs(probs)
     
     # Single chord label
     state_root,schema = state
     chord_root,label = emission
     # Probability is 0 if the roots don't match
     if state_root != chord_root:
         return float('-inf')
     else:
         return self.emission_dist[schema].logprob(label)
Ejemplo n.º 5
0
 def _get_transition_backoff_scaler(self, context):
     # This is just for the schema distribution
     if context not in self._discount_cache:
         # The prob mass reserved for unseen events can be computed by 
         #  summing probabilities over all seen events and subtracting 
         #  from 1.
         # Our discounting model distributes this probability evenly over 
         #  the unseen events, so we can compute the discounted mass by 
         #  getting the probability of one unseen event and multiplying it.
         seen_labels = set([lab for lab in self.schemata+[None] if 
                                     self.schema_transition_counts[context][lab] > 0])
         if len(seen_labels) == 0:
             # Not seen anything in this context. All mass is discounted!
             self._discount_cache[context] = 0.0
         else:
             unseen_labels = set(self.schemata+[None]) - seen_labels
             # Try getting some event that won't have been seen
             # Compute how much mass is reserved for unseen events
             discounted_mass = self.schema_transition_dist[context].prob(
                                                 "%%% UNSEEN LABEL %%%") \
                                             * len(unseen_labels)
             # Compute how much probability the n-1 order model assigns to 
             #  things unseen by this model
             backoff_context = context[:-1]
             backoff_seen_mass = sum_logs([
                 self.backoff_model.schema_transition_log_probability_schemata(lab, 
                                                     *backoff_context) 
                                                 for lab in unseen_labels])
             self._discount_cache[context] = logprob(discounted_mass) - \
                                                     backoff_seen_mass
     return self._discount_cache[context]
Ejemplo n.º 6
0
    def emission_log_probability(self, emission, state):
        """
        Gives the probability P(emission | label). Returned as a base 2
        log.
        
        The emission should be a pair of (root,label), together defining a 
        chord.
        
        There's a special case of this. If the emission is a list, it's 
        assumed to be a I{distribution} over emissions. The list should 
        contain (prob,em) pairs, where I{em} is an emission, such as is 
        normally passed into this function, and I{prob} is the weight to 
        give to this possible emission. The probabilities of the possible 
        emissions are summed up, weighted by the I{prob} values.
        
        """
        if type(emission) is list:
            # Average probability over the possible emissions
            probs = []
            for (prob, em) in emission:
                probs.append(logprob(prob) + \
                             self.emission_log_probability(em, state))
            return sum_logs(probs)

        # Single chord label
        state_root, schema = state
        chord_root, label = emission
        # Probability is 0 if the roots don't match
        if state_root != chord_root:
            return float('-inf')
        else:
            return self.emission_dist[schema].logprob(label)
Ejemplo n.º 7
0
 def _get_transition_backoff_scaler(self, context):
     # This is just for the schema distribution
     if context not in self._discount_cache:
         # The prob mass reserved for unseen events can be computed by
         #  summing probabilities over all seen events and subtracting
         #  from 1.
         # Our discounting model distributes this probability evenly over
         #  the unseen events, so we can compute the discounted mass by
         #  getting the probability of one unseen event and multiplying it.
         seen_labels = set([
             lab for lab in self.schemata + [None]
             if self.schema_transition_counts[context][lab] > 0
         ])
         if len(seen_labels) == 0:
             # Not seen anything in this context. All mass is discounted!
             self._discount_cache[context] = 0.0
         else:
             unseen_labels = set(self.schemata + [None]) - seen_labels
             # Try getting some event that won't have been seen
             # Compute how much mass is reserved for unseen events
             discounted_mass = self.schema_transition_dist[context].prob(
                                                 "%%% UNSEEN LABEL %%%") \
                                             * len(unseen_labels)
             # Compute how much probability the n-1 order model assigns to
             #  things unseen by this model
             backoff_context = context[:-1]
             backoff_seen_mass = sum_logs([
                 self.backoff_model.
                 schema_transition_log_probability_schemata(
                     lab, *backoff_context) for lab in unseen_labels
             ])
             self._discount_cache[context] = logprob(discounted_mass) - \
                                                     backoff_seen_mass
     return self._discount_cache[context]
Ejemplo n.º 8
0
 def backward_log_probabilities(self, sequence, normalize=True, array=False):
     """We override this to provide a faster implementation.
     
     @see: forward_log_probability
     
     @type array: bool
     @param array: if True, returns a numpy 2d array instead of a list of 
         dicts.
     
     """
     T = len(sequence)
     N = len(self.label_dom)
     beta = numpy.zeros((T, N), numpy.float64)
     
     # Initialize with the probabilities of transitioning to the final state
     for i,si in enumerate(self.label_dom):
         beta[T-1, i] = self.transition_log_probability(None, si)
     
     # Iterate backwards over the other timesteps
     for t in range(T-2, -1, -1):
         for i,si in enumerate(self.label_dom):
             # Multiply each next state's prob by the transition prob 
             #  from this state to that and the emission prob in that next 
             #  state
             log_probs = [
                 beta[t+1, j] + self.transition_log_probability(sj, si) + \
                     self.emission_log_probability(sequence[t+1], sj) \
                     for j,sj in enumerate(self.label_dom)]
             beta[t, i] = sum_logs(log_probs)
         # Normalize by dividing all values by the total probability
         if normalize:
             total = sum_logs(beta[t,:])
             for j in range(N):
                 beta[t,j] -= total
                 
     if not array:
         # Convert this into a list of dicts
         matrix = []
         for t in range(T):
             timestep = {}
             for (i,label) in enumerate(self.label_dom):
                 timestep[label] = beta[t,i]
             matrix.append(timestep)
         return matrix
     else:
         return beta
Ejemplo n.º 9
0
    def emission_log_probability(self, emission, state):
        """
        Gives the probability P(emission | label). Returned as a base 2
        log.
        
        The emission should be a pair of (root,label), together defining a 
        chord.
        
        There's a special case of this. If the emission is a list, it's 
        assumed to be a I{distribution} over emissions. The list should 
        contain (prob,em) pairs, where I{em} is an emission, such as is 
        normally passed into this function, and I{prob} is the weight to 
        give to this possible emission. The probabilities of the possible 
        emissions are summed up, weighted by the I{prob} values.
        
        """
        if type(emission) is list:
            # Average probability over the possible emissions
            probs = []
            for (prob, em) in emission:
                probs.append(logprob(prob) + \
                             self.emission_log_probability(em, state))
            return sum_logs(probs)

        # Single chord label
        point, function = state
        chord_root, label = emission
        X, Y, x, y = point
        # Work out the chord substitution
        subst = (chord_root - coordinate_to_et_2d((x, y))) % 12

        # Generate the substitution given the chord function
        subst_prob = self.subst_emission_dist[function].logprob(subst)
        # Generate the label given the subst and chord function
        label_prob = self.type_emission_dist[(subst, function)].logprob(label)
        return subst_prob + label_prob
Ejemplo n.º 10
0
 def emission_log_probability(self, emission, state):
     """
     Gives the probability P(emission | label). Returned as a base 2
     log.
     
     The emission should be a pair of (root,label), together defining a 
     chord.
     
     There's a special case of this. If the emission is a list, it's 
     assumed to be a I{distribution} over emissions. The list should 
     contain (prob,em) pairs, where I{em} is an emission, such as is 
     normally passed into this function, and I{prob} is the weight to 
     give to this possible emission. The probabilities of the possible 
     emissions are summed up, weighted by the I{prob} values.
     
     """
     if type(emission) is list:
         # Average probability over the possible emissions
         probs = []
         for (prob,em) in emission:
             probs.append(logprob(prob) + \
                          self.emission_log_probability(em, state))
         return sum_logs(probs)
     
     # Single chord label
     point,function = state
     chord_root,label = emission
     X,Y,x,y = point
     # Work out the chord substitution
     subst = (chord_root - coordinate_to_et_2d((x,y))) % 12
     
     # Generate the substitution given the chord function
     subst_prob = self.subst_emission_dist[function].logprob(subst)
     # Generate the label given the subst and chord function
     label_prob = self.type_emission_dist[(subst,function)].logprob(label)
     return subst_prob + label_prob