system: [SYSTEM]

user: 3. 特征工程 3.1. 特征工程 推荐系统是利用“用户信息”、“物品信息”和“场景信息”这三大部分有价值数据，通过构建推荐模型得出推荐列表的工程系统。在这个系统之中，特征工程就是利用工程手段从“用户信息”、“物品信息”和“场景信息”中提取特征的过程。特征其实是对某个行为过程相关信息的抽象表达。在构建推荐系统特征工程时，我们遵循尽可能地让特征工程抽取出的一组特征，能够保留推荐环境及用户行为过程中的所有“有用”信息，并且尽量摒弃冗余信息的原则。 3.2. 推荐系统中的常用特征 用户行为数据：是人与物之间的“连接”日志。用户行为在推荐系统中一般分为显性反馈行为(Explicit Feedback)和隐性反馈行为(Implicit Feedback)两种，在不同的业务场景中，它们会以不同的形式体现 [ 3 ]，如下表2。 Table 2 The user behavior data of different application syste 业务场景 线性反馈行为 隐性反馈行为 电子商务网站 对商品的评分 点击、加入购物车、购买等 视频网站 对视频的评分、点赞等 点击、播放、播放时长等 新闻类网站 赞、踩等行为 点击、评论等 音乐网站 对歌曲、歌手、专辑的评分 点击、播放、收藏等 表2. 不同业务场景下的用户行为数据 用户关系数据：就是人与人之间连接的记录。也可以分为“显性”和“隐性”两种，或者称为“强关系”和“弱关系”。如下图5。 图5. 社交网络关系图 利用用户关系数据的方式： 1) 将用户关系作为召回层的一种物品召回方式； 2) 也可以通过用户关系建立关系图，使用Graph Embedding的方法生成用户和物品的Embedding； 3) 还可以直接利用关系数据，通过“好友”的特征为用户添加新的属性特征； 4) 甚至可以利用用户关系数据直接建立社会化推荐系统。 属性、标签类数据：直接描述用户或者物品的特征。属性和标签的主体可以是用户，也可以是物品。它们的来源非常多样，大体上包含下表3中的几类。 Table 3 The classification and source of property and label dat 主体 类别 来源 用户 人口属性数据 (性别、年龄、住址等) 用户注册信息、第三方DMP (Data Management Platform、 数据管理平台) 用户兴趣标签 用户选择 物品 物品标签 用户或者系统管理员添加 物品属性(例如，商品的类别、价格； 电影的分类、年代、演员、导演等信息) 后台录入、第三方数据库 表3. 属性、标签类数据的分类和来源 内容类数据：可以看作属性标签型特征的延伸，同样是描述物品或用户的数据，但相比标签类特征，内容类数据往往是大段的描述型文字、图片，甚至视频。如下图6。 图6. 利用计算机视觉模型进行目标检测 场景信息(上下文信息)：描述推荐行为产生的场景的信息。最常用的上下文信息是“时间”、“地点”、“当前所处推荐页面”、“季节”、“月份”、“是否节假日”、“天气”、“空气质量”、“社会大事件”等等。场景特征描述的是用户所处的客观的推荐环境，广义上来讲，任何影响用户决定的因素都可以当作是场景特征的一部分 [ 4 ]。 3.3. 利用Spark解决特征处理问题 Spark是一个分布式计算平台。Spark最典型的应用方式就是建立在大量廉价的计算节点上，这些节点可以是廉价主机，也可以是虚拟的Docker Container (Docker容器)。 3.3.1. Spark架构 Spark程序由Manager Node (管理节点)进行调度组织，由Worker Node (工作节点)进行具体的计算任务执行，最终将结果返回给Drive Program (驱动程序)。在物理的Worker Node上，数据还会分为不同的 partition (数据分片)，可以说partition是Spark的基础数据单元。如下图7。 图7. Spark架构图 3.3.2. Spark工作原理 在Spark平台上处理这个任务的时候，会将这个任务拆解成一个子任务DAG (Directed Acyclic Graph，有向无环图)，再根据DAG决定程序各步骤执行的方法。Spark程序分别从textFile和hadoopFile读取文件，再经过一系列map、filter等操作后进行join，最终得到了处理结果。如下图8。 图8. 某Spark程序的任务有向无环图 3.3.3. One-Hot编码处理类别型特征 所有的特征都可以分为两大类。第一类是类别、ID型特征(以下简称类别型特征)。第二类是数值型特征，能用数字直接表示的特征就是数值型特征。进行特征处理的目的，是把所有的特征全部转换成一个数值型的特征向量，对于数值型特征，直接把这个数值放到特征向量上相应的维度上就可以了。 我们使用One-hot编码(也被称为独热编码)，它是将类别、ID型特征转换成数值向量的一种最典型的编码方式。它通过把所有其他维度置为0，单独将当前类别或者ID对应的维度置为1的方式生成特征向量。 SparrowRecsys使用Spark的机器学习库MLlib来完成One-hot特征的处理。 One-hot编码也可以自然衍生成Multi-hot编码(多热编码)。比如，对于历史行为序列类、标签特征等数据来说，用户往往会与多个物品产生交互行为，或者一个物品被打上多个标签，这时最常用的特征向量生成方式就是把其转换成Multi-hot编码。 3.3.4. 数值型特征的处理——归一化和分桶 当我们把特征的原始数值直接输入推荐模型，就会导致这两个特征对于模型的影响程度有显著的区别，归一化能够解决特征取值范围不统一的问题。“分桶(Bucketing)”，就是将样本按照某特征的值从高到低排序，然后按照桶的数量找到分位数，将样本分到各自的桶中，再用桶ID作为特征值，用分桶的方式来解决特征值分布极不均匀的问题。 3.4. Embedding技术 Embedding就是用一个数值向量“表示”一个对象(Object)的方法。对象可以是一个词、一个物品，也可以是一部电影等等。一个物品能被向量表示，是因为这个向量跟其他物品向量之间的距离反映了这些物品的相似性。更进一步来说，两个向量间的距离向量甚至能够反映它们之间的关系。 Embedding技术对深度学习推荐系统的重要性 Embedding是处理稀疏特征的利器。因为推荐场景中的类别、ID型特征非常多，大量使用One-hot编码会导致样本特征向量极度稀疏，而深度学习的结构特点又不利于稀疏特征向量的处理，因此几乎所有深度学习推荐模型都会由Embedding层负责将稀疏高维特征向量转换成稠密低维特征向量。 Embedding可以融合大量有价值信息，本身就是极其重要的特征向量。相比由原始信息直接处理得来的特征向量，Embedding的表达能力更强，特别是Graph Embedding技术被提出后，Embedding几乎可以引入任何信息进行编码，使其本身就包含大量有价值的信息，所以通过预训练得到的Embedding向量本身就是极其重要的特征向量 [ 5 ]。 3.5. 使用Spark生成Item2vec和Graph Embedding 3.5.1. Item2vec：序列数据的处理 Item2vec是基于自然语言处理模型Word2vec提出的，所以Item2vec要处理的是类似文本句子、观影序列之类的序列数据。在开始Item2vec的训练之前，我们先为它准备好训练用的序列数据。通过处理rating表得到观影序列，rating (评分)的数据表，里面包含了用户对看过电影的评分和评分的时间。如下表4。 因为MovieLens这个rating表本质上只是一个评分的表，不是真正的“观影序列”，所以我们对评分做一个过滤，只放用户打分比较高的电影。样本处理的思路，对一个用户来说，我们先过滤掉他评分低的电影，再把他评论过的电影按照时间戳排序。 序列数据的处理在Spark上实现的步骤： · 读取ratings原始数据到Spark平台； · 用where语句过滤评分低的评分记录； · 用groupByuserId操作聚合每个用户的评分记录，DataFrame中每条记录是一个用户的评分序列； · 定义一个自定义操作sortUdf，用它实现每个用户的评分记录按照时间戳进行排序； · 把每个用户的评分记录处理成一个字符串的形式，供后续训练过程使用。 Table 4 The rating tabl userid movieid rating timestamp 1 2 3.5 1112486027 1 29 3.5 1112484676 1 32 3.5 1112484819 1 47 3.5 1112484727 1 50 3.5 1112484580 1 112 3.5 1094785740 1 151 4.0 1094785734 1 223 4.0 1112485573 1 253 4.0 1112484940 1 260 4.0 1112484826 1 293 4.0 1112484703 1 296 4.0 1112484767 1 318 4.0 1112484798 1 337 3.5 1094785709 1 367 3.5 1112485980 表4. 评级评分表 3.5.2. Item2vec：模型训练 首先是创建Word2vec模型并设定模型参数。我们要清楚Word2vec模型的关键参数有3个，分别是setVectorSize、setWindowSize和setNumIterations。其中，setVectorSize用于设定生成的Embedding向量的维度，setWindowSize用于设定在序列数据上采样的滑动窗口大小，setNumIterations用于设定训练时的迭代次数。这些超参数的具体选择就要根据实际的训练效果来做调整了。 其次，模型的训练过程就是调用模型的fit接口。训练完成后，模型会返回一个包含了所有模型参数的对象。 最后一步就是提取和保存Embedding向量，通过调用getVectors接口就可以提取出某个电影ID对应的Embedding向量，之后就可以把它们保存到文件或者其他数据库中，供其他模块使用。 3.5.3. Graph Embedding：数据准备 我们选择基于随机游走的Graph Embedding方法——Deep Walk方法进行实现，算法流程如下图9。在Deep Walk方法中，需要准备的最关键数据是物品之间的转移概率矩阵。下图是Deep Walk的算法流程图，转移概率矩阵表达了图3(b)中的物品关系图，它定义了随机游走过程中，从物品A到物品B的跳转概率。 生成转移概率矩阵的函数输入是在训练Item2vec时处理好的观影序列数据。输出的是转移概率矩阵，由于转移概率矩阵比较稀疏，因此采用了一个双层Map的结构去实现它。比如说，要得到物品A到物品B的转移概率，那么transfer Matrix (itemA) (itemB)就是这一转移概率。 图9. Deep Walk的算法流程 在求取转移概率矩阵的过程中，我先利用Spark的flatMap操作把观影序列“打碎”成一个个影片对，再利用countByValue操作统计这些影片对的数量，最后根据这些影片对的数量求取每两个影片之间的转移概率。 在获得了物品之间的转移概率矩阵之后，进入图3(c)的步骤，进行随机游走采样 [ 6 ]。 3.5.4. Graph Embedding：随机游走采样过程 随机游走采样的过程是利用转移概率矩阵生成新的序列样本的过程。我们要根据物品出现次数的分布随机选择一个起始物品，之后就进入随机游走的过程。在每次游走时，根据转移概率矩阵查找到两个物品之间的转移概率，然后根据这个概率进行跳转。比如当前的物品是A，从转移概率矩阵中查找到A可能跳转到物品B或物品C，转移概率分别是0.4和0.6，那么我们就按照这个概率来随机游走到B或C，依次进行下去，直到样本的长度达到了我们的要求。根据上面随机游走的过程，我们使用Scala进行了实现。 通过随机游走产生了训练所需的sampleCount个样本之后，后续过程和Item2vec的过程完全一致了，就是把这些训练样本输入到Word2vec模型中，完成最终Graph Embedding的生成。