# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import math import paddle.fluid as fluid from paddlerec.core.utils import envs from paddlerec.core.model import ModelBase import numpy as np class Model(ModelBase): def __init__(self, config): ModelBase.__init__(self, config) def _init_hyper_parameters(self): self.num_users = envs.get_global_env("hyper_parameters.num_users") self.num_items = envs.get_global_env("hyper_parameters.num_items") self.latent_dim = envs.get_global_env("hyper_parameters.latent_dim") self.layers = envs.get_global_env("hyper_parameters.fc_layers") def input_data(self, is_infer=False, **kwargs): user_input = fluid.data( name="user_input", shape=[-1, 1], dtype="int64", lod_level=0) item_input = fluid.data( name="item_input", shape=[-1, 1], dtype="int64", lod_level=0) label = fluid.data( name="label", shape=[-1, 1], dtype="int64", lod_level=0) if is_infer: inputs = [user_input] + [item_input] else: inputs = [user_input] + [item_input] + [label] return inputs def net(self, inputs, is_infer=False): num_layer = len(self.layers) # Number of layers in the MLP MF_Embedding_User = fluid.embedding( input=inputs[0], size=[self.num_users, self.latent_dim], param_attr=fluid.initializer.Normal( loc=0.0, scale=0.01), is_sparse=True) MF_Embedding_Item = fluid.embedding( input=inputs[1], size=[self.num_items, self.latent_dim], param_attr=fluid.initializer.Normal( loc=0.0, scale=0.01), is_sparse=True) MLP_Embedding_User = fluid.embedding( input=inputs[0], size=[self.num_users, int(self.layers[0] / 2)], param_attr=fluid.initializer.Normal( loc=0.0, scale=0.01), is_sparse=True) MLP_Embedding_Item = fluid.embedding( input=inputs[1], size=[self.num_items, int(self.layers[0] / 2)], param_attr=fluid.initializer.Normal( loc=0.0, scale=0.01), is_sparse=True) # MF part mf_user_latent = fluid.layers.flatten(x=MF_Embedding_User, axis=1) mf_item_latent = fluid.layers.flatten(x=MF_Embedding_Item, axis=1) mf_vector = fluid.layers.elementwise_mul(mf_user_latent, mf_item_latent) # MLP part # The 0-th layer is the concatenation of embedding layers mlp_user_latent = fluid.layers.flatten(x=MLP_Embedding_User, axis=1) mlp_item_latent = fluid.layers.flatten(x=MLP_Embedding_Item, axis=1) mlp_vector = fluid.layers.concat( input=[mlp_user_latent, mlp_item_latent], axis=-1) for i in range(1, num_layer): mlp_vector = fluid.layers.fc( input=mlp_vector, size=self.layers[i], act='relu', param_attr=fluid.ParamAttr( initializer=fluid.initializer.TruncatedNormal( loc=0.0, scale=1.0 / math.sqrt(mlp_vector.shape[1])), regularizer=fluid.regularizer.L2DecayRegularizer( regularization_coeff=1e-4)), name='layer_' + str(i)) # Concatenate MF and MLP parts predict_vector = fluid.layers.concat( input=[mf_vector, mlp_vector], axis=-1) # Final prediction layer prediction = fluid.layers.fc( input=predict_vector, size=1, act='sigmoid', param_attr=fluid.initializer.MSRAInitializer(uniform=True), name='prediction') if is_infer: self._infer_results["prediction"] = prediction return cost = fluid.layers.log_loss( input=prediction, label=fluid.layers.cast( x=inputs[2], dtype='float32')) avg_cost = fluid.layers.mean(cost) self._cost = avg_cost self._metrics["cost"] = avg_cost