Skip to content

Commit 5a9e962

Browse files
author
浅梦
authored
add weightsequencelayer
add weightsequencelayer
1 parent f6b34cb commit 5a9e962

File tree

20 files changed

+223
-125
lines changed

20 files changed

+223
-125
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ Let's [**Get Started!**](https://deepctr-doc.readthedocs.io/en/latest/Quick-Star
5151
Please follow our wechat to join group:
5252
- 公众号:**浅梦的学习笔记**
5353
- wechat ID: **deepctrbot**
54-
![wechat](./docs/pics/weichennote.png)
54+
55+
![wechat](./docs/pics/weichennote.png)

deepctr/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
from . import models
33
from .utils import check_version
44

5-
__version__ = '0.6.2'
5+
__version__ = '0.6.3'
66
check_version(__version__)

deepctr/inputs.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from tensorflow.python.keras.layers import Embedding, Input, Flatten
1414
from tensorflow.python.keras.regularizers import l2
1515

16-
from .layers.sequence import SequencePoolingLayer, SequenceMultiplyLayer
16+
from .layers.sequence import SequencePoolingLayer, WeightedSequenceLayer
1717
from .layers.utils import Hash,concat_fun,Linear
1818

1919
class SparseFeat(namedtuple('SparseFeat', ['name', 'dimension', 'use_hash', 'dtype','embedding_name','embedding'])):
@@ -28,7 +28,7 @@ def __hash__(self):
2828
return self.name.__hash__()
2929

3030
def __eq__(self, other):
31-
if self.name == other.name:
31+
if self.name == other.name and self.embedding_name == other.embedding_name:
3232
return True
3333
return False
3434

@@ -53,13 +53,13 @@ def __eq__(self, other):
5353
def __repr__(self):
5454
return 'DenseFeat:'+self.name
5555

56-
class VarLenSparseFeat(namedtuple('VarLenFeat', ['name', 'dimension', 'maxlen', 'combiner', 'use_hash', 'dtype','embedding_name','embedding'])):
56+
class VarLenSparseFeat(namedtuple('VarLenFeat', ['name', 'dimension', 'maxlen', 'combiner', 'use_hash', 'dtype','weight_name','embedding_name','embedding'])):
5757
__slots__ = ()
5858

59-
def __new__(cls, name, dimension, maxlen, combiner="mean", use_hash=False, dtype="float32", embedding_name=None,embedding=True):
59+
def __new__(cls, name, dimension, maxlen,combiner="mean", use_hash=False, dtype="float32", weight_name=None,embedding_name=None,embedding=True):
6060
if embedding_name is None:
6161
embedding_name = name
62-
return super(VarLenSparseFeat, cls).__new__(cls, name, dimension, maxlen, combiner, use_hash, dtype, embedding_name,embedding)
62+
return super(VarLenSparseFeat, cls).__new__(cls, name, dimension, maxlen, combiner, use_hash, dtype,weight_name, embedding_name,embedding)
6363

6464
def __hash__(self):
6565
return self.name.__hash__()
@@ -72,6 +72,7 @@ def __eq__(self, other):
7272
def __repr__(self):
7373
return 'VarLenSparseFeat:'+self.name
7474

75+
7576
def get_feature_names(feature_columns):
7677
features = build_input_features(feature_columns)
7778
return list(features.keys())
@@ -95,6 +96,9 @@ def build_input_features(feature_columns, mask_zero=True, prefix=''):
9596
input_features[fc.name + "_seq_length"] = Input(shape=(
9697
1,), name=prefix + 'seq_length_' + fc.name)
9798
input_features[fc.name + "_seq_max_length"] = fc.maxlen
99+
if fc.weight_name is not None:
100+
input_features[fc.weight_name] = Input(shape=(fc.maxlen,1),name=prefix + fc.weight_name ,dtype="float32")
101+
98102
else:
99103
raise TypeError("Invalid feature column type,got",type(fc))
100104

