├── data └── .gitkeep ├── results └── .gitkeep ├── converter ├── __init__.py ├── converter.py ├── LastFM.py ├── SyntheticClick.py ├── MovieLens1M.py └── MovieLens100k.py ├── config ├── ML1M │ ├── popular.ini │ ├── random.ini │ ├── sketch.ini │ ├── iMF.ini │ ├── static_MF.ini │ ├── iFMs.ini │ └── static_FMs.ini ├── LastFM │ ├── popular.ini │ ├── sketch.ini │ ├── iMF.ini │ ├── static_MF.ini │ ├── iFMs.ini │ └── static_FMs.ini ├── ML100k │ ├── popular.ini │ ├── user_knn.ini │ ├── sketch.ini │ ├── iMF.ini │ ├── bprmf.ini │ ├── static_MF.ini │ ├── iFMs.ini │ └── static_FMs.ini ├── click │ ├── popular.ini │ ├── user_knn.ini │ ├── sketch.ini │ ├── iMF.ini │ ├── static_MF.ini │ ├── iFMs.ini │ └── static_FMs.ini └── example.ini ├── .gitignore ├── requirements.txt ├── README.md ├── tool ├── clickgenerator.jl └── parse_result.py ├── experiment.py └── notebook ├── LastFM.ipynb └── claim-iMF.ipynb /data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /results/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /converter/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/ML1M/popular.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: popular 7 | -------------------------------------------------------------------------------- /config/ML1M/random.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: random 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .*.swp 3 | *.pyc 4 | .ipynb_checkpoints/ 5 | results/* 6 | data/* 7 | !.gitkeep 8 | -------------------------------------------------------------------------------- /config/LastFM/popular.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: LastFM 3 | Trial: 1 4 | 5 | [Model] 6 | Name: popular 7 | -------------------------------------------------------------------------------- /config/ML100k/popular.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 1 4 | 5 | [Model] 6 | Name: popular 7 | -------------------------------------------------------------------------------- /config/click/popular.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 1 4 | 5 | [Model] 6 | Name: popular 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | numpy == 1.11.0 2 | pandas == 0.18.1 3 | scipy == 0.17.1 4 | scikit_learn == 0.17.1 5 | flurs == 0.0.1 6 | -------------------------------------------------------------------------------- /config/ML100k/user_knn.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 1 4 | 5 | [Model] 6 | Name: user-knn 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 5 11 | -------------------------------------------------------------------------------- /config/click/user_knn.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 1 4 | 5 | [Model] 6 | Name: user-knn 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 5 11 | -------------------------------------------------------------------------------- /config/ML1M/sketch.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: sketch 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 40 11 | ell: 1 12 | -------------------------------------------------------------------------------- /config/LastFM/sketch.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: LastFM 3 | Trial: 1 4 | 5 | [Model] 6 | Name: sketch 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 20 11 | ell: -1 12 | -------------------------------------------------------------------------------- /config/ML100k/sketch.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 1 4 | 5 | [Model] 6 | Name: sketch 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 40 11 | ell: 1 12 | -------------------------------------------------------------------------------- /config/click/sketch.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 1 4 | 5 | [Model] 6 | Name: sketch 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 30 11 | ell: -1 12 | -------------------------------------------------------------------------------- /config/ML1M/iMF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: iMF 7 | Epoch: 10 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg: 0.01 12 | learn_rate: 0.002 13 | -------------------------------------------------------------------------------- /config/LastFM/iMF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: LastFM 3 | Trial: 1 4 | 5 | [Model] 6 | Name: iMF 7 | Epoch: 15 8 | 9 | [Parameters] 10 | k: 20 11 | l2_reg: 0.01 12 | learn_rate: 0.02 13 | -------------------------------------------------------------------------------- /config/ML100k/iMF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 5 4 | 5 | [Model] 6 | Name: iMF 7 | Epoch: 30 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg: 0.01 12 | learn_rate: 0.002 13 | -------------------------------------------------------------------------------- /config/click/iMF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 5 4 | 5 | [Model] 6 | Name: iMF 7 | Epoch: 30 8 | 9 | [Parameters] 10 | k: 2 11 | l2_reg: 0.01 12 | learn_rate: 0.0003 13 | -------------------------------------------------------------------------------- /config/example.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 1 4 | 5 | [Model] 6 | Name: static-MF 7 | Epoch: 1 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg: 0.01 12 | learn_rate: 0.003 13 | -------------------------------------------------------------------------------- /config/ML100k/bprmf.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 5 4 | 5 | [Model] 6 | Name: bprmf 7 | Epoch: 30 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg: 0.01 12 | learn_rate: 0.001 13 | -------------------------------------------------------------------------------- /config/LastFM/static_MF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: LastFM 3 | Trial: 1 4 | 5 | [Model] 6 | Name: static-MF 7 | Epoch: 15 8 | 9 | [Parameters] 10 | k: 20 11 | l2_reg: 0.01 12 | learn_rate: 0.03 13 | -------------------------------------------------------------------------------- /config/ML100k/static_MF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 5 4 | 5 | [Model] 6 | Name: static-MF 7 | Epoch: 30 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg: 0.01 12 | learn_rate: 0.002 13 | -------------------------------------------------------------------------------- /config/ML1M/static_MF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: static-MF 7 | Epoch: 10 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg: 0.01 12 | learn_rate: 0.003 13 | -------------------------------------------------------------------------------- /config/click/static_MF.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 5 4 | 5 | [Model] 6 | Name: static-MF 7 | Epoch: 30 8 | 9 | [Parameters] 10 | k: 2 11 | l2_reg: 0.01 12 | learn_rate: 0.0003 13 | -------------------------------------------------------------------------------- /config/ML1M/iFMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: iFMs 7 | Epoch: 3 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg_w0: 2 12 | l2_reg_w: 8 13 | l2_reg_V: 16 14 | learn_rate: 0.004 15 | -------------------------------------------------------------------------------- /config/LastFM/iFMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: LastFM 3 | Trial: 1 4 | 5 | [Model] 6 | Name: iFMs 7 | Epoch: 3 8 | 9 | [Parameters] 10 | k: 20 11 | l2_reg_w0: 2 12 | l2_reg_w: 8 13 | l2_reg_V: 16 14 | learn_rate: 0.004 15 | -------------------------------------------------------------------------------- /config/ML100k/iFMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 5 4 | 5 | [Model] 6 | Name: iFMs 7 | Epoch: 3 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg_w0: 2 12 | l2_reg_w: 8. 13 | l2_reg_V: 16 14 | learn_rate: 0.004 15 | -------------------------------------------------------------------------------- /config/ML1M/static_FMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML1M 3 | Trial: 1 4 | 5 | [Model] 6 | Name: static-FMs 7 | Epoch: 3 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg_w0: 2 12 | l2_reg_w: 8 13 | l2_reg_V: 16 14 | learn_rate: 0.008 15 | -------------------------------------------------------------------------------- /config/click/iFMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 5 4 | 5 | [Model] 6 | Name: iFMs 7 | Epoch: 5 8 | 9 | [Parameters] 10 | k: 2 11 | l2_reg_w0: 0.01 12 | l2_reg_w: 0.01 13 | l2_reg_V: 0.01 14 | learn_rate: 0.00006 15 | -------------------------------------------------------------------------------- /config/LastFM/static_FMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: LastFM 3 | Trial: 1 4 | 5 | [Model] 6 | Name: static-FMs 7 | Epoch: 3 8 | 9 | [Parameters] 10 | k: 20 11 | l2_reg_w0: 2 12 | l2_reg_w: 8 13 | l2_reg_V: 16 14 | learn_rate: 0.002 15 | -------------------------------------------------------------------------------- /config/ML100k/static_FMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: ML100k 3 | Trial: 5 4 | 5 | [Model] 6 | Name: static-FMs 7 | Epoch: 3 8 | 9 | [Parameters] 10 | k: 40 11 | l2_reg_w0: 2 12 | l2_reg_w: 8. 13 | l2_reg_V: 16 14 | learn_rate: 0.004 15 | -------------------------------------------------------------------------------- /config/click/static_FMs.ini: -------------------------------------------------------------------------------- 1 | [Common] 2 | Dataset: click 3 | Trial: 5 4 | 5 | [Model] 6 | Name: static-FMs 7 | Epoch: 5 8 | 9 | [Parameters] 10 | k: 2 11 | l2_reg_w0: 0.01 12 | l2_reg_w: 0.01 13 | l2_reg_V: 0.01 14 | learn_rate: 0.00006 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Item Recommendation in a Streaming Environment 2 | === 3 | 4 | This repository discloses implementation used in the following research papers: 5 | 6 | - T. Kitazawa. **[Incremental Factorization Machines for Persistently Cold-Starting Online Item Recommendation](https://arxiv.org/abs/1607.02858)**. arXiv:1607.02858 [cs.LG], July 2016. 7 | - T. Kitazawa. **[Sketching Dynamic User-Item Interactions for Online Item Recommendation](http://dl.acm.org/citation.cfm?id=3022152)**. In *Proc. of CHIIR 2017*, March 2017. 8 | 9 | Recommendation algorithms are implemented in [FluRS](https://github.com/takuti/flurs/tree/0.0.1), a Python library for online item recommendation tasks. 10 | 11 | ## Usage 12 | 13 | $ python experiment.py -f path/to/config/file.ini 14 | 15 | Examples of config files are available at: [config/](config/) 16 | 17 | The results will be written text files under *results/*. -------------------------------------------------------------------------------- /converter/converter.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from .MovieLens1M import MovieLens1MConverter 4 | from .MovieLens100k import MovieLens100kConverter 5 | from .LastFM import LastFMConverter 6 | from .SyntheticClick import SyntheticClickConverter 7 | 8 | 9 | class Converter: 10 | 11 | def __init__(self): 12 | pass 13 | 14 | def convert(self, dataset='ML1M'): 15 | """Convert a specified dataset to be used by experiments. 16 | 17 | Args: 18 | dataset (str): Dataset name. 19 | 20 | Returns: 21 | instance of a converter: Its instance variables are used by experiments. 22 | 23 | """ 24 | if dataset == 'ML1M': 25 | c = MovieLens1MConverter() 26 | elif dataset == 'ML100k': 27 | c = MovieLens100kConverter() 28 | elif dataset == 'LastFM': 29 | c = LastFMConverter() 30 | elif dataset == 'click': 31 | c = SyntheticClickConverter() 32 | 33 | c.convert() 34 | return c 35 | -------------------------------------------------------------------------------- /tool/clickgenerator.jl: -------------------------------------------------------------------------------- 1 | using SyntheticImplicitFeedback 2 | 3 | # define a set of features 4 | features = Feature[] 5 | 6 | push!(features, Feature("Age", 1950, () -> rand(1930:2000))) 7 | push!(features, Feature("Sex", 0, () -> rand(0:1))) 8 | push!(features, Feature("Geo", 1, () -> rand(1:50))) # US has 50 states 9 | push!(features, Feature("Ad", 0, () -> rand(0:4))) 10 | 11 | # define a set of rules 12 | rules = Rule[] 13 | 14 | push!(rules, Rule(s -> true, 0.001)) 15 | push!(rules, Rule(s -> s["Ad"] == 2, 0.01)) 16 | push!(rules, Rule(s -> s["Age"] >= 1980 && s["Age"] <= 1989 && s["Geo"] == 32 && s["Ad"] == 0, 0.30)) # New York 17 | push!(rules, Rule(s -> s["Age"] >= 1950 && s["Age"] <= 1959 && s["Geo"] == 32 && s["Ad"] == 1, 0.30)) 18 | push!(rules, Rule(s -> s["Age"] >= 1980 && s["Age"] <= 1989 && s["Geo"] == 3 && s["Ad"] == 1, 0.30)) # Arizona 19 | push!(rules, Rule(s -> s["Age"] >= 1950 && s["Age"] <= 1959 && s["Geo"] == 3 && s["Ad"] == 0, 0.30)) 20 | 21 | function run(features, rules, N) 22 | for i in 1:N 23 | s = Dict() 24 | 25 | for f in features 26 | s[f.name] = f.random() 27 | end 28 | 29 | generate(s, rules) && println(join(values(s), "\t")) 30 | end 31 | end 32 | 33 | # first generate 0.5 million impressions 34 | run(features, rules, 500000) 35 | 36 | # change the most popular ad from #2 to #3 37 | rules[2].f = (s -> s["Ad"] == 3) 38 | 39 | # generate additional 0.5 million impressions 40 | run(features, rules, 500000) 41 | -------------------------------------------------------------------------------- /converter/LastFM.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from flurs.data.entity import User, Item, Event 4 | import numpy as np 5 | import pandas as pd 6 | import os 7 | 8 | 9 | class LastFMConverter: 10 | 11 | """See `notebook/LastFM.ipynb` to know how `lastfm.tsv` was generated. 12 | """ 13 | 14 | def __init__(self): 15 | # contexts in this dataset 16 | # others: 1 normalized time 17 | # user: 1 standardized age, 1 sex, 16 for country 18 | # item: none 19 | self.contexts = {'others': 1, 'user': 18, 'item': 1} 20 | 21 | self.can_repeat = True 22 | 23 | def convert(self): 24 | """Create a list of samples and count number of users/items. 25 | 26 | """ 27 | df_lastfm = pd.read_csv(os.path.join(os.path.dirname(__file__), '../data/lastfm.tsv'), delimiter='\t') 28 | 29 | self.samples = [] 30 | self.dts = [] 31 | 32 | # number of artists will be dimension of item contexts 33 | n_artist = len(set(df_lastfm['artist_index'])) 34 | self.contexts['item'] = n_artist 35 | 36 | countries = list(set(df_lastfm['country'])) 37 | n_country = len(countries) # 16 in total 38 | d_country = dict(zip(countries, range(n_country))) 39 | 40 | for i, row in df_lastfm.iterrows(): 41 | country_vec = np.zeros(n_country) 42 | country_vec[d_country[row['country']]] = 1. 43 | 44 | user = User(row['u_index'], 45 | np.concatenate((np.array([row['age']]), np.array([row['gender']]), country_vec))) 46 | 47 | artist_vec = np.zeros(n_artist) 48 | artist_vec[row['artist_index']] = 1 49 | 50 | item = Item(row['i_index'], artist_vec) 51 | 52 | sample = Event(user, item, 1., np.array([row['time']])) 53 | self.samples.append(sample) 54 | 55 | self.dts.append(row['dt']) 56 | 57 | self.n_user = len(set(df_lastfm['userid'])) 58 | self.n_item = len(set(df_lastfm['track-id'])) 59 | self.n_sample = len(self.samples) 60 | self.n_batch_train = int(self.n_sample * 0.2) # 20% for pre-training to avoid cold-start 61 | self.n_batch_test = int(self.n_sample * 0.1) # 10% for evaluation of pre-training 62 | self.n_test = self.n_sample - (self.n_batch_train + self.n_batch_test) 63 | -------------------------------------------------------------------------------- /converter/SyntheticClick.py: -------------------------------------------------------------------------------- 1 | # conding: utf-8 2 | 3 | from flurs.data.entity import User, Item, Event 4 | import numpy as np 5 | import os 6 | 7 | 8 | class SyntheticClickConverter: 9 | 10 | """Dataset will be generated as: 11 | 12 | $ julia tool/clickgenerator.jl > data/click.tsv 13 | """ 14 | 15 | def __init__(self): 16 | # contexts in this dataset 17 | # others: none 18 | # user: 1 normalized age, 1 sex, 50 for living state 19 | # item: 3 garegories 20 | self.contexts = {'others': 1, 'user': 52, 'item': 3} 21 | 22 | # 3 ad categories (e.g. life, tech, money) for 5 ad 23 | # (Google also has ad interest categories: https://support.google.com/ads/answer/2842480?hl=en) 24 | self.categories = [0, 0, 2, 2, 1] 25 | 26 | self.can_repeat = True 27 | 28 | def convert(self): 29 | """Create a list of samples and count number of users/items. 30 | 31 | """ 32 | 33 | clicks = [] 34 | with open(os.path.join(os.path.dirname(__file__), '../data/click.tsv')) as f: 35 | clicks = list(map(lambda l: list(map(int, l.rstrip().split('\t'))), f.readlines())) 36 | 37 | self.samples = [] 38 | 39 | u_index = 0 # each sample indicates different visitors 40 | n_geo = 50 # 50 states in US 41 | 42 | ad_ids = [] 43 | ad_categories = [] 44 | 45 | for ad_id, year, geo, sex in clicks: 46 | if ad_id not in ad_ids: 47 | ad_ids.append(ad_id) 48 | ad_categories.append(self.categories[ad_id]) 49 | i_index = ad_ids.index(ad_id) 50 | 51 | geo_vec = np.zeros(n_geo) 52 | geo_vec[geo - 1] = 1. 53 | 54 | # normalized age in [0, 1] 55 | # clickgenerator.jl generates a birth year in [1930, 2000] 56 | age = 1. - ((2000 - year) / 70.) 57 | 58 | user = User(0, np.concatenate((np.array([age]), np.array([sex]), geo_vec))) 59 | 60 | # category vector 61 | category = np.zeros(3) 62 | category[ad_categories[i_index]] = 1 63 | 64 | item = Item(i_index, category) 65 | 66 | sample = Event(user, item, 1.) 67 | self.samples.append(sample) 68 | 69 | u_index += 1 70 | 71 | self.n_user = u_index 72 | self.n_item = 5 # 5 ad variants 73 | self.n_sample = len(self.samples) 74 | self.n_batch_train = int(self.n_sample * 0.2) # 20% for pre-training to avoid cold-start 75 | self.n_batch_test = int(self.n_sample * 0.1) # 10% for evaluation of pre-training 76 | self.n_test = self.n_sample - (self.n_batch_train + self.n_batch_test) 77 | -------------------------------------------------------------------------------- /tool/parse_result.py: -------------------------------------------------------------------------------- 1 | import click 2 | import numpy as np 3 | 4 | 5 | def measure(n_item, at, metric, rank): 6 | # rank is in [0, n_item) 7 | if metric == 'recall': 8 | return 1. if (rank < at) else 0. 9 | elif metric == 'precision': 10 | return 1 / at if (rank < at) else 0. 11 | elif metric == 'map' or metric == 'mrr': 12 | return 1 / (rank + 1) 13 | elif metric == 'auc': 14 | correct = n_item - (rank + 1) 15 | pairs = 1 * (n_item - 1) 16 | return correct / pairs 17 | elif metric == 'mpr': 18 | return rank / (n_item - 1) * 100 19 | elif metric == 'ndcg': 20 | dcg = 1 / np.log2(rank + 2) if (rank < at) else 0. 21 | idcg = sum([1 / np.log2(n + 1) for n in range(1, at + 1)]) 22 | return dcg / idcg 23 | 24 | 25 | def parse_result(filepath, window_size, n_item, at=10, 26 | metrics=['recall', 'precision', 'map', 'mrr', 'auc', 'mpr', 'ndcg']): 27 | f = open(filepath) 28 | lines = [[float(v) for v in l.rstrip().split('\t')] for l in f.readlines()] 29 | f.close() 30 | 31 | mat = np.array(lines) 32 | n_test = mat.shape[0] 33 | ranks = mat[:, 1] 34 | 35 | windows = {metric: np.zeros(window_size) for metric in metrics} 36 | sums = {metric: 0 for metric in metrics} 37 | res = {metric: np.zeros(n_test) for metric in metrics} 38 | 39 | percentiles = np.zeros(n_test) 40 | 41 | for i, rank in enumerate(ranks): 42 | for metric in metrics: 43 | wi = i % window_size 44 | 45 | old = windows[metric][wi] 46 | 47 | new = measure(n_item, at, metric, rank) 48 | windows[metric][wi] = new 49 | 50 | sums[metric] = sums[metric] - old + new 51 | 52 | res[metric][i] = sums[metric] / min(i + 1, window_size) 53 | 54 | percentiles[i] = measure(n_item, at, 'mpr', rank) 55 | 56 | return {**res, 57 | **{'top1_scores': mat[:, 0], 58 | 'avg_recommend': np.mean(mat[:, 2]), 59 | 'avg_update': np.mean(mat[:, 3]), 60 | 'static_mpr': np.mean(percentiles), 61 | 'static_recall': np.mean((ranks < at).astype(np.int))}} 62 | 63 | 64 | @click.command() 65 | @click.option('--filepath', '-f', help='Give a path to your result file.') 66 | @click.option('--window_size', '-w', default=1, help='Size of a window for stream evaluation.') 67 | @click.option('--at', default=10, help='Evaluate recall@{at}.') 68 | @click.option('--n_item', default=5, help='Number of items on the dataset.') 69 | def cli(filepath, window_size, at, n_item): 70 | res = parse_result(filepath, window_size, at, n_item) 71 | 72 | f = open(filepath + '.evaluate.txt', 'w') 73 | f.write(res['avg_recommend'] + '\n') 74 | f.write(res['avg_update'] + '\n') 75 | f.write(res['MPR'] + '\n') 76 | f.write('\n'.join(map(str, res['incremental_recalls']))) 77 | f.close() 78 | 79 | 80 | if __name__ == '__main__': 81 | cli() 82 | -------------------------------------------------------------------------------- /converter/MovieLens1M.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from flurs.data.entity import User, Item, Event 4 | 5 | import numpy as np 6 | import time 7 | import os 8 | from calendar import monthrange 9 | from datetime import datetime, timedelta 10 | 11 | 12 | class MovieLens1MConverter: 13 | 14 | def __init__(self): 15 | here = os.path.dirname(__file__) 16 | self.path = { 17 | 'ratings': os.path.join(here, '../data/ml-1m/ratings.dat'), 18 | 'items': os.path.join(here, '../data/ml-1m/movies.dat'), 19 | 'users': os.path.join(here, '../data/ml-1m/users.dat') 20 | } 21 | 22 | # contexts in this dataset 23 | # 1 delta time, 18 genres, and 23 demographics (1 for M/F, 1 for age, 21 for occupation(0-20)) 24 | # 7 for day of week, 18 for the last rated item genres, 7 for the last day of week 25 | self.contexts = {'others': 7 + 18 + 7, 'item': 18, 'user': 23} 26 | 27 | self.can_repeat = False 28 | 29 | def convert(self): 30 | """Create a list of samples and count number of users/items. 31 | 32 | """ 33 | self.__load_ratings() 34 | 35 | users = self.__load_users() 36 | movies, movie_titles = self.__load_movies() 37 | 38 | user_ids = [] 39 | item_ids = [] 40 | 41 | self.samples = [] 42 | 43 | head_date = datetime(*time.localtime(self.ratings[0, 3])[:6]) 44 | self.dts = [] 45 | 46 | last = {} 47 | 48 | for user_id, item_id, rating, timestamp in self.ratings: 49 | # give an unique user index 50 | if user_id not in user_ids: 51 | user_ids.append(user_id) 52 | u_index = user_ids.index(user_id) 53 | 54 | # give an unique item index 55 | if item_id not in item_ids: 56 | item_ids.append(item_id) 57 | i_index = item_ids.index(item_id) 58 | 59 | # delta days 60 | date = datetime(*time.localtime(timestamp)[:6]) 61 | dt = self.__delta(head_date, date) 62 | self.dts.append(dt) 63 | 64 | weekday_vec = np.zeros(7) 65 | weekday_vec[date.weekday()] = 1 66 | 67 | if user_id in last: 68 | last_item_vec = last[user_id]['item'] 69 | last_weekday_vec = last[user_id]['weekday'] 70 | else: 71 | last_item_vec = np.zeros(18) 72 | last_weekday_vec = np.zeros(7) 73 | 74 | others = np.concatenate((weekday_vec, last_item_vec, last_weekday_vec)) 75 | 76 | user = User(u_index, users[user_id]) 77 | item = Item(i_index, movies[item_id]) 78 | 79 | sample = Event(user, item, 1., others) 80 | self.samples.append(sample) 81 | 82 | # record users' last rated movie features 83 | last[user_id] = {'item': movies[item_id], 'weekday': weekday_vec} 84 | 85 | self.n_user = len(user_ids) 86 | self.n_item = len(item_ids) 87 | self.n_sample = len(self.samples) 88 | self.n_batch_train = int(self.n_sample * 0.2) # 20% for pre-training to avoid cold-start 89 | self.n_batch_test = int(self.n_sample * 0.1) # 10% for evaluation of pre-training 90 | self.n_test = self.n_sample - (self.n_batch_train + self.n_batch_test) 91 | 92 | def __load_movies(self): 93 | """Load movie genres as a context. 94 | 95 | Returns: 96 | dict of movie vectors: item_id -> numpy array (n_genre,) 97 | 98 | """ 99 | with open(self.path['items'], encoding='ISO-8859-1') as f: 100 | lines = list(map(lambda l: l.rstrip().split('::'), f.readlines())) 101 | 102 | all_genres = ['Action', 103 | 'Adventure', 104 | 'Animation', 105 | "Children's", 106 | 'Comedy', 107 | 'Crime', 108 | 'Documentary', 109 | 'Drama', 110 | 'Fantasy', 111 | 'Film-Noir', 112 | 'Horror', 113 | 'Musical', 114 | 'Mystery', 115 | 'Romance', 116 | 'Sci-Fi', 117 | 'Thriller', 118 | 'War', 119 | 'Western'] 120 | n_genre = len(all_genres) 121 | 122 | movies = {} 123 | movie_titles = {} 124 | for item_id_str, title, genres in lines: 125 | movie_vec = np.zeros(n_genre) 126 | for genre in genres.split('|'): 127 | i = all_genres.index(genre) 128 | movie_vec[i] = 1. 129 | item_id = int(item_id_str) 130 | movies[item_id] = movie_vec 131 | movie_titles[item_id] = title 132 | 133 | return movies, movie_titles 134 | 135 | def __load_users(self): 136 | """Load user demographics as contexts.User ID -> {sex (M/F), age (7 groupd), occupation(0-20; 21)} 137 | 138 | Returns: 139 | dict of user vectors: user_id -> numpy array (1+1+21,); (sex_flg + age_group + n_occupation, ) 140 | 141 | """ 142 | with open(self.path['users'], encoding='ISO-8859-1') as f: 143 | lines = list(map(lambda l: l.rstrip().split('::'), f.readlines())) 144 | 145 | ages = [1, 18, 25, 35, 45, 50, 56] 146 | 147 | users = {} 148 | for user_id_str, sex_str, age_str, occupation_str, zip_code in lines: 149 | user_vec = np.zeros(1 + 1 + 21) # 1 categorical, 1 value, 21 categorical 150 | user_vec[0] = 0 if sex_str == 'M' else 1 # sex 151 | user_vec[1] = ages.index(int(age_str)) # age group (1, 18, ...) 152 | user_vec[2 + int(occupation_str)] = 1 # occupation (1-of-21) 153 | users[int(user_id_str)] = user_vec 154 | 155 | return users 156 | 157 | def __load_ratings(self): 158 | """Load all samples in the dataset. 159 | 160 | """ 161 | ratings = [] 162 | with open(self.path['ratings'], encoding='ISO-8859-1') as f: 163 | lines = list(map(lambda l: list(map(int, l.rstrip().split('::'))), f.readlines())) 164 | for l in lines: 165 | # Since we consider positive-only feedback setting, ratings < 5 will be excluded. 166 | if l[2] == 5: 167 | ratings.append(l) 168 | self.ratings = np.asarray(ratings) 169 | 170 | # sorted by timestamp 171 | self.ratings = self.ratings[np.argsort(self.ratings[:, 3])] 172 | 173 | def __delta(self, d1, d2, opt='d'): 174 | """Compute difference between given 2 dates in month/day. 175 | 176 | """ 177 | delta = 0 178 | 179 | if opt == 'm': 180 | while True: 181 | mdays = monthrange(d1.year, d1.month)[1] 182 | d1 += timedelta(days=mdays) 183 | if d1 <= d2: 184 | delta += 1 185 | else: 186 | break 187 | else: 188 | delta = (d2 - d1).days 189 | 190 | return delta 191 | -------------------------------------------------------------------------------- /converter/MovieLens100k.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | from flurs.data.entity import User, Item, Event 4 | 5 | import numpy as np 6 | import time 7 | import os 8 | from calendar import monthrange 9 | from datetime import datetime, timedelta 10 | 11 | 12 | class MovieLens100kConverter: 13 | 14 | def __init__(self): 15 | here = os.path.dirname(__file__) 16 | self.path = { 17 | 'ratings': os.path.join(here, '../data/ml-100k/u.data'), 18 | 'items': os.path.join(here, '../data/ml-100k/u.item'), 19 | 'users': os.path.join(here, '../data/ml-100k/u.user') 20 | } 21 | 22 | # contexts in this dataset 23 | # 18 genres, and 23 demographics (1 for M/F, 1 for age, 21 for occupation(0-20)) 24 | # 7 for day of week, 18 for the last rated item genres, 7 for the last day of week 25 | self.contexts = {'others': 7 + 18 + 7, 'item': 18, 'user': 23} 26 | 27 | self.can_repeat = False 28 | 29 | def convert(self): 30 | """Create a list of samples and count number of users/items. 31 | 32 | """ 33 | self.__load_ratings() 34 | 35 | users = self.__load_users() 36 | movies = self.__load_movies() 37 | 38 | user_ids = [] 39 | item_ids = [] 40 | 41 | self.samples = [] 42 | 43 | head_date = datetime(*time.localtime(self.ratings[0, 3])[:6]) 44 | self.dts = [] 45 | 46 | last = {} 47 | 48 | for user_id, item_id, rating, timestamp in self.ratings: 49 | # give an unique user index 50 | if user_id not in user_ids: 51 | user_ids.append(user_id) 52 | u_index = user_ids.index(user_id) 53 | 54 | # give an unique item index 55 | if item_id not in item_ids: 56 | item_ids.append(item_id) 57 | i_index = item_ids.index(item_id) 58 | 59 | # delta days 60 | date = datetime(*time.localtime(timestamp)[:6]) 61 | dt = self.__delta(head_date, date) 62 | self.dts.append(dt) 63 | 64 | weekday_vec = np.zeros(7) 65 | weekday_vec[date.weekday()] = 1 66 | 67 | if user_id in last: 68 | last_item_vec = last[user_id]['item'] 69 | last_weekday_vec = last[user_id]['weekday'] 70 | else: 71 | last_item_vec = np.zeros(18) 72 | last_weekday_vec = np.zeros(7) 73 | 74 | others = np.concatenate((weekday_vec, last_item_vec, last_weekday_vec)) 75 | 76 | user = User(u_index, users[user_id]) 77 | item = Item(i_index, movies[item_id]) 78 | 79 | sample = Event(user, item, 1., others) 80 | self.samples.append(sample) 81 | 82 | # record users' last rated movie features 83 | last[user_id] = {'item': movies[item_id], 'weekday': weekday_vec} 84 | 85 | self.n_user = len(user_ids) 86 | self.n_item = len(item_ids) 87 | self.n_sample = len(self.samples) 88 | self.n_batch_train = int(self.n_sample * 0.2) # 20% for pre-training to avoid cold-start 89 | self.n_batch_test = int(self.n_sample * 0.1) # 10% for evaluation of pre-training 90 | self.n_test = self.n_sample - (self.n_batch_train + self.n_batch_test) 91 | 92 | def __load_movies(self): 93 | """Load movie genres as a context. 94 | 95 | Returns: 96 | dict of movie vectors: item_id -> numpy array (n_genre,) 97 | 98 | """ 99 | with open(self.path['items'], encoding='ISO-8859-1') as f: 100 | lines = list(map(lambda l: l.rstrip().split('|'), f.readlines())) 101 | 102 | all_genres = ['Action', 103 | 'Adventure', 104 | 'Animation', 105 | "Children's", 106 | 'Comedy', 107 | 'Crime', 108 | 'Documentary', 109 | 'Drama', 110 | 'Fantasy', 111 | 'Film-Noir', 112 | 'Horror', 113 | 'Musical', 114 | 'Mystery', 115 | 'Romance', 116 | 'Sci-Fi', 117 | 'Thriller', 118 | 'War', 119 | 'Western'] 120 | n_genre = len(all_genres) 121 | 122 | movies = {} 123 | for line in lines: 124 | movie_vec = np.zeros(n_genre) 125 | for i, flg_chr in enumerate(line[-n_genre:]): 126 | if flg_chr == '1': 127 | movie_vec[i] = 1. 128 | movie_id = int(line[0]) 129 | movies[movie_id] = movie_vec 130 | 131 | return movies 132 | 133 | def __load_users(self): 134 | """Load user demographics as contexts.User ID -> {sex (M/F), age (7 groupd), occupation(0-20; 21)} 135 | 136 | Returns: 137 | dict of user vectors: user_id -> numpy array (1+1+21,); (sex_flg + age_group + n_occupation, ) 138 | 139 | """ 140 | with open(self.path['users'], encoding='ISO-8859-1') as f: 141 | lines = list(map(lambda l: l.rstrip().split('|'), f.readlines())) 142 | 143 | ages = [1, 18, 25, 35, 45, 50, 56, 999] 144 | 145 | all_occupations = ['administrator', 146 | 'artist', 147 | 'doctor', 148 | 'educator', 149 | 'engineer', 150 | 'entertainment', 151 | 'executive', 152 | 'healthcare', 153 | 'homemaker', 154 | 'lawyer', 155 | 'librarian', 156 | 'marketing', 157 | 'none', 158 | 'other', 159 | 'programmer', 160 | 'retired', 161 | 'salesman', 162 | 'scientist', 163 | 'student', 164 | 'technician', 165 | 'writer'] 166 | 167 | users = {} 168 | for user_id_str, age_str, sex_str, occupation_str, zip_code in lines: 169 | user_vec = np.zeros(1 + 1 + 21) # 1 categorical, 1 value, 21 categorical 170 | user_vec[0] = 0 if sex_str == 'M' else 1 # sex 171 | 172 | # age (ML1M is "age group", but 100k has actual "age") 173 | age = int(age_str) 174 | for i in range(7): 175 | if age >= ages[i] and age < ages[i + 1]: 176 | user_vec[1] = i 177 | break 178 | 179 | user_vec[2 + all_occupations.index(occupation_str)] = 1 # occupation (1-of-21) 180 | users[int(user_id_str)] = user_vec 181 | 182 | return users 183 | 184 | def __load_ratings(self): 185 | """Load all samples in the dataset. 186 | 187 | """ 188 | ratings = [] 189 | with open(self.path['ratings'], encoding='ISO-8859-1') as f: 190 | lines = list(map(lambda l: list(map(int, l.rstrip().split('\t'))), f.readlines())) 191 | for l in lines: 192 | # Since we consider positive-only feedback setting, ratings < 5 will be excluded. 193 | if l[2] == 5: 194 | ratings.append(l) 195 | self.ratings = np.asarray(ratings) 196 | 197 | # sorted by timestamp 198 | self.ratings = self.ratings[np.argsort(self.ratings[:, 3])] 199 | 200 | def __delta(self, d1, d2, opt='d'): 201 | """Compute difference between given 2 dates in month/day. 202 | 203 | """ 204 | delta = 0 205 | 206 | if opt == 'm': 207 | while True: 208 | mdays = monthrange(d1.year, d1.month)[1] 209 | d1 += timedelta(days=mdays) 210 | if d1 <= d2: 211 | delta += 1 212 | else: 213 | break 214 | else: 215 | delta = (d2 - d1).days 216 | 217 | return delta 218 | -------------------------------------------------------------------------------- /experiment.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | import click 4 | 5 | from flurs.recommender.user_knn import UserKNNRecommender 6 | from flurs.recommender.mf import MFRecommender 7 | from flurs.recommender.bprmf import BPRMFRecommender 8 | from flurs.recommender.fm import FMRecommender 9 | from flurs.recommender.sketch import SketchRecommender 10 | from flurs.baseline.random import Random 11 | from flurs.baseline.popular import Popular 12 | 13 | from flurs.evaluator import Evaluator 14 | 15 | from converter.converter import Converter 16 | 17 | from logging import getLogger, StreamHandler, Formatter, DEBUG 18 | logger = getLogger(__name__) 19 | handler = StreamHandler() 20 | handler.setFormatter(Formatter('[%(process)d] %(message)s')) 21 | handler.setLevel(DEBUG) 22 | logger.setLevel(DEBUG) 23 | logger.addHandler(handler) 24 | 25 | 26 | class Runner: 27 | 28 | def __init__(self, params, dataset='ML1M', n_epoch=1): 29 | self.params = params 30 | 31 | # number of epochs for the batch training 32 | self.n_epoch = n_epoch 33 | 34 | # load dataset 35 | self.data = Converter().convert(dataset=dataset) 36 | 37 | logger.debug('[exp] %s | n_epoch = %d' % (dataset, n_epoch)) 38 | logger.debug('[exp] n_sample = %d; %d (20%%) + %d (10%%) + %d (70%%)' % ( 39 | self.data.n_sample, self.data.n_batch_train, self.data.n_batch_test, self.data.n_test)) 40 | logger.debug('[exp] n_user = %d, n_item = %d' % (self.data.n_user, self.data.n_item)) 41 | 42 | def iMF(self, is_static=False): 43 | """Incremental Matrix Factorization 44 | 45 | Args: 46 | is_static (bool): choose whether a model is incrementally updated. 47 | True -- baseline 48 | False -- incremental matrix factorization 49 | 50 | Returns: 51 | list of float values: Simple Moving Averages (i.e. incremental recall). 52 | float: average time to recommend/update for one sample 53 | 54 | """ 55 | if is_static: 56 | logger.debug('# static MF') 57 | else: 58 | logger.debug('# iMF') 59 | 60 | def create(): 61 | rec = MFRecommender(int(self.params['k']), 62 | self.params['l2_reg'], 63 | self.params['learn_rate']) 64 | rec.init_recommender(is_static) 65 | return rec 66 | 67 | model, res = self.__run(create) 68 | return res 69 | 70 | def bprmf(self): 71 | """Incremental Matrix Factorization with BPR optimization 72 | 73 | Returns: 74 | list of float values: Simple Moving Averages (i.e. incremental recall). 75 | float: average time to recommend/update for one sample 76 | 77 | """ 78 | logger.debug('# BPRMF') 79 | 80 | def create(): 81 | rec = BPRMFRecommender(int(self.params['k']), 82 | self.params['l2_reg'], 83 | self.params['learn_rate']) 84 | rec.init_recommender() 85 | return rec 86 | 87 | model, res = self.__run(create) 88 | return res 89 | 90 | def iFMs(self, is_context_aware=False, is_static=False): 91 | """Incremental Factorization Machines 92 | 93 | Args: 94 | is_context_aware (bool): Choose whether a feature vector incorporates contextual variables of the dataset. 95 | 96 | Returns: 97 | list of float values: Simple Moving Averages (i.e. incremental recall). 98 | float: average time to recommend/update for one sample 99 | 100 | """ 101 | if is_static: 102 | logger.debug('# static FMs') 103 | else: 104 | logger.debug('# iFMs') 105 | 106 | def create(): 107 | rec = FMRecommender(sum(self.data.contexts.values()), 108 | int(self.params['k']), 109 | self.params['l2_reg_w0'], 110 | self.params['l2_reg_w'], 111 | self.params['l2_reg_v'], 112 | self.params['learn_rate']) 113 | rec.init_recommender(is_static) 114 | return rec 115 | 116 | model, res = self.__run(create) 117 | 118 | logger.debug( 119 | 'Regularization parameters: w0 = %s, w = %s, V = %s' % ( 120 | model.l2_reg_w0, 121 | model.l2_reg_w, 122 | model.l2_reg_V)) 123 | 124 | return res 125 | 126 | def user_knn(self): 127 | """User-based incremental collaborative filtering. 128 | 129 | Returns: 130 | list of float values: Simple Moving Averages (i.e. incremental recall). 131 | float: average time to recommend/update for one sample 132 | 133 | """ 134 | logger.debug('# user knn') 135 | 136 | def create(): 137 | rec = UserKNNRecommender(k=int(self.params['k'])) 138 | rec.init_recommender() 139 | return rec 140 | 141 | model, res = self.__run(create) 142 | 143 | return res 144 | 145 | def sketch(self): 146 | """Online Matrix Sketching 147 | 148 | Returns: 149 | list of float values: Simple Moving Averages (i.e. incremental recall). 150 | float: average time to recommend/update for one sample 151 | 152 | """ 153 | logger.debug('# matrix sketching') 154 | 155 | def create(): 156 | rec = SketchRecommender(p=sum(self.data.contexts.values()), 157 | k=int(self.params['k']), 158 | ell=int(self.params['ell'])) 159 | rec.init_recommender() 160 | return rec 161 | 162 | model, res = self.__run(create) 163 | 164 | return res 165 | 166 | def random(self): 167 | """Random baseline 168 | 169 | Returns: 170 | list of float values: Simple Moving Averages (i.e. incremental recall). 171 | float: average time to recommend/update for one sample 172 | 173 | """ 174 | logger.debug('# random baseline') 175 | 176 | def create(): 177 | rec = Random() 178 | rec.init_recommender() 179 | return rec 180 | 181 | model, res = self.__run(create) 182 | 183 | return res 184 | 185 | def popular(self): 186 | """Popularity (non-personalized) baseline 187 | 188 | Returns: 189 | list of float values: Simple Moving Averages (i.e. incremental recall). 190 | float: average time to recommend/update for one sample 191 | 192 | """ 193 | logger.debug('# popularity baseline') 194 | 195 | def create(): 196 | rec = Popular() 197 | rec.init_recommender() 198 | return rec 199 | 200 | model, res = self.__run(create) 201 | 202 | return res 203 | 204 | def __run(self, callback): 205 | """Test runner. 206 | 207 | Args: 208 | callback (function): Create a model used by this test run. 209 | 210 | Returns: 211 | instance of incremental model class: Created by the callback function. 212 | list of float values: Simple Moving Averages (i.e. incremental recall). 213 | float: average time to recommend/update for one sample 214 | 215 | """ 216 | batch_tail = self.data.n_batch_train + self.data.n_batch_test 217 | 218 | model = callback() 219 | if hasattr(self.data, 'maxlen'): 220 | evaluator = Evaluator(model, self.data.can_repeat, self.data.maxlen) 221 | else: 222 | evaluator = Evaluator(model, self.data.can_repeat) 223 | 224 | # pre-train 225 | # 20% for batch training | 10% for batch evaluate 226 | # after the batch training, 10% samples are used for incremental updating 227 | evaluator.fit( 228 | self.data.samples[:self.data.n_batch_train], 229 | self.data.samples[self.data.n_batch_train:batch_tail], 230 | n_epoch=self.n_epoch 231 | ) 232 | 233 | # 70% incremental evaluation and updating 234 | res = evaluator.evaluate(self.data.samples[batch_tail:]) 235 | 236 | return model, res 237 | 238 | 239 | @click.command() 240 | @click.option('--config', '-f', help='Give a path to your config file.') 241 | def cli(config): 242 | 243 | def save(path, res_tuples): 244 | f = open(path, 'w') 245 | lines = ['\t'.join([str(v) for v in t]) for t in res_tuples] 246 | f.write('\n'.join(lines)) 247 | f.close() 248 | 249 | # parse given config file 250 | parser = configparser.ConfigParser() 251 | parser.read(config) 252 | 253 | c = parser['Common'] 254 | dataset = c.get('Dataset') # ['ML1M', 'ML100k', 'LastFM', 'click'] 255 | n_trial = c.getint('Trial', 1) 256 | 257 | # ['static-MF', 'iMF', 'static-FMs', 'iFMs', 'sketch', 'random', 'popular'] 258 | m = parser['Model'] 259 | model = m['Name'] 260 | n_epoch = m.getint('Epoch', 1) 261 | 262 | if 'Parameters' in parser: 263 | params = dict([(k, float(v)) for k, v in parser['Parameters'].items()]) 264 | else: 265 | params = {} 266 | 267 | exp = Runner(params=params, dataset=dataset, n_epoch=n_epoch) 268 | 269 | for i in range(n_trial): 270 | if model == 'static-MF' or model == 'iMF': 271 | res = exp.iMF(is_static=True) if model == 'static-MF' else exp.iMF() 272 | elif model == 'bprmf': 273 | res = exp.bprmf() 274 | elif model == 'user-knn': 275 | res = exp.user_knn() 276 | elif model == 'sketch': 277 | res = exp.sketch() 278 | elif model == 'random': 279 | res = exp.random() 280 | elif model == 'popular': 281 | res = exp.popular() 282 | elif model == 'static-FMs' or model == 'iFMs': 283 | res = exp.iFMs(is_static=True) if model == 'static-FMs' else exp.iFMs() 284 | 285 | save('results/%s_%s_%s.tsv' % (dataset, model, i + 1), list(res)) 286 | 287 | 288 | if __name__ == '__main__': 289 | import configparser 290 | cli() 291 | -------------------------------------------------------------------------------- /notebook/LastFM.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Preprocessing for the LastFM dataset" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "collapsed": true 15 | }, 16 | "outputs": [], 17 | "source": [ 18 | "import numpy as np\n", 19 | "import pandas as pd\n", 20 | "\n", 21 | "import time\n", 22 | "from calendar import monthrange\n", 23 | "from datetime import datetime, timedelta" 24 | ] 25 | }, 26 | { 27 | "cell_type": "markdown", 28 | "metadata": {}, 29 | "source": [ 30 | "Convert the original TSV data into DataFrames, and sort them by timestamp" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 3, 36 | "metadata": { 37 | "collapsed": false 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "df_plays = pd.read_csv('../../../data/lastfm-dataset-1K/userid-timestamp-artid-artname-traid-traname.tsv',\n", 42 | " delimiter='\\t',\n", 43 | " header=None,\n", 44 | " names=('userid', 'timestamp', 'artist-id', 'artist-name', 'track-id', 'track-name'))\n", 45 | "\n", 46 | "df_users = pd.read_csv('../../../data/lastfm-dataset-1K/userid-profile.tsv',\n", 47 | " delimiter='\\t')\n", 48 | "\n", 49 | "df_lastfm = pd.merge(df_plays, df_users, how='inner', left_on='userid', right_on='#id').drop(\n", 50 | " ['#id', 'artist-name', 'track-name', 'registered'], axis=1).sort_values(\n", 51 | " by='timestamp').reset_index(drop=True)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 4, 57 | "metadata": { 58 | "collapsed": false 59 | }, 60 | "outputs": [ 61 | { 62 | "data": { 63 | "text/html": [ 64 | "
\n", 65 | "\n", 66 | " \n", 67 | " \n", 68 | " \n", 69 | " \n", 70 | " \n", 71 | " \n", 72 | " \n", 73 | " \n", 74 | " \n", 75 | " \n", 76 | " \n", 77 | " \n", 78 | " \n", 79 | " \n", 80 | " \n", 81 | " \n", 82 | " \n", 83 | " \n", 84 | " \n", 85 | " \n", 86 | " \n", 87 | " \n", 88 | " \n", 89 | " \n", 90 | " \n", 91 | " \n", 92 | " \n", 93 | " \n", 94 | " \n", 95 | " \n", 96 | " \n", 97 | " \n", 98 | " \n", 99 | " \n", 100 | " \n", 101 | " \n", 102 | " \n", 103 | " \n", 104 | " \n", 105 | " \n", 106 | " \n", 107 | " \n", 108 | " \n", 109 | " \n", 110 | " \n", 111 | " \n", 112 | " \n", 113 | " \n", 114 | " \n", 115 | " \n", 116 | " \n", 117 | " \n", 118 | " \n", 119 | " \n", 120 | " \n", 121 | " \n", 122 | " \n", 123 | " \n", 124 | " \n", 125 | " \n", 126 | " \n", 127 | " \n", 128 | " \n", 129 | " \n", 130 | "
useridtimestampartist-idtrack-idgenderagecountry
0user_0003912005-02-14T00:00:07Zfbd86487-ccb5-4a57-a860-cc3d360b51156b4977f4-3c7a-492a-af61-1e877fa66f52fNaNGermany
1user_0008712005-02-14T00:00:38Zb3a6ebdf-4ee6-4ec0-912c-be03ade6c8339ecc2ab3-7294-43ad-bdeb-f51388a7a6e0NaNNaNArgentina
2user_0007092005-02-14T00:01:44Zb4d32cff-f19e-455f-86c4-f347d824ca611d0f1ea5-0a92-4578-a7e7-3f2a7129da61mNaNCanada
3user_0002852005-02-14T00:02:10Z95e1ead9-4d31-4808-a7ac-32c3614c116b46909ba9-46c7-461e-a2ef-280eacd550e4f23.0United States
4user_0001422005-02-14T00:02:40Z51086134-0896-4c00-b54a-c5c37aeaf2bf14025355-94c2-4e9b-b63f-c16cab9e8086NaNNaNNorway
\n", 131 | "
" 132 | ], 133 | "text/plain": [ 134 | " userid timestamp artist-id \\\n", 135 | "0 user_000391 2005-02-14T00:00:07Z fbd86487-ccb5-4a57-a860-cc3d360b5115 \n", 136 | "1 user_000871 2005-02-14T00:00:38Z b3a6ebdf-4ee6-4ec0-912c-be03ade6c833 \n", 137 | "2 user_000709 2005-02-14T00:01:44Z b4d32cff-f19e-455f-86c4-f347d824ca61 \n", 138 | "3 user_000285 2005-02-14T00:02:10Z 95e1ead9-4d31-4808-a7ac-32c3614c116b \n", 139 | "4 user_000142 2005-02-14T00:02:40Z 51086134-0896-4c00-b54a-c5c37aeaf2bf \n", 140 | "\n", 141 | " track-id gender age country \n", 142 | "0 6b4977f4-3c7a-492a-af61-1e877fa66f52 f NaN Germany \n", 143 | "1 9ecc2ab3-7294-43ad-bdeb-f51388a7a6e0 NaN NaN Argentina \n", 144 | "2 1d0f1ea5-0a92-4578-a7e7-3f2a7129da61 m NaN Canada \n", 145 | "3 46909ba9-46c7-461e-a2ef-280eacd550e4 f 23.0 United States \n", 146 | "4 14025355-94c2-4e9b-b63f-c16cab9e8086 NaN NaN Norway " 147 | ] 148 | }, 149 | "execution_count": 4, 150 | "metadata": {}, 151 | "output_type": "execute_result" 152 | } 153 | ], 154 | "source": [ 155 | "df_lastfm.head()" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "Obtain a subset of the dataset similary to:\n", 163 | "\n", 164 | "- [Fast Incremental Matrix Factorization for Recommendation with Positive-only Feedback](http://link.springer.com/chapter/10.1007%2F978-3-319-08786-3_41)\n", 165 | "- [Forgetting Methods for Incremental Matrix Factorization in Recommender Systems](http://dl.acm.org/citation.cfm?id=2695820)" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 5, 171 | "metadata": { 172 | "collapsed": false 173 | }, 174 | "outputs": [ 175 | { 176 | "data": { 177 | "text/plain": [ 178 | "(177196, 7)" 179 | ] 180 | }, 181 | "execution_count": 5, 182 | "metadata": {}, 183 | "output_type": "execute_result" 184 | } 185 | ], 186 | "source": [ 187 | "# 3 month from the first observation\n", 188 | "df_lastfm_3m = df_lastfm[df_lastfm['timestamp'] < '2005-05-15T00:00:00Z']\n", 189 | "df_lastfm_3m.shape" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 6, 195 | "metadata": { 196 | "collapsed": false 197 | }, 198 | "outputs": [ 199 | { 200 | "data": { 201 | "text/plain": [ 202 | "(159798, 7)" 203 | ] 204 | }, 205 | "execution_count": 6, 206 | "metadata": {}, 207 | "output_type": "execute_result" 208 | } 209 | ], 210 | "source": [ 211 | "# remove NaN track_id/artist_id events\n", 212 | "df_lastfm_3m = df_lastfm_3m.loc[pd.notnull(df_lastfm_3m['track-id'])].reset_index(drop=True)\n", 213 | "df_lastfm_3m = df_lastfm_3m.loc[pd.notnull(df_lastfm_3m['artist-id'])].reset_index(drop=True)\n", 214 | "df_lastfm_3m.shape" 215 | ] 216 | }, 217 | { 218 | "cell_type": "code", 219 | "execution_count": 7, 220 | "metadata": { 221 | "collapsed": false 222 | }, 223 | "outputs": [ 224 | { 225 | "data": { 226 | "text/html": [ 227 | "
\n", 228 | "\n", 229 | " \n", 230 | " \n", 231 | " \n", 232 | " \n", 233 | " \n", 234 | " \n", 235 | " \n", 236 | " \n", 237 | " \n", 238 | " \n", 239 | " \n", 240 | " \n", 241 | " \n", 242 | " \n", 243 | " \n", 244 | " \n", 245 | " \n", 246 | " \n", 247 | " \n", 248 | " \n", 249 | " \n", 250 | " \n", 251 | " \n", 252 | " \n", 253 | " \n", 254 | " \n", 255 | " \n", 256 | " \n", 257 | " \n", 258 | " \n", 259 | " \n", 260 | " \n", 261 | " \n", 262 | " \n", 263 | " \n", 264 | " \n", 265 | " \n", 266 | " \n", 267 | " \n", 268 | " \n", 269 | " \n", 270 | " \n", 271 | " \n", 272 | " \n", 273 | " \n", 274 | " \n", 275 | " \n", 276 | " \n", 277 | " \n", 278 | " \n", 279 | " \n", 280 | " \n", 281 | " \n", 282 | " \n", 283 | " \n", 284 | " \n", 285 | " \n", 286 | " \n", 287 | " \n", 288 | " \n", 289 | " \n", 290 | " \n", 291 | " \n", 292 | " \n", 293 | "
useridtimestampartist-idtrack-idgenderagecountry
159793user_0002982005-05-14T23:57:15Z79239441-bfd5-4981-a70c-55c3f15c12873dc94d29-3f33-4032-8772-e599f081d085m28.0Argentina
159794user_0008702005-05-14T23:58:42Z1928bd00-5ccb-4dcb-809e-4d1af14bce44e8ba000e-6304-473c-b6a7-49010b1433d7mNaNUnited Kingdom
159795user_0002932005-05-14T23:59:25Z64d62f45-e001-40a1-a055-c3545fcc14deb83f61e2-a566-49cc-b8dd-9ad5a0caddb2NaN39.0United Kingdom
159796user_0002982005-05-14T23:59:45Z60530915-9371-4b16-bc31-5c4456317ae022ea4524-3dcb-44e6-a1bc-4edfd25ad988m28.0Argentina
159797user_0001422005-05-14T23:59:51Z936addc3-91aa-49de-8ec0-0dc186de151fc0d44a5d-f84b-4d4b-babc-0f3937ef6edbNaNNaNNorway
\n", 294 | "
" 295 | ], 296 | "text/plain": [ 297 | " userid timestamp \\\n", 298 | "159793 user_000298 2005-05-14T23:57:15Z \n", 299 | "159794 user_000870 2005-05-14T23:58:42Z \n", 300 | "159795 user_000293 2005-05-14T23:59:25Z \n", 301 | "159796 user_000298 2005-05-14T23:59:45Z \n", 302 | "159797 user_000142 2005-05-14T23:59:51Z \n", 303 | "\n", 304 | " artist-id \\\n", 305 | "159793 79239441-bfd5-4981-a70c-55c3f15c1287 \n", 306 | "159794 1928bd00-5ccb-4dcb-809e-4d1af14bce44 \n", 307 | "159795 64d62f45-e001-40a1-a055-c3545fcc14de \n", 308 | "159796 60530915-9371-4b16-bc31-5c4456317ae0 \n", 309 | "159797 936addc3-91aa-49de-8ec0-0dc186de151f \n", 310 | "\n", 311 | " track-id gender age country \n", 312 | "159793 3dc94d29-3f33-4032-8772-e599f081d085 m 28.0 Argentina \n", 313 | "159794 e8ba000e-6304-473c-b6a7-49010b1433d7 m NaN United Kingdom \n", 314 | "159795 b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 NaN 39.0 United Kingdom \n", 315 | "159796 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 m 28.0 Argentina \n", 316 | "159797 c0d44a5d-f84b-4d4b-babc-0f3937ef6edb NaN NaN Norway " 317 | ] 318 | }, 319 | "execution_count": 7, 320 | "metadata": {}, 321 | "output_type": "execute_result" 322 | } 323 | ], 324 | "source": [ 325 | "df_lastfm_3m.tail()" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 12, 331 | "metadata": { 332 | "collapsed": false 333 | }, 334 | "outputs": [ 335 | { 336 | "data": { 337 | "text/plain": [ 338 | "(92, 51072, 8087, 17)" 339 | ] 340 | }, 341 | "execution_count": 12, 342 | "metadata": {}, 343 | "output_type": "execute_result" 344 | } 345 | ], 346 | "source": [ 347 | "user_ids = list(set(df_lastfm_3m['userid']))\n", 348 | "item_ids = list(set(df_lastfm_3m['track-id']))\n", 349 | "artist_ids = list(set(df_lastfm_3m['artist-id']))\n", 350 | "countries = list(set(df_lastfm_3m['country']))\n", 351 | "\n", 352 | "len(user_ids), len(item_ids), len(artist_ids), len(countries)" 353 | ] 354 | }, 355 | { 356 | "cell_type": "markdown", 357 | "metadata": {}, 358 | "source": [ 359 | "Fill NaN variables.\n", 360 | "\n", 361 | "- sex: male\n", 362 | "- age: avg. of the samples\n", 363 | "- country: United States" 364 | ] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": 13, 369 | "metadata": { 370 | "collapsed": false 371 | }, 372 | "outputs": [ 373 | { 374 | "data": { 375 | "text/html": [ 376 | "
\n", 377 | "\n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | " \n", 410 | " \n", 411 | " \n", 412 | " \n", 413 | " \n", 414 | " \n", 415 | " \n", 416 | " \n", 417 | " \n", 418 | " \n", 419 | " \n", 420 | " \n", 421 | " \n", 422 | " \n", 423 | " \n", 424 | " \n", 425 | " \n", 426 | " \n", 427 | " \n", 428 | " \n", 429 | " \n", 430 | " \n", 431 | " \n", 432 | " \n", 433 | " \n", 434 | " \n", 435 | " \n", 436 | " \n", 437 | " \n", 438 | " \n", 439 | " \n", 440 | " \n", 441 | " \n", 442 | "
useridtimestampartist-idtrack-idgenderagecountry
159793user_0002982005-05-14T23:57:15Z79239441-bfd5-4981-a70c-55c3f15c12873dc94d29-3f33-4032-8772-e599f081d085m28.000000Argentina
159794user_0008702005-05-14T23:58:42Z1928bd00-5ccb-4dcb-809e-4d1af14bce44e8ba000e-6304-473c-b6a7-49010b1433d7m27.216032United Kingdom
159795user_0002932005-05-14T23:59:25Z64d62f45-e001-40a1-a055-c3545fcc14deb83f61e2-a566-49cc-b8dd-9ad5a0caddb2m39.000000United Kingdom
159796user_0002982005-05-14T23:59:45Z60530915-9371-4b16-bc31-5c4456317ae022ea4524-3dcb-44e6-a1bc-4edfd25ad988m28.000000Argentina
159797user_0001422005-05-14T23:59:51Z936addc3-91aa-49de-8ec0-0dc186de151fc0d44a5d-f84b-4d4b-babc-0f3937ef6edbm27.216032Norway
\n", 443 | "
" 444 | ], 445 | "text/plain": [ 446 | " userid timestamp \\\n", 447 | "159793 user_000298 2005-05-14T23:57:15Z \n", 448 | "159794 user_000870 2005-05-14T23:58:42Z \n", 449 | "159795 user_000293 2005-05-14T23:59:25Z \n", 450 | "159796 user_000298 2005-05-14T23:59:45Z \n", 451 | "159797 user_000142 2005-05-14T23:59:51Z \n", 452 | "\n", 453 | " artist-id \\\n", 454 | "159793 79239441-bfd5-4981-a70c-55c3f15c1287 \n", 455 | "159794 1928bd00-5ccb-4dcb-809e-4d1af14bce44 \n", 456 | "159795 64d62f45-e001-40a1-a055-c3545fcc14de \n", 457 | "159796 60530915-9371-4b16-bc31-5c4456317ae0 \n", 458 | "159797 936addc3-91aa-49de-8ec0-0dc186de151f \n", 459 | "\n", 460 | " track-id gender age country \n", 461 | "159793 3dc94d29-3f33-4032-8772-e599f081d085 m 28.000000 Argentina \n", 462 | "159794 e8ba000e-6304-473c-b6a7-49010b1433d7 m 27.216032 United Kingdom \n", 463 | "159795 b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 m 39.000000 United Kingdom \n", 464 | "159796 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 m 28.000000 Argentina \n", 465 | "159797 c0d44a5d-f84b-4d4b-babc-0f3937ef6edb m 27.216032 Norway " 466 | ] 467 | }, 468 | "execution_count": 13, 469 | "metadata": {}, 470 | "output_type": "execute_result" 471 | } 472 | ], 473 | "source": [ 474 | "df_lastfm_3m['gender'] = df_lastfm_3m['gender'].fillna('m')\n", 475 | "df_lastfm_3m['age'] = df_lastfm_3m['age'].fillna(np.mean(df_lastfm_3m['age']))\n", 476 | "df_lastfm_3m['country'] = df_lastfm_3m['country'].fillna('United States')\n", 477 | "\n", 478 | "df_lastfm_3m.tail()" 479 | ] 480 | }, 481 | { 482 | "cell_type": "code", 483 | "execution_count": 14, 484 | "metadata": { 485 | "collapsed": false 486 | }, 487 | "outputs": [ 488 | { 489 | "data": { 490 | "text/html": [ 491 | "
\n", 492 | "\n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | " \n", 528 | " \n", 529 | " \n", 530 | " \n", 531 | " \n", 532 | " \n", 533 | " \n", 534 | " \n", 535 | " \n", 536 | " \n", 537 | " \n", 538 | " \n", 539 | " \n", 540 | " \n", 541 | " \n", 542 | " \n", 543 | " \n", 544 | " \n", 545 | " \n", 546 | " \n", 547 | " \n", 548 | " \n", 549 | " \n", 550 | " \n", 551 | " \n", 552 | " \n", 553 | " \n", 554 | " \n", 555 | " \n", 556 | " \n", 557 | "
useridtimestampartist-idtrack-idgenderagecountry
159793user_0002982005-05-14T23:57:15Z79239441-bfd5-4981-a70c-55c3f15c12873dc94d29-3f33-4032-8772-e599f081d0851.028.000000Argentina
159794user_0008702005-05-14T23:58:42Z1928bd00-5ccb-4dcb-809e-4d1af14bce44e8ba000e-6304-473c-b6a7-49010b1433d71.027.216032United Kingdom
159795user_0002932005-05-14T23:59:25Z64d62f45-e001-40a1-a055-c3545fcc14deb83f61e2-a566-49cc-b8dd-9ad5a0caddb21.039.000000United Kingdom
159796user_0002982005-05-14T23:59:45Z60530915-9371-4b16-bc31-5c4456317ae022ea4524-3dcb-44e6-a1bc-4edfd25ad9881.028.000000Argentina
159797user_0001422005-05-14T23:59:51Z936addc3-91aa-49de-8ec0-0dc186de151fc0d44a5d-f84b-4d4b-babc-0f3937ef6edb1.027.216032Norway
\n", 558 | "
" 559 | ], 560 | "text/plain": [ 561 | " userid timestamp \\\n", 562 | "159793 user_000298 2005-05-14T23:57:15Z \n", 563 | "159794 user_000870 2005-05-14T23:58:42Z \n", 564 | "159795 user_000293 2005-05-14T23:59:25Z \n", 565 | "159796 user_000298 2005-05-14T23:59:45Z \n", 566 | "159797 user_000142 2005-05-14T23:59:51Z \n", 567 | "\n", 568 | " artist-id \\\n", 569 | "159793 79239441-bfd5-4981-a70c-55c3f15c1287 \n", 570 | "159794 1928bd00-5ccb-4dcb-809e-4d1af14bce44 \n", 571 | "159795 64d62f45-e001-40a1-a055-c3545fcc14de \n", 572 | "159796 60530915-9371-4b16-bc31-5c4456317ae0 \n", 573 | "159797 936addc3-91aa-49de-8ec0-0dc186de151f \n", 574 | "\n", 575 | " track-id gender age \\\n", 576 | "159793 3dc94d29-3f33-4032-8772-e599f081d085 1.0 28.000000 \n", 577 | "159794 e8ba000e-6304-473c-b6a7-49010b1433d7 1.0 27.216032 \n", 578 | "159795 b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 1.0 39.000000 \n", 579 | "159796 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 1.0 28.000000 \n", 580 | "159797 c0d44a5d-f84b-4d4b-babc-0f3937ef6edb 1.0 27.216032 \n", 581 | "\n", 582 | " country \n", 583 | "159793 Argentina \n", 584 | "159794 United Kingdom \n", 585 | "159795 United Kingdom \n", 586 | "159796 Argentina \n", 587 | "159797 Norway " 588 | ] 589 | }, 590 | "execution_count": 14, 591 | "metadata": {}, 592 | "output_type": "execute_result" 593 | } 594 | ], 595 | "source": [ 596 | "# encode gender m/f into 0/1\n", 597 | "df_lastfm_3m['gender'] = pd.get_dummies(df_lastfm_3m['gender'])['m']\n", 598 | "df_lastfm_3m.tail()" 599 | ] 600 | }, 601 | { 602 | "cell_type": "code", 603 | "execution_count": 15, 604 | "metadata": { 605 | "collapsed": false 606 | }, 607 | "outputs": [ 608 | { 609 | "data": { 610 | "text/html": [ 611 | "
\n", 612 | "\n", 613 | " \n", 614 | " \n", 615 | " \n", 616 | " \n", 617 | " \n", 618 | " \n", 619 | " \n", 620 | " \n", 621 | " \n", 622 | " \n", 623 | " \n", 624 | " \n", 625 | " \n", 626 | " \n", 627 | " \n", 628 | " \n", 629 | " \n", 630 | " \n", 631 | " \n", 632 | " \n", 633 | " \n", 634 | " \n", 635 | " \n", 636 | " \n", 637 | " \n", 638 | " \n", 639 | " \n", 640 | " \n", 641 | " \n", 642 | " \n", 643 | " \n", 644 | " \n", 645 | " \n", 646 | " \n", 647 | " \n", 648 | " \n", 649 | " \n", 650 | " \n", 651 | " \n", 652 | " \n", 653 | " \n", 654 | " \n", 655 | " \n", 656 | " \n", 657 | " \n", 658 | " \n", 659 | " \n", 660 | " \n", 661 | " \n", 662 | " \n", 663 | " \n", 664 | " \n", 665 | " \n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | "
useridtimestampartist-idtrack-idgenderagecountry
159793user_0002982005-05-14T23:57:15Z79239441-bfd5-4981-a70c-55c3f15c12873dc94d29-3f33-4032-8772-e599f081d0851.02.998332e-01Argentina
159794user_0008702005-05-14T23:58:42Z1928bd00-5ccb-4dcb-809e-4d1af14bce44e8ba000e-6304-473c-b6a7-49010b1433d71.0-1.358756e-15United Kingdom
159795user_0002932005-05-14T23:59:25Z64d62f45-e001-40a1-a055-c3545fcc14deb83f61e2-a566-49cc-b8dd-9ad5a0caddb21.04.506848e+00United Kingdom
159796user_0002982005-05-14T23:59:45Z60530915-9371-4b16-bc31-5c4456317ae022ea4524-3dcb-44e6-a1bc-4edfd25ad9881.02.998332e-01Argentina
159797user_0001422005-05-14T23:59:51Z936addc3-91aa-49de-8ec0-0dc186de151fc0d44a5d-f84b-4d4b-babc-0f3937ef6edb1.0-1.358756e-15Norway
\n", 678 | "
" 679 | ], 680 | "text/plain": [ 681 | " userid timestamp \\\n", 682 | "159793 user_000298 2005-05-14T23:57:15Z \n", 683 | "159794 user_000870 2005-05-14T23:58:42Z \n", 684 | "159795 user_000293 2005-05-14T23:59:25Z \n", 685 | "159796 user_000298 2005-05-14T23:59:45Z \n", 686 | "159797 user_000142 2005-05-14T23:59:51Z \n", 687 | "\n", 688 | " artist-id \\\n", 689 | "159793 79239441-bfd5-4981-a70c-55c3f15c1287 \n", 690 | "159794 1928bd00-5ccb-4dcb-809e-4d1af14bce44 \n", 691 | "159795 64d62f45-e001-40a1-a055-c3545fcc14de \n", 692 | "159796 60530915-9371-4b16-bc31-5c4456317ae0 \n", 693 | "159797 936addc3-91aa-49de-8ec0-0dc186de151f \n", 694 | "\n", 695 | " track-id gender age \\\n", 696 | "159793 3dc94d29-3f33-4032-8772-e599f081d085 1.0 2.998332e-01 \n", 697 | "159794 e8ba000e-6304-473c-b6a7-49010b1433d7 1.0 -1.358756e-15 \n", 698 | "159795 b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 1.0 4.506848e+00 \n", 699 | "159796 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 1.0 2.998332e-01 \n", 700 | "159797 c0d44a5d-f84b-4d4b-babc-0f3937ef6edb 1.0 -1.358756e-15 \n", 701 | "\n", 702 | " country \n", 703 | "159793 Argentina \n", 704 | "159794 United Kingdom \n", 705 | "159795 United Kingdom \n", 706 | "159796 Argentina \n", 707 | "159797 Norway " 708 | ] 709 | }, 710 | "execution_count": 15, 711 | "metadata": {}, 712 | "output_type": "execute_result" 713 | } 714 | ], 715 | "source": [ 716 | "# standardize age to be zero-mean\n", 717 | "df_lastfm_3m['age'] = (df_lastfm_3m['age'] - df_lastfm_3m['age'].mean()) / df_lastfm_3m['age'].std(ddof=0)\n", 718 | "df_lastfm_3m.tail()" 719 | ] 720 | }, 721 | { 722 | "cell_type": "markdown", 723 | "metadata": {}, 724 | "source": [ 725 | "Create features used in the model.\n", 726 | "\n", 727 | "1. **elapsed days** from the first sample\n", 728 | "2. **user indices** corresponds to *userid*\n", 729 | "3. **item indices** corresponds to *track-id*" 730 | ] 731 | }, 732 | { 733 | "cell_type": "code", 734 | "execution_count": 16, 735 | "metadata": { 736 | "collapsed": false 737 | }, 738 | "outputs": [], 739 | "source": [ 740 | "now = datetime.now()\n", 741 | "midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)\n", 742 | "\n", 743 | "max_sec = (now.replace(hour=23, minute=59, second=59, microsecond=59) - midnight).seconds" 744 | ] 745 | }, 746 | { 747 | "cell_type": "code", 748 | "execution_count": 17, 749 | "metadata": { 750 | "collapsed": false 751 | }, 752 | "outputs": [ 753 | { 754 | "data": { 755 | "text/plain": [ 756 | "(159798, 159798, 159798, 159798, 159798)" 757 | ] 758 | }, 759 | "execution_count": 17, 760 | "metadata": {}, 761 | "output_type": "execute_result" 762 | } 763 | ], 764 | "source": [ 765 | "# compute elapsed days from the first sample\n", 766 | "head_date = datetime.strptime(df_lastfm_3m.iloc[0]['timestamp'], \"%Y-%m-%dT%H:%M:%SZ\")\n", 767 | "\n", 768 | "user_ids = []\n", 769 | "track_ids = []\n", 770 | "artist_ids = []\n", 771 | "\n", 772 | "u_indices = []\n", 773 | "i_indices = []\n", 774 | "a_indices = []\n", 775 | "dts = []\n", 776 | "times = []\n", 777 | "\n", 778 | "for i, row in df_lastfm_3m.iterrows():\n", 779 | " if row['userid'] not in user_ids:\n", 780 | " user_ids.append(row['userid'])\n", 781 | " u_index = user_ids.index(row['userid'])\n", 782 | " u_indices.append(u_index)\n", 783 | " \n", 784 | " if row['track-id'] not in track_ids:\n", 785 | " track_ids.append(row['track-id'])\n", 786 | " i_index = track_ids.index(row['track-id'])\n", 787 | " i_indices.append(i_index)\n", 788 | " \n", 789 | " if row['artist-id'] not in artist_ids:\n", 790 | " artist_ids.append(row['artist-id'])\n", 791 | " a_index = artist_ids.index(row['artist-id'])\n", 792 | " a_indices.append(a_index)\n", 793 | " \n", 794 | " date = datetime.strptime(row['timestamp'], \"%Y-%m-%dT%H:%M:%SZ\")\n", 795 | " dt = (date - head_date).days\n", 796 | " dts.append(dt)\n", 797 | "\n", 798 | " # normalized time in a day [0.0, 1.0]\n", 799 | " time = (date - midnight).seconds / float(max_sec)\n", 800 | " times.append(time)\n", 801 | " \n", 802 | "len(u_indices), len(i_indices), len(a_indices), len(dts), len(times)" 803 | ] 804 | }, 805 | { 806 | "cell_type": "code", 807 | "execution_count": 18, 808 | "metadata": { 809 | "collapsed": true 810 | }, 811 | "outputs": [], 812 | "source": [ 813 | "df_lastfm_3m['u_index'] = u_indices\n", 814 | "df_lastfm_3m['i_index'] = i_indices\n", 815 | "df_lastfm_3m['artist_index'] = a_indices\n", 816 | "df_lastfm_3m['dt'] = dts\n", 817 | "df_lastfm_3m['time'] = times" 818 | ] 819 | }, 820 | { 821 | "cell_type": "code", 822 | "execution_count": 19, 823 | "metadata": { 824 | "collapsed": false 825 | }, 826 | "outputs": [ 827 | { 828 | "data": { 829 | "text/html": [ 830 | "
\n", 831 | "\n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | " \n", 843 | " \n", 844 | " \n", 845 | " \n", 846 | " \n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | " \n", 862 | " \n", 863 | " \n", 864 | " \n", 865 | " \n", 866 | " \n", 867 | " \n", 868 | " \n", 869 | " \n", 870 | " \n", 871 | " \n", 872 | " \n", 873 | " \n", 874 | " \n", 875 | " \n", 876 | " \n", 877 | " \n", 878 | " \n", 879 | " \n", 880 | " \n", 881 | " \n", 882 | " \n", 883 | " \n", 884 | " \n", 885 | " \n", 886 | " \n", 887 | " \n", 888 | " \n", 889 | " \n", 890 | " \n", 891 | " \n", 892 | " \n", 893 | " \n", 894 | " \n", 895 | " \n", 896 | " \n", 897 | " \n", 898 | " \n", 899 | " \n", 900 | " \n", 901 | " \n", 902 | " \n", 903 | " \n", 904 | " \n", 905 | " \n", 906 | " \n", 907 | " \n", 908 | " \n", 909 | " \n", 910 | " \n", 911 | " \n", 912 | " \n", 913 | " \n", 914 | " \n", 915 | " \n", 916 | " \n", 917 | " \n", 918 | " \n", 919 | " \n", 920 | " \n", 921 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 926 | "
useridtimestampartist-idtrack-idgenderagecountryu_indexi_indexartist_indexdttime
0user_0003912005-02-14T00:00:07Zfbd86487-ccb5-4a57-a860-cc3d360b51156b4977f4-3c7a-492a-af61-1e877fa66f520.0-1.358756e-15Germany00000.000081
1user_0008712005-02-14T00:00:38Zb3a6ebdf-4ee6-4ec0-912c-be03ade6c8339ecc2ab3-7294-43ad-bdeb-f51388a7a6e01.0-1.358756e-15Argentina11100.000440
2user_0007092005-02-14T00:01:44Zb4d32cff-f19e-455f-86c4-f347d824ca611d0f1ea5-0a92-4578-a7e7-3f2a7129da611.0-1.358756e-15Canada22200.001204
3user_0002852005-02-14T00:02:10Z95e1ead9-4d31-4808-a7ac-32c3614c116b46909ba9-46c7-461e-a2ef-280eacd550e40.0-1.612447e+00United States33300.001505
4user_0001422005-02-14T00:02:40Z51086134-0896-4c00-b54a-c5c37aeaf2bf14025355-94c2-4e9b-b63f-c16cab9e80861.0-1.358756e-15Norway44400.001852
\n", 927 | "
" 928 | ], 929 | "text/plain": [ 930 | " userid timestamp artist-id \\\n", 931 | "0 user_000391 2005-02-14T00:00:07Z fbd86487-ccb5-4a57-a860-cc3d360b5115 \n", 932 | "1 user_000871 2005-02-14T00:00:38Z b3a6ebdf-4ee6-4ec0-912c-be03ade6c833 \n", 933 | "2 user_000709 2005-02-14T00:01:44Z b4d32cff-f19e-455f-86c4-f347d824ca61 \n", 934 | "3 user_000285 2005-02-14T00:02:10Z 95e1ead9-4d31-4808-a7ac-32c3614c116b \n", 935 | "4 user_000142 2005-02-14T00:02:40Z 51086134-0896-4c00-b54a-c5c37aeaf2bf \n", 936 | "\n", 937 | " track-id gender age country \\\n", 938 | "0 6b4977f4-3c7a-492a-af61-1e877fa66f52 0.0 -1.358756e-15 Germany \n", 939 | "1 9ecc2ab3-7294-43ad-bdeb-f51388a7a6e0 1.0 -1.358756e-15 Argentina \n", 940 | "2 1d0f1ea5-0a92-4578-a7e7-3f2a7129da61 1.0 -1.358756e-15 Canada \n", 941 | "3 46909ba9-46c7-461e-a2ef-280eacd550e4 0.0 -1.612447e+00 United States \n", 942 | "4 14025355-94c2-4e9b-b63f-c16cab9e8086 1.0 -1.358756e-15 Norway \n", 943 | "\n", 944 | " u_index i_index artist_index dt time \n", 945 | "0 0 0 0 0 0.000081 \n", 946 | "1 1 1 1 0 0.000440 \n", 947 | "2 2 2 2 0 0.001204 \n", 948 | "3 3 3 3 0 0.001505 \n", 949 | "4 4 4 4 0 0.001852 " 950 | ] 951 | }, 952 | "execution_count": 19, 953 | "metadata": {}, 954 | "output_type": "execute_result" 955 | } 956 | ], 957 | "source": [ 958 | "df_lastfm_3m.head()" 959 | ] 960 | }, 961 | { 962 | "cell_type": "code", 963 | "execution_count": 20, 964 | "metadata": { 965 | "collapsed": false 966 | }, 967 | "outputs": [ 968 | { 969 | "data": { 970 | "text/html": [ 971 | "
\n", 972 | "\n", 973 | " \n", 974 | " \n", 975 | " \n", 976 | " \n", 977 | " \n", 978 | " \n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | " \n", 1000 | " \n", 1001 | " \n", 1002 | " \n", 1003 | " \n", 1004 | " \n", 1005 | " \n", 1006 | " \n", 1007 | " \n", 1008 | " \n", 1009 | " \n", 1010 | " \n", 1011 | " \n", 1012 | " \n", 1013 | " \n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1017 | " \n", 1018 | " \n", 1019 | " \n", 1020 | " \n", 1021 | " \n", 1022 | " \n", 1023 | " \n", 1024 | " \n", 1025 | " \n", 1026 | " \n", 1027 | " \n", 1028 | " \n", 1029 | " \n", 1030 | " \n", 1031 | " \n", 1032 | " \n", 1033 | " \n", 1034 | " \n", 1035 | " \n", 1036 | " \n", 1037 | " \n", 1038 | " \n", 1039 | " \n", 1040 | " \n", 1041 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1046 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | " \n", 1051 | " \n", 1052 | " \n", 1053 | " \n", 1054 | " \n", 1055 | " \n", 1056 | " \n", 1057 | " \n", 1058 | " \n", 1059 | " \n", 1060 | " \n", 1061 | " \n", 1062 | " \n", 1063 | " \n", 1064 | " \n", 1065 | " \n", 1066 | " \n", 1067 | "
useridtimestampartist-idtrack-idgenderagecountryu_indexi_indexartist_indexdttime
159793user_0002982005-05-14T23:57:15Z79239441-bfd5-4981-a70c-55c3f15c12873dc94d29-3f33-4032-8772-e599f081d0851.02.998332e-01Argentina106855391890.998102
159794user_0008702005-05-14T23:58:42Z1928bd00-5ccb-4dcb-809e-4d1af14bce44e8ba000e-6304-473c-b6a7-49010b1433d71.0-1.358756e-15United Kingdom36510712879890.999109
159795user_0002932005-05-14T23:59:25Z64d62f45-e001-40a1-a055-c3545fcc14deb83f61e2-a566-49cc-b8dd-9ad5a0caddb21.04.506848e+00United Kingdom2913963602890.999606
159796user_0002982005-05-14T23:59:45Z60530915-9371-4b16-bc31-5c4456317ae022ea4524-3dcb-44e6-a1bc-4edfd25ad9881.02.998332e-01Argentina10156487890.999838
159797user_0001422005-05-14T23:59:51Z936addc3-91aa-49de-8ec0-0dc186de151fc0d44a5d-f84b-4d4b-babc-0f3937ef6edb1.0-1.358756e-15Norway448519392890.999907
\n", 1068 | "
" 1069 | ], 1070 | "text/plain": [ 1071 | " userid timestamp \\\n", 1072 | "159793 user_000298 2005-05-14T23:57:15Z \n", 1073 | "159794 user_000870 2005-05-14T23:58:42Z \n", 1074 | "159795 user_000293 2005-05-14T23:59:25Z \n", 1075 | "159796 user_000298 2005-05-14T23:59:45Z \n", 1076 | "159797 user_000142 2005-05-14T23:59:51Z \n", 1077 | "\n", 1078 | " artist-id \\\n", 1079 | "159793 79239441-bfd5-4981-a70c-55c3f15c1287 \n", 1080 | "159794 1928bd00-5ccb-4dcb-809e-4d1af14bce44 \n", 1081 | "159795 64d62f45-e001-40a1-a055-c3545fcc14de \n", 1082 | "159796 60530915-9371-4b16-bc31-5c4456317ae0 \n", 1083 | "159797 936addc3-91aa-49de-8ec0-0dc186de151f \n", 1084 | "\n", 1085 | " track-id gender age \\\n", 1086 | "159793 3dc94d29-3f33-4032-8772-e599f081d085 1.0 2.998332e-01 \n", 1087 | "159794 e8ba000e-6304-473c-b6a7-49010b1433d7 1.0 -1.358756e-15 \n", 1088 | "159795 b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 1.0 4.506848e+00 \n", 1089 | "159796 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 1.0 2.998332e-01 \n", 1090 | "159797 c0d44a5d-f84b-4d4b-babc-0f3937ef6edb 1.0 -1.358756e-15 \n", 1091 | "\n", 1092 | " country u_index i_index artist_index dt time \n", 1093 | "159793 Argentina 10 6855 391 89 0.998102 \n", 1094 | "159794 United Kingdom 36 51071 2879 89 0.999109 \n", 1095 | "159795 United Kingdom 29 13963 602 89 0.999606 \n", 1096 | "159796 Argentina 10 1564 87 89 0.999838 \n", 1097 | "159797 Norway 4 48519 392 89 0.999907 " 1098 | ] 1099 | }, 1100 | "execution_count": 20, 1101 | "metadata": {}, 1102 | "output_type": "execute_result" 1103 | } 1104 | ], 1105 | "source": [ 1106 | "df_lastfm_3m.tail()" 1107 | ] 1108 | }, 1109 | { 1110 | "cell_type": "markdown", 1111 | "metadata": {}, 1112 | "source": [ 1113 | "Export the DataFrame into an intermediate TSV file." 1114 | ] 1115 | }, 1116 | { 1117 | "cell_type": "code", 1118 | "execution_count": 21, 1119 | "metadata": { 1120 | "collapsed": true 1121 | }, 1122 | "outputs": [], 1123 | "source": [ 1124 | "df_lastfm_3m.to_csv('lastfm.tsv', sep='\\t', index=False)" 1125 | ] 1126 | }, 1127 | { 1128 | "cell_type": "code", 1129 | "execution_count": null, 1130 | "metadata": { 1131 | "collapsed": true 1132 | }, 1133 | "outputs": [], 1134 | "source": [] 1135 | } 1136 | ], 1137 | "metadata": { 1138 | "kernelspec": { 1139 | "display_name": "Python 3", 1140 | "language": "python", 1141 | "name": "python3" 1142 | }, 1143 | "language_info": { 1144 | "codemirror_mode": { 1145 | "name": "ipython", 1146 | "version": 3 1147 | }, 1148 | "file_extension": ".py", 1149 | "mimetype": "text/x-python", 1150 | "name": "python", 1151 | "nbconvert_exporter": "python", 1152 | "pygments_lexer": "ipython3", 1153 | "version": "3.5.2" 1154 | } 1155 | }, 1156 | "nbformat": 4, 1157 | "nbformat_minor": 0 1158 | } 1159 | -------------------------------------------------------------------------------- /notebook/claim-iMF.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import matplotlib as mpl \n", 12 | "mpl.use(\"pgf\") \n", 13 | "pgf_with_rc_fonts = {\n", 14 | " \"font.serif\": [],\n", 15 | "}\n", 16 | "\n", 17 | "import numpy as np\n", 18 | "import pandas as pd\n", 19 | "\n", 20 | "%matplotlib inline\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "from matplotlib import gridspec\n", 23 | "\n", 24 | "mpl.rcParams['pdf.fonttype'] = 42\n", 25 | "mpl.rcParams['ps.fonttype'] = 42\n", 26 | "mpl.rcParams['axes.labelsize'] = 'large'\n", 27 | "mpl.rcParams['axes.facecolor'] = 'w'" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 2, 33 | "metadata": { 34 | "collapsed": true 35 | }, 36 | "outputs": [], 37 | "source": [ 38 | "import os\n", 39 | "import sys\n", 40 | "\n", 41 | "pardir = os.path.abspath(os.pardir)\n", 42 | "sys.path.append(pardir)" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 3, 48 | "metadata": { 49 | "collapsed": true 50 | }, 51 | "outputs": [], 52 | "source": [ 53 | "%load_ext autoreload\n", 54 | "%autoreload 2\n", 55 | "from experiment import Runner" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": 4, 61 | "metadata": { 62 | "collapsed": false 63 | }, 64 | "outputs": [ 65 | { 66 | "name": "stderr", 67 | "output_type": "stream", 68 | "text": [ 69 | "[37863] [exp] ML1M | n_epoch = 1\n", 70 | "[37863] [exp] n_sample = 226310; 45262 (20%) + 22631 (10%) + 158417 (70%)\n", 71 | "[37863] [exp] n_user = 6014, n_item = 3232\n" 72 | ] 73 | } 74 | ], 75 | "source": [ 76 | "exp_ML1M = Runner(dataset='ML1M', params={})" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "### prepare" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 5, 89 | "metadata": { 90 | "collapsed": true 91 | }, 92 | "outputs": [], 93 | "source": [ 94 | "# http://public.tableau.com/profile/chris.gerrard#!/vizhome/TableauColors/ColorPaletteswithRGBValues\n", 95 | "colors = [(31, 119, 180), (174, 199, 232), (255, 127, 14), (255, 187, 120),\n", 96 | " (44, 160, 44), (152, 223, 138), (214, 39, 40), (255, 152, 150), \n", 97 | " (148, 103, 189), (197, 176, 213), (140, 86, 75), (196, 156, 148), \n", 98 | " (227, 119, 194), (247, 182, 210), (127, 127, 127), (199, 199, 199),\n", 99 | " (188, 189, 34), (219, 219, 141), (23, 190, 207), (158, 218, 229)]\n", 100 | "\n", 101 | "# [0, 255] -> [0.0, 1.0]\n", 102 | "for i in range(len(colors)): \n", 103 | " r, g, b = colors[i] \n", 104 | " colors[i] = (r / 255., g / 255., b / 255.) " 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 6, 110 | "metadata": { 111 | "collapsed": true 112 | }, 113 | "outputs": [], 114 | "source": [ 115 | "def read_result(path):\n", 116 | " t_reco = t_update = MPR = 0.\n", 117 | " recalls = []\n", 118 | "\n", 119 | " with open(path) as f:\n", 120 | " lines = list(map(lambda l: float(l.rstrip()), f.readlines()))\n", 121 | " t_reco = lines[0]\n", 122 | " t_update = lines[1]\n", 123 | " MPR = lines[2]\n", 124 | " recalls = lines[3:]\n", 125 | " \n", 126 | " return {'t_reco': t_reco, 't_update': t_update, 'MPR': MPR, 'recalls': recalls}" 127 | ] 128 | }, 129 | { 130 | "cell_type": "markdown", 131 | "metadata": {}, 132 | "source": [ 133 | "### Try various learning rate on ML1M" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 7, 139 | "metadata": { 140 | "collapsed": false 141 | }, 142 | "outputs": [], 143 | "source": [ 144 | "iMF1 = read_result('../results/claim-iMF/ML1M_iMF_5000_k40_reg001_eta0001.txt')\n", 145 | "iMF2 = read_result('../results/claim-iMF/ML1M_iMF_5000_k40_reg001_eta0003.txt')\n", 146 | "iMF3 = read_result('../results/claim-iMF/ML1M_iMF_5000_k40_reg001_eta0006.txt')\n", 147 | "iMF4 = read_result('../results/claim-iMF/ML1M_iMF_5000_k40_reg001_eta0030.txt')\n", 148 | "\n", 149 | "popular = read_result('../results/claim-iMF/ML1M_popular_5000.txt')\n", 150 | "# random = read_result('../results/claim-iMF/ML1M_random_5000.txt')" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 26, 156 | "metadata": { 157 | "collapsed": false 158 | }, 159 | "outputs": [ 160 | { 161 | "data": { 162 | "image/png": "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\n/j+iO8LmmlS/BQAAAABJRU5ErkJggg==\n", 163 | "text/plain": [ 164 | "" 165 | ] 166 | }, 167 | "metadata": {}, 168 | "output_type": "display_data" 169 | } 170 | ], 171 | "source": [ 172 | "fig, ax = plt.subplots()\n", 173 | "\n", 174 | "every = int(len(iMF1['recalls']) / 8)\n", 175 | "\n", 176 | "ax.plot(iMF1['recalls'], label=r'$\\eta$ = 0.001 (%.1f%%)' % iMF1['MPR'], c=colors[1],\n", 177 | " ms=6., mfc='none', marker='o', mew=1.2, mec=colors[0], markevery=(int(every/2), every)) # epoch = 30\n", 178 | "ax.plot(iMF2['recalls'], label=r'$\\eta$ = 0.003 (%.1f%%)' % iMF2['MPR'], c=colors[3],\n", 179 | " ms=6., mfc='none', marker='^', mew=1.2, mec=colors[2], markevery=(int(every/2), every)) # epoch = 10\n", 180 | "ax.plot(iMF3['recalls'], label=r'$\\eta$ = 0.006 (%.1f%%)' % iMF3['MPR'], c=colors[5],\n", 181 | " ms=6., mfc='none', marker='s', mew=1.2, mec=colors[4], markevery=(int(every/2), every)) # epoch = 5\n", 182 | "ax.plot(iMF4['recalls'], label=r'$\\eta$ = 0.030 (%.1f%%)' % iMF4['MPR'], c=colors[7],\n", 183 | " ms=6., mfc='none', marker='D', mew=1.2, mec=colors[6], markevery=(int(every/2), every)) # epoch = 1\n", 184 | "\n", 185 | "ax.plot(popular['recalls'], label=r'Popularity', c=colors[14], lw=0.8)\n", 186 | "# ax.plot(random['recalls'], label=r'random (%.1f%%)' % random['MPR'], c=colors[10])\n", 187 | "\n", 188 | "ax.set_xlabel(r'old $\\leftarrow$ sample index $\\rightarrow$ new')\n", 189 | "ax.set_ylabel('windowed recall@10')\n", 190 | "\n", 191 | "T = 5000\n", 192 | "ax.axvline(x=(T - 1), linewidth=1, linestyle=':', color=colors[14])\n", 193 | "\n", 194 | "ax.set_xlim([-T, 160000])\n", 195 | "ax.set_xticks([])\n", 196 | "\n", 197 | "ax.set_ylim([0.0, 0.15])\n", 198 | "ax.set_yticks([0.00, 0.025, 0.05, 0.075, 0.10, 0.125, 0.15])\n", 199 | "\n", 200 | "legend = ax.legend(bbox_to_anchor=(0., 0.65, 1., .102), loc=4,\n", 201 | " ncol=3, borderaxespad=1., frameon=False)\n", 202 | "for h in legend.legendHandles:\n", 203 | " h.set_linewidth(2.0)\n", 204 | "\n", 205 | "ax.yaxis.grid()\n", 206 | "\n", 207 | "fig.set_size_inches((10, 3))\n", 208 | "fig.patch.set_alpha(0.0)\n", 209 | "plt.tight_layout()\n", 210 | "\n", 211 | "plt.show()\n", 212 | "\n", 213 | "fig.savefig('../../../paper/images/thesis/iMF_vs_popularity.eps')" 214 | ] 215 | }, 216 | { 217 | "cell_type": "markdown", 218 | "metadata": {}, 219 | "source": [ 220 | "#### Observations\n", 221 | "\n", 222 | "- Popularity is much better than iMF (also competitive to the best UserKNN result)\n", 223 | " - Hybrid method is important in a persistently cold-starting setting.\n", 224 | " - (balance between personalized and non-personalized method)\n", 225 | "- Higher learning rate => better MPR\n", 226 | " - However, recall behavior does not correspond to MPR.\n", 227 | " - (i.e. iMF ($\\eta=0.006$) is better than iMF ($\\eta=0.030$) in terms of recall)\n", 228 | "- Results are higly sensitive to learning rate\n", 229 | " - In a streaming environment, models may not be updated in a uniform pace." 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "### LastFM" 237 | ] 238 | }, 239 | { 240 | "cell_type": "code", 241 | "execution_count": 9, 242 | "metadata": { 243 | "collapsed": true 244 | }, 245 | "outputs": [], 246 | "source": [ 247 | "lfm_iMF = read_result('../results/claim-iMF/LastFM_iMF_5000_k20_reg001_eta003.txt')\n", 248 | "\n", 249 | "lfm_popular = read_result('../results/claim-iMF/LastFM_popular_5000.txt')" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": 10, 255 | "metadata": { 256 | "collapsed": false 257 | }, 258 | "outputs": [ 259 | { 260 | "data": { 261 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYQAAADRCAYAAADMicUjAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXd4VFXawH9nJjMpQAgt9N5FmgiyWAAbigWsWBCxfIor\nK6LrorguiNhQUcRFwEXFii5rwUUpArEjrBJ67wkSaggQkmnn++PMnZYpN8mkEM7veeaZOfe+99xz\nJ5P73nPeJqSUaDQajUZjqegBaDQajaZyoBWCRqPRaACtEDQajUbjRSsEjUaj0QBaIWg0Go3Gi1YI\nGo1GowFMKgQhxBVCiE1CiC1CiDERZF4XQmwVQmQKIbqF7LMIIX4XQswL2DZOCJHl3f67EOKK0l2K\nRqPRaEpDQiwBIYQFeAO4BNgHrBRCfCml3BQgcyXQWkrZVghxHjAd6B3QzShgA5Aa0v1kKeXkUl6D\nRqPRaOKAmRlCL2CrlHK3lNIJzAEGhcgMAt4DkFL+CtQUQtQHEEI0AQYC/wrTtyjpwDUajUYTX8wo\nhMbA3oB2lndbNJnsAJlXgceAcCHRI71LTP8SQtQ0N2SNRqPRlAUxl4xKgxDiKiBHSpkphOhH8Ixg\nGjBBSimFEBOBycA9YfrQuTU0Go0mjkgpw67OmFEI2UCzgHYT77ZQmaZhZG4ErhVCDASSgRpCiPek\nlMOklAcD5N8CvooyeBPDNM+yZcvo379/XPvUaDSa0wEhIq/Um1kyWgm0EUI0F0LYgVuAeSEy84Bh\n3pP1BnKllDlSyrFSymZSylbe45ZKKQ25BgHHXw+sM3tBGo1Go4k/MWcIUkq3EGIksAilQGZJKTcK\nIe5Xu+VMKeXXQoiBQohtwEngLhPnnuR1T/UAu4D7S3wVxUTPDjQajaYoorKnvxZCyMo+Ro1Gozld\nEEJEtCGckZHKy5Ytq+ghaDQaTaWjTL2MNBpNMC1atGD37t0VPQzNGUDz5s3ZtWtXsY7RS0YaTTni\nna5X9DA0ZwCRfmt6yUij0Wg0MTkjFYK2IWjijcsF+/dX9Cg0mtJxRioEjSbeTJsGDRtW9Cg0mtKh\nbQgaTSmREiwW/+doaBuCprzQNgSNpgI4caKiRxAfzj77bL7//vsyPcfChQu5/vrry/QcpcXhcNCx\nY0cOHz5c0UMpd85IhaBtCJp4sm9fRY8gPqxbt46LLroIgPHjx2OxWJg6dWqQzJQpU7BYLEyYMAGA\n7777DqvVSmpqqu81aFBodnw/f//733niiScAOHjwILfddhuNGzemVq1aXHjhhaxYsSJIfubMmbRp\n04a0tDR69erFTz/9FPUapkyZQqtWrahevTqdOnVi27ZtAKxZs4azzz6b9PR0Xn31VZ+8y+Wid+/e\nZGf707PZ7Xbuuecenn/++VhfWdVDSlmpX2qI8WXp0qVx71Nz5rJwoZRqsSi2bFn8nsuC8ePHyw4d\nOshzzz03aPs555wjO3ToIJ9++mkppZQZGRmyadOmpvpcuXKlbNeuna+9Y8cO+eqrr8qcnBzp8Xjk\nzJkzZd26deXJkyellFJmZmbK6tWry1WrVkkppXzzzTdlvXr1pMfjCdv/W2+9Jbt27So3bdrk6//o\n0aNSSikHDhwoFy5cKPft2yfr1Kkjc3JypJRSvvjii/Kll14q0ldWVpasW7eudDgcpq6tMhLpt+bd\nHvZ+e0bOEHQuI008+fRT6NFDff7ss4odS2lo2bIlS5cu9bXPPfdc8vPz2bhxIwAbNmygoKCAnj17\nlqj/b775hr59+wad7+GHHyY9PR0hBP/3f/+Hw+Fg8+bNvvN16tSJbt1URd5hw4Zx+PBhDhw4UKRv\nKSUTJkzg1VdfpX379r7+09LSANi5cyf9+/enYcOGtG3blj179rB7924+++wzRo8eXaS/xo0bU7t2\nbZYvX16iaz1dOSMVgkYTLzwemDULcnNV+4YbKnY88UQIwR133MHs2bMBmD17NsOGDSuxUXzt2rW+\nm3U4MjMzcTqdtGnTBoALLriAnTt3smLFCjweD7NmzaJbt27Ur1+/yLFZWVlkZWWxdu1amjVrRuvW\nrRk/frxvf+fOnVm0aBFZWVns3r2b1q1b8/DDD/Pyyy9jtVrDjqdDhw6sXr26RNd6umJKIQghrhBC\nbBJCbBFCjIkg87oQYqu3Alq3kH0WIcTvQoh5AdtqCSEWCSE2CyEWlmfFNG1D0MQLIzPA55/Hpz8h\n4vOKF7fffjtz5szB5XIxZ84chg4dWkQmOzub2rVrU6tWLWrXrs3cuXPD9pWbm0uNGjXC7svLy2PY\nsGGMHz/eJ9O0aVMmTpzI+eefT1JSEs888wwzZ84Me3xWVhYAixcvZv369SxdupSPP/6YWbNmAfDS\nSy8xbdo0Bg8ezGuvvcaPP/5IamoqzZs3Z/DgwfTv37/IuGvUqEGuoenPEGLmMhJCWIA3gEuAfcBK\n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\n", 262 | "text/plain": [ 263 | "" 264 | ] 265 | }, 266 | "metadata": {}, 267 | "output_type": "display_data" 268 | } 269 | ], 270 | "source": [ 271 | "fig, ax = plt.subplots()\n", 272 | "\n", 273 | "ax.plot(lfm_iMF['recalls'], label='iMF (%.1f%%)' % lfm_iMF['MPR'])\n", 274 | "ax.plot(lfm_popular['recalls'], label='popularity (%.1f%%)' % lfm_popular['MPR'])\n", 275 | "\n", 276 | "T = 5000\n", 277 | "ax.axvline(x=(T - 1), linewidth=1, linestyle=':', color=colors[14])\n", 278 | "\n", 279 | "ax.set_xlim([-T, 160000])\n", 280 | "ax.set_xticks([])\n", 281 | "\n", 282 | "ax.legend()\n", 283 | "\n", 284 | "ax.yaxis.grid()\n", 285 | "\n", 286 | "fig.set_size_inches((5.5, 3))\n", 287 | "fig.patch.set_alpha(0.0)\n", 288 | "plt.tight_layout()" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": null, 294 | "metadata": { 295 | "collapsed": true 296 | }, 297 | "outputs": [], 298 | "source": [] 299 | } 300 | ], 301 | "metadata": { 302 | "kernelspec": { 303 | "display_name": "Python [Root]", 304 | "language": "python", 305 | "name": "Python [Root]" 306 | }, 307 | "language_info": { 308 | "codemirror_mode": { 309 | "name": "ipython", 310 | "version": 3 311 | }, 312 | "file_extension": ".py", 313 | "mimetype": "text/x-python", 314 | "name": "python", 315 | "nbconvert_exporter": "python", 316 | "pygments_lexer": "ipython3", 317 | "version": "3.5.2" 318 | } 319 | }, 320 | "nbformat": 4, 321 | "nbformat_minor": 0 322 | } 323 | --------------------------------------------------------------------------------