├── LICENSE ├── README.md └── script ├── common ├── Dice.py ├── __init__.py ├── rnn.py ├── tgin.py └── utils.py ├── dataset_process ├── __init__.py ├── content_wise │ ├── __init__.py │ ├── generate_voc.py │ ├── local_aggregator.py │ ├── prepare_data_cw.sh │ ├── process_data.py │ └── split_by_user.py ├── data_iterator.py ├── gen_wnd_edges.py ├── prepare_data.py ├── read_content_wise_dataset_zhibo.py └── shuffle.py ├── models ├── DEI2N.py ├── DEI2N_MHTA.py ├── DEI2N_NO_IL.py ├── DEI2N_NO_TIM.py ├── DEI2N_NO_UIM.py ├── DIAN.py ├── DIHN.py ├── DIN.py ├── DIN_V2_Gru_Gru_att.py ├── DIN_V2_Gru_QA_attGru.py ├── DIN_V2_Gru_Vec_attGru.py ├── DIN_V2_Gru_Vec_attGru_Neg.py ├── DIN_V2_Gru_att_Gru.py ├── DNN.py ├── DNN_Multi_Head.py ├── LR.py ├── PNN.py ├── WideDeep.py ├── __init__.py └── base_model.py ├── settings.py └── train.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 skx300 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Experiment code for the paper "Deep Evolutional Instant Interest Network for CTR in Trigger-Induced Recommendation" 2 | 3 | This code is based on tgin (https://github.com/alibaba/tgin) which is an inheritance from DMIN (https://github.com/mengxiaozhibo/DMIN). 4 | 5 | Zhibo Xiao, Luwei Yang, Tao Zhang, Wen Jiang and Wei Ning 6 | 7 | in the 17th ACM International Conference on Web Search and Data Mining(WSDM'24, Oral Presentation) 8 | 9 | 10 | # Usage 11 | ```python 12 | python script/train.py train DIN 13 | ``` 14 | 15 | # Required Packages 16 | * python 3.6.13 17 | * tensorflow==1.15.0 18 | -------------------------------------------------------------------------------- /script/common/Dice.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import tensorflow as tf 4 | 5 | 6 | def dice(_x, axis=-1, epsilon=0.000000001, name=''): 7 | with tf.variable_scope(name, reuse=tf.AUTO_REUSE): 8 | alphas = tf.get_variable('alpha' + name, _x.get_shape()[-1], 9 | initializer=tf.constant_initializer(0.0), 10 | dtype=tf.float32) 11 | input_shape = list(_x.get_shape()) 12 | 13 | reduction_axes = list(range(len(input_shape))) 14 | del reduction_axes[axis] 15 | broadcast_shape = [1] * len(input_shape) 16 | broadcast_shape[axis] = input_shape[axis] 17 | 18 | # case: train mode (uses stats of the current batch) 19 | mean = tf.reduce_mean(_x, axis=reduction_axes) 20 | brodcast_mean = tf.reshape(mean, broadcast_shape) 21 | std = tf.reduce_mean(tf.square(_x - brodcast_mean) + epsilon, axis=reduction_axes) 22 | std = tf.sqrt(std) 23 | brodcast_std = tf.reshape(std, broadcast_shape) 24 | x_normed = (_x - brodcast_mean) / (brodcast_std + epsilon) 25 | # x_normed = tf.layers.batch_normalization(_x, center=False, scale=False) 26 | x_p = tf.sigmoid(x_normed) 27 | 28 | return alphas * (1.0 - x_p) * _x + x_p * _x 29 | 30 | 31 | def parametric_relu(_x): 32 | alphas = tf.get_variable('alpha', _x.get_shape()[-1], 33 | initializer=tf.constant_initializer(0.0), 34 | dtype=tf.float32) 35 | pos = tf.nn.relu(_x) 36 | neg = alphas * (_x - abs(_x)) * 0.5 37 | 38 | return pos + neg 39 | -------------------------------------------------------------------------------- /script/common/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:34 4 | -------------------------------------------------------------------------------- /script/common/tgin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import tensorflow as tf 3 | from settings import * 4 | from tensorflow.python.framework import ops 5 | 6 | def tgin(ub0_triangle_node, ub0_triangle_score, cand0_triangle_node, cand0_triangle_score, 7 | ub1_triangle_node, ub1_triangle_score, cand1_triangle_node, cand1_triangle_score, 8 | pos_aware_tp_fea, user_profile, var_scp, reuse=tf.AUTO_REUSE): 9 | """ 10 | Args: 11 | ub0_triangle_node: [B, SEQ_LENGTH, TRIANGLE_NUM*3, H] 12 | ub_triangle_score: [B, SEQ_LENGTH, TRIANGLE_NUM, 1] 13 | cand_triangle_node: [B, TRIANGLE_NUM*3, H] 14 | cand_triangle_score: [B, TRIANGLE_NUM, 1] 15 | pos_aware_tp_fea: [B, T, H] 16 | Output: 17 | output: [B, H] 18 | """ 19 | with tf.variable_scope(name_or_scope=var_scp, reuse=reuse): 20 | weight_collects = [ops.GraphKeys.GLOBAL_VARIABLES, ops.GraphKeys.MODEL_VARIABLES] 21 | hop0_out, ub0_triangle_agg = triangle_net(ub0_triangle_node, ub0_triangle_score, 22 | cand0_triangle_node, cand0_triangle_score, pos_aware_tp_fea, var_scp="tn0") 23 | hop1_out, ub1_triangle_agg = triangle_net(ub1_triangle_node, ub1_triangle_score, 24 | cand1_triangle_node, cand1_triangle_score, pos_aware_tp_fea, var_scp="tn1") 25 | multi_seqs = tf.stack([hop0_out, hop1_out], axis=1) 26 | output = fusion_unit(multi_seqs, user_profile, var_scp="fu") 27 | if use_negsampling: 28 | return output, ub0_triangle_agg, ub1_triangle_agg 29 | else: 30 | return output 31 | 32 | 33 | def triangle_net(ub_triangle_node, ub_triangle_score, cand_triangle_node, cand_triangle_score, 34 | pos_aware_tp_fea, var_scp, reuse=tf.AUTO_REUSE): 35 | """ 36 | Args: 37 | ub_triangle_node: [B, SEQ_LENGTH, TRIANGLE_NUM*3, H] 38 | ub_triangle_score: [B, SEQ_LENGTH, TRIANGLE_NUM, 1] 39 | cand_triangle_node: [B, TRIANGLE_NUM*3, H] 40 | cand_triangle_score: [B, TRIANGLE_NUM, 1] 41 | pos_aware_tp_fea: [B, T, H] 42 | Output: 43 | output: [B, H] 44 | """ 45 | with tf.variable_scope(name_or_scope=var_scp, reuse=reuse): 46 | #------------------------------- History triangle aggregation -------------------------------# 47 | ub_tri_shape = ub_triangle_node.get_shape().as_list() 48 | ub_triangle_node_reshape = tf.reshape(ub_triangle_node, [-1, ub_tri_shape[1], int(ub_tri_shape[2] / 3), 3, ub_tri_shape[3]]) 49 | ub_triangle_score_reshape = tf.reshape(ub_triangle_score, [-1, ub_tri_shape[1], int(ub_tri_shape[2] / 3), 1]) 50 | # [B, SEQ_LENGTH, H] 51 | ub_triangle_agg = triangle_aggregation(ub_triangle_node_reshape, ub_triangle_score_reshape, 52 | "agg", single_tri_agg_flag, multi_tri_agg_flag, hidden_units=HIDDEN_SIZE) 53 | 54 | #----------------------------- Candidate triangle aggregation -----------------------------# 55 | cand_tri_shape = cand_triangle_node.get_shape().as_list() 56 | cand_triangle_node_reshape = tf.reshape(cand_triangle_node, [-1, 1, int(cand_tri_shape[1] / 3), 3, cand_tri_shape[2]]) 57 | cand_triangle_score_reshape = tf.reshape(cand_triangle_score, [-1, 1, int(cand_tri_shape[1] / 3), 1]) 58 | cand_triangle_agg = triangle_aggregation(cand_triangle_node_reshape, cand_triangle_score_reshape, 59 | "agg", single_tri_agg_flag, multi_tri_agg_flag, hidden_units=HIDDEN_SIZE) 60 | # [B, H] 61 | cand_triangle_agg = tf.reshape(cand_triangle_agg, [-1, cand_triangle_agg.get_shape().as_list()[-1]]) 62 | 63 | # print("var_scp: " + var_scp + ", ub_triangle_agg shape: " + str(ub_triangle_agg.get_shape().as_list())) 64 | # print("var_scp: " + var_scp + ", cand_triangle_agg shape: " + str(cand_triangle_agg.get_shape().as_list())) 65 | 66 | #-------------------------------- aux_loss self-attention --------------------------------# 67 | if use_negsampling: 68 | # luwei:这个应该对应paper里的Behavior Refiner Layer 69 | ub_triangle_agg = aux_loss_mhsa(ub_triangle_agg, num_units=EMBEDDING_DIM*2, 70 | num_heads=4, dropout_rate=0., is_training=True) 71 | 72 | #------------------------------- position aware attention -------------------------------# 73 | pos_attn_output = pos_aware_attention(cand_triangle_agg, ub_triangle_agg, pos_aware_tp_fea, var_scp + "_pos_attn") 74 | # luwei: 文章里好像没有这一步 75 | output = tf.concat([pos_attn_output, cand_triangle_agg], -1) 76 | return output, ub_triangle_agg 77 | 78 | 79 | def triangle_aggregation(triangle_node, triangle_weight, var_scp, single_tri_agg="reduce_mean", multi_tri_agg="weighted_sum", 80 | hidden_units=HIDDEN_SIZE, reuse=tf.AUTO_REUSE): 81 | """ 82 | Args: 83 | triangle_node: [B, SEQ_LENGTH, TRIANGLE_NUM, 3, H] 84 | triangle_weight: [B, SEQ_LENGTH, TRIANGLE_NUM, 1] 85 | # node-pooling >>> triangle-pooling 86 | single_tri_agg: reduce_mean| mhsa 87 | multi_tri_agg: reduce_mean | weighted_sum | mhsa 88 | Output: 89 | output: [B, SEQ_LENGTH, H] 90 | """ 91 | with tf.variable_scope(name_or_scope=var_scp, reuse=reuse): 92 | weight_collects = [ops.GraphKeys.GLOBAL_VARIABLES, ops.GraphKeys.MODEL_VARIABLES] 93 | # Intra-triangle Aggregation Layer. 94 | if single_tri_agg == "reduce_mean": 95 | triangle_vect = tf.reduce_mean(triangle_node, axis=3) 96 | else: 97 | print('other aggregation strategies...') 98 | # triangle_node_shape = triangle_node.get_shape().as_list() 99 | # triangle_node_stack = tf.reshape(triangle_node, [-1, triangle_node_shape[3], triangle_node_shape[4]]) 100 | # triangle_vect = average_mhsa(triangle_node_stack, var_scp="single_mhsa") 101 | triangle_vect = tf.reduce_mean(triangle_vect, axis=3) 102 | 103 | w = tf.get_variable('w', [triangle_vect.get_shape().as_list()[-1], hidden_units], 104 | initializer=tf.random_normal_initializer(mean=0, stddev=0.1, dtype=tf.float32), 105 | collections=weight_collects) 106 | b = tf.get_variable('b', [hidden_units], initializer=tf.constant_initializer(0.0), collections=weight_collects) 107 | triangle_output = tf.einsum("ijkl,lm->ijkm", triangle_vect, w) + b 108 | 109 | # Inter-triangle Aggregation Layer. 110 | if multi_tri_agg == "mhsa": 111 | triangle_ouput_shape = triangle_output.get_shape().as_list() 112 | # [B * SEQ_LENGTH, TRIANGLE_NUM, H] 113 | triangle_stack = tf.reshape(triangle_output, [-1, triangle_ouput_shape[2], triangle_ouput_shape[3]]) 114 | # [B * SEQ_LENGTH, TRIANGLE_NUM, H] 115 | output = mhsa_inter_triangle_aggregation(triangle_stack, 116 | num_units=EMBEDDING_DIM*2, 117 | num_heads=4, 118 | dropout_rate=0., 119 | is_training=True) 120 | # [B, SEQ_LENGTH, TRIANGLE_NUM, H] 121 | output = tf.reshape(output, [-1, 122 | triangle_ouput_shape[1], 123 | triangle_ouput_shape[2], 124 | output.get_shape().as_list()[-1]]) 125 | # [B, SEQ_LENGTH, H] 126 | output = tf.reduce_mean(output, axis=2) 127 | elif multi_tri_agg == "weighted_sum": 128 | output = tf.reduce_sum(triangle_output * triangle_weight, axis=2) / (tf.reduce_sum(triangle_weight, axis=2) + 1e-9) 129 | else: 130 | output = tf.reduce_mean(triangle_output, axis=2) 131 | return output 132 | 133 | 134 | def layer_norm(inputs, name, epsilon=1e-8): 135 | mean, variance = tf.nn.moments(inputs, [-1], keep_dims=True) 136 | normalized = (inputs - mean) / (tf.sqrt(variance + epsilon)) 137 | 138 | params_shape = inputs.get_shape()[-1:] 139 | gamma = tf.get_variable(name+'gamma', params_shape, tf.float32, tf.ones_initializer()) 140 | beta = tf.get_variable(name+'beta', params_shape, tf.float32, tf.zeros_initializer()) 141 | 142 | outputs = gamma * normalized + beta 143 | return outputs 144 | 145 | 146 | def mhsa_inter_triangle_aggregation(inputs, num_units, num_heads, dropout_rate, name="", 147 | is_training=True, is_layer_norm=True, keys_missing_value=0): 148 | """ 149 | Args: 150 | inputs: [B*T, TRIANGLE_NUM, H] 151 | Output: 152 | outputs: [B*T, TRIANGLE_NUM, H] 153 | """ 154 | x_shape = inputs.get_shape().as_list() 155 | keys_empty = tf.reduce_sum(inputs, axis=2) # [B*T, TRIANGLE_NUM] 156 | keys_empty_cond = tf.equal(keys_empty, keys_missing_value) 157 | keys_empty_cond = tf.expand_dims(keys_empty_cond, 2) 158 | #Luwei: [B*T*num_heads, TRIANGLE_NUM, TRIANGLE_NUM] 159 | keys_empty_cond = tf.tile(keys_empty_cond, [num_heads, 1, x_shape[1]]) # [B*T, TRIANGLE_NUM, TRIANGLE_NUM] 160 | 161 | Q_K_V = tf.layers.dense(inputs, 3 * num_units) 162 | Q, K, V = tf.split(Q_K_V, 3, -1) 163 | Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # [B*T*num_heads, TRIANGLE_NUM, num_units] 164 | K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # [B*T*num_heads, TRIANGLE_NUM, num_units] 165 | V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # [B*T*num_heads, TRIANGLE_NUM, num_units] 166 | 167 | outputs = tf.matmul(Q_, tf.transpose(K_, [0, 2, 1])) # [B*T*num_heads, TRIANGLE_NUM, TRIANGLE_NUM] 168 | align= outputs / (36 ** 0.5) 169 | # Diag mask 170 | diag_val = tf.ones_like(align[0, :, :]) # [TRIANGLE_NUM, TRIANGLE_NUM] 171 | tril = tf.linalg.LinearOperatorLowerTriangular(diag_val).to_dense() # [TRIANGLE_NUM, TRIANGLE_NUM] 172 | key_masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(align)[0], 1, 1]) # [B*T*num_heads, TRIANGLE_NUM, TRIANGLE_NUM] 173 | paddings = tf.ones_like(key_masks) * (-2 ** 32 + 1) 174 | 175 | # [B*T*num_heads, TRIANGLE_NUM, TRIANGLE_NUM] 176 | outputs = tf.where(tf.equal(key_masks, 0), paddings, align) 177 | outputs = tf.where(keys_empty_cond, paddings, outputs) 178 | outputs = tf.nn.softmax(outputs) # [B*T*num_heads, TRIANGLE_NUM, TRIANGLE_NUM] 179 | # Attention Matmul 180 | outputs = tf.layers.dropout(outputs, dropout_rate, training=is_training) 181 | outputs = tf.matmul(outputs, V_) 182 | outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2) # [B*T, TRIANGLE_NUM, num_units] 183 | 184 | outputs = tf.layers.dense(outputs, num_units) # [B*T, TRIANGLE_NUM, num_units] 185 | outputs = tf.layers.dropout(outputs, dropout_rate, training=is_training) 186 | # Residual connection 187 | outputs += inputs 188 | # Normalize 189 | if is_layer_norm: 190 | outputs = layer_norm(outputs,name=name) 191 | 192 | # LUWEI:这里为啥要加这两层Dense?在DMIN里是没有的 193 | outputs1 = tf.layers.dense(outputs, EMBEDDING_DIM*4, activation=tf.nn.relu) 194 | outputs1 = tf.layers.dense(outputs1, EMBEDDING_DIM*2) 195 | outputs = outputs1+outputs 196 | return outputs 197 | 198 | 199 | def pos_aware_attention(queries, keys, keys_tp, var_scp, hidden_units=HIDDEN_SIZE, keys_missing_value=0, reuse=tf.AUTO_REUSE): 200 | """ 201 | # pos aware attention H = V^T * relu(w_h * h_i + w_tp * h_tp_i + b) 202 | Args: 203 | queries: cand_triangle_agg [B, H] 204 | keys: ub_triangle_agg [B, T, H] 205 | keys_tp: pos_aware_tp_fea [B, T, H_tp] 206 | Output: 207 | output: [B, H] 208 | """ 209 | with tf.variable_scope(name_or_scope=var_scp, reuse=reuse): 210 | weight_collects = [ops.GraphKeys.GLOBAL_VARIABLES, ops.GraphKeys.MODEL_VARIABLES] 211 | queries_hidden_units = queries.get_shape().as_list()[-1] 212 | queries = tf.tile(queries, [1, tf.shape(keys)[1]]) # [B, T*H] 213 | queries = tf.reshape(queries, [-1, tf.shape(keys)[1], queries_hidden_units]) # [B, T, H] 214 | 215 | #-----------------------------------------------------------------------------------------------# 216 | w_q = tf.get_variable('w_q', [queries.get_shape().as_list()[-1], hidden_units], 217 | initializer=tf.random_normal_initializer(mean=0, stddev=0.1, dtype=tf.float32), 218 | collections=weight_collects) 219 | w_h = tf.get_variable('w_h', [keys.get_shape().as_list()[-1], hidden_units], 220 | initializer=tf.random_normal_initializer(mean=0, stddev=0.1, dtype=tf.float32), 221 | collections=weight_collects) 222 | w_tp = tf.get_variable('w_tp', [keys_tp.get_shape().as_list()[-1], hidden_units], 223 | initializer=tf.random_normal_initializer(mean=0, stddev=0.1, dtype=tf.float32), 224 | collections=weight_collects) 225 | b = tf.get_variable('b', [hidden_units], initializer=tf.constant_initializer(0.0), collections=weight_collects) 226 | # print("var_scp: " + var_scp + ", keys shape: " + str(keys.get_shape().as_list())) 227 | # print("var_scp: " + var_scp + ", keys_tp shape: " + str(keys_tp.get_shape().as_list())) 228 | 229 | #-----------------------------------------------------------------------------------------------# 230 | din_hidden_fea = tf.nn.leaky_relu(tf.einsum("ijk,kl->ijl", keys, w_h) + tf.einsum("ijk,kl->ijl", 231 | keys_tp, w_tp) + b) # [B, T, h] 232 | # print("var_scp: " + var_scp + ", din_hidden_fea shape: " + str(din_hidden_fea.get_shape().as_list())) 233 | # print("var_scp: " + var_scp + ", queries shape: " + str(queries.get_shape().as_list())) 234 | 235 | outputs = tf.reduce_mean(tf.multiply(tf.einsum("ijk,kl->ijl", queries, w_q), din_hidden_fea), axis=2) 236 | outputs = tf.expand_dims(outputs, axis=1) 237 | 238 | keys_empty = tf.reduce_sum(keys, axis=2) # [B, T] 239 | keys_empty_cond = tf.equal(keys_empty, keys_missing_value) # [B, T] 240 | keys_empty_cond = tf.reshape(keys_empty_cond, [-1, 1, keys_empty_cond.get_shape().as_list()[-1]]) # [bs, 1, max_len] 241 | 242 | # Scale 243 | outputs = outputs / (keys.get_shape().as_list()[-1] ** 0.5) 244 | # Activation 245 | paddings = tf.ones_like(outputs) * (-2 ** 32 + 1) 246 | outputs = tf.where(keys_empty_cond, paddings, outputs) 247 | weighted = tf.nn.softmax(outputs) # [B, 1, T] 248 | # Weighted sum 249 | weighted_sum = tf.matmul(weighted, keys) # [B, 1, H] 250 | weighted_sum = tf.reshape(weighted_sum, [-1, keys.get_shape().as_list()[-1]]) 251 | 252 | return weighted_sum 253 | 254 | 255 | def fusion_unit(multi_seqs, user_profile, var_scp, hidden_units=HIDDEN_SIZE, keys_missing_value=0, reuse=tf.AUTO_REUSE): 256 | """ 257 | Args: 258 | multi_seqs: [B, X, H] 259 | user_profile: [B, H_up] 260 | Output: 261 | output: [B, H] 262 | """ 263 | with tf.variable_scope(name_or_scope=var_scp, reuse=reuse): 264 | #--------------------------------------------------------------------------------------------------------------------# 265 | weight_collects = [ops.GraphKeys.GLOBAL_VARIABLES, ops.GraphKeys.MODEL_VARIABLES] 266 | 267 | w_up = tf.get_variable('w_up', [user_profile.get_shape().as_list()[-1], hidden_units], 268 | initializer=tf.random_normal_initializer(mean=0, stddev=0.1, dtype=tf.float32), 269 | collections=weight_collects) 270 | b_up = tf.get_variable('b_up', [hidden_units], initializer=tf.constant_initializer(0.0), collections=weight_collects) 271 | user_profile_output = tf.matmul(user_profile, w_up) + b_up # [B, hidden_units] 272 | 273 | w_ms = tf.get_variable('w_ms', [multi_seqs.get_shape().as_list()[-1], hidden_units], 274 | initializer=tf.random_normal_initializer(mean=0, stddev=0.1, dtype=tf.float32), 275 | collections=weight_collects) 276 | b_ms = tf.get_variable('b_ms', [hidden_units], initializer=tf.constant_initializer(0.0), collections=weight_collects) 277 | multi_seqs_output = tf.einsum("ijk,kl->ijl", multi_seqs, w_ms) + b_ms # [B, X, hidden_units] 278 | #--------------------------------------------------------------------------------------------------------------------# 279 | 280 | user_profile_output = tf.tile(user_profile_output, [1, tf.shape(multi_seqs_output)[1]]) # [B, X*H] 281 | user_profile_output = tf.reshape(user_profile_output, [-1, tf.shape(multi_seqs_output)[1], 282 | multi_seqs_output.get_shape().as_list()[-1]]) # [B, X, hidden_units] 283 | 284 | keys_empty = tf.reduce_sum(multi_seqs, axis=2) # [B, X] 285 | keys_empty_cond = tf.equal(keys_empty, keys_missing_value) # [B, X] 286 | keys_empty_cond = tf.reshape(keys_empty_cond, [-1, 1, keys_empty_cond.get_shape().as_list()[-1]]) # [B, 1, X] 287 | outputs = tf.reduce_mean(tf.multiply(multi_seqs_output, user_profile_output), axis=2) 288 | outputs = tf.reshape(outputs, [-1, 1, outputs.get_shape().as_list()[-1]]) # [B, 1, X] 289 | 290 | # Scale 291 | outputs = outputs / (multi_seqs.get_shape().as_list()[-1] ** 0.5) 292 | # Activation 293 | paddings = tf.ones_like(outputs) * (-2 ** 32 + 1) 294 | outputs = tf.where(keys_empty_cond, paddings, outputs) 295 | weighted = tf.nn.softmax(outputs) # [B, 1, T] 296 | # Weighted sum 297 | weighted_sum = tf.matmul(weighted, multi_seqs) # [B, 1, H] 298 | weighted_sum = tf.reshape(weighted_sum, [-1, multi_seqs.get_shape().as_list()[-1]]) 299 | 300 | return weighted_sum 301 | 302 | 303 | def aux_loss_mhsa(inputs, num_units, num_heads, dropout_rate, name="", is_training=True, is_layer_norm=True): 304 | """ 305 | Args: 306 | inputs: [B, T, H] 307 | Output: 308 | outputs:[B*T, TRIANGLE_NUM, H] 309 | """ 310 | Q_K_V = tf.layers.dense(inputs, 3 * num_units) 311 | Q, K, V = tf.split(Q_K_V, 3, -1) 312 | Q_ = tf.concat(tf.split(Q, num_heads, axis=2), axis=0) # [B*num_heads, T, num_units] 313 | K_ = tf.concat(tf.split(K, num_heads, axis=2), axis=0) # [B*num_heads, T, num_units] 314 | V_ = tf.concat(tf.split(V, num_heads, axis=2), axis=0) # [B*num_heads, T, num_units] 315 | 316 | outputs = tf.matmul(Q_, tf.transpose(K_, [0, 2, 1])) # [B*num_heads, T, T] 317 | align= outputs / (36 ** 0.5) 318 | # Diag mask 319 | diag_val = tf.ones_like(align[0, :, :]) 320 | tril = tf.linalg.LinearOperatorLowerTriangular(diag_val).to_dense() 321 | 322 | key_masks = tf.tile(tf.expand_dims(tril, 0), [tf.shape(align)[0], 1, 1]) # [B*num_heads, T, T] 323 | padding = tf.ones_like(key_masks) * (-2 ** 32 + 1) 324 | outputs = tf.where(tf.equal(key_masks, 0), padding, align) 325 | outputs = tf.nn.softmax(outputs) 326 | 327 | # Attention Matmul 328 | outputs = tf.layers.dropout(outputs, dropout_rate, training=is_training) 329 | outputs = tf.matmul(outputs,V_) 330 | outputs = tf.concat(tf.split(outputs, num_heads, axis=0), axis=2) 331 | outputs = tf.layers.dense(outputs, num_units) 332 | outputs = tf.layers.dropout(outputs, dropout_rate, training=is_training) 333 | 334 | # Residual connection 335 | outputs += inputs 336 | # Normalize 337 | if is_layer_norm: 338 | outputs = layer_norm(outputs,name=name) 339 | return outputs 340 | -------------------------------------------------------------------------------- /script/dataset_process/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 10:49 4 | -------------------------------------------------------------------------------- /script/dataset_process/content_wise/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/3 22:33 4 | -------------------------------------------------------------------------------- /script/dataset_process/content_wise/generate_voc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/3 22:40 4 | 5 | import pickle 6 | import os 7 | 8 | data_dir = "processed_data" 9 | 10 | f_train = open(os.path.join(data_dir, "local_train_splitByUser_cw"), "r") 11 | uid_dict = {} 12 | itemid_dict = {} 13 | series_dict = {} 14 | 15 | iddd = 0 16 | for line in f_train: 17 | elements = line.strip("\n").split("\t") 18 | clk = elements[0] 19 | uid = elements[1] 20 | iid = elements[2] 21 | series_id = elements[3] 22 | item_id_list = elements[4] 23 | series_id_list = elements[5] 24 | if uid not in uid_dict: 25 | uid_dict[uid] = 0 26 | uid_dict[uid] += 1 27 | if iid not in itemid_dict: 28 | itemid_dict[iid] = 0 29 | itemid_dict[iid] += 1 30 | if series_id not in series_dict: 31 | series_dict[series_id] = 0 32 | series_dict[series_id] += 1 33 | 34 | for i in item_id_list.split(""): 35 | if i not in itemid_dict: 36 | itemid_dict[i] = 0 37 | itemid_dict[i] += 1 38 | iddd += 1 39 | for s in series_id_list.split(""): 40 | if s not in series_dict: 41 | series_dict[s] = 0 42 | series_dict[s] += 1 43 | 44 | # 经常出现的排在前面 45 | sorted_uid_dict = sorted(uid_dict.items(), key=lambda x: x[1], reverse=True) 46 | sorted_itemid_dict = sorted(itemid_dict.items(), key=lambda x: x[1], reverse=True) 47 | sorted_series_dict = sorted(series_dict.items(), key=lambda x: x[1], reverse=True) 48 | 49 | uid_voc = {} 50 | index = 0 51 | for key, value in sorted_uid_dict: 52 | uid_voc[key] = index 53 | index += 1 54 | 55 | itemid_voc = {} 56 | itemid_voc['default_item_id'] = 0 57 | index = 1 58 | for key, value in sorted_itemid_dict: 59 | itemid_voc[key] = index 60 | index += 1 61 | 62 | series_voc = {} 63 | series_voc['default_series'] = 0 64 | index = 1 65 | for key, value in sorted_series_dict: 66 | series_voc[key] = index 67 | index += 1 68 | 69 | # for python3 70 | pickle.dump(uid_voc, open(os.path.join(data_dir, "uid_voc_cw.pkl"), "wb")) 71 | pickle.dump(itemid_voc, open(os.path.join(data_dir, "itemid_voc_cw.pkl"), "wb")) 72 | pickle.dump(series_voc, open(os.path.join(data_dir, "series_voc_cw.pkl"), "wb")) -------------------------------------------------------------------------------- /script/dataset_process/content_wise/local_aggregator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/3 22:38 4 | 5 | import os 6 | 7 | data_dir = "processed_data" 8 | 9 | fin = open(os.path.join(data_dir, "jointed-new-split-info-cw"), "r") 10 | ftrain = open(os.path.join(data_dir, "local_train_cw"), "w") 11 | ftest = open(os.path.join(data_dir, "local_test_cw"), "w") 12 | 13 | last_user = "0" 14 | common_fea = "" 15 | line_idx = 0 16 | item_id_list = [] 17 | series_id_list = [] 18 | time_stamp_list = [] 19 | for line in fin: 20 | elements = line.strip().split('\t') 21 | ds = elements[0] 22 | clk = int(elements[1]) 23 | user = elements[2] 24 | item_id = elements[3] 25 | dt = elements[-1] 26 | series_id = elements[4] 27 | time_stamp = elements[12] 28 | 29 | if ds == "20180118": 30 | fo = ftrain 31 | else: 32 | fo = ftest 33 | 34 | if user != last_user: 35 | item_id_list = [] 36 | series_id_list = [] 37 | time_stamp_list =[] 38 | else: 39 | history_clk_num = len(item_id_list) 40 | item_str = "" 41 | series_str = "" 42 | time_stamp_str = "" 43 | for s in series_id_list: 44 | series_str += s + "" 45 | for i in item_id_list: 46 | item_str += i + "" 47 | for t in time_stamp_list: 48 | time_stamp_str += t + "" 49 | if len(series_str) > 0: series_str = series_str[:-1] # remove last char "" 50 | if len(item_str) > 0: item_str = item_str[:-1] 51 | if len(time_stamp_str) >0 :time_stamp_str = time_stamp_str[:-1] 52 | if history_clk_num >= 10: 53 | # if history_clk_num >= 1: 54 | # 把用户历史行为的item和series也聚合在了一起。 55 | print(str(clk) + "\t" + user + "\t" + item_id + "\t" + series_id + "\t" + time_stamp + "\t" + item_str + "\t" + series_str + "\t" + time_stamp_str, end="\n", file=fo) 56 | last_user = user 57 | if clk: 58 | item_id_list.append(item_id) 59 | series_id_list.append(series_id) 60 | time_stamp_list.append(time_stamp) 61 | 62 | line_idx += 1 -------------------------------------------------------------------------------- /script/dataset_process/content_wise/prepare_data_cw.sh: -------------------------------------------------------------------------------- 1 | python process_data.py 2 | python local_aggregator.py 3 | python split_by_user.py 4 | python generate_voc.py -------------------------------------------------------------------------------- /script/dataset_process/content_wise/process_data.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/3 22:33 4 | 5 | import random 6 | import os 7 | import dask.dataframe as ddf 8 | 9 | data_dir = "processed_data" 10 | 11 | 12 | def read_cw_datasets(): 13 | # parquet data files 14 | file_name_interaction = ".../ContentWiseImpressions/data/CW10M/interactions" 15 | file_name_imp_direct_link = ".../ContentWiseImpressions/data/CW10M/impressions-direct-link" 16 | file_name_imp_non_direct_link = ".../ContentWiseImpressions/data/CW10M/impressions-non-direct-link" 17 | 18 | data_interaction = ddf.read_parquet(file_name_interaction, engine="pyarrow") 19 | # data_imp_direct_link = ddf.read_parquet(file_name_imp_direct_link, engine="pyarrow") 20 | # data_imp_non_direct_link = ddf.read_parquet(file_name_imp_non_direct_link, engine="pyarrow") 21 | 22 | # sample 10% records in the original data 23 | data_interaction_sampled = data_interaction.sample(frac=0.25, random_state=132) 24 | 25 | print(data_interaction_sampled) 26 | print("data_interaction shape: {}".format(data_interaction_sampled.shape)) 27 | print(data_interaction_sampled.head()) 28 | 29 | if not os.path.exists(data_dir): 30 | os.mkdir(data_dir) 31 | 32 | # format: [unixtime, user_id, item_id, series_id, episode_number, series_length, item_type, recommendation_id, 33 | # interaction_type, version_factor, explicit_rating] 34 | data_interaction_sampled.to_csv(os.path.join(data_dir, 'data_interaction_sampled.csv'), single_file=True) 35 | 36 | # reformat the data, put the unixtime at the end 37 | # format: [user_id, item_id, series_id, episode_number, series_length, item_type, recommendation_id, 38 | # interaction_type, version_factor, explicit_rating, unixtime] 39 | fi = open(os.path.join(data_dir, 'data_interaction_sampled.csv'), "r") 40 | fo = open(os.path.join(data_dir, "user_log_info"), "w") 41 | for line in fi: 42 | elements = line.strip().split(",") 43 | if elements[0] == "utc_ts_milliseconds": 44 | continue 45 | 46 | elements_new = elements[1:] 47 | elements_new.append(elements[0]) 48 | # format as [user, item, ... , timestamp] 49 | print("\t".join(elements_new), end="\n", file=fo) 50 | 51 | 52 | def process_meta(): 53 | fi = open(os.path.join(data_dir, "user_log_info"), "r") 54 | fo = open(os.path.join(data_dir, "item-info_cw"), "w") 55 | item_info = dict() 56 | for line in fi: 57 | elements = line.strip().split("\t") 58 | item_id = elements[1] 59 | series_id = elements[2] 60 | item_info[item_id] = series_id 61 | 62 | for item_id, series_id in item_info.items(): 63 | print(item_id + "\t" + series_id, end="\n", file=fo) 64 | 65 | 66 | def manual_join(): 67 | # aggregate user behaviors 68 | user_map = {} 69 | item_list = [] 70 | item_info_map = {} 71 | with open(os.path.join(data_dir, 'user_log_info'), "r") as file: 72 | for line in file: 73 | # format: [user_id, item_id, series_id, episode_number, series_length, item_type, recommendation_id, 74 | # interaction_type, version_factor, explicit_rating, unixtime] 75 | elements = line.strip().split("\t") 76 | 77 | # aggregate user behaviors 78 | if elements[0] not in user_map: 79 | user_map[elements[0]] = [] 80 | user_map[elements[0]].append(("\t".join(elements), float(elements[-1]))) 81 | 82 | item_list.append(elements[1]) 83 | 84 | # store item information [series_id, episode_number, series_length, item_type] 85 | if elements[1] not in item_info_map: 86 | item_info_map[elements[1]] = elements[2:6] 87 | 88 | # sampling the negative samples 89 | fo = open(os.path.join(data_dir, "jointed-new-cw"), "w") 90 | for key in user_map: 91 | # sort the user behavior items by unix timestamp 92 | sorted_user_hb = sorted(user_map[key], key=lambda x: x[1]) 93 | for line, t in sorted_user_hb: 94 | elements = line.split("\t") 95 | item = elements[1] 96 | j = 0 97 | while True: 98 | # sampling the negative samples 99 | item_neg_index = random.randint(0, len(item_list) - 1) 100 | item_neg = item_list[item_neg_index] 101 | if item_neg == item: 102 | continue 103 | elements[1] = item_neg 104 | elements[2:6] = item_info_map[item_neg] # store the negative item information 105 | print("0" + "\t" + "\t".join(elements), end="\n", file=fo) 106 | j += 1 107 | if j == 1: # negative sampling frequency 108 | break 109 | print("1" + "\t" + line, end="\n", file=fo) 110 | 111 | 112 | # put the split mark 113 | def split_test(): 114 | fi = open(os.path.join(data_dir, "jointed-new-cw"), "r") 115 | fo = open(os.path.join(data_dir, "jointed-new-split-info-cw"), "w") 116 | # 统计user在行为表里出现的次数 117 | user_count = {} 118 | for line in fi: 119 | elements = line.strip().split('\t') 120 | user = elements[1] 121 | if user not in user_count: 122 | user_count[user] = 0 123 | user_count[user] += 1 124 | fi.seek(0) 125 | 126 | i = 0 127 | last_user = "abcd1234" 128 | for line in fi: 129 | line = line.strip() 130 | elements = line.split("\t") 131 | user = elements[1] 132 | if user == last_user: 133 | if i < user_count[user] - 2: # 1 +negative samples 134 | print("20180118" + "\t" + line, end="\n", file=fo) # for python3 135 | else: # 每个用户的最后一个行为和其负样本 136 | print("20190119" + "\t" + line, end="\n", file=fo) # for python3 137 | else: 138 | last_user = user 139 | i = 0 140 | if i < user_count[user] - 2: 141 | print("20180118" + "\t" + line, end="\n", file=fo) # for python3 142 | else: # 每个用户的最后一个行为和其负样本 143 | print("20190119" + "\t" + line, end="\n", file=fo) # for python3 144 | i += 1 145 | 146 | 147 | read_cw_datasets() 148 | process_meta() 149 | manual_join() 150 | split_test() -------------------------------------------------------------------------------- /script/dataset_process/content_wise/split_by_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/3 22:39 4 | 5 | import random 6 | import os 7 | 8 | data_dir = "processed_data" 9 | 10 | fi = open(os.path.join(data_dir, "local_train_cw"), "r") 11 | ftrain = open(os.path.join(data_dir, "local_train_splitByUser_cw"), "w") 12 | fvalid = open(os.path.join(data_dir, "local_valid_splitByUser_cw"), "w") 13 | ftest = open(os.path.join(data_dir, "local_test_splitByUser_cw"), "w") 14 | while True: 15 | rand_int = random.randint(1, 10) 16 | noclk_line = fi.readline().strip() 17 | clk_line = fi.readline().strip() 18 | if noclk_line == "" or clk_line == "": 19 | break 20 | if noclk_line != "" or clk_line != "": 21 | clk_line_str = clk_line.strip("\n").split("\t") 22 | time_stamp_target = clk_line_str[4] 23 | time_stamp_trigger = clk_line_str[7].split("")[-1] 24 | if int(time_stamp_target) - int(time_stamp_trigger)>28800000: 25 | continue 26 | if rand_int == 2: 27 | print(noclk_line, end="\n", file=ftest) # for python3 28 | print(clk_line, end="\n", file=ftest) # for python3 29 | elif rand_int == 4: 30 | print(noclk_line, end="\n", file=fvalid) # for python3 31 | print(clk_line, end="\n", file=fvalid) # for python3 32 | else: 33 | print(noclk_line, end="\n", file=ftrain) # for python3 34 | print(clk_line, end="\n", file=ftrain) # for python3 -------------------------------------------------------------------------------- /script/dataset_process/data_iterator.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (C) 2016-2018 Alibaba Group Holding Limited 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # ============================================================================== 16 | 17 | 18 | import numpy 19 | import json 20 | #import cPickle as pkl 21 | import _pickle as pkl 22 | import random 23 | import gzip 24 | from dataset_process import shuffle 25 | 26 | 27 | def unicode_to_utf8(d): 28 | return dict((key.encode("UTF-8"), value) for (key, value) in d.items()) 29 | 30 | 31 | def load_dict(filename): 32 | try: 33 | with open(filename, 'rb') as f: 34 | return unicode_to_utf8(json.load(f)) 35 | except: 36 | with open(filename, 'rb') as f: 37 | return pkl.load(f) 38 | #return unicode_to_utf8(pkl.load(f)) 39 | 40 | 41 | def fopen(filename, mode='r'): 42 | if filename.endswith('.gz'): 43 | return gzip.open(filename, mode) 44 | return open(filename, mode) 45 | 46 | 47 | class DataIterator: 48 | def __init__(self, source, 49 | uid_voc, 50 | mid_voc, 51 | cat_voc, 52 | item_info, 53 | reviews_info, 54 | batch_size=128, 55 | maxlen=20, 56 | maxlen_hard=10, 57 | skip_empty=False, 58 | shuffle_each_epoch=False, 59 | sort_by_length=True, 60 | max_batch_size=20, 61 | minlen=None): 62 | # shuffle the input file 63 | if shuffle_each_epoch: 64 | self.source_orig = source 65 | self.source = shuffle.main(self.source_orig, temporary=True) 66 | else: 67 | self.source = fopen(source, 'r') 68 | self.source_dicts = [] 69 | for source_dict in [uid_voc, mid_voc, cat_voc]: 70 | self.source_dicts.append(load_dict(source_dict)) 71 | 72 | # Mapping Dict: {item:category} 73 | f_meta = open(item_info, "r") 74 | meta_map = {} 75 | for line in f_meta: 76 | arr = line.strip().split("\t") 77 | if arr[0] not in meta_map: 78 | meta_map[arr[0]] = arr[1] 79 | self.meta_id_map = {} 80 | for key in meta_map: 81 | val = meta_map[key] 82 | if key in self.source_dicts[1]: 83 | mid_idx = self.source_dicts[1][key] 84 | else: 85 | mid_idx = 0 86 | if val in self.source_dicts[2]: 87 | cat_idx = self.source_dicts[2][val] 88 | else: 89 | cat_idx = 0 90 | self.meta_id_map[mid_idx] = cat_idx 91 | 92 | # Get all the interacted items 93 | f_review = open(reviews_info, "r") #[user, item, rating, timestamp] 94 | self.mid_list_for_random = [] 95 | for line in f_review: 96 | arr = line.strip().split("\t") 97 | tmp_idx = 0 98 | if arr[1] in self.source_dicts[1]: # if the item exsist, 99 | tmp_idx = self.source_dicts[1][arr[1]] # get item's ID 100 | self.mid_list_for_random.append(tmp_idx) # list of all the interacted items 101 | 102 | self.batch_size = batch_size 103 | self.maxlen = maxlen 104 | self.maxlen_hard = maxlen_hard 105 | self.minlen = minlen 106 | self.skip_empty = skip_empty 107 | self.n_uid = len(self.source_dicts[0]) 108 | self.n_mid = len(self.source_dicts[1]) 109 | self.n_cat = len(self.source_dicts[2]) 110 | 111 | self.shuffle = shuffle_each_epoch 112 | self.sort_by_length = sort_by_length 113 | 114 | self.source_buffer = [] 115 | self.k = batch_size * max_batch_size 116 | 117 | self.end_of_data = False 118 | 119 | def get_n(self): 120 | return self.n_uid, self.n_mid, self.n_cat 121 | 122 | def __iter__(self): 123 | return self 124 | 125 | def reset(self): 126 | if self.shuffle: 127 | self.source = shuffle.main(self.source_orig, temporary=True) 128 | else: 129 | self.source.seek(0) 130 | 131 | def __next__(self): 132 | if self.end_of_data: 133 | self.end_of_data = False 134 | self.reset() 135 | raise StopIteration 136 | 137 | source = [] 138 | target = [] 139 | target_aux = [] 140 | 141 | # Buffer: ss is one line of local_train_splitByUser/local_test_splitByUser 142 | if len(self.source_buffer) == 0: 143 | for k_ in range(self.k): 144 | ss = self.source.readline() 145 | if ss == "": 146 | break 147 | self.source_buffer.append(ss.strip("\n").split("\t")) 148 | 149 | # sort by history behavior length 150 | if self.sort_by_length: 151 | his_length = numpy.array([len(s[5].split("")) for s in self.source_buffer]) 152 | tidx = his_length.argsort() 153 | _sbuf = [self.source_buffer[i] for i in tidx] 154 | self.source_buffer = _sbuf 155 | else: 156 | self.source_buffer.reverse() 157 | 158 | if len(self.source_buffer) == 0: 159 | self.end_of_data = False 160 | self.reset() 161 | raise StopIteration 162 | 163 | try: 164 | ''' 165 | each ss, [label, user, item, category, time_stamp, [item list], [item cate list], [time_stamp_list] 166 | [['0', 167 | 'AZPJ9LUT0FEPY', 168 | 'B00AMNNTIA', 169 | 'Literature & Fiction', 170 | '0307744434\x020062248391\x020470530707\x020978924622\x021590516400', 171 | 'Books\x02Books\x02Books\x02Books\x02Books']] 172 | ''' 173 | while True: 174 | # read from source file and map to word index 175 | try: 176 | ss = self.source_buffer.pop() 177 | except IndexError: 178 | break 179 | uid = self.source_dicts[0][ss[1]] if ss[1] in self.source_dicts[0] else 0 180 | mid = self.source_dicts[1][ss[2]] if ss[2] in self.source_dicts[1] else 0 181 | cat = self.source_dicts[2][ss[3]] if ss[3] in self.source_dicts[2] else 0 182 | time_stamp = int(int(ss[4])/1000) 183 | tmp = [] 184 | for fea in ss[5].split(""): 185 | m = self.source_dicts[1][fea] if fea in self.source_dicts[1] else 0 186 | tmp.append(m) 187 | mid_list = tmp 188 | 189 | tmp1 = [] 190 | for fea in ss[6].split(""): 191 | c = self.source_dicts[2][fea] if fea in self.source_dicts[2] else 0 192 | tmp1.append(c) 193 | cat_list = tmp1 194 | 195 | # get the time_stamp 196 | tmp2 = [] 197 | for fea in ss[7].split(""): 198 | c = int(time_stamp/60 - int(fea)/1000/60) 199 | if c>=86400: 200 | c=86400 201 | tmp2.append(c) 202 | time_stamp_list = tmp2 203 | 204 | #get the trigger item 205 | trigger_mid = [] 206 | trigger_cate= [] 207 | 208 | if time_stamp_list[-1] <= 480: 209 | trigger_mid.append(mid_list[-1]) 210 | trigger_cate.append(cat_list[-1]) 211 | else: 212 | continue 213 | 214 | #generate the hard seq 215 | tmp_mid = [] 216 | tmp_cat = [] 217 | tmp_time =[] 218 | 219 | for index,cat_value in enumerate(cat_list): 220 | if cat_value == trigger_cate[0]: 221 | tmp_mid.append(mid_list[index]) 222 | tmp_cat.append(cat_value) 223 | tmp_time.append(time_stamp_list[index]) 224 | mid_hard_list = tmp_mid 225 | cat_hard_list = tmp_cat 226 | time_stamp_hard_list = tmp_time 227 | # print(len(mid_hard_list)) 228 | 229 | # read from source file and map to word index 230 | # if len(mid_list) > self.maxlen: 231 | # mid_list = mid_list[-self.maxlen:] 232 | # cat_list = cat_list[-self.maxlen:] 233 | # elif len(mid_list) < self.maxlen: 234 | # bu_len=self.maxlen-len(mid_list) 235 | # mid_list+=[0]*bu_len 236 | # cat_list+=[0]*bu_len 237 | 238 | # if len(mid_list) > self.maxlen: 239 | # continue 240 | # while len(mid_list) < self.maxlen: 241 | # mid_list.append(0) 242 | # cat_list.append(0) 243 | if self.minlen != None: 244 | if len(mid_list) <= self.minlen: 245 | continue 246 | if self.skip_empty and (not mid_list): 247 | continue 248 | 249 | #-------------------------------- Negative sample -------------------------------# 250 | noclk_mid_list = [] 251 | noclk_cat_list = [] 252 | for pos_mid in mid_list: 253 | noclk_tmp_mid = [] 254 | noclk_tmp_cat = [] 255 | noclk_index = 0 256 | while True: 257 | # Random sample negative item for (history records) mid_list 258 | # Including item+category,luwei: mid_list_for_random是按照reviews-info里的分布来得到的。所以负采样的时候也是按照这个分布 259 | noclk_mid_indx = random.randint(0, len(self.mid_list_for_random) - 1) 260 | noclk_mid = self.mid_list_for_random[noclk_mid_indx] 261 | if noclk_mid == pos_mid: 262 | continue 263 | noclk_tmp_mid.append(noclk_mid) 264 | noclk_tmp_cat.append(self.meta_id_map[noclk_mid]) 265 | noclk_index += 1 266 | if noclk_index >= 5: 267 | break 268 | noclk_mid_list.append(noclk_tmp_mid) 269 | noclk_cat_list.append(noclk_tmp_cat) 270 | #print("mid_list",len(mid_list)) 271 | #print("cat_list",len(cat_list)) 272 | #print("noclk_mid_list",len(noclk_mid_list)) 273 | # print("noclk_cat_list",len(noclk_cat_list)) 274 | source.append([uid, mid, cat, time_stamp, trigger_mid, trigger_cate, mid_list, cat_list, time_stamp_list, mid_hard_list, cat_hard_list, time_stamp_hard_list, noclk_mid_list, noclk_cat_list]) 275 | target.append([float(ss[0]), 1-float(ss[0])]) 276 | if cat==trigger_cate[0]: 277 | target_aux.append([1.0, 0.0]) 278 | else: 279 | target_aux.append([0.0, 1.0]) 280 | 281 | if len(source) >= self.batch_size or len(target) >= self.batch_size or len(target_aux) >= self.batch_size: 282 | break 283 | except IOError: 284 | self.end_of_data = True 285 | 286 | # all sentence pairs in maxibatch filtered out because of length 287 | if len(source) == 0 or len(target) == 0: 288 | source, target, target_aux = self.__next__() 289 | 290 | return source, target, target_aux -------------------------------------------------------------------------------- /script/dataset_process/gen_wnd_edges.py: -------------------------------------------------------------------------------- 1 | #coding=utf-8 2 | import os 3 | import sys 4 | import pickle 5 | import logging 6 | from tqdm import tqdm 7 | from collections import namedtuple, Counter 8 | 9 | data_path = "dataset/electronics" 10 | 11 | reviews_file = os.path.join(data_path, "reviews-info") 12 | train_file = os.path.join(data_path, "local_train_splitByUser") 13 | reviews_cache_file = os.path.join(data_path, "cache", "rating.pkl") 14 | edge_output_file_v1 = os.path.join(data_path, "graph", "edges_train.csv") 15 | 16 | if not os.path.exists(os.path.join(data_path, "cache")): 17 | os.mkdir(os.path.join(data_path, "cache")) 18 | if not os.path.exists(os.path.join(data_path, "graph")): 19 | os.mkdir(os.path.join(data_path, "graph")) 20 | 21 | 22 | SAMPLE_SEP = "\t" 23 | SEQ_SEP = "\x02" 24 | 25 | SeqItem = namedtuple("SeqItem", ["mid", "cate", "ts"]) 26 | Edge = namedtuple("Edge", ["frm", "to"]) 27 | WEdge = namedtuple("EdgeEx", ["frm", "to", "freq"]) 28 | 29 | Rating = namedtuple("Rating", ["uid", "mid", "score", "ts"]) 30 | logging.basicConfig(stream=sys.stdout, 31 | level=logging.INFO, 32 | format="%(asctime)s[%(levelname)s] %(message)s") 33 | 34 | def load_ratings(reviews_file, reviews_cache_file = None): 35 | if reviews_cache_file is not None and os.path.exists(reviews_cache_file): 36 | logging.info("loading reviews from cache: %s", reviews_cache_file) 37 | with open(reviews_cache_file, "rb") as fi: 38 | return pickle.load(fi) 39 | rating_map = {} 40 | total_reviews = 0 41 | logging.info("load ratings from {}".format(reviews_file)) 42 | # with open(reviews_file, encoding="utf8") as fi: 43 | with open(reviews_file) as fi: 44 | for i, line in tqdm(enumerate(fi)): 45 | [uid, mid, score, ts] = line.split("\t") 46 | rating = Rating(uid, mid, float(score), int(ts)) 47 | total_reviews += 1 48 | rating_map[(uid, mid)] = rating 49 | total_ratings = len(rating_map) 50 | logging.info("total reviews: %d, total ratings: %d", total_reviews, total_ratings) 51 | 52 | if reviews_cache_file is not None: 53 | with open(reviews_cache_file, "wb") as fo: 54 | pickle.dump(rating_map, fo) 55 | logging.info("caching reviews to %s", reviews_cache_file) 56 | return rating_map 57 | 58 | 59 | def check_order(item_list): 60 | last_ts = 0 61 | for item in item_list: 62 | ts = item.ts 63 | if ts >= last_ts: 64 | last_ts = ts 65 | continue 66 | else: 67 | return False 68 | return True 69 | 70 | def get_pred_edges(dataset_file, rating_map): 71 | edges = [] 72 | logging.info("loading pred edges from %s", dataset_file) 73 | # with open(dataset_file, encoding="utf8") as fi: 74 | with open(dataset_file) as fi: 75 | for i, line in tqdm(enumerate(fi)): 76 | [label, uid, mid, cate, his_mid_list, his_cate_list] = line.strip().split(SAMPLE_SEP) 77 | if label == "0": 78 | continue 79 | if his_mid_list.strip() == "": 80 | continue 81 | his_mid_list = his_mid_list.split(SEQ_SEP) 82 | his_cate_list = his_cate_list.split(SEQ_SEP) 83 | his_item_list = [SeqItem(hmid, hcate, rating_map[(uid, hmid)].ts) for hmid, hcate in zip(his_mid_list, his_cate_list)] 84 | item_list = his_item_list + [SeqItem(mid, cate, rating_map[(uid, mid)].ts)] 85 | if not check_order(item_list): 86 | logging.error("[INVALID ORDER]: {}".format(his_item_list)) 87 | continue 88 | edge = Edge(item_list[-2].mid, item_list[-1].mid) # edge directly connection last his item and target item. 89 | edges.append(edge) 90 | return set(edges) 91 | 92 | def gen_full_edges(train_file, rating_map, check_triangle=False): 93 | edge_counter = Counter() 94 | # with open(train_file, encoding = "utf8") as fi: 95 | with open(train_file) as fi: 96 | error_cnt = 0 97 | user_cache = {} 98 | logging.info("loading reviews from %s", train_file) 99 | for i, line in tqdm(enumerate(fi)): 100 | [label, uid, mid, cate, his_mid_list, his_cate_list] = line.strip().split(SAMPLE_SEP) 101 | if label == "0": 102 | continue 103 | if his_mid_list.strip() == "": 104 | continue 105 | his_mid_list = his_mid_list.split(SEQ_SEP) 106 | his_cate_list = his_cate_list.split(SEQ_SEP) 107 | 108 | his_item_list = [SeqItem(hmid, hcate, rating_map[(uid, hmid)].ts) for hmid, hcate in zip(his_mid_list, his_cate_list)] 109 | item_list = his_item_list + [SeqItem(mid, cate, rating_map[(uid, mid)].ts)] 110 | 111 | if not check_order(item_list): 112 | error_cnt += 1 113 | logging.error("[INVALID ORDER]: {}".format(item_list)) 114 | continue 115 | if uid not in user_cache: 116 | user_cache[uid] = [] 117 | user_cache[uid].extend(item_list) 118 | logging.info("error count: %d", error_cnt) 119 | logging.info("couting edges...") 120 | for i, (uid, item_list) in tqdm(enumerate(user_cache.items())): 121 | item_list = sorted(list(set(item_list)), key=lambda x:x.ts) 122 | if len(item_list) < 2: 123 | continue 124 | if len(item_list) == 2: 125 | edge = Edge(item_list[0].mid, item_list[1].mid) 126 | edge_counter.update([edge]) 127 | continue 128 | else: 129 | item_cnt = len(item_list) 130 | for j in range(item_cnt - 2): 131 | [a, b, c] = item_list[j: j + 3] 132 | edge_counter.update([Edge(a.mid, b.mid), Edge(a.mid, c.mid), Edge(b.mid, c.mid)]) 133 | edges = [WEdge(edge.frm, edge.to, cnt) for edge, cnt in list(edge_counter.most_common())] 134 | logging.info("total edges: %d", len(edges)) 135 | return edges 136 | 137 | 138 | def main(): 139 | rating_map = load_ratings(reviews_file, reviews_cache_file) 140 | 141 | train_pred_edges = get_pred_edges(train_file, rating_map) 142 | logging.info("total train pred edges: %d", len(train_pred_edges)) 143 | for edge in list(train_pred_edges)[:5]: 144 | logging.info(edge) 145 | 146 | full_edges = gen_full_edges(train_file, rating_map, check_triangle=True) 147 | 148 | logging.info("writing edges to %s", edge_output_file_v1) 149 | cnt = 0 150 | with open(edge_output_file_v1, "w") as fo: 151 | for we in tqdm(full_edges): 152 | e = Edge(we.frm, we.to) 153 | if True: #if e not in test_pred_edges: 154 | line = "{}\t{}\t{}\n".format(we.frm, we.to, we.freq) 155 | cnt += 1 156 | fo.write(line) 157 | logging.info("done! total edges: %d", cnt) 158 | 159 | 160 | def check_edges(): 161 | voc = {} 162 | with open(data_path+"/mid_voc.csv", encoding = "utf8") as fi: 163 | for line in fi: 164 | [name, id] = line.strip().split("\t") 165 | voc[name] = id 166 | 167 | logging.info("checking mids...") 168 | missing_cnt = 0 169 | with open(edge_output_file_v1, encoding = "utf8") as fi: 170 | for line in tqdm(fi): 171 | [from_mid, to_mid, freq] = line.strip().split("\t") 172 | if from_mid not in voc or to_mid not in voc: 173 | missing_cnt += 1 174 | logging.info("number of oov mids in %s: %d", edge_output_file_v1, missing_cnt) 175 | 176 | 177 | if __name__ == '__main__': 178 | main() 179 | #check_edges() 180 | -------------------------------------------------------------------------------- /script/dataset_process/prepare_data.py: -------------------------------------------------------------------------------- 1 | import time 2 | import numpy 3 | # import cPickle as pickle 4 | from settings import * 5 | 6 | 7 | # prepare data for tgin 8 | def prepare_data_tgin(input, tri0_input, tri1_input, target, n_tri, return_neg=False): 9 | def fun(tri0_input): 10 | mid0_triangle = [tri0_inp[0] for tri0_inp in tri0_input] # [B, TRIANGLE_NUM, 3] 11 | cat0_triangle = [tri0_inp[1] for tri0_inp in tri0_input] 12 | mid0_weight = [tri0_inp[2] for tri0_inp in tri0_input] 13 | mid0_tri_list = [tri0_inp[3] for tri0_inp in tri0_input] 14 | cat0_tri_list = [tri0_inp[4] for tri0_inp in tri0_input] 15 | wi0_tri_list = [tri0_inp[5] for tri0_inp in tri0_input] 16 | 17 | return mid0_triangle, cat0_triangle, mid0_weight, \ 18 | mid0_tri_list, cat0_tri_list, wi0_tri_list 19 | 20 | # x: a list of sentences 21 | t1 = time.time() 22 | lengths_x = [len(s[4]) for s in input] # Length of history list 23 | seqs_mid = [inp[3] for inp in input] # Items of history list 24 | seqs_cat = [inp[4] for inp in input] # Item cates of history list 25 | noclk_seqs_mid = [inp[5] for inp in input] # Negetive item list of each items [B, max_len, n_neg] 26 | noclk_seqs_cat = [inp[6] for inp in input] # Negetive cate list of each items [B, max_len, n_neg] 27 | 28 | mid0_tri_cand, cat0_tri_cand, wi0_tri_cand, \ 29 | mid0_tri_list, cat0_tri_list, wi0_tri_list = fun(tri0_input) 30 | mid1_tri_cand, cat1_tri_cand, wi1_tri_cand, \ 31 | mid1_tri_list, cat1_tri_list, wi1_tri_list = fun(tri1_input) 32 | 33 | mid0_cand_lengths = [len(i) for i in mid0_tri_cand] 34 | mid1_cand_lengths = [len(i) for i in mid1_tri_cand] 35 | 36 | # ------------------------------ luwei: truncate the user behavior sequence Negative list ------------------------------# 37 | if maxlen is not None: 38 | new_seqs_mid = [] 39 | new_seqs_cat = [] 40 | new_noclk_seqs_mid = [] 41 | new_noclk_seqs_cat = [] 42 | new_lengths_x = [] 43 | # 0-hop 44 | new_mid0_tri_list = [] 45 | new_cat0_tri_list = [] 46 | new_wi0_tri_list = [] 47 | # 1-hop 48 | new_mid1_tri_list = [] 49 | new_cat1_tri_list = [] 50 | new_wi1_tri_list = [] 51 | 52 | for l_x, inp, tri0_inp, tri1_inp in zip(lengths_x, input, tri0_input, tri1_input): 53 | if l_x > maxlen: 54 | new_seqs_mid.append(inp[3][l_x - maxlen:]) # luwei: truncate user seq to maxlen 55 | new_seqs_cat.append(inp[4][l_x - maxlen:]) 56 | new_noclk_seqs_mid.append(inp[5][l_x - maxlen:]) 57 | new_noclk_seqs_cat.append(inp[6][l_x - maxlen:]) 58 | new_lengths_x.append(maxlen) 59 | # 0-hop 60 | new_mid0_tri_list.append(tri0_inp[3][l_x - maxlen:]) 61 | new_cat0_tri_list.append(tri0_inp[4][l_x - maxlen:]) 62 | new_wi0_tri_list.append(tri0_inp[5][l_x - maxlen:]) 63 | # 1-hop 64 | new_mid1_tri_list.append(tri1_inp[3][l_x - maxlen:]) 65 | new_cat1_tri_list.append(tri1_inp[4][l_x - maxlen:]) 66 | new_wi1_tri_list.append(tri1_inp[5][l_x - maxlen:]) 67 | else: 68 | new_seqs_mid.append(inp[3]) 69 | new_seqs_cat.append(inp[4]) 70 | new_noclk_seqs_mid.append(inp[5]) 71 | new_noclk_seqs_cat.append(inp[6]) 72 | new_lengths_x.append(l_x) 73 | # 0-hop 74 | new_mid0_tri_list.append(tri0_inp[3]) 75 | new_cat0_tri_list.append(tri0_inp[4]) 76 | new_wi0_tri_list.append(tri0_inp[5]) 77 | # 1-hop 78 | new_mid1_tri_list.append(tri1_inp[3]) 79 | new_cat1_tri_list.append(tri1_inp[4]) 80 | new_wi1_tri_list.append(tri1_inp[5]) 81 | 82 | lengths_x = new_lengths_x 83 | seqs_mid = new_seqs_mid 84 | seqs_cat = new_seqs_cat 85 | noclk_seqs_mid = new_noclk_seqs_mid 86 | noclk_seqs_cat = new_noclk_seqs_cat 87 | # 0-hop 88 | mid0_tri_list = new_mid0_tri_list 89 | cat0_tri_list = new_cat0_tri_list 90 | wi0_tri_list = new_wi0_tri_list 91 | # 1-hop 92 | mid1_tri_list = new_mid1_tri_list 93 | cat1_tri_list = new_cat1_tri_list 94 | wi1_tri_list = new_wi1_tri_list 95 | 96 | if len(lengths_x) < 1: 97 | return None, None, None, None 98 | 99 | # ------------------------------ History list ------------------------------# 100 | maxlen_x = maxlen 101 | n_samples = len(seqs_mid) 102 | neg_samples = len(noclk_seqs_mid[0][0]) 103 | 104 | mid_his = numpy.zeros((n_samples, maxlen_x)).astype('int64') 105 | cat_his = numpy.zeros((n_samples, maxlen_x)).astype('int64') 106 | noclk_mid_his = numpy.zeros((n_samples, maxlen_x, neg_samples)).astype('int64') 107 | noclk_cat_his = numpy.zeros((n_samples, maxlen_x, neg_samples)).astype('int64') 108 | mid_mask = numpy.zeros((n_samples, maxlen_x)).astype('int64') # mask-tag is 0 109 | mid0_tri_mask = numpy.zeros((n_samples, n_tri * 3)).astype('int64') 110 | mid1_tri_mask = numpy.zeros((n_samples, n_tri * 3)).astype('int64') 111 | 112 | # 0-hop 113 | mids_tri0 = numpy.zeros((n_samples, n_tri * 3)).astype('int64') 114 | cats_tri0 = numpy.zeros((n_samples, n_tri * 3)).astype('int64') 115 | wi_tri0 = numpy.zeros((n_samples, n_tri)).astype('float64') 116 | 117 | mid0_his = numpy.zeros((n_samples, maxlen_x, n_tri * 3)).astype('int64') 118 | cat0_his = numpy.zeros((n_samples, maxlen_x, n_tri * 3)).astype('int64') 119 | wi0_his = numpy.zeros((n_samples, maxlen_x, n_tri * 1)).astype('float64') 120 | mid0_his_tri_mask = numpy.zeros((n_samples, maxlen_x, n_tri * 3)).astype('int64') 121 | # 1-hop 122 | mids_tri1 = numpy.zeros((n_samples, n_tri * 3)).astype('int64') 123 | cats_tri1 = numpy.zeros((n_samples, n_tri * 3)).astype('int64') 124 | wi_tri1 = numpy.zeros((n_samples, n_tri)).astype('float64') 125 | 126 | mid1_his = numpy.zeros((n_samples, maxlen_x, n_tri * 3)).astype('int64') 127 | cat1_his = numpy.zeros((n_samples, maxlen_x, n_tri * 3)).astype('int64') 128 | wi1_his = numpy.zeros((n_samples, maxlen_x, n_tri * 1)).astype('float64') 129 | mid1_his_tri_mask = numpy.zeros((n_samples, maxlen_x, n_tri * 3)).astype('int64') 130 | 131 | for idx, [s_mid0_cand, s_cat0_cand, s_wi0_cand, 132 | s_mid1_cand, s_cat1_cand, s_wi1_cand, 133 | s_x, s_y, no_sx, no_sy, 134 | s_mid0, s_cat0, s_wi0, 135 | s_mid1, s_cat1, s_wi1] in enumerate(zip(mid0_tri_cand, cat0_tri_cand, wi0_tri_cand, 136 | mid1_tri_cand, cat1_tri_cand, wi1_tri_cand, 137 | seqs_mid, seqs_cat, 138 | noclk_seqs_mid, noclk_seqs_cat, 139 | mid0_tri_list, cat0_tri_list, wi0_tri_list, 140 | mid1_tri_list, cat1_tri_list, wi1_tri_list)): 141 | # condidates 142 | mid0_tri_mask[idx, :mid0_cand_lengths[idx]] = 1 143 | mids_tri0[idx, :mid0_cand_lengths[idx]] = s_mid0_cand 144 | cats_tri0[idx, :mid0_cand_lengths[idx]] = s_cat0_cand 145 | wi_tri0[idx, :mid0_cand_lengths[idx] // 3] = s_wi0_cand 146 | 147 | mid1_tri_mask[idx, :mid1_cand_lengths[idx]] = 1 148 | mids_tri1[idx, :mid1_cand_lengths[idx]] = s_mid1_cand 149 | cats_tri1[idx, :mid1_cand_lengths[idx]] = s_cat1_cand 150 | wi_tri1[idx, :mid1_cand_lengths[idx] // 3] = s_wi1_cand 151 | 152 | # history records 153 | mid_mask[idx, :lengths_x[idx]] = 1 154 | mid_his[idx, :lengths_x[idx]] = s_x 155 | cat_his[idx, :lengths_x[idx]] = s_y 156 | noclk_mid_his[idx, :lengths_x[idx], :] = no_sx 157 | noclk_cat_his[idx, :lengths_x[idx], :] = no_sy 158 | 159 | # input each triangle 160 | s_mid0_lengths = [len(tri_list) for tri_list in s_mid0] 161 | s_mid1_lengths = [len(tri_list) for tri_list in s_mid1] 162 | for t_idx, [s_mid0_tri, s_cat0_tri, s_wi0_tri, 163 | s_mid1_tri, s_cat1_tri, s_wi1_tri] in enumerate(zip(s_mid0, s_cat0, s_wi0, 164 | s_mid1, s_cat1, s_wi1)): 165 | mid0_his_tri_mask[idx, :lengths_x[idx], :s_mid0_lengths[t_idx]] = 1 166 | mid0_his[idx, :lengths_x[idx], :s_mid0_lengths[t_idx]] = s_mid0_tri 167 | cat0_his[idx, :lengths_x[idx], :s_mid0_lengths[t_idx]] = s_cat0_tri 168 | wi0_his[idx, :lengths_x[idx], :s_mid0_lengths[t_idx] // 3] = s_wi0_tri 169 | 170 | mid1_his_tri_mask[idx, :lengths_x[idx], :s_mid1_lengths[t_idx]] = 1 171 | mid1_his[idx, :lengths_x[idx], :s_mid1_lengths[t_idx]] = s_mid1_tri 172 | cat1_his[idx, :lengths_x[idx], :s_mid1_lengths[t_idx]] = s_cat1_tri 173 | wi1_his[idx, :lengths_x[idx], :s_mid1_lengths[t_idx] // 3] = s_wi1_tri 174 | 175 | # ------------------------------ Record batch ------------------------------# 176 | uids = numpy.array([inp[0] for inp in input]) 177 | mids = numpy.array([inp[1] for inp in input]) 178 | cats = numpy.array([inp[2] for inp in input]) 179 | 180 | if return_neg: 181 | origin_inp = [uids, mids, cats, mid_his, cat_his, mid_mask, numpy.array(target), numpy.array(lengths_x), 182 | noclk_mid_his, noclk_cat_his] 183 | tri0_inp = [mids_tri0, cats_tri0, wi_tri0, mid0_his, cat0_his, wi0_his, mid0_tri_mask, mid0_his_tri_mask] 184 | tri1_inp = [mids_tri1, cats_tri1, wi_tri1, mid1_his, cat1_his, wi1_his, mid1_tri_mask, mid1_his_tri_mask] 185 | return origin_inp, tri0_inp, tri1_inp 186 | 187 | else: 188 | origin_inp = [uids, mids, cats, mid_his, cat_his, mid_mask, numpy.array(target), numpy.array(lengths_x)] 189 | tri0_inp = [mids_tri0, cats_tri0, wi_tri0, mid0_his, cat0_his, wi0_his, mid0_tri_mask, mid0_his_tri_mask] 190 | tri1_inp = [mids_tri1, cats_tri1, wi_tri1, mid1_his, cat1_his, wi1_his, mid1_tri_mask, mid1_his_tri_mask] 191 | return origin_inp, tri0_inp, tri1_inp 192 | 193 | 194 | # prepare data for general model 195 | def prepare_data(input, target, target_aux, maxlen=20, maxlen_hard=10, return_neg=False): 196 | # x: a list of sentences 197 | lengths_x = [len(s[7]) for s in input] 198 | seqs_mid = [inp[6] for inp in input] 199 | seqs_cat = [inp[7] for inp in input] 200 | seqs_time_stamp = [inp[8] for inp in input] 201 | 202 | lengths_hard_x = [len(s[9]) for s in input] 203 | seqs_hard_mid = [inp[9] for inp in input] 204 | seqs_hard_cat = [inp[10] for inp in input] 205 | seqs_hard_time_stamp = [inp[11] for inp in input] 206 | 207 | noclk_seqs_mid = [inp[12] for inp in input] 208 | noclk_seqs_cat = [inp[13] for inp in input] 209 | 210 | if maxlen is not None: 211 | new_seqs_mid = [] 212 | new_seqs_cat = [] 213 | new_seqs_time_stamp = [] 214 | new_noclk_seqs_mid = [] 215 | new_noclk_seqs_cat = [] 216 | new_lengths_x = [] 217 | for l_x, inp in zip(lengths_x, input): 218 | if l_x > maxlen: 219 | new_seqs_mid.append(inp[6][l_x - maxlen:]) 220 | new_seqs_cat.append(inp[7][l_x - maxlen:]) 221 | new_seqs_time_stamp.append(inp[8][l_x - maxlen:]) 222 | new_noclk_seqs_mid.append(inp[12][l_x - maxlen:]) 223 | new_noclk_seqs_cat.append(inp[13][l_x - maxlen:]) 224 | new_lengths_x.append(maxlen) 225 | else: 226 | new_seqs_mid.append(inp[6]) 227 | new_seqs_cat.append(inp[7]) 228 | new_seqs_time_stamp.append(inp[8]) 229 | new_noclk_seqs_mid.append(inp[12]) 230 | new_noclk_seqs_cat.append(inp[13]) 231 | new_lengths_x.append(l_x) 232 | lengths_x = new_lengths_x 233 | seqs_mid = new_seqs_mid 234 | seqs_cat = new_seqs_cat 235 | seqs_time_stamp = new_seqs_time_stamp 236 | noclk_seqs_mid = new_noclk_seqs_mid 237 | noclk_seqs_cat = new_noclk_seqs_cat 238 | if len(lengths_x) < 1: 239 | return None, None, None, None 240 | 241 | if maxlen_hard is not None: 242 | new_seqs_hard_mid = [] 243 | new_seqs_hard_cat = [] 244 | new_seqs_hard_time_stamp = [] 245 | new_lengths_hard_x = [] 246 | for l_x, inp in zip(lengths_hard_x, input): 247 | if l_x > maxlen_hard: 248 | new_seqs_hard_mid.append(inp[9][l_x - maxlen_hard:]) 249 | new_seqs_hard_cat.append(inp[10][l_x - maxlen_hard:]) 250 | new_seqs_hard_time_stamp.append(inp[11][l_x - maxlen_hard:]) 251 | new_lengths_hard_x.append(maxlen_hard) 252 | else: 253 | new_seqs_hard_mid.append(inp[9]) 254 | new_seqs_hard_cat.append(inp[10]) 255 | new_seqs_hard_time_stamp.append(inp[11]) 256 | new_lengths_hard_x.append(l_x) 257 | lengths_hard_x = new_lengths_hard_x 258 | seqs_hard_mid = new_seqs_hard_mid 259 | seqs_hard_cat = new_seqs_hard_cat 260 | seqs_hard_time_stamp = new_seqs_hard_time_stamp 261 | if len(lengths_hard_x) < 1: 262 | return None, None, None, None 263 | 264 | n_samples = len(seqs_mid) 265 | maxlen_x = maxlen 266 | neg_samples = len(noclk_seqs_mid[0][0]) 267 | # print ("maxlen_x",maxlen_x) 268 | mid_his = numpy.zeros((n_samples, maxlen_x)).astype('int64') 269 | cat_his = numpy.zeros((n_samples, maxlen_x)).astype('int64') 270 | time_stamp_his = numpy.zeros((n_samples, maxlen_x)).astype('int64') 271 | 272 | mid_hard_his = numpy.zeros((n_samples, maxlen_hard)).astype('int64') 273 | cat_hard_his = numpy.zeros((n_samples, maxlen_hard)).astype('int64') 274 | time_stamp_hard_his = numpy.zeros((n_samples, maxlen_hard)).astype('int64') 275 | mid_hard_mask = numpy.zeros((n_samples, maxlen_hard)).astype('float32') 276 | 277 | noclk_mid_his = numpy.zeros((n_samples, maxlen_x, neg_samples)).astype('int64') 278 | noclk_cat_his = numpy.zeros((n_samples, maxlen_x, neg_samples)).astype('int64') 279 | mid_mask = numpy.zeros((n_samples, maxlen_x)).astype('float32') 280 | for idx, [s_x, s_y, s_t, s_h_x, s_h_y, s_h_t, no_sx, no_sy] in enumerate(zip(seqs_mid, seqs_cat, seqs_time_stamp, seqs_hard_mid, seqs_hard_cat, seqs_hard_time_stamp, noclk_seqs_mid, noclk_seqs_cat)): 281 | mid_mask[idx, :lengths_x[idx]] = 1. 282 | mid_his[idx, :lengths_x[idx]] = s_x 283 | cat_his[idx, :lengths_x[idx]] = s_y 284 | time_stamp_his[idx, :lengths_x[idx]] = s_t 285 | 286 | mid_hard_mask[idx, :lengths_hard_x[idx]] = 1. 287 | mid_hard_his[idx, :lengths_hard_x[idx]] = s_h_x 288 | cat_hard_his[idx, :lengths_hard_x[idx]] = s_h_y 289 | time_stamp_hard_his[idx, :lengths_hard_x[idx]] = s_h_t 290 | 291 | noclk_mid_his[idx, :lengths_x[idx], :] = no_sx 292 | noclk_cat_his[idx, :lengths_x[idx], :] = no_sy 293 | 294 | uids = numpy.array([inp[0] for inp in input]) 295 | mids = numpy.array([inp[1] for inp in input]) 296 | cats = numpy.array([inp[2] for inp in input]) 297 | time_stamp = numpy.array([inp[3] for inp in input]) 298 | trigger_mid = numpy.array([inp[4] for inp in input]) 299 | trigger_cat = numpy.array([inp[5] for inp in input]) 300 | 301 | if return_neg: 302 | return uids, mids, cats, time_stamp, trigger_mid, trigger_cat, mid_his, cat_his, time_stamp_his, mid_mask, mid_hard_his, cat_hard_his, time_stamp_hard_his, mid_hard_mask, numpy.array(target), numpy.array( 303 | lengths_x), numpy.array(lengths_hard_x), noclk_mid_his, noclk_cat_his, numpy.array(target_aux) 304 | 305 | else: 306 | return uids, mids, cats, time_stamp, trigger_mid, trigger_cat, mid_his, cat_his, time_stamp_his, mid_mask, mid_hard_his, cat_hard_his, time_stamp_hard_his, mid_hard_mask, numpy.array(target), numpy.array(lengths_x), numpy.array(lengths_hard_x), numpy.array(target_aux) 307 | -------------------------------------------------------------------------------- /script/dataset_process/read_content_wise_dataset_zhibo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/2 15:14 4 | 5 | import numpy as np 6 | import scipy as sp 7 | import dask.dataframe as ddf 8 | 9 | file_name = ".../ContentWiseImpressions/data/CW10M/splits/urm_items.train.npz" 10 | data = np.load(file_name) 11 | 12 | print(data) 13 | 14 | data2 = sp.sparse.load_npz(file_name) 15 | print(data2.toarray()) 16 | 17 | # read parquet data files 18 | file_name_interaction = ".../ContentWiseImpressions/data/CW10M/interactions" 19 | file_name_imp_direct_link = ".../ContentWiseImpressions/data/CW10M/impressions-direct-link" 20 | file_name_imp_non_direct_link = ".../ContentWiseImpressions/data/CW10M/impressions-non-direct-link" 21 | 22 | data_interaction = ddf.read_parquet(file_name_interaction, engine="pyarrow") 23 | data_imp_direct_link = ddf.read_parquet(file_name_imp_direct_link, engine="pyarrow") 24 | data_imp_non_direct_link = ddf.read_parquet(file_name_imp_non_direct_link, engine="pyarrow") 25 | 26 | print(data_interaction) 27 | print("data_interaction shape: {}".format(data_interaction.shape)) 28 | print(data_interaction.head()) 29 | # print(data_imp_direct_link) 30 | # print(data_imp_non_direct_link) -------------------------------------------------------------------------------- /script/dataset_process/shuffle.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020 Graphcore Ltd. All rights reserved. 2 | # 3 | # Copyright 1999-present Alibaba Group Holding Ltd. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # This file has been modified by Graphcore Ltd. 18 | # It has been modified to disable running this module as a script. 19 | 20 | 21 | import os 22 | import sys 23 | import random 24 | 25 | import tempfile 26 | from subprocess import call 27 | 28 | 29 | def main(file, temporary=False): 30 | tf_os, tpath = tempfile.mkstemp(dir='~/DIN-V2-CODE') 31 | tf = open(tpath, 'w') 32 | 33 | fd = open(file, "r") 34 | for l in fd: 35 | print >> tf, l.strip("\n") 36 | tf.close() 37 | 38 | lines = open(tpath, 'r').readlines() 39 | random.shuffle(lines) 40 | if temporary: 41 | path, filename = os.path.split(os.path.realpath(file)) 42 | fd = tempfile.TemporaryFile(prefix=filename + '.shuf', dir=path) 43 | else: 44 | fd = open(file + '.shuf', 'w') 45 | 46 | for l in lines: 47 | s = l.strip("\n") 48 | print >> fd, s 49 | 50 | if temporary: 51 | fd.seek(0) 52 | else: 53 | fd.close() 54 | 55 | os.remove(tpath) 56 | return fd 57 | 58 | 59 | if __name__ == '__main__': 60 | main(sys.argv[1]) 61 | -------------------------------------------------------------------------------- /script/models/DEI2N.py: -------------------------------------------------------------------------------- 1 | # /* 2 | # -*- coding: utf-8 -*- 3 | # MIT License 4 | # 5 | # Copyright (c) 2020 skx300 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # @Time : 2023/1/1 09:49 26 | from models.base_model import Model 27 | from common.utils import * 28 | 29 | 30 | class DEI2N(Model): 31 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 32 | super(DEI2N, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 33 | ATTENTION_SIZE, 34 | use_negsampling) 35 | 36 | is_training = True 37 | 38 | with tf.name_scope('UIM_layer'): 39 | inp = tf.concat([self.uid_batch_embedded, self.item_his_hard_eb_sum, self.trigger_eb, 40 | self.trigger_eb * self.item_his_hard_eb_sum, 41 | self.seq_hard_len_eb], 1) 42 | dnn1 = tf.layers.batch_normalization(inputs=inp, name='bn1_uin', training=is_training) 43 | dnn1 = tf.layers.dense(dnn1, EMBEDDING_DIM , activation=tf.nn.relu, name='f1_uim') 44 | # dnn2 = tf.layers.batch_normalization(inputs=dnn1, name='bn2_uin', training=is_training) 45 | # dnn3 = tf.layers.dense(dnn2, EMBEDDING_DIM, activation=tf.nn.relu, name='f2_uim') 46 | # dnn3 = tf.layers.batch_normalization(inputs=dnn3, name='bn3_uin', training=is_training) 47 | dnn3 = tf.layers.dense(dnn1, 1, activation=tf.sigmoid, name='f3_uim') 48 | 49 | with tf.name_scope('Attention_layer'): 50 | # clicks_trans_block = SelfAttentionPooling( 51 | # num_heads=2, 52 | # key_mask=self.mask, 53 | # query_mask=self.mask, 54 | # length=30, 55 | # linear_key_dim=HIDDEN_SIZE, 56 | # linear_value_dim=HIDDEN_SIZE, 57 | # output_dim=EMBEDDING_DIM * 2, 58 | # hidden_dim=EMBEDDING_DIM * 4, 59 | # num_layer=1, 60 | # keep_prob=self.keep_prob 61 | # ) 62 | item_his_eb_mix = tf.concat([self.item_his_eb, self.item_his_time_eb], axis=2) 63 | item_his_eb_mix = tf.layers.dense(item_his_eb_mix, EMBEDDING_DIM * 2, activation=None, 64 | name='item_his_eb_mix') 65 | # clicks_trans_output = clicks_trans_block.build(item_his_eb_mix, reuse=False, 66 | # scope='clicks_trans') # (batch_size, 30, output_dim) 67 | 68 | attention_output_soft = din_attention_dihn_dmin(self.item_eb, item_his_eb_mix, 69 | self.item_his_time_eb, 70 | ATTENTION_SIZE, self.mask, 71 | stag='click') 72 | att_fea_soft = tf.reduce_sum(attention_output_soft, 1) 73 | 74 | attention_output_trigger = din_attention_dihn_dmin(self.trigger_eb, item_his_eb_mix, 75 | self.item_his_time_eb, 76 | ATTENTION_SIZE, self.mask, 77 | stag='trigger_hard') 78 | att_fea_trigger = tf.reduce_sum(attention_output_trigger, 1) 79 | 80 | attention_output_hard = din_attention_dihn_dmin(self.item_eb, self.item_his_hard_eb, 81 | self.item_his_time_hard_eb, 82 | ATTENTION_SIZE, self.mask_hard, 83 | stag='click_hard') 84 | att_fea_hard = tf.reduce_sum(attention_output_hard, 1) 85 | 86 | tf.summary.histogram('att_fea_soft', att_fea_soft) 87 | 88 | att_fea_mix = tf.multiply(att_fea_trigger, dnn3) + tf.multiply(att_fea_soft, 1 - dnn3) 89 | 90 | # with tf.name_scope('trigger_target_interaction_layer'): 91 | Hadamard_fea = tf.multiply(self.trigger_eb, self.item_eb) 92 | cross_inp = tf.concat( 93 | [self.trigger_eb, self.item_eb, self.trigger_eb-self.item_eb ,Hadamard_fea], 1) 94 | 95 | inp = tf.concat( 96 | [self.uid_batch_embedded, self.item_his_eb_sum, cross_inp, 97 | self.item_eb * self.item_his_eb_sum, att_fea_hard, att_fea_mix], -1) 98 | # Fully connected layer 99 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DEI2N_MHTA.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # /* 3 | # MIT License 4 | # 5 | # Copyright (c) 2020 skx300 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # -*- coding: utf-8 -*- 26 | # @Time : 2023/1/1 09:49 27 | from models.base_model import Model 28 | from common.utils import * 29 | 30 | 31 | class DEI2N_MHTA(Model): 32 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 33 | super(DEI2N_MHTA, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 34 | ATTENTION_SIZE, 35 | use_negsampling) 36 | 37 | is_training = True 38 | 39 | with tf.name_scope('UIM_layer'): 40 | inp = tf.concat([self.uid_batch_embedded, self.item_his_hard_eb_sum, self.trigger_eb, 41 | self.trigger_eb * self.item_his_hard_eb_sum, 42 | self.seq_hard_len_eb], 1) 43 | dnn1 = tf.layers.batch_normalization(inputs=inp, name='bn1_uin', training=is_training) 44 | dnn1 = tf.layers.dense(dnn1, EMBEDDING_DIM , activation=tf.nn.relu, name='f1_uim') 45 | # dnn2 = tf.layers.batch_normalization(inputs=dnn1, name='bn2_uin', training=is_training) 46 | # dnn3 = tf.layers.dense(dnn2, EMBEDDING_DIM, activation=tf.nn.relu, name='f2_uim') 47 | # dnn3 = tf.layers.batch_normalization(inputs=dnn3, name='bn3_uin', training=is_training) 48 | dnn3 = tf.layers.dense(dnn1, 1, activation=tf.sigmoid, name='f3_uim') 49 | 50 | with tf.name_scope('Attention_layer'): 51 | # clicks_trans_block = SelfAttentionPooling( 52 | # num_heads=2, 53 | # key_mask=self.mask, 54 | # query_mask=self.mask, 55 | # length=30, 56 | # linear_key_dim=HIDDEN_SIZE, 57 | # linear_value_dim=HIDDEN_SIZE, 58 | # output_dim=EMBEDDING_DIM * 2, 59 | # hidden_dim=EMBEDDING_DIM * 4, 60 | # num_layer=1, 61 | # keep_prob=self.keep_prob 62 | # ) 63 | item_his_eb_mix = tf.concat([self.item_his_eb, self.item_his_time_eb], axis=2) 64 | item_his_eb_mix = tf.layers.dense(item_his_eb_mix, EMBEDDING_DIM * 2, activation=None, 65 | name='item_his_eb_mix') 66 | # clicks_trans_output = clicks_trans_block.build(item_his_eb_mix, reuse=False, 67 | # scope='clicks_trans') # (batch_size, 30, output_dim) 68 | 69 | attention_output_soft = multihead_target_attention(queries=self.item_eb, 70 | keys=item_his_eb_mix, 71 | num_heads=2, 72 | activation_fn=None, 73 | key_masks=self.mask, 74 | query_masks=None, 75 | num_units=HIDDEN_SIZE, 76 | num_output_units=EMBEDDING_DIM * 2, 77 | name='soft_seq_target_attention') 78 | 79 | att_fea_soft = tf.reduce_sum(attention_output_soft, 1) 80 | attention_output_trigger = multihead_target_attention(queries=self.trigger_eb, 81 | keys=item_his_eb_mix, 82 | num_heads=2, 83 | activation_fn=None, 84 | key_masks=self.mask, 85 | query_masks=None, 86 | num_units=HIDDEN_SIZE, 87 | num_output_units=EMBEDDING_DIM * 2, 88 | name='soft_seq_trigger_attention') 89 | att_fea_trigger = tf.reduce_sum(attention_output_trigger, 1) 90 | 91 | attention_output_hard = multihead_target_attention(queries=self.item_eb, 92 | keys=self.item_his_hard_eb, 93 | num_heads=2, 94 | activation_fn=None, 95 | key_masks=self.mask_hard, 96 | query_masks=None, 97 | num_units=HIDDEN_SIZE, 98 | num_output_units=EMBEDDING_DIM * 2, 99 | name='hard_seq_target_attention') 100 | att_fea_hard = tf.reduce_sum(attention_output_hard, 1) 101 | 102 | tf.summary.histogram('att_fea_soft', att_fea_soft) 103 | 104 | att_fea_mix = tf.multiply(att_fea_trigger, dnn3) + tf.multiply(att_fea_soft, 1 - dnn3) 105 | 106 | # with tf.name_scope('trigger_target_interaction_layer'): 107 | Hadamard_fea = tf.multiply(self.trigger_eb, self.item_eb) 108 | cross_inp = tf.concat( 109 | [self.trigger_eb, self.item_eb, self.trigger_eb-self.item_eb ,Hadamard_fea], 1) 110 | 111 | inp = tf.concat( 112 | [self.uid_batch_embedded, self.item_his_eb_sum, cross_inp, 113 | self.item_eb * self.item_his_eb_sum, att_fea_hard, att_fea_mix], -1) 114 | # Fully connected layer 115 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DEI2N_NO_IL.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # /* 3 | # MIT License 4 | # 5 | # Copyright (c) 2020 skx300 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | # -*- coding: utf-8 -*- 26 | # @Time : 2023/1/1 09:49 27 | 28 | from models.base_model import Model 29 | from common.utils import * 30 | 31 | 32 | class DEI2N_NO_IL(Model): 33 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 34 | super(DEI2N_NO_IL, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 35 | ATTENTION_SIZE, 36 | use_negsampling) 37 | 38 | is_training = False 39 | self.keep_prob = 1.0 40 | with tf.name_scope('UIM_layer'): 41 | inp = tf.concat([self.uid_batch_embedded, self.item_his_hard_eb_sum, self.trigger_eb, 42 | self.seq_hard_len_eb], 1) 43 | dnn1 = tf.layers.batch_normalization(inputs=inp, name='bn1_uin', training=is_training) 44 | dnn1 = tf.layers.dense(dnn1, EMBEDDING_DIM * 2, activation=tf.nn.relu, name='f1_uim') 45 | dnn2 = tf.layers.batch_normalization(inputs=dnn1, name='bn2_uin', training=is_training) 46 | dnn3 = tf.layers.dense(dnn2, EMBEDDING_DIM, activation=tf.nn.relu, name='f2_uim') 47 | dnn3 = tf.layers.batch_normalization(inputs=dnn3, name='bn3_uin', training=is_training) 48 | dnn3 = tf.layers.dense(dnn3, 1, activation=tf.sigmoid, name='f3_uim') 49 | 50 | with tf.name_scope('Attention_layer'): 51 | # clicks_trans_block = SelfAttentionPooling( 52 | # num_heads=2, 53 | # key_mask=self.mask, 54 | # query_mask=self.mask, 55 | # length=20, 56 | # linear_key_dim=HIDDEN_SIZE, 57 | # linear_value_dim=HIDDEN_SIZE, 58 | # output_dim=EMBEDDING_DIM * 2, 59 | # hidden_dim=EMBEDDING_DIM * 4, 60 | # num_layer=1, 61 | # keep_prob=self.keep_prob 62 | # ) 63 | item_his_eb_mix = tf.concat([self.item_his_eb, self.item_his_time_eb], axis=2) 64 | item_his_eb_mix = tf.layers.dense(item_his_eb_mix, EMBEDDING_DIM * 2, activation=None, 65 | name='item_his_eb_mix') 66 | # clicks_trans_output = clicks_trans_block.build(item_his_eb_mix, reuse=False, 67 | # scope='clicks_trans') # (batch_size, 30, output_dim) 68 | 69 | attention_output_soft = din_attention_dihn_dmin(self.item_eb, item_his_eb_mix, 70 | self.item_his_time_eb, 71 | ATTENTION_SIZE, self.mask, 72 | stag='click') 73 | att_fea_soft = tf.reduce_sum(attention_output_soft, 1) 74 | 75 | attention_output_trigger = din_attention_dihn_dmin(self.trigger_eb, item_his_eb_mix, 76 | self.item_his_time_eb, 77 | ATTENTION_SIZE, self.mask, 78 | stag='trigger_hard') 79 | att_fea_trigger = tf.reduce_sum(attention_output_trigger, 1) 80 | attention_output_hard = din_attention_dihn_dmin(self.item_eb, self.item_his_hard_eb, 81 | self.item_his_time_hard_eb, 82 | ATTENTION_SIZE, self.mask_hard, 83 | stag='click_hard') 84 | att_fea_hard = tf.reduce_sum(attention_output_hard, 1) 85 | 86 | tf.summary.histogram('att_fea_soft', att_fea_soft) 87 | 88 | att_fea_mix = tf.multiply(att_fea_trigger, dnn3) + tf.multiply(att_fea_soft, 1 - dnn3) 89 | 90 | # with tf.name_scope('trigger_target_interaction_layer'): 91 | Hadamard_fea = tf.multiply(self.trigger_eb, self.item_eb) 92 | cross_inp = tf.concat( 93 | [self.trigger_eb, self.item_eb, Hadamard_fea], 1) 94 | cross_inp1 = self.build_dnn_net(cross_inp, use_dice=True) 95 | 96 | inp = tf.concat( 97 | [self.uid_batch_embedded, self.item_his_eb_sum, cross_inp, 98 | self.item_eb * self.item_his_eb_sum, att_fea_hard, att_fea_mix], -1) 99 | # Fully connected layer 100 | self.build_fcn_net2(inp, use_dice=True) 101 | -------------------------------------------------------------------------------- /script/models/DEI2N_NO_TIM.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # MIT License 3 | # 4 | # Copyright (c) 2020 skx300 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | # -*- coding: utf-8 -*- 25 | # @Time : 2023/1/1 09:49 26 | 27 | from models.base_model import Model 28 | from common.utils import * 29 | 30 | 31 | class DEI2N_NO_TIM(Model): 32 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 33 | super(DEI2N_NO_TIM, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 34 | ATTENTION_SIZE, 35 | use_negsampling) 36 | 37 | is_training = True 38 | 39 | with tf.name_scope('UIM_layer'): 40 | inp = tf.concat([self.uid_batch_embedded, self.item_his_hard_eb_sum, self.trigger_eb, 41 | self.trigger_eb * self.item_his_hard_eb_sum, 42 | self.seq_hard_len_eb], 1) 43 | inp_out = self.build_dnn_net(inp, use_dice=True, is_training=is_training) 44 | dnn3 = tf.layers.dense(inp_out, 1, activation=tf.sigmoid, name='f3_uim') 45 | 46 | with tf.name_scope('Attention_layer'): 47 | # clicks_trans_block = SelfAttentionPooling( 48 | # num_heads=2, 49 | # key_mask=self.mask, 50 | # query_mask=self.mask, 51 | # length=20, 52 | # linear_key_dim=HIDDEN_SIZE, 53 | # linear_value_dim=HIDDEN_SIZE, 54 | # output_dim=EMBEDDING_DIM * 2, 55 | # hidden_dim=EMBEDDING_DIM * 4, 56 | # num_layer=1, 57 | # keep_prob=self.keep_prob 58 | # ) 59 | item_his_eb_mix = tf.concat([self.item_his_eb, self.item_his_time_eb], axis=2) 60 | item_his_eb_mix = tf.layers.dense(item_his_eb_mix, EMBEDDING_DIM * 2, activation=None, 61 | name='item_his_eb_mix') 62 | # clicks_trans_output = clicks_trans_block.build(item_his_eb_mix, reuse=False, 63 | # scope='clicks_trans') # (batch_size, 30, output_dim) 64 | 65 | attention_output_soft = din_attention_dihn_dmin(self.item_eb, self.item_his_eb, 66 | None, 67 | ATTENTION_SIZE, self.mask, 68 | stag='click') 69 | att_fea_soft = tf.reduce_sum(attention_output_soft, 1) 70 | 71 | attention_output_trigger = din_attention_dihn_dmin(self.trigger_eb, self.item_his_eb, 72 | None, 73 | ATTENTION_SIZE, self.mask, 74 | stag='trigger_hard') 75 | att_fea_trigger = tf.reduce_sum(attention_output_trigger, 1) 76 | attention_output_hard = din_attention_dihn_dmin(self.item_eb, self.item_his_hard_eb, 77 | self.item_his_time_hard_eb, 78 | ATTENTION_SIZE, self.mask_hard, 79 | stag='click_hard') 80 | att_fea_hard = tf.reduce_sum(attention_output_hard, 1) 81 | 82 | tf.summary.histogram('att_fea_soft', att_fea_soft) 83 | 84 | att_fea_mix = tf.multiply(att_fea_trigger, dnn3) + tf.multiply(att_fea_soft, 1 - dnn3) 85 | 86 | # with tf.name_scope('trigger_target_interaction_layer'): 87 | Hadamard_fea = tf.multiply(self.trigger_eb, self.item_eb) 88 | cross_inp = tf.concat( 89 | [self.trigger_eb, self.item_eb, Hadamard_fea], 1) 90 | 91 | inp = tf.concat( 92 | [self.uid_batch_embedded, self.item_his_eb_sum, cross_inp, 93 | self.item_eb * self.item_his_eb_sum, att_fea_hard, att_fea_mix], -1) 94 | # Fully connected layer 95 | self.build_fcn_net2(inp, use_dice=True) 96 | -------------------------------------------------------------------------------- /script/models/DEI2N_NO_UIM.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # # MIT License 3 | # # 4 | # # Copyright (c) 2020 skx300 5 | # # 6 | # # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # # of this software and associated documentation files (the "Software"), to deal 8 | # # in the Software without restriction, including without limitation the rights 9 | # # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # # copies of the Software, and to permit persons to whom the Software is 11 | # # furnished to do so, subject to the following conditions: 12 | # # 13 | # # The above copyright notice and this permission notice shall be included in all 14 | # # copies or substantial portions of the Software. 15 | # # 16 | # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # # SOFTWARE. 23 | # -*- coding: utf-8 -*- 24 | # @Time : 2023/1/1 09:49 25 | 26 | from models.base_model import Model 27 | from common.utils import * 28 | 29 | 30 | class DEI2N_NO_UIM(Model): 31 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 32 | super(DEI2N_NO_UIM, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 33 | ATTENTION_SIZE, 34 | use_negsampling) 35 | 36 | # is_training = True 37 | # self.keep_prob = 0.9 38 | # with tf.name_scope('UIM_layer'): 39 | # inp = tf.concat([self.uid_batch_embedded, self.item_his_hard_eb_sum, self.trigger_eb, 40 | # self.seq_hard_len_eb], 1) 41 | # dnn1 = tf.layers.batch_normalization(inputs=inp, name='bn1_uin', training=is_training) 42 | # dnn1 = tf.layers.dense(dnn1, EMBEDDING_DIM * 2, activation=tf.nn.relu, name='f1_uim') 43 | # dnn2 = tf.layers.batch_normalization(inputs=dnn1, name='bn2_uin', training=is_training) 44 | # dnn3 = tf.layers.dense(dnn2, EMBEDDING_DIM, activation=tf.nn.relu, name='f2_uim') 45 | # dnn3 = tf.layers.batch_normalization(inputs=dnn3, name='bn3_uin', training=is_training) 46 | # dnn3 = tf.layers.dense(dnn3, 1, activation=tf.sigmoid, name='f3_uim') 47 | 48 | with tf.name_scope('Attention_layer'): 49 | # clicks_trans_block = SelfAttentionPooling( 50 | # num_heads=2, 51 | # key_mask=self.mask, 52 | # query_mask=self.mask, 53 | # length=30, 54 | # linear_key_dim=HIDDEN_SIZE, 55 | # linear_value_dim=HIDDEN_SIZE, 56 | # output_dim=EMBEDDING_DIM * 2, 57 | # hidden_dim=EMBEDDING_DIM * 4, 58 | # num_layer=1, 59 | # keep_prob=self.keep_prob 60 | # ) 61 | item_his_eb_mix = tf.concat([self.item_his_eb, self.item_his_time_eb], axis=2) 62 | item_his_eb_mix = tf.layers.dense(item_his_eb_mix, EMBEDDING_DIM * 2, activation=None, 63 | name='item_his_eb_mix') 64 | # clicks_trans_output = clicks_trans_block.build(item_his_eb_mix, reuse=False, 65 | # scope='clicks_trans') # (batch_size, 30, output_dim) 66 | 67 | attention_output_soft = din_attention_dihn_dmin(self.item_eb, item_his_eb_mix, 68 | self.item_his_time_eb, 69 | ATTENTION_SIZE, self.mask, 70 | stag='click') 71 | att_fea_soft = tf.reduce_sum(attention_output_soft, 1) 72 | 73 | attention_output_trigger = din_attention_dihn_dmin(self.trigger_eb, item_his_eb_mix, 74 | self.item_his_time_eb, 75 | ATTENTION_SIZE, self.mask, 76 | stag='trigger_hard') 77 | att_fea_trigger = tf.reduce_sum(attention_output_trigger, 1) 78 | attention_output_hard = din_attention_dihn_dmin(self.item_eb, self.item_his_hard_eb, 79 | self.item_his_time_hard_eb, 80 | ATTENTION_SIZE, self.mask_hard, 81 | stag='click_hard') 82 | att_fea_hard = tf.reduce_sum(attention_output_hard, 1) 83 | 84 | tf.summary.histogram('att_fea_soft', att_fea_soft) 85 | 86 | # att_fea_mix = tf.multiply(att_fea_trigger, dnn3) + tf.multiply(att_fea_soft, 1 - dnn3) 87 | 88 | with tf.name_scope('trigger_target_interaction_layer'): 89 | Hadamard_fea = tf.multiply(self.trigger_eb, self.item_eb) 90 | cross_inp = tf.concat( 91 | [self.trigger_eb, self.item_eb, Hadamard_fea], 1) 92 | 93 | inp = tf.concat( 94 | [self.uid_batch_embedded, self.item_his_eb_sum, cross_inp, 95 | self.item_eb * self.item_his_eb_sum, att_fea_hard, att_fea_trigger, att_fea_soft], -1) 96 | # Fully connected layer 97 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DIAN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:49 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class DIAN(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=True): 11 | super(DIAN, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling) 14 | 15 | # UIM Attention layer 16 | is_training = False 17 | with tf.name_scope('UIM_layer'): 18 | inp = tf.concat([self.uid_batch_embedded, self.trigger_eb, self.item_his_eb_mean, self.item_his_eb_sum * self.trigger_eb], 1) 19 | dnn1 = tf.layers.dense(inp, 200, activation=None, name='f1_uim') 20 | bn1 = tf.contrib.layers.batch_norm(dnn1, is_training=is_training, activation_fn=tf.nn.relu, scope='bn1_uim') 21 | dnn2 = tf.layers.dense(bn1, EMBEDDING_DIM * 2, activation=None, name='f2_uim') 22 | bn2 = tf.contrib.layers.batch_norm(dnn2, is_training=is_training, activation_fn=tf.nn.relu, scope='bn2_uim') 23 | dnn3 = tf.layers.dense(bn2, 1, activation=None, name='f3_uim') 24 | uim_logit = tf.nn.sigmoid(dnn3) 25 | self.aux_loss = - tf.reduce_mean(tf.log(uim_logit) * self.target_aux_ph[0] + self.target_aux_ph[1] * tf.log(1 - uim_logit)) 26 | 27 | with tf.name_scope('Trigger_Aware_net'): 28 | Hta_s = multihead_target_attention(queries=self.item_eb, 29 | keys=self.item_his_eb, 30 | num_heads=4, 31 | activation_fn=None, 32 | key_masks=self.mask, 33 | query_masks=None, 34 | num_units=HIDDEN_SIZE, 35 | num_output_units=EMBEDDING_DIM * 2, 36 | name='short_seq_target_attention') 37 | Hta_s = tf.reduce_sum(Hta_s, 1) 38 | Hta_l = multihead_target_attention(queries=self.item_eb, 39 | keys=self.item_his_hard_eb, 40 | num_heads=4, 41 | activation_fn=None, 42 | key_masks=self.mask_hard, 43 | query_masks=None, 44 | num_units=HIDDEN_SIZE, 45 | num_output_units=EMBEDDING_DIM * 2, 46 | name='long_seq_target_attention') 47 | Hta_l = tf.reduce_sum(Hta_l, 1) 48 | 49 | Htri_s = multihead_target_attention(queries=self.trigger_eb, 50 | keys=self.item_his_eb, 51 | num_heads=4, 52 | activation_fn=None, 53 | key_masks=self.mask, 54 | query_masks=None, 55 | num_units=HIDDEN_SIZE, 56 | num_output_units=EMBEDDING_DIM * 2, 57 | name='short_seq_trigger_attention') 58 | Htri_s = tf.reduce_sum(Htri_s, 1) 59 | Htri_l = multihead_target_attention(queries=self.trigger_eb, 60 | keys=self.item_his_hard_eb, 61 | num_heads=4, 62 | activation_fn=None, 63 | key_masks=self.mask_hard, 64 | query_masks=None, 65 | num_units=HIDDEN_SIZE, 66 | num_output_units=EMBEDDING_DIM * 2, 67 | name='long_seq_trigger_attention') 68 | Htri_l = tf.reduce_sum(Htri_l, 1) 69 | 70 | 71 | inp = tf.concat( 72 | [self.uid_batch_embedded, self.item_eb, self.trigger_eb, self.item_his_eb_sum, 73 | self.item_eb * self.item_his_eb_sum, Hta_s, Hta_l, Htri_s, Htri_l], -1) 74 | prop_aware = self.build_fcn_net_DIAN(inp, use_dice=True, name="layer_aware") 75 | 76 | with tf.name_scope('Trigger_Free_net'): 77 | Hta_s_free = multihead_target_attention(queries=self.item_eb, 78 | keys=self.item_his_eb, 79 | num_heads=4, 80 | activation_fn=None, 81 | key_masks=self.mask, 82 | query_masks=None, 83 | num_units=HIDDEN_SIZE, 84 | num_output_units=EMBEDDING_DIM * 2, 85 | name='short_seq_target_attention_free') 86 | Hta_s_free = tf.reduce_sum(Hta_s_free, 1) 87 | Hta_l_free = multihead_target_attention(queries=self.item_eb, 88 | keys=self.item_his_hard_eb, 89 | num_heads=4, 90 | activation_fn=None, 91 | key_masks=self.mask_hard, 92 | query_masks=None, 93 | num_units=HIDDEN_SIZE, 94 | num_output_units=EMBEDDING_DIM * 2, 95 | name='long_seq_target_attention_free') 96 | Hta_l_free = tf.reduce_sum(Hta_l_free, 1) 97 | inp_free = tf.concat( 98 | [self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, 99 | self.item_eb * self.item_his_eb_sum, Hta_l_free, Hta_s_free], -1) 100 | prop_freee = self.build_fcn_net_DIAN(inp_free, use_dice=True, name="layer_free") 101 | 102 | self.build_fcn_net(inp, use_dice=True) 103 | self.y_hat = prop_freee * uim_logit + prop_aware * (1 - uim_logit) 104 | 105 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 106 | with tf.control_dependencies(update_ops): 107 | with tf.name_scope('Metrics'): 108 | # Cross-entropy loss and optimizer initialization 109 | ctr_loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph) 110 | self.loss = ctr_loss 111 | if self.use_negsampling: 112 | self.loss += 0.1 * self.aux_loss 113 | tf.summary.scalar('loss', self.loss) 114 | self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss) 115 | 116 | # Accuracy metric 117 | self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32)) 118 | tf.summary.scalar('accuracy', self.accuracy) 119 | 120 | self.merged = tf.summary.merge_all() 121 | -------------------------------------------------------------------------------- /script/models/DIHN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:49 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class DIHN(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=True): 11 | super(DIHN, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling) 14 | 15 | # UIM Attention layer 16 | is_training = True 17 | with tf.name_scope('UIM_layer'): 18 | attention_output = din_attention_hian(self.trigger_eb, self.item_his_eb, self.time_stamp_his_batch_ph, 19 | ATTENTION_SIZE, self.mask, stag='click_uim') 20 | att_fea = tf.reduce_sum(attention_output, 1) 21 | inp = tf.concat([self.uid_batch_embedded, self.trigger_eb, att_fea, self.item_his_eb_sum, 22 | self.item_his_eb_sum * self.trigger_eb], 1) 23 | dnn1 = tf.layers.dense(inp, 200, activation=None, name='f1_uim') 24 | bn1 = tf.contrib.layers.batch_norm(dnn1, is_training=is_training, activation_fn=tf.nn.relu, scope='bn1_uim') 25 | 26 | dnn2 = tf.layers.dense(bn1, EMBEDDING_DIM * 2, activation=None, name='f2_uim') 27 | bn2 = tf.contrib.layers.batch_norm(dnn2, is_training=is_training, activation_fn=tf.nn.relu, scope='bn2_uim') 28 | dnn3 = tf.layers.dense(bn2, 2, activation=None, name='f3_uim') 29 | uim_logit = tf.nn.softmax(dnn3) + 0.00000001 30 | self.aux_loss = - tf.reduce_mean(tf.log(uim_logit) * self.target_aux_ph) 31 | 32 | fusing_embedding = tf.multiply(dnn2, self.trigger_eb) + tf.multiply(1 - dnn2, self.item_eb) 33 | # fusing_embedding = tf.concat([self.trigger_eb,self.item_eb],1) 34 | 35 | clicks_trans_block = SelfAttentionPooling( 36 | num_heads=2, 37 | key_mask=self.mask, 38 | query_mask=self.mask, 39 | length=30, 40 | linear_key_dim=HIDDEN_SIZE, 41 | linear_value_dim=HIDDEN_SIZE, 42 | output_dim=EMBEDDING_DIM * 2, 43 | hidden_dim=EMBEDDING_DIM * 4, 44 | num_layer=2, 45 | keep_prob=1.0 46 | ) 47 | clicks_trans_output = clicks_trans_block.build(self.item_his_eb, reuse=False, 48 | scope='clicks_trans') # (batch_size, 30, output_dim) 49 | 50 | with tf.name_scope('hybrid_interest_extracting_module'): 51 | hard_trans_block = SelfAttentionPooling( 52 | num_heads=2, 53 | key_mask=self.mask_hard, 54 | query_mask=self.mask_hard, 55 | length=20, 56 | linear_key_dim=HIDDEN_SIZE, 57 | linear_value_dim=HIDDEN_SIZE, 58 | output_dim=EMBEDDING_DIM * 2, 59 | hidden_dim=EMBEDDING_DIM * 4, 60 | num_layer=2, 61 | keep_prob=1.0 62 | ) 63 | hard_trans_output = hard_trans_block.build(self.item_his_hard_eb, reuse=False, 64 | scope='hard_trans') # (batch_size, 30, output_dim) 65 | subcate_clicks_hard_pool_res = tf.reduce_mean(hard_trans_output, axis=1) 66 | 67 | # att_fea_fusing = time_attention_pooling(clicks_trans_output, fusing_embedding, 68 | # self.mask, False, 'click_attention_pooling') 69 | att_fea_fusing = din_attention_hian(fusing_embedding, clicks_trans_output, self.time_stamp_his_batch_ph, 70 | ATTENTION_SIZE, self.mask, stag='fusing_gate_attention') 71 | att_fea_fusing =tf.reduce_sum(att_fea_fusing,1) 72 | 73 | inp = tf.concat( 74 | [self.uid_batch_embedded, self.item_eb, self.item_eb * self.item_his_eb_sum, self.item_his_eb_sum, 75 | self.trigger_eb, att_fea_fusing, subcate_clicks_hard_pool_res], -1) 76 | # Fully connected layer 77 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DIN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:49 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class DIN(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 11 | super(DIN, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling) 14 | 15 | # # Attention layer 16 | # with tf.name_scope('Attention_layer'): 17 | # attention_output = din_attention(self.item_eb, self.item_his_eb, ATTENTION_SIZE, self.mask) 18 | # att_fea = tf.reduce_sum(attention_output, 1) 19 | # tf.summary.histogram('att_fea', att_fea) 20 | # inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, att_fea], -1) 21 | # # Fully connected layer 22 | # self.build_fcn_net(inp, use_dice=True) 23 | 24 | # Attention layer 25 | with tf.name_scope('Attention_layer'): 26 | attention_output = din_attention(self.item_eb, self.item_his_eb, ATTENTION_SIZE, self.mask, stag='click') 27 | att_fea = tf.reduce_sum(attention_output, 1) 28 | attention_output1 = din_attention(self.trigger_eb, self.item_his_eb, ATTENTION_SIZE, self.mask, 29 | stag='click_hard') 30 | att_fea1 = tf.reduce_sum(attention_output1, 1) 31 | tf.summary.histogram('att_fea', att_fea) 32 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, 33 | self.item_eb * self.item_his_eb_sum, att_fea1, att_fea], -1) 34 | # inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, 35 | # self.item_eb * self.item_his_eb_sum, att_fea], -1) 36 | # Fully connected layer 37 | self.build_fcn_net(inp, use_dice=True) 38 | -------------------------------------------------------------------------------- /script/models/DIN_V2_Gru_Gru_att.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:43 4 | 5 | from common.rnn import dynamic_rnn 6 | from common.utils import * 7 | from models.base_model import Model 8 | 9 | 10 | class DIN_V2_Gru_Gru_att(Model): 11 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 12 | super(DIN_V2_Gru_Gru_att, self).__init__(n_uid, n_mid, n_cat, 13 | EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, 14 | use_negsampling) 15 | 16 | # RNN layer(-s) 17 | with tf.name_scope('rnn_1'): 18 | rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 19 | sequence_length=self.seq_len_ph, dtype=tf.float32, 20 | scope="gru1") 21 | tf.summary.histogram('GRU_outputs', rnn_outputs) 22 | 23 | with tf.name_scope('rnn_2'): 24 | rnn_outputs2, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 25 | sequence_length=self.seq_len_ph, dtype=tf.float32, 26 | scope="gru2") 27 | tf.summary.histogram('GRU2_outputs', rnn_outputs2) 28 | 29 | # Attention layer 30 | with tf.name_scope('Attention_layer_1'): 31 | att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs2, ATTENTION_SIZE, self.mask, 32 | softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 33 | att_fea = tf.reduce_sum(att_outputs, 1) 34 | tf.summary.histogram('att_fea', att_fea) 35 | 36 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, att_fea], 1) 37 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DIN_V2_Gru_QA_attGru.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:46 4 | 5 | from common.rnn import dynamic_rnn 6 | from models.base_model import Model 7 | from common.utils import * 8 | 9 | 10 | class DIN_V2_Gru_QA_attGru(Model): 11 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 12 | super(DIN_V2_Gru_QA_attGru, self).__init__(n_uid, n_mid, n_cat, 13 | EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, 14 | use_negsampling) 15 | 16 | # RNN layer(-s) 17 | with tf.name_scope('rnn_1'): 18 | rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 19 | sequence_length=self.seq_len_ph, dtype=tf.float32, 20 | scope="gru1") 21 | tf.summary.histogram('GRU_outputs', rnn_outputs) 22 | 23 | # Attention layer 24 | with tf.name_scope('Attention_layer_1'): 25 | att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask, 26 | softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 27 | tf.summary.histogram('alpha_outputs', alphas) 28 | 29 | with tf.name_scope('rnn_2'): 30 | rnn_outputs2, final_state2 = dynamic_rnn(QAAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 31 | att_scores = tf.expand_dims(alphas, -1), 32 | sequence_length=self.seq_len_ph, dtype=tf.float32, 33 | scope="gru2") 34 | tf.summary.histogram('GRU2_Final_State', final_state2) 35 | 36 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, final_state2], 1) 37 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DIN_V2_Gru_Vec_attGru.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # @Time : 2023/1/1 09:51 5 | 6 | from common.rnn import dynamic_rnn 7 | from models.base_model import Model 8 | from common.utils import * 9 | 10 | 11 | class DIN_V2_Gru_Vec_attGru(Model): 12 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 13 | super(DIN_V2_Gru_Vec_attGru, self).__init__(n_uid, n_mid, n_cat, 14 | EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, 15 | use_negsampling) 16 | 17 | # RNN layer(-s) 18 | with tf.name_scope('rnn_1'): 19 | rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 20 | sequence_length=self.seq_len_ph, dtype=tf.float32, 21 | scope="gru1") 22 | tf.summary.histogram('GRU_outputs', rnn_outputs) 23 | 24 | # Attention layer 25 | with tf.name_scope('Attention_layer_1'): 26 | att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask, 27 | softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 28 | tf.summary.histogram('alpha_outputs', alphas) 29 | print('self.item_eb.get_shape()',self.item_eb.get_shape()) 30 | print('rnn_outputs.get_shape()',rnn_outputs.get_shape()) 31 | print('att_outputs.get_shape())',att_outputs.get_shape()) 32 | print('alphas.get_shape()',alphas.get_shape()) 33 | with tf.name_scope('rnn_2'): 34 | rnn_outputs2, final_state2 = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 35 | att_scores = tf.expand_dims(alphas, -1), 36 | sequence_length=self.seq_len_ph, dtype=tf.float32, 37 | scope="gru2") 38 | tf.summary.histogram('GRU2_Final_State', final_state2) 39 | 40 | #inp = tf.concat([self.uid_batch_embedded, self.item_eb, final_state2, self.item_his_eb_sum], 1) 41 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, final_state2], 1) 42 | print('inp.get_shape()',inp.get_shape()) 43 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DIN_V2_Gru_Vec_attGru_Neg.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:50 4 | 5 | from common.rnn import dynamic_rnn 6 | from models.base_model import Model 7 | from common.utils import * 8 | 9 | 10 | class DIN_V2_Gru_Vec_attGru_Neg(Model): 11 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=True): 12 | super(DIN_V2_Gru_Vec_attGru_Neg, self).__init__(n_uid, n_mid, n_cat, 13 | EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, 14 | use_negsampling) 15 | 16 | # RNN layer(-s) 17 | # with tf.name_scope('rnn_1'): 18 | # rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 19 | # sequence_length=self.seq_len_ph, dtype=tf.float32, 20 | # scope="gru1") 21 | # tf.summary.histogram('GRU_outputs', rnn_outputs) 22 | # 23 | # aux_loss_1 = self.auxiliary_loss_v2(rnn_outputs[:, :-1, :], self.item_his_eb[:, 1:, :], 24 | # self.noclk_item_his_eb[:, 1:, :], self.item_his_time_eb[:,1:,:] - self.item_his_time_eb[:,:-1,:], 25 | # self.mask[:, 1:], stag="gru") 26 | # self.aux_loss = aux_loss_1 27 | # 28 | # # Attention layer 29 | # with tf.name_scope('Attention_layer_1'): 30 | # att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask, 31 | # softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 32 | # tf.summary.histogram('alpha_outputs', alphas) 33 | # att_outputs_trigger, alphas_trigger = din_fcn_attention(self.trigger_eb, rnn_outputs, ATTENTION_SIZE, 34 | # self.mask, 35 | # softmax_stag=1, stag='1_2', mode='LIST', 36 | # return_alphas=True) 37 | # with tf.name_scope('rnn_2'): 38 | # rnn_outputs2, final_state2 = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 39 | # att_scores=tf.expand_dims(alphas, -1), 40 | # sequence_length=self.seq_len_ph, dtype=tf.float32, 41 | # scope="gru2") 42 | # tf.summary.histogram('GRU2_Final_State', final_state2) 43 | # 44 | # rnn_outputs_trigger, final_state_trigger = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 45 | # att_scores=tf.expand_dims(alphas_trigger, -1), 46 | # sequence_length=self.seq_len_ph, dtype=tf.float32, 47 | # scope="gru2_trigger") 48 | # 49 | # inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, 50 | # self.item_eb * self.item_his_eb_sum, self.trigger_eb, final_state2, final_state_trigger], 1) 51 | # # inp = tf.concat([self.user_feat, self.item_feat, self.item_his_eb_sum, 52 | # # self.item_eb * self.item_his_eb_sum, final_state2], 1) 53 | # self.build_fcn_net(inp, use_dice=True) 54 | ############################################# 55 | # RNN layer(-s) 56 | with tf.name_scope('rnn_1'): 57 | rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 58 | sequence_length=self.seq_len_ph, dtype=tf.float32, 59 | scope="gru1") 60 | tf.summary.histogram('GRU_outputs', rnn_outputs) 61 | 62 | aux_loss_1 = self.auxiliary_loss(rnn_outputs[:, :-1, :], self.item_his_eb[:, 1:, :], 63 | self.noclk_item_his_eb[:, 1:, :], 64 | self.mask[:, 1:], stag="gru") 65 | self.aux_loss = aux_loss_1 66 | 67 | # Attention layer 68 | with tf.name_scope('Attention_layer_1'): 69 | att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask, 70 | softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 71 | tf.summary.histogram('alpha_outputs', alphas) 72 | att_outputs_trigger, alphas_trigger = din_fcn_attention(self.trigger_eb, rnn_outputs, ATTENTION_SIZE, 73 | self.mask, 74 | softmax_stag=1, stag='1_2', mode='LIST', 75 | return_alphas=True) 76 | with tf.name_scope('rnn_2'): 77 | rnn_outputs2, final_state2 = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 78 | att_scores=tf.expand_dims(alphas, -1), 79 | sequence_length=self.seq_len_ph, dtype=tf.float32, 80 | scope="gru2") 81 | tf.summary.histogram('GRU2_Final_State', final_state2) 82 | 83 | rnn_outputs_trigger, final_state_trigger = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 84 | att_scores=tf.expand_dims(alphas_trigger, -1), 85 | sequence_length=self.seq_len_ph, dtype=tf.float32, 86 | scope="gru2_trigger") 87 | 88 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, 89 | self.item_eb * self.item_his_eb_sum, self.trigger_eb, final_state2, final_state_trigger], 1) 90 | # inp = tf.concat([self.user_feat, self.item_feat, self.item_his_eb_sum, 91 | # self.item_eb * self.item_his_eb_sum, final_state2], 1) 92 | self.build_fcn_net(inp, use_dice=True) 93 | 94 | 95 | 96 | ############################################# 97 | # RNN layer(-s) 98 | # with tf.name_scope('rnn_1'): 99 | # rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 100 | # sequence_length=self.seq_len_ph, dtype=tf.float32, 101 | # scope="gru1") 102 | # tf.summary.histogram('GRU_outputs', rnn_outputs) 103 | # 104 | # aux_loss_1 = self.auxiliary_loss(rnn_outputs[:, :-1, :], self.item_his_eb[:, 1:, :], 105 | # self.noclk_item_his_eb[:, 1:, :], 106 | # self.mask[:, 1:], stag="gru") 107 | # self.aux_loss = aux_loss_1 108 | # 109 | # # Attention layer 110 | # with tf.name_scope('Attention_layer_1'): 111 | # att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask, 112 | # softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 113 | # tf.summary.histogram('alpha_outputs', alphas) 114 | # 115 | # with tf.name_scope('rnn_2'): 116 | # rnn_outputs2, final_state2 = dynamic_rnn(VecAttGRUCell(HIDDEN_SIZE), inputs=rnn_outputs, 117 | # att_scores = tf.expand_dims(alphas, -1), 118 | # sequence_length=self.seq_len_ph, dtype=tf.float32, 119 | # scope="gru2") 120 | # tf.summary.histogram('GRU2_Final_State', final_state2) 121 | # 122 | # inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, final_state2], 1) 123 | # self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DIN_V2_Gru_att_Gru.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # @Time : 2023/1/1 09:39 5 | 6 | from common.rnn import dynamic_rnn 7 | from common.utils import * 8 | from models.base_model import Model 9 | 10 | 11 | class DIN_V2_Gru_att_Gru(Model): 12 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 13 | super(DIN_V2_Gru_att_Gru, self).__init__(n_uid, n_mid, n_cat, 14 | EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, 15 | use_negsampling) 16 | 17 | # RNN layer(-s) 18 | with tf.name_scope('rnn_1'): 19 | rnn_outputs, _ = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=self.item_his_eb, 20 | sequence_length=self.seq_len_ph, dtype=tf.float32, 21 | scope="gru1") 22 | tf.summary.histogram('GRU_outputs', rnn_outputs) 23 | 24 | # Attention layer 25 | with tf.name_scope('Attention_layer_1'): 26 | att_outputs, alphas = din_fcn_attention(self.item_eb, rnn_outputs, ATTENTION_SIZE, self.mask, 27 | softmax_stag=1, stag='1_1', mode='LIST', return_alphas=True) 28 | tf.summary.histogram('alpha_outputs', alphas) 29 | 30 | with tf.name_scope('rnn_2'): 31 | rnn_outputs2, final_state2 = dynamic_rnn(GRUCell(HIDDEN_SIZE), inputs=att_outputs, 32 | sequence_length=self.seq_len_ph, dtype=tf.float32, 33 | scope="gru2") 34 | tf.summary.histogram('GRU2_Final_State', final_state2) 35 | 36 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum, final_state2], 1) 37 | # Fully connected layer 38 | self.build_fcn_net(inp, use_dice=True) -------------------------------------------------------------------------------- /script/models/DNN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:47 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class DNN(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 11 | super(DNN, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling) 14 | 15 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum], 1) 16 | self.build_fcn_net(inp, use_dice=False) -------------------------------------------------------------------------------- /script/models/DNN_Multi_Head.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:52 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class DNN_Multi_Head(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=True, maxlen=30): 11 | super(DNN_Multi_Head, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling, maxlen) 14 | # print('self.item_eb.get_shape()', self.item_eb.get_shape()) 15 | # print('self.item_his_eb.get_shape()', self.item_his_eb.get_shape()) 16 | # other_embedding_size = 2 17 | # self.position_his = tf.range(maxlen) 18 | # self.position_embeddings_var = tf.get_variable("position_embeddings_var", [maxlen, other_embedding_size]) 19 | # self.position_his_eb = tf.nn.embedding_lookup(self.position_embeddings_var, self.position_his) # T,E 20 | # self.position_his_eb = tf.tile(self.position_his_eb, [tf.shape(self.item_his_eb)[0], 1]) # B*T,E 21 | # self.position_his_eb = tf.reshape(self.position_his_eb, [tf.shape(self.item_his_eb)[0], -1, 22 | # self.position_his_eb.get_shape().as_list()[ 23 | # 1]]) # B,T,E 24 | # with tf.name_scope("multi_head_attention"): 25 | # multihead_attention_outputs = self_multi_head_attn(self.item_his_eb, num_units=EMBEDDING_DIM * 2, 26 | # num_heads=4, dropout_rate=0, is_training=True) 27 | # print('multihead_attention_outputs.get_shape()', multihead_attention_outputs.get_shape()) 28 | # multihead_attention_outputs1 = tf.compat.v1.layers.dense(multihead_attention_outputs, EMBEDDING_DIM * 4, 29 | # activation=tf.nn.relu) 30 | # multihead_attention_outputs1 = tf.compat.v1.layers.dense(multihead_attention_outputs1, EMBEDDING_DIM * 2) 31 | # multihead_attention_outputs = multihead_attention_outputs1 + multihead_attention_outputs 32 | # # multihead_attention_outputs = layer_norm(multihead_attention_outputs, name='multi_head_attention') 33 | # aux_loss_1 = self.auxiliary_loss(multihead_attention_outputs[:, :-1, :], self.item_his_eb[:, 1:, :], 34 | # self.noclk_item_his_eb[:, 1:, :], 35 | # self.mask[:, 1:], stag="gru") 36 | # self.aux_loss = aux_loss_1 37 | # 38 | # inp = tf.concat( 39 | # [self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum], 1) 40 | # with tf.name_scope("multi_head_attention"): 41 | # multihead_attention_outputss = self_multi_head_attn_v2(multihead_attention_outputs, num_units=36, 42 | # num_heads=4, dropout_rate=0, is_training=True) 43 | # for i, multihead_attention_outputs_v2 in enumerate(multihead_attention_outputss): 44 | # multihead_attention_outputs3 = tf.compat.v1.layers.dense(multihead_attention_outputs_v2, 45 | # EMBEDDING_DIM * 4, activation=tf.nn.relu) 46 | # multihead_attention_outputs3 = tf.compat.v1.layers.dense(multihead_attention_outputs3, 47 | # EMBEDDING_DIM * 2) 48 | # multihead_attention_outputs_v2 = multihead_attention_outputs3 + multihead_attention_outputs_v2 49 | # # multihead_attention_outputs_v2= layer_norm(multihead_attention_outputs_v2, name='multi_head_attention'+str(i)) 50 | # print('multihead_attention_outputs_v2.get_shape()', multihead_attention_outputs_v2.get_shape()) 51 | # with tf.name_scope('Attention_layer' + str(i)): 52 | # # 这里使用position embedding来算attention 53 | # print('self.position_his_eb.get_shape()', self.position_his_eb.get_shape()) 54 | # print('self.item_eb.get_shape()', self.item_eb.get_shape()) 55 | # attention_output, attention_score, attention_scores_no_softmax = din_attention_new(self.item_eb, 56 | # multihead_attention_outputs_v2, 57 | # self.position_his_eb, 58 | # ATTENTION_SIZE, 59 | # self.mask, 60 | # stag=str(i)) 61 | # print('attention_output.get_shape()', attention_output.get_shape()) 62 | # att_fea = tf.reduce_sum(attention_output, 1) 63 | # inp = tf.concat([inp, att_fea], 1) 64 | # # Fully connected layer 65 | # self.build_fcn_net(inp, use_dice=True) 66 | 67 | #####add trigger attention 68 | 69 | other_embedding_size = 2 70 | self.position_his = tf.range(maxlen) 71 | self.position_embeddings_var = tf.get_variable("position_embeddings_var", [maxlen, other_embedding_size]) 72 | self.position_his_eb = tf.nn.embedding_lookup(self.position_embeddings_var, self.position_his) # T,E 73 | self.position_his_eb = tf.tile(self.position_his_eb, [tf.shape(self.item_his_eb)[0], 1]) # B*T,E 74 | self.position_his_eb = tf.reshape(self.position_his_eb, [tf.shape(self.item_his_eb)[0], -1, 75 | self.position_his_eb.get_shape().as_list()[ 76 | 1]]) # B,T,E 77 | with tf.name_scope("multi_head_attention"): 78 | multihead_attention_outputs = self_multi_head_attn(self.item_his_eb, num_units=EMBEDDING_DIM * 2, 79 | num_heads=4, dropout_rate=0, is_training=True) 80 | print('multihead_attention_outputs.get_shape()', multihead_attention_outputs.get_shape()) 81 | multihead_attention_outputs1 = tf.compat.v1.layers.dense(multihead_attention_outputs, EMBEDDING_DIM * 4, 82 | activation=tf.nn.relu) 83 | multihead_attention_outputs1 = tf.compat.v1.layers.dense(multihead_attention_outputs1, EMBEDDING_DIM * 2) 84 | multihead_attention_outputs = multihead_attention_outputs1 + multihead_attention_outputs 85 | # multihead_attention_outputs = layer_norm(multihead_attention_outputs, name='multi_head_attention') 86 | aux_loss_1 = self.auxiliary_loss(multihead_attention_outputs[:, :-1, :], self.item_his_eb[:, 1:, :], 87 | self.noclk_item_his_eb[:, 1:, :], 88 | self.mask[:, 1:], stag="gru") 89 | self.aux_loss = aux_loss_1 90 | 91 | inp = tf.concat( 92 | [self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.item_eb * self.item_his_eb_sum], 1) 93 | with tf.name_scope("multi_head_attention"): 94 | multihead_attention_outputss = self_multi_head_attn_v2(multihead_attention_outputs, num_units=36, 95 | num_heads=4, dropout_rate=0, is_training=True) 96 | for i, multihead_attention_outputs_v2 in enumerate(multihead_attention_outputss): 97 | multihead_attention_outputs3 = tf.compat.v1.layers.dense(multihead_attention_outputs_v2, 98 | EMBEDDING_DIM * 4, activation=tf.nn.relu) 99 | multihead_attention_outputs3 = tf.compat.v1.layers.dense(multihead_attention_outputs3, 100 | EMBEDDING_DIM * 2) 101 | multihead_attention_outputs_v2 = multihead_attention_outputs3 + multihead_attention_outputs_v2 102 | # multihead_attention_outputs_v2= layer_norm(multihead_attention_outputs_v2, name='multi_head_attention'+str(i)) 103 | print('multihead_attention_outputs_v2.get_shape()', multihead_attention_outputs_v2.get_shape()) 104 | with tf.name_scope('Attention_layer' + str(i)): 105 | # 这里使用position embedding来算attention 106 | attention_output, attention_score, attention_scores_no_softmax = din_attention_new(self.item_eb, 107 | multihead_attention_outputs_v2, 108 | self.position_his_eb, 109 | ATTENTION_SIZE, 110 | self.mask, 111 | stag=str(i)) 112 | att_fea = tf.reduce_sum(attention_output, 1) 113 | 114 | attention_output_tr, attention_score_tr, attention_scores_no_softmax_tr = din_attention_new(self.trigger_eb, 115 | multihead_attention_outputs_v2, 116 | self.position_his_eb, 117 | ATTENTION_SIZE, 118 | self.mask, 119 | stag='tr'+str(i)) 120 | att_fea_tr = tf.reduce_sum(attention_output_tr, 1) 121 | inp = tf.concat([inp, att_fea, att_fea_tr], 1) 122 | # Fully connected layer 123 | self.build_fcn_net(inp, use_dice=True) 124 | -------------------------------------------------------------------------------- /script/models/LR.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:47 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class LR(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 11 | super(LR, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling) 14 | 15 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum], 1) 16 | self.build_lr_net(inp) -------------------------------------------------------------------------------- /script/models/PNN.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:48 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | 9 | class PNN(Model): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 11 | super(PNN, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 12 | ATTENTION_SIZE, 13 | use_negsampling) 14 | 15 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, 16 | self.item_eb * self.item_his_eb_sum], 1) 17 | 18 | # Fully connected layer 19 | self.build_fcn_net(inp, use_dice=False) -------------------------------------------------------------------------------- /script/models/WideDeep.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:44 4 | 5 | from models.base_model import Model 6 | from common.utils import * 7 | 8 | class WideDeep(Model): 9 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False): 10 | super(WideDeep, self).__init__(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, 11 | ATTENTION_SIZE, 12 | use_negsampling) 13 | 14 | inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum, self.trigger_eb], 1) 15 | bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1') 16 | dnn1 = tf.layers.dense(bn1, 200, activation=None, name='f1') 17 | dnn1 = prelu(dnn1, 'p1') 18 | dnn2 = tf.layers.dense(dnn1, 80, activation=None, name='f2') 19 | dnn2 = prelu(dnn2, 'p2') 20 | dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3') 21 | d_layer_wide = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_eb * self.item_his_eb_sum], axis=-1) 22 | d_layer_wide = tf.layers.dense(d_layer_wide, 2, activation=None, name='f_fm') 23 | self.y_hat = tf.nn.softmax(dnn3 + d_layer_wide) + 0.00000001 24 | 25 | with tf.name_scope('Metrics'): 26 | # Cross-entropy loss and optimizer initialization 27 | self.loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph) 28 | tf.summary.scalar('loss', self.loss) 29 | self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss) 30 | 31 | # Accuracy metric 32 | self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32)) 33 | tf.summary.scalar('accuracy', self.accuracy) 34 | self.merged = tf.summary.merge_all() 35 | 36 | 37 | # inp = tf.concat([self.uid_batch_embedded, self.item_eb, self.item_his_eb_sum], 1) 38 | # # Fully connected layer 39 | # bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1') 40 | # dnn1 = tf.layers.dense(bn1, 200, activation=None, name='f1') 41 | # dnn1 = prelu(dnn1, 'p1') 42 | # dnn2 = tf.layers.dense(dnn1, 80, activation=None, name='f2') 43 | # dnn2 = prelu(dnn2, 'p2') 44 | # dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3') 45 | # d_layer_wide = tf.concat([tf.concat([self.item_eb,self.item_his_eb_sum], axis=-1), 46 | # self.item_eb * self.item_his_eb_sum], axis=-1) 47 | # d_layer_wide = tf.layers.dense(d_layer_wide, 2, activation=None, name='f_fm') 48 | # self.y_hat = tf.nn.softmax(dnn3 + d_layer_wide) 49 | # 50 | # with tf.name_scope('Metrics'): 51 | # # Cross-entropy loss and optimizer initialization 52 | # self.loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph) 53 | # tf.summary.scalar('loss', self.loss) 54 | # self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss) 55 | # 56 | # # Accuracy metric 57 | # self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32)) 58 | # tf.summary.scalar('accuracy', self.accuracy) 59 | # self.merged = tf.summary.merge_all() -------------------------------------------------------------------------------- /script/models/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:31 4 | -------------------------------------------------------------------------------- /script/models/base_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2023/1/1 09:31 4 | 5 | from common.utils import * 6 | from common.Dice import dice 7 | 8 | 9 | class Model(object): 10 | def __init__(self, n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE, use_negsampling=False, 11 | maxlen=30, maxlen_hard=20): 12 | with tf.name_scope('Inputs'): 13 | self.mid_his_batch_ph = tf.placeholder(tf.int32, [None, maxlen], name='mid_his_batch_ph') 14 | self.cat_his_batch_ph = tf.placeholder(tf.int32, [None, maxlen], name='cat_his_batch_ph') 15 | self.time_stamp_his_batch_ph = tf.placeholder(tf.int32, [None, maxlen], name='time_stamp_his_batch_ph') 16 | 17 | self.uid_batch_ph = tf.placeholder(tf.int32, [None, ], name='uid_batch_ph') 18 | self.mid_batch_ph = tf.placeholder(tf.int32, [None, ], name='mid_batch_ph') 19 | self.cat_batch_ph = tf.placeholder(tf.int32, [None, ], name='cat_batch_ph') 20 | 21 | self.mid_hard_his_batch_ph = tf.placeholder(tf.int32, [None, maxlen_hard], name='mid_hard_his_batch_ph') 22 | self.cat_hard_his_batch_ph = tf.placeholder(tf.int32, [None, maxlen_hard], name='cat_hard_his_batch_ph') 23 | self.time_stamp_hard_his_batch_ph = tf.placeholder(tf.int32, [None, maxlen_hard], name='time_stamp_hard_his_batch_ph') 24 | 25 | self.mask_hard = tf.placeholder(tf.float32, [None, None], name='mask_hard') 26 | self.seq_hard_len_ph = tf.placeholder(tf.int32, [None], name='seq_hard_len_ph') 27 | 28 | self.mask = tf.placeholder(tf.float32, [None, None], name='mask') 29 | self.seq_len_ph = tf.placeholder(tf.int32, [None], name='seq_len_ph') 30 | 31 | 32 | self.mid_trigger_his_batch_ph = tf.placeholder(tf.int32, [None, 1], name='mid_trigger_his_batch_ph') 33 | self.cat_trigger_his_batch_ph = tf.placeholder(tf.int32, [None, 1], name='cat_trigger_his_batch_ph') 34 | 35 | self.target_ph = tf.placeholder(tf.float32, [None, None], name='target_ph') 36 | self.target_aux_ph = tf.placeholder(tf.float32, [None, None], name='target_aux_ph') 37 | self.lr = tf.placeholder(tf.float64, []) 38 | self.use_negsampling = use_negsampling 39 | if use_negsampling: 40 | self.noclk_mid_batch_ph = tf.placeholder(tf.int32, [None, None, None], 41 | name='noclk_mid_batch_ph') # generate 3 item IDs from negative sampling. 42 | self.noclk_cat_batch_ph = tf.placeholder(tf.int32, [None, None, None], name='noclk_cat_batch_ph') 43 | 44 | # Embedding layer 45 | with tf.name_scope('Embedding_layer'): 46 | self.uid_embeddings_var = tf.get_variable("uid_embedding_var", [n_uid, EMBEDDING_DIM]) 47 | tf.summary.histogram('uid_embeddings_var', self.uid_embeddings_var) 48 | self.uid_batch_embedded = tf.nn.embedding_lookup(self.uid_embeddings_var, self.uid_batch_ph) 49 | 50 | self.mid_embeddings_var = tf.get_variable("mid_embedding_var", [n_mid, EMBEDDING_DIM]) 51 | tf.summary.histogram('mid_embeddings_var', self.mid_embeddings_var) 52 | self.mid_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.mid_batch_ph) 53 | self.mid_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, self.mid_his_batch_ph) 54 | 55 | self.mid_trigger_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, 56 | self.mid_trigger_his_batch_ph) 57 | self.mid_hard_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, 58 | self.mid_hard_his_batch_ph) 59 | 60 | if self.use_negsampling: 61 | self.noclk_mid_his_batch_embedded = tf.nn.embedding_lookup(self.mid_embeddings_var, 62 | self.noclk_mid_batch_ph) 63 | 64 | self.cat_embeddings_var = tf.get_variable("cat_embedding_var", [n_cat, EMBEDDING_DIM]) 65 | tf.summary.histogram('cat_embeddings_var', self.cat_embeddings_var) 66 | self.cat_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.cat_batch_ph) 67 | self.cat_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, self.cat_his_batch_ph) 68 | self.cat_trigger_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, 69 | self.cat_trigger_his_batch_ph) 70 | self.cat_hard_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, 71 | self.cat_hard_his_batch_ph) 72 | 73 | if self.use_negsampling: 74 | self.noclk_cat_his_batch_embedded = tf.nn.embedding_lookup(self.cat_embeddings_var, 75 | self.noclk_cat_batch_ph) 76 | 77 | self.item_eb = tf.concat([self.mid_batch_embedded, self.cat_batch_embedded], 1) 78 | self.item_his_eb = tf.concat([self.mid_his_batch_embedded, self.cat_his_batch_embedded], 2) 79 | self.item_his_eb_sum = tf.reduce_sum(self.item_his_eb, 1) 80 | 81 | self.item_his_hard_eb = tf.concat([self.mid_hard_his_batch_embedded, self.cat_hard_his_batch_embedded], 2) 82 | self.item_his_hard_eb_sum = tf.reduce_sum(self.item_his_hard_eb, 1) 83 | self.item_his_eb_mean = tf.reduce_mean(self.item_his_eb, 1) 84 | self.item_his_hard_eb_mean = tf.reduce_mean(self.item_his_hard_eb, 1) 85 | 86 | self.trigger_eb = tf.reduce_sum(tf.concat([self.mid_trigger_his_batch_embedded, self.cat_trigger_his_batch_embedded], 2), 1) 87 | 88 | self.time_embeddings_var = tf.get_variable("time_embedding_var", [86401, EMBEDDING_DIM]) 89 | self.item_his_time_eb = tf.nn.embedding_lookup(self.time_embeddings_var, self.time_stamp_his_batch_ph) 90 | 91 | self.item_his_time_hard_eb = tf.nn.embedding_lookup(self.time_embeddings_var, self.time_stamp_hard_his_batch_ph) 92 | 93 | 94 | self.seq_hard_len_var = tf.get_variable("seq_length_eb", [maxlen_hard+1, 4]) 95 | self.seq_hard_len_eb = tf.nn.embedding_lookup(self.seq_hard_len_var, self.seq_hard_len_ph) 96 | 97 | if self.use_negsampling: 98 | self.noclk_item_his_eb = tf.concat( 99 | [self.noclk_mid_his_batch_embedded[:, :, 0, :], self.noclk_cat_his_batch_embedded[:, :, 0, :]], 100 | -1) # 0 means only using the first negative item ID. 3 item IDs are inputed in the line 24. 101 | self.noclk_item_his_eb = tf.reshape(self.noclk_item_his_eb, 102 | [-1, tf.shape(self.noclk_mid_his_batch_embedded)[1], 103 | 36]) # cat embedding 18 concate item embedding 18. 104 | 105 | self.noclk_his_eb = tf.concat([self.noclk_mid_his_batch_embedded, self.noclk_cat_his_batch_embedded], -1) 106 | self.noclk_his_eb_sum_1 = tf.reduce_sum(self.noclk_his_eb, 2) 107 | self.noclk_his_eb_sum = tf.reduce_sum(self.noclk_his_eb_sum_1, 1) 108 | 109 | def build_fcn_net(self, inp, use_dice=False): 110 | bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1') 111 | dnn1 = tf.layers.dense(bn1, 200, activation=None, name='f1') 112 | if use_dice: 113 | dnn1 = dice(dnn1, name='dice_1') 114 | else: 115 | dnn1 = prelu(dnn1, 'prelu1') 116 | 117 | dnn2 = tf.layers.dense(dnn1, 80, activation=None, name='f2') 118 | if use_dice: 119 | dnn2 = dice(dnn2, name='dice_2') 120 | else: 121 | dnn2 = prelu(dnn2, 'prelu2') 122 | dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3') 123 | self.y_hat = tf.nn.softmax(dnn3) + 0.00000001 124 | 125 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 126 | with tf.control_dependencies(update_ops): 127 | with tf.name_scope('Metrics'): 128 | # Cross-entropy loss and optimizer initialization 129 | ctr_loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph) 130 | self.loss = ctr_loss 131 | if self.use_negsampling: 132 | self.loss += self.aux_loss 133 | tf.summary.scalar('loss', self.loss) 134 | self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss) 135 | 136 | # Accuracy metric 137 | self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32)) 138 | tf.summary.scalar('accuracy', self.accuracy) 139 | self.merged = tf.summary.merge_all() 140 | 141 | 142 | def build_fcn_net2(self, inp, use_dice=False): 143 | bn1 = tf.layers.batch_normalization(inputs=inp, name='bn1') 144 | dnn1 = tf.layers.dense(bn1, 300, activation=None, name='f1') 145 | if use_dice: 146 | dnn1 = dice(dnn1, name='dice_1') 147 | else: 148 | dnn1 = prelu(dnn1, 'prelu1') 149 | 150 | dnn2 = tf.layers.dense(dnn1, 120, activation=None, name='f2') 151 | if use_dice: 152 | dnn2 = dice(dnn2, name='dice_2') 153 | else: 154 | dnn2 = prelu(dnn2, 'prelu2') 155 | dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3') 156 | self.y_hat = tf.nn.softmax(dnn3) + 0.00000001 157 | 158 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 159 | with tf.control_dependencies(update_ops): 160 | with tf.name_scope('Metrics'): 161 | # Cross-entropy loss and optimizer initialization 162 | ctr_loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph) 163 | self.loss = ctr_loss 164 | if self.use_negsampling: 165 | self.loss += self.aux_loss 166 | tf.summary.scalar('loss', self.loss) 167 | self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss) 168 | 169 | # Accuracy metric 170 | self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32)) 171 | tf.summary.scalar('accuracy', self.accuracy) 172 | self.merged = tf.summary.merge_all() 173 | 174 | def build_dnn_net(self, inp, use_dice, is_training=True): 175 | bn1 = tf.layers.batch_normalization(inputs=inp, name='bn11', training=is_training) 176 | dnn = tf.layers.dense(bn1, 18, activation=None, name='IL_f1') 177 | if use_dice: 178 | dnn1 = dice(dnn, name='dice_dnn_1') 179 | else: 180 | dnn1 = prelu(dnn, 'prelu_dnn_1') 181 | # dnn2 = tf.layers.dense(dnn1, 18, activation=None, name='IL_f2') 182 | # bn2 = tf.layers.batch_normalization(inputs=dnn2, name='bn22', training=is_training) 183 | # if use_dice: 184 | # dnn3 = dice(bn2, name='dice_dnn_2') 185 | # else: 186 | # dnn3 = prelu(bn2, 'prelu_dnn_2') 187 | return dnn1 188 | 189 | def build_fcn_net_DIAN(self, inp, use_dice=False, name="layer"): 190 | bn1 = tf.layers.batch_normalization(inputs=inp, name=name+'bn1') 191 | dnn1 = tf.layers.dense(bn1, 200, activation=None, name=name+'f1') 192 | if use_dice: 193 | dnn1 = dice(dnn1, name=name+'dice_1') 194 | else: 195 | dnn1 = prelu(dnn1, name+'prelu1') 196 | 197 | dnn2 = tf.layers.dense(dnn1, 80, activation=None, name=name+'f2') 198 | if use_dice: 199 | dnn2 = dice(dnn2, name=name+'dice_2') 200 | else: 201 | dnn2 = prelu(dnn2, name+'prelu2') 202 | dnn3 = tf.layers.dense(dnn2, 2, activation=None, name=name + 'f3') 203 | y_hat = tf.nn.softmax(dnn3) + 0.00000001 204 | return y_hat 205 | 206 | def build_lr_net(self, inp): 207 | dnn = tf.layers.dense(inp, 2, activation=None, name='f3') 208 | self.y_hat = tf.nn.softmax(dnn) + 0.00000001 209 | 210 | update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS) 211 | with tf.control_dependencies(update_ops): 212 | with tf.name_scope('Metrics'): 213 | # Cross-entropy loss and optimizer initialization 214 | ctr_loss = - tf.reduce_mean(tf.log(self.y_hat) * self.target_ph) 215 | self.loss = ctr_loss 216 | if self.use_negsampling: 217 | self.loss += self.aux_loss 218 | tf.summary.scalar('loss', self.loss) 219 | self.optimizer = tf.train.AdamOptimizer(learning_rate=self.lr).minimize(self.loss) 220 | 221 | # Accuracy metric 222 | self.accuracy = tf.reduce_mean(tf.cast(tf.equal(tf.round(self.y_hat), self.target_ph), tf.float32)) 223 | tf.summary.scalar('accuracy', self.accuracy) 224 | self.merged = tf.summary.merge_all() 225 | 226 | def auxiliary_loss(self, h_states, click_seq, noclick_seq, mask, stag=None): 227 | mask = tf.cast(mask, tf.float32) 228 | click_input_ = tf.concat([h_states, click_seq], -1) 229 | noclick_input_ = tf.concat([h_states, noclick_seq], -1) 230 | click_prop_ = self.auxiliary_net(click_input_, stag=stag)[:, :, 0] 231 | noclick_prop_ = self.auxiliary_net(noclick_input_, stag=stag)[:, :, 0] 232 | click_loss_ = - tf.reshape(tf.log(click_prop_), [-1, tf.shape(click_seq)[1]]) * mask 233 | noclick_loss_ = - tf.reshape(tf.log(1.0 - noclick_prop_), [-1, tf.shape(noclick_seq)[1]]) * mask 234 | loss_ = tf.reduce_mean(click_loss_ + noclick_loss_) 235 | return loss_ 236 | 237 | def auxiliary_loss_v2(self, h_states, click_seq, noclick_seq, time_stamp_seq, mask, stag=None): 238 | mask = tf.cast(mask, tf.float32) 239 | click_input_ = tf.concat([h_states, click_seq, time_stamp_seq], -1) 240 | noclick_input_ = tf.concat([h_states, noclick_seq, time_stamp_seq], -1) 241 | click_prop_ = self.auxiliary_net(click_input_, stag=stag)[:, :, 0] 242 | noclick_prop_ = self.auxiliary_net(noclick_input_, stag=stag)[:, :, 0] 243 | click_loss_ = - tf.reshape(tf.log(click_prop_), [-1, tf.shape(click_seq)[1]]) * mask 244 | noclick_loss_ = - tf.reshape(tf.log(1.0 - noclick_prop_), [-1, tf.shape(noclick_seq)[1]]) * mask 245 | loss_ = tf.reduce_mean(click_loss_ + noclick_loss_) 246 | return loss_ 247 | 248 | def auxiliary_net(self, in_, stag='auxiliary_net'): 249 | bn1 = tf.layers.batch_normalization(inputs=in_, name='bn1' + stag, reuse=tf.AUTO_REUSE) 250 | dnn1 = tf.layers.dense(bn1, 100, activation=None, name='f1' + stag, reuse=tf.AUTO_REUSE) 251 | dnn1 = tf.nn.sigmoid(dnn1) 252 | dnn2 = tf.layers.dense(dnn1, 50, activation=None, name='f2' + stag, reuse=tf.AUTO_REUSE) 253 | dnn2 = tf.nn.sigmoid(dnn2) 254 | dnn3 = tf.layers.dense(dnn2, 2, activation=None, name='f3' + stag, reuse=tf.AUTO_REUSE) 255 | y_hat = tf.nn.softmax(dnn3) + 0.00000001 256 | return y_hat 257 | 258 | def train(self, sess, inps): 259 | if self.use_negsampling: 260 | loss, accuracy, aux_loss, _ = sess.run([self.loss, self.accuracy, self.aux_loss, self.optimizer], 261 | feed_dict={ 262 | self.uid_batch_ph: inps[0], 263 | self.mid_batch_ph: inps[1], 264 | self.cat_batch_ph: inps[2], 265 | self.mid_trigger_his_batch_ph: inps[3], 266 | self.cat_trigger_his_batch_ph: inps[4], 267 | self.mid_his_batch_ph: inps[5], 268 | self.cat_his_batch_ph: inps[6], 269 | self.time_stamp_his_batch_ph: inps[7], 270 | self.mask: inps[8], 271 | self.mid_hard_his_batch_ph: inps[9], 272 | self.cat_hard_his_batch_ph: inps[10], 273 | self.time_stamp_hard_his_batch_ph: inps[11], 274 | self.mask_hard: inps[12], 275 | self.target_ph: inps[13], 276 | self.seq_len_ph: inps[14], 277 | self.seq_hard_len_ph: inps[15], 278 | self.lr: inps[16], 279 | self.noclk_mid_batch_ph: inps[17], 280 | self.noclk_cat_batch_ph: inps[18], 281 | self.target_aux_ph: inps[19], 282 | }) 283 | return loss, accuracy, aux_loss 284 | else: 285 | loss, accuracy, _ = sess.run([self.loss, self.accuracy, self.optimizer], feed_dict={ 286 | self.uid_batch_ph: inps[0], 287 | self.mid_batch_ph: inps[1], 288 | self.cat_batch_ph: inps[2], 289 | self.mid_trigger_his_batch_ph: inps[3], 290 | self.cat_trigger_his_batch_ph: inps[4], 291 | self.mid_his_batch_ph: inps[5], 292 | self.cat_his_batch_ph: inps[6], 293 | self.time_stamp_his_batch_ph: inps[7], 294 | self.mask: inps[8], 295 | self.mid_hard_his_batch_ph: inps[9], 296 | self.cat_hard_his_batch_ph: inps[10], 297 | self.time_stamp_hard_his_batch_ph: inps[11], 298 | self.mask_hard: inps[12], 299 | self.target_ph: inps[13], 300 | self.seq_len_ph: inps[14], 301 | self.seq_hard_len_ph: inps[15], 302 | self.lr: inps[16], 303 | self.target_aux_ph: inps[19], 304 | }) 305 | # print('self.seq_len_ph',self.seq_len_ph) 306 | return loss, accuracy, 0 307 | 308 | def calculate(self, sess, inps): 309 | if self.use_negsampling: 310 | probs, loss, accuracy, aux_loss = sess.run([self.y_hat, self.loss, self.accuracy, self.aux_loss], 311 | feed_dict={ 312 | self.uid_batch_ph: inps[0], 313 | self.mid_batch_ph: inps[1], 314 | self.cat_batch_ph: inps[2], 315 | self.mid_trigger_his_batch_ph: inps[3], 316 | self.cat_trigger_his_batch_ph: inps[4], 317 | self.mid_his_batch_ph: inps[5], 318 | self.cat_his_batch_ph: inps[6], 319 | self.time_stamp_his_batch_ph: inps[7], 320 | self.mask: inps[8], 321 | self.mid_hard_his_batch_ph: inps[9], 322 | self.cat_hard_his_batch_ph: inps[10], 323 | self.time_stamp_hard_his_batch_ph: inps[11], 324 | self.mask_hard: inps[12], 325 | self.target_ph: inps[13], 326 | self.seq_len_ph: inps[14], 327 | self.seq_hard_len_ph: inps[15], 328 | self.noclk_mid_batch_ph: inps[16], 329 | self.noclk_cat_batch_ph: inps[17], 330 | self.target_aux_ph: inps[18], 331 | }) 332 | return probs, loss, accuracy, aux_loss 333 | else: 334 | probs, loss, accuracy = sess.run([self.y_hat, self.loss, self.accuracy], feed_dict={ 335 | self.uid_batch_ph: inps[0], 336 | self.mid_batch_ph: inps[1], 337 | self.cat_batch_ph: inps[2], 338 | self.mid_trigger_his_batch_ph: inps[3], 339 | self.cat_trigger_his_batch_ph: inps[4], 340 | self.mid_his_batch_ph: inps[5], 341 | self.cat_his_batch_ph: inps[6], 342 | self.time_stamp_his_batch_ph: inps[7], 343 | self.mask: inps[8], 344 | self.mid_hard_his_batch_ph: inps[9], 345 | self.cat_hard_his_batch_ph: inps[10], 346 | self.time_stamp_hard_his_batch_ph: inps[11], 347 | self.mask_hard: inps[12], 348 | self.target_ph: inps[13], 349 | self.seq_len_ph: inps[14], 350 | self.seq_hard_len_ph: inps[15], 351 | self.target_aux_ph: inps[18], 352 | }) 353 | return probs, loss, accuracy, 0 354 | 355 | def save(self, sess, path): 356 | saver = tf.train.Saver() 357 | saver.save(sess, save_path=path) 358 | 359 | def restore(self, sess, path): 360 | saver = tf.train.Saver() 361 | saver.restore(sess, save_path=path) 362 | print('model restored from %s' % path) 363 | -------------------------------------------------------------------------------- /script/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | EMBEDDING_DIM = 18 3 | HIDDEN_SIZE = 18 * 2 4 | ATTENTION_SIZE = 18 * 2 5 | POS_EMBEDDING_DIM = 2 6 | 7 | # train.py 8 | # model_type='TGIN' 9 | data_path='dataset' 10 | dataset='content_wise' 11 | tri_data='wnd3_alpha_01_theta_09_tri_num_10' 12 | n_tri=10 13 | batch_size=256 14 | maxlen=30 # 20 15 | maxlen_hard=20 16 | test_iter=100 17 | save_iter=100 18 | lr=1e-3 19 | 20 | # model.py 21 | n_neg = 5 22 | use_dice = True 23 | use_negsampling = True 24 | 25 | # tgin.py 26 | single_tri_agg_flag = 'reduce_mean' 27 | # multi_tri_agg_flag = 'weighted_sum' 28 | multi_tri_agg_flag = 'mhsa' 29 | -------------------------------------------------------------------------------- /script/train.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # @Time : 2022/12/30 16:29 4 | 5 | import numpy 6 | import time 7 | import os 8 | import random 9 | import sys 10 | 11 | from dataset_process.data_iterator import DataIterator 12 | from models.DIN import DIN 13 | from models.DIN_V2_Gru_Gru_att import DIN_V2_Gru_Gru_att 14 | from models.DIN_V2_Gru_QA_attGru import DIN_V2_Gru_QA_attGru 15 | from models.DIN_V2_Gru_Vec_attGru import DIN_V2_Gru_Vec_attGru 16 | from models.DIN_V2_Gru_Vec_attGru_Neg import DIN_V2_Gru_Vec_attGru_Neg 17 | from models.DIN_V2_Gru_att_Gru import DIN_V2_Gru_att_Gru 18 | from models.DNN import DNN 19 | from models.DNN_Multi_Head import DNN_Multi_Head 20 | from models.PNN import PNN 21 | from models.DEI2N import DEI2N 22 | from models.DEI2N_NO_IL import DEI2N_NO_IL 23 | from models.DEI2N_NO_UIM import DEI2N_NO_UIM 24 | from models.DEI2N_NO_TIM import DEI2N_NO_TIM 25 | from models.DIHN import DIHN 26 | from models.DEI2N_MHTA import DEI2N_MHTA 27 | from models.WideDeep import WideDeep 28 | from models.DIAN import DIAN 29 | from models.LR import LR 30 | from dataset_process.prepare_data import prepare_data, prepare_data_tgin 31 | from common.utils import * 32 | from settings import * 33 | 34 | best_auc = 0.0 35 | 36 | 37 | def model_selection(model_type, n_uid, n_mid, n_cat): 38 | print("Model_type: {}".format(model_type)) 39 | 40 | if model_type == 'DNN': 41 | model = DNN(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 42 | elif model_type == 'PNN': 43 | model = PNN(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 44 | elif model_type == 'Wide': 45 | model = WideDeep(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 46 | elif model_type == 'DIN': 47 | model = DIN(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 48 | elif model_type == 'DIN-V2-gru-att-gru': 49 | model = DIN_V2_Gru_att_Gru(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 50 | elif model_type == 'DIN-V2-gru-gru-att': 51 | model = DIN_V2_Gru_Gru_att(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 52 | elif model_type == 'DIN-V2-gru-qa-attGru': 53 | model = DIN_V2_Gru_QA_attGru(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 54 | elif model_type == 'DIN-V2-gru-vec-attGru': 55 | model = DIN_V2_Gru_Vec_attGru(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 56 | elif model_type == 'DIEN': 57 | model = DIN_V2_Gru_Vec_attGru_Neg(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 58 | elif model_type == 'DMIN': 59 | model = DNN_Multi_Head(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 60 | elif model_type == 'DEI2N': 61 | model = DEI2N(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 62 | elif model_type == 'DEI2N_NO_IL': 63 | model = DEI2N_NO_IL(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 64 | elif model_type == 'DEI2N_NO_UIM': 65 | model = DEI2N_NO_UIM(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 66 | elif model_type == 'DEI2N_NO_TIM': 67 | model = DEI2N_NO_TIM(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 68 | elif model_type == 'DIHN': 69 | model = DIHN(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 70 | elif model_type == 'DIAN': 71 | model = DIAN(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 72 | elif model_type == 'LR': 73 | model = LR(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 74 | elif model_type == 'DEI2N_MHTA': 75 | model = DEI2N_MHTA(n_uid, n_mid, n_cat, EMBEDDING_DIM, HIDDEN_SIZE, ATTENTION_SIZE) 76 | else: 77 | print("Invalid model_type : %s", model_type) 78 | 79 | return model 80 | 81 | 82 | def eval(sess, test_data, model, model_path, maxlen=30, maxlen_hard=20, model_type='DNN'): 83 | loss_sum = 0. 84 | accuracy_sum = 0. 85 | aux_loss_sum = 0. 86 | nums = 0 87 | stored_arr = [] 88 | # for src, tri0_src, tri1_src, tgt in test_data: 89 | for one_pass_data in test_data: 90 | nums += 1 91 | src, tgt, tgt_aux = one_pass_data 92 | uids, mids, cats, time_stamp, trigger_mid, trigger_cat, mid_his, cat_his, time_stamp_his, mid_mask, mid_hard_his, cat_hard_his, time_stamp_hard_his, mid_hard_mask, target, sl, sl_hard, noclk_mids, noclk_cats, target_aux = prepare_data(src, 93 | tgt, 94 | tgt_aux, 95 | maxlen, 96 | maxlen_hard, 97 | return_neg=True) 98 | prob, loss, acc, aux_loss = model.calculate(sess, [uids, mids, cats, trigger_mid, trigger_cat, mid_his, cat_his, time_stamp_his, mid_mask, mid_hard_his, cat_hard_his, time_stamp_hard_his, mid_hard_mask, target, sl, sl_hard, 99 | noclk_mids, noclk_cats, target_aux]) 100 | loss_sum += loss 101 | aux_loss_sum = aux_loss 102 | accuracy_sum += acc 103 | prob_1 = prob[:, 0].tolist() 104 | target_1 = target[:, 0].tolist() 105 | for p, t in zip(prob_1, target_1): 106 | stored_arr.append([p, t]) 107 | test_auc = calc_auc(stored_arr) 108 | accuracy_sum = accuracy_sum / nums 109 | loss_sum = loss_sum / nums 110 | aux_loss_sum / nums 111 | global best_auc 112 | if best_auc < test_auc: 113 | best_auc = test_auc 114 | model.save(sess, model_path) 115 | return test_auc, loss_sum, accuracy_sum, aux_loss_sum 116 | 117 | 118 | def train(seed=1234, model_type='DNN'): 119 | if dataset == "electronics": 120 | train_file = os.path.join(data_path, dataset, "local_train_splitByUser") 121 | test_file = os.path.join(data_path, dataset, "local_test_splitByUser") 122 | valid_file = os.path.join(data_path, dataset, "local_valid_splitByUser") 123 | uid_voc = os.path.join(data_path, dataset, "uid_voc.pkl") 124 | mid_voc = os.path.join(data_path, dataset, "mid_voc.pkl") 125 | cat_voc = os.path.join(data_path, dataset, "cat_voc.pkl") 126 | item_info = os.path.join(data_path, dataset, "item-info") 127 | reviews_info = os.path.join(data_path, dataset, "reviews-info") 128 | elif dataset == "content_wise": 129 | train_file = os.path.join(data_path, dataset, "local_train_splitByUser_cw") 130 | test_file = os.path.join(data_path, dataset, "local_test_splitByUser_cw") 131 | valid_file = os.path.join(data_path, dataset, "local_test_splitByUser_cw") 132 | uid_voc = os.path.join(data_path, dataset, "uid_voc_cw.pkl") 133 | mid_voc = os.path.join(data_path, dataset, "itemid_voc_cw.pkl") 134 | cat_voc = os.path.join(data_path, dataset, "series_voc_cw.pkl") 135 | item_info = os.path.join(data_path, dataset, "item-info_cw") 136 | reviews_info = os.path.join(data_path, dataset, "user_log_info") 137 | else: 138 | print("Please provide valid dataset!") 139 | 140 | model_path = "dnn_save_path/ckpt_noshuff" + model_type + str(seed) 141 | best_model_path = "dnn_best_model/ckpt_noshuff" + model_type + str(seed) 142 | gpu_options = tf.GPUOptions(allow_growth=True) 143 | with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess: 144 | t1 = time.time() 145 | train_data = DataIterator(train_file, uid_voc, mid_voc, cat_voc, item_info, reviews_info, 146 | batch_size, maxlen, maxlen_hard, shuffle_each_epoch=False) 147 | test_data = DataIterator(test_file, uid_voc, mid_voc, cat_voc, item_info, reviews_info, 148 | batch_size, maxlen, maxlen_hard) 149 | valid_data = DataIterator(valid_file, uid_voc, mid_voc, cat_voc, item_info, reviews_info, 150 | batch_size, maxlen, maxlen_hard) 151 | print('# Load data time (s):', round(time.time() - t1, 2)) 152 | 153 | n_uid, n_mid, n_cat = train_data.get_n() 154 | print('n_uid: {}, n_mid: {}, n_cat: {}'.format(n_uid, n_mid, n_cat)) 155 | t1 = time.time() 156 | model = model_selection(model_type, n_uid, n_mid, n_cat) 157 | 158 | sess.run(tf.global_variables_initializer()) 159 | sess.run(tf.local_variables_initializer()) 160 | sys.stdout.flush() 161 | print('# Contruct model time (s):', round(time.time() - t1, 2)) 162 | 163 | t1 = time.time() 164 | print('test_auc: %.4f -- test_loss: %.4f -- test_accuracy: %.4f -- test_aux_loss: %.4f' % eval( 165 | sess, test_data, model, best_model_path, maxlen, maxlen_hard, model_type)) 166 | print('# Eval model time (s):', round(time.time() - t1, 2)) 167 | sys.stdout.flush() 168 | 169 | start_time = time.time() 170 | iter = 0 171 | lr = 0.001 172 | print("lr: {}".format(lr)) 173 | for itr in range(2): 174 | loss_sum = 0.0 175 | accuracy_sum = 0. 176 | aux_loss_sum = 0. 177 | # for src, tri0_src, tri1_src, tgt in train_data: 178 | for one_pass_data in train_data: 179 | src, tgt, tgt_aux = one_pass_data 180 | uids, mids, cats, time_stamp, trigger_mid, trigger_cat, mid_his, cat_his, time_stamp_his, mid_mask, \ 181 | mid_hard_his, cat_hard_his, time_stamp_hard_his, mid_hard_mask, target, sl, sl_hard, noclk_mids, noclk_cats, target_aux = prepare_data(src, tgt, tgt_aux, maxlen, maxlen_hard, return_neg=True) 182 | 183 | loss, acc, aux_loss = model.train(sess, 184 | [uids, mids, cats, trigger_mid, trigger_cat, mid_his, cat_his, time_stamp_his, mid_mask, mid_hard_his, cat_hard_his, time_stamp_hard_his, mid_hard_mask, target, sl, sl_hard, lr, 185 | noclk_mids, noclk_cats, target_aux]) 186 | loss_sum += loss 187 | accuracy_sum += acc 188 | aux_loss_sum += aux_loss 189 | iter += 1 190 | 191 | # Print & Save 192 | sys.stdout.flush() 193 | if (iter % test_iter) == 0 and itr>0 and iter>15100: 194 | print('[Time] ', time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) 195 | print('Best_auc:', best_auc) 196 | print('itr: %d --> iter: %d --> train_loss: %.4f -- train_accuracy: %.4f -- tran_aux_loss: %.4f' % \ 197 | (itr, iter, loss_sum / test_iter, accuracy_sum / test_iter, aux_loss_sum / test_iter)) 198 | print('test_auc: %.4f -- test_loss: %.4f -- test_accuracy: %.4f -- test_aux_loss: %.4f' % eval( 199 | sess, valid_data, model, best_model_path, maxlen, maxlen_hard, model_type)) 200 | loss_sum = 0.0 201 | accuracy_sum = 0.0 202 | aux_loss_sum = 0.0 203 | # if (iter % save_iter) == 0: 204 | # print('save model iter: %d' % (iter)) 205 | # model.save(sess, model_path + "--" + str(iter)) 206 | 207 | 208 | def test(seed=1234, model_type='DNN'): 209 | if dataset == "electronics": 210 | train_file = os.path.join(data_path, dataset, "local_train_splitByUser") 211 | test_file = os.path.join(data_path, dataset, "local_test_splitByUser") 212 | valid_file = os.path.join(data_path, dataset, "local_valid_splitByUser") 213 | uid_voc = os.path.join(data_path, dataset, "uid_voc.pkl") 214 | mid_voc = os.path.join(data_path, dataset, "mid_voc.pkl") 215 | cat_voc = os.path.join(data_path, dataset, "cat_voc.pkl") 216 | item_info = os.path.join(data_path, dataset, "item-info") 217 | reviews_info = os.path.join(data_path, dataset, "reviews-info") 218 | elif dataset == "content_wise": 219 | train_file = os.path.join(data_path, dataset, "local_train_splitByUser_cw") 220 | test_file = os.path.join(data_path, dataset, "local_test_splitByUser_cw") 221 | valid_file = os.path.join(data_path, dataset, "local_test_splitByUser_cw") 222 | uid_voc = os.path.join(data_path, dataset, "uid_voc_cw.pkl") 223 | mid_voc = os.path.join(data_path, dataset, "itemid_voc_cw.pkl") 224 | cat_voc = os.path.join(data_path, dataset, "series_voc_cw.pkl") 225 | item_info = os.path.join(data_path, dataset, "item-info_cw") 226 | reviews_info = os.path.join(data_path, dataset, "user_log_info") 227 | else: 228 | print("Please provide valid dataset!") 229 | 230 | model_path = "dnn_best_model/ckpt_noshuff" + model_type + str(seed) 231 | gpu_options = tf.GPUOptions(allow_growth=True) 232 | with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess: 233 | t1 = time.time() 234 | train_data = DataIterator(train_file, uid_voc, mid_voc, cat_voc, item_info, reviews_info, 235 | batch_size, maxlen, maxlen_hard, shuffle_each_epoch=False) 236 | test_data = DataIterator(test_file, uid_voc, mid_voc, cat_voc, item_info, reviews_info, 237 | batch_size, maxlen, maxlen_hard) 238 | print('# Load data time (s):', round(time.time() - t1, 2)) 239 | n_uid, n_mid, n_cat = train_data.get_n() 240 | 241 | model = model_selection(model_type, n_uid, n_mid, n_cat) 242 | model.restore(sess, model_path) 243 | print('test_auc: %.4f -- test_loss: %.4f -- test_accuracy: %.4f -- test_aux_loss: %.4f' % eval( 244 | sess, test_data, model, model_path, maxlen, maxlen_hard, model_type)) 245 | 246 | 247 | if __name__ == '__main__': 248 | if len(sys.argv) == 5: 249 | SEED = int(sys.argv[4]) 250 | else: 251 | SEED = 1234 252 | # tf.set_random_seed(SEED) 253 | tf.compat.v1.set_random_seed(SEED) 254 | numpy.random.seed(SEED) 255 | random.seed(SEED) 256 | os.environ["CUDA_VISIBLE_DEVICES"] = sys.argv[3] 257 | if sys.argv[1] == 'train': 258 | train(model_type=sys.argv[2], seed=SEED) 259 | elif sys.argv[1] == 'test': 260 | test(model_type=sys.argv[2], seed=SEED) 261 | else: 262 | print('do nothing...') 263 | --------------------------------------------------------------------------------