Skip to content

Commit 235faf8

Browse files
Refactoring and added new comments
1 parent 0c70662 commit 235faf8

10 files changed

+362
-257
lines changed

.idea/workspace.xml

+141-120
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

engine/export_data_to_django.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
def execute_sql(s):
8-
con = psycopg2.connect('dbname=django_pugliaeventi user=postgres password=Frapama29')
8+
con = psycopg2.connect('dbname=django_pugliaeventi user=postgres password=password')
99
with con:
1010
cur = con.cursor()
1111
cur.execute(s)
@@ -58,6 +58,11 @@ def import_places():
5858

5959

6060
def import_sample_ratings():
61+
"""
62+
Questo metodo effettua l'importazione nel db Django dei rating creati in maniera casuale preesistenti nel dataset
63+
ratings_train.csv. Tuttavia, tali ratings non sono direttamente utilizzati in Django, per cui tale funzione al
64+
momento risulta essere inutile.
65+
"""
6166
with open('data/ratings_train.csv') as f:
6267
i = 1
6368
for row in f.readlines():
@@ -187,7 +192,7 @@ def import_eventi():
187192

188193
if __name__ == "__main__":
189194
import_places()
190-
import_sample_ratings()
195+
# import_sample_ratings() INUTILE
191196
import_comuni()
192197
import_distanze()
193198
import_eventi()

engine/lightfm_data_fetcher.py

+1-13
Original file line numberDiff line numberDiff line change
@@ -215,18 +215,8 @@ def _parse_item_metadata(num_items, item_metadata_raw, item_tags_raw):
215215

