推出适用于设备端推荐的自适应框架

发布人:Ellie Zhou、Tian Lin、Shuangfeng Li 以及 Sushant Prakash

简介和动机

我们非常高兴地宣布推出一种自适应框架,用于使用您自己的数据以及高级用户建模架构构建设备端推荐 ML 解决方案。

在之前开源了 设备端推荐解决方案 后,我们发现社区中的许多人都对引入设备端推荐系统 AI 颇有兴趣。受反馈内容的激励与启发,我们考虑了各种不同的用例,并创建了一种框架,该框架可以生成适应不同种类的数据、特征和架构的 TensorFlow Lite 推荐模型,对之前的模型进行了完善。

此框架的优势

  • 灵活: 自适应框架支持用户以可配置方式创建模型。

  • 优化了模型表征: 为了完善 之前的模型,我们的新推荐模型可以利用多种特征,而非单单一种特征。

个性化推荐在如今的数字化生活中越来越重要。随着越来越多的用户操作已转移至边缘设备,支持设备端推荐系统将变为一个重要方向。与完全基于服务器的传统推荐系统相比,设备端解决方案具有独特的优势,如保护用户隐私、快速地对设备端用户操作作出反应、利用轻量级 TensorFlow Lite 推理,以及绕过网络依赖。我们欢迎您体验此 框架 并在您的应用中创建推荐体验。

在本文中,我们将

  • 介绍完善后的模型架构和框架自适应性。
  • 带您循序渐进地了解如何利用框架。
  • 根据使用公开数据集完成的研究提供数据分析。

您可以在 TensorFlow 网站上找到更多详细信息。

模型

推荐模型通常根据用户之前的活动预测用户未来活动。我们的框架支持模型使用上下文信息展开预测,框架可以按照以下架构进行描述:

图 1:可配置推荐模型的图示。根据用户定义的配置创建每个模块

在上下文方面,由编码器聚合所有用户活动的表征,以生成上下文嵌入。我们支持三种不同类型的编码器:1) 词袋(又名为 BOW),2) 1-D 卷积(又名为 CNN)和 3) LSTM。在标签方面,同样会将正样本标签项和词汇表中的所有其他负样本标签项编码为向量。将上下文和标签嵌入与点积进行结合并提供给 softmax 交叉熵损失。

在框架内部,将 ContextEncoder、LabelEncoder 和 DotProductSimilarity 的 tf.keras 层封装为 RecommendationModel 中的重要组件。

为了为每个用户活动建立模型,我们可以使用活动项目编号(称为基于编号的模型)、项目的多个特征(称为基于特征的模型)或二者的组合。基于特征的模型会使用多个特征,集中性地对用户行为进行编码。借助我们的框架,您可以可配置方式创建基于编号或基于特征的模型。

与上一版本类似,系统会在训练后导出 TensorFlow Lite 模型,可以直接在推荐候选条目中提供前 K 个预测。

分步教程

为了展示这个全新的自适应框架,我们使用多个特征对含有 MovieLens 数据集的设备端电影推荐模型进行了训练,并将其整合到演示版应用中(模型和应用仅用于演示目的)。MovieLens 1M 数据集包含 6039 位用户对 3951 部电影的评分,每个用户仅对一小部分电影进行评分。

让我们了解一下如何在此 Notebook 中分步使用框架。

(a) 环境准备

git clone https://github.com/tensorflow/examples
cd examples/lite/examples/recommendation/ml/
pip install -r requirements.txt

(b) 准备训练数据

请参考 movielens 示例生成文件准备训练数据。需要注意的是,预计 TensorFlow Lite 输入特征为 FixedLenFeature,因此请填充或截取特征,并在输入配置中设置特征长度。您可以按照您的想法随意使用以下命令来处理示例数据集。

python -m data.example_generation_movielens \
 --data_dir=data/raw \
 --output_dir=data/examples \
 --min_timeline_length=3 \
 --max_context_length=10 \
 --max_context_movie_genre_length=32 \
 --min_rating=2 \
 --train_data_fraction=0.9 \
 --build_vocabs=True

