From ab595ec4e3d4b8cef978538738fee9770cfc0827 Mon Sep 17 00:00:00 2001 From: Qinlong Wang Date: Sun, 20 Dec 2020 21:45:49 +0800 Subject: [PATCH 1/3] Add a transform_fn into DenseFeat (#309) * Add transform_fn for DenseFeat --- deepctr/feature_column.py | 16 +++++++++++++--- deepctr/inputs.py | 8 ++++++-- tests/utils.py | 10 +++++++++- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/deepctr/feature_column.py b/deepctr/feature_column.py index 64cd4dd3..8e198f38 100644 --- a/deepctr/feature_column.py +++ b/deepctr/feature_column.py @@ -87,11 +87,21 @@ def __hash__(self): return self.name.__hash__() -class DenseFeat(namedtuple('DenseFeat', ['name', 'dimension', 'dtype'])): +class DenseFeat(namedtuple('DenseFeat', ['name', 'dimension', 'dtype', 'transform_fn'])): + """ Dense feature + Args: + name: feature name, + dimension: dimension of the feature, default = 1. + dtype: dtype of the feature, default="float32". + transform_fn: If not None, a function that can be used to transfrom + values of the feature. the function takes the input Tensor as its + argument, and returns the output Tensor. + (e.g. lambda x: (x - 3.0) / 4.2). + """ __slots__ = () - def __new__(cls, name, dimension=1, dtype="float32"): - return super(DenseFeat, cls).__new__(cls, name, dimension, dtype) + def __new__(cls, name, dimension=1, dtype="float32", transform_fn=None): + return super(DenseFeat, cls).__new__(cls, name, dimension, dtype, transform_fn) def __hash__(self): return self.name.__hash__() diff --git a/deepctr/inputs.py b/deepctr/inputs.py index 44e17906..92019a85 100644 --- a/deepctr/inputs.py +++ b/deepctr/inputs.py @@ -9,7 +9,7 @@ from collections import defaultdict from itertools import chain -from tensorflow.python.keras.layers import Embedding +from tensorflow.python.keras.layers import Embedding, Lambda from tensorflow.python.keras.regularizers import l2 from .layers.sequence import SequencePoolingLayer, WeightedSequenceLayer @@ -138,7 +138,11 @@ def get_dense_input(features, feature_columns): filter(lambda x: isinstance(x, fc_lib.DenseFeat), feature_columns)) if feature_columns else [] dense_input_list = [] for fc in dense_feature_columns: - dense_input_list.append(features[fc.name]) + if fc.transform_fn is None: + dense_input_list.append(features[fc.name]) + else: + transform_result = Lambda(fc.transform_fn)(features[fc.name]) + dense_input_list.append(transform_result) return dense_input_list diff --git a/tests/utils.py b/tests/utils.py index c5fa96e2..db570297 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -52,7 +52,15 @@ def get_test_data(sample_size=1000, embedding_size=4, sparse_feature_num=1, dens SparseFeat(prefix + 'sparse_feature_' + str(i), dim, embedding_size, use_hash=hash_flag, dtype=tf.int32,group_name=group_name)) for i in range(dense_feature_num): - feature_columns.append(DenseFeat(prefix + 'dense_feature_' + str(i), 1, dtype=tf.float32)) + transform_fn = lambda x: (x - 0.0)/ 1.0 + feature_columns.append( + DenseFeat( + prefix + 'dense_feature_' + str(i), + 1, + dtype=tf.float32, + transform_fn=transform_fn + ) + ) for i, mode in enumerate(sequence_feature): dim = np.random.randint(1, 10) maxlen = np.random.randint(1, 10) From f937aea4cce154ac0f54f7d65c4ff812152c3118 Mon Sep 17 00:00:00 2001 From: Jason Zan <631763140@qq.com> Date: Sun, 3 Jan 2021 20:06:33 +0800 Subject: [PATCH 2/3] add DCN-M and DCN-Mix (#316) - Add DCN-M and DCN-Mix - update test files for DCN and DCNMix - set `h5py==2.10.0` for pytest - simplify `interaction.py ` - modify BilinearInteraction in FiBiNET --- deepctr/layers/__init__.py | 3 +- deepctr/layers/interaction.py | 210 +++++++++++++++++++++++++++++----- deepctr/models/__init__.py | 3 +- deepctr/models/dcn.py | 14 ++- deepctr/models/dcnmix.py | 83 ++++++++++++++ setup.py | 2 +- tests/models/DCNMix_test.py | 26 +++++ tests/models/DCN_test.py | 10 +- 8 files changed, 314 insertions(+), 37 deletions(-) create mode 100644 deepctr/models/dcnmix.py create mode 100644 tests/models/DCNMix_test.py diff --git a/deepctr/layers/__init__.py b/deepctr/layers/__init__.py index 89cc60fb..9617a8f8 100644 --- a/deepctr/layers/__init__.py +++ b/deepctr/layers/__init__.py @@ -2,7 +2,7 @@ from .activation import Dice from .core import DNN, LocalActivationUnit, PredictionLayer -from .interaction import (CIN, FM, AFMLayer, BiInteractionPooling, CrossNet, +from .interaction import (CIN, FM, AFMLayer, BiInteractionPooling, CrossNet, CrossNetMix, InnerProductLayer, InteractingLayer, OutterProductLayer, FGCNNLayer, SENETLayer, BilinearInteraction, FieldWiseBiInteraction, FwFMLayer) @@ -20,6 +20,7 @@ 'FM': FM, 'AFMLayer': AFMLayer, 'CrossNet': CrossNet, + 'CrossNetMix': CrossNetMix, 'BiInteractionPooling': BiInteractionPooling, 'LocalActivationUnit': LocalActivationUnit, 'Dice': Dice, diff --git a/deepctr/layers/interaction.py b/deepctr/layers/interaction.py index c4a277d1..92107a11 100644 --- a/deepctr/layers/interaction.py +++ b/deepctr/layers/interaction.py @@ -142,7 +142,8 @@ def get_config(self, ): config = {'attention_factor': self.attention_factor, 'l2_reg_w': self.l2_reg_w, 'dropout_rate': self.dropout_rate, 'seed': self.seed} base_config = super(AFMLayer, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class BiInteractionPooling(Layer): @@ -322,7 +323,8 @@ def get_config(self, ): config = {'layer_size': self.layer_size, 'split_half': self.split_half, 'activation': self.activation, 'seed': self.seed} base_config = super(CIN, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class CrossNet(Layer): @@ -340,16 +342,20 @@ class CrossNet(Layer): - **l2_reg**: float between 0 and 1. L2 regularizer strength applied to the kernel weights matrix + - **parameterization**: string, ``"vector"`` or ``"matrix"`` , way to parameterize the cross network. + - **seed**: A Python integer to use as random seed. References - [Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[C]//Proceedings of the ADKDD'17. ACM, 2017: 12.](https://arxiv.org/abs/1708.05123) """ - def __init__(self, layer_num=2, l2_reg=0, seed=1024, **kwargs): + def __init__(self, layer_num=2, parameterization='vector', l2_reg=0, seed=1024, **kwargs): self.layer_num = layer_num + self.parameterization = parameterization self.l2_reg = l2_reg self.seed = seed + print('CrossNet parameterization:', self.parameterization) super(CrossNet, self).__init__(**kwargs) def build(self, input_shape): @@ -359,12 +365,22 @@ def build(self, input_shape): "Unexpected inputs dimensions %d, expect to be 2 dimensions" % (len(input_shape),)) dim = int(input_shape[-1]) - self.kernels = [self.add_weight(name='kernel' + str(i), - shape=(dim, 1), - initializer=glorot_normal( - seed=self.seed), - regularizer=l2(self.l2_reg), - trainable=True) for i in range(self.layer_num)] + if self.parameterization == 'vector': + self.kernels = [self.add_weight(name='kernel' + str(i), + shape=(dim, 1), + initializer=glorot_normal( + seed=self.seed), + regularizer=l2(self.l2_reg), + trainable=True) for i in range(self.layer_num)] + elif self.parameterization == 'matrix': + self.kernels = [self.add_weight(name='kernel' + str(i), + shape=(dim, dim), + initializer=glorot_normal( + seed=self.seed), + regularizer=l2(self.l2_reg), + trainable=True) for i in range(self.layer_num)] + else: # error + raise ValueError("parameterization should be 'vector' or 'matrix'") self.bias = [self.add_weight(name='bias' + str(i), shape=(dim, 1), initializer=Zeros(), @@ -380,18 +396,152 @@ def call(self, inputs, **kwargs): x_0 = tf.expand_dims(inputs, axis=2) x_l = x_0 for i in range(self.layer_num): - xl_w = tf.tensordot(x_l, self.kernels[i], axes=(1, 0)) - dot_ = tf.matmul(x_0, xl_w) - x_l = dot_ + self.bias[i] + x_l + if self.parameterization == 'vector': + xl_w = tf.tensordot(x_l, self.kernels[i], axes=(1, 0)) + dot_ = tf.matmul(x_0, xl_w) + x_l = dot_ + self.bias[i] + elif self.parameterization == 'matrix': + dot_ = tf.einsum('ij,bjk->bik', self.kernels[i], x_l) # W * xi (bs, dim, 1) + dot_ = dot_ + self.bias[i] # W * xi + b + dot_ = x_0 * dot_ # x0 · (W * xi + b) Hadamard-product + else: # error + print("parameterization should be 'vector' or 'matrix'") + x_l = dot_ + x_l x_l = tf.squeeze(x_l, axis=2) return x_l def get_config(self, ): - config = {'layer_num': self.layer_num, + config = {'layer_num': self.layer_num, 'parameterization': self.parameterization, 'l2_reg': self.l2_reg, 'seed': self.seed} base_config = super(CrossNet, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config + + def compute_output_shape(self, input_shape): + return input_shape + + +class CrossNetMix(Layer): + """The Cross Network part of DCN-Mix model, which improves DCN-M by: + 1 add MOE to learn feature interactions in different subspaces + 2 add nonlinear transformations in low-dimensional space + + Input shape + - 2D tensor with shape: ``(batch_size, units)``. + + Output shape + - 2D tensor with shape: ``(batch_size, units)``. + + Arguments + - **low_rank** : Positive integer, dimensionality of low-rank sapce. + + - **num_experts** : Positive integer, number of experts. + + - **layer_num**: Positive integer, the cross layer number + + - **l2_reg**: float between 0 and 1. L2 regularizer strength applied to the kernel weights matrix + + - **seed**: A Python integer to use as random seed. + + References + - [Wang R, Shivanna R, Cheng D Z, et al. DCN-M: Improved Deep & Cross Network for Feature Cross Learning in Web-scale Learning to Rank Systems[J]. 2020.](https://arxiv.org/abs/2008.13535) + """ + + def __init__(self, low_rank=32, num_experts=4, layer_num=2, l2_reg=0, seed=1024, **kwargs): + self.low_rank = low_rank + self.num_experts = num_experts + self.layer_num = layer_num + self.l2_reg = l2_reg + self.seed = seed + super(CrossNetMix, self).__init__(**kwargs) + + def build(self, input_shape): + + if len(input_shape) != 2: + raise ValueError( + "Unexpected inputs dimensions %d, expect to be 2 dimensions" % (len(input_shape),)) + + dim = int(input_shape[-1]) + + # U: (dim, low_rank) + self.U_list = [self.add_weight(name='U_list' + str(i), + shape=(self.num_experts, dim, self.low_rank), + initializer=glorot_normal( + seed=self.seed), + regularizer=l2(self.l2_reg), + trainable=True) for i in range(self.layer_num)] + # V: (dim, low_rank) + self.V_list = [self.add_weight(name='V_list' + str(i), + shape=(self.num_experts, dim, self.low_rank), + initializer=glorot_normal( + seed=self.seed), + regularizer=l2(self.l2_reg), + trainable=True) for i in range(self.layer_num)] + # C: (low_rank, low_rank) + self.C_list = [self.add_weight(name='C_list' + str(i), + shape=(self.num_experts, self.low_rank, self.low_rank), + initializer=glorot_normal( + seed=self.seed), + regularizer=l2(self.l2_reg), + trainable=True) for i in range(self.layer_num)] + + self.gating = [tf.keras.layers.Dense(1, use_bias=False) for i in range(self.num_experts)] + + self.bias = [self.add_weight(name='bias' + str(i), + shape=(dim, 1), + initializer=Zeros(), + trainable=True) for i in range(self.layer_num)] + # Be sure to call this somewhere! + super(CrossNetMix, self).build(input_shape) + + def call(self, inputs, **kwargs): + if K.ndim(inputs) != 2: + raise ValueError( + "Unexpected inputs dimensions %d, expect to be 2 dimensions" % (K.ndim(inputs))) + + x_0 = tf.expand_dims(inputs, axis=2) + x_l = x_0 + for i in range(self.layer_num): + output_of_experts = [] + gating_score_of_experts = [] + for expert_id in range(self.num_experts): + # (1) G(x_l) + # compute the gating score by x_l + gating_score_of_experts.append(self.gating[expert_id](tf.squeeze(x_l, axis=2))) + + # (2) E(x_l) + # project the input x_l to $\mathbb{R}^{r}$ + v_x = tf.einsum('ij,bjk->bik', tf.transpose(self.V_list[i][expert_id]), x_l) # (bs, low_rank, 1) + + # nonlinear activation in low rank space + v_x = tf.nn.tanh(v_x) + v_x = tf.einsum('ij,bjk->bik', self.C_list[i][expert_id], v_x) # (bs, low_rank, 1) + v_x = tf.nn.tanh(v_x) + + # project back to $\mathbb{R}^{d}$ + uv_x = tf.einsum('ij,bjk->bik', self.U_list[i][expert_id], v_x) # (bs, dim, 1) + + dot_ = uv_x + self.bias[i] + dot_ = x_0 * dot_ # Hadamard-product + + output_of_experts.append(tf.squeeze(dot_, axis=2)) + + # (3) mixture of low-rank experts + output_of_experts = tf.stack(output_of_experts, 2) # (bs, dim, num_experts) + gating_score_of_experts = tf.stack(gating_score_of_experts, 1) # (bs, num_experts, 1) + moe_out = tf.matmul(output_of_experts, tf.nn.softmax(gating_score_of_experts, 1)) + x_l = moe_out + x_l # (bs, dim, 1) + x_l = tf.squeeze(x_l, axis=2) + return x_l + + def get_config(self, ): + + config = {'low_rank': self.low_rank, 'num_experts': self.num_experts, 'layer_num': self.layer_num, + 'l2_reg': self.l2_reg, 'seed': self.seed} + base_config = super(CrossNetMix, self).get_config() + base_config.update(config) + return base_config def compute_output_shape(self, input_shape): return input_shape @@ -527,7 +677,8 @@ def compute_output_shape(self, input_shape): def get_config(self, ): config = {'reduce_sum': self.reduce_sum, } base_config = super(InnerProductLayer, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class InteractingLayer(Layer): @@ -619,7 +770,8 @@ def get_config(self, ): config = {'att_embedding_size': self.att_embedding_size, 'head_num': self.head_num, 'use_res': self.use_res, 'seed': self.seed} base_config = super(InteractingLayer, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class OutterProductLayer(Layer): @@ -762,7 +914,8 @@ def compute_output_shape(self, input_shape): def get_config(self, ): config = {'kernel_type': self.kernel_type, 'seed': self.seed} base_config = super(OutterProductLayer, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class FGCNNLayer(Layer): @@ -866,7 +1019,8 @@ def get_config(self, ): config = {'kernel_width': self.kernel_width, 'filters': self.filters, 'new_maps': self.new_maps, 'pooling_width': self.pooling_width} base_config = super(FGCNNLayer, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config def _conv_output_shape(self, input_shape, kernel_size): # channels_last @@ -965,20 +1119,21 @@ def compute_mask(self, inputs, mask=None): def get_config(self, ): config = {'reduction_ratio': self.reduction_ratio, 'seed': self.seed} base_config = super(SENETLayer, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class BilinearInteraction(Layer): """BilinearInteraction Layer used in FiBiNET. Input shape - - A list of 3D tensor with shape: ``(batch_size,1,embedding_size)``. + - A list of 3D tensor with shape: ``(batch_size,1,embedding_size)``. Its length is ``filed_size``. Output shape - - 3D tensor with shape: ``(batch_size,1,embedding_size)``. + - 3D tensor with shape: ``(batch_size,filed_size*(filed_size-1)/2,embedding_size)``. Arguments - - **str** : String, types of bilinear functions used in this layer. + - **bilinear_type** : String, types of bilinear functions used in this layer. - **seed** : A Python integer to use as random seed. @@ -1034,18 +1189,20 @@ def call(self, inputs, **kwargs): for v, w in zip(itertools.combinations(inputs, 2), self.W_list)] else: raise NotImplementedError - return concat_func(p) + output = concat_func(p, axis=1) + return output def compute_output_shape(self, input_shape): filed_size = len(input_shape) embedding_size = input_shape[0][-1] - return (None, 1, filed_size * (filed_size - 1) // 2 * embedding_size) + return (None, filed_size * (filed_size - 1) // 2, embedding_size) def get_config(self, ): config = {'bilinear_type': self.bilinear_type, 'seed': self.seed} base_config = super(BilinearInteraction, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class FieldWiseBiInteraction(Layer): @@ -1171,7 +1328,8 @@ def compute_output_shape(self, input_shape): def get_config(self, ): config = {'use_bias': self.use_bias, 'seed': self.seed} base_config = super(FieldWiseBiInteraction, self).get_config() - return dict(list(base_config.items()) + list(config.items())) + base_config.update(config) + return base_config class FwFMLayer(Layer): diff --git a/deepctr/models/__init__.py b/deepctr/models/__init__.py index bc536a5e..a8ec03f4 100644 --- a/deepctr/models/__init__.py +++ b/deepctr/models/__init__.py @@ -2,6 +2,7 @@ from .autoint import AutoInt from .ccpm import CCPM from .dcn import DCN +from .dcnmix import DCNMix from .deepfm import DeepFM from .dien import DIEN from .din import DIN @@ -19,5 +20,5 @@ from .flen import FLEN from .fwfm import FwFM -__all__ = ["AFM", "CCPM","DCN", "MLR", "DeepFM", "MLR", "NFM", "DIN", "DIEN", "FNN", "PNN", +__all__ = ["AFM", "CCPM", "DCN", "DCNMix", "MLR", "DeepFM", "MLR", "NFM", "DIN", "DIEN", "FNN", "PNN", "WDL", "xDeepFM", "AutoInt", "ONN", "FGCNN", "DSIN", "FiBiNET", 'FLEN', "FwFM"] diff --git a/deepctr/models/dcn.py b/deepctr/models/dcn.py index 8762cb1b..7998557f 100644 --- a/deepctr/models/dcn.py +++ b/deepctr/models/dcn.py @@ -3,8 +3,12 @@ Author: Weichen Shen,wcshen1994@163.com + Shuxun Zan, zanshuxun@aliyun.com + Reference: [1] Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[C]//Proceedings of the ADKDD'17. ACM, 2017: 12. (https://arxiv.org/abs/1708.05123) + + [2] Wang R, Shivanna R, Cheng D Z, et al. DCN-M: Improved Deep & Cross Network for Feature Cross Learning in Web-scale Learning to Rank Systems[J]. 2020. (https://arxiv.org/abs/2008.13535) """ import tensorflow as tf @@ -14,8 +18,8 @@ from ..layers.utils import add_func, combined_dnn_input -def DCN(linear_feature_columns, dnn_feature_columns, cross_num=2, dnn_hidden_units=(128, 128,), l2_reg_linear=1e-5, - l2_reg_embedding=1e-5, +def DCN(linear_feature_columns, dnn_feature_columns, cross_num=2, cross_parameterization='vector', + dnn_hidden_units=(128, 128,), l2_reg_linear=1e-5, l2_reg_embedding=1e-5, l2_reg_cross=1e-5, l2_reg_dnn=0, seed=1024, dnn_dropout=0, dnn_use_bn=False, dnn_activation='relu', task='binary'): """Instantiates the Deep&Cross Network architecture. @@ -23,7 +27,9 @@ def DCN(linear_feature_columns, dnn_feature_columns, cross_num=2, dnn_hidden_uni :param linear_feature_columns: An iterable containing all the features used by linear part of the model. :param dnn_feature_columns: An iterable containing all the features used by deep part of the model. :param cross_num: positive integet,cross layer number + :param cross_parameterization: str, ``"vector"`` or ``"matrix"``, how to parameterize the cross network. :param dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of DNN + :param l2_reg_linear: float. L2 regularizer strength applied to linear part :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param l2_reg_cross: float. L2 regularizer strength applied to cross net :param l2_reg_dnn: float. L2 regularizer strength applied to DNN @@ -51,7 +57,7 @@ def DCN(linear_feature_columns, dnn_feature_columns, cross_num=2, dnn_hidden_uni if len(dnn_hidden_units) > 0 and cross_num > 0: # Deep & Cross deep_out = DNN(dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(dnn_input) - cross_out = CrossNet(cross_num, l2_reg=l2_reg_cross)(dnn_input) + cross_out = CrossNet(cross_num, parameterization=cross_parameterization, l2_reg=l2_reg_cross)(dnn_input) stack_out = tf.keras.layers.Concatenate()([cross_out, deep_out]) final_logit = tf.keras.layers.Dense( 1, use_bias=False, kernel_initializer=tf.keras.initializers.glorot_normal(seed))(stack_out) @@ -60,7 +66,7 @@ def DCN(linear_feature_columns, dnn_feature_columns, cross_num=2, dnn_hidden_uni final_logit = tf.keras.layers.Dense( 1, use_bias=False, kernel_initializer=tf.keras.initializers.glorot_normal(seed))(deep_out) elif cross_num > 0: # Only Cross - cross_out = CrossNet(cross_num, l2_reg=l2_reg_cross)(dnn_input) + cross_out = CrossNet(cross_num, parameterization=cross_parameterization, l2_reg=l2_reg_cross)(dnn_input) final_logit = tf.keras.layers.Dense( 1, use_bias=False, kernel_initializer=tf.keras.initializers.glorot_normal(seed))(cross_out) else: # Error diff --git a/deepctr/models/dcnmix.py b/deepctr/models/dcnmix.py new file mode 100644 index 00000000..41c9b911 --- /dev/null +++ b/deepctr/models/dcnmix.py @@ -0,0 +1,83 @@ +# -*- coding:utf-8 -*- +""" +Author: + Weichen Shen,wcshen1994@163.com + + Shuxun Zan, zanshuxun@aliyun.com + +Reference: + [1] Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[C]//Proceedings of the ADKDD'17. ACM, 2017: 12. (https://arxiv.org/abs/1708.05123) + + [2] Wang R, Shivanna R, Cheng D Z, et al. DCN-M: Improved Deep & Cross Network for Feature Cross Learning in Web-scale Learning to Rank Systems[J]. 2020. (https://arxiv.org/abs/2008.13535) +""" +import tensorflow as tf + +from ..feature_column import build_input_features, get_linear_logit, input_from_feature_columns +from ..layers.core import PredictionLayer, DNN +from ..layers.interaction import CrossNetMix +from ..layers.utils import add_func, combined_dnn_input + + +def DCNMix(linear_feature_columns, dnn_feature_columns, cross_num=2, + dnn_hidden_units=(128, 128,), l2_reg_linear=1e-5, l2_reg_embedding=1e-5, low_rank=32, num_experts=4, + l2_reg_cross=1e-5, l2_reg_dnn=0, seed=1024, dnn_dropout=0, dnn_use_bn=False, + dnn_activation='relu', task='binary'): + """Instantiates the Deep&Cross Network architecture. + + :param linear_feature_columns: An iterable containing all the features used by linear part of the model. + :param dnn_feature_columns: An iterable containing all the features used by deep part of the model. + :param cross_num: positive integet,cross layer number + :param dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of DNN + :param l2_reg_linear: float. L2 regularizer strength applied to linear part + :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector + :param l2_reg_cross: float. L2 regularizer strength applied to cross net + :param l2_reg_dnn: float. L2 regularizer strength applied to DNN + :param seed: integer ,to use as random seed. + :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. + :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not DNN + :param dnn_activation: Activation function to use in DNN + :param low_rank: Positive integer, dimensionality of low-rank sapce. + :param num_experts: Positive integer, number of experts. + :param task: str, ``"binary"`` for binary logloss or ``"regression"`` for regression loss + :return: A Keras model instance. + + """ + if len(dnn_hidden_units) == 0 and cross_num == 0: + raise ValueError("Either hidden_layer or cross layer must > 0") + + features = build_input_features(dnn_feature_columns) + inputs_list = list(features.values()) + + linear_logit = get_linear_logit(features, linear_feature_columns, seed=seed, prefix='linear', + l2_reg=l2_reg_linear) + + sparse_embedding_list, dense_value_list = input_from_feature_columns(features, dnn_feature_columns, + l2_reg_embedding, seed) + + dnn_input = combined_dnn_input(sparse_embedding_list, dense_value_list) + + if len(dnn_hidden_units) > 0 and cross_num > 0: # Deep & Cross + deep_out = DNN(dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(dnn_input) + cross_out = CrossNetMix(low_rank=low_rank, num_experts=num_experts, layer_num=cross_num, + l2_reg=l2_reg_cross)(dnn_input) + stack_out = tf.keras.layers.Concatenate()([cross_out, deep_out]) + final_logit = tf.keras.layers.Dense( + 1, use_bias=False, kernel_initializer=tf.keras.initializers.glorot_normal(seed))(stack_out) + elif len(dnn_hidden_units) > 0: # Only Deep + deep_out = DNN(dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(dnn_input) + final_logit = tf.keras.layers.Dense( + 1, use_bias=False, kernel_initializer=tf.keras.initializers.glorot_normal(seed))(deep_out) + elif cross_num > 0: # Only Cross + cross_out = CrossNetMix(low_rank=low_rank, num_experts=num_experts, layer_num=cross_num, + l2_reg=l2_reg_cross)(dnn_input) + final_logit = tf.keras.layers.Dense( + 1, use_bias=False, kernel_initializer=tf.keras.initializers.glorot_normal(seed))(cross_out) + else: # Error + raise NotImplementedError + + final_logit = add_func([final_logit, linear_logit]) + output = PredictionLayer(task)(final_logit) + + model = tf.keras.models.Model(inputs=inputs_list, outputs=output) + + return model diff --git a/setup.py b/setup.py index 5f06066d..16bbd85c 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ long_description = fh.read() REQUIRED_PACKAGES = [ - 'h5py','requests' + 'h5py==2.10.0','requests' ] setuptools.setup( diff --git a/tests/models/DCNMix_test.py b/tests/models/DCNMix_test.py new file mode 100644 index 00000000..bf465831 --- /dev/null +++ b/tests/models/DCNMix_test.py @@ -0,0 +1,26 @@ +import pytest +import tensorflow as tf + +from deepctr.models import DCNMix +from ..utils import check_model, get_test_data, SAMPLE_SIZE, get_test_data_estimator, check_estimator, \ + Estimator_TEST_TF1 + + +@pytest.mark.parametrize( + 'cross_num,hidden_size,sparse_feature_num', + [(0, (8,), 2), (1, (), 1), (1, (8,), 3) + ] +) +def test_DCNMix(cross_num, hidden_size, sparse_feature_num): + model_name = "DCNMix" + + sample_size = SAMPLE_SIZE + x, y, feature_columns = get_test_data(sample_size, sparse_feature_num=sparse_feature_num, + dense_feature_num=sparse_feature_num) + + model = DCNMix(feature_columns, feature_columns, cross_num=cross_num, dnn_hidden_units=hidden_size, dnn_dropout=0.5) + check_model(model, model_name, x, y) + + +if __name__ == "__main__": + pass diff --git a/tests/models/DCN_test.py b/tests/models/DCN_test.py index 509e73e9..53fbd095 100644 --- a/tests/models/DCN_test.py +++ b/tests/models/DCN_test.py @@ -8,18 +8,20 @@ @pytest.mark.parametrize( - 'cross_num,hidden_size,sparse_feature_num', - [(0, (8,), 2), (1, (), 1), (1, (8,), 3) + 'cross_num,hidden_size,sparse_feature_num,cross_parameterization', + [(0, (8,), 2, 'vector'), (1, (), 1, 'vector'), (1, (8,), 3, 'vector'), + (0, (8,), 2, 'matrix'), (1, (), 1, 'matrix'), (1, (8,), 3, 'matrix'), ] ) -def test_DCN(cross_num, hidden_size, sparse_feature_num): +def test_DCN(cross_num, hidden_size, sparse_feature_num, cross_parameterization): model_name = "DCN" sample_size = SAMPLE_SIZE x, y, feature_columns = get_test_data(sample_size, sparse_feature_num=sparse_feature_num, dense_feature_num=sparse_feature_num) - model = DCN(feature_columns, feature_columns, cross_num=cross_num, dnn_hidden_units=hidden_size, dnn_dropout=0.5) + model = DCN(feature_columns, feature_columns, cross_num=cross_num, cross_parameterization=cross_parameterization, + dnn_hidden_units=hidden_size, dnn_dropout=0.5) check_model(model, model_name, x, y) From 8360dc7b4ed4a1f303a2a179b98c088c89de025d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E6=A2=A6?= Date: Wed, 6 Jan 2021 22:09:38 +0800 Subject: [PATCH 3/3] update docs (#317) update docs --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- .github/ISSUE_TEMPLATE/question.md | 4 ++-- .github/workflows/ci.yml | 2 +- CONTRIBUTING.md | 15 +++++++++++-- README.md | 9 ++++---- deepctr/__init__.py | 2 +- deepctr/estimator/models/afm.py | 2 +- deepctr/estimator/models/autoint.py | 2 +- deepctr/estimator/models/ccpm.py | 2 +- deepctr/estimator/models/dcn.py | 2 +- deepctr/estimator/models/deepfm.py | 2 +- deepctr/estimator/models/fibinet.py | 2 +- deepctr/estimator/models/fnn.py | 2 +- deepctr/estimator/models/fwfm.py | 2 +- deepctr/estimator/models/nfm.py | 2 +- deepctr/estimator/models/pnn.py | 2 +- deepctr/estimator/models/wdl.py | 2 +- deepctr/estimator/models/xdeepfm.py | 2 +- deepctr/models/afm.py | 2 +- deepctr/models/autoint.py | 2 +- deepctr/models/ccpm.py | 2 +- deepctr/models/dcn.py | 2 +- deepctr/models/dcnmix.py | 6 ++--- deepctr/models/deepfm.py | 2 +- deepctr/models/dien.py | 2 +- deepctr/models/din.py | 2 +- deepctr/models/dsin.py | 2 +- deepctr/models/fgcnn.py | 2 +- deepctr/models/fibinet.py | 2 +- deepctr/models/flen.py | 2 +- deepctr/models/fnn.py | 2 +- deepctr/models/mlr.py | 2 +- deepctr/models/nfm.py | 2 +- deepctr/models/onn.py | 2 +- deepctr/models/pnn.py | 2 +- deepctr/models/wdl.py | 2 +- deepctr/models/xdeepfm.py | 2 +- docs/pics/DCN-M.png | Bin 0 -> 66893 bytes docs/pics/DCN-Mix.png | Bin 0 -> 57513 bytes docs/source/Features.md | 21 ++++++++++++++++-- docs/source/History.md | 1 + docs/source/Models.rst | 2 ++ docs/source/conf.py | 2 +- docs/source/deepctr.models.dcnmix.rst | 7 ++++++ docs/source/deepctr.models.rst | 1 + docs/source/index.rst | 5 ++--- .../run_estimator_pandas_classification.py | 3 ++- .../run_estimator_tfrecord_classification.py | 6 ++--- setup.py | 2 +- 49 files changed, 96 insertions(+), 56 deletions(-) create mode 100644 docs/pics/DCN-M.png create mode 100644 docs/pics/DCN-Mix.png create mode 100644 docs/source/deepctr.models.dcnmix.rst diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 78b188ff..b5d3b712 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -19,8 +19,8 @@ Steps to reproduce the behavior: **Operating environment(运行环境):** - python version [e.g. 3.5, 3.7] - - tensorflow version [e.g. 1.4.0, 1.15.0, 2.3.0] - - deepctr version [e.g. 0.8.2,] + - tensorflow version [e.g. 1.4.0, 1.15.0, 2.4.0] + - deepctr version [e.g. 0.8.3,] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 7362b9c8..7f4dec64 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -16,5 +16,5 @@ Add any other context about the problem here. **Operating environment(运行环境):** - python version [e.g. 3.6] - - tensorflow version [e.g. 1.4.0, 1.5.0, 2.3.0] - - deepctr version [e.g. 0.8.2,] + - tensorflow version [e.g. 1.4.0, 1.5.0, 2.4.0] + - deepctr version [e.g. 0.8.3,] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5132f12..88dae87c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: python-version: [3.6,3.7] - tf-version: [1.4.0,1.15.0,2.1.0,2.3.0] + tf-version: [1.4.0,1.15.0,2.1.0,2.4.0] exclude: - python-version: 3.7 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cfc7b519..ebc80d58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ This project is under development and we need developers to participate in. - +# Join us If you - familiar with and interested in ctr prediction algorithms @@ -7,4 +7,15 @@ If you - have spare time to learn and develop - familiar with git -please send a brief introduction of your background and experience to wcshen1994@163.com, welcome to join us! \ No newline at end of file +please send a brief introduction of your background and experience to wcshen1994@163.com, welcome to join us! + +# Creating a pull request +1. **Become a collaborator**: Send an email with introduction and your github account name to wcshen1994@163.com and waiting for invitation to become a collaborator. +2. **Fork&Dev**: Fork your own branch(`dev_yourname`) in `DeepCTR` from the `master` branch for development.If the `master` is updated during the development process, remember to merge and update to `dev_yourname` regularly. +3. **Testing**: Test logical correctness and effect when finishing the code development of the `dev_yourname` branch. +4. **Pre-release** : After testing contact wcshen1994@163.com for pre-release integration, usually your branch `dev_yourname` will be merged into `release` branch by squash merge. +5. **Release a new version**: After confirming that the change is no longer needed, `release` branch will be merged into `master` and a new python package will be released on pypi. + +# Discussions + +https://github.com/shenweichen/DeepCTR/discussions \ No newline at end of file diff --git a/README.md b/README.md index 4727aa0e..39a24f41 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Documentation Status](https://readthedocs.org/projects/deepctr-doc/badge/?version=latest)](https://deepctr-doc.readthedocs.io/) -[![Build Status](https://travis-ci.org/shenweichen/DeepCTR.svg?branch=master)](https://travis-ci.org/shenweichen/DeepCTR) +![CI status](https://github.com/shenweichen/deepctr/workflows/CI/badge.svg) [![Coverage Status](https://coveralls.io/repos/github/shenweichen/DeepCTR/badge.svg?branch=master)](https://coveralls.io/github/shenweichen/DeepCTR?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/d4099734dc0e4bab91d332ead8c0bdd0)](https://www.codacy.com/app/wcshen1994/DeepCTR?utm_source=github.com&utm_medium=referral&utm_content=shenweichen/DeepCTR&utm_campaign=Badge_Grade) [![Disscussion](https://img.shields.io/badge/chat-wechat-brightgreen?style=flat)](./README.md#disscussiongroup) @@ -45,8 +45,8 @@ Let's [**Get Started!**](https://deepctr-doc.readthedocs.io/en/latest/Quick-Star | Attentional Factorization Machine | [IJCAI 2017][Attentional Factorization Machines: Learning the Weight of Feature Interactions via Attention Networks](http://www.ijcai.org/proceedings/2017/435) | | Neural Factorization Machine | [SIGIR 2017][Neural Factorization Machines for Sparse Predictive Analytics](https://arxiv.org/pdf/1708.05027.pdf) | | xDeepFM | [KDD 2018][xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems](https://arxiv.org/pdf/1803.05170.pdf) | -| AutoInt | [arxiv 2018][AutoInt: Automatic Feature Interaction Learning via Self-Attentive Neural Networks](https://arxiv.org/abs/1810.11921) | -| Deep Interest Network | [KDD 2018][Deep Interest Network for Click-Through Rate Prediction](https://arxiv.org/pdf/1706.06978.pdf) | +| Deep Interest Network | [KDD 2018][Deep Interest Network for Click-Through Rate Prediction](https://arxiv.org/pdf/1706.06978.pdf) +| AutoInt | [CIKM 2019][AutoInt: Automatic Feature Interaction Learning via Self-Attentive Neural Networks](https://arxiv.org/abs/1810.11921) || | Deep Interest Evolution Network | [AAAI 2019][Deep Interest Evolution Network for Click-Through Rate Prediction](https://arxiv.org/pdf/1809.03672.pdf) | | FwFM | [WWW 2018][Field-weighted Factorization Machines for Click-Through Rate Prediction in Display Advertising](https://arxiv.org/pdf/1806.03514.pdf) | | ONN | [arxiv 2019][Operation-aware Neural Networks for User Response Prediction](https://arxiv.org/pdf/1904.12579.pdf) | @@ -54,6 +54,7 @@ Let's [**Get Started!**](https://deepctr-doc.readthedocs.io/en/latest/Quick-Star | Deep Session Interest Network | [IJCAI 2019][Deep Session Interest Network for Click-Through Rate Prediction ](https://arxiv.org/abs/1905.06482) | | FiBiNET | [RecSys 2019][FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction](https://arxiv.org/pdf/1905.09433.pdf) | | FLEN | [arxiv 2019][FLEN: Leveraging Field for Scalable CTR Prediction](https://arxiv.org/pdf/1911.04690.pdf) | +| DCN V2 | [arxiv 2020][DCN V2: Improved Deep & Cross Network and Practical Lessons for Web-scale Learning to Rank Systems](https://arxiv.org/abs/2008.13535) | ## Citation @@ -75,7 +76,7 @@ If you find this code useful in your research, please cite it using the followin ## DisscussionGroup 交流群 -Please follow our wechat to join group: +- [Discussions](https://github.com/shenweichen/DeepCTR/discussions) - 公众号:**浅梦的学习笔记** - wechat ID: **deepctrbot** diff --git a/deepctr/__init__.py b/deepctr/__init__.py index c763d56b..b268aaeb 100644 --- a/deepctr/__init__.py +++ b/deepctr/__init__.py @@ -1,4 +1,4 @@ from .utils import check_version -__version__ = '0.8.2' +__version__ = '0.8.3' check_version(__version__) diff --git a/deepctr/estimator/models/afm.py b/deepctr/estimator/models/afm.py index 82066e2b..47fd2204 100644 --- a/deepctr/estimator/models/afm.py +++ b/deepctr/estimator/models/afm.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Xiao J, Ye H, He X, et al. Attentional factorization machines: Learning the weight of feature interactions via attention networks[J]. arXiv preprint arXiv:1708.04617, 2017. diff --git a/deepctr/estimator/models/autoint.py b/deepctr/estimator/models/autoint.py index f29a0447..40e3b1e1 100644 --- a/deepctr/estimator/models/autoint.py +++ b/deepctr/estimator/models/autoint.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Song W, Shi C, Xiao Z, et al. AutoInt: Automatic Feature Interaction Learning via Self-Attentive Neural Networks[J]. arXiv preprint arXiv:1810.11921, 2018.(https://arxiv.org/abs/1810.11921) diff --git a/deepctr/estimator/models/ccpm.py b/deepctr/estimator/models/ccpm.py index 1586b9f7..8d0f6dfd 100644 --- a/deepctr/estimator/models/ccpm.py +++ b/deepctr/estimator/models/ccpm.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Liu Q, Yu F, Wu S, et al. A convolutional click prediction model[C]//Proceedings of the 24th ACM International on Conference on Information and Knowledge Management. ACM, 2015: 1743-1746. diff --git a/deepctr/estimator/models/dcn.py b/deepctr/estimator/models/dcn.py index 1f62fa60..ceba2b6b 100644 --- a/deepctr/estimator/models/dcn.py +++ b/deepctr/estimator/models/dcn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[C]//Proceedings of the ADKDD'17. ACM, 2017: 12. (https://arxiv.org/abs/1708.05123) diff --git a/deepctr/estimator/models/deepfm.py b/deepctr/estimator/models/deepfm.py index 30193c45..0021b506 100644 --- a/deepctr/estimator/models/deepfm.py +++ b/deepctr/estimator/models/deepfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Guo H, Tang R, Ye Y, et al. Deepfm: a factorization-machine based neural network for ctr prediction[J]. arXiv preprint arXiv:1703.04247, 2017.(https://arxiv.org/abs/1703.04247) diff --git a/deepctr/estimator/models/fibinet.py b/deepctr/estimator/models/fibinet.py index 99f4e746..619f4f8e 100644 --- a/deepctr/estimator/models/fibinet.py +++ b/deepctr/estimator/models/fibinet.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Huang T, Zhang Z, Zhang J. FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction[J]. arXiv preprint arXiv:1905.09433, 2019. diff --git a/deepctr/estimator/models/fnn.py b/deepctr/estimator/models/fnn.py index 3b200ddf..aeb7de1a 100644 --- a/deepctr/estimator/models/fnn.py +++ b/deepctr/estimator/models/fnn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Zhang W, Du T, Wang J. Deep learning over multi-field categorical data[C]//European conference on information retrieval. Springer, Cham, 2016: 45-57.(https://arxiv.org/pdf/1601.02376.pdf) diff --git a/deepctr/estimator/models/fwfm.py b/deepctr/estimator/models/fwfm.py index 9948ecf0..f5de40ab 100644 --- a/deepctr/estimator/models/fwfm.py +++ b/deepctr/estimator/models/fwfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Harshit Pande Reference: diff --git a/deepctr/estimator/models/nfm.py b/deepctr/estimator/models/nfm.py index 3bc3378c..ff10b776 100644 --- a/deepctr/estimator/models/nfm.py +++ b/deepctr/estimator/models/nfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] He X, Chua T S. Neural factorization machines for sparse predictive analytics[C]//Proceedings of the 40th International ACM SIGIR conference on Research and Development in Information Retrieval. ACM, 2017: 355-364. (https://arxiv.org/abs/1708.05027) diff --git a/deepctr/estimator/models/pnn.py b/deepctr/estimator/models/pnn.py index fc0f33fe..add1da8b 100644 --- a/deepctr/estimator/models/pnn.py +++ b/deepctr/estimator/models/pnn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Qu Y, Cai H, Ren K, et al. Product-based neural networks for user response prediction[C]//Data Mining (ICDM), 2016 IEEE 16th International Conference on. IEEE, 2016: 1149-1154.(https://arxiv.org/pdf/1611.00144.pdf) diff --git a/deepctr/estimator/models/wdl.py b/deepctr/estimator/models/wdl.py index 29e7df2e..381d5f28 100644 --- a/deepctr/estimator/models/wdl.py +++ b/deepctr/estimator/models/wdl.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Cheng H T, Koc L, Harmsen J, et al. Wide & deep learning for recommender systems[C]//Proceedings of the 1st Workshop on Deep Learning for Recommender Systems. ACM, 2016: 7-10.(https://arxiv.org/pdf/1606.07792.pdf) diff --git a/deepctr/estimator/models/xdeepfm.py b/deepctr/estimator/models/xdeepfm.py index a86b5e97..65a0658a 100644 --- a/deepctr/estimator/models/xdeepfm.py +++ b/deepctr/estimator/models/xdeepfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Lian J, Zhou X, Zhang F, et al. xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems[J]. arXiv preprint arXiv:1803.05170, 2018.(https://arxiv.org/pdf/1803.05170.pdf) diff --git a/deepctr/models/afm.py b/deepctr/models/afm.py index 52743f0a..dd664373 100644 --- a/deepctr/models/afm.py +++ b/deepctr/models/afm.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Xiao J, Ye H, He X, et al. Attentional factorization machines: Learning the weight of feature interactions via attention networks[J]. arXiv preprint arXiv:1708.04617, 2017. diff --git a/deepctr/models/autoint.py b/deepctr/models/autoint.py index 6ea70951..c742e3c2 100644 --- a/deepctr/models/autoint.py +++ b/deepctr/models/autoint.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Song W, Shi C, Xiao Z, et al. AutoInt: Automatic Feature Interaction Learning via Self-Attentive Neural Networks[J]. arXiv preprint arXiv:1810.11921, 2018.(https://arxiv.org/abs/1810.11921) diff --git a/deepctr/models/ccpm.py b/deepctr/models/ccpm.py index 7c7731dc..cfe0013f 100644 --- a/deepctr/models/ccpm.py +++ b/deepctr/models/ccpm.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Liu Q, Yu F, Wu S, et al. A convolutional click prediction model[C]//Proceedings of the 24th ACM International on Conference on Information and Knowledge Management. ACM, 2015: 1743-1746. diff --git a/deepctr/models/dcn.py b/deepctr/models/dcn.py index 7998557f..5a14f5a7 100644 --- a/deepctr/models/dcn.py +++ b/deepctr/models/dcn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Shuxun Zan, zanshuxun@aliyun.com diff --git a/deepctr/models/dcnmix.py b/deepctr/models/dcnmix.py index 41c9b911..ea62cd24 100644 --- a/deepctr/models/dcnmix.py +++ b/deepctr/models/dcnmix.py @@ -1,14 +1,14 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Shuxun Zan, zanshuxun@aliyun.com Reference: [1] Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[C]//Proceedings of the ADKDD'17. ACM, 2017: 12. (https://arxiv.org/abs/1708.05123) - [2] Wang R, Shivanna R, Cheng D Z, et al. DCN-M: Improved Deep & Cross Network for Feature Cross Learning in Web-scale Learning to Rank Systems[J]. 2020. (https://arxiv.org/abs/2008.13535) + [2] Wang R, Shivanna R, Cheng D Z, et al. DCN V2: Improved Deep & Cross Network and Practical Lessons for Web-scale Learning to Rank Systems[J]. 2020. (https://arxiv.org/abs/2008.13535) """ import tensorflow as tf @@ -22,7 +22,7 @@ def DCNMix(linear_feature_columns, dnn_feature_columns, cross_num=2, dnn_hidden_units=(128, 128,), l2_reg_linear=1e-5, l2_reg_embedding=1e-5, low_rank=32, num_experts=4, l2_reg_cross=1e-5, l2_reg_dnn=0, seed=1024, dnn_dropout=0, dnn_use_bn=False, dnn_activation='relu', task='binary'): - """Instantiates the Deep&Cross Network architecture. + """Instantiates the Deep&Cross Network with mixture of experts architecture. :param linear_feature_columns: An iterable containing all the features used by linear part of the model. :param dnn_feature_columns: An iterable containing all the features used by deep part of the model. diff --git a/deepctr/models/deepfm.py b/deepctr/models/deepfm.py index ebf62946..125744ad 100644 --- a/deepctr/models/deepfm.py +++ b/deepctr/models/deepfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Guo H, Tang R, Ye Y, et al. Deepfm: a factorization-machine based neural network for ctr prediction[J]. arXiv preprint arXiv:1703.04247, 2017.(https://arxiv.org/abs/1703.04247) diff --git a/deepctr/models/dien.py b/deepctr/models/dien.py index aff88585..3b167472 100644 --- a/deepctr/models/dien.py +++ b/deepctr/models/dien.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Zhou G, Mou N, Fan Y, et al. Deep Interest Evolution Network for Click-Through Rate Prediction[J]. arXiv preprint arXiv:1809.03672, 2018. (https://arxiv.org/pdf/1809.03672.pdf) diff --git a/deepctr/models/din.py b/deepctr/models/din.py index a0235748..1e2c536d 100644 --- a/deepctr/models/din.py +++ b/deepctr/models/din.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Zhou G, Zhu X, Song C, et al. Deep interest network for click-through rate prediction[C]//Proceedings of the 24th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining. ACM, 2018: 1059-1068. (https://arxiv.org/pdf/1706.06978.pdf) diff --git a/deepctr/models/dsin.py b/deepctr/models/dsin.py index d80f294c..de52ea0b 100644 --- a/deepctr/models/dsin.py +++ b/deepctr/models/dsin.py @@ -1,7 +1,7 @@ # coding: utf-8 """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Feng Y, Lv F, Shen W, et al. Deep Session Interest Network for Click-Through Rate Prediction[J]. arXiv preprint arXiv:1905.06482, 2019.(https://arxiv.org/abs/1905.06482) diff --git a/deepctr/models/fgcnn.py b/deepctr/models/fgcnn.py index 5fb079a6..3ee1eaa4 100644 --- a/deepctr/models/fgcnn.py +++ b/deepctr/models/fgcnn.py @@ -2,7 +2,7 @@ """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Liu B, Tang R, Chen Y, et al. Feature Generation by Convolutional Neural Network for Click-Through Rate Prediction[J]. arXiv preprint arXiv:1904.04447, 2019. diff --git a/deepctr/models/fibinet.py b/deepctr/models/fibinet.py index d57d1059..c0407c6a 100644 --- a/deepctr/models/fibinet.py +++ b/deepctr/models/fibinet.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Huang T, Zhang Z, Zhang J. FiBiNET: Combining Feature Importance and Bilinear feature Interaction for Click-Through Rate Prediction[J]. arXiv preprint arXiv:1905.09433, 2019. diff --git a/deepctr/models/flen.py b/deepctr/models/flen.py index 3c9d629b..3865dc6c 100644 --- a/deepctr/models/flen.py +++ b/deepctr/models/flen.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Tingyi Tan,5636374@qq.com + Tingyi Tan, 5636374@qq.com Reference: [1] Chen W, Zhan L, Ci Y, Lin C. FLEN: Leveraging Field for Scalable CTR Prediction . arXiv preprint arXiv:1911.04690, 2019.(https://arxiv.org/pdf/1911.04690) diff --git a/deepctr/models/fnn.py b/deepctr/models/fnn.py index 69f93e02..b2d729ac 100644 --- a/deepctr/models/fnn.py +++ b/deepctr/models/fnn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Zhang W, Du T, Wang J. Deep learning over multi-field categorical data[C]//European conference on information retrieval. Springer, Cham, 2016: 45-57.(https://arxiv.org/pdf/1601.02376.pdf) diff --git a/deepctr/models/mlr.py b/deepctr/models/mlr.py index ea003ff5..3cff00d4 100644 --- a/deepctr/models/mlr.py +++ b/deepctr/models/mlr.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Gai K, Zhu X, Li H, et al. Learning Piece-wise Linear Models from Large Scale Data for Ad Click Prediction[J]. arXiv preprint arXiv:1704.05194, 2017.(https://arxiv.org/abs/1704.05194) diff --git a/deepctr/models/nfm.py b/deepctr/models/nfm.py index 7e14879c..5c643a94 100644 --- a/deepctr/models/nfm.py +++ b/deepctr/models/nfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] He X, Chua T S. Neural factorization machines for sparse predictive analytics[C]//Proceedings of the 40th International ACM SIGIR conference on Research and Development in Information Retrieval. ACM, 2017: 355-364. (https://arxiv.org/abs/1708.05027) diff --git a/deepctr/models/onn.py b/deepctr/models/onn.py index bc3a0bed..bc96abaa 100644 --- a/deepctr/models/onn.py +++ b/deepctr/models/onn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Yang Y, Xu B, Shen F, et al. Operation-aware Neural Networks for User Response Prediction[J]. arXiv preprint arXiv:1904.12579, 2019. (https://arxiv.org/pdf/1904.12579) diff --git a/deepctr/models/pnn.py b/deepctr/models/pnn.py index c015759f..c450c6ac 100644 --- a/deepctr/models/pnn.py +++ b/deepctr/models/pnn.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Qu Y, Cai H, Ren K, et al. Product-based neural networks for user response prediction[C]//Data Mining (ICDM), 2016 IEEE 16th International Conference on. IEEE, 2016: 1149-1154.(https://arxiv.org/pdf/1611.00144.pdf) diff --git a/deepctr/models/wdl.py b/deepctr/models/wdl.py index 7820cf0c..1d8a51e4 100644 --- a/deepctr/models/wdl.py +++ b/deepctr/models/wdl.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Cheng H T, Koc L, Harmsen J, et al. Wide & deep learning for recommender systems[C]//Proceedings of the 1st Workshop on Deep Learning for Recommender Systems. ACM, 2016: 7-10.(https://arxiv.org/pdf/1606.07792.pdf) diff --git a/deepctr/models/xdeepfm.py b/deepctr/models/xdeepfm.py index f86652e0..2f518cd5 100644 --- a/deepctr/models/xdeepfm.py +++ b/deepctr/models/xdeepfm.py @@ -1,7 +1,7 @@ # -*- coding:utf-8 -*- """ Author: - Weichen Shen,wcshen1994@163.com + Weichen Shen, wcshen1994@163.com Reference: [1] Lian J, Zhou X, Zhang F, et al. xDeepFM: Combining Explicit and Implicit Feature Interactions for Recommender Systems[J]. arXiv preprint arXiv:1803.05170, 2018.(https://arxiv.org/pdf/1803.05170.pdf) diff --git a/docs/pics/DCN-M.png b/docs/pics/DCN-M.png new file mode 100644 index 0000000000000000000000000000000000000000..0504d4e09505582fbb410d3ed110a57a4454465d GIT binary patch literal 66893 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 X?04G7zsdNbRW(!r z2m}D0fPVmP7VuQ@v9|#LEiFI*003eDAHo6s)@`0O@j?RtKGJn7REdP(M{gI!t1A{`RW&J_^dw|Bu z`hg{=ybj2I-|B&-Gl(kz06)~y&BGG_2&F+fx3{O;DZT?@IuB4l5WAgX+g~{J6yN`a zpa0~!t)~p~yaQhexrK#?EdWr~f^^P@mbPFylw%+kaj~*@0RSpu5X)QISy+Miw0$o) zJG-7@kPqO$|EF?n|AZ|p?*CDyg@x@O_?IqVPO#!@_7B{gEPQ|7{C9piJ9&Za^)sCV zzsc=9)b+qC9oTMTj;@-gm>k6GF86P0gZK=H(e|K5zc3!DotL^kh(UflHp>UfU|WLk z8Nd^FTK<@wwZ`w|@A}!@HU#OQ%uv#Y_6DaTf)d0G z4;*iu$^*&(g57krMPx(EZAAsnmY|uzM zdyUh)pq$V&PrKWv@A$Nwca|1Ew3*Pc=zjxDqsP40JeZNAoLsM z_Z)^lpSXbEw*Wt218@O(9{iE->d&Xvz(bIJ@(1--ULnBd=hKHjb2tKvU=7ZICg23# zdx6**%=3F~2EYPHvH#=#_u4H%S)IVo>9fDmlt8U+fVG}-dHr54C)MwqgervB2-OKy z3D4uR;LGF7;@<$j6~WIXd}(}{-=+NrKfxkFKfxTqFu@GLxVe3>$L}1!mj`G8GhjWx z+R7Si$3N+ee-12F0{<$$99TYBHa-JB3m}9q4%VcEuK?C90aBHJ)8|}VtUx(6 zf0OkOPG}^w0@?{}hJJ=NLTdm{C<0mw{Q~`PihrwL_2*l({i8O$pYPZad^7fc@cgc? z1xPuq^Ryp45GfK_5yAW_#naly6YRHuva6e~hrO+xC#MoP^ILOjxL97izAfixfs$OYa2#Xvbw12h87KnKtb^Z~=bB-nd?0BZme*awaw5C{>30zwO6hQJ`a z5JAW#h%`hVq5`=A(SsO6?m=uJE)Xw>KO_Va14)9UL0&-$A!U#{NHgR!q#rU4nT4!C zwjoD&P&{%xIy`o~b9lmd(s)XEns~SI%OQ7}Odme<&K-Zy%_=Nbh_?-Ac zU=LQu*T=WScgGLJkHt^Je}i9*--bVkKZ}pV#}ZHwun`CnTqe*UFe0!e@FoZ+NFm54 zs3K?wN7E02eL_M)20}hUDR49z6FLw+B8(->AS@;PNZ3y}Pq;@!M8r%aNF+z3O=Lmj zNfb`>g6JI)f@px~2N9Z>j2K3IiCC4`gxG~Rm^g*_EpZd^0Pzy>F$pyZFUe&RZ4xUI zUy=lp*Ch2My(B+Kj!9`q`AFqRZ<9KZJ|=xmT0+`R`i*pljEw9onGBgOnLSw$*>ke@ zWS_`p$qvcSkPDD2liwltB9A98Apb}{PQF7yL2-^kk-~(+lOmp?kfN1hiULhZOL>t} zgVKr;PMJ&=u2tr9;x6p%l_RmiX09c&p4VmR$#O+d6+%y84Lki z<)r6S;&kGC&e_I^!3CKME*Ekxj0%wpDG5CkDiWH#$Z+w- z#o&uI7dM4@giVAKg*%0@A`&7_BCkazMbC&{7Y!Dz7u~)jaLMvg`lTT;3NckNxLBnQtHcKHh573(XnuguGxm9vn0B{w6_DSuBsTYg4?OTj`RM`2Ep zThU4}UvWw4ypn^`JEhI5B3C`GR$V<%zN{Re{P7y}n%cGKYhP8)sOYPtsZ6P!Rkc+u zQbnpss6A5qs7|1+sh+4ls==;dsZpqbye@S;;Ch=Tx#lg+G|jmi0yjKv)NA2sUDtZ5 z_3b9_O_!TB+JLr(_EYWeI($0rIt{vnx;nb)x{G?Edj5Kyw`gyf-+Fs%|Mu0}3AZQp z`Src@TMVcS?idsr92j0ROfsA?5;h7j>NaLEwl}UdAu=&A$v4?ERWVIA{c%U)PWYV> zGd?pPv(I-~?mFH5U`}OjVP0{M;GW^VxA!m>+7>w$d-pZ&zr2sMRJKgDT(eTNdSm8crOlPY)yK8pP0%geZN~kwd$K#yL(?Pw0px+%gIZ5I&|v@K#qSm7HTzKhVfsU~ zw}E$s4~>tD&lg_--zeWDKUKfHNBEB{AGP>%`9Job4NwYr6$lBuAJ_uthKIs`1gQnR zc})7)@o`VENN`f{Zirz>eJFcqQ0RP^T3AsyWw=N9XoPG;RwRC;edO0Dv8WeO*l4Th zt{9P+lo(8`Wo%cRXx#HST)a*E*96Ig%qN6TT%L?1DkQ#1qDk^gnt!VGwE7w6v*>5L z$>zzQQ^ZrUQb|+2QfHpucwYO0=f#s3m^8bz;qPp|L z>FSR)x3%N-@C}-cmz(^Xm0OBiUyw$~A1DtLZaa2|d8c4ke79v!XK!lXdH?tz8qJJ; zb0~e-d1QFBbnJ~G!aP4Ye^QUt#D2p$<8W>k9u_}s0Vw#5X9IfR+XVnX0oo3>M^I5pRmKLWse@N+Kcr%M8yn(DU|0X#9WKRzJk zG(E5pyYv%4$UQ*Q1dYS(umb>5Jpi0|;BdzoINZr=&{r4-fRFCK{L52rLTUiWmOn+q zOjbds*FPuRM}USHPaR4Kg`5TOXdqA;2(Ar)fjSU?h6$K{XNKTG@d*fth)GDvK!z%6 z01pC%;^9LH2u_0nkO1&GfKNkkhD%h5kXF}%=&U>4rN@b{h`FzpHPh?$BYDK`djyk^ zGB7eRv+$nd<3BGTE+Hu;Epu7Cq=cmtI1A{}uBco&E z6SH&k3qKZ@mRD9$+dI2^`v>U5qf@;g0Q8qye`xkEdeMM-;o;*$@rh3Lg5Y_BA1Dnz z0hcJ@86{mJ3wPSHmmU+-T}^yd)=a`JriY}z@6k`nz#~4(i#k>9r)GaovEctL&HhmA zA9_uK<}=>U1jWOH5PFusJ1P6$IFy)AJ-=NlXlo^X;0Uw(r& z#Q{VQ6)=85a+%R6INEOOpBM|_v$+V{tRn#q1{~1*Jpl)>XH(haG}>ZW6!v{_Ku+$g zD~5dx2cT9JW^-}CDXaYNtQ^@?wmFUFSQgoTVKx7)nuy=!v4f7kDP{hvPzisP96G*S z$_R4C|Fih1zt{5WcY!8=eFwn~Q2u|FD)+Cl1N(l0?Kt2!UH_p#sRnX1x>0yu?!3e_ z(Heu_VmkLFq2aJLZyhnNRtV3HF7+!knG}GluBHk=r>ED4_umQv>2wugxS@a3=qL@t zp7Zf;#qyq%?+Pl=*r(LLqd^m5uf=cTXV7;PIY<#a5h22lJz_b|Y!X4GVlwk%aDdgZ z4h|TsDh3`-Bj3t1!uv9E;WJH>I3O|?ORv_lbwpd~ieUurQS{TgifmLEfrPN>T|T>M zv~V>JnB&I*VYr80T>mZ&US@K+L_drfcvhLs|N=x)x}QNd!6&IRa9QM zT-4C$9?m`*IQxw>YgT*17il`TS+&Lu*wg8{7-`HPh(VI|Inv?8q}vGp)HWN?+RD$ z9dt}PBXT9pI&)B>ZKGU#s+sWt#|s5_X){_6qc+Q2cV<3-egk}{b#!6h0NKu9%nIqM($Gte$_tHo^j;!j5TZ` z)1u33R^BzwTO9Hc2mDiuxMI)NxsG*p^=ctg+e|4g6K^;>V*=)G$MZkqv5WBl^uahe zk)q)E+9!nN)*)jPFMM1HrGQN)1&$V@`Lpq&ubTk;;Ofi&IuE@0FB33!P~7mpbLuRO z!C2z}zJeTZPLxOd?9BmL=1e}_SnXRXIG{WLjsprDO@M>zDAx(W&dU+4@ZdlpRQ!_; z*<(S4i{Cqq7VEr%We6YTsvwZ}q6LL-HCE#JyoPIAuCUS-<9 zb(Wv+eKu97!oA+dfy5|S@E%oklhnY>XqDjFxbsHXx}!Ur?o~$r$2eh=&q4R08e`Z#FiJt8fi;vJ3IkYd%GFTiJPuU&nQsQC0_oydkVM!802@;-l zLi-%+6rL2B6I_AMeAnFPM8R4og$$Ve)$)8EwTXQji#_ab8~ED%iJR43z(e+>Umt!J z6%MeUp6Hy1N7$y$!b3bG>IQi%9{C;T)bIw0Hj`?*cW?aoy0|KRm+TRIz4chz(VU^` zgY9fhTfjLv+73Tr@5Hm@iC>=7M(gNy(b&(56+3A@acj%E*DUHMg2brp8<)?X_~5=jR5wAn%K| z4~WtZ4fko#PiL}1r+CnhXTd3UqLKqv+jz_wS2Edkbzj)6{FUX$dsa+DPebC9bCH_u z6AB3muEnP9nXo54`O_zjlb?f|tlTGk9XZJ%kd{qmU?v*akz+JE$)??JM+{%^l;g$n z!lZiMd%Lcq(m7cvmO`)vi#Wja03mQv+qW>B-|xgbniEfT3U_PZb!~utlfq%@BdlTEjW5-yPttj(-9e-^ckasmm zzBVW2*My1x-wH6G z%P<(O4o`f>@R1Mt5~>K(&>MXLZ&^i4;Q-}Da0w34G{?%QVJ?=2z<1^CU}*6w95DY3 zl7S+c;cLbL(cV`{xM{pjm&J#g3VW^C3a)hMvD1njV=jduPLYMwr}W z#rtL+MQ5)>$Kdd_L9tJeP3XOYA_TI&i2?`o9cII421ao}Jh-Zo70pbaP^A%JDTLtY zAApzq1!P3{6|Yy{d>q@4tXQgFJV>}iGU}03z!l;4ahr)+&GN2xPGC0zXp}2hWwA+~ z=@t*sSEBEIKJaymrT!DOhG4_nO?{s8z!R~7dT^aKGbh9W%l>@|NP9<6`csQ%80m_Q z=z-z@!W0C?BS^|K3-A$BMh2syH~Nd`kTdZ{O&@Q43F;IR`;=$?P+3frbE()zAr`b- z;Sus@(9ZKKO$^mAYPYe94Zb zw>fH;Dt?N%;}v0rmqh&(ceBU)&*V@+9;YOHn_C_0XD%Cl!xEx>XR^ZUrTeasvLB((xF%%MU3%kgR)LZ6iX_9t-3;pBk*_@AOrz53^Ez*HuOI>XrRo2? z!}wYIGOh>UfU{d|rN{j8d^lj>m5IW?<}+YiH-BT~&_s=nC|vOty=(m4=eVt0S^Q$T z+F6ND0-sjZt+}5lHD^%qCX;a z{cGj}_MQmrq_bi5dH=pmU2fX0y)t zI}h#EiJP9cgPi2=)bvnOLA!V^q(-8_tU0WQ` z0s8j;LMa+9w1a{pR2c(KhB@A|y%;)UaLm5dMuW~A6tSFvegH0d3~-FYhx^gMcEVrC z4d0=ynHvhnFO-q!nysG#&Ixd`a#-P6PkV-tQmo^e;E~|F_s) zX|lXH22rWDzQcD=(aGzvo>|n zVrR5wH-mOpOS5m(evSDO;R7bEV4&_iMVk2XZ-cw z8J2{z0RKVY5N$b^urWO2N`Q`bCG`Gd14 z5YRoV1VE(tUjWJN{@2i9v*uxeWW!ei=1qIc>{+hewJM=&Xb>e~)8#zOG`{mzHKtpZ zfGusKXu+IuqO?70qM#Lv-()V^nnmha>wn9eL?%AKRWHTVS@6r+-bdJ2^>{`2#{1d2P7yD=SntJBf-!C3e~GJ96+Lrz1+lC$-@E zm4kdEE-D$(cY4``y*#_Vas|l{YLFI*sC3_>jRlQ52?u!DL95nTdoRS}!S5I@6xFn5 z76oE_{k3~nOtDafr8|dT(KS{!!BX8~vF4?BB4=S${{+O$z`9DgmXp8HM7m+hs%MdNq?KMk19iO&_U1avht z6-iWt=>81=t*ZjSaet0~qhJ%tZAS?qpb6G(KET%QOA(_&7wy(x)p~(-C_c=4fuqf^ zdXyf;-TW|%#-FGJ&9R4hvzM;n-1fXTWnIDhob0+AE64D7>g`Zbx+TL|J*^}2TdC|f zJzhxr3yOiGnqtJqWw$s3`(;gH44$Cz8dk zUNRK)kB9a|EeI+HG~8PFQ&fZ4-x$Y3WiEC~vwFZS@}3$LrT5ZGUDWUwi%rsysIEa= zOMS3?^de8d*GdHWV=|4(tv zJTKYY7ZfVV;pKY;!Jgk`XV@Dj!rxw;Ps0Ja@M_6V_#XW6%5L;f^>tR_+)cK9`rg*@ zE0!#x$rAiMUHic)QB_+#2=C9l=ycl`FJHc`?dj0ft!bJsGCa7S`eAWWZhF7F0Ua?< z6&gr`PQ6%6fe6_f6E8|ww_tg}ji{?mu@ugzaMy`7WJOKWk1by==oAivmrEARPi8SH zdt{B9uw}yH1&zE{`ZUDt&&s2#Kbf4T_n167L+Sh0ywbY32M4UnB3I|S&ZWu92HqY#5mj3x%kn-$S73Q|k~M1=9#N~ixj^TkXIVFZj#nq_%O6ymt4A5@#sV5V%5T{=wjWT!g@oK59WfCgP!#+aa(8n z{n1sH$E-eva~UO#mMGQSx1XKeefJ&j-{sBRz&o@N=Jzkl{-o%$_PGRZG75J}pz()Q zqvxIH8qD}K%LnJ{jw$MMzV(s|FlZJkc<5B--Dr?AYwSFd-ZY!^V=e@~N={35O$#uld?_}AlbF8tepLta;4QM%n3xDy^ z%gzwEL&j}vR}F%xQ3KyFkaJ)$K+2Bej<-};;(d>zSc-DNPmD0H>UmBoNwlNXM(#X} zR1H<%+y3zBq}HOD>2fNKK=F^p8%m0W4BXR|k=T2k!A7p_*&62h!XhsZb;=iYfVh6dLa~tk?mt@TpY}mqQ%_A6KggHo?!Z+xH>v8K&+0nc- zvM%zvFU3f>$7_jLi9dc8%oM0LCUtMSK*7}IkgKJ^XJ?wXZ@7xkqQB5`E14*!h`)VM zOMI#vk+S(+;ma!ArgTDT&NZ@$8KZ~ljCW0lEV;sKPe1%pi(Dkgzwle(Huu%r&&=8u zf+z)hu&ij%+&<{ofZax^rlaCpl}9)75}iE><= zMGi(5UGa9=a`xMyQuREH-+63F z;-D(ftj$MXncX0%I$?de>H6kW>Y(NBl^E*PnUx{$VXx_>suRC3(9oIVc(1GSYq=l* zw{U*i5C1vAu*;l0vi#(oyp)j{CY5o*HFRr+sA%F)DrMZ$m51cLHZA*M*9Yyx!)yuf-GJv+1{*eCoIyexUOMMVCVP8v;tZgQA z_1bY8B^&o^KMZD6k&Jv@QF(VxX6!;N(|h5tmdppyCw>Eaa#E&)&R!WDtAcA}%Sn$O zMY3tom8ri<>>uSyevD7;rsB)jjtF^z(H{+DU-MWk&d-X#o{ccSO&t|D)Nk5Dlyq}% zyLqR^rPZ@T<`WPyv(yfQVblVQ{aul*253)l6eMWXTQU`Ir)RoRZQq!uGquXwrNf)t z_T@taU6m#U?QBBKUa@iT*qGf>SldGEV>*M)&6_^Q*`vPZ6xdM?366`^uH(He`>AQ) zCZ36|-WArms35=C+JaQ)sg332_Ug!{o>U^1{m!DMFRRE2<42Fc(dVhc;5M$|CJO7Q z+-1bLfjmv$Os1(6qdaHa-1{)AFNSYZ(4ROjXkN$+r7>p;Y-Ne)5Q6W=j<{gY#!wi} zdp*10xh&5;<&biqb5+@QdK`I@dFT3D${BU*gcH56Gb|@}kkc5toqeuh13@~}(!O{! z-UY@qGYkZnxj5@`U+1!=*Q59l6l~w{B;UpP9Y&!P?cK=|pS0%fl&D2lv+u$6t)h8g zj!5qkyX5;$6X4Ua?FP7)p_mW>M~e=F5lG5}>OgQCZkHJC4aV8n&2hk!wbfT2+l2uC z!u{S!IQB7&5(gkHi*Ud;JIv{%1`Jx!Ah*C6)n%|8uF>J!m5}X%Nd<5QJi_;v1%o>A zU{L3iXF$dNG!DpCYeis~R>A$d2y^{@MVP=}$ZCJd>b0l;w=4J5A?nKdi%|W45j;R& z^Y2z}M)!C10iz6muX00`|J#`VS?BnV8*@Y}pOuHm?w?_W$G5U`i?K+i6+mLnf`PW8v1`Hz?ntK`_mhtd*s*b&7lt-5& zrKA;$zk9t-`OLIIMf`nP{3rXeN!9Ad4}H1l)ZU|NTtpL`4D@S48_!$2y4%|3j4KgL zO<$JYn7@{4(Y2+MElI^K)9xvL`)#?YXubQRTy~5?_UD59r{~h0Msy_?f?C3Pot{2< zK0&Fm@ol(w{g~=K4zLMG6E0id#$?3&aO#j;?~t62wWN1RiO|!2GsI}w>u*2vVlM&D zVTul^b%;#fm~*LVDAOfVzFb~j%{|#lPl{eBJliBTU~%%HQF!(^3$G+2Z|w`#$=$Jr zF>)v8llB*~9@gq_Kcw2!XQ&l{zzE&D)zHhy#Fsq^nDe)`1lQI)v8Eh0Qs?9B&lh$H z2|g;B@G~wQ{c`wp{|=f!>Z2>c_m!g$-@GRlD=zi=AeTNXv24WiT!7rmPBMC&)EP^< z|6TJV2mXj_d|?sVRjcU(pS=IRTlYn^fb}qh$(p@mD)Woc(z`}c-BT$WcAm2yN8)xJ zq1>Crk#o`fXVtls*xZBOz6JDkKLV6Nw^r;*pzlxjssA^(UGnRB-t>39>Tf1c|HQe6 ze`{@La{n8;?)cg=2O)|w5y`5`0ee5N{v*o-m0v0_VKR2L=qW=MtxM+I>=6lf*sul} z#|jhEN0~{vtJ>k_usy2BS>=pT>#ke(1~Vx6JU`+{$+6Rk(Oj0&D$tVs%48C` z6lzr$&uQq55bo5DPxCoeh*YpeP|J-TR2G|G`%vZj`srPh&!flv=l4sy6VTiTtyD~C z(|M_m4COhYA=vR?AV^-VZW!)Z8nx=# zBd9chrLgyit8idE&~=>XGg#Yq)O0ewHsju8yr`pnRNekaxCQh~snD`HcUJB)8E=+) z$@EMWyfyD~@nXNZ5zaEVIU96Ih7A%FDSumEhrlm`4uS?lk_1Fx@HY_c)o4bvA&WfF zNHRMoJ66(YpKBy=SZ?C`xTK0qT%da~^xGhpsvg`&AK}BgSxUG9P@{!$lDX`w3$CGAi^IUpI|&v|yTuVduhHw%)|T6O$;+Z0On_Kp^g6mhZVU0q#b z*mOHy>CzC=>#Ko3{sdMOIe3vv`%loCC@}*#>55;`3Ws2*4V-FCL?dsF`e<=rfxA97 z09{j%%W`mir#Et%B#_1b&cIU@bnGT><)-&9Gs9=$?)=LHX4NW@(G;~^{yx=0Ik zfmn=h%q925+I1b-;RO>euI zR}`E?cx|1il`cb9yTAjL5dOShCo6ve1yX+Sc;a8rCmnX*IVo1Kou*hbiHN8wr4Pu` z8QA2ls*72d>Mgfp>pZ)uPWn=WTR>edUa){7xY(>pl~U`W7FLxWb-R#f^|RXW>i||2 z{|`8TBK=qb+`j`ii=Q3i0IXUrmQ`vW+*W%9`uFD#K=UKP3^69Xb%;{H5a58s?vqk5 zl#tbr13so6X6f{s3&UG&1AE}R((oVhH~{1TW4Ga7Pe50E*$oGDf!jDZfV=-BBp%EK z`e7f<4~h1hXt8iL9DrxO7@+(&g;3k4l*OLz?Ly!SunDyG;Ry^w0)MebLBD-~k@Q|4 z1@}86u(v?#>X0J=?X)#sijAPc6o7`*wjAsq@;`w)&MGYJ!jZgK+aYnoRDVfyFLHEl zYd--a*kgHYdd&CvSo=t>jl(<((TxMl*&fySIv+fUeGnUa6z4miC!C^8bZEkLrwrVObdGGg4b7MVHW2vtGm1#dG#VBGZo1 z_!BOv%WKZbjdgbwUAdS%T0SW62cZkz`b#3?K!>&4Zcl;3r)kAAfnp5tJyqw0#!enI z(bbX3e#s?RoSUua{U=w; z2i_(;-Y^racF^np@u9FC@B|2gUY-rZ)6dvue#T$fc6N;!))iIq znslejPmSF2k`~u`Vv*>&VB4+zLgyP~S;QC^I%JNOfm!p-uAL`(;nsmMt$uy^^CC*6 z?IRtc;UU9r1BI%kmNjS9X?1k6Widt=JU_@r)Sl*)=KQ)GHlvBODiaz^sI=Pe2~c56 z>}KhvB~pVVKU<*o1NZqAsZI~Y|FJ1k`V9Mb=f!_LXnwl+@|QDgh?iS&;D9&rhvNWC zLeTHZ^Dx}CHvj6{Np&bBuYd!}LiEAS4~l4%H5ko8zdzWb`PMiohW<)txg zg1x%uvFr953rRejcsKW%fTthNAq+C*L#^foq9hg*zRrid8%PSN*tUH#FjPaW>3Ah& zuw?wKjq2T6YiaKI%R>@FFTS>h6G1J}V;TrvMAMs(3n^&vnLL zggdyhO=#MsC0C50rEb*fmF6#B5_#Z#7FOq7NN;RjfAwsP3?;zux6wjU2Ii4zG+S&FX6M9EMxM$D6Q6TIT9V&%1O((^=I% z4s-L0wN&FZE9It}GE0j@a%(GHCcvRGmib@~5sm{E(*3j|(gPr7HUTq>kt<(6itF@-;ogV3t4uO3%93BE4cm-r1jPVLMGxHM7NJTl% zFPt`W}*9+rc66Y zj-rT5-xijuvAyZP4%*%1udS&4f++y^z%%F<$oWnb35H~@n6J$}F=N7cm(h}=E^MN~ z0a?m`1=q)ltoS^sy8>v(+2ghH2m4P8l-NJX=lJwn!$J&c!2JPlWUJ=V$F!vwe>!+W zE;{RWJj`|XQPfgMd~0`S6%b&m;-3c^cT)4!5!*gu^WZL2>Gznn%ks%ZZ;n3WGl3@iQ8 z8;(F4PLrYsb+j1G&<`XN3K!m`wB9L;N_@5@Ekz1iWF@ilt-csGyNKN*SGFBxbw zMj6f#HJeAVz%ZN~7m#Purv+9~w$(%8L1HJbTxH^Een5g}Nc*be=9G2MlEvFVsL5D z){+l%)+BByU1-xe-^19mJBs;!3WKckf|bvwxD2&7fjx-IYlzB}7jsn1%Xb;F)R9^l zm}--A#Si4oP1Ko_OrIAw=80Zq0`f9q*gpp8hdttJGl$wCX#EN1U=f*T6M6>y)hygC z+(bWVMs_intKQkXHAGFk7$kdH@S}ot0`r;zwSp}iKc!f zC-eA6R642@{wKHcziL$f=?outl2Oj&Vy)JKz{30mKggE3f=}sw0h)cLly=HTs_pWR zA(Qsd7`i^?*QN8$-K#b%?6#b# zpIn;0_{qC`ldRT1muH`_>2WrdC^{7dZ-$WwIl21VO+ULtTCVutcYpKy6NIioZr zRggJ-sXVp+^#P3bQdiooPh3Me2|St(82MYAV&}X|j};${_JgKO*aGTnUM|KPc_zcQ z<)|Zz?$)=-5WexH(3+!}OO{>qw;GtN@42~WP>qwdWRjwr->kCm#Ws@UYg*(OU1Qki zsnf8o{r2|C=uQ`-7kQgNsf;u<`UoR=F6_dq%Vmwu_CmseH3(BoZ+dA&(Je8Dm+!WG zJ~+Fi)M&y89a2diy*2y(k@E9m;t)seLmv0F+kzWe;~{Mx-Nyzieari9b_PuDaWnc^gDwX(b|w^?fGeHM}{;uZ@n+o42GHt6I*SLS(otR zr8taTpQyLra%*-t67YSdZ>Z`pp;A)3^T6JEGW83JFUTdUDyFGiJAY&Z>n$D#QxwmN zXp%$K$C@=0_sNXoz2S}H$I_pwe`;d%=r#nPE!Ook&Ob`z*qObiMI*K4YWtTOp( zWi??J+VS7G5vktOzUOGGl<0!6?PVNS!B%(Q?Js$K5U=if7WS0pFnR|8U6$uZ!J;d_ z3pW>X4NrMGddEdGnNeUX8IYHnjEddMB4AMRqdLPWaT6Y5Mcx`**qp@%8s?q@g<{PDQj= zv}BIGZ7UL-rN-sF@2VcgbBOc7?w8bPH#K-o+Aao0VS6SD+O$Iy&d6Wz5&mdSAt4Mi z9h;h%tQ<)Te__R`&f92I6Lb8WIW}qs?{QEkkGpy9Q9%sG%O1H&MZeMnZ9t%S%36A7 zX9FzR+g#T1BS*EQ#XU z@&4F|ABWXc=`|)t4AF->;12ejc3hx{kF7y<1=<@q<}=V`grD@C-DXm2JMUX-*Y={S z&7q=mYrOlA!Q!hq^C%I2nV2>|@8J1*lw&8YeCW|@-LtaNXZR)?t7F&oOu0nHKCY$h zzBK{1UBd$f&=qrPLqu)8kV&)Uf2bHofg;pIJ_qf>G$Ll_ zS#0C1`{TZ;Poi~MB{9%JZ1Ytc%3Pv1Xm(P#fC_zB(?o%cm1mM_;HlH5HkVs2+Yzxc zqD(1!(TI4iYDH@LV~VK9{A#}L-&;D_ zi{l2$H=4hFEx6)5&v*AxR;=3W``YTkN}Bl{mc`*Mm-u+z9l&)r(di=Tp)vOXu+@?& z@9?v+6~a?t9Ovo~p!v{lF)qCHJtIx09g{Mx+N_n^cY~h181loMsr2;Guv;>KJ$1v5$x}!~# zi-!$O=KLm@2KP~KnPLNTP{Qh=r%Kw997c!HW|HwdJHChBWWh~_$HvnecT+VLIh2tD z!U^`?{?kk){$g^4*N*x7?tO?>I(Kvae0_?dAyY>mrQfLNzVJjPy7dco?5AAg;IYmM zFYY%1AzzN0Y$s1e>)1$_dQih=*jK4T^aP_>jy~}6zCTUml|W&i@`}x@If#(I{rYPt^L^GmW= zmn>dCGa zp#owvB}D7v+{Hao9)Af-1{)@Bl7qX2W7HOlrF9_dwg`@%#R%B)csQ&AHJ z_jV=4l&J!2DSQ*l(2gcnAJ#cUSe0f`ZzPQN9O}dCzV+w{t#jSlMpoabo^y;Vmwgnh zeEA@=s&^Ge)+F&A-Q?`iJFoi6GUv08troAi8Si}jbl_G6$<+6J!J36B(?FT8zNN3g zv%Gxh7c;JrcSQr_8YxgP9VF>Q6#kl}MgG*u>!$BvP1COOJg$i>Akg)wsAsD zY~8bD;AvC`sV2@O86BU<347EOcSDMrsb@8VuL_r<$P>lS-4E_2gTqG*IXzpGfMG7W zvnnry&MWp=o$?{quzxeYVt{P>k=!^eDfw`l5ugX^?ud3h4Hl7b4aO=AHJU0!ZeYcI zif+D7S=%7Vn{3WGN#Q?mIlf$(=GbKk{n4%78~;vQVpj+}a;-os&+0f$eKYGM4n@?) zL1-#0^@WA`JQhyFad9^!w|X8$9d~Crpe^8>~2|~ z6vNWm!d2t@g$~qSa*413Vo!Vgz-?onnpSw|<(k$Fn|VV7`srLLGY^u9prpawQmE0Y zye2Ki+18~+sm6{svXw(DSY!_ol$KI8*PTFD+HTIWg8FvnB-?%@4LpQG11N1LiP?SS zo@csvQ+D)p5=aJrMs3E|mNz&Iu3^F?6j!>m_X$>w(-WjzkLLO{8f>OhnL4!Z49CG{ zOZU}UC$l1Y=87Nb9N&1*;pJGgaa8Wxmhsu+O@pymsBGXJ(%pI!0HqxPBj7?mDAq16 zh{G7p8Ke=0tRF3wfPlA24nMXK7MDmLLr=9 zbJo)FtncH8n1+fPKBmuKCSWZaf>+USFXX12>)j6(jT-Zh)_U%5&+F^DzkI$mrR)}4 zcSu@$zT=zW>A;WjzYIoq6T7CfpmfOG``rNu&(5(~W~N=KxZC@qBE zNvHurJoA0mUh6GupL6y(-#*vpA4#tH%sjJ?agTf4v!3>vaj0VdMM05I>IS=VG);F=Dj&Cj01x z5EH@PRGp{JRZZ`*CR2%WWtj2HXKrPs`q>4@;f%DYdka}aUsQ@j|CJ|XQ|3Ta4qDdL2 zTaOTAU)^i|{@Qyf;=KhLc>KA+-AiQ}~|6Yl|B{CXUJXgl$s z=IMo*Mt%sn$Jz4T<;`giFE5$ZtTUg+ooxcdRGLt7jl@s6i(3<&;8i;t=|?~JD`Z?6 z&|4M`C|47BVtToSBg|NtZCx0=Ch}zHF`6XxtDSvLuo1Z*^K?m`;4JyB`f}T;4}&F^CyUxE%WA|AMcR#vxWFWKQga^s&*#LTZrleGAD_@mpUHya~7~8asM~klrYG%i$6w@jZPMu3Jzb-b=e00wIv~#ypqfmpYEjMb#LTR z&BL?{RKeW&LBDzpp55~bLvucomo22Eq@|>=B2C+ODykAJ($l@AALs#e^`bg{Q;FI< z#u|g)SEG?nWx5t$AN{&6Rg3=Q!J8gwi9Vs^Y}W6m@3&YbUuLo^-?t8pQ8#-><*5rK z#HwGpP}Z}~BObk*&${nSS!~ub`q7*UI3w*C)r7pzj1#BkjxF)*LXng=7Cx`uR#mWX znQHqTvF*=RY!mUk7R~)pVlutjX$H@}&_ySNio}!r9%7a-N4yOhQW^t?o%T-MOzp03 ze*5E_a;ls_cx}>=i_w$e5{nSV_n|E~?ta?&U4>wco(-xYE?4sv-(H`@1v`w%otgvz zHO|4?SwV>IzL$%CmQ>k>Px#T&ITiuJWH%B7xUCC2JD+f7RDd#w9^6$$|M0W@1?dZ< zKhP}v3#|SRO_twP?34hr?uFA;~hH{#NALrJ2mKH{&a_t9t8O8|oOMQ2=$U~sEG1|>ygI-Molng*P6$zjtNu*(dwNCcf~`VK z9-{f+gAO%95xSm}-h){e{RNr247wlz}A7dfm`=2EaDeL2r>8R`)ae4pLxda zsFEg&^0B~pu_sG|Ju2}(JDW4PsD2c8IQ8L#T4fK>&>*Mb!uG16aDz09ier;{T|@c| z=Su2QupLF|3zAL!zz({>(8!Mx_s~l|(7ed~R-V-Q+M0xtOhMkIij0R|AMA^*e4tc& z=%5)L1XO8xH_@v=AfX9XlDh{sk8NW>ESZd&z&>qrvKrdEEv7%x<&Jyh!3MX_gGR)~td=*OcYp5m z+i*04y_U-`~r6x`Q#gn!WrLf>DAz7{YRju9wy*1i*OdB}F zv~`C^LJWQQnFic^pxpRI7~hCqzRp`czQxsHndd7~<6Na>5&`z&ubGFh78C1j!vKnG zzpIb#0wK2~CRV!cJ343c<_i#?2H_krb|~$+mUX98LQ=scrFX&IwK#I_!)9^Y7lW3A z9mMAweza57CN=#V(8$5EH)m6qH*I7CvNUYVif5#o?8&u*a#`C+#Lzzebp4P5g#2GV=F?%6xB}-+iDkMQ(;8ZpieYT!_2JgJz)z{L?*+~Qo*>lS0ZzNvLu`VvkQ>IT z5C~iL-{oo$-!OkA*Ae=YRxwR0+se{Ed+GI6Tl9B?^?e(T|DF>C4Lfbj_-ExU^|oY&Ch&b#iwI zm>)R#?ozdp;`}KKE3V+5EaS2q_s1ME>u`_%nc4dPQE8;3ztmOyQm<5Lk>@I(8vJ8y zPDN$K^V{K^U5=SLo^01y^hEdLuAn2)r}55=u5x2MdcdrM=;~Yh4{j3o#I|9P-r_0| z-&rpOscv6MJncDl0kTXN3-+t-rUAyt(fG0HS0H#IGTiiuM|R44kN=3~(lqw|t5pGp zY2ApGoZXMbETqrmps|qh`hr@Wrcv>J2TDW({P1Q{#(L5i>msW-T@23Q zY$-`hP5TAumR9eSa}s1-jp6s0vz@YaqKG%p8OhBw0U|m5X3^GWS*?BUTv}1jL~Gv+ zaWg>s7i7cHw|s?T+L~hf>f#Y(hhGyo&;AmA-;u@6C{0Ark}=R&AwPHfqL?rmfB|Wyg&} znsk41svhKW{idzWD5`NV6#pHI!lZguTjv_+X_=fgK$scUhNv3e)S^e8E;#b+8RN^b z55`AYl@Fvl@j7vFz0;F#BHYg|sPat0i|3)N>XN=nmeea3s}xh(8N5|-`K&A7i*u(Rb&p@& z&Cz2qgoK1|9oHUL5xU_!%LIXw%n)rS=ncqEE=_GdWV|*$iN3zoRl3v$HLs{}7Peou zPW@iBmyG-p<%9hBxp60@e(5r=>RbxPDVI^cDm&V#A2u!1XX>Y;P|my?%gHHaH%%GeH&^6 zdyO&ZjjmU$WbHliX8Jc66HIfW}bALuv(7hbM?SrIT7h&oSFhd8nIlsDB=kYEvk%$SIhmq+Z}olZ6s&J_t0uveAtCih^(KnxTySUk-jNXU{y<3zSy+W1f1(%IfWoXgkacRW{t3%wqbAEqZ=Z z{w5Gccnx8M?CaVG|1HEAci(KAZ4OLqdJ?R5Q&S?W=-UH}@OA9_f+^&XJ>Bqg zL;K!P?Fsx!hV6Gi^}haZnYc--xqm&NHZ~lCb1BtyBc@I7Uyu*WwZ9;F8+*U)PJkUQ ze7eN=cbfR`f9R03PVf73;-z{s5YDoaj%29AyW)bNRIQC&W?zjco{og(&>v|+c2;O1 zPZ`?`qz1jq_9O)s%~+47YTp}O+l4k&DP{+KObRX#_mWN%bQ2V-4!_vC4*YTm8*Rsb zrEUwSxR*^cB=M@ZJ8tWU6w!wQ*+wyxo?WCP(Q%x|K&`-8N2aRz=*6L% z_wU` ziq(I=Y+ckUj=M9k5@L%9Z~i{u9}$~xl=dmM2hp2ilaiC$_@Zl(9ssU_>MO{Df`7iWyC?Q%Y3A0T)(y`Fp)dDf)U z>sTW**<2OWwMe^ZhXzEff?)FJ)~148uYU!ox`sGv z^GPdpWWuc9DF41$uid>Ijj{>04fn-80h&J5&T)*$KV&gXoF#a{9%c7gKlBJVCMlF$ zSn)lXqFDDOBloPzAcP`5&3563{*4|ZNl~etf)NxEG3D2_ad+$u@`P!0RfyW%C8yP) zj<$RXEUJcS0vnzhmP#$SR?#i(py`T#dAapvi&~1a&~e=}^c$rV6udkh`GF#wDq-x8 zH7e_;mF&AAl771K2E&85(<;6XzgV;MH~Ht&uF^&Nts0x`i_%_&4$Xk<#$D}+Y4@#> z)Fn-UBRb)lJOSHajvQXE=-fh=De&QQ#QTg~u4jv+`56tlhA1rt&{rgN8`Z=${6>lS zTfl?)GdGJ8v3ZV+Z-N~krVhfx=;|NI-WPISO4(O;H5EGDQ3~R^IWiPrckxV&qF3K- z{Pj{eE6t>m>c{dp+rO@(t?4|kxJcQKPR70Em}7AgnmyHid1qMFmpTIGkP#lg1(5WK z(tE z`p&6YOYtU6Au4>r{iy;9g$YL}pe03mamFTi0rg%^Nkypo| z={F>xStLhOiN4fZW9NjL5RMZLaV?2g{F!0R#p?`moUPq2UI#4_RR^?fgSRyp@b;n7 z{s9{d6rE;8O&Prgv?F`!xgO?9o1v`}e!stb*ihW|L_#l(c~|GsIMv5SXS_Jchcs2Q z=FrPgH-6r>H}u;6h22Y0Wt}e!rZPiabF(47ba`qkxs%jXnLP_V?z4+#ZZDd=*`9aG z)!9Q}BT_1BKdVy3(+W?OqgsL2O&dKf1rDLoUNb3iL{TCxfKrIRA zpQw%$(QJjoio912i$4`!-QIq(eReT5|>SxM|E=6P5Z)eBqhZJgEIFtOt$Bni}WaL+PgetUNI%42a@71oQfpaAcIW}RJp6&FlQU)V}XRag6!(*yVM{&tF@|5!657oQgH9=KE`_=-f*g5 zb4rP9{`Pe{PMd_t(r67qI0MYNrXp^X*Is2!$k8@)V-i+sGmzo9D-&#qF+b~yCY>7S zUn7RRcz-(R;~7>jYmPe1Plio2c_o~tQLunyEm?v$2$iqLZQP%M*) zv7GQ&ogteh-#hgS@-iZ71!00aJaW`I)#_#a!w#WZYyQJa$QzaxgEy^oob2~oSv4qT zdE{Na!S^Tzq7i2Cnl*v2Y4VX0ElF5nm~eF;k3Pv&vXHO+>h+{|Q;OY`mRP(&GG;h+ z$L|uE{+Q${LcB!oNOR3{PLBS>WTk96dN`$9Fh(qIbARdVKNy$)jnbC?@M3Y-_sZ~- zKZ9+FBZ>O%yEA5`upZodY!ceFa~p(Sc*?gt2dSY#%z(!XI8+SJOF;cuG4$>ZEGn zNvfX=I+rv8o0<(kaDUq}YnR@6ZiHS~%yW_jQ5xt75k{*JdB6c&0!V1|CT7Ghi2fvY z_8UH(7zh0;2YN1)8_kb>OY3|xfgkJLKtHd38yrCKi3lsAofuc+Q)?V##?S%>7uc6n zN|&CB#E!oLH^gZ|#*TTw(eZTovMwL#!~TH;c$d0LfmOS^W}2gG*&(?H{HCiViSiYY zL(VLaFP!5ws!@A-Q~tioW99gy-67QWa%kx1qpemMo7=v)%~j2qy_9#KdPAk|YF-#n zFoYTOl%T>2cGjp=eni73%%Y`fKtIlqM3E!5P zTE2Vg4tbK*-j{QU>E61aJBeV^x_&?QHeaY~k9j!?-}maa+1tFKJ0p9pbe)eh?Y@bA z1q>_AF#K@wc>hw7;dj|b6rMeelT0tOjW0E%v75?eB*cXX4$$RLg9CUH`gpAPwcadp z_*eUb>8uAJI@0OyTZB4^LR0#Q33{ChQ;}%@WZAjs^Scr~LRcvImEbY9+NZ~~b?mRt znSHq=l!ZlQ&w+fJ*E2C0L(!!hp?LC6=10mYFru1r)NrD4zr3p^pUeEkSSgHKx1sm) zNqGy|m73Vs(vUS`R1pPfR#Hg;?#23?X%L+0hn4e*Hl9Oz4rQipLfX8igCdfi#l63T zRsVpm>6Ox&dXmG{Z*zmN0+}}++f)`k;HetJ?~-vt;`FWY*F~H~9N3NA0+AdsGgD7L z&%n?B6}Ocp9t$RuvlSg!Cp{)RGe^-aI7}y)RJVcRa`2n=OaCY~v-Nhw1wbZ9wuLYaIRjDE12`E)~(7h99PiHnIECxhdD`pDs$- zy~61B&C<;P=BUvY${>&$oC@eX*)p#2 zWB7(%^QF|!g?D!lPrcLHnQT5s8~2tTwjG@w^gek-UN#@0qkTa3gV1s9!`OtvP(GU9 z9)_?8Jt$I64IAS`PY~-ZrzR}t10C$MxP9~51lo>1eoCr_ynXQYeg%Xfj;gbJ;_d*N zkrqTk4<4F_a#4rICqG+L3(O;wotaL#B*zMf3;Cw2!_ExS!j7^b2zW=<2pG5KiC~Gx zq?9H@FE~Yw>T6!wy`iS`vQsfR{bGu<{9X2&DN{ULU#<7+nYK5%pv!nixd%#sDinUK zGj1Jl3_I1$eV<#*5u!bWOSA*7$_)`q+}IT)jI<%G5HCwV+w*(xL&eYcszXWVG+CWb zjp-HoKMHb%-@FNS^j?Wfl;bm&SP#$3nI9D|Toelq#m7%TW7nmBy4N=^-M5B26;V5` zs>HQjDR~9d2+a9}`*LFKZ?08*5%E+U!+aEP!=0j?A;*6m*&o7FkGxx5-RFdAhnwME z47;diEW~KE4^J`(#l}D&H>}0P^7hy2|E~D|w*s-dMv+@YockBw#@s%auhld#)u}xpq=46r$FY*8@peS*3*9v zbct_;C*xA+0@`ZZXSZN(Vwg+fOjKj!Og0H%8cPqk^NkUc&D>IL?i@Cu$Iw4PnUHZ=C%B44n- zHHvMw-T?kb6Oi+Yr?#`Vu&e)R^SMI*9Oo0?ii(1b|La#D?`hYe<#8Kyov*}?(qH2v zk%#dlIpF(5yaVFvzaS{IQ%KsLz$jX-Wf%U6{#H+MX^msWUS&dAdABJc8K;LUcgXdmZAg4h8~H-LN$g=IM`HUhuw~Xdy8a%HWtf7;5LZ3$p9yF>M;F_8)| zDBYS88zb|Wxp&?W;SPO`j#P0(k?&bp+a-#od{jTMjDao>d zt?z4Gil0bLUrW*-&Z342zSD(K^nG3bh)HjDO99-^XMsmUI-~;gt^W>j)0yMU_PvUG zdNhT(7k`M#B~1~5%h616|N4=tZF6z%wgy7Guc3`2-7CvQaf@!-(@HOIS>1A)8mVTE z&Fw3g!44)~q#z>cEH=)XM@uu;%vC!hM0J-UPrfYjsXjk*>k_YoOtlisn;QaHxqr`l zVhL)%iFK(%bD0qhDYrZNvraIIb*9|X-DfN#3dv-28N-u;gGGV32S}3rg2K=htcgpU z@9At^{a+ki@BBC(cw(x}77#-Z`#nL=9dHzZS(LGsA>ZaS1Ejd=n@N%){tX0XP7m#SPNzoS0}lY3t= zn%eJ&AxBUR=ns-%RA!D}kW)IevviP&@>)2a&H#d-4FjWXY9Lkj1)dDTfvb0H2_oQ= z51OouUS$N|FwKx)dyG1C25U6sJ?w9-u_1!WfHcm(D(n7RQ{;mr*65+8pz)v+S|dtZ z@iQYx8Vi*6@GAggx2Yf!e+fMEfB>Vp#8Z*5qp(?37?~TOD6xnDP#5|M{$Jf9kFJA~ zpga3-f)osSB+Zcj&+l!w1x#Xwh+mL1!9f(Aam1sI&RF8X8hAFcv?GO7z2s)2 z?epk<*_=*9A<;%SEf z_&jlA^tYBs|IrdC7$KcMjnJo#Uyzw8Y1)1?xUj3(@E1fA$ij@>2S3De>bI)UzgGno z@5FU<9rZ7rSdFJ%p$P-k8UrZpQ1mZIFav2;dO`o$)4X9JPJzizJt z&*Bp8=l#?{X4|vLcmF=Y?^=vpvns`_IM0&aMfuJ_VabxAyF~)@X30-NHwnU(%EpIK zAVxy^-hAR|uE}lJ_9Ndk=bhE8tQ3j1GfKxD*Uw$gArq$3PuEVTA#f>r(qBMABKD2? z@xoX$WLF7HeAnHite3VoiX0TtEnjL{s&46!m5Y1DR)iFx_aWDSyzS5my&Ic0PFoFd zvqA72UQ-^@nGwR94U@jVS6*~gmzZ)CUzknXV|{}sX_7_vjs?2Y4_)~MX*2B*7rDpKyaFCN zUcd?_b@rCv`oeD|(SH<#k}>`q^l%L&u)@xQ^-!?J4$jzrwv?3`b`!n5CtV56MgFZ| z9#}ZwvV(P*IXsmM)Xs){Spxd)L^IfnE>d=Xpc};Me?dCuLR$7ze=mypqo@u|bo3x# ze#C<%@TCP!eNStA8&8e9iQi$2AFZdKHHXsC2Fp5gguj=Yz30BDU~m=JD|NfNEJaN? zY~ZSE*5h!~OJ5H_18G^ss@OattiJYds{2bb3{$I}>p+o^npz=khOLhdkFpvTnFG(#vk(%+2!U;s&F z20`IdI-fdW|J?I}uH|lvp~t{9nKTmots3GV)vV1dAt_8>=^A^fIAMReIz)Ru%Cade z$kqAYvupg$nX)HNf7+Me&n1AX`rkAr%s_p?G=;toM!&{)t>9ljcoG%|W1^L?7CrdE z^$Vg?u>eRi^Khyy3|u9n2qW`PfU}9<4$h{OF!U_5BiKhNVDW;(Ntguxy>arOar|xS zf1IgyOR|{Be6Y%OKpwSn{BKpE|5a5tIAZHb?t6!t=?ueYO4Sk=_kVUdH|X+s>eU)? zuvAdeG)B0d)_gQp4DvVkuY zQs@j|d>X(HgC@ZK(FE{;^0%|P}##D>)ne?P5>wHo1++mZdBYh5( zR+$HyIn}T~UiQ#$FN-}z-FiKn>i+0;MQ_Oh%EEA69Cyxy=O9v0Y+qwQK!=*zS38&6 zGpdG{Y=;vOz(VL){Cp&7{NwTZCWTXprQ2Zrv|zOVD@2-&t(g|6`QIY#zv;{VLk{*& zxu5FQs=N-mAl~9-(;!Ny7@ll^lrQ8kq1qn*Ge}gr_X5#Nkv-syW z*ptd>0-K+0(0BX^+Bn>*k$N@fF`RdN%Qq=)ojK&?KlmSP%_ITZSc zDSAk}R*Wrf%Hr+$puC~MZ@SuM&v^?GnVkDQWm!uRslmtrP5xOZtB<7pcy^7SEoWIm z*NBe9Are#C%jEs`ki;@CF5dgvLB@>P-9R+=&*F#s*0z?whzj7sfH9rszQ-XM!hnQ@ zb|Y8CXOWv+z@qQ=By=gPRw8M!9+|-CKM#e!LbqU#ESRl5MI`%2Oh6y0%M@DQwJcc^ z9x3i3+1+6XjNlZ+9zf(18tG^I+~o1`s_i3XPeuhixLH4)Ydtg6yk>8|zJrkFndx8M zk$1Y3HKV{jY+ab@wi$< zpz5L_hRfeHwWK`ZM*Fa)Savc{%q`8hXE{PWLQ`f}@~LrPK(Q!3vNO^Z#gCynN>?J$ zCu&Z;pT{xx!^WNSZ8?E(xZ4wx+>>Qp`eI@BKaTM#e{Cri<8}D(L+fd?Ub~8XHn(S# zIYO@qp1L$5ic-pnFtp|Np|HfAI9HNbl9M%BRWe@W*zZm-o1Yh-k$##f3J@bClmGpU zYt|?ZucRt>5x!#GmRRlY5LPZu(PEeQs11A+kva71c!n1u0`Z~p)$Pg>U2}z)50a%k zJRcuiycg&3%**B_PWheo4MA18QUwmByMto-^Yo4N1=NrkhP5uqrHO^jZl&^E0Y0EP z$eED67^0*VJT!rhOpPVPaC{wPDRn0e>7LaqmR>$BV=oYK{o=5<-E82hOq)6zMb0wO zl%OvB-sJlG=7$7>_XdmY75p$GyhU~sGa%A93GFHPsod{x+JQm$>Y#>hi)b^Xx0Eo%3PPX%zE zWd3qW+PKs!=8YVzyPdPMW~&?jX;~vr&Ii%_!(z|(hBLD^+o%>f{#i6*idarbfpGSK z`_SSX{pEq8ihaKz604q}wy&_7ya8hU7qH9`7-0mel+EYcXy%pyDnud!o4qSsCzE|l zr>uxS&}9=5;)<;%d~Z zmWu1(dy5NiXDr!Hi^e*}kS3(EujDTxRQNYmO{rQnP)cXLucwRI{HKv7H~yf+s>`}b z7mW0jnQS{x&Ad!M^={Ewl&eF=)7{NkxNK>2wkq?ER+4d}7;eDVBPLbP-7#d-fTCIV zHcEVfHB&Pby{)_sC=W{X4%967_BJ?-CVJ^#h`Tb_*QhCfEBbBx0+5`#GDl6L+u;}b zV8-CV`S_Pdx1~3M$MDLDPDF3UhBzTbLjPrDc40efz=8RkMeujfbvz^U`6 z04>|HSy4)<*TXd}FQ4|Eq?q)#tI;pB6F2%{>t~V8>k|P&opW5(BAvGYjX8vx2)qKY z!ngKNPbu*a)b+;9?&8iwDYwsh@i((#quFz%E%!b;pexdbHHGwpTD9wxVz7;$k`!AKq<t?#VHUUd%X=?FE_3KLDOjA{mrS2xVj z^YY+pD{jeouD&wLrc2-4za?~R%tsX4**A9VhH6Kc<+GR!v8+>)Pe zo^7NC^xkl;GgLWO#Qw=;v%jb;M0$9;?NHPXM+5&!vU_wWKP*1@L@qAL!(RE(cwbA8 z_{ncI>>An|XJU>yoJd`oq&kwYj92WkZK&E?jR>U^1tTL3%lRklALYHjDCFt!wMjN0 z*WiJSSK3s%wTiv4!uMWNS8-tu@e#G%DvMz;k5GOYMD$Fxb(Ki^y^$do4TO#Rn^iuG zlF~|i^`oW^ImyPhLXWo_PM-T8F6%Xh=s1%7>V2W?tl=AnZ2^1uV^GD3EFXunbxRUq zdV^l^x8)Ko25%7R_*k1P;Sur~>=VX)uBGSfcJC$1-xXQA9AV74eBCYT`hL~KzG44E zwJOrZH^LM;NCkebRt|4hyq7aamfpyY&zr&L`=*rI+TWUg#%-bQ6apIoqc%UNnE#rn z`Kr)LA-nGudu$WuAuj04>8x&fNn}H6jHOZJJ%XQ?lZ%upOIG3o$guVSXd*@p{M<4>H-5ma2&RG!ULz9;cz7L&{)!zJn*c3zNt@$YaTq%b3helak9F9>)t zuetjRi~js^rV$WbL*rK!>p}LAX5Jd?0de)3cjwkJY~73S5fK-B2qZ7q=b-T(zaZ~Y zYvXE+0E+wE=oGycJfNpj(12f+X+Xyz)08_o{kQ6IhvEj1$Mu9z{xe2NVX^Oxm1$O94J%M@mTEYi^*GDS?&UB=$-~^>eS>3U~K;8pZ7hsRL#l%T9Hj+hm z?;@I}YpkNfosf)2KR&T*L-nQ)O~;OY47BQ8*G!Qn4aIArne0i)Jkch+c5m<9a=rhilDF83 zC!F=b4OUkFwDCi4#^5KYzaZw-?A?0q9#XIFebjlHv`=g@X?QqBYTR?55O35@Bqi+d zZKuacDr;XA*UhK&X@uHJ+qR;0XZ~aUZiRU|r?{FE4226usy&ygM%K&nF_*fL60<44 zAY3%FF^>r~n@}G2&skXB{i7NL0e08Mj$wwcHz2phoVY}ah_w5CjP)UM7A0}#N^7AQI z=WONHe9vm7?fM_ zJw2z5r>u)c^(vM`3Ks|Bb!wwh4vk;ODX*?pdZ}eQ^aN!sV~1RegZ2PAu&yCzQqrTq z)G}6sQ+^}@laEjaqh3XAYZ_FFbETi5UhYH?-c|HXuMQj;I55Nz4oSZi@X9@Hn!kU8 zp2oa{!?+%LcVB9GB9`6a(UX(9kYhtSOo<(g`s@BdPYdilIHMHKRfxF?2l?pz9E97r z=OnLnO>}s=_*=@mTz;FF>N>bL*fZx$CANI+u5o^Y8l281Evf3D+J&P!xQ^zXEh7i5 ziWg#|1gH;P$-B)sb-YZ#R*GDWZ+H5V^t8S$*z))dr5CJA%0|Qs;Xu z^XL-pSZuZ1`D*7+cq`xjj9P!#62R*OgrFNBkA?ndV|jxFEzzMpeTF8`pgT+icRq9r z<`kXXlZ>IozDHy%Y~CO|$f`usE8V&V_A~4!3NsPw8WMpy})}`yr!NtJQ6SQ{9hJDlNod&*2 zU0@$=TsXLX9C|ny-b+6}Z>vNO&p23Tl=r-(N2dgv@RWGo$L&38Zb!Uav*rBs@!X?{ zF9L+J(k+eVf5!R(WFj_gmg; z7fvTJ?M}4bV|iH&_D>QtCivdYOjHd3>241UV>j;|5&NY5k@hY7#j}0Tb7x|BKQ17z zn-Y?qj-_%&xx`78>OVKE);I7i3OPCCzv(vfMkmUpJ1svV7sP>cMWu>zwz%!zV$F{U#nyOUS(0w|l zwSG^rqDC%9TMJsXEY@~wgBo)-Do5yL=S;HldZN;;N0zdzGYC?(>dR%CAd#)U*3W?Q zmP)7#v?;DUMwZ}USJGk}zHza|p>ADkChrXSQW5$X)u~AdImDe=o&DqPf(J^7Jz4SN zm0BADPo^W+Opem6$;o3!w~lH%nk1jqzBtoIhFNZqzW%t0Nb=X$Q=A-6ovI#LYM()^ z65mX>MHYOE%08FVV~}R`^Imy*(o&F*dhQqzNN64QSD)iv-_L|CX%*?3pFkb_`Yk~X zA^gVQta+4;iYZuNS3GWecW0?%&Pn!tiHTf_(D_?KDMI#)8U=tth}?j)Mi=Q<|gn#ntsO+(a`BK^2@&tMz`B5N$1-JsV#l%Hr9 z**It=3&2>lcp~4yAZ5qf7tcJq@A0Fzpub)A{mqukY*rgQj<^P#a-`D^6y88w zV+IJ|o1Nd1#5Q!k9}IrAYl8m%`4_|;zk3?!71kVCUxD$k~-M<^fbS$-N?gE%zkT^+tJsbZzhB)Ql!6h zLXlSU+@o0kgdv@&bK&PtyAE4DyY1$Q`tB}IeB`k(sM6~bl+(+W6||zM!ui24NB-i+ z{Oyjh<_~;va^u$#gLkZ5)vZS?e^?qB<`5-{7YSQcgPkqgF|15FtOt#f0;A5$C-s*C zqbSgJ;rq^~t?{kL>bC}oPiS!H4;oBBY_3=6S|^Xzm)q};9KgKt=Y*bLF!$C~mknho z!aQ9TvQb%ah_U{X^+_q+SNFsN;Sg=PNB-@>Nj6**_owji<|l?kn*NpTN6e91Yt?FH zD+?>O;T#P|6Z1`rRgZdl&m367%Nd?`sqHT+dJn%6ec~%Q<}Cb(=;>T84Rn(HVE1lk z(fYkjW^|J#6B%DmAd5a&aqJhtr^7wGDJ6Egj&2NLDr%=8`#3v{P&s`y0b}v}^>zgd z{33noGHCac&0g+q!ZK|o3HkxI9_Wc4Sc}=Oj7`eJYO4E18DgEFcP2PjE%nc9l@&Rk zw_RFjFB<7+uJh)GXIgK$rx}!siv|^V+sKzi^zd_1qsQ9i#=TR2q|Oty!rPa|gHlqb z5uV=+jyHaL+yR+j=u4$k^SKSwH!IQw>kVAp^Rh=d!X5N#I`%aSv23(<+uF)SQrIRA zw+go@DLA^?;LO24jbn;h+Ap%tm2JO$ySyV#TPe;hrP&XtGmppWsT-0q^igRGpCZ*+ zPYEJ*v()p#gS+W=qK^e;UkJWZglBeh43T zcK8^!Y^&SfqbqP-{K)mX>Q$Kpm2Bz~nYfuqK{vVseLCu;2{m_Ce;uvLaQN|?h92_p zkfXy*%X!bgis9_ct>tn>xtawR0?#qDhN}8|jq60?PEjN3F^5ZE(qQAeKi=7B(w+H* z9u7D-Nl==_k2P`mUm;NP=nVd-apV(l$tb!W!|9U;uDxYCHw`X?l2!lMv7{c)#VX_> zGsGjX`Z!0Uv-m%5_+v{f25Xg^LI0&>qI|!M;LD2l4I0)*{iHJwRh7+9LNUC;G^3Ehh|+ zktU9ES=agHop)bqtj1Qjg0(2@i8%}8kcfKUa|y$zla%0^-4zGTg^ne>hhM6g5jtA; znY?j4tW7uAr=B0G=l`4ih^ z1dkHa9Q|b-PQf#eWHp%WBFEm#=9`^5(l(^0=cQETQ7+y~vQ)_K9oSNM)5v!H>}BKl z&*t5dseaRzH2A(tGh?FeB2grAIni)rHhGZmChkJP z(v%q!+v(}4GHj5S%Rr}<75dd$G3GVxB0<4`)4fJAi|Y1`k=aMK(e?h?x%rR9P&YK( z_Cg?(g{p?^rOPbPrK-x0WD^IsjazH=oc5Y|E)8o=`0(M9mgEsd5u~HX@4tAjZzOAG zSXyN$?0CB9&Zb^@fm4Q6q*$xI%#+*M96aKSgYM=-xC|@vS$}vVoRNw*qbPwxlB9MQ zo72N$$t84i^-8!|ve;qu$1MW282uDu%(ap<^Dx&HTOsEOvr1Fb*Pk98X<>|+#+nlH zAGJDnH4HSGCDs}!mX!ABFBpurRep_X7qtviJr%Vj?KIFn+G!Qa*ggO~?9?A@F(HmU zee>;46u;Atnc=uuTf?t?`}Lnc2w>=aqcBJl>Y=%k+6CO1{mX^}*d?RY!jgE^EnsIM z%Mc&yxCq``M>oESe{ZXRF+IWZY8~oJQP{9uO{_NUy`DLO@T%r%sEE-zZ}L7dda7*4 zv%m)R4i!qWW!=4sZixhe8zdP$D{Z@Cd*Ux&==_I?{~HhBc@ir{aAaW${#V8Jh52v) z8B_T4OnkwAask3WE{^`w6`Sgx$K&gj=^*eBkovF}SfFea77R8{*Y8LQs`#HCbr$1`?yvNF7zb{p8cSb1u5ez1BsxBxj9 zK`lMtr^)>JL#C_cc{|jTpsUs`$8?#L_#U)F0&Pw|cqFUJ{nrFxRt)|wcAITodiU56 zdbaX6Xmoco3iKKBG5|~4%jwa{xQE!JZvZYu#jNLU>+S@%(Y5^HmYDR*03CU+c`Fy~ zyt%7V_`PRnyRH0tSeY(vet3+~f;(D65viqM8Zr;zDI$5^wX$mAPb6DU-chmFYO~Me zB>X6)Z~*xeS8yi~B_pUdnuNsfT`T@6^L>TPWX>Vys%O!=Ru3{9dS2(L$hkzJseed~D_Pd{DBAZ5+T~z*M6+LzShMm^5Ec*^$7efUz>W4-(G`T!+x8W z4}Ns259BsVvr#{P4Lwy$5qa+JC}44gN99$Pg~IPZW>{|frWjnbU)>f zHyJi?#_@WWU!`+Nzd8^%L-?kxW%4tc|Bdc~w^gMNGk+aD_=ub9c+vw-o{W9Uo2|vH zq)>37o4x1`acK`88VGWewqca^ip|C#>ab2JHarW*Wee|U)7mPb>%=~Jw>XE$qn{fT z|F(kI62Up$85JQ9+N5#xyRuKhl5fj3K|?*t-4ugn+CP zYaP!Bw8<|O`&8Y~oyWgt)a2mesjF;7Ck3wR9nEME_53+(rJ{UPybg0}{2e&(96L^! z`g$adq~BYvUc*i;Km#|8Mr-`htP+fyPaH+rvXV+ot3EHa{DP#t>uG*V&77-|DR9OK zJxMC5t&D2gV;m9i6nx}G?*U5Q%Q0SC2)@05h$fqQ67GZjFNmdPhyBLd!G<82m1+Pm z!m+Qv_IWfTUgLN!qg7OL-D&$^=0vl`#!CiAv_Fro#(xu&M9-P8>v;ot^V<5>|33l- z$q7+(by!yGl3~?w1L0}UMZRlMF>F8O)(+n0+^=)_Qu7;ZXcbwsegPSwNIZUxJ5jja zbr&{x`#>n=-7Z~cS7i>Vff&%)Jrml=jokc!S;RTPoQ9^ImRMulC-_UZYFNXvZIvtf zV~~>P^;Cqj67^t)zCX4#&tX;JEF_W2oVSak`MEw+ehX5DASorPIJ-w6+U!H0P?UKY zQQfp*Re=3y!__P&X#BMqwn>GBOr?rXAdyZG_Au4qYVVod^T7di@Djrklf_vbwAmUnz6b!nt=ZHDHA<~-JH{%a!^b>TCJ~slu^Cg7`ktJ0QqZ@f-OhQf zt~sd-Imu48Hjyy`3)j<3k_xkyd#i1$=5ngieVKjCeJ}j*4}>2dJ(FPY%5tEccYk)$ zrBqW^KF_`<*dl0||sY(qn; z&6HrPovfD?`_t}Wg_bFopPap~M;Ie-`b06Wa(38F;=S6;^1$6sJ>96F>JcjgksUR! zHkDGp@J)A&f$Mw1^o`>N-MJ~T~gmBr%Pp2wwob{X!B;EAm zoGK`%x=R0#_P#tG%D(M;q>^u`Da zmF)HuO|i8RIB@c3t0a!>n@Fk&_+u2$jhl@24We;?!nN@m)KCZG)n`PW#}QEYF-lnl z5Qk|UxXd3S`#v6BM6cS|<3Gu27@l9F`K`h>C;%pPQ99eu=;uE>o-ugnuy6Lk3{fB5 z?;bKcscdn90BZ$S#VlT>z4M3pN0Q%Fm>Qc>vk5*fml!x7ah)z#@u>%y zFGg9@;*Ta3r`25>-#%uY?9t$1{TKuRM?p8wkK92!gf-tZ8>*z2v@TKTZ{X{b?JkjZ z((`y|>qDAcWhFJRptYbEo$DyTp=<3=emk7zS=(G@;_ZZZLV9QUw4poceDsIoS$g*! zw4+YAXs9m4+s&`Iv2iWAW4Tt7d~W6M;(LtEno7G&xT#~P+FCet zvgccsMw74AOgw8>O6!wD|-Dza?fY6w&g%C2xPhl~_ey z+v+ZBC$Nz~w8prs4xeQ}={sgeW^3U#k?VUzK>kOCukfK?j-%K25>d4sX$1H>zYgdN zT_FAi5g6vfUg$13{T&y?*FR6OY`?HE8`+jpckmlzJl8YMQ`wY3Vv775Xql zvGm1R_XiMboY2hG_U;|c-46oC4moZ2z2k6OsjEV1ebz|ocTD(};yCP|z?cL70LCoi z=V6Cb2ok)W*Lx#xmslzJsUAOC0i~Lz0DKmlkG=_7Mo-bfb>Ck=oT=Ew zVJuD68#^nr_2+2-zZ+**hcI%jU@M$(+D^nbFID;*{6LFEcMBxWEjF;#S+e}oH<*)| zu_ftdt&v-v6OmmY=PKvbxzq6kNek}g{Xq(^ot-a=P3vO`Zb%8xRiq)-77&a>k^ge} z;-?!dZf7-m#J_qwUf>*&96#kHr^xFX9F}$|T0P6rrUhZT6OR-oHRib6x_=y*F?wmJ z{-QosHoxUSxnMwEgc7C?F|Ha>L4<9;@5|HqDB;;=&LugDdN^Bs#u5F4VK2n3*}RU= z{}-2$D|qYdaY8mB%OF!rPfMdwr$S4*0;`s3j)n?;m!869U2=IeLd@VdRLJZe(e3w8 zvg_}(myleUUYY(#2<0_wwaB{+i9Q3AdvGj+d4(-jq>eCG1y3eZc)df(LpUjim>OXd zndYkMx>~oPZ%sSZ0bq@=XAF7^K)T%&1)5Lqbhu63_#i=$hT{|0l_$<`RT113J0!ln zjXA}2Yoy0H(kkA~?$O|C!IbmKf-{rv8QBWjkJ3-Abx+UXzLzIKNW4*?XsoCt5%Znm zJ?r+WUbHNV5O(eKOy0|)TN_MYz4YxTYw?_B9v@KrA&wHRc7FRNO3FO)L$}S>j!#qz z9sr%!|7;%CH_*^_#e$@xWo3+GH2o5p%^}TL4kb@MkDm8cqlh2`=c0WvrdWz*=rDSu zccp{<61#DY6X)tCv^>waB#uLko@J>g$C$}m=}5)B5^IbMJZAdRH1Jxm%O0N8ylQ^9 zm>2|_nhD(SAFR^tZo$XmeJAu{PH^2Q8HxybP_pWqli0WPpz&d#bWpr5W3I%zs*3N8 z+TSs8w;zy?1CI3Vpfk-Jxt^FZ^Bq9!E`n^g+LECr3Pxh4!Az_p{WZX)E%OEtwe~ik z3b)-)hLsz)=)OJc8M>eI%W&jt=3#@Lq6t!<&8>)|8S+Py3E7^RJe$9?Vze*^Q4B7R6cJa;wTF48w5AD{%x~rmxLRN=aZ=7zk`Qp&BRc$vU=SaAwko-Jm8G&t@fUWBLZvy}< zO{kSq_YqVUSpaB3e1;9|$F46+(sqFCm%fjl9t_&+QJY<@gH0SIXH=aeqxP0(H{U95 z5Q`ajvWGqzo*jzH5|>*j_Il?o z%vnq|C(!TXP5P0S)*2h?^OS|&zRx-2oDmm%@u5?MccOtffm&%y>~SZ|%Xs?Mxg4*k z&@HWf;u_VbEV@z+i4lRYZlT+sFgGE7zhl$LU}$3`t5J(JVMP2!L8$w(Ag5fJRkdJ2 zeUqOYf75rnY}VuLx2AA)Uy_b3%g&9TvUL&txSmlQgSVT`>UgRfnsik!{d`3Kq*Hzo zWx~vTC{MD!WeC?vgJ-CiOUFdMW3|tTpXPAq;}%g7&47-mL>2n@2E>sMgZ2njyY9f7 zq{hMHE9HgnfxsG{@u7$nWz1m za?OzmqY72L{&m)y8JAwX8gJtrDnxsaZd54D`V{#*U*yyzXzi@E_gOt7Q^6xTFjQ3W znwSFjc}@HHMP8f0>F*LcD+$*8cbQwhS-#_f zrgb}<03qY(?C}83+x8nI7a9SE2TXdgBrZqB(NDGGukPKUheAmNsv#ug6=gsh5wz%HW41 zuEvyiP%kRF0{-;x82}*dg4)7IGmm38_@pIi%pj5vK`U$?<^vEmOYm>(k^;Q&;Sb32 z65LDv#2?`|$6sIoI{XRC&vuA#r3v&bu3&>UE<|M7AIWG%+2DDa&7<744|8Ngt|)JJ z>c*OoteMSXY;Wj^bW0@YThwzf$;U$2Z{3Lr|LfMtUoT8TI*04}3PGi4oyNIk6r~ zV5v{p?%`gof8cu2hm4$IZF!q7Wg=1^>!NSSv-DTVGWaCx;2T^GKOjipSLzKKNSS*q zSC%TJ#Gd-!-=ih3Dz&b^k)m5Kf9kBq#&RIP)P%$>g!?xySN+Nu4+*jOerRFGfpenD z{``U*fy2X`O<~D9JaM#Bh#=I8&~LjPShno3gcLf3M*{f;=!0J{_=VwdIp)-5-pOGW^=&g5THk41mvOc+`k4s6I&{! z%3rYnzDo_IJ~(yrm2RCji}u<3i9@eqFGzSMeM7f+D`iQ)3&!0$r}!nugP8f)gsdUf za#K@WLO#d!dvazhQW+JKH}9pBlf+}F4Lp5xiM zduM0@O5aB}-NqHi6?>2hMEv9Ay`|-4B*xpANUzpq$lCRYIcH<@1)Zi|4$8FQgx+z& z@`=}3wSf`mpAHDXuUOi=WcM&fj5cf`+T)Saphi9(F^f- zGs~h1*tRQ==FJ<*Ii3}b_YSVY2ZPpmFV-;zSy_4ywBb(DjEReT25rt`o>3gxR8NM3 z$k8^u4Ile*mq*R;AuC9}vo_PG)PN*!vLqofzAqPRiLMhG)}VO02v%5?SPkDIC@smh z2In|3M6kuk2y`EO>eTBnQyB%kbYdLZhbp#=vkX!25s`enSBDp=$qW!CRrbCtqXAQzb64 z+LP2CDNe0Z3##AFKG1vU_TiJ*cb)T5h%=2yWxuTGd&hg~l|MHO99Jz_DqDQmsP!!9 z?iH7G(N{6KlC(%b!}hmY3Um%?-M<%y?WWWIfiG0dCZ^P#A)oY6J0DCz(GFn_^{g(g znmGd5*&mRy&6X11ACRi{Go82(xDG@iZ#c2-&M!0T*GIpP&zVVg<)RsF72iHRZJAW8 zaN10Zs!)8?=EMH`kb%K#q&*pvN$MHvh*UZ}^-0C$y@!EvpR}<@F7tki_{M&XE{k1X z8rVfRgII)i&6=f{sMVLz1Z+v;4F-rk<{Fj9xn3eSDxD9jM`oVna)^#%vf3$8{UIzl zARaQM2+vX~NS!XPe7Xmnla}~?isQWi^H-~)S@BDy-3u#PO0z0M^aSZjZTq6D=;zHG z9ygen9Zv5e7X8%p08}o=?w>bH$Q}~7VAL%zf~y`n-p6~>&- zrRR6I`GcVqzxWBK&F9JHPpd{WeCBBH=;2BtNO$Vv4?|RgaZx;%vEJ=&PpN0~N9`5r zUtFwn?%=WI;C)#;y?nBG_N2w0<%sAF@Gy7%{2(hMwdskKoMp6#HA}2o_=iGo4r$X=i&Hq1Hdv?Qqv0ZmVB}(jE8oPPdwct z*R7=gpfZhjuvMMtP|4nGSjwNA`P*XyzegHyqD0i>9ERcpmck#dA@W~;H)(2McVeQRf&Fuy{~1hPBkW^|N?*TtA=kp2_VrG~3Nq^?1ItfkqDY!GMn_|b>uFAQS!S24OE*=*vyLlt!g&g z7j&OHcR67gCER⪼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 literal 0 HcmV?d00001 diff --git a/docs/source/Features.md b/docs/source/Features.md index ad213556..bb74f280 100644 --- a/docs/source/Features.md +++ b/docs/source/Features.md @@ -36,11 +36,15 @@ DNN based CTR prediction models usually have following 4 modules: - trainable: default `True`.Whether or not the embedding is trainable. ### DenseFeat -``DenseFeat`` is a namedtuple with signature ``DenseFeat(name, dimension, dtype)`` +``DenseFeat`` is a namedtuple with signature ``DenseFeat(name, dimension, dtype, transform_fn)`` - name : feature name - dimension : dimension of dense feature vector. - dtype : default `float32`.dtype of input tensor. +- transform_fn : If not None, a function that can be used to transfrom + values of the feature. the function takes the input Tensor as its + argument, and returns the output Tensor. + (e.g. `lambda x: (x - 3.0) / 4.2)`. ### VarLenSparseFeat @@ -175,6 +179,18 @@ The output of Cross Net and MLP are concatenated.The concatenated vector are fee [Wang R, Fu B, Fu G, et al. Deep & cross network for ad click predictions[C]//Proceedings of the ADKDD'17. ACM, 2017: 12.](https://arxiv.org/abs/1708.05123) +### DCN-Mix (Improved Deep & Cross Network with mix of experts and matrix kernel) + +DCN-Mix uses a matrix kernel instead of vector kernel in CrossNet compared with DCN,and it uses mixture of experts to learn feature interactions. + +[**DCN-Mix Model API**](./deepctr.models.dcnmix.html) + +![DCN-Mix](../pics/DCN-Mix.png) + +[Wang R, Shivanna R, Cheng D Z, et al. DCN V2: Improved Deep & Cross Network and Practical Lessons for Web-scale Learning to Rank Systems[J]. arXiv preprint arXiv:2008.13535, 2020. +](https://arxiv.org/abs/2008.13535) + + ### DIN (Deep Interest Network) DIN introduce a attention method to learn from sequence(multi-valued) feature. @@ -233,7 +249,8 @@ By stacking multiple interacting layers,AutoInt is able to model different order ![AutoInt](../pics/AutoInt.png) -[Song W, Shi C, Xiao Z, et al. AutoInt: Automatic Feature Interaction Learning via Self-Attentive Neural Networks[J]. arXiv preprint arXiv:1810.11921, 2018.](https://arxiv.org/abs/1810.11921) +[Song W, Shi C, Xiao Z, et al. Autoint: Automatic feature interaction learning via self-attentive neural networks[C]//Proceedings of the 28th ACM International Conference on Information and Knowledge Management. 2019: 1161-1170. +](https://arxiv.org/abs/1810.11921) ### ONN(Operation-aware Neural Networks for User Response Prediction) diff --git a/docs/source/History.md b/docs/source/History.md index 44e7aa43..5653a935 100644 --- a/docs/source/History.md +++ b/docs/source/History.md @@ -1,4 +1,5 @@ # History +- 01/06/2021 : [v0.8.3](https://github.com/shenweichen/DeepCTR/releases/tag/v0.8.3) released.Add [DCN-Mix](./Features.html#dcn-mix-improved-deep-cross-network-with-mix-of-experts-and-matrix-kernel) model.Support `transform_fn` in `DenseFeat`. - 10/11/2020 : [v0.8.2](https://github.com/shenweichen/DeepCTR/releases/tag/v0.8.2) released.Refactor `DNN` Layer. - 09/12/2020 : [v0.8.1](https://github.com/shenweichen/DeepCTR/releases/tag/v0.8.1) released.Improve the reproducibility & fix some bugs. - 06/27/2020 : [v0.8.0](https://github.com/shenweichen/DeepCTR/releases/tag/v0.8.0) released. diff --git a/docs/source/Models.rst b/docs/source/Models.rst index 0479d305..c7c80141 100644 --- a/docs/source/Models.rst +++ b/docs/source/Models.rst @@ -12,6 +12,7 @@ DeepCTR Models API NFM AFM DCN + DCNMix DIN DIEN DSIN @@ -21,4 +22,5 @@ DeepCTR Models API FGCNN FiBiNET FLEN + \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 823e43c0..5a45c11a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -26,7 +26,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = '0.8.2' +release = '0.8.3' # -- General configuration --------------------------------------------------- diff --git a/docs/source/deepctr.models.dcnmix.rst b/docs/source/deepctr.models.dcnmix.rst new file mode 100644 index 00000000..98b769e9 --- /dev/null +++ b/docs/source/deepctr.models.dcnmix.rst @@ -0,0 +1,7 @@ +deepctr.models.dcnmix module +========================= + +.. automodule:: deepctr.models.dcnmix + :members: + :no-undoc-members: + :no-show-inheritance: diff --git a/docs/source/deepctr.models.rst b/docs/source/deepctr.models.rst index ee64ff0d..0f1209b2 100644 --- a/docs/source/deepctr.models.rst +++ b/docs/source/deepctr.models.rst @@ -10,6 +10,7 @@ Submodules deepctr.models.autoint deepctr.models.ccpm deepctr.models.dcn + deepctr.models.dcnmix deepctr.models.deepfm deepctr.models.dien deepctr.models.din diff --git a/docs/source/index.rst b/docs/source/index.rst index fa5047e3..6b6130b0 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -42,17 +42,16 @@ You can read the latest code and related projects News ----- +01/06/2021 : Add `DCN-Mix <./Features.html#dcn-mix-improved-deep-cross-network-with-mix-of-experts-and-matrix-kernel>`_ (`中文介绍 `_) and support ``transform_fn`` in ``DenseFeat``. `Changelog `_ 10/11/2020 : Refactor ``DNN`` Layer. `Changelog `_ 09/12/2020 : Improve the reproducibility & fix some bugs. `Changelog `_ -06/27/2020 : Support ``Tensorflow Estimator`` for large scale data and distributed training.Support different initializers for different embedding weights and loading pretrained embeddings.Add new model ``FwFM``. `Changelog `_ - DisscussionGroup 交流群 ----------------------- -公众号:**浅梦的学习笔记** wechat ID: **deepctrbot** +`Discussions `_ 公众号:**浅梦的学习笔记** wechat ID: **deepctrbot** .. image:: ../pics/code.png diff --git a/examples/run_estimator_pandas_classification.py b/examples/run_estimator_pandas_classification.py index 9d19e2a3..9817d20a 100644 --- a/examples/run_estimator_pandas_classification.py +++ b/examples/run_estimator_pandas_classification.py @@ -47,7 +47,8 @@ test_model_input = input_fn_pandas(test, sparse_features + dense_features, None, shuffle=False) # 4.Define Model,train,predict and evaluate - model = DeepFMEstimator(linear_feature_columns, dnn_feature_columns, task='binary') + model = DeepFMEstimator(linear_feature_columns, dnn_feature_columns, task='binary', + config=tf.estimator.RunConfig(tf_random_seed=2021)) model.train(train_model_input) pred_ans_iter = model.predict(test_model_input) diff --git a/examples/run_estimator_tfrecord_classification.py b/examples/run_estimator_tfrecord_classification.py index 9249387c..dcf43217 100644 --- a/examples/run_estimator_tfrecord_classification.py +++ b/examples/run_estimator_tfrecord_classification.py @@ -1,10 +1,9 @@ import tensorflow as tf -from tensorflow.python.ops.parsing_ops import FixedLenFeature +from tensorflow.python.ops.parsing_ops import FixedLenFeature from deepctr.estimator import DeepFMEstimator from deepctr.estimator.inputs import input_fn_tfrecord - if __name__ == "__main__": # 1.generate feature_column for linear part and dnn part @@ -36,7 +35,8 @@ batch_size=2 ** 14, num_epochs=1, shuffle_factor=0) # 3.Define Model,train,predict and evaluate - model = DeepFMEstimator(linear_feature_columns, dnn_feature_columns, task='binary') + model = DeepFMEstimator(linear_feature_columns, dnn_feature_columns, task='binary', + config=tf.estimator.RunConfig(tf_random_seed=2021)) model.train(train_model_input) eval_result = model.evaluate(test_model_input) diff --git a/setup.py b/setup.py index 16bbd85c..29bbe8fc 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ setuptools.setup( name="deepctr", - version="0.8.2", + version="0.8.3", author="Weichen Shen", author_email="wcshen1994@163.com", description="Easy-to-use,Modular and Extendible package of deep learning based CTR(Click Through Rate) prediction models with tensorflow 1.x and 2.x .",