model.py 6.5 KB
Newer Older
T
tangwei 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#   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.

T
tangwei 已提交
15 16
from collections import OrderedDict

17 18
import paddle.fluid as fluid

19 20
from paddlerec.core.utils import envs
from paddlerec.core.model import Model as ModelBase
T
tangwei 已提交
21

22 23 24 25

class Model(ModelBase):
    def __init__(self, config):
        ModelBase.__init__(self, config)
T
for mat  
tangwei 已提交
26

27
    def init_network(self):
T
tangwei 已提交
28 29 30 31 32 33 34 35 36 37 38 39
        self.cross_num = envs.get_global_env("hyper_parameters.cross_num",
                                             None, self._namespace)
        self.dnn_hidden_units = envs.get_global_env(
            "hyper_parameters.dnn_hidden_units", None, self._namespace)
        self.l2_reg_cross = envs.get_global_env(
            "hyper_parameters.l2_reg_cross", None, self._namespace)
        self.dnn_use_bn = envs.get_global_env("hyper_parameters.dnn_use_bn",
                                              None, self._namespace)
        self.clip_by_norm = envs.get_global_env(
            "hyper_parameters.clip_by_norm", None, self._namespace)
        cat_feat_num = envs.get_global_env("hyper_parameters.cat_feat_num",
                                           None, self._namespace)
X
xujiaqi01 已提交
40 41 42 43 44

        self.sparse_inputs = self._sparse_data_var[1:]
        self.dense_inputs = self._dense_data_var
        self.target_input = self._sparse_data_var[0]

45 46 47 48 49 50 51
        cat_feat_dims_dict = OrderedDict()
        for line in open(cat_feat_num):
            spls = line.strip().split()
            assert len(spls) == 2
            cat_feat_dims_dict[spls[0]] = int(spls[1])
        self.cat_feat_dims_dict = cat_feat_dims_dict if cat_feat_dims_dict else OrderedDict(
        )
T
tangwei 已提交
52 53
        self.is_sparse = envs.get_global_env("hyper_parameters.is_sparse",
                                             None, self._namespace)
54

X
xujiaqi01 已提交
55 56
        self.dense_feat_names = [i.name for i in self.dense_inputs]
        self.sparse_feat_names = [i.name for i in self.sparse_inputs]
57 58 59 60 61 62 63 64

        # {feat_name: dims}
        self.feat_dims_dict = OrderedDict(
            [(feat_name, 1) for feat_name in self.dense_feat_names])
        self.feat_dims_dict.update(self.cat_feat_dims_dict)

        self.net_input = None
        self.loss = None
T
tangwei 已提交
65

X
xujiaqi01 已提交
66
    def _create_embedding_input(self):
67
        # sparse embedding
X
xujiaqi01 已提交
68 69
        sparse_emb_dict = OrderedDict()
        for var in self.sparse_inputs:
T
tangwei 已提交
70 71 72 73 74 75 76 77
            sparse_emb_dict[var.name] = fluid.embedding(
                input=var,
                size=[
                    self.feat_dims_dict[var.name] + 1,
                    6 * int(pow(self.feat_dims_dict[var.name], 0.25))
                ],
                is_sparse=self.is_sparse)

78
        # combine dense and sparse_emb
X
xujiaqi01 已提交
79
        dense_input_list = self.dense_inputs
80 81 82 83 84 85 86 87 88 89 90 91
        sparse_emb_list = list(sparse_emb_dict.values())

        sparse_input = fluid.layers.concat(sparse_emb_list, axis=-1)
        sparse_input = fluid.layers.flatten(sparse_input)

        dense_input = fluid.layers.concat(dense_input_list, axis=-1)
        dense_input = fluid.layers.flatten(dense_input)
        dense_input = fluid.layers.cast(dense_input, 'float32')

        net_input = fluid.layers.concat([dense_input, sparse_input], axis=-1)

        return net_input
