MIR workshop 2011 day5 lab
MIR Workshop 2011 Day 5 Lab on Music Recommendation
Douglas Eck, Google
Overview
This lab covers the construction of parts of a music recommender. Focus is placed on building a similarity matrix from data and querying that matrix based on cosine distance. Fast programmers should be able to accomplish considerably more.
- The basics (some Python code available to help).
- Calculate acoustic features on CAL500 dataset (students should have already done this.)
- Read in user tag annotations for the same dataset provided by UCSD.
- Build similarity matrix based on word vectors derived from these annotations. In my implementation the matrix is stored as a dictionary of vectors, but this is python-specific.
- Query similarity matrix with a track to get top hits based on cosine distance.
- Build second similarity matrix using acoustic features.
- Query this similarity matrix with track to get top hits based on cosine distance.
- Extra (I didn't write code for this, but can help students find examples).
- Query the EchoNest for additional acoustic features and compare to yours.
- Use the CAL500 user annotations as ground truth and evaluate your audio features (ROC curve or some precision measure).
- Compare a 2D visualization of acoustic features versus UCSD user annotations.
- Train a classifier on CAL500. Train / test splits will be generated this afternoon.
Lab code is found in /usr/ccrma/courses/mir20110/cal500_new. A previous version was uploaded at /usr/ccrma/courses/mir2011/cal500 but it was using filenames from my University of Montreal lab. I renamed audio files to match those of UCSD Cal500
An example call for the code provided by me:
%python cal500_example.py norah_jones-dont_know_why 1 Cosine distance=0.0000 Track=norah_jones-dont_know_why 2 Cosine distance=0.1111 Track=carole_king-youve_got_a_friend 3 Cosine distance=0.1365 Track=alicia_keys-fallin 4 Cosine distance=0.1387 Track=dido-here_with_me 5 Cosine distance=0.1610 Track=fiona_apple-love_ridden 6 Cosine distance=0.1646 Track=barry_manilow-mandy 7 Cosine distance=0.1661 Track=stan_getz-corcovado_quiet_nights_of_quiet_stars 8 Cosine distance=0.1696 Track=aimee_mann-wise_up 9 Cosine distance=0.1758 Track=marvelettes-please_mr._postman 10 Cosine distance=0.1772 Track=ben_folds_five-brick 11 Cosine distance=0.1795 Track=cranberries-linger 12 Cosine distance=0.1833 Track=sade-smooth_operator 13 Cosine distance=0.1860 Track=john_lennon-imagine 14 Cosine distance=0.1864 Track=dionne_warwick-walk_on_by 15 Cosine distance=0.1918 Track=5th_dimension-one_less_bell_to_answer 16 Cosine distance=0.1923 Track=carpenters-rainy_days_and_mondays 17 Cosine distance=0.1932 Track=diana_ross_and_the_supremes-where_did_our_love_go 18 Cosine distance=0.1939 Track=smokey_robinson_and_the_miracles-ooo_baby_baby 19 Cosine distance=0.1939 Track=fleetwood_mac-say_you_love_me 20 Cosine distance=0.1970 Track=rufus_wainwright-cigarettes_and_chocolate_milk
Functions provided by me in cal500_example.py. Students can recode as they please in any language. They are free to use as much of my code as they want.
def RemapFilename(infile): """Maps a filename from Doug name to real cal500 name.""" def RenameCal500(dest_directory='high_bitrate'): """Renames cal500 files from Doug naming scheme to standard one and places them in dest_directory.""" def MakeFilenameMap(infile = 'cal500_doug_filenames.txt', outfile = 'cal500_filename_map.txt'): """Create text file mapping old (Doug) filenames to standard Cal500 filenames.""" def GetKeyFromAnnotationPath(annotation_path): """Gets key from annnotation file path, stripping off the number. # Ex: norine_braun-spanish_banks_02.txt yields # norine_braun-spanish_banks # Mapping from text values in CAL500 annotation files to (somewhat arbitrary) numeric values. ANNOTATION_MAP = { 'yes': 1.0, 'prominent': 1.0, 'present': 0.75, 'uncertain': 0.5, 'no': 0.0, 'none': 0.0, '5': 5/5.0, '4': 4/5.0, '3': 3/5.0, '2': 2/5.0, '1': 1/5.0, '0': 0.0 } def AddTagWeightsToDictFromAnnotationFile(annotation_path, tag_weights): """Reads tag weights into a dictionary keyed by tag name and adds them to the defaultdict tag_weights. Use key 'counter' to track number of annotations.""" def BuildTagDictionary(tag_directory='annotations'): """Builds dictionary mapping cal500 key to a weighted tag vector. Returns dictionary and our vocabulary of tags.""" def BuildVectorDictionaryFromTagDictionary(tag_dict, vocabulary): """Transforms tag dictionaries int tag vectors using vocabulary.""" def CosineDistance(v1, v2): """Calculates cosine distance using numpy.""" def ScoreQuery(vector_dict, query): """Finds nearest neigbors for query in vector_dict.""" def PrintScoreDict(score_dict, query, k=20): """Print score dictionary for a query.""" # Here is the main function as called above. if __name__=='__main__': if len(sys.argv)>1: query = sys.argv[1] else: query = 'norah_jones-dont_know_why' # Build vectors from words. tag_dict, vocabulary = BuildTagDictionary() vector_dict = BuildVectorDictionaryFromTagDictionary(tag_dict, vocabulary) # Score a query. score_dict = ScoreQuery(vector_dict, query) PrintScoreDict(score_dict, query)