Skip to content

Commit bb802a6

Browse files
committed
new: add a deepfm model for rating prediction.
Add a deepfm model for rating prediction and been tested with scripts `test_rating_pred.py`.
1 parent b2a7c9e commit bb802a6

File tree

2 files changed

+186
-6
lines changed

2 files changed

+186
-6
lines changed

models/rating_prediction/deepfm.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Implementation of DeepFM with tensorflow.
6+
7+
Reference:
8+
[1] DeepFM: A Factorization-Machine based Neural Network for CTR Prediction,
9+
Huifeng Guo, Ruiming Tang, Yunming Yey, Zhenguo Li, Xiuqiang He.
10+
"""
11+
12+
import math
13+
import time
14+
import tensorflow as tf
15+
from sklearn.metrics import mean_squared_error
16+
from utils.evaluation.RatingMetrics import *
17+
18+
__author__ = "Buracag Yang"
19+
__copyright__ = "Copyright 2018, The DeepRec Project"
20+
21+
__license__ = "GPL"
22+
__version__ = "1.0.0"
23+
__maintainer__ = "Buracag Yang"
24+
__email__ = "15591875898@163.com"
25+
__status__ = "Development"
26+
27+
28+
class DeepFM(object):
29+
def __init__(self, sess, num_user, num_item, **kwargs):
30+
self.sess = sess
31+
self.num_user = num_user
32+
self.num_item = num_item
33+
self.epochs = kwargs['epochs']
34+
self.batch_size = kwargs['batch_size']
35+
self.learning_rate = kwargs['learning_rate']
36+
self.reg_rate = kwargs['reg_rate']
37+
self.num_factors = kwargs['num_factors']
38+
self.display_step = kwargs['display_step']
39+
self.show_time = kwargs['show_time']
40+
self.T = kwargs['T']
41+
self.layers = kwargs['layers']
42+
self.field_size = kwargs['field_size']
43+
44+
self.train_features = None
45+
self.y = None
46+
self.dropout_keep = None
47+
self.first_oder_weight = None
48+
self.feature_embeddings = None
49+
self.feature_bias = None
50+
self.bias = None
51+
self.pred_rating = None
52+
self.pred = None
53+
self.loss = None
54+
self.optimizer = None
55+
self.num_training = None
56+
print("You are running DeepFM.")
57+
58+
def build_network(self, feature_size):
59+
self.train_features = tf.placeholder(tf.int32, shape=[None, None])
60+
self.y = tf.placeholder(tf.float32, shape=[None, 1])
61+
self.dropout_keep = tf.placeholder(tf.float32)
62+
self.first_oder_weight = tf.Variable(tf.random_normal([feature_size], mean=0.0, stddev=0.01))
63+
self.feature_embeddings = tf.Variable(tf.random_normal([feature_size, self.num_factors], mean=0.0, stddev=0.01))
64+
self.feature_bias = tf.Variable(tf.random_uniform([feature_size, 1], 0.0, 0.0))
65+
self.bias = tf.Variable(tf.constant(0.0))
66+
67+
# f(x)
68+
with tf.variable_scope("First-order"):
69+
y1 = tf.reduce_sum(tf.nn.embedding_lookup(self.first_oder_weight, self.train_features), 1, keepdims=True)
70+
71+
with tf.variable_scope("Second-order"):
72+
nonzero_embeddings = tf.nn.embedding_lookup(self.feature_embeddings, self.train_features)
73+
sum_square = tf.square(tf.reduce_sum(nonzero_embeddings, 1))
74+
square_sum = tf.reduce_sum(tf.square(nonzero_embeddings), 1)
75+
y_fm = 0.5 * tf.reduce_sum(tf.subtract(sum_square, square_sum), 1, keepdims=True)
76+
y_fm = tf.nn.dropout(y_fm, self.dropout_keep)
77+
78+
with tf.variable_scope("Deep_part"):
79+
deep_inputs = tf.reshape(nonzero_embeddings, shape=[-1, self.field_size*self.num_factors]) # None * (F*K)
80+
for i in range(len(self.layers)):
81+
deep_inputs = tf.contrib.layers.fully_connected(
82+
inputs=deep_inputs, num_outputs=self.layers[i],
83+
weights_regularizer=tf.contrib.layers.l2_regularizer(self.reg_rate), scope='mlp%d' % i)
84+
# TODO: dropout
85+
86+
y_deep = tf.contrib.layers.fully_connected(
87+
inputs=deep_inputs, num_outputs=1, activation_fn=tf.nn.relu,
88+
weights_regularizer=tf.contrib.layers.l2_regularizer(self.reg_rate),
89+
scope='deep_out')
90+
y_d = tf.reshape(y_deep, shape=[-1, 1])
91+
92+
with tf.variable_scope("DeepFM-out"):
93+
f_b = tf.reduce_sum(tf.nn.embedding_lookup(self.feature_bias, self.train_features), 1)
94+
b = self.bias * tf.ones_like(self.y)
95+
self.pred_rating = tf.add_n([y1, y_fm, y_d, f_b, b])
96+
self.pred = tf.sigmoid(self.pred_rating)
97+
98+
self.loss = tf.nn.l2_loss(tf.subtract(self.y, self.pred_rating)) + \
99+
tf.contrib.layers.l2_regularizer(self.reg_rate)(self.feature_embeddings)
100+
101+
self.optimizer = tf.train.AdagradOptimizer(self.learning_rate).minimize(self.loss)
102+
103+
def train(self, train_data):
104+
self.num_training = len(train_data['Y'])
105+
total_batch = int(self.num_training / self.batch_size)
106+
rng_state = np.random.get_state()
107+
np.random.shuffle(train_data['Y'])
108+
np.random.set_state(rng_state)
109+
np.random.shuffle(train_data['X'])
110+
111+
# train
112+
for i in range(total_batch):
113+
start_time = time.time()
114+
batch_y = train_data['Y'][i * self.batch_size:(i + 1) * self.batch_size]
115+
batch_x = train_data['X'][i * self.batch_size:(i + 1) * self.batch_size]
116+
loss, _ = self.sess.run((self.loss, self.optimizer),
117+
feed_dict={self.train_features: batch_x,
118+
self.y: batch_y,
119+
self.dropout_keep: 0.5})
120+
if i % self.display_step == 0:
121+
print("Index: %04d; cost= %.9f" % (i + 1, np.mean(loss)))
122+
if self.show_time:
123+
print("one iteration: %s seconds." % (time.time() - start_time))
124+
125+
def test(self, test_data):
126+
num_example = len(test_data['Y'])
127+
feed_dict = {self.train_features: test_data['X'], self.y: test_data['Y'], self.dropout_keep: 1.0}
128+
predictions = self.sess.run(self.pred_rating, feed_dict=feed_dict)
129+
y_pred = np.reshape(predictions, (num_example,))
130+
y_true = np.reshape(test_data['Y'], (num_example,))
131+
predictions_bounded = np.maximum(y_pred, np.ones(num_example) * min(y_true)) # bound the lower values
132+
predictions_bounded = np.minimum(predictions_bounded, np.ones(num_example) * max(y_true))
133+
_RMSE = math.sqrt(mean_squared_error(y_true, predictions_bounded))
134+
print("RMSE:" + str(_RMSE))
135+
136+
def execute(self, train_data, test_data):
137+
init = tf.global_variables_initializer()
138+
self.sess.run(init)
139+
140+
for epoch in range(self.epochs):
141+
print("Epoch: %04d;" % epoch)
142+
self.train(train_data)
143+
if epoch % self.T == 0:
144+
self.test(test_data)
145+
146+
def save(self, path):
147+
saver = tf.train.Saver()
148+
saver.save(self.sess, path)
149+
150+
# def predict(self, user_id, item_id):
151+
# return self.sess.run([self.pred_rating], feed_dict={self.user_id: user_id, self.item_id: item_id})[0]

test/test_rating_pred.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,53 @@
99
from models.rating_prediction.nrr import NRR
1010
from models.rating_prediction.autorec import *
1111
from models.rating_prediction.nfm import NFM
12+
from models.rating_prediction.deepfm import DeepFM
1213
from utils.load_data.load_data_rating import *
1314
from utils.load_data.load_data_content import *
1415

1516

1617
def parse_args():
1718
parser = argparse.ArgumentParser(description='nnRec')
18-
parser.add_argument('--model', choices=['MF', 'NNMF', 'NRR', 'I-AutoRec', 'U-AutoRec', 'FM', 'NFM', 'AFM'],
19-
default='U-AutoRec')
19+
parser.add_argument('--model', choices=['MF', 'NNMF', 'NRR', 'I-AutoRec', 'U-AutoRec',
20+
'FM', 'NFM', 'AFM', 'DEEP-FM'],
21+
default='DEEP-FM')
2022
parser.add_argument('--epochs', type=int, default=200)
21-
parser.add_argument('--num_factors', type=int, default=10)
22-
parser.add_argument('--display_step', type=int, default=1000)
2323
parser.add_argument('--batch_size', type=int, default=256) # 128 for unlimpair
2424
parser.add_argument('--learning_rate', type=float, default=1e-3) # 1e-4 for unlimpair
2525
parser.add_argument('--reg_rate', type=float, default=0.1) # 0.01 for unlimpair
26+
parser.add_argument('--num_factors', type=int, default=10)
27+
parser.add_argument('--display_step', type=int, default=1000)
28+
parser.add_argument('--show_time', type=bool, default=False)
29+
parser.add_argument('--T', type=int, default=2)
30+
parser.add_argument('--deep_layers', type=str, default="200, 200, 200")
31+
parser.add_argument('--field_size', type=int, default=10)
32+
2633
return parser.parse_args()
2734

2835

2936
if __name__ == '__main__':
3037
args = parse_args()
3138
epochs = args.epochs
39+
batch_size = args.batch_size
3240
learning_rate = args.learning_rate
3341
reg_rate = args.reg_rate
3442
num_factors = args.num_factors
3543
display_step = args.display_step
36-
batch_size = args.batch_size
44+
show_time = args.show_time,
45+
46+
kws = {
47+
'epochs': epochs,
48+
'batch_size': batch_size,
49+
'learning_rate': learning_rate,
50+
'reg_rate': reg_rate,
51+
'num_factors': num_factors,
52+
'display_step': display_step,
53+
'show_time': show_time[0],
54+
'T': args.T,
55+
'layers': list(map(int, args.deep_layers.split(','))),
56+
'field_size': args.field_size
57+
58+
}
3759

3860
train_data, test_data, n_user, n_item = load_data_rating(path="../Data/ml100k/movielens_100k.dat",
3961
header=['user_id', 'item_id', 'rating', 't'],
@@ -69,9 +91,16 @@ def parse_args():
6991
batch_size=batch_size, display_step=display_step)
7092
model.build_network(feature_M)
7193

94+
if args.model == "DEEP-FM":
95+
train_data, test_data, feature_M = load_data_fm()
96+
n_user = 957
97+
n_item = 4082
98+
model = DeepFM(sess, n_user, n_item, **kws)
99+
model.build_network(feature_M)
100+
72101
# build and execute the model
73102
if model is not None:
74-
if args.model in ('FM', 'NFM'):
103+
if args.model in ('FM', 'NFM', 'DEEP-FM'):
75104
model.execute(train_data, test_data)
76105
else:
77106
model.build_network()

0 commit comments

Comments
 (0)