forked from jrzaurin/neural_cf
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MLP_gluon.py
176 lines (153 loc) · 7.04 KB
/
MLP_gluon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
"""
@author: Javier Rodriguez (jrzaurin@gmail.com)
"""
import numpy as np
import pandas as pd
import os
import heapq
import argparse
import mxnet as mx
from mxnet import gluon, autograd, ndarray
from mxnet.gluon import Block, nn, HybridBlock
from GMF_gluon import train, evaluate, checkpoint
from Dataset import Dataset as ml1mDataset
from time import time
from utils import *
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument("--datadir", type=str, default="Data_Javier/",
help="data directory.")
parser.add_argument("--modeldir", type=str, default="models",
help="models directory")
parser.add_argument("--dataname", type=str, default="ml-1m",
help="chose a dataset.")
parser.add_argument("--epochs", type=int, default=20,
help="number of epochs.")
parser.add_argument("--batch_size", type=int, default=256,
help="batch size.")
parser.add_argument("--layers", type=str, default="[64,32,16,8]",
help="layer architecture. The first elements is used for the embedding \
layers and equals n_emb*2")
parser.add_argument("--dropouts", type=str, default="[0,0,0]",
help="dropout per dense layer. len(dropouts) = len(layers)-1")
parser.add_argument("--l2reg", type=float, default=0.,
help="l2 regularization")
parser.add_argument("--lr", type=float, default=0.01,
help="learning rate.")
parser.add_argument("--learner", type=str, default="adam",
help="Specify an optimizer: adagrad, adam, rmsprop, sgd")
parser.add_argument("--validate_every", type=int, default=1,
help="validate every n epochs")
parser.add_argument("--save_model", type=int , default=1)
parser.add_argument("--n_neg", type=int, default=4,
help="number of negative instances to consider per positive instance.")
parser.add_argument("--topK", type=int, default=10,
help="number of items to retrieve for recommendation.")
return parser.parse_args()
class MLP(HybridBlock):
"""
Concatenate Embeddings that are then passed through a series of Dense
layers
"""
def __init__(self, n_user, n_item, layers, dropouts):
super(MLP, self).__init__()
self.layers = layers
self.n_layers = len(layers)
self.n_user = n_user
self.n_item = n_item
self.mlp = gluon.nn.HybridSequential()
with self.name_scope():
self.embeddings_user = nn.Embedding(n_user, int(layers[0]/2), weight_initializer='normal')
self.embeddings_item = nn.Embedding(n_item, int(layers[0]/2), weight_initializer='normal')
for i in range(1,self.n_layers):
self.mlp.add(nn.Dense(in_units=layers[i-1], units=layers[i], prefix="linear{}".format(i)))
self.mlp.add(nn.Dropout(rate=dropouts[i-1]))
self.out = nn.Dense(in_units=layers[-1], units=1, activation='sigmoid', weight_initializer='uniform')
def forward(self, users, items):
user_emb = self.embeddings_user(users)
item_emb = self.embeddings_item(items)
emb_vector = ndarray.concat(user_emb,item_emb,dim=1)
emb_vector = self.mlp(emb_vector)
preds = self.out(emb_vector)
return preds
if __name__ == '__main__':
args = parse_args()
datadir = args.datadir
dataname = args.dataname
modeldir = args.modeldir
layers = eval(args.layers)
ll = str(layers[-1]) #last layer
dropouts = eval(args.dropouts)
dp = "wdp" if dropouts[0]!=0 else "wodp"
l2reg = args.l2reg
n_emb = int(layers[0]/2)
batch_size = args.batch_size
epochs = args.epochs
learner = args.learner
lr = args.lr
validate_every = args.validate_every
save_model = args.save_model
topK = args.topK
n_neg = args.n_neg
modelfname = "gluon_MLP" + \
"_".join(["_bs", str(batch_size)]) + \
"_".join(["_reg", str(l2reg).replace(".", "")]) + \
"_".join(["_lr", str(lr).replace(".", "")]) + \
"_".join(["_n_emb", str(n_emb)]) + \
"_".join(["_ll", ll]) + \
"_".join(["_dp", dp]) + \
".params"
modelpath = os.path.join(modeldir, modelfname)
resultsdfpath = os.path.join(modeldir, 'results_df.p')
dataset = ml1mDataset(os.path.join(datadir, dataname))
trainRatings, testRatings, testNegatives = dataset.trainMatrix, dataset.testRatings, dataset.testNegatives
n_users, n_items = trainRatings.shape
test = get_test_instances(testRatings, testNegatives)
test_dataset = mx.gluon.data.dataset.ArrayDataset(test)
test_loader = mx.gluon.data.DataLoader(dataset=test_dataset,
batch_size=100,
shuffle=False)
ctx = mx.gpu() if mx.test_utils.list_gpus() else mx.cpu()
model = MLP(n_users, n_items, layers, dropouts)
model.hybridize()
model.initialize(ctx=ctx)
criterion = gluon.loss.L2Loss()
if learner.lower() == "adagrad":
trainer = gluon.Trainer(model.collect_params(), 'AdaGrad', {'learning_rate': lr, 'wd': l2reg})
elif learner.lower() == "rmsprop":
trainer = gluon.Trainer(model.collect_params(), 'RMSProp', {'learning_rate': lr, 'wd': l2reg})
elif learner.lower() == "adam":
trainer = gluon.Trainer(model.collect_params(), 'Adam', {'learning_rate': lr, 'wd': l2reg})
else:
trainer = gluon.Trainer(model.collect_params(), 'SGD', {'learning_rate': lr, 'wd': l2reg})
best_hr, best_ndcgm, best_iter=0,0,0
for epoch in range(1,epochs+1):
t1 = time()
train(model, criterion, trainer, epoch, batch_size, ctx,
trainRatings,n_items,n_neg,testNegatives)
t2 = time()
if epoch % validate_every == 0:
(hr, ndcg) = evaluate(model, test_loader, ctx, topK)
print("Epoch: {} {:.2f}s, HR = {:.4f}, NDCG = {:.4f}, validated in {:.2f}s".
format(epoch, t2-t1, hr, ndcg, time()-t2))
if hr > best_hr:
best_hr, best_ndcg, best_iter, train_time = hr, ndcg, epoch, t2-t1
if save_model:
checkpoint(model, modelpath)
print("End. Best Iteration {}: HR = {:.4f}, NDCG = {:.4f}. ".format(best_iter, best_hr, best_ndcg))
if save_model:
print("The best MLP model is saved to {}".format(modelpath))
if save_model:
if not os.path.isfile(resultsdfpath):
results_df = pd.DataFrame(columns = ["modelname", "best_hr", "best_ndcg", "best_iter",
"train_time"])
experiment_df = pd.DataFrame([[modelfname, best_hr, best_ndcg, best_iter, train_time]],
columns = ["modelname", "best_hr", "best_ndcg", "best_iter","train_time"])
results_df = results_df.append(experiment_df, ignore_index=True)
results_df.to_pickle(resultsdfpath)
else:
results_df = pd.read_pickle(resultsdfpath)
experiment_df = pd.DataFrame([[modelfname, best_hr, best_ndcg, best_iter, train_time]],
columns = ["modelname", "best_hr", "best_ndcg", "best_iter","train_time"])
results_df = results_df.append(experiment_df, ignore_index=True)
results_df.to_pickle(resultsdfpath)