Files
2026-07-13 12:40:42 +08:00

390 lines
13 KiB
Python
Executable File

# 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
from paddle import nn
class DNNLayer(nn.Layer):
def __init__(
self,
sparse_feature_number,
sparse_feature_dim,
dense_feature_dim,
num_field,
layer_sizes,
sync_mode=None,
):
super().__init__()
self.sync_mode = sync_mode
self.sparse_feature_number = sparse_feature_number
self.sparse_feature_dim = sparse_feature_dim
self.dense_feature_dim = dense_feature_dim
self.num_field = num_field
self.layer_sizes = layer_sizes
self.embedding = paddle.nn.Embedding(
self.sparse_feature_number,
self.sparse_feature_dim,
sparse=True,
weight_attr=paddle.ParamAttr(
name="SparseFeatFactors",
initializer=paddle.nn.initializer.Uniform(),
),
)
sizes = [
sparse_feature_dim * num_field + dense_feature_dim,
*self.layer_sizes,
2,
]
acts = ["relu" for _ in range(len(self.layer_sizes))] + [None]
self._mlp_layers = []
for i in range(len(layer_sizes) + 1):
linear = paddle.nn.Linear(
in_features=sizes[i],
out_features=sizes[i + 1],
weight_attr=paddle.ParamAttr(
initializer=paddle.nn.initializer.Normal(
std=1.0 / math.sqrt(sizes[i])
)
),
)
self.add_sublayer(f'linear_{i}', linear)
self._mlp_layers.append(linear)
if acts[i] == 'relu':
act = paddle.nn.ReLU()
self.add_sublayer(f'act_{i}', act)
self._mlp_layers.append(act)
def forward(self, sparse_inputs, dense_inputs):
sparse_embs = []
for s_input in sparse_inputs:
if self.sync_mode == "gpubox":
emb = paddle.static.nn.sparse_embedding(
input=s_input,
size=[self.sparse_feature_number, self.sparse_feature_dim],
param_attr=paddle.ParamAttr(name="embedding"),
)
else:
emb = self.embedding(s_input)
emb = paddle.reshape(emb, shape=[-1, self.sparse_feature_dim])
# emb.stop_gradient = True
sparse_embs.append(emb)
y_dnn = paddle.concat(x=[*sparse_embs, dense_inputs], axis=1)
if self.sync_mode == 'heter':
with paddle.base.device_guard('gpu'):
for n_layer in self._mlp_layers:
y_dnn = n_layer(y_dnn)
else:
for n_layer in self._mlp_layers:
y_dnn = n_layer(y_dnn)
return y_dnn
class FlDNNLayer(nn.Layer):
def __init__(
self,
sparse_feature_number,
sparse_feature_dim,
dense_feature_dim,
sparse_number,
sync_mode=None,
):
super().__init__()
self.PART_A_DEVICE_FlAG = 'gpu:0'
self.PART_A_JOINT_OP_DEVICE_FlAG = 'gpu:2'
self.PART_B_DEVICE_FlAG = 'gpu:1'
self.PART_B_JOINT_OP_DEVICE_FlAG = 'gpu:3'
self.sync_mode = sync_mode
self.sparse_feature_number = sparse_feature_number
self.sparse_feature_dim = sparse_feature_dim
self.slot_num = sparse_number
self.dense_feature_dim = dense_feature_dim
layer_sizes_a = [
self.slot_num * self.sparse_feature_dim,
5,
7,
] # for test
layer_sizes_b = [self.dense_feature_dim, 6, 7]
layer_sizes_top = [7, 2]
self.embedding = paddle.nn.Embedding(
self.sparse_feature_number,
self.sparse_feature_dim,
sparse=True,
weight_attr=paddle.ParamAttr(
name="SparseFeatFactors",
initializer=paddle.nn.initializer.Uniform(),
),
)
# part_a fc
acts = ["relu" for _ in range(len(layer_sizes_a))]
self._mlp_layers_a = []
for i in range(len(layer_sizes_a) - 1):
linear = paddle.nn.Linear(
in_features=layer_sizes_a[i],
out_features=layer_sizes_a[i + 1],
weight_attr=paddle.ParamAttr(
initializer=paddle.nn.initializer.Normal(
std=1.0 / math.sqrt(layer_sizes_a[i])
)
),
)
self.add_sublayer(f'linear_{i}', linear)
self._mlp_layers_a.append(linear)
act = paddle.nn.ReLU()
self.add_sublayer(f'act_{i}', act)
self._mlp_layers_a.append(act)
# part_b fc
acts = ["relu" for _ in range(len(layer_sizes_b))]
self._mlp_layers_b = []
for i in range(len(layer_sizes_b) - 1):
linear = paddle.nn.Linear(
in_features=layer_sizes_b[i],
out_features=layer_sizes_b[i + 1],
weight_attr=paddle.ParamAttr(
initializer=paddle.nn.initializer.Normal(
std=1.0 / math.sqrt(layer_sizes_b[i])
)
),
)
self.add_sublayer(f'linear_{i}', linear)
self._mlp_layers_b.append(linear)
act = paddle.nn.ReLU()
self.add_sublayer(f'act_{i}', act)
self._mlp_layers_b.append(act)
# top fc
acts = ["relu" for _ in range(len(layer_sizes_top))]
self._mlp_layers_top = []
for i in range(len(layer_sizes_top) - 1):
linear = paddle.nn.Linear(
in_features=layer_sizes_top[i],
out_features=layer_sizes_top[i + 1],
weight_attr=paddle.ParamAttr(
initializer=paddle.nn.initializer.Normal(
std=1.0 / math.sqrt(layer_sizes_top[i])
)
),
)
self.add_sublayer(f'linear_{i}', linear)
self._mlp_layers_top.append(linear)
act = paddle.nn.ReLU()
self.add_sublayer(f'act_{i}', act)
self._mlp_layers_top.append(act)
def bottom_a_layer(self, sparse_inputs):
with paddle.base.device_guard(self.PART_A_DEVICE_FlAG):
sparse_embs = []
for s_input in sparse_inputs:
emb = self.embedding(s_input)
emb = paddle.reshape(emb, shape=[-1, self.sparse_feature_dim])
sparse_embs.append(emb)
y = paddle.concat(x=sparse_embs, axis=1)
y = self._mlp_layers_a[0](y)
y = self._mlp_layers_a[1](y)
y = self._mlp_layers_a[2](y)
with paddle.base.device_guard(
self.PART_A_JOINT_OP_DEVICE_FlAG
): # joint point
bottom_a = self._mlp_layers_a[3](y)
return bottom_a
def bottom_b_layer(self, dense_inputs):
with paddle.base.device_guard(self.PART_B_DEVICE_FlAG):
y = self._mlp_layers_b[0](dense_inputs)
y = self._mlp_layers_b[1](y)
y = self._mlp_layers_b[2](y)
bottom_b = self._mlp_layers_b[3](y)
return bottom_b
def interactive_layer(self, bottom_a, bottom_b):
with paddle.base.device_guard(
self.PART_B_JOINT_OP_DEVICE_FlAG
): # joint point
interactive = paddle.add(bottom_a, bottom_b)
return interactive
def top_layer(self, interactive, label_input):
with paddle.base.device_guard(self.PART_B_DEVICE_FlAG):
y = self._mlp_layers_top[0](interactive)
y_top = self._mlp_layers_top[1](y)
predict_2d = paddle.nn.functional.softmax(y_top)
(
auc,
batch_auc,
[
self.batch_stat_pos,
self.batch_stat_neg,
self.stat_pos,
self.stat_neg,
],
) = paddle.static.auc(
input=predict_2d,
label=label_input,
num_thresholds=2**12,
slide_steps=20,
)
cost = paddle.nn.functional.cross_entropy(
input=y_top, label=label_input
)
avg_cost = paddle.mean(x=cost)
return auc, avg_cost
def forward(self, sparse_inputs, dense_inputs, label_input):
bottom_a = self.bottom_a_layer(sparse_inputs)
bottom_b = self.bottom_b_layer(dense_inputs)
interactive = self.interactive_layer(bottom_a, bottom_b)
auc, avg_cost = self.top_layer(interactive, label_input)
return auc, avg_cost
class StaticModel:
def __init__(self, config):
self.cost = None
self.infer_target_var = None
self.config = config
self._init_hyper_parameters()
self.sync_mode = config.get("runner.sync_mode")
def _init_hyper_parameters(self):
self.is_distributed = False
self.distributed_embedding = False
if self.config.get("hyper_parameters.distributed_embedding", 0) == 1:
self.distributed_embedding = True
self.sparse_feature_number = self.config.get(
"hyper_parameters.sparse_feature_number"
)
self.sparse_feature_dim = self.config.get(
"hyper_parameters.sparse_feature_dim"
)
self.sparse_inputs_slots = self.config.get(
"hyper_parameters.sparse_inputs_slots"
)
self.dense_input_dim = self.config.get(
"hyper_parameters.dense_input_dim"
)
self.learning_rate = self.config.get(
"hyper_parameters.optimizer.learning_rate"
)
self.fc_sizes = self.config.get("hyper_parameters.fc_sizes")
def create_feeds(self, is_infer=False):
dense_input = paddle.static.data(
name="dense_input",
shape=[None, self.dense_input_dim],
dtype="float32",
)
sparse_input_ids = [
paddle.static.data(name=str(i), shape=[None, 1], dtype="int64")
for i in range(1, self.sparse_inputs_slots)
]
label = paddle.static.data(name="label", shape=[None, 1], dtype="int64")
feeds_list = [label, *sparse_input_ids, dense_input]
return feeds_list
def net(self, input, is_infer=False):
self.label_input = input[0]
self.sparse_inputs = input[1 : self.sparse_inputs_slots]
self.dense_input = input[-1]
sparse_number = self.sparse_inputs_slots - 1
dnn_model = DNNLayer(
self.sparse_feature_number,
self.sparse_feature_dim,
self.dense_input_dim,
sparse_number,
self.fc_sizes,
sync_mode=self.sync_mode,
)
raw_predict_2d = dnn_model.forward(self.sparse_inputs, self.dense_input)
predict_2d = paddle.nn.functional.softmax(raw_predict_2d)
self.predict = predict_2d
(
auc,
batch_auc,
[
self.batch_stat_pos,
self.batch_stat_neg,
self.stat_pos,
self.stat_neg,
],
) = paddle.static.auc(
input=self.predict,
label=self.label_input,
num_thresholds=2**12,
slide_steps=20,
)
self.inference_target_var = auc
if is_infer:
fetch_dict = {'auc': auc}
return fetch_dict
cost = paddle.nn.functional.cross_entropy(
input=raw_predict_2d, label=self.label_input
)
avg_cost = paddle.mean(x=cost)
self._cost = avg_cost
fetch_dict = {'cost': avg_cost, 'auc': auc}
return fetch_dict
def fl_net(self, input, is_infer=False):
self.label_input = input[0]
self.sparse_inputs = input[1 : self.sparse_inputs_slots]
self.dense_input = input[-1]
self.sparse_number = self.sparse_inputs_slots - 1
fl_dnn_model = FlDNNLayer(
self.sparse_feature_number,
self.sparse_feature_dim,
self.dense_input_dim,
self.sparse_number,
sync_mode=self.sync_mode,
)
auc, avg_cost = fl_dnn_model.forward(
self.sparse_inputs, self.dense_input, self.label_input
)
fetch_dict = {'cost': avg_cost, 'auc': auc}
self._cost = avg_cost
return fetch_dict