From 2029c82c4b5bde78fc30af64f0e3da70b78023dd Mon Sep 17 00:00:00 2001 From: toyoululu <549616079@qq.com> Date: Sun, 5 Dec 2021 19:50:47 +0800 Subject: [PATCH] Update DIEN.py --- .../sparrowrecsys/offline/tensorflow/DIEN.py | 241 +++++++++--------- 1 file changed, 119 insertions(+), 122 deletions(-) diff --git a/TFRecModel/src/com/sparrowrecsys/offline/tensorflow/DIEN.py b/TFRecModel/src/com/sparrowrecsys/offline/tensorflow/DIEN.py index 2f601f0d..ad90862e 100644 --- a/TFRecModel/src/com/sparrowrecsys/offline/tensorflow/DIEN.py +++ b/TFRecModel/src/com/sparrowrecsys/offline/tensorflow/DIEN.py @@ -26,33 +26,31 @@ "/resources/webroot/sampledata/testSamples.csv") - -def get_dataset_with_negtive_movie(path,batch_size,seed_num): +def get_dataset_with_negtive_movie(path, batch_size, seed_num): tmp_df = pd.read_csv(path) - tmp_df.fillna(0,inplace=True) + tmp_df.fillna(0, inplace=True) random.seed(seed_num) - negtive_movie_df=tmp_df.loc[:,'userRatedMovie2':'userRatedMovie5'].applymap( lambda x: random.sample( set(range(0, 1001))-set([int(x)]), 1)[0] ) - negtive_movie_df.columns = ['negtive_userRatedMovie2','negtive_userRatedMovie3','negtive_userRatedMovie4','negtive_userRatedMovie5'] - tmp_df=pd.concat([tmp_df,negtive_movie_df],axis=1) + negtive_movie_df = tmp_df.loc[:, 'userRatedMovie2':'userRatedMovie5'].applymap( + lambda x: random.sample(set(range(0, 1001)) - set([int(x)]), 1)[0]) + negtive_movie_df.columns = ['negtive_userRatedMovie2', 'negtive_userRatedMovie3', 'negtive_userRatedMovie4', + 'negtive_userRatedMovie5'] + tmp_df = pd.concat([tmp_df, negtive_movie_df], axis=1) for i in tmp_df.select_dtypes('O').columns: tmp_df[i] = tmp_df[i].astype('str') - - if tf.__version__<'2.3.0': - tmp_df = tmp_df.sample( n= batch_size*( len(tmp_df)//batch_size ) ,random_state=seed_num ) - - - dataset = tf.data.Dataset.from_tensor_slices( ( dict(tmp_df)) ) + + dataset = tf.data.Dataset.from_tensor_slices((dict(tmp_df))) dataset = dataset.batch(batch_size) return dataset -train_dataset = get_dataset_with_negtive_movie(training_samples_file_path,12,seed_num=2020) -test_dataset = get_dataset_with_negtive_movie(test_samples_file_path,12,seed_num=2021) + +batch_size = 12 +train_dataset = get_dataset_with_negtive_movie(training_samples_file_path, batch_size, seed_num=2020) +test_dataset = get_dataset_with_negtive_movie(test_samples_file_path, batch_size, seed_num=2021) # Config RECENT_MOVIES = 5 # userRatedMovie{1-5} EMBEDDING_SIZE = 10 - # define input for keras model inputs = { 'movieAvgRating': tf.keras.layers.Input(name='movieAvgRating', shape=(), dtype='float32'), @@ -79,15 +77,14 @@ def get_dataset_with_negtive_movie(path,batch_size,seed_num): 'movieGenre1': tf.keras.layers.Input(name='movieGenre1', shape=(), dtype='string'), 'movieGenre2': tf.keras.layers.Input(name='movieGenre2', shape=(), dtype='string'), 'movieGenre3': tf.keras.layers.Input(name='movieGenre3', shape=(), dtype='string'), - + 'negtive_userRatedMovie2': tf.keras.layers.Input(name='negtive_userRatedMovie2', shape=(), dtype='int32'), 'negtive_userRatedMovie3': tf.keras.layers.Input(name='negtive_userRatedMovie3', shape=(), dtype='int32'), 'negtive_userRatedMovie4': tf.keras.layers.Input(name='negtive_userRatedMovie4', shape=(), dtype='int32'), - 'negtive_userRatedMovie5': tf.keras.layers.Input(name='negtive_userRatedMovie5', shape=(), dtype='int32'), - - 'label':tf.keras.layers.Input(name='label', shape=(), dtype='int32') -} + 'negtive_userRatedMovie5': tf.keras.layers.Input(name='negtive_userRatedMovie5', shape=(), dtype='int32'), + 'label': tf.keras.layers.Input(name='label', shape=(), dtype='int32') +} # user id embedding feature user_col = tf.feature_column.categorical_column_with_identity(key='userId', num_buckets=30001) @@ -106,9 +103,7 @@ def get_dataset_with_negtive_movie(path,batch_size,seed_num): vocabulary_list=genre_vocab) item_genre_emb_col = tf.feature_column.embedding_column(item_genre_col, EMBEDDING_SIZE) - - -candidate_movie_col = [ tf.feature_column.numeric_column(key='movieId', default_value=0), ] +candidate_movie_col = [tf.feature_column.numeric_column(key='movieId', default_value=0), ] # user behaviors recent_rate_col = [ @@ -119,7 +114,6 @@ def get_dataset_with_negtive_movie(path,batch_size,seed_num): tf.feature_column.numeric_column(key='userRatedMovie5', default_value=0), ] - negtive_movie_col = [ tf.feature_column.numeric_column(key='negtive_userRatedMovie2', default_value=0), tf.feature_column.numeric_column(key='negtive_userRatedMovie3', default_value=0), @@ -127,8 +121,6 @@ def get_dataset_with_negtive_movie(path,batch_size,seed_num): tf.feature_column.numeric_column(key='negtive_userRatedMovie5', default_value=0), ] - - # user profile user_profile = [ user_emb_col, @@ -147,8 +139,7 @@ def get_dataset_with_negtive_movie(path,batch_size,seed_num): tf.feature_column.numeric_column('movieRatingStddev'), ] -label =[ tf.feature_column.numeric_column(key='label', default_value=0), ] - +label = [tf.feature_column.numeric_column(key='label', default_value=0), ] candidate_layer = tf.keras.layers.DenseFeatures(candidate_movie_col)(inputs) user_behaviors_layer = tf.keras.layers.DenseFeatures(recent_rate_col)(inputs) @@ -158,98 +149,106 @@ def get_dataset_with_negtive_movie(path,batch_size,seed_num): y_true = tf.keras.layers.DenseFeatures(label)(inputs) # Activation Unit -movie_emb_layer = tf.keras.layers.Embedding(input_dim=1001,output_dim=EMBEDDING_SIZE,mask_zero=True)# mask zero +movie_emb_layer = tf.keras.layers.Embedding(input_dim=1001, output_dim=EMBEDDING_SIZE, mask_zero=True) # mask zero + +user_behaviors_emb_layer = movie_emb_layer(user_behaviors_layer) +candidate_emb_layer = movie_emb_layer(candidate_layer) +negtive_movie_emb_layer = movie_emb_layer(negtive_movie_layer) -user_behaviors_emb_layer = movie_emb_layer(user_behaviors_layer) -candidate_emb_layer = movie_emb_layer(candidate_layer) -negtive_movie_emb_layer = movie_emb_layer(negtive_movie_layer) +candidate_emb_layer = tf.squeeze(candidate_emb_layer, axis=1) -candidate_emb_layer = tf.squeeze(candidate_emb_layer,axis=1) +user_behaviors_hidden_state = tf.keras.layers.GRU(EMBEDDING_SIZE, return_sequences=True)(user_behaviors_emb_layer) -user_behaviors_hidden_state=tf.keras.layers.GRU(EMBEDDING_SIZE, return_sequences=True)(user_behaviors_emb_layer) class attention(tf.keras.layers.Layer): def __init__(self, embedding_size=EMBEDDING_SIZE, time_length=5, ): super().__init__() - self.time_length = time_length + self.time_length = time_length self.embedding_size = embedding_size self.RepeatVector_time = tf.keras.layers.RepeatVector(self.time_length) - self.RepeatVector_emb = tf.keras.layers.RepeatVector(self.embedding_size) - self.Multiply = tf.keras.layers.Multiply() - self.Dense32 = tf.keras.layers.Dense(32,activation='sigmoid') - self.Dense1 = tf.keras.layers.Dense(1,activation='sigmoid') - self.Flatten = tf.keras.layers.Flatten() - self.Permute = tf.keras.layers.Permute((2, 1)) - + self.RepeatVector_emb = tf.keras.layers.RepeatVector(self.embedding_size) + self.Multiply = tf.keras.layers.Multiply() + self.Dense32 = tf.keras.layers.Dense(32, activation='sigmoid') + self.Dense1 = tf.keras.layers.Dense(1, activation='sigmoid') + self.Flatten = tf.keras.layers.Flatten() + self.Permute = tf.keras.layers.Permute((2, 1)) + def build(self, input_shape): pass - + def call(self, inputs): - candidate_inputs,gru_hidden_state=inputs + candidate_inputs, gru_hidden_state = inputs repeated_candidate_layer = self.RepeatVector_time(candidate_inputs) - activation_product_layer = self.Multiply([gru_hidden_state,repeated_candidate_layer]) + activation_product_layer = self.Multiply([gru_hidden_state, repeated_candidate_layer]) activation_unit = self.Dense32(activation_product_layer) - activation_unit = self.Dense1(activation_unit) - Repeat_attention_s=tf.squeeze(activation_unit,axis=2) - Repeat_attention_s=self.RepeatVector_emb(Repeat_attention_s) - Repeat_attention_s=self.Permute(Repeat_attention_s) + activation_unit = self.Dense1(activation_unit) + Repeat_attention_s = tf.squeeze(activation_unit, axis=2) + Repeat_attention_s = self.RepeatVector_emb(Repeat_attention_s) + Repeat_attention_s = self.Permute(Repeat_attention_s) return Repeat_attention_s -attention_score=attention()( [candidate_emb_layer, user_behaviors_hidden_state]) +attention_score = attention()([candidate_emb_layer, user_behaviors_hidden_state]) class GRU_gate_parameter(tf.keras.layers.Layer): - def __init__(self,embedding_size=EMBEDDING_SIZE): + def __init__(self, embedding_size=EMBEDDING_SIZE): super().__init__() - self.embedding_size = embedding_size - self.Multiply = tf.keras.layers.Multiply() - self.Dense_sigmoid = tf.keras.layers.Dense( self.embedding_size,activation='sigmoid' ) - self.Dense_tanh =tf.keras.layers.Dense( self.embedding_size,activation='tanh' ) - + self.embedding_size = embedding_size + self.Multiply = tf.keras.layers.Multiply() + # self.Dense_sigmoid = tf.keras.layers.Dense( self.embedding_size,activation='sigmoid' ) #这里只作为一个激活函数,应该直接用tf.sigmoid,不应该引入线性层的参数 + # self.Dense_tanh =tf.keras.layers.Dense( self.embedding_size,activation='tanh' ) #这里只作为一个激活函数,应该直接用tf.tanh,不应该引入线性层的参数 + def build(self, input_shape): - self.input_w = tf.keras.layers.Dense(self.embedding_size,activation=None,use_bias=True) - self.hidden_w = tf.keras.layers.Dense(self.embedding_size,activation=None,use_bias=False) + self.input_w = tf.keras.layers.Dense(self.embedding_size, activation=None, use_bias=True) + self.hidden_w = tf.keras.layers.Dense(self.embedding_size, activation=None, use_bias=False) + + def call(self, inputs, Z_t_inputs=None): + gru_inputs, hidden_inputs = inputs + if Z_t_inputs == None: + return tf.sigmoid(self.input_w(gru_inputs) + self.hidden_w(hidden_inputs)) + else: + # return self.Dense_tanh( self.input_w(gru_inputs) + self.hidden_w(self.Multiply([hidden_inputs,Z_t_inputs]) )) + return tf.tanh( + self.input_w(gru_inputs) + self.Multiply([Z_t_inputs, self.hidden_w(hidden_inputs)])) # 他之前的公式打错了 - def call(self, inputs,Z_t_inputs=None ): - gru_inputs,hidden_inputs = inputs - if Z_t_inputs==None: - return self.Dense_sigmoid( self.input_w(gru_inputs) + self.hidden_w(hidden_inputs) ) - else: - return self.Dense_tanh( self.input_w(gru_inputs) + self.hidden_w(self.Multiply([hidden_inputs,Z_t_inputs]) )) - class AUGRU(tf.keras.layers.Layer): - def __init__(self,embedding_size=EMBEDDING_SIZE, time_length=5): + def __init__(self, embedding_size=EMBEDDING_SIZE, time_length=5): super().__init__() self.time_length = time_length - self.embedding_size = embedding_size - self.Multiply = tf.keras.layers.Multiply() - self.Add=tf.keras.layers.Add() - + self.embedding_size = embedding_size + self.Multiply = tf.keras.layers.Multiply() + self.Add = tf.keras.layers.Add() + # self.RepeatVector = tf.keras.layers.RepeatVector(batch_size) + def build(self, input_shape): self.R_t = GRU_gate_parameter() - self.Z_t = GRU_gate_parameter() - self.H_t_next = GRU_gate_parameter() + self.Z_t = GRU_gate_parameter() + self.H_t_next = GRU_gate_parameter() - def call(self, inputs ): - gru_hidden_state_inputs,attention_s=inputs + def call(self, inputs): + gru_hidden_state_inputs, attention_s = inputs initializer = tf.keras.initializers.GlorotUniform() - AUGRU_hidden_state = tf.reshape(initializer(shape=(1,self.embedding_size )),shape=(-1,self.embedding_size )) - for t in range(self.time_length): - r_t= self.R_t( [gru_hidden_state_inputs[:,t,:], AUGRU_hidden_state] ) - z_t= self.Z_t( [gru_hidden_state_inputs[:,t,:], AUGRU_hidden_state] ) - h_t_next= self.H_t_next( [gru_hidden_state_inputs[:,t,:], AUGRU_hidden_state] , z_t ) - Rt_attention =self.Multiply([attention_s[:,t,:] , r_t]) - - AUGRU_hidden_state = self.Add( [self.Multiply([(1-Rt_attention),AUGRU_hidden_state ] ), self.Multiply([Rt_attention ,h_t_next ] )]) - + # 原来的代码AUGRU_hidden_state一开始维数:(1,embedding_size),后面就变成(batch,embedding_size) + AUGRU_hidden_state = initializer(shape=(1, self.embedding_size)) + # AUGRU_hidden_state=self.RepeatVector(AUGRU_hidden_state) + # AUGRU_hidden_state=tf.reshape(AUGRU_hidden_state,[-1,self.embedding_size]) #所以直接变成(batch,embedding_size) + + for t in range(self.time_length): + r_t = self.R_t([gru_hidden_state_inputs[:, t, :], AUGRU_hidden_state]) + z_t = self.Z_t([gru_hidden_state_inputs[:, t, :], AUGRU_hidden_state]) + h_t_next = self.H_t_next([gru_hidden_state_inputs[:, t, :], AUGRU_hidden_state], z_t) + Rt_attention = self.Multiply([attention_s[:, t, :], r_t]) + AUGRU_hidden_state = self.Add( + [self.Multiply([(1 - Rt_attention), AUGRU_hidden_state]), self.Multiply([Rt_attention, h_t_next])]) return AUGRU_hidden_state -augru_emb=AUGRU()( [ user_behaviors_hidden_state ,attention_score ] ) -concat_layer = tf.keras.layers.concatenate([ augru_emb, candidate_emb_layer,user_profile_layer,context_features_layer]) +augru_emb = AUGRU()([user_behaviors_hidden_state, attention_score]) + +concat_layer = tf.keras.layers.concatenate([augru_emb, candidate_emb_layer, user_profile_layer, context_features_layer]) output_layer = tf.keras.layers.Dense(128)(concat_layer) output_layer = tf.keras.layers.PReLU()(output_layer) @@ -259,58 +258,56 @@ def call(self, inputs ): class auxiliary_loss_layer(tf.keras.layers.Layer): - def __init__(self,time_length=5 ): + def __init__(self, time_length=5): super().__init__() - self.time_len = time_length-1 - self.Dense_sigmoid_positive32 = tf.keras.layers.Dense(32,activation='sigmoid') - self.Dense_sigmoid_positive1 = tf.keras.layers.Dense(1,activation='sigmoid') - self.Dense_sigmoid_negitive32 = tf.keras.layers.Dense(32,activation='sigmoid') - self.Dense_sigmoid_negitive1 = tf.keras.layers.Dense(1,activation='sigmoid') - self.Dot = tf.keras.layers.Dot(axes=(1, 1)) - self.auc =tf.keras.metrics.AUC() - + self.time_len = time_length - 1 + self.Multiply = tf.keras.layers.Multiply() + self.auc = tf.keras.metrics.AUC() + def build(self, input_shape): pass - - def call(self, inputs,alpha=0.5): - negtive_movie_t1,postive_movie_t0,movie_hidden_state,y_true,y_pred=inputs - #auxiliary_loss_values = [] - positive_concat_layer=tf.keras.layers.concatenate([ movie_hidden_state[:,0:4,:], postive_movie_t0[:,1:5,:] ]) - positive_concat_layer=self.Dense_sigmoid_positive32( positive_concat_layer ) - positive_loss = self.Dense_sigmoid_positive1(positive_concat_layer) - - negtive_concat_layer=tf.keras.layers.concatenate([ movie_hidden_state[:,0:4,:], negtive_movie_t1[:,:,:] ]) - negtive_concat_layer=self.Dense_sigmoid_negitive32( negtive_concat_layer ) - negtive_loss = self.Dense_sigmoid_negitive1(negtive_concat_layer) - auxiliary_loss_values = positive_loss + negtive_loss - - final_loss = tf.keras.losses.binary_crossentropy( y_true, y_pred )-alpha* tf.reduce_mean( tf.reduce_sum( auxiliary_loss_values,axis=1 )) + + def call(self, inputs, alpha=0.5): + negtive_movie_t1, postive_movie_t0, movie_hidden_state, y_true, y_pred = inputs + + positive_loss = 1 - tf.sigmoid( + tf.reduce_sum(self.Multiply([movie_hidden_state[:, 0:4, :], postive_movie_t0[:, 1:5, :]]), + axis=-1)) # dim:(batchsize,4) + negtive_loss = tf.sigmoid( + tf.reduce_sum(self.Multiply([movie_hidden_state[:, 0:4, :], negtive_movie_t1[:, :, :]]), + axis=-1)) # dim:(batchsize,4) + + auxiliary_loss_values = positive_loss + negtive_loss # 原来代码中positive_loss,negtive_loss 处理的方式一样,没有体现出负采样,向量越接近点积的结果越大,sigmoid越大,1-sigmoid作为损失值越小,负采样相反 + + final_loss = tf.keras.losses.binary_crossentropy(y_true, y_pred) + alpha * tf.reduce_mean( + tf.reduce_sum(auxiliary_loss_values, axis=1)) # alpha前面应该是+,之前的损失函数值可能为负 self.add_loss(final_loss, inputs=True) - self.auc.update_state(y_true, y_pred ) - self.add_metric(self.auc.result(), aggregation="mean", name="auc_value") - - return final_loss + self.auc.update_state(y_true, y_pred) + self.add_metric(self.auc.result(), aggregation="mean", name="auc_value") + + return final_loss -auxiliary_loss_value=auxiliary_loss_layer()( [ negtive_movie_emb_layer,user_behaviors_emb_layer,user_behaviors_hidden_state,y_true,y_pred] ) -model = tf.keras.Model(inputs=inputs, outputs=[y_pred,auxiliary_loss_value]) +auxiliary_loss_value = auxiliary_loss_layer()( + [negtive_movie_emb_layer, user_behaviors_emb_layer, user_behaviors_hidden_state, y_true, y_pred]) + +model = tf.keras.Model(inputs=inputs, outputs=[y_pred, auxiliary_loss_value]) model.compile(optimizer="adam") # train the model -model.fit(train_dataset, epochs=5) +with tf.device( + '/cpu:0'): # 如果用GPU可能会报错 Error polling for event status: failed to query event: CUDA_ERROR_ILLEGAL_ADDRESS + model.fit(train_dataset, epochs=5) # evaluate the model -test_loss, test_roc_auc = model.evaluate(test_dataset) -print('\n\nTest Loss {}, Test ROC AUC {},'.format(test_loss, test_roc_auc)) - - - -model.summary() +test_loss, test_accuracy, test_roc_auc = model.evaluate(test_dataset) +print('\n\nTest Loss {}, Test Accuracy {}, Test ROC AUC {}'.format(test_loss, test_accuracy, + test_roc_auc)) # print some predict results predictions = model.predict(test_dataset) for prediction, goodRating in zip(predictions[0][:12], list(test_dataset)[0]): print("Predicted good rating: {:.2%}".format(prediction[0]), " | Actual rating label: ", - ("Good Rating" if bool(goodRating) else "Bad Rating")) + ("Good Rating" if bool(goodRating) else "Bad Rating")) \ No newline at end of file