Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update DIEN.py #59

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 119 additions & 122 deletions TFRecModel/src/com/sparrowrecsys/offline/tensorflow/DIEN.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand All @@ -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)
Expand All @@ -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 = [
Expand All @@ -119,16 +114,13 @@ 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),
tf.feature_column.numeric_column(key='negtive_userRatedMovie4', default_value=0),
tf.feature_column.numeric_column(key='negtive_userRatedMovie5', default_value=0),
]



# user profile
user_profile = [
user_emb_col,
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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"))