MovieLens 数据包含 ratings.dat(列: 用户编号电影编号评分时间戳)和 movies.dat(列:电影编号标题类型)。在示例生成脚本中,我们同时采用了这两个文件,仅将评分保持在 2 分以上、构成用户电影交互时间线,作为标签的示例活动以及之前的用户活动作为预测上下文。请查找生成的 tf.Example

0 : {
  features: {
    feature: {
      key  : "context_movie_id"
      value: { int64_list: { value: [ 1124, 2240, 3251, ..., 1268 ] } }
    }
    feature: {
      key  : "context_movie_rating"
      value: { float_list: {value: [ 3.0, 3.0, 4.0, ..., 3.0 ] } }
    }
    feature: {
      key  : "context_movie_year"
      value: { int64_list: { value: [ 1981, 1980, 1985, ..., 1990 ] } }
    }
    feature: {
      key  : "context_movie_id"
      value: { int64_list: { value: [ 1124, 2240, 3251, ..., 1268 ] } }
    }
    feature: {
      key  : "context_movie_genre"
      value: { bytes_list: { value: [ "Drama", "Drama", "Mystery", ..., "UNK" ] } }
    }
    feature: {
      key  : "label_movie_id"
      value: { int64_list: { value: [ 3252 ] }  }
    }
  }
}

(c) 创建输入配置

准备好数据后,请设置输入配置,例如,这是 movielens 电影推荐模型的一个示例配置。

activity_feature_groups {
  features {
    feature_name: "context_movie_id"
    feature_type: INT
    vocab_size: 3953
    embedding_dim: 8
    feature_length: 10
  }
  features {
    feature_name: "context_movie_rating"
    feature_type: FLOAT
    feature_length: 10
  }
  encoder_type: CNN
}
activity_feature_groups {
  features {
    feature_name: "context_movie_genre"
    feature_type: STRING
    vocab_name: "movie_genre_vocab.txt"
    vocab_size: 19
    embedding_dim: 4
    feature_length: 32
  }
  encoder_type: CNN
}
label_feature {
  feature_name: "label_movie_id"
  feature_type: INT
  vocab_size: 3953
  embedding_dim: 8
  feature_length: 1
}

(d) 训练模型

模型训练器将根据输入配置构建含有简单界面的推荐模型。

python -m model.recommendation_model_launcher -- \
 --training_data_filepattern "data/examples/train_movielens_1m.tfrecord" \
 --testing_data_filepattern "data/examples/test_movielens_1m.tfrecord"\
 --model_dir "model/model_dir" \
 --vocab_dir "data/examples" \
 --input_config_file "configs/sample_input_config.pbtxt" \
 --batch_size 32 \
 --learning_rate 0.01 \
 --steps_per_epoch 2 \
 --num_epochs 2 \
 --num_eval_steps 2 \
 --run_mode "train_and_eval" \
 --gradient_clip_norm 1.0 \
 --num_predictions 10 \
 --hidden_layer_dims "32,32" \
 --eval_top_k "1,5" \
 --conv_num_filter_ratios "2,4" \
 --conv_kernel_size 4 \
 --lstm_num_units 16

在推荐模型内部,核心组件将被打包到 keras 层(context_encoder.pylabel_encoder.pydotproduct_similarity.py),其中每一层都可以被自身使用。下图介绍了代码结构:

图 2:使用上下文信息预测下一部电影的模型架构示例。输入内容为上述配置所指定的 (a) 电影编号、(b) 评分和 (c) 类型的历史记录

通过此框架,您可以通过命令直接执行模型训练启动器:

python -m model.recommendation_model_launcher \
 --input_config_file "configs/sample_input_config.pbtxt" \
 --vocab_dir "data/examples" \
 --run_mode "export" \
 --checkpoint_path "model/model_dir/ckpt-1000" \
 --num_predictions 10 \
 --hidden_layer_dims "32,32" \
 --conv_num_filter_ratios "2,4" \
 --conv_kernel_size 4 \
 --lstm_num_units 16

