深度树匹配模型(TDM)

算法介绍

Tree-based Deep Match(TDM)是由阿里妈妈精准定向广告算法团队自主研发,基于深度学习上的大规模(千万级+)推荐系统算法框架。在大规模推荐系统的实践中,基于商品的协同过滤算法(Item-CF)是应用较为广泛的,而受到图像检索的启发,基于内积模型的向量检索算法也崭露头角,这些推荐算法产生了一定的效果,但因为受限于算法模型本身的理论限制,推荐的最终结果并不十分理想。近些年,深度学习技术逐渐兴起,在包括如图像、自然语言处理、语音等领域的应用产生了巨大的效果推动。受制于候选集合的大规模,在推荐系统里全面应用深度学习,进行计算存在效果低下的问题。TDM原创性的提出了以树结构组织大规模候选,建立目标(兴趣)的层次化依赖关系,并通过逐层树检索的方式进行用户对目标(兴趣)的偏好计算,从而实现用户目标(兴趣)的最终推荐。无论是在公开数据集上离线测试结果[1],还是在阿里妈妈实际业务的在线测试上,TDM都取得了非常显著的效果提升。

基础知识

写在最前面

本文主要面向推荐系统的学术爱好者,实际从业者,介绍TDM系统的组成原理,TDM开源项目的使用指导,以及如何将TDM算法应用到使用者实际业务中去。下述介绍假定读者具备一定的推荐系统/深度学习的概念了解,基本的数据结构与算法理解,以及基本编程语言(C++,Python)的掌握,如果读者对这些技术的了解存在疑虑,可参考下述知识点进行学习。

需要的前提知识

  • 基本概念
    • 推荐系统:推荐技术提供用户对某个目标(兴趣)的偏好程度预测能力,基于偏好预测排序输出用户喜好的目标(兴趣)集合。
    • 深度学习:基于深度神经网络结构的机器学习算法分支。
  • 数据结构与算法
    • :树是计算机科学中数据结构的经典类型,在数据组织上具有良好的效率。
    • BeamSearch算法:一种启发式的贪心搜索方法。
    • Kmeans聚类算法:一种基于向量量化的无监督聚类算法。
  • 语言基础

文档内容说明

本文旨在介绍TDM以及如何使用TDM开源项目进行实际业务生产所用,阅读完成后,可以了解到:

  • TDM的基本系统组成
  • TDM开源代码的运行和使用
  • 应用TDM到具体实践的方法

受限于篇幅以及主旨,以下内容本文不涉及,或请参阅相关文档:

TDM适用的范围

TDM面向解决的是大规模推荐问题,自主提出的以树方式组织大规模候选建立层次关系进而支撑层次检索的框架,具有普遍的适用性和优秀的实验效果。TDM算法具有极高的业务适用性,在包括如视频推荐、商品推荐、新闻推荐等业务领域已经成功应用,并取得了非常可观的业务效果。更多的业务领域TDM应用实践正在开展中,本文后续会不断更新TDM的业务实践效果。

TDM框架介绍

算法原理

TDM是为大规模推荐系统设计的、能承载任意先进模型来高效检索用户兴趣的推荐算法解决方案。TDM基于树结构,提出了一套对用户兴趣度量进行层次化建模与检索的方法论,使得系统能直接利高级深度学习模型,在全库范围内检索用户兴趣。详细的算法介绍,参见TDM在KDD会议上的论文,其基本原理是使用树结构对全库item进行索引,然后训练深度模型以支持树上的逐层检索,从而将大规模推荐中全库检索的复杂度由O(n)(n为所有item的量级)下降至O(log n)。

系统组成

树结构

树在TDM框架中承担的是索引结构的角色,即全库item都会通过树结构被索引起来。

树中的每一个叶节点对应库中的一个item;非叶节点表示item的集合。这样的一种层次化结构,体现了粒度从粗到细的item架构。此时,推荐任务转换成了如何从树中检索一系列叶节点,作为用户最感兴趣的item返回。值得一提的是,虽然图中展示的树结构是一个二叉树,但在实际应用中并无此限制。

基于树的检索算法

在一些传统的树状索引如二叉搜索树、B-树等结构中,检索过程往往是使用键值在树上进行逐层往下的top 1检索,最终找到一个满足条件的叶节点并返回。而在TDM框架中,基于树结构进行大规模推荐的方法,是每一个用户寻找K个最感兴趣的叶节点。因此,检索过程也做了相应的改变:在逐层往下的检索过程中,每一层都保留K个节点并往下扩展,即经典的BeamSearch方法。这一检索方案兼具效率与精度,剩下的问题是如何得到每层内精准的兴趣判别器,来找到每层内的K个最感兴趣的节点。

