Package jazzparser :: Package taggers :: Package segmidi :: Package chordlabel :: Module tagger
[hide private]
[frames] | no frames]

Source Code for Module jazzparser.taggers.segmidi.chordlabel.tagger

  1  """Tagger to combine a chord labeling model with a chord-input supertagger. 
  2   
  3  """ 
  4  """ 
  5  ============================== License ======================================== 
  6   Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding 
  7    
  8   This file is part of The Jazz Parser. 
  9    
 10   The Jazz Parser is free software: you can redistribute it and/or modify 
 11   it under the terms of the GNU General Public License as published by 
 12   the Free Software Foundation, either version 3 of the License, or 
 13   (at your option) any later version. 
 14    
 15   The Jazz Parser is distributed in the hope that it will be useful, 
 16   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18   GNU General Public License for more details. 
 19    
 20   You should have received a copy of the GNU General Public License 
 21   along with The Jazz Parser.  If not, see <http://www.gnu.org/licenses/>. 
 22   
 23  ============================ End license ====================================== 
 24   
 25  """ 
 26  __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"  
 27   
 28  from jazzparser.taggers.tagger import Tagger 
 29  from jazzparser.taggers.ngram_multi.tagger import MultiChordNgramTagger 
 30  from jazzparser.misc.chordlabel.hmm import HPChordLabeler 
 31  from jazzparser.misc.chordlabel.midi import midi_to_emission_stream 
 32  from jazzparser.utils.options import ModuleOption 
 33  from jazzparser.data.db_mirrors import Chord 
 34  from jazzparser.data.input import DbInput 
 35  from jazzparser.utils.strings import str_to_bool 
 36  from . import tools 
 37   
38 -class ChordLabelNgramTagger(Tagger):
39 """ 40 Tagger that loads a chord labeling model to assign chord labels to MIDI 41 data, then hands over to a chord supertagger to process the output of 42 the labeler. 43 44 """ 45 COMPATIBLE_FORMALISMS = ['music_halfspan'] 46 TAGGER_OPTIONS = MultiChordNgramTagger.TAGGER_OPTIONS + [ 47 ModuleOption('labeling_model', 48 help_text="Model name for chord labeler", 49 usage="labeling_model=M, where M is a trained chord labeling model", 50 required=True), 51 ModuleOption('partition_labeler', filter=str_to_bool, 52 help_text="By default, the chord labeling model is not loaded "\ 53 "with a partition number, even if the supertagging model is. "\ 54 "If this is True, the same partition number will be used for "\ 55 "the labeler's model as was given to the supertagger.", 56 usage="partition_labeler=B, where B is True or False", 57 default=False), 58 ModuleOption('latticen', filter=int, 59 help_text="Number of chords per segment to get in the lattice", 60 usage="latticen=N, where N is an integer", 61 default=3), 62 ModuleOption('lattice_beam', filter=float, 63 help_text="Beam ratio to apply to the chord lattice. Removes all "\ 64 "chord labels with probability < ratio * highest probability "\ 65 "in timestep. Default: 1e-5", 66 usage="lattice_beam=F, where F is a float < 1.0 (e.g. 1e-6)", 67 default=1e-5), 68 ModuleOption('label_viterbi', filter=str_to_bool, 69 help_text="Use Viterbi decoding instead of forward-backward. "\ 70 "Only one chord will be returned for every timestep, no "\ 71 "matter what latticen is. Default: False", 72 usage="viterbi=B, where B is True or False", 73 default=False), 74 ] 75 INPUT_TYPES = ['segmidi'] 76 name = "chordlabel" 77 shell_tools = Tagger.shell_tools + [ 78 tools.ChordLabelTool(), 79 ] 80 LEXICAL_PROBABILITY = True 81
82 - def __init__(self, grammar, input, options={}, logger=None, *args, **kwargs):
83 Tagger.__init__(self, grammar, input, options, logger=logger) 84 # Make a copy of the options that we will pass through to the tagger 85 options = self.options.copy() 86 # Remove the options that the tagger doesn't need 87 labeling_model_name = options.pop('labeling_model') 88 latticen = options.pop('latticen') 89 beam_ratio = options.pop('lattice_beam') 90 viterbi = options.pop('label_viterbi') 91 partition_labeler = options.pop('partition_labeler') 92 93 # Partition the labeling model if requested and a partition number 94 # was given for the supertagger 95 if partition_labeler and 'partition' in self.options and \ 96 self.options['partition'] is not None: 97 labeling_model_name += "%d" % self.options['partition'] 98 99 self.logger.info("Labeling model: %s" % labeling_model_name) 100 # First run the chord labeler on the MIDI input 101 # Load a labeling model 102 labeler = HPChordLabeler.load_model(labeling_model_name) 103 self.labeler = labeler 104 # Get chord labels from the model: get a lattice of possible chords 105 lattice = labeler.label_lattice(input, options={ 106 'n' : latticen, 107 'nokey' : True, 108 'viterbi' : viterbi }, 109 corpus=True) 110 # Store the lattice for later reference 111 self.lattice = lattice 112 # Also store the labeler's emission matrix 113 emissions = midi_to_emission_stream(input, remove_empty=False)[0] 114 self.labeler_emission_matrix = labeler.get_small_emission_matrix(emissions) 115 # Beam the lattice to get rid of very low probability labels 116 lattice.apply_ratio_beam(ratio=beam_ratio) 117 118 self.tagger = MultiChordNgramTagger(grammar, lattice, options, 119 logger=logger, *args, **kwargs) 120 self._lex_prob_cache = [{} for i in range(self.input_length)]
121
122 - def get_signs(self, offset=0):
123 return self.tagger.get_signs(offset=offset)
124
125 - def get_word(self, index):
126 return self.input[index]
127
128 - def get_string_input(self):
129 return [str(i) for i in range(self.input_length)]
130
131 - def lexical_probability(self, start_time, end_time, span_label):
132 """ 133 Lexical probabilities for a probabilistic parser. This will only 134 get used if the parsing model can't compute a probability itself 135 (i.e. in the case of MIDI input). 136 137 """ 138 # Take product of emission probabilities for time steps in this range 139 prob = 1.0 140 for time in range(start_time, end_time): 141 prob *= self.single_step_lexical_probability(time, span_label) 142 return prob
143
144 - def single_step_lexical_probability(self, time, span_label):
145 if span_label not in self._lex_prob_cache[time]: 146 # Compute the lexical probability for this single time step 147 # Consider each of the chord labels in the lattice at this time 148 chord_probs = [] 149 for (chord,prob) in self.lattice[time]: 150 # Get the emission probabilities for this chord form the 151 # chord labeling model 152 chord_label = self.labeler.chord_types.index(chord.model_label) 153 # All keys have the same em prob, so we don't worry about that 154 chord_em_prob = self.labeler_emission_matrix[time, chord.root, chord_label] 155 # Get the probability of generating this chord label from 156 # the supertagger's distributions 157 tagger_label = self.tagger.model.chordmap[chord.label] 158 tagger_em_prob = self.tagger.model.model.emission_probability( 159 (chord.root,tagger_label), span_label) 160 # Multiply these probabilities to get the probability of the 161 # chord label and emission given the tag 162 chord_probs.append(chord_em_prob * tagger_em_prob) 163 # Sum over the chords in the lattice to get the probabilitiy of 164 # the emission given the tag 165 self._lex_prob_cache[time][span_label] = sum(chord_probs, 0.0) 166 return self._lex_prob_cache[time][span_label]
167