一般来说,一个典型的推荐系统正如下图所描述的那样,主要由数据预处理、召回模型开发和优化、排序模型开发和优化等等。举例来说,在召回阶段,一般会使用协同过滤,基于图的方法,甚至是基于神经网络的方法来做用户和商品的匹配;而在排序和重排阶段,一些业务指标,比如CTR、CVR、Novelty等,会被机器学习模型来直接建模。对于一个算法工程师来说,日常优化工作的重点基本都集中在离线数据和模型上面。在这里,我们会使用 MetaSpore
来介绍基本的数据预处理、召回、排序等模块,来展示利用我们的系统如何快速构建一个工业级推荐系统。
在这个Demo的项目里,我们使用 MoiveLens-1M 这个数据集来演示。此外,为了给每个电影一个真实的IMDB超链接,我们使用了另外一个 github项目 中处理好的数据 ml-25m-imdb 。您可以从上面提供的网址中下载项目所需要的数据,并存储在您的云端 S3 存储上。
在我们深入到具体的数据和模型开发工作之前,我们需要通过给出的 YAML
配置模版对不同阶段的配置文件进行初始化,主要是替换模版中一些需要定制的变量。举例来说,我们需要替换自己具体的 S3 路径 MY_S3_BUCKET
:
export MY_S3_BUCKET='your S3 bucket directory'
envsubst < template.yaml > output.yaml
在后面我们运行 python 脚本之前,我们假设大家已经完成了初始化模型配置文件的工作。
在这个阶段,我们主要做 4 件事情,分别是:训练集和测试集的划分,组织训练样本,生成模型训练需要的离散特征和连续特征,初始化用户特征、电影特征以便于存储与 MongoDB
中。
python fg_movielens.py --conf fg.yaml
当我们执行完以上脚本之后,我们需要使用 Spark
用户和电影特征灌到 MongoDB
中:
spark-submit \
--master local \
--name write_mongo \
--packages org.mongodb.spark:mongo-spark-connector_2.12:3.0.1 \
dump/write_mongo.py --origin items --dest item --queryid movie_id
spark-submit \
--master local \
--name write_mongo \
--packages org.mongodb.spark:mongo-spark-connector_2.12:3.0.1 \
dump/write_mongo.py --origin users --dest user --queryid user_id
spark-submit \
--master local \
--name write_mongo \
--packages org.mongodb.spark:mongo-spark-connector_2.12:3.0.1 \
dump/write_mongo.py --origin item_feature --dest item_feature --queryid movie_id
当然,我们可以根据自己场景的需要,来更改这些输入、输出路径。
在这个阶段,我们主要介绍 3 中召回算法,包括 Item CF
, Swing
, Two-Twoer
,这些算法的执行过程会在接下来进行详细的说明。
首先,我们运行 Item CF
的训练脚本:
python item_cf.py --conf item_cf.yaml
这条命令执行结束之后,我们算法中计算的相似度 I2I 矩阵会被保存到 item_cf_out_path
这个路径下,其中 item_cf_out_path
是 item_cf.yaml
中到配置项。现在我们可以像之前的步骤那样,将我们计算的结果灌到 MongoDB
中。
spark-submit \
--master local \
--name write_mongo \
--packages org.mongodb.spark:mongo-spark-connector_2.12:3.0.1 \
dump/write_mongo.py --origin itemcf --dest itemcf --queryid key
首先,我们可以运行Swing的训练脚本:
python swing.py --conf swing.yaml
在这条命令执行结束之后,我们算法中计算的相似度 I2I 矩阵会被保存到 item_cf_out_path
这个路径下,其中 item_cf_out_path
是 item_cf.yaml
中到配置项。现在我们可以像之前的步骤那样,将我们计算的结果灌到 MongoDB
中。
spark-submit \
--master local \
--name write_mongo \
--packages org.mongodb.spark:mongo-spark-connector_2.12:3.0.1 \
dump/write_mongo.py --origin swing --dest swing --queryid key
SimpleX
算法是一种简单并鲁棒的双塔模型的实现,我们可以运行 SimpleX
训练脚本:
python simplex.py --conf simplex.yaml
在执行完这条命令之后,我们电影的 embedding 向量会被自动输出到 Milvus
到数据库中,其中有关 Milvus 服务的配置,比如域名、端口等详细的配置在 simplex.yaml
文件中。
在排序阶段,我们把个性化排序问题建模成 CTR 预估
问题,这种建模方法已经在业界被广泛采用。这里我们展示两种模型,如何在 MetaSpore
中使用:第一种是经典的树模型,我们以LightGBM
为例,第二种是是神经网络模型,我们以 Wide & Deep
为例。
如前文所述,我们这里使用 LightGBM
模型来解决排序问题,我们可以使用一下训练脚本:
python lgbm_model_train.py --conf lgbm.yaml
这里需要注意的是,当我们树模型训练完成之后,我们使用一下代码转化成 ONNX 格式,以便于 MetaSpore Serving
加载并进行线上预测。
def convert_model(lgbm_model: LGBMClassifier or Booster, input_size: int) -> bytes:
initial_types = [("input", FloatTensorType([-1, input_size]))]
onnx_model = convert_lightgbm(lgbm_model, initial_types=initial_types, target_opset = 9)
return onnx_model
如前文所述,我们这里使用 Wide & Deep
模型 来演示在 MetaSpore
平台进行分布式训练的能力,我们可以运行一下训练脚本:
python widedeep.py --conf widedeep.yaml
在上面脚本执行结束之后,ONNX 格式的模型文件已经被自动导出到 S3 的路径: model_export_path
,而这个路径变量在 widedeep.yaml
文件中配置。
最后,对于 MetaSpore
中开发的模型,如 Item CF
,Swing
,Wide & Deep
等,我们实现了一个轻量级的超参数搜索工具,我们这里以 Wide & Deep tuner 配置来说明如何使用:
app_name: Wide&Deep CTR Model Tuner
num_experiment: 3
model_name: widedeep
result_path: ${MY_S3_DIR}/tuner/model/movielens/widedeep/
dataset:
train: ${MY_S3_DIR}/movielens/rank/train.parquet
test: ${MY_S3_DIR}/movielens/rank/test.parquet
common_param:
local: False
column_name_path: ${MY_S3_DIR}/movielens/schema/widedeep/column_schema
combine_schema_path: ${MY_S3_DIR}/movielens/schema/widedeep/combine_column_schema
...
hyper_param:
use_wide: [True, False]
embedding_size: [10, 20]
deep_hidden_units: [[1024, 512, 256, 128, 1], [1024, 512, 1]]
adam_learning_rate: [0.00001, 0.0001, 0.001]
...
- 我们在配置文件中需要对一些基本的配置项进行定义,包括
app_name
,num_expriment
等; - 我们在配置文件中需要对
dataset
的路径进行定义; - 我们在配置文件中需要对
common_param
来配置哪些模型参数是不希望在本次实验中进行搜索; - 我们在配置文件中需要对
hyper_param
来配置哪写模型参数是希望在在本次实验中进行搜索的。
在以上 YAML
文件被完善的配置之后,我们可以运行:
python widedeep_tuner.py --conf widedeep_tuner.yaml
当这条命令运行完成之后,超参数寻优的结果被保存在 YAML
文件中定义的 result_path
路径中。