Table of contents
1. GFM (generalized matrix factorization) model:
5. MLP (Multilayer Perceptron) model:
4. Implementation results (for detailed results of the paper, please see the original text)
1. Top-K project recommendations
5. The results of running my code
1. Research background
Use neural networks for collaborative filtering in recommendation systems. Traditional collaborative filtering methods, such as matrix factorization, have been widely used in recommendation systems. However, they often rely on linear models that may not capture the complex nonlinear relationships between users and items. This article aims to explore the potential of deep learning and neural networks to improve collaborative filtering performance.
论文地址:NeuMF
Set of numbers:MovieLens< /span>NeuMF Daigo site:Pinterest、
2. Relevant knowledge
1, GFM (square resolution) model:
From the explanation in the paper, user and itemAll sparse vectors are obtained throughone-hot encoding, and then through oneembeddingThe layer mapping isuser vector and item vector. In this way, the hidden vectors of user and item are obtained, which can be used Hadamard product(Element-wise Produce) to interactuser and < /span>item vector.
2、One-hot编码
One-hotEncoding is a common feature encoding technique used to convert discrete categorical features into something machine learning models can understand digital representation. For example, if we have a feature "Color""red", possible values are " and "green ". Using one-hot encoding, we can represent "red" is[1, 0, 0], "blue< /span>,. [ 0, 0, 1] is expressed as"green"[0, 1, 0] is expressed as "
3. embedding layer
Embedding layer (embedding) is a technology that maps high-dimensional discrete data to low-dimensional continuous vector space. For example, in recommendation systems, representing users and items as embedding vectors canbetter capture the correlation between users and items.
Advantages:It is able to reduce the dimension of features, remove the disorder of discrete data, and map related entities to similar vector space positions. These embedding vectors can serve as input to machine learning models, thereby improving the model's performance and generalization capabilities (referring to machine learning). The ability of a learning model to perform on unseen data).
4, Harden 马积
B=[[5,6],[7,8]]
A ⊙ B = [[1 5, 2 6], [3 7, 4 8]] = [[5, 12], [21, 32]]
5. MLP (Multilayer Perceptron) model:
Output Layer: The final output has only one value, the closer it is to 1, the closer it is user may like this moreitem. After comparing with y ^ u i , the loss is backpropagated to optimize the entire model. Its Embedding layer is the same as InputLayer and GMF. The activation function used by Neural CF Layers is ReLU.
6, Gekikatsu functionReLU
Activation function usedReLUFunction:For inputs greater than zero, the output is equal to the input; for For inputs less than or equal to zero, the output is zero. The ReLU function usually has better computational efficiency and convergence.
7、NeuMF模型:
Fusion of two separate parts. As shown in the following formula:
Taking the logarithm of the likelihood function and then inverting it, the objective function to be minimized can be obtained as follows:
3. Research methods
1, Blank Evaluation Method
White space evaluation method is a method of evaluating artificial intelligence systems or algorithms. Its main purpose is to evaluate the performance and capabilities of the system in the face of new situations or unknown data without prior knowledge or preset conditions.
The basic idea is to apply the trained model or system to new test sets or data and observe its behavior and output in unknown situations. Different from traditional evaluation methods, the blank evaluation method emphasizes the robustness and generalization ability of the system in the face of new situations.
2. Data set division
The data set is divided using the negative sampling rate method: means that when training the two-classification model, in order to balance the proportion of positive samples and negative samples, from the negative samples The sampling ratio.
3. Introduction to data sets
(1)MovieLens 1M, Yes a>MovieLens A version of the data set, containing 100 more than 10,000 user comments on movies rating data. Includes: user rating data, movie information and user information.
(2) Pinterest is a world-renowned image sharing and discovery platform. Users can Pinterest to collect, organize and share your favorite images and visual content.
4. Implementation results (for detailed results of the paper, please see the original text)
1,Top-KItem recommendation
Top-KItem recommendation is a common recommendation system method, which aims to recommend top-K to users based on their interests and preferences projects or items.
The training loss and recommendation performance of the NCF method are related to the number of iterations of MovieLens (the coefficient is < /span>)8
NCFThe performance of the method is related to the number of negative samples per positive instance(factors=16). The figure also shows the performance of BPR, which samples only one negative instance and learns paired with one positive instance. BPR with larger sampling rate. This shows that the pointwise logarithmic loss is more advantageous than the pairwiseBPRloss. For both data sets, the optimal sampling rate is approximately 3 to 6. On Pinterest, we found that when the sampling rate is greater than 7, < The performance of the /span> method begins to degrade. It shows that setting the sample rate too aggressively can adversely affect performance. NCF
non-uniformMLPtargeton HRsum< /span> K=10NDCG lower
5. The results of running my code
The original code of the author of the paper: https://github.com/hexiangnan/neural_collaborative_filtering
Since this code uses python2., the version of keras is also 1.0.7, and it has been configured incorrectly. OK, so I used his code, code link: https://github.com/wyl6/Recommender-Systems-Samples/tree/master/RecSys%20And%20Deep%20Learning/DNN/ncf. Blog link:
However, due to the version update, I made further modifications to the above code using keras 2.10.0 and theano 1.0.5 versions. The complete code has been uploaded to GitHub (Xian -Gang/NCF (github.com)) and the resource area of csdn.
Part of the code
GMF code:
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 14 14:07:54 2023
@author: Lenovo
"""
import numpy as np
import theano.tensor as T
import tensorflow.keras as keras
from keras import backend as K
from keras import initializers
from keras.models import Sequential, Model, load_model, save_model
from keras.layers import Add, Embedding, Input, Reshape, Flatten, Multiply
from keras.layers.core import Dense, Lambda, Activation
from keras.optimizers import Adagrad, Adam, SGD, RMSprop
from keras.regularizers import l2
from Dataset import Dataset
from evaluate import evaluate_model
from time import time
import multiprocessing as mp
import sys
import math
import argparse
import warnings
warnings.filterwarnings("ignore")
# In[1]:arguments
#################### Arguments ####################
def parse_args():
parser = argparse.ArgumentParser(description='Run GMF')
parser.add_argument('--path', nargs='?', default='Data/', help='Input data path')
parser.add_argument('--dataset', nargs='?', default='ml-1m', help='Choose a dataset.')
parser.add_argument('--epochs', type=int, default=1, help='Number of epochs.')
parser.add_argument('--batch_size', type=int, default=256, help='Batch size.')
parser.add_argument('--num_factors', type=int, default=8, help='Embedding size.')
parser.add_argument('--regs', nargs='?', default='[0,0]', help="Regularization for user and item embeddings.")
parser.add_argument('--num_neg', type=int, default=4, help='Number of negative instances to pair with a positive instance.')
parser.add_argument('--lr', type=float, default=0.001, help='Learning rate.')
parser.add_argument('--learner', nargs='?', default='adam', help='Specify an optimizer: adagrad, adam, rmsprop, sgd')
parser.add_argument('--verbose', type=int, default=1, help='Show performance per X iterations')
parser.add_argument('--out', type=int, default=1, help='Whether to save the trained model.')
return parser.parse_args()
def get_model(num_users, num_items, latent_dim, regs=[0,0]):
user_input = Input(shape=(1,), dtype='int32', name='user_input')
item_input = Input(shape=(1,), dtype='int32', name='item_input')
MF_Embedding_User = Embedding(input_dim=num_users, output_dim=latent_dim, name='user_embedding',
embeddings_regularizer = l2(regs[0]), input_length=1)
MF_Embedding_Item = Embedding(input_dim=num_items, output_dim=latent_dim, name='item_embedding',
embeddings_regularizer = l2(regs[1]), input_length=1)
user_latent = Flatten()(MF_Embedding_User(user_input))
item_latent = Flatten()(MF_Embedding_Item(item_input))
predict_vector = Multiply()([user_latent, item_latent])
prediction = Dense(1, activation='sigmoid', kernel_initializer='lecun_uniform', name = 'prediction')(predict_vector)
model = Model(inputs=[user_input, item_input], outputs=prediction)
return model
def get_train_instances(train, num_negatives):
user_input, item_input, labels = [], [], []
num_items = train.shape[1]
for u, i in train.keys():
user_input.append(u)
item_input.append(i)
labels.append(1)
for t in range(num_negatives):
j = np.random.randint(num_items)
while (u,j) in train:
j = np.random.randint(num_items)
user_input.append(u)
item_input.append(j)
labels.append(0)
return user_input, item_input, labels
# In[2]: configure parameters
if(__name__ == '__main__'):
args = parse_args()
num_factors = args.num_factors
regs = eval(args.regs);print('regs', regs)
num_negatives = args.num_neg
learner = args.learner
learning_rate = args.lr
epochs = args.epochs
batch_size = args.batch_size
verbose = args.verbose
topK = 10
evaluation_threads = 1
print('GMF arguments: %s' % (args))
model_out_file = 'Pretrain/%s_GMF_%d_%d.h5' %(args.dataset, num_factors, time())
# In[3]: load datasets
t1 = time()
dataset = Dataset(args.path + args.dataset)
train, testRatings, testNegatives = dataset.trainMatrix, dataset.testRatings, dataset.testNegatives
num_users, num_items = train.shape
print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d"
%(time()-t1, num_users, num_items, train.nnz, len(testRatings)))
# In[4]: build model
model = get_model(num_users, num_items, num_factors, regs)
if learner.lower() == "adagrad":
model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "rmsprop":
model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "adam":
model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy')
else:
model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy')
#print(model.summary())
# In[4]: trian and test
# Init performance
t1 = time()
(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()
print('Init: HR = %.4f, NDCG = %.4f\t [%.1f s]' % (hr, ndcg, time()-t1))
# train model
best_hr, best_ndcg, best_iter = hr, ndcg, -1
for epoch in range(epochs):
t1 = time()
user_input, item_input, labels = get_train_instances(train, num_negatives)
hist = model.fit([np.array(user_input), np.array(item_input)], #input
np.array(labels), # labels
batch_size=batch_size, epochs=1, verbose=0, shuffle=True)
t2 = time()
if epoch %verbose == 0:
(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
hr, ndcg, loss = np.array(hits).mean(), np.array(ndcgs).mean(), hist.history['loss'][0]
print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]'
% (epoch, t2-t1, hr, ndcg, loss, time()-t2))
if hr > best_hr:
best_hr, best_ndcg, best_iter = hr, ndcg, epoch
if args.out > 0:
model.save_weights(model_out_file, overwrite=True)
print("End. Best Iteration %d: HR = %.4f, NDCG = %.4f. " %(best_iter, best_hr, best_ndcg))
if args.out > 0:
print("The best GMF model is saved to %s" %(model_out_file))
MLP code:
'''
Created on Aug 9, 2016
Keras Implementation of Multi-Layer Perceptron (GMF) recommender model in:
He Xiangnan et al. Neural Collaborative Filtering. In WWW 2017.
@author: Xiangnan He ([email protected])
'''
import numpy as np
import theano
import theano.tensor as T
import keras
from keras import backend as K
from keras import initializers as initializations
from keras.regularizers import l2
from keras.models import Sequential, Model
from keras.layers.core import Dense, Lambda, Activation
from keras.layers import Embedding, Input, Dense, Add, Reshape, Flatten, Dropout, Concatenate
from keras.constraints import maxnorm
from keras.optimizers import Adagrad, Adam, SGD, RMSprop
from evaluate import evaluate_model
from Dataset import Dataset
from time import time
import sys
import argparse
import multiprocessing as mp
#################### Arguments ####################
def parse_args():
parser = argparse.ArgumentParser(description="Run MLP.")
parser.add_argument('--path', nargs='?', default='Data/',
help='Input data path.')
parser.add_argument('--dataset', nargs='?', default='ml-1m',
help='Choose a dataset.')
parser.add_argument('--epochs', type=int, default=1,
help='Number of epochs.')
parser.add_argument('--batch_size', type=int, default=256,
help='Batch size.')
parser.add_argument('--layers', nargs='?', default='[64,32,16,8]',
help="Size of each layer. Note that the first layer is the concatenation of user and item embeddings. So layers[0]/2 is the embedding size.")
parser.add_argument('--reg_layers', nargs='?', default='[0,0,0,0]',
help="Regularization for each layer")
parser.add_argument('--num_neg', type=int, default=4,
help='Number of negative instances to pair with a positive instance.')
parser.add_argument('--lr', type=float, default=0.001,
help='Learning rate.')
parser.add_argument('--learner', nargs='?', default='adam',
help='Specify an optimizer: adagrad, adam, rmsprop, sgd')
parser.add_argument('--verbose', type=int, default=1,
help='Show performance per X iterations')
parser.add_argument('--out', type=int, default=1,
help='Whether to save the trained model.')
return parser.parse_args()
def get_model(num_users, num_items, layers = [20,10], reg_layers=[0,0]):
assert len(layers) == len(reg_layers)
num_layer = len(layers) #Number of layers in the MLP
# Input variables
user_input = Input(shape=(1,), dtype='int32', name = 'user_input')
item_input = Input(shape=(1,), dtype='int32', name = 'item_input')
MLP_Embedding_User = Embedding(input_dim = num_users, output_dim = int(layers[0]/2), name = 'user_embedding',
embeddings_regularizer = l2(reg_layers[0]), input_length=1)
MLP_Embedding_Item = Embedding(input_dim = num_items, output_dim = int(layers[0]/2), name = 'item_embedding',
embeddings_regularizer = l2(reg_layers[0]), input_length=1)
# Crucial to flatten an embedding vector!
user_latent = Flatten()(MLP_Embedding_User(user_input))
item_latent = Flatten()(MLP_Embedding_Item(item_input))
# The 0-th layer is the concatenation of embedding layers
vector = Concatenate(axis=-1)([user_latent, item_latent])
# MLP layers
for idx in range(1, num_layer):
layer = Dense(layers[idx], kernel_regularizer= l2(reg_layers[idx]), activation='relu', name = 'layer%d' %idx)
vector = layer(vector)
# Final prediction layer
prediction = Dense(1, activation='sigmoid', kernel_initializer='lecun_uniform', name = 'prediction')(vector)
model = Model(inputs=[user_input, item_input],
outputs=prediction)
return model
def get_train_instances(train, num_negatives):
user_input, item_input, labels = [],[],[]
num_items = train.shape[1]
for (u, i) in train.keys():
# positive instance
user_input.append(u)
item_input.append(i)
labels.append(1)
# negative instances
for t in range(num_negatives):
j = np.random.randint(num_items)
while (u, j) in train:
j = np.random.randint(num_items)
user_input.append(u)
item_input.append(j)
labels.append(0)
return user_input, item_input, labels
if __name__ == '__main__':
args = parse_args()
path = args.path
dataset = args.dataset
layers = eval(args.layers)
print(layers, type(layers[0]))
reg_layers = eval(args.reg_layers)
print(reg_layers, type(reg_layers[0]))
num_negatives = args.num_neg
learner = args.learner
learning_rate = args.lr
batch_size = args.batch_size
epochs = args.epochs
verbose = args.verbose
topK = 10
evaluation_threads = 1 #mp.cpu_count()
print("MLP arguments: %s " %(args))
model_out_file = 'Pretrain/%s_MLP_%s_%d.h5' %(args.dataset, args.layers, time())
# Loading data
t1 = time()
dataset = Dataset(args.path + args.dataset)
train, testRatings, testNegatives = dataset.trainMatrix, dataset.testRatings, dataset.testNegatives
num_users, num_items = train.shape
print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d"
%(time()-t1, num_users, num_items, train.nnz, len(testRatings)))
# Build model
model = get_model(num_users, num_items, layers, reg_layers)
if learner.lower() == "adagrad":
model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "rmsprop":
model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "adam":
model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy')
else:
model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy')
# Check Init performance
t1 = time()
(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()
print('Init: HR = %.4f, NDCG = %.4f [%.1f]' %(hr, ndcg, time()-t1))
# Train model
best_hr, best_ndcg, best_iter = hr, ndcg, -1
for epoch in range(epochs):
t1 = time()
# Generate training instances
user_input, item_input, labels = get_train_instances(train, num_negatives)
# Training
hist = model.fit([np.array(user_input), np.array(item_input)], #input
np.array(labels), # labels
batch_size=batch_size, epochs=1, verbose=0, shuffle=True)
t2 = time()
# Evaluation
if epoch %verbose == 0:
(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
hr, ndcg, loss = np.array(hits).mean(), np.array(ndcgs).mean(), hist.history['loss'][0]
print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]'
% (epoch, t2-t1, hr, ndcg, loss, time()-t2))
if hr > best_hr:
best_hr, best_ndcg, best_iter = hr, ndcg, epoch
if args.out > 0:
model.save_weights(model_out_file, overwrite=True)
print("End. Best Iteration %d: HR = %.4f, NDCG = %.4f. " %(best_iter, best_hr, best_ndcg))
if args.out > 0:
print("The best MLP model is saved to %s" %(model_out_file))
NeuMF code
# -*- coding: utf-8 -*-
"""
Created on Fri Jul 14 21:51:58 2023
@author: Lenovo
"""
'''
Created on Aug 9, 2016
Keras Implementation of Neural Matrix Factorization (NeuMF) recommender model in:
He Xiangnan et al. Neural Collaborative Filtering. In WWW 2017.
@author: Xiangnan He ([email protected])
'''
import numpy as np
import theano
import theano.tensor as T
import keras
from keras import backend as K
from keras.regularizers import l1, l2, l1_l2
from keras.models import Sequential, Model
from keras.layers.core import Dense, Lambda, Activation
from keras.layers import Embedding, Input, Dense, concatenate, Reshape, Multiply, Flatten, Dropout, Concatenate
from keras.optimizers import Adagrad, Adam, SGD, RMSprop
from evaluate import evaluate_model
from Dataset import Dataset
from time import time
import sys
import GMF, MLP
import argparse
# In[1]: parameters
#################### Arguments ####################
def parse_args():
parser = argparse.ArgumentParser(description="Run NeuMF.")
parser.add_argument('--path', nargs='?', default='Data/',
help='Input data path.')
parser.add_argument('--dataset', nargs='?', default='ml-1m',
help='Choose a dataset.')
parser.add_argument('--epochs', type=int, default=1,
help='Number of epochs.')
parser.add_argument('--batch_size', type=int, default=256,
help='Batch size.')
parser.add_argument('--num_factors', type=int, default=8,
help='Embedding size of MF model.')
parser.add_argument('--layers', nargs='?', default='[64,32,16,8]',
help="MLP layers. Note that the first layer is the concatenation of user and item embeddings. So layers[0]/2 is the embedding size.")
parser.add_argument('--reg_mf', type=float, default=0,
help='Regularization for MF embeddings.')
parser.add_argument('--reg_layers', nargs='?', default='[0,0,0,0]',
help="Regularization for each MLP layer. reg_layers[0] is the regularization for embeddings.")
parser.add_argument('--num_neg', type=int, default=4,
help='Number of negative instances to pair with a positive instance.')
parser.add_argument('--lr', type=float, default=0.001,
help='Learning rate.')
parser.add_argument('--learner', nargs='?', default='adam',
help='Specify an optimizer: adagrad, adam, rmsprop, sgd')
parser.add_argument('--verbose', type=int, default=1,
help='Show performance per X iterations')
parser.add_argument('--out', type=int, default=1,
help='Whether to save the trained model.')
parser.add_argument('--mf_pretrain', nargs='?', default='',
help='Specify the pretrain model file for MF part. If empty, no pretrain will be used')
parser.add_argument('--mlp_pretrain', nargs='?', default='',
help='Specify the pretrain model file for MLP part. If empty, no pretrain will be used')
return parser.parse_args()
# In[2]: model
def get_model(num_users, num_items, mf_dim=10, layers=[10], reg_layers=[0], reg_mf=0):
assert len(layers) == len(reg_layers)
num_layer = len(layers) #Number of layers in the MLP
# Input variables
user_input = Input(shape=(1,), dtype='int32', name = 'user_input')
item_input = Input(shape=(1,), dtype='int32', name = 'item_input')
# Embedding layer
MF_Embedding_User = Embedding(input_dim = num_users, output_dim = mf_dim, name = 'mf_embedding_user',
embeddings_regularizer = l2(reg_mf), input_length=1)
MF_Embedding_Item = Embedding(input_dim = num_items, output_dim = mf_dim, name = 'mf_embedding_item',
embeddings_regularizer = l2(reg_mf), input_length=1)
MLP_Embedding_User = Embedding(input_dim = num_users, output_dim = int(layers[0]/2), name = "mlp_embedding_user",
embeddings_regularizer = l2(reg_layers[0]), input_length=1)
MLP_Embedding_Item = Embedding(input_dim = num_items, output_dim = int(layers[0]/2), name = 'mlp_embedding_item',
embeddings_regularizer = l2(reg_layers[0]), input_length=1)
# MF part
mf_user_latent = Flatten()(MF_Embedding_User(user_input))
mf_item_latent = Flatten()(MF_Embedding_Item(item_input))
mf_vector = Multiply()([mf_user_latent, mf_item_latent]) # element-wise multiply
# MLP part
mlp_user_latent = Flatten()(MLP_Embedding_User(user_input))
mlp_item_latent = Flatten()(MLP_Embedding_Item(item_input))
mlp_vector = Concatenate(axis = 1)([mlp_user_latent, mlp_item_latent])
for idx in range(1, num_layer):
layer = Dense(layers[idx], kernel_regularizer= l2(reg_layers[idx]), activation='relu', name="layer%d" %idx)
mlp_vector = layer(mlp_vector)
# Concatenate MF and MLP parts
#mf_vector = Lambda(lambda x: x * alpha)(mf_vector)
#mlp_vector = Lambda(lambda x : x * (1-alpha))(mlp_vector)
predict_vector = Concatenate(axis = -1)([mf_vector, mlp_vector])
# Final prediction layer
prediction = Dense(1, activation='sigmoid', kernel_initializer='lecun_uniform', name = "prediction")(predict_vector)
model = Model(inputs=[user_input, item_input],
outputs=prediction)
return model
def load_pretrain_model(model, gmf_model, mlp_model, num_layers):
# MF embeddings
gmf_user_embeddings = gmf_model.get_layer('user_embedding').get_weights()
gmf_item_embeddings = gmf_model.get_layer('item_embedding').get_weights()
model.get_layer('mf_embedding_user').set_weights(gmf_user_embeddings)
model.get_layer('mf_embedding_item').set_weights(gmf_item_embeddings)
# MLP embeddings
mlp_user_embeddings = mlp_model.get_layer('user_embedding').get_weights()
mlp_item_embeddings = mlp_model.get_layer('item_embedding').get_weights()
model.get_layer('mlp_embedding_user').set_weights(mlp_user_embeddings)
model.get_layer('mlp_embedding_item').set_weights(mlp_item_embeddings)
# MLP layers
for i in range(1, num_layers):
mlp_layer_weights = mlp_model.get_layer('layer%d' %i).get_weights()
model.get_layer('layer%d' %i).set_weights(mlp_layer_weights)
# Prediction weights
gmf_prediction = gmf_model.get_layer('prediction').get_weights()
mlp_prediction = mlp_model.get_layer('prediction').get_weights()
new_weights = np.concatenate((gmf_prediction[0], mlp_prediction[0]), axis=0)
new_b = gmf_prediction[1] + mlp_prediction[1]
print('new_b.shape', new_b.shape)
model.get_layer('prediction').set_weights([0.5*new_weights, 0.5*new_b])
return model
def get_train_instances(train, num_negatives):
user_input, item_input, labels = [],[],[]
num_items = train.shape[1]
for (u, i) in train.keys():
# positive instance
user_input.append(u)
item_input.append(i)
labels.append(1)
# negative instances
for t in range(num_negatives):
j = np.random.randint(num_items)
while (u, j) in train:
j = np.random.randint(num_items)
user_input.append(u)
item_input.append(j)
labels.append(0)
return user_input, item_input, labels
# In[3]:
if __name__ == '__main__':
args = parse_args()
num_epochs = args.epochs
batch_size = args.batch_size
mf_dim = args.num_factors
layers = eval(args.layers)
reg_mf = args.reg_mf
reg_layers = eval(args.reg_layers)
num_negatives = args.num_neg
learning_rate = args.lr
learner = args.learner
verbose = args.verbose
mf_pretrain = args.mf_pretrain
mlp_pretrain = args.mlp_pretrain
topK = 10
evaluation_threads = 1#mp.cpu_count()
print("NeuMF arguments: %s " %(args))
model_out_file = 'Pretrain/%s_NeuMF_%d_%s_%d.h5' %(args.dataset, mf_dim, args.layers, time())
# Loading data
t1 = time()
dataset = Dataset(args.path + args.dataset)
train, testRatings, testNegatives = dataset.trainMatrix, dataset.testRatings, dataset.testNegatives
num_users, num_items = train.shape
print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d"
%(time()-t1, num_users, num_items, train.nnz, len(testRatings)))
# Build model
model = get_model(num_users, num_items, mf_dim, layers, reg_layers, reg_mf)
if learner.lower() == "adagrad":
model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "rmsprop":
model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "adam":
model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy')
else:
model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy')
# Load pretrain model
if mf_pretrain != '' and mlp_pretrain != '':
gmf_model = GMF.get_model(num_users,num_items,mf_dim)
gmf_model.load_weights(mf_pretrain)
mlp_model = MLP.get_model(num_users,num_items, layers, reg_layers)
mlp_model.load_weights(mlp_pretrain)
model = load_pretrain_model(model, gmf_model, mlp_model, len(layers))
print("Load pretrained GMF (%s) and MLP (%s) models done. " %(mf_pretrain, mlp_pretrain))
# Init performance
(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()
print('Init: HR = %.4f, NDCG = %.4f' % (hr, ndcg))
best_hr, best_ndcg, best_iter = hr, ndcg, -1
if args.out > 0:
model.save_weights(model_out_file, overwrite=True)
# Training model
for epoch in range(num_epochs):
t1 = time()
# Generate training instances
user_input, item_input, labels = get_train_instances(train, num_negatives)
# Training
hist = model.fit([np.array(user_input), np.array(item_input)], #input
np.array(labels), # labels
batch_size=batch_size, epochs=1, verbose=0, shuffle=True)
t2 = time()
# Evaluation
if epoch %verbose == 0:
(hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
hr, ndcg, loss = np.array(hits).mean(), np.array(ndcgs).mean(), hist.history['loss'][0]
print('Iteration %d [%.1f s]: HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]'
% (epoch, t2-t1, hr, ndcg, loss, time()-t2))
if hr > best_hr:
best_hr, best_ndcg, best_iter = hr, ndcg, epoch
if args.out > 0:
model.save_weights(model_out_file, overwrite=True)
print("End. Best Iteration %d: HR = %.4f, NDCG = %.4f. " %(best_iter, best_hr, best_ndcg))
if args.out > 0:
print("The best NeuMF model is saved to %s" %(model_out_file))
The complete code has been uploaded to GitHub (Xian-Gang/NCF (github.com)) and the resource area of csdn.
1、GMF
2、MLP
3、NeuMF
At the same time, the corresponding pre-training file will be generated: