├── 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 | " userid | \n",
70 | " timestamp | \n",
71 | " artist-id | \n",
72 | " track-id | \n",
73 | " gender | \n",
74 | " age | \n",
75 | " country | \n",
76 | "
\n",
77 | " \n",
78 | " \n",
79 | " \n",
80 | " | 0 | \n",
81 | " user_000391 | \n",
82 | " 2005-02-14T00:00:07Z | \n",
83 | " fbd86487-ccb5-4a57-a860-cc3d360b5115 | \n",
84 | " 6b4977f4-3c7a-492a-af61-1e877fa66f52 | \n",
85 | " f | \n",
86 | " NaN | \n",
87 | " Germany | \n",
88 | "
\n",
89 | " \n",
90 | " | 1 | \n",
91 | " user_000871 | \n",
92 | " 2005-02-14T00:00:38Z | \n",
93 | " b3a6ebdf-4ee6-4ec0-912c-be03ade6c833 | \n",
94 | " 9ecc2ab3-7294-43ad-bdeb-f51388a7a6e0 | \n",
95 | " NaN | \n",
96 | " NaN | \n",
97 | " Argentina | \n",
98 | "
\n",
99 | " \n",
100 | " | 2 | \n",
101 | " user_000709 | \n",
102 | " 2005-02-14T00:01:44Z | \n",
103 | " b4d32cff-f19e-455f-86c4-f347d824ca61 | \n",
104 | " 1d0f1ea5-0a92-4578-a7e7-3f2a7129da61 | \n",
105 | " m | \n",
106 | " NaN | \n",
107 | " Canada | \n",
108 | "
\n",
109 | " \n",
110 | " | 3 | \n",
111 | " user_000285 | \n",
112 | " 2005-02-14T00:02:10Z | \n",
113 | " 95e1ead9-4d31-4808-a7ac-32c3614c116b | \n",
114 | " 46909ba9-46c7-461e-a2ef-280eacd550e4 | \n",
115 | " f | \n",
116 | " 23.0 | \n",
117 | " United States | \n",
118 | "
\n",
119 | " \n",
120 | " | 4 | \n",
121 | " user_000142 | \n",
122 | " 2005-02-14T00:02:40Z | \n",
123 | " 51086134-0896-4c00-b54a-c5c37aeaf2bf | \n",
124 | " 14025355-94c2-4e9b-b63f-c16cab9e8086 | \n",
125 | " NaN | \n",
126 | " NaN | \n",
127 | " Norway | \n",
128 | "
\n",
129 | " \n",
130 | "
\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 | " userid | \n",
233 | " timestamp | \n",
234 | " artist-id | \n",
235 | " track-id | \n",
236 | " gender | \n",
237 | " age | \n",
238 | " country | \n",
239 | "
\n",
240 | " \n",
241 | " \n",
242 | " \n",
243 | " | 159793 | \n",
244 | " user_000298 | \n",
245 | " 2005-05-14T23:57:15Z | \n",
246 | " 79239441-bfd5-4981-a70c-55c3f15c1287 | \n",
247 | " 3dc94d29-3f33-4032-8772-e599f081d085 | \n",
248 | " m | \n",
249 | " 28.0 | \n",
250 | " Argentina | \n",
251 | "
\n",
252 | " \n",
253 | " | 159794 | \n",
254 | " user_000870 | \n",
255 | " 2005-05-14T23:58:42Z | \n",
256 | " 1928bd00-5ccb-4dcb-809e-4d1af14bce44 | \n",
257 | " e8ba000e-6304-473c-b6a7-49010b1433d7 | \n",
258 | " m | \n",
259 | " NaN | \n",
260 | " United Kingdom | \n",
261 | "
\n",
262 | " \n",
263 | " | 159795 | \n",
264 | " user_000293 | \n",
265 | " 2005-05-14T23:59:25Z | \n",
266 | " 64d62f45-e001-40a1-a055-c3545fcc14de | \n",
267 | " b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 | \n",
268 | " NaN | \n",
269 | " 39.0 | \n",
270 | " United Kingdom | \n",
271 | "
\n",
272 | " \n",
273 | " | 159796 | \n",
274 | " user_000298 | \n",
275 | " 2005-05-14T23:59:45Z | \n",
276 | " 60530915-9371-4b16-bc31-5c4456317ae0 | \n",
277 | " 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 | \n",
278 | " m | \n",
279 | " 28.0 | \n",
280 | " Argentina | \n",
281 | "
\n",
282 | " \n",
283 | " | 159797 | \n",
284 | " user_000142 | \n",
285 | " 2005-05-14T23:59:51Z | \n",
286 | " 936addc3-91aa-49de-8ec0-0dc186de151f | \n",
287 | " c0d44a5d-f84b-4d4b-babc-0f3937ef6edb | \n",
288 | " NaN | \n",
289 | " NaN | \n",
290 | " Norway | \n",
291 | "
\n",
292 | " \n",
293 | "
\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 | " userid | \n",
382 | " timestamp | \n",
383 | " artist-id | \n",
384 | " track-id | \n",
385 | " gender | \n",
386 | " age | \n",
387 | " country | \n",
388 | "
\n",
389 | " \n",
390 | " \n",
391 | " \n",
392 | " | 159793 | \n",
393 | " user_000298 | \n",
394 | " 2005-05-14T23:57:15Z | \n",
395 | " 79239441-bfd5-4981-a70c-55c3f15c1287 | \n",
396 | " 3dc94d29-3f33-4032-8772-e599f081d085 | \n",
397 | " m | \n",
398 | " 28.000000 | \n",
399 | " Argentina | \n",
400 | "
\n",
401 | " \n",
402 | " | 159794 | \n",
403 | " user_000870 | \n",
404 | " 2005-05-14T23:58:42Z | \n",
405 | " 1928bd00-5ccb-4dcb-809e-4d1af14bce44 | \n",
406 | " e8ba000e-6304-473c-b6a7-49010b1433d7 | \n",
407 | " m | \n",
408 | " 27.216032 | \n",
409 | " United Kingdom | \n",
410 | "
\n",
411 | " \n",
412 | " | 159795 | \n",
413 | " user_000293 | \n",
414 | " 2005-05-14T23:59:25Z | \n",
415 | " 64d62f45-e001-40a1-a055-c3545fcc14de | \n",
416 | " b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 | \n",
417 | " m | \n",
418 | " 39.000000 | \n",
419 | " United Kingdom | \n",
420 | "
\n",
421 | " \n",
422 | " | 159796 | \n",
423 | " user_000298 | \n",
424 | " 2005-05-14T23:59:45Z | \n",
425 | " 60530915-9371-4b16-bc31-5c4456317ae0 | \n",
426 | " 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 | \n",
427 | " m | \n",
428 | " 28.000000 | \n",
429 | " Argentina | \n",
430 | "
\n",
431 | " \n",
432 | " | 159797 | \n",
433 | " user_000142 | \n",
434 | " 2005-05-14T23:59:51Z | \n",
435 | " 936addc3-91aa-49de-8ec0-0dc186de151f | \n",
436 | " c0d44a5d-f84b-4d4b-babc-0f3937ef6edb | \n",
437 | " m | \n",
438 | " 27.216032 | \n",
439 | " Norway | \n",
440 | "
\n",
441 | " \n",
442 | "
\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 | " userid | \n",
497 | " timestamp | \n",
498 | " artist-id | \n",
499 | " track-id | \n",
500 | " gender | \n",
501 | " age | \n",
502 | " country | \n",
503 | "
\n",
504 | " \n",
505 | " \n",
506 | " \n",
507 | " | 159793 | \n",
508 | " user_000298 | \n",
509 | " 2005-05-14T23:57:15Z | \n",
510 | " 79239441-bfd5-4981-a70c-55c3f15c1287 | \n",
511 | " 3dc94d29-3f33-4032-8772-e599f081d085 | \n",
512 | " 1.0 | \n",
513 | " 28.000000 | \n",
514 | " Argentina | \n",
515 | "
\n",
516 | " \n",
517 | " | 159794 | \n",
518 | " user_000870 | \n",
519 | " 2005-05-14T23:58:42Z | \n",
520 | " 1928bd00-5ccb-4dcb-809e-4d1af14bce44 | \n",
521 | " e8ba000e-6304-473c-b6a7-49010b1433d7 | \n",
522 | " 1.0 | \n",
523 | " 27.216032 | \n",
524 | " United Kingdom | \n",
525 | "
\n",
526 | " \n",
527 | " | 159795 | \n",
528 | " user_000293 | \n",
529 | " 2005-05-14T23:59:25Z | \n",
530 | " 64d62f45-e001-40a1-a055-c3545fcc14de | \n",
531 | " b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 | \n",
532 | " 1.0 | \n",
533 | " 39.000000 | \n",
534 | " United Kingdom | \n",
535 | "
\n",
536 | " \n",
537 | " | 159796 | \n",
538 | " user_000298 | \n",
539 | " 2005-05-14T23:59:45Z | \n",
540 | " 60530915-9371-4b16-bc31-5c4456317ae0 | \n",
541 | " 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 | \n",
542 | " 1.0 | \n",
543 | " 28.000000 | \n",
544 | " Argentina | \n",
545 | "
\n",
546 | " \n",
547 | " | 159797 | \n",
548 | " user_000142 | \n",
549 | " 2005-05-14T23:59:51Z | \n",
550 | " 936addc3-91aa-49de-8ec0-0dc186de151f | \n",
551 | " c0d44a5d-f84b-4d4b-babc-0f3937ef6edb | \n",
552 | " 1.0 | \n",
553 | " 27.216032 | \n",
554 | " Norway | \n",
555 | "
\n",
556 | " \n",
557 | "
\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 | " userid | \n",
617 | " timestamp | \n",
618 | " artist-id | \n",
619 | " track-id | \n",
620 | " gender | \n",
621 | " age | \n",
622 | " country | \n",
623 | "
\n",
624 | " \n",
625 | " \n",
626 | " \n",
627 | " | 159793 | \n",
628 | " user_000298 | \n",
629 | " 2005-05-14T23:57:15Z | \n",
630 | " 79239441-bfd5-4981-a70c-55c3f15c1287 | \n",
631 | " 3dc94d29-3f33-4032-8772-e599f081d085 | \n",
632 | " 1.0 | \n",
633 | " 2.998332e-01 | \n",
634 | " Argentina | \n",
635 | "
\n",
636 | " \n",
637 | " | 159794 | \n",
638 | " user_000870 | \n",
639 | " 2005-05-14T23:58:42Z | \n",
640 | " 1928bd00-5ccb-4dcb-809e-4d1af14bce44 | \n",
641 | " e8ba000e-6304-473c-b6a7-49010b1433d7 | \n",
642 | " 1.0 | \n",
643 | " -1.358756e-15 | \n",
644 | " United Kingdom | \n",
645 | "
\n",
646 | " \n",
647 | " | 159795 | \n",
648 | " user_000293 | \n",
649 | " 2005-05-14T23:59:25Z | \n",
650 | " 64d62f45-e001-40a1-a055-c3545fcc14de | \n",
651 | " b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 | \n",
652 | " 1.0 | \n",
653 | " 4.506848e+00 | \n",
654 | " United Kingdom | \n",
655 | "
\n",
656 | " \n",
657 | " | 159796 | \n",
658 | " user_000298 | \n",
659 | " 2005-05-14T23:59:45Z | \n",
660 | " 60530915-9371-4b16-bc31-5c4456317ae0 | \n",
661 | " 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 | \n",
662 | " 1.0 | \n",
663 | " 2.998332e-01 | \n",
664 | " Argentina | \n",
665 | "
\n",
666 | " \n",
667 | " | 159797 | \n",
668 | " user_000142 | \n",
669 | " 2005-05-14T23:59:51Z | \n",
670 | " 936addc3-91aa-49de-8ec0-0dc186de151f | \n",
671 | " c0d44a5d-f84b-4d4b-babc-0f3937ef6edb | \n",
672 | " 1.0 | \n",
673 | " -1.358756e-15 | \n",
674 | " Norway | \n",
675 | "
\n",
676 | " \n",
677 | "
\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 | " userid | \n",
836 | " timestamp | \n",
837 | " artist-id | \n",
838 | " track-id | \n",
839 | " gender | \n",
840 | " age | \n",
841 | " country | \n",
842 | " u_index | \n",
843 | " i_index | \n",
844 | " artist_index | \n",
845 | " dt | \n",
846 | " time | \n",
847 | "
\n",
848 | " \n",
849 | " \n",
850 | " \n",
851 | " | 0 | \n",
852 | " user_000391 | \n",
853 | " 2005-02-14T00:00:07Z | \n",
854 | " fbd86487-ccb5-4a57-a860-cc3d360b5115 | \n",
855 | " 6b4977f4-3c7a-492a-af61-1e877fa66f52 | \n",
856 | " 0.0 | \n",
857 | " -1.358756e-15 | \n",
858 | " Germany | \n",
859 | " 0 | \n",
860 | " 0 | \n",
861 | " 0 | \n",
862 | " 0 | \n",
863 | " 0.000081 | \n",
864 | "
\n",
865 | " \n",
866 | " | 1 | \n",
867 | " user_000871 | \n",
868 | " 2005-02-14T00:00:38Z | \n",
869 | " b3a6ebdf-4ee6-4ec0-912c-be03ade6c833 | \n",
870 | " 9ecc2ab3-7294-43ad-bdeb-f51388a7a6e0 | \n",
871 | " 1.0 | \n",
872 | " -1.358756e-15 | \n",
873 | " Argentina | \n",
874 | " 1 | \n",
875 | " 1 | \n",
876 | " 1 | \n",
877 | " 0 | \n",
878 | " 0.000440 | \n",
879 | "
\n",
880 | " \n",
881 | " | 2 | \n",
882 | " user_000709 | \n",
883 | " 2005-02-14T00:01:44Z | \n",
884 | " b4d32cff-f19e-455f-86c4-f347d824ca61 | \n",
885 | " 1d0f1ea5-0a92-4578-a7e7-3f2a7129da61 | \n",
886 | " 1.0 | \n",
887 | " -1.358756e-15 | \n",
888 | " Canada | \n",
889 | " 2 | \n",
890 | " 2 | \n",
891 | " 2 | \n",
892 | " 0 | \n",
893 | " 0.001204 | \n",
894 | "
\n",
895 | " \n",
896 | " | 3 | \n",
897 | " user_000285 | \n",
898 | " 2005-02-14T00:02:10Z | \n",
899 | " 95e1ead9-4d31-4808-a7ac-32c3614c116b | \n",
900 | " 46909ba9-46c7-461e-a2ef-280eacd550e4 | \n",
901 | " 0.0 | \n",
902 | " -1.612447e+00 | \n",
903 | " United States | \n",
904 | " 3 | \n",
905 | " 3 | \n",
906 | " 3 | \n",
907 | " 0 | \n",
908 | " 0.001505 | \n",
909 | "
\n",
910 | " \n",
911 | " | 4 | \n",
912 | " user_000142 | \n",
913 | " 2005-02-14T00:02:40Z | \n",
914 | " 51086134-0896-4c00-b54a-c5c37aeaf2bf | \n",
915 | " 14025355-94c2-4e9b-b63f-c16cab9e8086 | \n",
916 | " 1.0 | \n",
917 | " -1.358756e-15 | \n",
918 | " Norway | \n",
919 | " 4 | \n",
920 | " 4 | \n",
921 | " 4 | \n",
922 | " 0 | \n",
923 | " 0.001852 | \n",
924 | "
\n",
925 | " \n",
926 | "
\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 | " userid | \n",
977 | " timestamp | \n",
978 | " artist-id | \n",
979 | " track-id | \n",
980 | " gender | \n",
981 | " age | \n",
982 | " country | \n",
983 | " u_index | \n",
984 | " i_index | \n",
985 | " artist_index | \n",
986 | " dt | \n",
987 | " time | \n",
988 | "
\n",
989 | " \n",
990 | " \n",
991 | " \n",
992 | " | 159793 | \n",
993 | " user_000298 | \n",
994 | " 2005-05-14T23:57:15Z | \n",
995 | " 79239441-bfd5-4981-a70c-55c3f15c1287 | \n",
996 | " 3dc94d29-3f33-4032-8772-e599f081d085 | \n",
997 | " 1.0 | \n",
998 | " 2.998332e-01 | \n",
999 | " Argentina | \n",
1000 | " 10 | \n",
1001 | " 6855 | \n",
1002 | " 391 | \n",
1003 | " 89 | \n",
1004 | " 0.998102 | \n",
1005 | "
\n",
1006 | " \n",
1007 | " | 159794 | \n",
1008 | " user_000870 | \n",
1009 | " 2005-05-14T23:58:42Z | \n",
1010 | " 1928bd00-5ccb-4dcb-809e-4d1af14bce44 | \n",
1011 | " e8ba000e-6304-473c-b6a7-49010b1433d7 | \n",
1012 | " 1.0 | \n",
1013 | " -1.358756e-15 | \n",
1014 | " United Kingdom | \n",
1015 | " 36 | \n",
1016 | " 51071 | \n",
1017 | " 2879 | \n",
1018 | " 89 | \n",
1019 | " 0.999109 | \n",
1020 | "
\n",
1021 | " \n",
1022 | " | 159795 | \n",
1023 | " user_000293 | \n",
1024 | " 2005-05-14T23:59:25Z | \n",
1025 | " 64d62f45-e001-40a1-a055-c3545fcc14de | \n",
1026 | " b83f61e2-a566-49cc-b8dd-9ad5a0caddb2 | \n",
1027 | " 1.0 | \n",
1028 | " 4.506848e+00 | \n",
1029 | " United Kingdom | \n",
1030 | " 29 | \n",
1031 | " 13963 | \n",
1032 | " 602 | \n",
1033 | " 89 | \n",
1034 | " 0.999606 | \n",
1035 | "
\n",
1036 | " \n",
1037 | " | 159796 | \n",
1038 | " user_000298 | \n",
1039 | " 2005-05-14T23:59:45Z | \n",
1040 | " 60530915-9371-4b16-bc31-5c4456317ae0 | \n",
1041 | " 22ea4524-3dcb-44e6-a1bc-4edfd25ad988 | \n",
1042 | " 1.0 | \n",
1043 | " 2.998332e-01 | \n",
1044 | " Argentina | \n",
1045 | " 10 | \n",
1046 | " 1564 | \n",
1047 | " 87 | \n",
1048 | " 89 | \n",
1049 | " 0.999838 | \n",
1050 | "
\n",
1051 | " \n",
1052 | " | 159797 | \n",
1053 | " user_000142 | \n",
1054 | " 2005-05-14T23:59:51Z | \n",
1055 | " 936addc3-91aa-49de-8ec0-0dc186de151f | \n",
1056 | " c0d44a5d-f84b-4d4b-babc-0f3937ef6edb | \n",
1057 | " 1.0 | \n",
1058 | " -1.358756e-15 | \n",
1059 | " Norway | \n",
1060 | " 4 | \n",
1061 | " 48519 | \n",
1062 | " 392 | \n",
1063 | " 89 | \n",
1064 | " 0.999907 | \n",
1065 | "
\n",
1066 | " \n",
1067 | "
\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 |
--------------------------------------------------------------------------------