T
for mat  
tangwei 已提交
92

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
    def _deep_net(self, input, hidden_units, use_bn=False, is_test=False):
        for units in hidden_units:
            input = fluid.layers.fc(input=input, size=units)
            if use_bn:
                input = fluid.layers.batch_norm(input, is_test=is_test)
            input = fluid.layers.relu(input)
        return input

    def _cross_layer(self, x0, x, prefix):
        input_dim = x0.shape[-1]
        w = fluid.layers.create_parameter(
            [input_dim], dtype='float32', name=prefix + "_w")
        b = fluid.layers.create_parameter(
            [input_dim], dtype='float32', name=prefix + "_b")
        xw = fluid.layers.reduce_sum(x * w, dim=1, keep_dim=True)  # (N, 1)
        return x0 * xw + b + x, w
T
for mat  
tangwei 已提交
109

110 111 112 113 114 115 116 117 118 119
    def _cross_net(self, input, num_corss_layers):
        x = x0 = input
        l2_reg_cross_list = []
        for i in range(num_corss_layers):
            x, w = self._cross_layer(x0, x, "cross_layer_{}".format(i))
            l2_reg_cross_list.append(self._l2_loss(w))
        l2_reg_cross_loss = fluid.layers.reduce_sum(
            fluid.layers.concat(
                l2_reg_cross_list, axis=-1))
        return x, l2_reg_cross_loss
T
for mat  
tangwei 已提交
120

121 122
    def _l2_loss(self, w):
        return fluid.layers.reduce_sum(fluid.layers.square(w))
T
for mat  
tangwei 已提交
123

124
    def train_net(self):
X
fix  
xujiaqi01 已提交
125
        self.model._init_slots()
126
        self.init_network()
T
tangwei 已提交
127

X
xujiaqi01 已提交
128
        self.net_input = self._create_embedding_input()
T
tangwei 已提交
129 130 131

        deep_out = self._deep_net(self.net_input, self.dnn_hidden_units,
                                  self.dnn_use_bn, False)
132 133

        cross_out, l2_reg_cross_loss = self._cross_net(self.net_input,
T
for mat  
tangwei 已提交
134 135
                                                       self.cross_num)

136 137 138 139 140 141 142 143 144 145 146 147
        last_out = fluid.layers.concat([deep_out, cross_out], axis=-1)
        logit = fluid.layers.fc(last_out, 1)

        self.prob = fluid.layers.sigmoid(logit)

        # auc
        prob_2d = fluid.layers.concat([1 - self.prob, self.prob], 1)
        label_int = fluid.layers.cast(self.target_input, 'int64')
        auc_var, batch_auc_var, self.auc_states = fluid.layers.auc(
            input=prob_2d, label=label_int, slide_steps=0)
        self._metrics["AUC"] = auc_var
        self._metrics["BATCH_AUC"] = batch_auc_var
T
tangwei 已提交
148

149
        # logloss
T
tangwei 已提交
150 151 152
        logloss = fluid.layers.log_loss(
            self.prob, fluid.layers.cast(
                self.target_input, dtype='float32'))
153 154 155 156 157 158 159 160
        self.avg_logloss = fluid.layers.reduce_mean(logloss)

        # reg_coeff * l2_reg_cross
        l2_reg_cross_loss = self.l2_reg_cross * l2_reg_cross_loss
        self.loss = self.avg_logloss + l2_reg_cross_loss
        self._cost = self.loss

    def optimizer(self):
T
tangwei 已提交
161 162
        learning_rate = envs.get_global_env("hyper_parameters.learning_rate",
                                            None, self._namespace)
163 164 165 166
        optimizer = fluid.optimizer.Adam(learning_rate, lazy_mode=True)
        return optimizer

    def infer_net(self, parameter_list):
X
fix  
xujiaqi01 已提交
167
        self.model._init_slots()
168
        self.deepfm_net()