216216
def fetch_pugliaeventi(indicator_features=True, tag_features=False, min_rating=0.0):
217217
"""
218-
Fetch the `Movielens 100k dataset <http://grouplens.org/datasets/movielens/100k/>`_.
219-
220-
The dataset contains 100,000 interactions from 1000 users on 1700 movies,
221-
and is exhaustively described in its
222-
`README <http://files.grouplens.org/datasets/movielens/ml-100k-README.txt>`_.
223-
224218
Parameters
225219
----------
226-
227-
data_home: path, optional
228-
Path to the directory in which the downloaded data should be placed.
229-
Defaults to ``~/lightfm_data/``.
230220
indicator_features: bool, optional
231221
Use an [n_items, n_items] identity matrix for item features. When True with genre_features,
232222
indicator and genre features are concatenated into a single feature matrix of shape
@@ -237,8 +227,6 @@ def fetch_pugliaeventi(indicator_features=True, tag_features=False, min_rating=0
237227
[n_items, n_items + n_genres].
238228
min_rating: float, optional
239229
Minimum rating to include in the interaction matrix.
240-
download_if_missing: bool, optional
241-
Download the data if not present. Raises an IOError if False and data is missing.
242230
243231
Notes
244232
-----
@@ -262,7 +250,7 @@ def fetch_pugliaeventi(indicator_features=True, tag_features=False, min_rating=0
262250

263251
if not (indicator_features or tag_features):
264252
raise ValueError('At least one of item_indicator_features '
265-
'or genre_features must be True')
253+
'or tag_features must be True')
266254

267255
# Load raw data
268256
(ratings_train, ratings_test, items, users, labels_item, labels_user) = _read_raw_data()

engine/lightfm_pugliaeventi.py

+30-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
from lightfm.cross_validation import random_train_test_split
2-
31
from engine.lightfm_data_fetcher import fetch_pugliaeventi
42
from engine.lightfm_data_fetcher import _build_interaction_matrix, _read_item_data, _parse_item_metadata
53
from lightfm.evaluation import auc_score, precision_at_k, recall_at_k
@@ -9,10 +7,6 @@
97
import pickle
108

119

12-
# Set the number of threads; you can increase this
13-
# if you have more physical cores available.
14-
15-
1610
NUM_THREADS = 2
1711
NUM_COMPONENTS = 30
1812
NUM_EPOCHS = 50
@@ -23,14 +17,19 @@
2317

2418

2519
def find_recommendations(user, model, data):
20+
"""
21+
Ricerca di raccomandazioni utili per un utente specifico:
22+
Il seguente metodo consente di ricercare raccomandazioni per un utente specifico. Il metodo predict del modello di
23+
LightFM restituisce la lista degli ID dei luoghi, che successivamente viene ordinata in maniera decrescente (dal
24+
luogo maggiormente raccomandato a quello meno raccomandato)
25+
"""
26+
2627
# number of users and places in training data
2728
n_users, n_items = data['train'].shape
2829

2930
# places the user already rated
3031
# known_positives = data['item_labels'][data['train'].tocsr()[user].indices]
3132

32-
# movies our model predicts they will like
33-
3433
scores = model.predict(user,
3534
np.arange(n_items),
3635
item_features=data['item_features'])
@@ -57,6 +56,18 @@ def find_recommendations(user, model, data):
5756

5857

5958
def add_rating_to_model(max_user_id, max_item_id, user_id, item_id, rating):
59+
"""
60+
Aggiunta di un nuovo rating al modello:
61+
Il seguente metodo consente di aggiungere un nuovo rating al modello. In tal caso, considerato che l'utente esiste
62+
già, non è necessario apprendere nuovamente il modello. Infatti, è sufficiente utilizzare il metodo fit_partial del
63+
modello di LightFM per poter aggiungere il rating ad un utente esistente.
64+
Il modello viene caricato in memoria utilizzando il checkpoint salvato in formato .pickle. Successivamente, viene
65+
costruita una matrice delle interazioni il cui shape è pari a max_user_id x max_item_id e contiene la nuova
66+
interazione che si vuole aggiungere al modello. Il metodo fit_partial viene chiamato passando la matrice delle
67+
interazioni creata, le features degli item, il numero di epochs da applicare per l'apprendimento e il numero di
68+
threads da impiegare.
69+
"""
70+
6071
if os.path.isfile(os.path.join(script_dir, MODEL_CHECKPOINT_PATH)):
6172
with open(os.path.join(script_dir, MODEL_CHECKPOINT_PATH), 'rb') as fle:
6273
model = pickle.load(fle)
@@ -77,6 +88,17 @@ def add_rating_to_model(max_user_id, max_item_id, user_id, item_id, rating):
7788

7889

7990
def learn_model(force_model_creation=False):
91+
"""
92+
Apprendimento del modello LightFM:
93+
Il seguente metodo consente di caricare (se presente in formato .pickle) oppure apprendere il modello LightFM.
94+
L'apprendimento del modello avviene con il metodo fit del modello di LightFM, passandogli il dataset contenente le
95+
interazioni tra utenti e item (luoghi in questo caso), le features dei luoghi, il numero di epochs da applicare con
96+
l'apprendimento e il numero di threads da impiegare.
97+
In caso di apprendimento di un modello, i dati da impiegare vengono caricati utilizzando il metodo
98+
fetch_pugliaeventi del modulo lightfm_data_featcher, in cui i dati vengono caricati dai file .csv presenti nel
99+
folder engine/data e vengono strutturati in un formato utile per la creazione del modello in LightFM.
100+
"""
101+
80102
data = fetch_pugliaeventi(min_rating=0.0, indicator_features=False, tag_features=True)
81103

82104
if os.path.isfile(os.path.join(script_dir, MODEL_CHECKPOINT_PATH)) and not force_model_creation:

engine/lightfm_pugliaeventi_old.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88
import pickle
99

1010

11-
# Set the number of threads; you can increase this
12-
# if you have more physical cores available.
13-
14-
1511
NUM_THREADS = 2
1612
NUM_COMPONENTS = 30
1713
NUM_EPOCHS = 50
@@ -31,7 +27,7 @@ def sample_recommendation(model, data, user_ids):
3127
# places they already rated
3228
known_positives = data['item_labels'][data['train'].tocsr()[user_id].indices]
3329

34-
# movies our model predicts they will like
30+
# places our model predicts they will like
3531

3632
scores = model.predict(user_id,
3733
np.arange(n_items),

pugliaeventi/urls.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""
1616
from django.conf.urls import url
1717
from django.contrib import admin
18+
from django.shortcuts import render
1819
from django.urls import include, path
1920

2021
from recommender_webapp import views
@@ -36,7 +37,7 @@
3637
path('ratings/<int:place_id>/<int:mood>/<int:companionship>/', views.add_rating_config, name='add_rating_conf'),
3738

3839
# place it at whatever base url you like
39-
url(r'^ajax_select/', include(ajax_select_urls)),
40-
#url(r'.*', lambda request: render(request, '404.html'), name='404')
40+
url(r'^ajax_select/', include(ajax_select_urls))
41+
# url(r'.*', lambda request: render(request, '404.html'), name='404')
4142

4243
]

pugliaeventi/views.py

+18-17
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,26 @@
66
from recommender_webapp.models import Mood, Rating
77

88

9-
"""
10-
Pagina principale:
11-
La view, prima di procedere con la visualizzazione della pagina principale, verifica il numero di rating effettuati
12-
dall'utente. In questa prima versione, il rating consiste nell'aggiunta di un luogo al proprio profilo, senza un rate
13-
numerico. Dato che LightFM implementa un modello implicito, non è necessario un rate numerico. Se il numero di rating
14-
è inferiore al numero di rating necessari per la configurazione del profilo, significa che l'utente non ha ancora
15-
concluso la procedura di configurazione. In tal caso, l'utente viene reindirizzato alla pagina della configurazione
16-
del profilo. In caso contrario, si procede con la visualizzazione della pagina principale.
17-
La pagina principale consente di visualizzare i posti raccomandati in base al mood (angry, joyful, sad), alla
18-
companionship (withFriends oppure alone), alla distanza in KM e alla presenza di eventi. Questi sono i filtri presenti
19-
nel form. Per ricevere le raccomandazioni viene utilizzato il metodo find_recommendations del modulo lightfm_manager.
20-
L'ID utente utilizzato da LightFM, e quindi passato al metodo find_recommendations, è una stringa che si costituisce
21-
delle seguenti componenti: (ID utente in Django incrementato di 100) + ID mood + ID companionship. Ad esempio, se l'id
22-
utente in django è 4, l'ID mood è 2 e l'ID companionship è 1 allora la stringa rappresentante l'utente è: 10421. L'id
23-
utente di django viene sommato a 100 in quanto gli utenti da 1 a 100 sono già presenti nel dataset di LightFM (vedi
24-
data/users.csv e ratings_train.csv)
25-
"""
269
@csrf_protect
2710
def index(request):
11+
"""
12+
Pagina principale:
13+
La view, prima di procedere con la visualizzazione della pagina principale, verifica il numero di rating effettuati
14+
dall'utente. In questa prima versione, il rating consiste nell'aggiunta di un luogo al proprio profilo, senza un rate
15+
numerico. Dato che LightFM implementa un modello implicito, non è necessario un rate numerico. Se il numero di rating
16+
è inferiore al numero di rating necessari per la configurazione del profilo, significa che l'utente non ha ancora
17+
concluso la procedura di configurazione. In tal caso, l'utente viene reindirizzato alla pagina della configurazione
18+
del profilo. In caso contrario, si procede con la visualizzazione della pagina principale.
19+
La pagina principale consente di visualizzare i posti raccomandati in base al mood (angry, joyful, sad), alla
20+
companionship (withFriends oppure alone), alla distanza in KM e alla presenza di eventi. Questi sono i filtri presenti
21+
nel form. Per ricevere le raccomandazioni viene utilizzato il metodo find_recommendations del modulo lightfm_manager.
22+
L'ID utente utilizzato da LightFM, e quindi passato al metodo find_recommendations, è una stringa che si costituisce
23+
delle seguenti componenti: (ID utente in Django incrementato di 100) + ID mood + ID companionship. Ad esempio, se l'id
24+
utente in django è 4, l'ID mood è 2 e l'ID companionship è 1 allora la stringa rappresentante l'utente è: 10421. L'id
25+
utente di django viene sommato a 100 in quanto gli utenti da 1 a 100 sono già presenti nel dataset di LightFM (vedi
26+
data/users.csv e ratings_train.csv)
27+
"""
28+
2829
context = {}
2930
recommended_places = []
3031
# places_dict = data_loader.data_in_memory['places_dict']

recommender_webapp/common/lightfm_manager.py

+34
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@
77

88

99
def add_user(user_id, user_location, user_contexts, data):
10+
"""
11+
Aggiunta di un nuovo utente al sistema di raccomandazione:
12+
Quando un nuovo utente si registra al sistema e completa la procedura di configurazione del profilo, oltre che
13+
memorizzare l'utente e i rating nel database Django, è necessario memorizzare le informazioni anche nel dataset di
14+
LightFM (users.csv e ratings_train.csv). Successivamente è INDISPENSABILE apprendere nuovamente il modello. A tal
15+
scopo viene utilizzato il metodo learn_model del modulo lightfm_pugliaeventi.
16+
"""
17+
1018
lightfm_user_id = constant.DJANGO_USER_ID_BASE_START_LIGHTFM + user_id
1119
for user_context in user_contexts:
1220
contextual_lightfm_user_id = str(lightfm_user_id) + str(user_context.get('mood').value) + str(user_context.get('companionship').value)
@@ -29,6 +37,20 @@ def add_user(user_id, user_location, user_contexts, data):
2937

3038

3139
def add_rating(contextual_lightfm_user_id, place_id, rating):
40+
"""
41+
Aggiunta di un nuovo rating al sistema di raccomandazione:
42+
Quando un utente aggiunge un nuovo luogo al suo profilo (lo seleziona come valido in un determinato contesto),
43+
oltre che memorizzare il rating nel database Django, è necessario memorizzare le informazioni anche nel dataset di
44+
LightFM (ratings_train.csv).
45+
Diversamente dal caso precedente, in questo caso l'utente già esiste ed è presente nel modello LightFM. In tal caso,
46+
per aggiungere un rating per un utente specifico non è necessario apprendere nuovamente il modello. La funzione
47+
fit_partial del modello di LightFM consente di aggiungere al modello preesistente il rating per l'utente
48+
specificato. Tutto questo processo viene implementato all'interno del metodo add_rating_to_model del modulo
49+
lightfm_pugliaeventi. A tal metodo è necessario passare (oltre che ID utente, ID luogo e rating) l'attuale ID
50+
massimo utente e l'attuale ID massimo item (sono necessari in quanto LightFM costruisce una matrice di shape
51+
max_user_id x max_item_id).
52+
"""
53+
3254
# Add rating to ratings.csv
3355
with open(r'engine/data/ratings_train.csv', 'a') as f:
3456
writer = csv.writer(f)
@@ -51,6 +73,18 @@ def add_rating(contextual_lightfm_user_id, place_id, rating):
5173

5274

5375
def find_recommendations(user, user_location, distance, any_events):
76+
"""
77+
Ricerca di raccomandazioni utili per un utente specifico:
78+
Il seguente metodo consente di ricercare raccomandazioni per un utente specifico ed implementa anche operazioni di
79+
post-filtering sui risultati restituiti da LightFM. Mediante il metodo find_recommendations del modulo
80+
lightfm_pugliaeventi vengono recuperati gli id dei luoghi raccomandati. LightFM restituisce un lista in cui gli ID
81+
dei luoghi sono ordinati per rilevanza e di tale lista vengono prelevati solamente i primi 300 (costante
82+
NUM_RECOMMENDATIONS_FROM_LIGHTFM). A partire da ciascun ID, vengono prelevati gli oggetti Place dal DB Django.
83+
Successivamente, se l'utente ha selezionato la voce "any_events", significa che è interessato a luoghi in cui ci
84+
sono degli eventi in programma. Inoltre, se l'utente ha specificato un range di KM, è necessario procedere ad un
85+
ulteriore filtraggio dei luoghi in base alla distanza dalla location dell'utente.
86+
"""
87+
5488
recommended_places = []
5589
user = int(user) - 1 # LightFM uses a zero-based indexing
5690
model, data = lightfm_pugliaeventi.learn_model()

0 commit comments

Comments
 (0)