MIR workshop 2011 day5 lab

From CCRMA Wiki
Jump to: navigation, search

MIR Workshop 2011 Day 5 Lab on Music Recommendation
Douglas Eck, Google


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.
    '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]
    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)