深度网络模型

深度网络在TDM框架中承担的是用户兴趣判别器的角色,其输出的(用户,节点)对的兴趣度,将被用于检索过程作为寻找每层Top K的评判指标。由于TDM框架具备高效的剪枝能力,因此其能兼容任意先进的深度网络模型来进行全库检索。

上述网络结构中,在用户特征方面仅使用了用户历史行为,并对历史行为根据其发生时间,进行了时间窗口划分。在节点特征方面,使用的是节点经过embedding后的向量作为输入。此外,模型借助attention结构,将用户行为中和本次判别相关的那部分筛选出来,以实现更精准的判别。

网络与树结构的联合训练

树索引结构在TDM框架中起到了两方面的作用,一是在训练过程提供了上溯正采样样本和平层负采样样本;二是在检索过程中决定了选择与剪枝方案。因此,良好的树结构应该能为训练提供易于区分的上层样本,并尽量体现真实的用户兴趣层级结构来帮助检索。基于数据驱动的索引学习及检索模型、索引结构联合训练的出发点,TDM框架进行了一种尝试:使用学习得到的叶节点(即item)embedding向量进行层次化聚类,来生成新的树索引结构。

检索模型训练、树索引结构学习过程迭代进行,最终得到稳定的结构与模型。

评测指标

TDM主要采用召回率、准确率两种评价指标:

  • 召回率:R = size({预测集合} ∩ {真集}) / size({真集})
  • 准确率:P = size({预测集合} ∩ {真集}) / size({预测集合})

TDM训练示例

单机试验小数据集

用户依照以下步骤可以在随机dummy小数据集上快速验证流程,此dummy数据集为随机生成,仅作快速验证流程使用。如果需要拿到效果,试验一个具有实际意义的数据集(例如UserBehavior数据集)

  • 准备工作目录
WORKPATH='/your/path/work/tdm_mock'    # 自选一个工作目录
git clone --recursive https://github.com/alibaba/x-deeplearning.git
cd x-deeplearning
# git submodule update --init --recursive     # 如果clone时没有--recursive,那么需要执行本行命令
cp -r XDL-Algorithm-Solution/TDM/script/tdm_ub_att_ubuntu/  "$WORKPATH"
  • 进入docker
DOCKER_PATH='registry.cn-hangzhou.aliyuncs.com/xdl/xdl:ubuntu-gpu-mxnet1.3'    # 选择docker镜像地址
sudo docker pull "$DOCKER_PATH"
# 下列命令启动一个名为tdm-mock的容器,当然这个名字可以自行指定,但不能与本机上已有的容器重名
sudo docker run -it --net=host --volume $HOME:$HOME -w $HOME `curl -s http://localhost:3476/docker/cli` --name tdm-mock "$DOCKER_PATH"
# sudo docker exec -ti tdm-mock bash    # 再次进入docker使用该命令,注意tdm-mock应与上述启动容器命令中指定的名字相同
# 用户需要安装并配置HADOOP
source /etc/profile    # 设置hadoop,以及将当前路径添加到PATH
  • 编译安装TDM
apt-get install swig    # 如果已经安装则无需安装
cd  XDL-Algorithm-Solution/TDM/src
mkdir build
cd build
cmake ..
make
cp -r ../python/store/store/ "$WORKPATH"
cp -r ../python/dist_tree/dist_tree/ "$WORKPATH"
cp -r ../python/cluster/ "$WORKPATH"
cp tdm/lib*.so "$WORKPATH"
  • 分隔数据集,生成原始样本csv
cd "$WORKPATH/cluster"
python data_cutter.py     \
  --input mock.dat        \    # 输入数据文件名称
  --train mock_train.csv  \    # 输出训练集数据文件名称
  --test mock_test.csv    \    # 输出测试集数据文件名称
  --number 10                  # 测试集用户数
CSV_HDFSPATH='hdfs://your/rawdata/hdfs/path'    # 指定csv产出目录
hadoop fs -ls "$CSV_HDFSPATH"    # 确认该目录之前不存在,若存在则删掉或换个目录
hadoop fs -mkdir "$CSV_HDFSPATH"
hadoop fs -put mock_train.csv "$CSV_HDFSPATH"
hadoop fs -put mock_test.csv "$CSV_HDFSPATH"
  • 样本产出及树初始化
