def unpack_contrastive_pairs(stream, vocab_dim, min_val=0.0, max_val=1.0, rotate_prob=0.0): """ vocab_dim: int """ for pair in stream: if pair is None: yield pair continue pos_entity, neg_entity = pair pos_chord_label = str(pos_entity.chord_label) neg_chord_label = str(neg_entity.chord_label) pos_chord_idx = labels.chord_label_to_class_index( pos_chord_label, vocab_dim) neg_chord_idx = labels.chord_label_to_class_index( neg_chord_label, vocab_dim) if np.random.binomial(1, rotate_prob): shift = (pos_chord_idx - neg_chord_idx) % 12 neg_entity = _padshift(neg_entity, shift, 3) yield biggie.Entity(cqt=pos_entity.cqt, chord_idx=pos_chord_idx, target=np.array([max_val])) yield biggie.Entity(cqt=neg_entity.cqt, chord_idx=pos_chord_idx, target=np.array([min_val]))
def _unpack_triples(stream): for triple in stream: if triple is None: yield triple continue x1, x2, z = triple yield biggie.Entity(cqt=x1.cqt, cqt_2=x2.cqt, score=float(x1.label == x2.label)) yield biggie.Entity(cqt=x1.cqt, cqt_2=z.cqt, score=float(x1.label == z.label))
def create_chord_entity(npz_file, lab_files, dtype=np.float32): """Create an entity from the given files. Parameters ---------- npz_file: str Path to a 'npz' archive, containing at least a value for 'cqt'. lab_files: list Collection of paths to corresponding lab-files. dtype: type Data type for the cqt array. Returns ------- entity: biggie.Entity Populated chord entity, with {cqt, chord_labels, time_points}. """ entity = biggie.Entity(**np.load(npz_file)) chord_labels = [] for lab_file in lab_files: intervals, labels = L.load_labeled_intervals(lab_file, compress=True) labels = mir_eval.util.interpolate_intervals(intervals, labels, entity.time_points, fill_value='N') chord_labels.append(labels) entity.chord_labels = np.array(chord_labels).T entity.cqt = entity.cqt.astype(dtype) return entity
def chord_index_to_onehot_vectors(stream, vocab_dim): one_hots = np.eye(vocab_dim) for entity in stream: if entity is None: yield entity continue yield biggie.Entity(cqt=entity.cqt, target=one_hots[entity.chord_idx])
def create_chord_entity(npz_file, jams_file, dtype=np.float32): """Create an entity from the given files. Parameters ---------- npz_file: str Path to a 'npz' archive, containing at least a value for 'cqt'. jams_file: str Path to a corresponding JAMS file. dtype: type Data type for the cqt array. Returns ------- entity: biggie.Entity Populated chord entity, with {cqt, chord_labels, *time_points}. """ entity = biggie.Entity(**np.load(npz_file)) jam = pyjams.load(jams_file) intervals = np.asarray(jam.chord[0].intervals) labels = [str(_) for _ in jam.chord[0].labels.value] entity.chord_labels = mir_eval.util.interpolate_intervals( intervals, labels, entity.time_points, fill_value='N') entity.cqt = entity.cqt.astype(dtype) return entity
def slice_embedding_entity(entity, length, idx=None): """Return a windowed slice of a cqt Entity. Parameters ---------- entity : Entity, with at least {cqt, icode} fields Observation to window. Note that entity.cqt is shaped (num_channels, num_frames, num_bins). length : int Length of the sliced array. idx : int, or None Centered frame index for the slice, or random if not provided. Returns ------- sample: biggie.Entity with fields {cqt, label} The windowed observation. """ idx = np.random.randint(entity.embedding.shape[0]) if idx is None else idx return biggie.Entity( embedding=entity.embedding[idx], time=entity.time_points[idx], fcode=entity.fcode, note_number=entity.note_number, icode=entity.icode)
def fretboard_mapper(stream, vocab, targets): """Stream filter for mapping chord label entities to frets. Parameters ---------- stream : generator Yields {cqt, chord_label} entities, or None. vocab : dl4mir.chord.lexicon.Vocab Map from chord labels to indices. targets : np.ndarray, shape=(num_classes, 6, num_frets) Target templates. Yields ------ entity : biggie.Entity Fretted entity with {cqt, frets}, or None if out of gamut. """ for entity in stream: if entity is None: yield entity idx = vocab.label_to_index(entity.chord_label) if idx is None: yield None else: yield biggie.Entity(cqt=entity.cqt, fretboard_target=targets[idx])
def fret_mapper(stream, voicings, num_frets=9): """Stream filter for mapping chord label entities to frets. Parameters ---------- stream : generator Yields {cqt, chord_label} entities, or None. voicings : dict Map of chord labels to tab strings. Yields ------ entity : biggie.Entity Fretted entity with {cqt, frets}, or None if out of gamut. """ for entity in stream: if entity is None: yield entity tab = voicings.get(str(entity.chord_label), None) if tab is None: yield None else: frets = { '{0}_index'.format(s): i % num_frets for s, i in zip('EADGBe', decode(tab)) } yield biggie.Entity(cqt=entity.cqt, **frets)
def multi_predict(entity, transform, p_vals, vocab, num_cpus=None): """Transform a CQT entity and apply Viterbi decoding. Parameters ---------- entity : biggie.Entity Entity to estimate. transform : optimus.Graph Consumes 'cqt' fields, returns 'posterior' fields. p_vals : list Set of self-transition penalties to apply. Returns ------- est_jams : pyjams.JAMS Populated JAMS object. """ z = convolve(entity, transform, 'cqt') pool = Pool(processes=num_cpus) threads = [ pool.apply_async( posterior_to_labeled_intervals, (biggie.Entity(posterior=z.posterior, chord_labels=z.chord_labels), p, vocab), ) for p in p_vals ] pool.close() pool.join() jam = pyjams.JAMS() for penalty, thread in zip(p_vals, threads): annot = jam.chord.create_annotation() populate_annotation(*thread.get(), annot=annot) annot.sandbox.penalty = penalty return jam
def main(args): config = json.load(open(args.config)) penalty_values = [float(_) for _ in config['penalty_values']] label_map = ENCODERS[args.label_type] for f in futils.load_textlist(args.posterior_filelist): print "[{0}] Decoding {1}".format(time.asctime(), f) # Read the whole stash to memory because the hdf5 reference doesn't # survive parallelization. stash = biggie.Stash(f) keys = stash.keys() stash = {k: biggie.Entity(**stash.get(k).values()) for k in keys} # Parse the posterior stash filepath for its model's params parts = list(os.path.splitext(f)[0].split('outputs/')[-1].split('/')) if len(parts) == 4: parts.append("best") model, dropout, fold_idx, split, checkpoint = parts model_params = dict(model=model, dropout=dropout, fold_idx=fold_idx, split=split, checkpoint=checkpoint) output_dir = os.path.join(args.output_directory, checkpoint) posterior_stash_to_jams(stash, penalty_values, output_dir, label_map, model_params)
def chord_index_to_affinity_vectors(stream, vocab_dim): affinity_vectors = labels.affinity_vectors(vocab_dim) for entity in stream: if entity is None: yield entity continue yield biggie.Entity(cqt=entity.cqt, target=affinity_vectors[entity.chord_idx])
def transpose(stream, axes, key): for entity in stream: if entity is None: yield entity continue values = entity.values() values[key] = np.transpose(values.pop(key), axes) yield biggie.Entity(**values)
def reshape(stream, newshape, key): for entity in stream: if entity is None: yield entity continue values = entity.values() values[key] = np.reshape(values.pop(key), newshape) yield biggie.Entity(**values)
def concatenate(stream, key='data', axis=-1): for entity in stream: if entity is None: yield entity continue values = entity.values() values[key] = np.concatenate([values.pop(key)] * 2, axis=axis) yield biggie.Entity(**values)
def stream_averages(mu=0, std=0.1, dropout=0.25, min_scale=0.25): averages = np.load("/home/ejhumphrey/Dropbox/tmp/chord_averages0.npy") while True: for chord_idx in range(157): cqt = averages[chord_idx] cqt = cqt + np.random.normal(mu, std, cqt.shape) cqt = cqt * np.random.binomial(1, 1.0 - dropout, size=cqt.shape) cqt = cqt * np.random.uniform(min_scale, 1.0) yield biggie.Entity(cqt=cqt, chord_idx=chord_idx)
def chord_index_to_tonnetz(stream, vocab_dim): chord_labels = [ labels.index_to_chord_label(n, vocab_dim) for n in range(vocab_dim) ] T = np.array([labels.chord_label_to_tonnetz(l) for l in chord_labels]).squeeze() for entity in stream: if entity is None: yield entity continue yield biggie.Entity(cqt=entity.cqt, target=T[entity.chord_idx])
def map_to_class_index(stream, index_mapper, *args, **kwargs): """ vocab_dim: int """ for entity in stream: if entity is None: yield entity continue class_idx = index_mapper(entity, *args, **kwargs) yield None if class_idx is None else biggie.Entity(data=entity.data, class_idx=class_idx)
def note_numbers_to_chroma(stream, bins_per_pitch=1): """ vocab_dim: int """ for entity in stream: if entity is None: yield entity continue pitches = set([_ % 12 for _ in eval(str(entity.note_numbers))]) chroma = np.zeros(12 * bins_per_pitch) for p in pitches: chroma[p * bins_per_pitch] = 1.0 yield biggie.Entity(data=entity.data, target=chroma)
def ideal_chroma_ss(ref_set, stash): for k, v in ref_set.iteritems(): intervals, labels = L.compress_labeled_intervals(**v) chord_labels = [str(l) for l in labels] chroma = L.chord_label_to_chroma(chord_labels) durations = intervals_to_durations(intervals) stash.add(str(k), biggie.Entity(chroma=chroma, chord_labels=chord_labels, time_points=intervals[:, 0], durations=durations), overwrite=True) print k
def note_numbers_to_pitch(stream, bins_per_pitch=1, max_pitch=84): """ vocab_dim: int """ for entity in stream: if entity is None: yield entity continue pitches = set(eval(str(entity.note_numbers))) pitch_vec = np.zeros(max_pitch + 1) for p in pitches: pitch_vec[p] = 1.0 yield biggie.Entity(data=entity.data, target=pitch_vec)
def map_to_chord_quality_index(stream, vocab_dim): """ vocab_dim: int """ for entity in stream: if entity is None: yield entity continue values = entity.values() cqt, chord_label = values.pop('cqt'), str(values.pop('chord_label')) qual_idx = labels.chord_label_to_quality_index(chord_label, vocab_dim) yield None if qual_idx is None else biggie.Entity(cqt=cqt, quality_idx=qual_idx)
def chord_index_to_tonnetz_distance(stream, vocab_dim): chord_labels = [ labels.index_to_chord_label(n, vocab_dim) for n in range(vocab_dim) ] X = np.array([labels.chord_label_to_tonnetz(l) for l in chord_labels]) ssm = cdist(X.squeeze(), X.squeeze()) sn_distance = 1 - ssm / ssm.max() for entity in stream: if entity is None: yield entity continue yield biggie.Entity(cqt=entity.cqt, target=sn_distance[entity.chord_idx])
def map_to_chroma(stream, bins_per_pitch=1): """ vocab_dim: int """ for entity in stream: if entity is None: yield entity continue values = entity.values() data, chord_label = values.pop('data'), str(values.pop('chord_label')) chroma = labels.chord_label_to_chroma(chord_label, bins_per_pitch) if (chroma < 0).any(): yield None yield biggie.Entity(data=data, target=chroma)
def beat_sync(entity, time_boundaries, new_labels=None, pool_func='median'): """Beat-synchronize an entity to a set of boundaries in time. Parameters ---------- entity : biggie.Entity Required fields {time_points, chord_labels} time_boundaries : array_like, shape=(N,) List of boundary points over which to pool data. new_labels : array_like Set of pre-aligned labels to over-ride the current ones. pool_func : str Method of pooling data; one of ['mean', 'median']. Returns ------- new_entity : biggie.Entity Same fields as input entity, with additional `durations` field. """ time_boundaries = list(time_boundaries) if time_boundaries[0] != 0.0: raise ValueError("Time boundaries should really start from 0.") data = entity.values() time_points = data.pop('time_points') chord_labels = data.pop('chord_labels') idxs = util.find_closest_idx(time_points, time_boundaries).tolist() if new_labels is None: chord_labels = util.boundary_pool(chord_labels, idxs, pool_func='mode') else: chord_labels = np.asarray(new_labels) for key in data: data_shape = list(data[key].shape) if len(time_points) in data_shape: axis = data_shape.index(len(time_points)) dtype = data[key].dtype.type pool_func = 'mode' if dtype == np.string_ else pool_func data[key] = util.boundary_pool(data[key], idxs, pool_func=pool_func, axis=axis) return biggie.Entity( time_points=time_boundaries[:-1], chord_labels=chord_labels, durations=util.boundaries_to_durations(time_boundaries), **data)
def convolve(entity, graph, input_key, axis=1, chunk_size=250): """Apply a graph convolutionally to a field in an an entity. Parameters ---------- entity : biggie.Entity Observation to predict. graph : optimus.Graph Network for processing an entity. data_key : str Name of the field to use for the input. chunk_size : int, default=None Number of slices to transform in a given step. When None, parses one slice at a time. Returns ------- output : biggie.Entity Result of the convolution operation. """ # TODO(ejhumphrey): Make this more stable, somewhat fragile as-is time_dim = graph.inputs.values()[0].shape[2] values = entity.values() input_stepper = optimus.array_stepper(values.pop(input_key), time_dim, axis=axis, mode='same') results = dict([(k, list()) for k in graph.outputs]) if chunk_size: chunk = [] for x in input_stepper: chunk.append(x) if len(chunk) == chunk_size: for k, v in graph(np.array(chunk)).items(): results[k].append(v) chunk = [] if len(chunk): for k, v in graph(np.array(chunk)).items(): results[k].append(v) else: for x in input_stepper: for k, v in graph(x[np.newaxis, ...]).items(): results[k].append(v) for k in results: results[k] = np.concatenate(results[k], axis=0) values.update(results) return biggie.Entity(**values)
def _circshift(entity, pitch_shift, bins_per_pitch): values = entity.values() data, chord_label = values.pop('data'), str(values.pop('chord_label')) # Change the chord label if it has a harmonic root. if chord_label not in [labels.NO_CHORD, labels.SKIP_CHORD]: root, quality, exts, bass = labels.split(chord_label) root = (labels.pitch_class_to_semitone(root) + pitch_shift) % 12 new_root = labels.semitone_to_pitch_class(root) new_label = labels.join(new_root, quality, exts, bass) # print "Input %12s // Shift: %3s // Output %12s" % \ # (chord_label, pitch_shift, new_label) chord_label = new_label # Always rotate the CQT. data = util.circshift(data, 0, pitch_shift) return biggie.Entity(data=data, chord_label=chord_label, **values)
def chroma_stepper(key, stash, index=None): """writeme.""" entity = stash.get(key) num_samples = len(entity.chord_labels) if index is None: index = {key: np.arange(num_samples)} valid_samples = index.get(key, []) idx = 0 count = 0 while count < len(valid_samples): n = valid_samples[idx] if n >= entity.chroma.shape[0]: print "Out of range! %s" % key break yield biggie.Entity(chroma=entity.chroma[n], chord_label=entity.chord_labels[n]) idx += 1 count += 1
def slice_cqt_entity(entity, length, idx=None): """Return a windowed slice of a chord Entity. Parameters ---------- entity : Entity, with at least {cqt, chord_labels} fields Observation to window. Note that entity.cqt is shaped (num_channels, num_frames, num_bins). length : int Length of the sliced array. idx : int, or None Centered frame index for the slice, or random if not provided. Returns ------- sample: biggie.Entity with fields {cqt, chord_label} The windowed chord observation. """ idx = np.random.randint(entity.cqt.shape[1]) if idx is None else idx cqt = np.array([slice_tile(x, idx, length) for x in entity.cqt]) return biggie.Entity(cqt=cqt, chord_label=entity.chord_labels[idx])
def classify_chroma(entity, templates): """ Parameters ---------- posterior: np.ndarray Posteriorgram of chord classes. viterbi_penalty: scalar, in (0, inf) Self-transition penalty; higher values produce more "stable" paths. Returns ------- predictions: dict Chord labels and dense count vectors. """ data = entity.values posterior = 1.0 - cdist(data.pop("chroma"), templates, 'cosine') scalar = posterior.sum(axis=1)[:, np.newaxis] scalar[scalar == 0] = 1.0 return biggie.Entity(posterior=posterior/scalar, **data)
def test_convolve(self): input_data = optimus.Input(name='x_in', shape=(None, 1, 1, 1)) flatten = optimus.Flatten(name='flatten', ndim=1) output_data = optimus.Output(name='x_out') edges = optimus.ConnectionManager([(input_data, flatten.input), (flatten.output, output_data)]) transform = optimus.Graph(name='test', inputs=[input_data], nodes=[flatten], connections=edges.connections, outputs=[output_data]) x = np.arange(10).reshape(1, 10, 1) y = np.array(['a', 'b']) entity = biggie.Entity(x_in=x, y=y) z = C.convolve(entity, transform, 'x_in') np.testing.assert_equal(z.x_out, np.arange(10)) np.testing.assert_equal(z.y, y)