def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, 32), item_dnn_hidden_units=(64, 32), dnn_activation='tanh', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, seed=1024, metric='cos'): """Instantiates the Deep Structured Semantic Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param user_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of user tower :param item_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of item tower :param dnn_activation: Activation function to use in deep net :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in deep net :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. """ embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, seed=seed, seq_mask_zero=True) user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns(user_features, user_feature_columns, l2_reg_embedding, seed=seed, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = combined_dnn_input(user_sparse_embedding_list, user_dense_value_list) item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) item_sparse_embedding_list, item_dense_value_list = input_from_feature_columns(item_features, item_feature_columns, l2_reg_embedding, seed=seed, embedding_matrix_dict=embedding_matrix_dict) item_dnn_input = combined_dnn_input(item_sparse_embedding_list, item_dense_value_list) user_dnn_out = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(user_dnn_input) item_dnn_out = DNN(item_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(item_dnn_input) score = Similarity(type=metric, gamma = 10)([user_dnn_out, item_dnn_out]) output = PredictionLayer("binary", False)(score) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("item_input", item_inputs_list) model.__setattr__("user_embedding", user_dnn_out) model.__setattr__("item_embedding", item_dnn_out) return model
def FM(user_feature_columns, item_feature_columns, l2_reg_embedding=1e-6, init_std=0.0001, seed=1024, metric='cos'): """Instantiates the FM architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. """ embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, seed, seq_mask_zero=True) user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns(user_features, user_feature_columns, l2_reg_embedding, init_std, seed, support_dense=False, embedding_matrix_dict=embedding_matrix_dict) item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) item_sparse_embedding_list, item_dense_value_list = input_from_feature_columns(item_features, item_feature_columns, l2_reg_embedding, init_std, seed, support_dense=False, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = concat_func(user_sparse_embedding_list, axis=1) user_vector_sum = Lambda(lambda x: reduce_sum(x, axis=1, keep_dims=False))(user_dnn_input) item_dnn_input = concat_func(item_sparse_embedding_list, axis=1) item_vector_sum = Lambda(lambda x: reduce_sum(x, axis=1, keep_dims=False))(item_dnn_input) score = Similarity(type=metric)([user_vector_sum, item_vector_sum]) output = PredictionLayer("binary", False)(score) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", user_vector_sum) model.__setattr__("item_input", item_inputs_list) model.__setattr__("item_embedding", item_vector_sum) return model
def SDM(user_feature_columns, item_feature_columns, history_feature_list, num_sampled=5, units=64, rnn_layers=2, dropout_rate=0.2, rnn_num_res=1, num_head=4, l2_reg_embedding=1e-6, dnn_activation='tanh', init_std=0.0001, seed=1024): """Instantiates the Sequential Deep Matching Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param history_feature_list: list,to indicate short and prefer sequence sparse field :param num_sampled: int, the number of classes to randomly sample per batch. :param units: int, dimension for each output layer :param rnn_layers: int, layer number of rnn :param dropout_rate: float in [0,1), the probability we will drop out a given DNN coordinate. :param rnn_num_res: int. The number of residual layers in rnn layers :param num_head: int int, the number of attention head :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_activation: Activation function to use in deep net :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :return: A Keras model instance. """ if len(item_feature_columns) > 1: raise ValueError("Now MIND only support 1 item feature like item_id") item_feature_column = item_feature_columns[0] item_feature_name = item_feature_column.name item_vocabulary_size = item_feature_columns[0].vocabulary_size features = build_input_features(user_feature_columns) user_inputs_list = list(features.values()) sparse_feature_columns = list( filter(lambda x: isinstance(x, SparseFeat), user_feature_columns)) if user_feature_columns else [] dense_feature_columns = list( filter(lambda x: isinstance(x, DenseFeat), user_feature_columns)) if user_feature_columns else [] if len(dense_feature_columns) != 0: raise ValueError("Now SDM don't support dense feature") varlen_sparse_feature_columns = list( filter(lambda x: isinstance(x, VarLenSparseFeat), user_feature_columns)) if user_feature_columns else [] sparse_varlen_feature_columns = [] prefer_history_columns = [] short_history_columns = [] prefer_fc_names = list(map(lambda x: "prefer_" + x, history_feature_list)) short_fc_names = list(map(lambda x: "short_" + x, history_feature_list)) for fc in varlen_sparse_feature_columns: feature_name = fc.name if feature_name in prefer_fc_names: prefer_history_columns.append(fc) elif feature_name in short_fc_names: short_history_columns.append(fc) else: sparse_varlen_feature_columns.append(fc) embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, seed, prefix="") item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) prefer_emb_list = embedding_lookup(embedding_matrix_dict, features, prefer_history_columns, prefer_fc_names, prefer_fc_names, to_list=True) # L^u short_emb_list = embedding_lookup(embedding_matrix_dict, features, short_history_columns, short_fc_names, short_fc_names, to_list=True) # S^u # dense_value_list = get_dense_input(features, dense_feature_columns) user_emb_list = embedding_lookup(embedding_matrix_dict, features, sparse_feature_columns, to_list=True) sequence_embed_dict = varlen_embedding_lookup( embedding_matrix_dict, features, sparse_varlen_feature_columns) sequence_embed_list = get_varlen_pooling_list( sequence_embed_dict, features, sparse_varlen_feature_columns, to_list=True) user_emb_list += sequence_embed_list # e^u # if len(user_emb_list) > 0 or len(dense_value_list) > 0: # user_emb_feature = combined_dnn_input(user_emb_list, dense_value_list) user_emb = concat_func(user_emb_list) user_emb_output = Dense(units, activation=dnn_activation, name="user_emb_output")(user_emb) prefer_sess_length = features['prefer_sess_length'] prefer_att_outputs = [] for i, prefer_emb in enumerate(prefer_emb_list): prefer_attention_output = AttentionSequencePoolingLayer( dropout_rate=0)([user_emb_output, prefer_emb, prefer_sess_length]) prefer_att_outputs.append(prefer_attention_output) prefer_att_concat = concat_func(prefer_att_outputs) prefer_output = Dense(units, activation=dnn_activation, name="prefer_output")(prefer_att_concat) short_sess_length = features['short_sess_length'] short_emb_concat = concat_func(short_emb_list) short_emb_input = Dense(units, activation=dnn_activation, name="short_emb_input")(short_emb_concat) short_rnn_output = DynamicMultiRNN( num_units=units, return_sequence=True, num_layers=rnn_layers, num_residual_layers=rnn_num_res, dropout_rate=dropout_rate)([short_emb_input, short_sess_length]) short_att_output = SelfMultiHeadAttention( num_units=units, head_num=num_head, dropout_rate=dropout_rate, future_binding=True, use_layer_norm=True)([short_rnn_output, short_sess_length ]) # [batch_size, time, num_units] short_output = UserAttention(num_units=units, activation=dnn_activation, use_res=True, dropout_rate=dropout_rate) \ ([user_emb_output, short_att_output, short_sess_length]) gate_input = concat_func([prefer_output, short_output, user_emb_output]) gate = Dense(units, activation='sigmoid')(gate_input) gate_output = Lambda( lambda x: tf.multiply(x[0], x[1]) + tf.multiply(1 - x[0], x[2]))( [gate, short_output, prefer_output]) gate_output_reshape = Lambda(lambda x: tf.squeeze(x, 1))(gate_output) item_index = EmbeddingIndex(list(range(item_vocabulary_size)))( item_features[item_feature_name]) item_embedding_matrix = embedding_matrix_dict[item_feature_name] item_embedding_weight = NoMask()(item_embedding_matrix(item_index)) pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) output = SampledSoftmaxLayer(num_sampled=num_sampled)([ pooling_item_embedding_weight, gate_output_reshape, item_features[item_feature_name] ]) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", gate_output_reshape) model.__setattr__("item_input", item_inputs_list) model.__setattr__( "item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) return model
def YoutubeDNN( user_feature_columns, item_feature_columns, num_sampled=5, user_dnn_hidden_units=(64, 16), dnn_activation='relu', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, init_std=0.0001, seed=1024, ): """Instantiates the YoutubeDNN Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param num_sampled: int, the number of classes to randomly sample per batch. :param user_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of user tower :param dnn_activation: Activation function to use in deep net :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in deep net :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :return: A Keras model instance. """ if len(item_feature_columns) > 1: raise ValueError( "Now YoutubeNN only support 1 item feature like item_id") item_feature_name = item_feature_columns[0].name embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, seed, prefix="") user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns( user_features, user_feature_columns, l2_reg_embedding, init_std, seed, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = combined_dnn_input(user_sparse_embedding_list, user_dense_value_list) item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) user_dnn_out = DNN( user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed, )(user_dnn_input) item_embedding = embedding_matrix_dict[item_feature_name] output = SampledSoftmaxLayer(item_embedding, num_sampled=num_sampled)( inputs=(user_dnn_out, item_features[item_feature_name])) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", user_dnn_out) model.__setattr__("item_input", item_inputs_list) model.__setattr__( "item_embedding", get_item_embedding(item_embedding, item_features[item_feature_name])) return model
def MIND(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1.0, dynamic_k=False, user_dnn_hidden_units=(64, 32), dnn_activation='relu', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, init_std=0.0001, seed=1024): """Instantiates the MIND Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param num_sampled: int, the number of classes to randomly sample per batch. :param k_max: int, the max size of user interest embedding :param p: float,the parameter for adjusting the attention distribution in LabelAwareAttention. :param dynamic_k: bool, whether or not use dynamic interest number :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in deep net :param user_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of user tower :param dnn_activation: Activation function to use in deep net :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in deep net :param l2_reg_dnn: L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :return: A Keras model instance. """ if len(item_feature_columns) > 1: raise ValueError("Now MIND only support 1 item feature like item_id") item_feature_column = item_feature_columns[0] item_feature_name = item_feature_column.name item_vocabulary_size = item_feature_columns[0].vocabulary_size item_embedding_dim = item_feature_columns[0].embedding_dim # item_index = Input(tensor=tf.constant([list(range(item_vocabulary_size))])) history_feature_list = [item_feature_name] features = build_input_features(user_feature_columns) sparse_feature_columns = list( filter(lambda x: isinstance(x, SparseFeat), user_feature_columns)) if user_feature_columns else [] dense_feature_columns = list( filter(lambda x: isinstance(x, DenseFeat), user_feature_columns)) if user_feature_columns else [] varlen_sparse_feature_columns = list( filter(lambda x: isinstance(x, VarLenSparseFeat), user_feature_columns)) if user_feature_columns else [] history_feature_columns = [] sparse_varlen_feature_columns = [] history_fc_names = list(map(lambda x: "hist_" + x, history_feature_list)) for fc in varlen_sparse_feature_columns: feature_name = fc.name if feature_name in history_fc_names: history_feature_columns.append(fc) else: sparse_varlen_feature_columns.append(fc) seq_max_len = history_feature_columns[0].maxlen inputs_list = list(features.values()) embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, seed, prefix="") item_features = build_input_features(item_feature_columns) query_emb_list = embedding_lookup(embedding_matrix_dict, item_features, item_feature_columns, history_feature_list, history_feature_list, to_list=True) keys_emb_list = embedding_lookup(embedding_matrix_dict, features, history_feature_columns, history_fc_names, history_fc_names, to_list=True) dnn_input_emb_list = embedding_lookup(embedding_matrix_dict, features, sparse_feature_columns, mask_feat_list=history_feature_list, to_list=True) dense_value_list = get_dense_input(features, dense_feature_columns) sequence_embed_dict = varlen_embedding_lookup( embedding_matrix_dict, features, sparse_varlen_feature_columns) sequence_embed_list = get_varlen_pooling_list( sequence_embed_dict, features, sparse_varlen_feature_columns, to_list=True) dnn_input_emb_list += sequence_embed_list # keys_emb = concat_func(keys_emb_list, mask=True) # query_emb = concat_func(query_emb_list, mask=True) history_emb = PoolingLayer()(NoMask()(keys_emb_list)) target_emb = PoolingLayer()(NoMask()(query_emb_list)) # target_emb_size = target_emb.get_shape()[-1].value # max_len = history_emb.get_shape()[1].value hist_len = features['hist_len'] high_capsule = CapsuleLayer(input_units=item_embedding_dim, out_units=item_embedding_dim, max_len=seq_max_len, k_max=k_max)((history_emb, hist_len)) if len(dnn_input_emb_list) > 0 or len(dense_value_list) > 0: user_other_feature = combined_dnn_input(dnn_input_emb_list, dense_value_list) other_feature_tile = tf.keras.layers.Lambda( tile_user_otherfeat, arguments={'k_max': k_max})(user_other_feature) user_deep_input = Concatenate()( [NoMask()(other_feature_tile), high_capsule]) else: user_deep_input = high_capsule user_embeddings = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed, name="user_embedding")(user_deep_input) item_inputs_list = list(item_features.values()) item_embedding_matrix = embedding_matrix_dict[item_feature_name] item_index = EmbeddingIndex(list(range(item_vocabulary_size)))( item_features[item_feature_name]) item_embedding_weight = NoMask()(item_embedding_matrix(item_index)) pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) if dynamic_k: user_embedding_final = LabelAwareAttention( k_max=k_max, pow_p=p, )((user_embeddings, target_emb, hist_len)) else: user_embedding_final = LabelAwareAttention( k_max=k_max, pow_p=p, )((user_embeddings, target_emb)) output = SampledSoftmaxLayer(num_sampled=num_sampled)([ pooling_item_embedding_weight, user_embedding_final, item_features[item_feature_name] ]) model = Model(inputs=inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", inputs_list) model.__setattr__("user_embedding", user_embeddings) model.__setattr__("item_input", item_inputs_list) model.__setattr__( "item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) return model
def Mind(user_feature_columns, item_feature_columns, num_sampled=5, k_max=2, p=1.0, dynamic_k=False, user_dnn_hidden_units=(64, 32), dnn_activation='relu', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, output_activation='linear', seed=1024): """ :param k_max: 用户兴趣胶囊的最大个数 """ # 目前这里只支持item_feature_columns为1的情况,即只能转入item_id if len(item_feature_columns) > 1: raise ValueError("Now MIND only support 1 item feature like item_id") # 获取item相关的配置参数 item_feature_column = item_feature_columns[0] item_feature_name = item_feature_column.name item_vocabulary_size = item_feature_column.vocabulary_size item_embedding_dim = item_feature_column.embedding_dim behavior_feature_list = [item_feature_name] # 为用户特征创建Input层 user_input_layer_dict = build_input_layers(user_feature_columns) item_input_layer_dict = build_input_layers(item_feature_columns) # 将Input层转化成列表的形式作为model的输入 user_input_layers = list(user_input_layer_dict.values()) item_input_layers = list(item_input_layer_dict.values()) # 筛选出特征中的sparse特征和dense特征,方便单独处理 sparse_feature_columns = list( filter(lambda x: isinstance(x, SparseFeat), user_feature_columns)) if user_feature_columns else [] dense_feature_columns = list( filter(lambda x: isinstance(x, DenseFeat), user_feature_columns)) if user_feature_columns else [] varlen_feature_columns = list( filter(lambda x: isinstance(x, VarLenSparseFeat), user_feature_columns)) if user_feature_columns else [] # 由于这个变长序列里面只有历史点击文章,没有类别啥的,所以这里直接可以用varlen_feature_columns # deepctr这里单独把点击文章这个放到了history_feature_columns seq_max_len = varlen_feature_columns[0].maxlen # 构建embedding字典 embedding_layer_dict = build_embedding_layers(user_feature_columns + item_feature_columns) # 获取当前的行为特征(doc)的embedding,这里面可能又多个类别特征,所以需要pooling下 query_embed_list = embedding_lookup(behavior_feature_list, item_input_layer_dict, embedding_layer_dict) # 长度为1 # 获取行为序列(doc_id序列, hist_doc_id) 对应的embedding,这里有可能有多个行为产生了行为序列,所以需要使用列表将其放在一起 keys_embed_list = embedding_lookup([varlen_feature_columns[0].name], user_input_layer_dict, embedding_layer_dict) # 长度为1 # 用户离散特征的输入层与embedding层拼接 dnn_input_emb_list = embedding_lookup( [col.name for col in sparse_feature_columns], user_input_layer_dict, embedding_layer_dict) # 获取dense dnn_dense_input = [] for fc in dense_feature_columns: if fc.name != 'hist_len': # 连续特征不要这个 dnn_dense_input.append(user_input_layer_dict[fc.name]) # 把keys_emb_list和query_emb_listpooling操作, 这是因为可能每个商品不仅有id,还可能用类别,品牌等多个embedding向量,这种需要pooling成一个 history_emb = PoolingLayer()(NoMask()(keys_embed_list)) # (None, 50, 8) target_emb = PoolingLayer()(NoMask()(query_embed_list)) # (None, 1, 8) hist_len = user_input_layer_dict['hist_len'] # 胶囊网络 # (None, 2, 8) 得到了两个兴趣胶囊 high_capsule = CapsuleLayer(input_units=item_embedding_dim, out_units=item_embedding_dim, max_len=seq_max_len, k_max=k_max)((history_emb, hist_len)) # 把用户的其他特征拼接到胶囊网络上来 if len(dnn_input_emb_list) > 0 or len(dnn_dense_input) > 0: user_other_feature = combined_dnn_input(dnn_input_emb_list, dnn_dense_input) # (None, 2, 32) 这里会发现其他的用户特征是每个胶囊复制了一份,然后拼接起来 other_feature_tile = tf.keras.layers.Lambda( tile_user_otherfeat, arguments={'k_max': k_max})(user_other_feature) user_deep_input = Concatenate()( [NoMask()(other_feature_tile), high_capsule]) # (None, 2, 40) else: user_deep_input = high_capsule # 接下来过一个DNN层,获取最终的用户表示向量 如果是三维输入, 那么最后一个维度与w相乘,所以这里如果不自己写,可以用Dense层的列表也可以 user_embeddings = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, output_activation=output_activation, seed=seed, name="user_embedding")( user_deep_input) # (None, 2, 8) # 接下来,过Label-aware layer if dynamic_k: user_embedding_final = LabelAwareAttention( k_max=k_max, pow_p=p, )((user_embeddings, target_emb, hist_len)) else: user_embedding_final = LabelAwareAttention( k_max=k_max, pow_p=p, )((user_embeddings, target_emb)) # 接下来 item_embedding_matrix = embedding_layer_dict[ item_feature_name] # 获取doc_id的embedding层 item_index = EmbeddingIndex(list(range(item_vocabulary_size)))( item_input_layer_dict[item_feature_name]) # 所有doc_id的索引 item_embedding_weight = NoMask()( item_embedding_matrix(item_index)) # 拿到所有item的embedding pooling_item_embedding_weight = PoolingLayer()([ item_embedding_weight ]) # 这里依然是当可能不止item_id,或许还有brand_id, cat_id等,需要池化 # 这里传入的是整个doc_id的embedding, user_embedding, 以及用户点击的doc_id,然后去进行负采样计算损失操作 output = SampledSoftmaxLayer(num_sampled)([ pooling_item_embedding_weight, user_embedding_final, item_input_layer_dict[item_feature_name] ]) model = Model(inputs=user_input_layers + item_input_layers, outputs=output) # 下面是等模型训练完了之后,获取用户和item的embedding model.__setattr__("user_input", user_input_layers) model.__setattr__("user_embedding", user_embeddings) model.__setattr__("item_input", item_input_layers) model.__setattr__( "item_embedding", get_item_embedding(pooling_item_embedding_weight, item_input_layer_dict[item_feature_name])) return model
def Sdm(user_feature_columns, item_feature_columns, history_feature_list, num_sampled=5, units=32, rnn_layers=2, dropout_rate=0.2, rnn_num_res=1, num_head=4, l2_reg_embedding=1e-6, dnn_activation='tanh', seed=1024): """ :param rnn_num_res: rnn的残差层个数 :param history_feature_list: short和long sequence field """ # item_feature目前只支持doc_id, 再加别的就不行了,其实这里可以改造下 if (len(item_feature_columns)) > 1: raise ValueError("SDM only support 1 item feature like doc_id") # 获取item_feature的一些属性 item_feature_column = item_feature_columns[0] item_feature_name = item_feature_column.name item_vocabulary_size = item_feature_column.vocabulary_size # 为用户特征创建Input层 user_input_layer_dict = build_input_layers(user_feature_columns) item_input_layer_dict = build_input_layers(item_feature_columns) # 将Input层转化成列表的形式作为model的输入 user_input_layers = list(user_input_layer_dict.values()) item_input_layers = list(item_input_layer_dict.values()) # 筛选出特征中的sparse特征和dense特征,方便单独处理 sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), user_feature_columns)) if user_feature_columns else [] dense_feature_columns = list(filter(lambda x: isinstance(x, DenseFeat), user_feature_columns)) if user_feature_columns else [] if len(dense_feature_columns) != 0: raise ValueError("SDM dont support dense feature") # 目前不支持Dense feature varlen_feature_columns = list(filter(lambda x: isinstance(x, VarLenSparseFeat), user_feature_columns)) if user_feature_columns else [] # 构建embedding字典 embedding_layer_dict = build_embedding_layers(user_feature_columns+item_feature_columns) # 拿到短期会话和长期会话列 之前的命名规则在这里起作用 sparse_varlen_feature_columns = [] prefer_history_columns = [] short_history_columns = [] prefer_fc_names = list(map(lambda x: "prefer_" + x, history_feature_list)) short_fc_names = list(map(lambda x: "short_" + x, history_feature_list)) for fc in varlen_feature_columns: if fc.name in prefer_fc_names: prefer_history_columns.append(fc) elif fc.name in short_fc_names: short_history_columns.append(fc) else: sparse_varlen_feature_columns.append(fc) # 获取用户的长期行为序列列表 L^u # [<tf.Tensor 'emb_prefer_doc_id_2/Identity:0' shape=(None, 50, 32) dtype=float32>, <tf.Tensor 'emb_prefer_cat1_2/Identity:0' shape=(None, 50, 32) dtype=float32>, <tf.Tensor 'emb_prefer_cat2_2/Identity:0' shape=(None, 50, 32) dtype=float32>] prefer_emb_list = embedding_lookup(prefer_fc_names, user_input_layer_dict, embedding_layer_dict) # 获取用户的短期序列列表 S^u # [<tf.Tensor 'emb_short_doc_id_2/Identity:0' shape=(None, 5, 32) dtype=float32>, <tf.Tensor 'emb_short_cat1_2/Identity:0' shape=(None, 5, 32) dtype=float32>, <tf.Tensor 'emb_short_cat2_2/Identity:0' shape=(None, 5, 32) dtype=float32>] short_emb_list = embedding_lookup(short_fc_names, user_input_layer_dict, embedding_layer_dict) # 用户离散特征的输入层与embedding层拼接 e^u user_emb_list = embedding_lookup([col.name for col in sparse_feature_columns], user_input_layer_dict, embedding_layer_dict) user_emb = concat_func(user_emb_list) user_emb_output = Dense(units, activation=dnn_activation, name='user_emb_output')(user_emb) # (None, 1, 32) # 长期序列行为编码 # 过AttentionSequencePoolingLayer --> Concat --> DNN prefer_sess_length = user_input_layer_dict['prefer_sess_length'] prefer_att_outputs = [] # 遍历长期行为序列 for i, prefer_emb in enumerate(prefer_emb_list): prefer_attention_output = AttentionSequencePoolingLayer(dropout_rate=0)([user_emb_output, prefer_emb, prefer_sess_length]) prefer_att_outputs.append(prefer_attention_output) prefer_att_concat = concat_func(prefer_att_outputs) # (None, 1, 64) <== Concat(item_embedding,cat1_embedding,cat2_embedding) prefer_output = Dense(units, activation=dnn_activation, name='prefer_output')(prefer_att_concat) # print(prefer_output.shape) # (None, 1, 32) # 短期行为序列编码 short_sess_length = user_input_layer_dict['short_sess_length'] short_emb_concat = concat_func(short_emb_list) # (None, 5, 64) 这里注意下, 对于短期序列,描述item的side info信息进行了拼接 short_emb_input = Dense(units, activation=dnn_activation, name='short_emb_input')(short_emb_concat) # (None, 5, 32) # 过rnn 这里的return_sequence=True, 每个时间步都需要输出h short_rnn_output = DynamicMultiRNN(num_units=units, return_sequence=True, num_layers=rnn_layers, num_residual_layers=rnn_num_res, # 这里竟然能用到残差 dropout_rate=dropout_rate)([short_emb_input, short_sess_length]) # print(short_rnn_output) # (None, 5, 32) # 过MultiHeadAttention # (None, 5, 32) short_att_output = MultiHeadAttention(num_units=units, head_num=num_head, dropout_rate=dropout_rate)([short_rnn_output, short_sess_length]) # (None, 5, 64) # user_attention # (None, 1, 32) short_output = UserAttention(num_units=units, activation=dnn_activation, use_res=True, dropout_rate=dropout_rate)([user_emb_output, short_att_output, short_sess_length]) # 门控融合 gated_input = concat_func([prefer_output, short_output, user_emb_output]) gate = Dense(units, activation='sigmoid')(gated_input) # (None, 1, 32) # temp = tf.multiply(gate, short_output) + tf.multiply(1-gate, prefer_output) 感觉这俩一样? gated_output = Lambda(lambda x: tf.multiply(x[0], x[1]) + tf.multiply(1-x[0], x[2]))([gate, short_output, prefer_output]) # [None, 1,32] gated_output_reshape = Lambda(lambda x: tf.squeeze(x, 1))(gated_output) # (None, 32) 这个维度必须要和docembedding层的维度一样,否则后面没法sortmax_loss # 接下来 item_embedding_matrix = embedding_layer_dict[item_feature_name] # 获取doc_id的embedding层 item_index = EmbeddingIndex(list(range(item_vocabulary_size)))(item_input_layer_dict[item_feature_name]) # 所有doc_id的索引 item_embedding_weight = NoMask()(item_embedding_matrix(item_index)) # 拿到所有item的embedding pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) # 这里依然是当可能不止item_id,或许还有brand_id, cat_id等,需要池化 # 这里传入的是整个doc_id的embedding, user_embedding, 以及用户点击的doc_id,然后去进行负采样计算损失操作 output = SampledSoftmaxLayer(num_sampled)([pooling_item_embedding_weight, gated_output_reshape, item_input_layer_dict[item_feature_name]]) model = Model(inputs=user_input_layers+item_input_layers, outputs=output) # 下面是等模型训练完了之后,获取用户和item的embedding model.__setattr__("user_input", user_input_layers) model.__setattr__("user_embedding", gated_output_reshape) # 用户embedding是取得门控融合的用户向量 model.__setattr__("item_input", item_input_layers) # item_embedding取得pooling_item_embedding_weight, 这个会发现是负采样操作训练的那个embedding矩阵 model.__setattr__("item_embedding", get_item_embedding(pooling_item_embedding_weight, item_input_layer_dict[item_feature_name])) return model
def DSSM(user_feature_columns, item_feature_columns, user_dnn_hidden_units=(64, 32), item_dnn_hidden_units=(64, 32), dnn_activation='tanh', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, init_std=0.0001, seed=1024, metric='cos'): embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, seed, seq_mask_zero=True) user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns( user_features, user_feature_columns, l2_reg_embedding, init_std, seed, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = combined_dnn_input(user_sparse_embedding_list, user_dense_value_list) item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) item_sparse_embedding_list, item_dense_value_list = input_from_feature_columns( item_features, item_feature_columns, l2_reg_embedding, init_std, seed, embedding_matrix_dict=embedding_matrix_dict) item_dnn_input = combined_dnn_input(item_sparse_embedding_list, item_dense_value_list) user_dnn_out = DNN( user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed, )(user_dnn_input) item_dnn_out = DNN(item_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed)(item_dnn_input) score = Similarity(type=metric)([user_dnn_out, item_dnn_out]) output = PredictionLayer("binary", False)(score) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) plot_model(model, to_file='dnn.png', show_shapes=True) print("go") model.__setattr__("user_input", user_inputs_list) model.__setattr__("item_input", item_inputs_list) model.__setattr__("user_embedding", user_dnn_out) model.__setattr__("item_embedding", item_dnn_out) return model
def DSSM4FatureColumn(user_feature_columns, item_feature_columns, user_inputs_keys, item_inputs_keys, user_dnn_hidden_units=(64, 32), item_dnn_hidden_units=(64, 32), dnn_activation='tanh', dnn_use_bn=False, l2_reg_dnn=0, dnn_dropout=0, seed=1024, metric='cos'): """Instantiates the Deep Structured Semantic Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param user_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of user tower :param item_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of item tower :param dnn_activation: Activation function to use in deep net :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in deep net :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. :param seed: integer ,to use as random seed. :param metric: str, ``"cos"`` for cosine or ``"ip"`` for inner product :return: A Keras model instance. """ # #TODO:build_input_features4fc函数增加不同输入维度的方法 user_features = build_input_features4fc( user_inputs_keys) # Input有不同维度的情况,需要修改该函数 user_inputs_list = list(user_features.values()) # #example for output user id or item id, this key must be in tfrecord # userid_output_ = tf.identity(user_features.get('userid')) user_dnn_input = tf.keras.layers.DenseFeatures(user_feature_columns)( user_features) item_features = build_input_features4fc(item_inputs_keys) item_inputs_list = list(item_features.values()) item_dnn_input = tf.keras.layers.DenseFeatures(item_feature_columns)( item_features) user_dnn_out = DNN(user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(user_dnn_input) item_dnn_out = DNN(item_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed=seed)(item_dnn_input) score = Similarity(type=metric)([user_dnn_out, item_dnn_out]) output = PredictionLayer("binary", False)(score) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("item_input", item_inputs_list) model.__setattr__("user_embedding", user_dnn_out) model.__setattr__("item_embedding", item_dnn_out) # model.__setattr__("user_ids", userid_output_) return model
def YoutubeDNN( user_feature_columns, item_feature_columns, num_sampled=5, user_dnn_hidden_units=(64, 32), dnn_activation='relu', dnn_use_bn=False, l2_reg_dnn=0, l2_reg_embedding=1e-6, dnn_dropout=0, init_std=0.0001, seed=1024, ): """Instantiates the YoutubeDNN Model architecture. :param user_feature_columns: An iterable containing user's features used by the model. :param item_feature_columns: An iterable containing item's features used by the model. :param num_sampled: int, the number of classes to randomly sample per batch. :param user_dnn_hidden_units: list,list of positive integer or empty list, the layer number and units in each layer of user tower :param dnn_activation: Activation function to use in deep net :param dnn_use_bn: bool. Whether use BatchNormalization before activation or not in deep net :param l2_reg_dnn: float. L2 regularizer strength applied to DNN :param l2_reg_embedding: float. L2 regularizer strength applied to embedding vector :param dnn_dropout: float in [0,1), the probability we will drop out a given DNN coordinate. :param init_std: float,to use as the initialize std of embedding vector :param seed: integer ,to use as random seed. :return: A Keras model instance. """ if len(item_feature_columns) > 1: raise ValueError( "Now YoutubeNN only support 1 item feature like item_id") item_feature_name = item_feature_columns[0].name item_vocabulary_size = item_feature_columns[0].vocabulary_size #构建User和 item 的 Embedding dict embedding_matrix_dict = create_embedding_matrix(user_feature_columns + item_feature_columns, l2_reg_embedding, init_std, seed, prefix="") #构建 User Input dict user_features = build_input_features(user_feature_columns) user_inputs_list = list(user_features.values()) user_sparse_embedding_list, user_dense_value_list = input_from_feature_columns( user_features, user_feature_columns, l2_reg_embedding, init_std, seed, embedding_matrix_dict=embedding_matrix_dict) user_dnn_input = combined_dnn_input(user_sparse_embedding_list, user_dense_value_list) # 构建 item Input dict item_features = build_input_features(item_feature_columns) item_inputs_list = list(item_features.values()) user_dnn_out = DNN( user_dnn_hidden_units, dnn_activation, l2_reg_dnn, dnn_dropout, dnn_use_bn, seed, )(user_dnn_input) print(item_features[item_feature_name]) print(list(range(item_vocabulary_size))) item_index = EmbeddingIndex(list(range(item_vocabulary_size)))( item_features[item_feature_name]) item_embedding_matrix = embedding_matrix_dict[item_feature_name] print(item_embedding_matrix(item_index)) item_embedding_weight = NoMask()(item_embedding_matrix(item_index)) pooling_item_embedding_weight = PoolingLayer()([item_embedding_weight]) # tf.nn.sampled_softmax_loss,在类别很多的情况下训练softmax分类器的高效的方法。 # 注意:仅在训练时使用这一采样操作,测试时还是用全部的类别 output = SampledSoftmaxLayer(num_sampled=num_sampled)([ pooling_item_embedding_weight, user_dnn_out, item_features[item_feature_name] ]) model = Model(inputs=user_inputs_list + item_inputs_list, outputs=output) model.__setattr__("user_input", user_inputs_list) model.__setattr__("user_embedding", user_dnn_out) model.__setattr__("item_input", item_inputs_list) model.__setattr__( "item_embedding", get_item_embedding(pooling_item_embedding_weight, item_features[item_feature_name])) return model