cd "$WORKPATH"
UPLOAD_HDFSPATH='hdfs://your/dist_tree/upload/hdfs/path/tree_data'    # 指定样本及树的产出目录
hadoop fs -ls "$UPLOAD_HDFSPATH"    # 确认该目录之前不存在,若存在则删掉或换个目录
hadoop fs -mkdir "$UPLOAD_HDFSPATH"
EMB_HDFSPATH='hdfs://your/emb_converted/hdfs/path'    # 指定训练的embedding结果产出目录
hadoop fs -ls "$EMB_HDFSPATH"       # 确认该目录之前不存在,若存在则删掉或换个目录
hadoop fs -mkdir "$EMB_HDFSPATH"
vim data/tdm.json   # train_rawdata_url 修改为 $CSV_HDFSPATH/mock_train.csv 的完整hdfs路径
                    # test_rawdata_url 修改为 $CSV_HDFSPATH/mock_test.csv 的完整hdfs路径
                    # upload_url 修改为 $UPLOAD_HDFSPATH 的完整hdfs路径,此为树及样本生成目录的hdfs路径
                    # model_url 修改为 $EMB_HDFSPATH 的完整hdfs路径,此为训练模型的sparse参数导出文件的hdfs路径
INIT_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_init'    # 指定样本产出及树初始化的ckpt目录
hadoop fs -ls "$INIT_CKPT_HDFS_PATH"
hadoop fs -mkdir "$INIT_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$INIT_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.tree_init.json   # dependent_dirs 修改为 $WORKPATH 的完整路径
                            # docker_image 修改为 $DOCKER_PATH
                            # checkpoint.output_dir 修改为 $INIT_CKPT_HDFS_PATH/checkpoint
python tree_init.py
hadoop fs -ls "$UPLOAD_HDFSPATH/data"
  • 训练
cd "$WORKPATH"
TRAIN_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_train'    # 指定训练的ckpt目录
hadoop fs -ls "$TRAIN_CKPT_HDFS_PATH"
hadoop fs -mkdir "$TRAIN_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$TRAIN_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.train.json   # dependent_dirs 修改为 $WORKPATH 的完整路径
                        # docker_image 修改为 $DOCKER_PATH
                        # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
vim data/tdm.json   # 因为是小数据集,因此参数做些相应调整如下:
                    # parall 修改为 4
                    # train_batch_size 修改为 30000
                    # save_checkpoint_interval 修改为 10
                    # predict_batch_size 修改为 100
                    # tdmop_layer_counts 修改为 0,1,2,3,4,5,6,7,8,9,1,1,1,1,1,1,1,1,2,2,3,7,2
vim train.py    # 修改train的代码中DataIO的参数 namenode="hdfs://your/namenode/hdfs/path:9000",这是样本读取目录的hdfs根结点路径
                # 最下面修改为 is_training=True
python train.py --run_mode=local --config=config.train.json
        # 可能会提示checkpoints找不到,这是正常的寻找ckpt的流程
        # 正常退出则会打印 finish put item_emb
hadoop fs -ls "$TRAIN_CKPT_HDFS_PATH/checkpoint"    # 查看训练保存的ckpt
hadoop fs -ls "$EMB_HDFSPATH"    # 查看生成的 item_emb 文件,大小不应为0
  • 聚类重新生成树
cd "$WORKPATH"
CLUSTER_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_cluster'    # 指定聚类的ckpt目录
hadoop fs -ls "$CLUSTER_CKPT_HDFS_PATH"
hadoop fs -mkdir "$CLUSTER_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$CLUSTER_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.tree_cluster.json    # dependent_dirs 修改为 $WORKPATH 的完整路径
                                # docker_image 修改为 $DOCKER_PATH
                                # checkpoint.output_dir 修改为 $CLUSTER_CKPT_HDFS_PATH/checkpoint
python tree_cluster.py
hadoop fs -ls "$UPLOAD_HDFSPATH/data" 
  • 再次训练

同上

  • 离线评测

在任意一次训练产出ckpt后即可按需进行评测。

cd "$WORKPATH"
TRAIN_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_train'    # 指定训练的ckpt目录用于离线评测
hadoop fs -ls "$TRAIN_CKPT_HDFS_PATH"
vim config.predict.json     # dependent_dirs 修改为 $WORKPATH 的完整路径
                            # docker_image 修改为 $DOCKER_PATH
                            # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