@@ -201,7 +205,7 @@ def embedding_lookup(sparse_embedding_dict,sparse_input_dict,sparse_feature_colu
201205
for fc in sparse_feature_columns:
202206
feature_name = fc.name
203207
embedding_name = fc.embedding_name
204-
if len(return_feat_list) == 0 or feature_name in return_feat_list and fc.embedding:
208+
if (len(return_feat_list) == 0 or feature_name in return_feat_list ) and fc.embedding:
205209
if fc.use_hash:
206210
lookup_idx = Hash(fc.dimension,mask_zero=(feature_name in mask_feat_list))(sparse_input_dict[feature_name])
207211
else:
@@ -221,7 +225,6 @@ def varlen_embedding_lookup(embedding_dict, sequence_input_dict, varlen_sparse_f
221225
else:
222226
lookup_idx = sequence_input_dict[feature_name]
223227
varlen_embedding_vec_dict[feature_name] = embedding_dict[embedding_name](lookup_idx)
224-
225228
return varlen_embedding_vec_dict
226229

227230
def get_varlen_pooling_list(embedding_dict, features, varlen_sparse_feature_columns):
@@ -231,11 +234,19 @@ def get_varlen_pooling_list(embedding_dict, features, varlen_sparse_feature_colu
231234
combiner = fc.combiner
232235
feature_length_name = feature_name + '_seq_length'
233236
if feature_length_name in features:
237+
if fc.weight_name is not None:
238+
seq_input =WeightedSequenceLayer()([embedding_dict[feature_name],features[feature_length_name],features[fc.weight_name]])
239+
else:
240+
seq_input = embedding_dict[feature_name]
234241
vec = SequencePoolingLayer(combiner, supports_masking=False)(
235-
[embedding_dict[feature_name], features[feature_length_name]])
242+
[seq_input, features[feature_length_name]])
236243
else:
244+
if fc.weight_name is not None:
245+
seq_input =WeightedSequenceLayer(supports_masking=True)([embedding_dict[feature_name],features[fc.weight_name]])
246+
else:
247+
seq_input = embedding_dict[feature_name]
237248
vec = SequencePoolingLayer(combiner, supports_masking=True)(
238-
embedding_dict[feature_name])
249+
seq_input)
239250
pooling_vec_list.append(vec)
240251
return pooling_vec_list
241252

@@ -252,12 +263,12 @@ def get_varlen_multiply_list(embedding_dict, features, varlen_sparse_feature_col
252263
else:
253264
raise TypeError("Invalid feature column type,got",type(value_feature))
254265
if key_feature_length_name in features:
255-
varlen_vec = SequenceMultiplyLayer(supports_masking=False)(
266+
varlen_vec = WeightedSequenceLayer()(
256267
[embedding_dict[key_feature.name], features[key_feature_length_name], value_input])
257268
vec = SequencePoolingLayer('sum', supports_masking=False)(
258269
[varlen_vec, features[key_feature_length_name]])
259270
else:
260-
varlen_vec = SequenceMultiplyLayer(supports_masking=True)(
271+
varlen_vec = WeightedSequenceLayer(supports_masking=True)(
261272
[embedding_dict[key_feature.name], value_input])
262273
vec = SequencePoolingLayer('sum', supports_masking=True)( varlen_vec)
263274
multiply_vec_list.append(vec)

deepctr/layers/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
OutterProductLayer, FGCNNLayer,SENETLayer,BilinearInteraction)
88
from .normalization import LayerNormalization
99
from .sequence import (AttentionSequencePoolingLayer, BiasEncoding, BiLSTM,
10-
KMaxPooling, SequencePoolingLayer,
10+
KMaxPooling, SequencePoolingLayer,WeightedSequenceLayer,
1111
Transformer, DynamicGRU)
1212
from .utils import NoMask, Hash,Linear
1313

@@ -38,4 +38,5 @@
3838
'DynamicGRU': DynamicGRU,
3939
'SENETLayer':SENETLayer,
4040
'BilinearInteraction':BilinearInteraction,
41+
'WeightedSequenceLayer':WeightedSequenceLayer
4142
}

deepctr/layers/sequence.py

Lines changed: 79 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def __init__(self, mode='mean', supports_masking=False, **kwargs):
4646
if mode not in ['sum', 'mean', 'max']:
4747
raise ValueError("mode must be sum or mean")
4848
self.mode = mode
49-
self.eps = 1e-8
49+
self.eps = tf.constant(1e-8,tf.float32)
5050
super(SequencePoolingLayer, self).__init__(**kwargs)
5151

5252
self.supports_masking = supports_masking
@@ -85,7 +85,7 @@ def call(self, seq_value_len_list, mask=None, **kwargs):
8585
hist = reduce_sum(hist, 1, keep_dims=False)
8686

8787
if self.mode == "mean":
88-
hist = div(hist, user_behavior_length + self.eps)
88+
hist = div(hist, tf.cast(user_behavior_length,tf.float32) + self.eps)
8989

9090
hist = tf.expand_dims(hist, axis=1)
9191
return hist
@@ -105,6 +105,83 @@ def get_config(self, ):
105105
return dict(list(base_config.items()) + list(config.items()))
106106

107107

108+
class WeightedSequenceLayer(Layer):
109+
"""The WeightedSequenceLayer is used to apply weight score on variable-length sequence feature/multi-value feature.
110+
111+
Input shape
112+
- A list of two tensor [seq_value,seq_len,seq_weight]
113+
114+
- seq_value is a 3D tensor with shape: ``(batch_size, T, embedding_size)``
115+
116+
- seq_len is a 2D tensor with shape : ``(batch_size, 1)``,indicate valid length of each sequence.
117+
118+
- seq_weight is a 3D tensor with shape: ``(batch_size, T, 1)``
119+
120+
Output shape
121+
- 3D tensor with shape: ``(batch_size, T, embedding_size)``.
122+
123+
Arguments
124+
- **weight_normalization**: bool.Whether normalize the weight socre before applying to sequence.
125+
126+
- **supports_masking**:If True,the input need to support masking.
127+
"""
128+
129+
def __init__(self,weight_normalization=False, supports_masking=False, **kwargs):
130+
super(WeightedSequenceLayer, self).__init__(**kwargs)
131+
self.weight_normalization = weight_normalization
132+
self.supports_masking = supports_masking
133+
134+
def build(self, input_shape):
135+
if not self.supports_masking:
136+
self.seq_len_max = int(input_shape[0][1])
137+
super(WeightedSequenceLayer, self).build(
138+
input_shape) # Be sure to call this somewhere!
139+
140+
def call(self, input_list, mask=None, **kwargs):
141+
if self.supports_masking:
142+
if mask is None:
143+
raise ValueError(
144+
"When supports_masking=True,input must support masking")
145+
key_input, value_input = input_list
146+
mask = tf.expand_dims(mask[0], axis=2)
147+
else:
148+
key_input, key_length_input, value_input = input_list
149+
mask = tf.sequence_mask(key_length_input,
150+
self.seq_len_max, dtype=tf.bool)
151+
mask = tf.transpose(mask, (0, 2, 1))
152+
153+
embedding_size = key_input.shape[-1]
154+
155+
if self.weight_normalization:
156+
paddings = tf.ones_like(value_input) * (-2 ** 32 + 1)
157+
else:
158+
paddings = tf.zeros_like(value_input)
159+
value_input = tf.where(mask, value_input, paddings)
160+
161+
if self.weight_normalization:
162+
value_input = softmax(value_input,dim=1)
163+
164+
165+
if len(value_input.shape) == 2:
166+
value_input = tf.expand_dims(value_input, axis=2)
167+
value_input = tf.tile(value_input, [1, 1, embedding_size])
168+
169+
return tf.multiply(key_input,value_input)
170+
171+
def compute_output_shape(self, input_shape):
172+
return input_shape[0]
173+
174+
def compute_mask(self, inputs, mask):
175+
if self.supports_masking:
176+
return mask[0]
177+
else:
178+
return None
179+
180+
def get_config(self, ):
181+
config = {'supports_masking': self.supports_masking}
182+
base_config = super(WeightedSequenceLayer, self).get_config()
183+
return dict(list(base_config.items()) + list(config.items()))
184+
108185
class AttentionSequencePoolingLayer(Layer):
109186
"""The Attentional sequence pooling operation used in DIN.
110187
@@ -741,50 +818,3 @@ def get_config(self, ):
741818
return dict(list(base_config.items()) + list(config.items()))
742819

743820

744-
class SequenceMultiplyLayer(Layer):
745-
746-
def __init__(self, supports_masking, **kwargs):
747-
super(SequenceMultiplyLayer, self).__init__(**kwargs)
748-
self.supports_masking = supports_masking
749-
750-
def build(self, input_shape):
751-
if not self.supports_masking:
752-
self.seq_len_max = int(input_shape[0][1])
753-
super(SequenceMultiplyLayer, self).build(
754-
input_shape) # Be sure to call this somewhere!
755-
756-
def call(self, input_list, mask=None, **kwargs):
757-
if self.supports_masking:
758-
if mask is None:
759-
raise ValueError(
760-
"When supports_masking=True,input must support masking")
761-
key_input, value_input = input_list
762-
mask = tf.cast(mask[0], tf.float32)
763-
mask = tf.expand_dims(mask, axis=2)
764-
else:
765-
key_input, key_length_input, value_input = input_list
766-
mask = tf.sequence_mask(key_length_input,
767-
self.seq_len_max, dtype=tf.float32)
768-
mask = tf.transpose(mask, (0, 2, 1))
769-
770-
embedding_size = key_input.shape[-1]
771-
mask = tf.tile(mask, [1, 1, embedding_size])
772-
key_input *= mask
773-
if len(tf.shape(value_input)) == 2:
774-
value_input = tf.expand_dims(value_input, axis=2)
775-
value_input = tf.tile(value_input, [1, 1, embedding_size])
776-
return tf.multiply(key_input,value_input)
777-
778-
def compute_output_shape(self, input_shape):
779-
return input_shape[0]
780-
781-
def compute_mask(self, inputs, mask):
782-
if self.supports_masking:
783-
return mask[0]
784-
else:
785-
return None
786-
787-
def get_config(self, ):
788-
config = {'supports_masking': self.supports_masking}
789-
base_config = super(SequenceMultiplyLayer, self).get_config()
790-
return dict(list(base_config.items()) + list(config.items()))

deepctr/layers/utils.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,18 @@ def call(self, inputs , **kwargs):
107107
def compute_output_shape(self, input_shape):
108108
return (None, 1)
109109

110+
def compute_mask(self, inputs, mask):
111+
return None
112+
110113
def get_config(self, ):
111114
config = {'mode': self.mode, 'l2_reg': self.l2_reg}
112115
base_config = super(Linear, self).get_config()
113116
return dict(list(base_config.items()) + list(config.items()))
114117

115118

116-
def concat_fun(inputs, axis=-1):
119+
def concat_fun(inputs, axis=-1,mask=False):
120+
if not mask:
121+
inputs = list(map(NoMask(), inputs))
117122
if len(inputs) == 1:
118123
return inputs[0]
119124
else:

deepctr/models/din.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ def DIN(dnn_feature_columns, history_feature_list, embedding_size=8, hist_len_ma
7979
dnn_input_emb_list += sequence_embed_list
8080

8181

82-
keys_emb = concat_fun(keys_emb_list)
82+
keys_emb = concat_fun(keys_emb_list,mask=True)
8383
deep_input_emb = concat_fun(dnn_input_emb_list)
84-
query_emb = concat_fun(query_emb_list)
84+
query_emb = concat_fun(query_emb_list,mask=True)
8585

8686
hist = AttentionSequencePoolingLayer(att_hidden_size, att_activation,
8787
weight_normalization=att_weight_normalization, supports_masking=True)([

deepctr/models/dsin.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,9 @@ def DSIN(dnn_feature_columns, sess_feature_list, embedding_size=8, sess_max_coun
103103
mask_feat_list=sess_feature_list)
104104
dense_value_list = get_dense_input(features, dense_feature_columns)
105105

106-
query_emb = concat_fun(query_emb_list)
106+
query_emb = concat_fun(query_emb_list,mask=True)
107107

108-
dnn_input_emb = concat_fun(dnn_input_emb_list)
109-
dnn_input_emb = Flatten()(NoMask()(dnn_input_emb))
108+
dnn_input_emb = Flatten()(concat_fun(dnn_input_emb_list))
110109

111110
tr_input = sess_interest_division(embedding_dict, user_behavior_input_dict, sparse_feature_columns,
112111
sess_feature_list, sess_max_count, bias_encoding=bias_encoding)
@@ -158,7 +157,7 @@ def sess_interest_division(sparse_embedding_dict, user_behavior_input_dict, spar
158157
sparse_fg_list, sess_feture_list, sess_feture_list)
159158
# [sparse_embedding_dict[feat](user_behavior_input_dict[sess_name][feat]) for feat in
160159
# sess_feture_list]
161-
keys_emb = concat_fun(keys_emb_list)
160+
keys_emb = concat_fun(keys_emb_list,mask=True)
162161
tr_input.append(keys_emb)
163162
if bias_encoding:
164163
tr_input = BiasEncoding(sess_max_count)(tr_input)

0 commit comments

Comments
 (0)