导出至 TensorFlow Lite 后,可以在 Notebook 中找到推理代码,同时我们会推荐读者查看该处的详细信息。

框架自适应性

我们的框架会提供 protobuf 接口,用户可在此接口中对特征组、类型和其他信息进行配置,以相应地构建模型。在此接口中,您可以配置:

特征

框架通常将特征分为 3 种类型:整型字符串浮点数。由于需要为整型和字符串特征创建嵌入空间,因此需要指定嵌入维度、词汇表名称和大小。浮点数特征值可直接使用。此外,对于设备端模型,我们建议使用可直接进行配置的固定长度特征。

message Feature {
  optional string feature_name = 1;

  // Supported feature types: STRING, INT, FLOAT.
  optional FeatureType feature_type = 2;

  optional string vocab_name = 3;

  optional int64 vocab_size = 4;

  optional int64 embedding_dim = 5;

  optional int64 feature_length = 6;
}

特征组

一个用户活动的一个特征可能具有多个值。例如,一部电影可以属于多个类别,每部电影将具有多个类型的特征值。为了处理不同特征形状,我们引入了“特征组”,用于将特征合并为组。可以将长度相同的特征放置到同一特征组中,以一起进行编码。在输入配置中,您可以设置全局特征组和活动特征组。

message FeatureGroup {
  repeated Feature features = 1;

  // Supported encoder types: BOW, CNN, LSTM.
  optional EncoderType encoder_type = 2;
}

输入配置

您可以使用输入配置界面同时设置所有特征和特征组。

message InputConfig {
  repeated FeatureGroup global_feature_groups = 1;

  repeated FeatureGroup activity_feature_groups = 2;

  optional Feature label_feature = 3;
}

input_pipeline.pyrecommendation_model.py 会使用输入配置将训练数据处理为 tf.data.Dataset,并相应地构建模型。在 ContexEncoder 内部,我们将为所有特征组创建 FeatureGroupEncoders,并将其用于计算输入特征中的特征组嵌入。通过顶部隐藏层馈送串联特征组嵌入,以获得最终上下文嵌入。值得注意的是,最终上下文嵌入和标签嵌入维度应该相同。

请在 附录 部分查看使用不同输入配置生成的不同模型图。

实验与分析

我们会借此机会,针对基于编号和基于特征的模型,分析其在不同配置下的性能,并提供一些实证结果。

对于基于编号的模型,仅会将 movie_id 用作输入特征。对于基于特征的模型,会使用 movie_idmovie_genre 特征。两种类型的模型都尝试使用了 3 种编码器类型 (BOW/CNN/LSTM) 和 3 种上下文历史长度 (10/50/100)。

基于编号的模型和基于特征的模型之间的比较情况。我们会基于 BOW/CNN/LSTM 编码器和上下文历史长度 10/50/100 对其进行比较

由于 MovieLens 数据集是一个实验数据集,其中大约含有 4000 部候选电影和 19 种电影类型,因此我们会在实验中缩小嵌入维度,以模拟生成场景。对于上述实验结果图表,将编号嵌入维度设置为 8 并将电影类型嵌入维度设置为 4。如果以 context10_cnn 为例,基于特征的模型性能比基于编号的模型性能高出 58.6%。此外,平均结果显示基于特征的模型性能要高出 48.35%。因此,在这种情况中,基于特征的模型性能要优于基于编号的模型,因为 movie_genre 特征向模型引入了其他信息。

此外,候选项目的大部分基础特征的词汇表都相对较小,因此嵌入空间也相对较小。例如,电影类型词汇表要比电影编号词汇表小很多。在这种情况中,使用基础特征会减小模型的内存大小,从而令其更适合设备端。

致谢

特别感谢 Cong Li、Josh Gordon、Khanh LeViet‎、Arun Venkatesan 和 Lawrence Chan 为此项工作提供了有价值的建议。

原文:Adaptive Framework for On-device Recommendation
中文:TensorFlow 公众号