# Paddle-TDM-DEMO 本代码仅作tdm组网示例,使用fake数据集,用于快速调研paddle-tdm。 ## 代码结构 ## 树结构的准备 ### 名词概念 为防止概念混淆,让我们明确tdm中名词的概念:

- **item**:具有实际物理含义,是我们希望通过tdm检索最后得到的结果,如一个商品,一篇文章,一个关键词等等,在tdm模型中,item位于树的叶子节点。item有其自身的ID,我们姑且称之为 `item_id`。 - **节点(node)**:tdm树的任意节点。我们完成聚类后,会生成一颗树,树的叶子节点对应着item。而非叶子节点,则是一种类别上的概括,从大方向的兴趣,不断细分到小方向的兴趣。我们希望这棵树的结构满足最大堆树的性质。同样,节点也有其自身的ID,我们称之为node_id。如上图,最左下方的节点,它的node_id是14,而对应的item_id是0. - **Node-Embedding**:注意,此处的Embedding,并非我们已有的item-embedding,而是构建完成的树的节点对应的Embedding,由item-embedding通过规则生成,是我们的网络主要训练的目标。ID范围为所有0->节点数-1。我们同时也需准备一个映射表,来告诉模型,item_id到node_id的映射关系。 - **Travel**:是指叶子节点从root开始直到其自身的遍历路径,如上图,14号节点的Travel:0->1->3->7->14 - **Layer**:指树的层,如上图,共有4层。 > Paddle-TDM在训练时,不会改动树的结构,只会改动Node-Embedding。 ### 树的准备流程 让我们以上图给出的简单树结构为例,来介绍TDM的模型准备流程。假设我们已经完成了树的聚类,并得到了如上图所示的树结构: - 问题一:叶子节点的Embedding值是多少?答:叶子节点的node-embedding与对应的item的embedding值一致 - 问题二:非叶子节点的Embedding值是多少?答:非叶子节点的Embedding值初始化目前有两种方案:1、随机初始化。2、使用其子节点的emb均值。 - 问题三:树一定要求二叉树吗?答:没有这样的硬性要求。 - 问题四:若有item不在最后一层,树不平衡怎么办?答:树尽量平衡,不在最后一层也没有关系,我们可以通过其他方式让网络正常训练。 - 问题五:是每个用户都有一棵树,还是所有用户共享一颗树?答:只有一棵树,通过每一层的模型牵引节点的emb,使其尽量满足最大堆性质。 完成以上步骤,我们已经得到了树的结构,与每个节点的全部信息,现在让我们将其转换为Paddle-TDM训练所需的格式。我们需要产出四个数据: #### Layer_list:记录了每一层都有哪些节点。训练用 ```bash # Layer list 1,2 3,4,5,6 7,8,9,10,11,12,13 14,15,16,17,18,19,20,21,22,23,24,25 ``` #### Travel_list:记录每个叶子节点的Travel路径。训练用 ```bash # Travel list 1,3,7,14 1,3,7,15 1,3,8,16 ... 2,5,12,25 2,6,13,0 ``` #### Tree_Info:记录了每个节点的信息,主要为:是否是item/item_id,所在层级,父节点,子节点。检索用 ```bash 0,0,0,1,2 0,1,0,3,4 0,1,0,5,6 0,2,1,7,8 ... 10,4,12,0,0 11,4,12,0,0 ``` #### Tree_Embedding:记录所有节点的Embedding表,格式如正常表。训练及检索用 以上数据设计的初衷是为了高效,在paddle网络中以Tensor的形式参与训练,运行时,不再需要进行频繁的树的结构的遍历,直接根据已有信息进行快速查找与训练。以上数据可以明文保存,但最终都需要转成ndarray,参与网络的初始化。 结合示例树,数据可以组织如右,下面介绍一些细节: - Layer list从第2(index=1)层开始即可,因为0号节点不参与训练也不参与检索; - Travel list的按照item_id的顺序组织,如第一行对应着item_id=0的遍历信息,同样,也不需要包含0号节点; - Travel_list每行的长度必须相等,遇到不在最后一层的item,需要padding 0 直至长度和其他item一致; - Tree_info包含了0号节点的信息,主要考量是,当我们拿到node_id查找其信息时,可以根据id在该数据中寻找第id行; - Tree_info各列的含义是:itme_id(若无则为0),层级Layer,父节点node_id(无则为0),子节点node_id(若无则为0,若子节点数量不满,则需要paddding 0) ## 数据准备 如前所述,若我们关心的是输入一个user emb,得到他所感兴趣的item id,那我们就准备user_emb + 正样本item的格式的数据,负采样会通过paddle的tdm_sampler op得到。数据的准备不涉及树的结构,因而可以快速复用其他任务的训练数据来验证TDM效果。 ```bash # emb(float) \t item_id (int) -0.9480544328689575 0.8702829480171204 -0.5691063404083252 ...... -0.04391402751207352 -0.5352795124053955 -0.9972627758979797 0.9397293329238892 4690780 ``` ## TDM网络设计 假设输入数据是 Emb + item_id,下面让我们开始介绍一个最简单的网络设计。

上图给出了一个非常简单的TDM示例网络,没有添加任何复杂的逻辑,纯用DNN实现。 TDM的组网,宏观上,可以概括为三个部分 - 第一部分,输入侧的组网,如果想要对user/query进行一些预处理,或者添加Attention结构,通常都是在这一层次实现。 - 第二部分,每层的输入与节点信息交互的组网,这一部分是将user/query的信息与node信息结合,在树的不同层下,进行不同粒度兴趣的学习。通常而言,第一部分与第二部分具有紧密的联系,可以统一为一个部分。 - 第三部分,最终的判别组网,将每层交互得到的信息进行最终的概率判决。但这一层也不是必须的,并不要求所有层的信息都经过一个统一的分类器,可以各层拥有独立的概率判决器。为了逻辑划分更加清晰,我们在示例中添加了这个层次的组网,方便您更加直观的理解tdm网络。 再次强调,该示例组网仅为展示tdm的基本运行逻辑,请基于这个框架,升级改进您自己的网络。 ### TDM代码细节