vim train.py    # 修改predict的代码中DataIO的参数 namenode="hdfs://your/namenode/hdfs/path:9000",注意和上面train不是同一处
                # 最下面修改为 is_training=False
                # 因为是小数据集,因此参数做些相应调整如下:
                # key_value["pr_test_each_layer_retrieve_num"] = "40"
                # key_value["pr_test_final_layer_retrieve_num"] = "20"
python train.py --run_mode=local --config=config.predict.json

分布式试验小数据集

  • 准备任务调度目录

可以直接使用上面单机试验小数据集的工作目录,额外需要做的修改都在下面给出。 注意docker应安装并配置HADOOP。

WORKPATH='/your/path/work/tdm_mock'
DISTPATH='/your/path/dist/tdm_mock'
# 将单机运行docker中的工作目录 $WORKPATH 拷贝至分布式任务调度机器上的目录 $DISTPATH ,用于提任务。
cd "$DISTPATH/data"
# 目录中仅保留三个文件 tdm.json、userbehavoir_fc.json、userbehavoir_stat.dat,其余文件删除
# 这些其余文件是单机运行时产出的,虽然不会导致运行结果出错,但提任务时因为要上传整个目录,若不删除则会拖慢上传速度
  • 分隔数据集,生成原始样本csv

该步骤如上单机产出即可,分布式实验时直接使用。

  • 样本产出及树初始化
cd "$DISTPATH"
INIT_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_init'    # 指定样本产出及树初始化的ckpt目录
hadoop fs -ls "$INIT_CKPT_HDFS_PATH"
hadoop fs -mkdir "$INIT_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$INIT_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.tree_init.json   # dependent_dirs 修改为 $DISTPATH 的完整路径
                            # checkpoint.output_dir 修改为 $INIT_CKPT_HDFS_PATH/checkpoint
xdl_submit.py --config config.tree_init.json
  • 训练
cd "$DISTPATH"
TRAIN_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_train'    # 指定训练的ckpt目录
hadoop fs -ls "$TRAIN_CKPT_HDFS_PATH"
hadoop fs -mkdir "$TRAIN_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$TRAIN_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim data/tdm.json   # 因为是小数据集,因此参数做些相应调整如下:
                    # train_batch_size 修改为 3000
                    # save_checkpoint_interval 修改为 100
vim config.train.json   # dependent_dirs 修改为 $DISTPATH 的完整路径
                        # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
                        # 因为是小数据集,因此计算资源申请参数做相应调整如下:
                        #   "worker": {
                        #     "instance_num": 2,
                        #     "cpu_cores": 8,
                        #     "gpu_cores": 1,
                        #     "memory_m": 20000
                        #   },
                        #   "ps": {
                        #     "instance_num": 1,
                        #     "cpu_cores": 8,
                        #     "gpu_cores": 0,
                        #     "memory_m": 16000
                        #   },
vim train.sh    # 最下面修改为 is_training=True
xdl_submit.py --config config.train.json
  • 聚类重新生成树
cd "$DISTPATH"
CLUSTER_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_cluster'    # 指定聚类的ckpt目录
hadoop fs -ls "$CLUSTER_CKPT_HDFS_PATH"
hadoop fs -mkdir "$CLUSTER_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$CLUSTER_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.tree_cluster.json    # dependent_dirs 修改为 $DISTPATH 的完整路径
                                # checkpoint.output_dir 修改为 $CLUSTER_CKPT_HDFS_PATH/checkpoint
xdl_submit.py --config config.tree_cluster.json
  • 再次训练

同上

  • 离线评测

在任意一次训练产出ckpt后即可按需进行评测。

cd "$DISTPATH"
TRAIN_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_mock_train'    # 指定训练的ckpt目录用于离线评测
vim config.predict.json     # dependent_dirs 修改为 $DISTPATH 的完整路径
                            # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
                            # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
                            # 因为是小数据集,因此计算资源申请参数做相应调整如下:
                            #   "worker": {
                            #     "instance_num": 2,
                            #     "cpu_cores": 8,
                            #     "gpu_cores": 1,
                            #     "memory_m": 20000
                            #   },
                            #   "ps": {
                            #     "instance_num": 1,
                            #     "cpu_cores": 8,
                            #     "gpu_cores": 0,
                            #     "memory_m": 16000
                            #   },
vim train.sh    # 最下面修改为 is_training=False
xdl_submit.py --config config.predict.json

