YoutubeDNN实现

本文介绍了YouTubeDNN推荐系统的原理及其实现方法。通过将推荐任务视为多分类问题,YouTubeDNN能够根据用户的历史行为和上下文信息,预测用户在特定时间观看不同视频的概率,并据此提供个性化的推荐。文中详细探讨了模型架构、训练技巧及其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

网上关于YouTubeDNN的介绍有很多,这里就不详细介绍

1.把推荐看做多分类问题

可以把推荐当作一个多分类问题,把每一个视频当作一个分类,则给定用户U和上下文C的条件下,在时间t观看第i个video(第i类)的概率为:

其中u是用户的向量表示(embedding),v表示video的向量表示(embedding)。在模型中,利用用户历史和上下文来学习用户的embedding,利用用户的embedding对每个用户进行视频推荐;类似于Word2vec,模型在训练过程中可以利用负采样进行优化。

2.模型架构

模型架构如图所示:

  • 模型输入:用户历史观看embedding,历史搜索embedding,画像特征,年龄、性别等

  • 模型输出:用户观看每个视频的概率,topn推荐

模型中,首先获取各个特征进行拼接,作为网络的输入,经过一系列隐藏层得到用户embedding,根据用户embedding与video embedding即可计算该用户在时间t观看视频的概率,依据概率分布选取topn作为候选推荐子集

3.模型trick

模型特征:

  • 历史观看:类似于Word2vec中的CBOW模型,对观看序列进行建模,得到每个video的embedding,然后把每个用户的历史观看embedding平均值作为输入

  • 历史搜索:同上,对历史搜索序列unigram, bigram建模得到embedding,将搜索序列embedding平均作为输入

  • 统计学特征:地理位置、设备

  • 画像特征:用户性别、登录状态、年龄(归一化到[0,1] )

  • 视频被上传的时间(Example Age):YouTube随时都会有一些新视频(fresh),研究发现,用户更加喜欢这些新视频,即使这些视频和他们过去的行为不太相关,因此考虑把视频被上传的时间作为特征。实验结果如图所示:

横坐标为视频上传时间,纵坐标为条件似然概率(衡量视频的流行度),绿色线为视频上传后随着时间的推移,流行度变化的经验概率分布,蓝色线为未考虑视频上传时间时,模型的概率分布,红色为加入视频上传时间后的分布,可以看出,在加入该变量的条件下,概率分布与经验分布大致相符。

4.实现代码

模型部分:


class YoutubeDNN(Model):

    def __init__(self, user_sparse_feature_columns, item_sparse_feature_columns, user_dense_feature_columns=(),
                 item_dense_feature_columns=(), num_sampled=1,
                 user_dnn_hidden_units=(64, 32), item_dnn_hidden_units=(64, 32), dnn_activation='relu',
               l2_reg_embedding=1e-6, dnn_dropout=0, **kwargs):
        super(YoutubeDNN, self).__init__(**kwargs)
        self.num_sampled = num_sampled
        self.user_sparse_feature_columns = user_sparse_feature_columns
        self.user_dense_feature_columns = user_dense_feature_columns
        self.item_sparse_feature_columns = item_sparse_feature_columns
        self.item_dense_feature_columns = item_dense_feature_columns

        self.user_embed_layers = {
            'embed_' + str(feat['feat']): Embedding(input_dim=feat['feat_num'],
                                         input_length=feat['feat_len'],
                                         output_dim=feat['embed_dim'],
                                         embeddings_initializer='random_uniform',
                                         embeddings_regularizer=l2(l2_reg_embedding))
            for feat in self.user_sparse_feature_columns
        }

        self.item_embed_layers = {
            'embed_' + str(feat['feat']): Embedding(input_dim=feat['feat_num'],
                                         input_length=feat['feat_len'],
                                         output_dim=feat['embed_dim'],
                                         embeddings_initializer='random_uniform',
                                         embeddings_regularizer=l2(l2_reg_embedding))
            for feat in self.item_sparse_feature_columns
        }

        self.user_dnn = DNN(user_dnn_hidden_units, dnn_activation, dnn_dropout)
        self.item_dnn = DNN(item_dnn_hidden_units, dnn_activation, dnn_dropout)
        self.sampled_softmax = SampledSoftmaxLayer(num_sampled=self.num_sampled)


    def call(self, inputs, training=None, mask=None):

        user_sparse_inputs, item_sparse_inputs, labels = inputs

        user_sparse_embed = tf.concat([self.user_embed_layers['embed_{}'.format(k)](v)
                                  for k, v in user_sparse_inputs.items()], axis=-1)

        user_dnn_input = user_sparse_embed
        self.user_dnn_out = self.user_dnn(user_dnn_input)

        item_sparse_embed = tf.concat([self.item_embed_layers['embed_{}'.format(k)](v)
                                       for k, v in item_sparse_inputs.items()], axis=-1)
        item_dnn_input = item_sparse_embed
        self.item_dnn_out = self.item_dnn(item_dnn_input)


        output = self.sampled_softmax([self.item_dnn_out, self.user_dnn_out, labels])

        return output
Sampled Softmax:
 SampledSoftmaxLayer(Layer):
 """Sampled Softmax Layer"""
def __init__(self, num_sampled=5, **kwargs):
        super(SampledSoftmaxLayer, self).__init__(**kwargs)
        self.num_sampled = num_sampled

    def build(self, input_shape):
        self.size = input_shape[0][2]
        self.zero_bias = self.add_weight(shape=[self.size],
                                         initializer=Zeros,
                                         dtype=tf.float32,
                                         trainable=False,
                                         name="bias")
        super(SampledSoftmaxLayer, self).build(input_shape)

    def call(self, inputs_with_label_idx, training=None, **kwargs):
 """
        The first input should be the model as it were, and the second the
        target (i.e., a repeat of the training data) to compute the labels
        argument
        """
item_embeddings, user_embeddings, label_idx = inputs_with_label_idx
        item_embeddings = tf.squeeze(item_embeddings, axis=1)  # (None, len)
        # item_embeddings = tf.transpose(item_embeddings)
        user_embeddings = tf.squeeze(user_embeddings, axis=1)  # (None, len)

        loss = tf.nn.sampled_softmax_loss(weights=item_embeddings,  # self.item_embedding.
                                          biases=self.zero_bias,
                                          labels=label_idx,
                                          inputs=user_embeddings,
                                          num_sampled=self.num_sampled,
                                          num_classes=self.size,  # self.target_song_size
                                          )
        return tf.expand_dims(loss, axis=1)

https://github.com/littlemesie/recommend-tf2.0推荐系统之深度学习模型,框架采用tensorflow2. Contribute to littlemesie/recommend-tf2.0 development by creating an account on GitHub.https://github.com/littlemesie/recommend-tf2.0

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值