分布式试验UB数据集

  • 准备任务调度目录
DOCKER_PATH='registry.cn-hangzhou.aliyuncs.com/xdl/xdl:ubuntu-gpu-mxnet1.3'    # 选择docker镜像地址
DISTPATH='/your/path/dist/tdm_ub_att_ubuntu'
git clone --recursive XDL-Algorithm-Solution.git
# git submodule update --init --recursive     # 如果clone时没有--recursive,那么需要执行本行命令
cp -r XDL-Algorithm-Solution/TDM/script/tdm_ub_att_ubuntu/  "$DISTPATH"
  • 分隔数据集,生成原始样本csv

该步骤见上面。

  • 样本产出及树初始化
cd "$DISTPATH"
UPLOAD_HDFSPATH='hdfs://your/dist_tree/upload/hdfs/path/tree_data'    # 指定样本及树的产出目录
hadoop fs -ls "$UPLOAD_HDFSPATH"    # 确认该目录之前不存在,若存在则删掉或换个目录
hadoop fs -mkdir "$UPLOAD_HDFSPATH"
EMB_HDFSPATH='hdfs://your/emb_converted/hdfs/path'    # 指定训练的embedding结果产出目录
hadoop fs -ls "$EMB_HDFSPATH"       # 确认该目录之前不存在,若存在则删掉或换个目录
hadoop fs -mkdir "$EMB_HDFSPATH"
vim data/tdm.json   # train_rawdata_url 修改为 $CSV_HDFSPATH/mock_train.csv 的完整hdfs路径
                    # test_rawdata_url 修改为 $CSV_HDFSPATH/mock_test.csv 的完整hdfs路径
                    # upload_url 修改为 $UPLOAD_HDFSPATH 的完整hdfs路径,此为树及样本生成目录的hdfs路径
                    # model_url 修改为 $EMB_HDFSPATH 的完整hdfs路径,此为训练模型的sparse参数导出文件的hdfs路径
INIT_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_ub_init'    # 指定样本产出及树初始化的ckpt目录
hadoop fs -ls "$INIT_CKPT_HDFS_PATH"
hadoop fs -mkdir "$INIT_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$INIT_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.tree_init.json   # dependent_dirs 修改为 $DISTPATH 的完整路径
                            # docker_image 修改为 $DOCKER_PATH
                            # checkpoint.output_dir 修改为 $INIT_CKPT_HDFS_PATH/checkpoint
                            # 计算资源申请参数可根据用户实际情况调整
xdl_submit.py --config config.tree_init.json    # 该步骤耗时约1~3小时,与实际计算资源有关
hadoop fs -ls "$UPLOAD_HDFSPATH/data" 
  • 训练
cd "$DISTPATH"
TRAIN_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_ub_train'    # 指定训练的ckpt目录
hadoop fs -ls "$TRAIN_CKPT_HDFS_PATH"
hadoop fs -mkdir "$TRAIN_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$TRAIN_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim data/tdm.json       # train_epochs 设置为样本需要训练的轮数
vim config.train.json   # dependent_dirs 修改为 $DISTPATH 的完整路径
                        # docker_image 修改为 $DOCKER_PATH
                        # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
                        # 计算资源申请参数可根据用户实际情况调整
vim train.py    # 修改train的代码中DataIO的参数 namenode="hdfs://your/namenode/hdfs/path:9000",这是样本读取目录的hdfs根结点路径
                # 最下面修改为 is_training=True
xdl_submit.py --config config.train.json    # 该步骤耗时约10小时或更久,与实际计算资源及样本轮数有关
hadoop fs -ls "$TRAIN_CKPT_HDFS_PATH/checkpoint"    # 查看训练保存的ckpt
hadoop fs -ls "$EMB_HDFSPATH"    # 查看生成的 item_emb 文件,大小不应为0
  • 聚类重新生成树
cd "$DISTPATH"
CLUSTER_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_ub_cluster'    # 指定聚类的ckpt目录
hadoop fs -ls "$CLUSTER_CKPT_HDFS_PATH"
hadoop fs -mkdir "$CLUSTER_CKPT_HDFS_PATH"    # 如果目录不存在,则创建
hadoop fs -rm -r "$CLUSTER_CKPT_HDFS_PATH/checkpoint"    # 否则删除其中的checkpoint目录
vim config.tree_cluster.json    # dependent_dirs 修改为 $DISTPATH 的完整路径
                                # docker_image 修改为 $DOCKER_PATH
                                # checkpoint.output_dir 修改为 $CLUSTER_CKPT_HDFS_PATH/checkpoint
                                # 计算资源申请参数可根据用户实际情况调整
xdl_submit.py --config config.tree_cluster.json    # 该步骤耗时约1~3小时,与实际计算资源有关
hadoop fs -ls "$UPLOAD_HDFSPATH/data" 
  • 再次训练

同上

  • 离线评测

在任意一次训练产出ckpt后即可按需进行评测。

cd "$DISTPATH"
TRAIN_CKPT_HDFS_PATH='hdfs://your/hdfs/path/tdm_ub_train'    # 指定训练的ckpt目录用于离线评测
vim config.predict.json     # dependent_dirs 修改为 $DISTPATH 的完整路径
                            # docker_image 修改为 $DOCKER_PATH
                            # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
                            # checkpoint.output_dir 修改为 $TRAIN_CKPT_HDFS_PATH/checkpoint
                            # 计算资源申请参数可根据用户实际情况调整
vim train.py    # 修改predict的代码中DataIO的参数 namenode="hdfs://your/namenode/hdfs/path:9000",注意和上面train不是同一处
                # 最下面修改为 is_training=False
xdl_submit.py --config config.predict.json    # 该步骤耗时约5-15分钟,与实际计算资源有关

数据处理

本小结会简要描述数据处理阶段主要完成的功能以及输入输出格式,主要包括生成训练样本、测试样本、初始树.

初始数据准备

在进行数据处理之前,先要准备原始训练数据以及原始测试数据, 这两份数据格式均为以逗号分隔的文本文件, 每行文本为一个行为记录. 比如下面的示例数据为id为1的用户的5条点击行为(以逗号分隔的各字段含义为user_id, item_id, category_id, behavior_type, timestamp).

1,2268318,2520377,pv,1511544070
1,2333346,2520771,pv,1511561733

数据处理脚本

  • 生成样本格式

生成的样本为文本文件, 每行为一个行为序列, 格式为:

sample id | group id | features | dense | label | ts
其中features为; 分隔的Key@Value序列

下面是行为序列样本示例:

619706_13|619706_13|train_unit_id@3829251:1.0;item_55@1180190;item_53@2964905;item_54@4871;item_56@2416791;item_57@1420124;item_58@1165085;item_59@33793;item_65@917114;item_64@4080531;item_67@3915603;item_66@511224;item_61@2694865;item_60@5159307;item_63@2638297;item_62@511224;item_69@917114;item_68@1400292;item_52@629303||1.0|
239290_1|239290_1|train_unit_id@4075487:1.0;item_65@4940273;item_64@3654350;item_67@1314642;item_66@1042927;item_69@345076;item_68@2365838||1.0|
273937_1|273937_1|train_unit_id@2137809:1.0;item_65@170544;item_64@3433418;item_67@848255;item_66@2127356;item_69@3699491;item_68@2122609||1.0|
  • 特征配置文件示例
{
  "features": {
    "item_1": {
      "start": 0,
      "end": 10,
      "value": 1
    },
    "item_2": {
      "start": 10,
      "end": 20,
      "value": 1
    }
}

此特征配置文件表示将用户的第0个到第10个行为序列作为一个特征组,命名为item_1;将用户的第10个到第20个行为序列作为一个特征组,命名为item_2;

  • 数据处理流程

数据处理分为数据读取, 生成训练样本, 生成概率统计文件, 生成测试样本, 生成初始化树(PB格式), 整个流程可以用下面的流程图描述.

  • 扩展及修改

数据处理阶段的主要逻辑都在generator.py脚本中, 脚本按上述流程组织成模块, 在需要时候非常易于扩展及修改, 下面简单描述下各流程的主要逻辑及可能的修改.

>> Read And Parse Raw Data

该流程读取并解析原始输入的训练或者测试数据, 并返回一个按列组织的字典, 及每个Key是一个列名, 对应的Value是该列所有的数据(实际存储为一个numpy array). 这部分逻辑比较简单, 主要就是按行读取, 并按逗号(可以扩展为其他分隔符)分割, 并将各列Parse成指定的数据类型. 用户实际使用时候, 可以根据自己的数据格式修改相应的读取及解析逻辑, 比如支持其他的辅助列.

>> Generate Train Sample

这一部分生成用户行为序列, 并写入样本文件中, 样本格式见上文. 这一部分逻辑分为两步: 先将各用户的行为聚合起来, 实际上就是生成一个以用户id为Key, 用户点击Item Id列表为Value的字典, 注意这里会对各用户的点击的Item进行排序, 排序的依据是点击行为发生的时间戳; 完成用户行为聚合后, 再将用户的行为按样本序列长度切割成样本.

>>>Generate Stat Info

该模块的功能为统计各Item在训练样本中出现的概率, 并将其写入统计文件.

Generate Test Sample

该模块逻辑基本和Generate Train Sample相同, 略.

Init Tree

该模块生成初始化树, 实际是一个逐层聚类过程, 将所有的Item按其属性不断的进行二分聚类, 直至每个类只有单个的Item属性, 聚类的最终效果是形成了一颗多层的二叉树, 所有的item均属于叶子节点, 所有的叶子结点从左到右相当于形成了一个有序的序列. 在实际的处理, 我们采用直接按Item的Category进行排序, 并在排序之后不断二分来形成初始化树, 树的格式及存储见下面树构建部分.在用户实际业务中用户可根据自己的业务场景修改相应的排序逻辑以支持不同的树初始化方式.

树构建

树构建阶段主要完成聚类树的生成, 该阶段的输入是根据初始树联合训练生成的item的向量, 并对向量进行聚类生成新的聚类树.

输入数据及格式

树构建阶段的输入是用初始树联合训练产生的向量, 输入的格式为文本格式, 每一行描述一个Item对应的id和向量, 均为数值类型, 各数值之间以逗号分隔, 下面为截选的部分输入数据.

2515040,-0.508644402027,-0.016029631719,-0.20682746172,-0.397063672543,-0.00334448833019,-0.960261583328,0.316593915224,-0.636762738228,-0.217385306954,0.0592824667692,0.35680475831,-0.43331822753,-0.369034737349,0.351467847824,0.0969775170088,0.265370905399,0.0815298631787,-0.389724433422,-0.339153647423,0.273165374994,-0.00598054006696,-0.488672584295,0.405939608812,-0.492451280355
235900,-0.0178719386458,0.117409579456,0.0135170938447,0.208914965391,0.270535558462,-0.295207798481,-0.177082359791,-0.312212228775,0.449806898832,0.338447093964,0.0621097162366,0.327057540417,0.126456350088,0.0875944793224,0.577477931976,-0.351881921291,0.138958856463,-0.538168728352,0.329808682203,-0.239835038781,0.19346319139,0.393561393023,0.111480668187,0.317542433739
3148360,0.359621971846,-0.127544790506,-0.297782152891,-0.368366599083,0.223647251725,-0.104716196656,-0.306075185537,-0.406704396009,0.10038292408,0.712464630604,0.195787191391,-0.0189999304712,-0.146252155304,0.15387006104,-0.297544956207,0.317622750998,0.0184208322316,-0.128658607602,0.0909625515342,-0.0311253629625,0.260530024767,-0.622160255909,0.687025904655,-0.309245109558

生成树存储结构及格式

聚类产生的是一个编码树, 并以Key Value的形式存储在任意分布式或本地Key Value存储中。

其中Key是各个节点的编码, Value则以PB的形式存储各种属性是信息, 节点存储信息的PB描述如下.

message Node {
  required int64 id = 1;
  required float probality = 2;
  required int32 leaf_cate_id = 3;
  required bool is_leaf = 4;
  repeated float embed_vec = 5;
  optional bytes data = 6;
}

在Key Value存储之上, 树提供各种丰富的接口, 可以访问树的祖先, 兄弟, 孩子, 并能按层进行迭代。

深度树匹配模型(TDM)的更多相关文章

  1. DSSM:深度语义匹配模型(及其变体CLSM、LSTM-DSSM)

    导语 在NLP领域,语义相似度的计算一直是个难题:搜索场景下Query和Doc的语义相似度.feeds场景下Doc和Doc的语义相似度.机器翻译场景下A句子和B句子的语义相似度等等.本文通过介绍DSS ...

  2. 深度语义匹配模型-DSSM 及其变种

    转自:http://ju.outofmemory.cn/entry/316660 感谢分享~ DSSM这篇paper发表在cikm2013,短小但是精炼,值得记录一下 ps:后来跟了几篇dssm的pa ...

  3. zz阿里妈妈深度树检索技术(TDM)及应用框架的探索实践

    分享嘉宾:何杰 阿里妈妈 高级算法专家 编辑整理:孙锴 内容来源:DataFun AI Talk 出品社区:DataFun 注:欢迎转载,转载请注明出处 导读:阿里妈妈是阿里巴巴集团旗下数字营销的大中 ...

  4. 基于海明距离的加权平均值人职匹配模型(Sqlserver2014/16内存表实现)

    最近给某大学网站制作一个功能,需要给全校所有的学生提供就业单位发布职位的自动匹配,学生登陆就业网,就可以查看适合自己的职位,进而可以在线投递. 全校有几万名学生,注册企业发布的职位也有上万,如何在很短 ...

  5. 基于PaddlePaddle的语义匹配模型DAM,让聊天机器人实现完美回复 |

    来源商业新知网,原标题:让聊天机器人完美回复 | 基于PaddlePaddle的语义匹配模型DAM 语义匹配 语义匹配是NLP的一项重要应用.无论是问答系统.对话系统还是智能客服,都可以认为是问题和回 ...

  6. LSM树存储模型

    ----<大规模分布式存储系统:原理解析与架构实战>读书笔记 之前研究了Bitcask存储模型,今天来看看LSM存储模型,两者尽管同属于基于键值的日志型存储模型.可是Bitcask使用哈希 ...

  7. 从零开始搭建FAQ引擎--深度语义匹配

    从零开始搭建FAQ引擎--深度语义匹配

  8. 【转】- 从FM推演各深度CTR预估模型(附代码)

    从FM推演各深度CTR预估模型(附代码) 2018年07月13日 15:04:34 阅读数:584 作者: 龙心尘 && 寒小阳 时间:2018年7月 出处: 龙心尘 寒小阳

  9. 基于TORCS和Torch7实现端到端连续动作自动驾驶深度强化学习模型(A3C)的训练

    基于TORCS(C++)和Torch7(lua)实现自动驾驶端到端深度强化学习模型(A3C-连续动作)的训练 先占坑,后续内容有空慢慢往里填 训练系统框架 先占坑,后续内容有空慢慢往里填 训练系统核心 ...

随机推荐

  1. 16- web测试总结

    在线用户不进行任何操作,对服务器也会产生压力.因为有会话的存在. 服务器tps与相应时间没有直接关系:每个口 关键性能指标:TPS.响应时间.并发数.思考时间.资源利用率(内存.cpu.磁盘).pv. ...

  2. 软件篇-05-融合ORB_SLAM2和IMU闭环控制SLAM底盘运动轨迹

      前面我们已经得到了当前底盘在世界坐标系中的位姿,这个位姿是通过融合ORB_SLAM2位姿和IMU积分得到的,在当前位姿已知的case下,给SLAM小车设置一个goal,我这里是通过上位机设置,然后 ...

  3. hdu1287 破译密码

    题意: 破译密码 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Subm ...

  4. hdu4561 连续最大积

    题意: 连续最大积 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total ...

  5. Python中hashlib模块的使用

    hashlib是 python 下一款与加密相关的库包,提供摘要算法:md5.sha1.sha224.sha256.sha384.sha512.blake2b.blake2s.sha3_224.sha ...

  6. 【JavaScript】Leetcode每日一题-平方数之和

    [JavaScript]Leetcode每日一题-平方数之和 [题目描述] 给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c . 示例1: 输入:c = 5 ...

  7. Github + Picgo + Typora 让笔记远走高飞

    Github设置 登录 2.新建仓库 设置token 打开设置 然后点击,按钮生成Generate token Picgo设置 下载PicGo 参考视频 PicGo设置 设置Server 图床设置 上 ...

  8. windows下使用dos命令手工与ntp服务器同步系统时间

    管理员模式的命令窗口 net stop w32time &w32tm /unregister &w32tm /register &net start w32time & ...

  9. 爬虫:获取动态加载数据(selenium)(某站)

    如果网站数据是动态加载,需要不停往下拉进度条才能显示数据,用selenium模拟浏览器下拉进度条可以实现动态数据的抓取. 本文希望找到某乎某话题下讨论较多的问题,以此再寻找每一问题涉及的话题关键词(侵 ...

  10. C++ primer plus读书笔记——第7章 函数——C++的编程模块

    第7章 函数--C++的编程模块 1. 函数的返回类型不能是数组,但可以是其他任何一种类型,甚至可以是结构和对象.有趣的是,C++函数不能直接返回数组,但可以将数组作为结构或对象的组成部分来返回. 2 ...