-->

深度学习推荐系统实战_23_模型实战准备二_模型特征训练样本的处理

你好,我是王哲,欢迎来到模型实战课准备的第二节课。

那这节课我们来聊一聊实战中的模型特征和训练样本的处理。

那我们为什么要花一节课的时间来讲这些呢?因为在推荐模型的课程中,我们的重点在于讲解模型结构的原理和实现。

而要实现并且训练这些模型,我们就必须先解决训练所需的样本和特征的处理问题。

那这节课我们就先来把模型实战的准备工作做完。

具体来说,今天我会带你用spark处理深度学习模型训练所需的样本和特征,再把特征存储到REDS中,这样模型线上服务的时候就可以调用了。

那这个时候你可能会有疑问,我们的深度学习模型会在tencer flow上进行训练。

那为什么我们要用spark来处理特征呢?可不可以直接让它资flow解决所有的事情呢?那这是一个好的问题。

在我们学习具体的技术之前,先解决这个架构上的疑问是很有必要的那在业界的实践中,我们需要记住一个原则,就是让合适的平台做合适的事情。

比如说数据处理是spark的专长,那流处理是flink的专长,构建和训练模型是TNRF flow的专长。

在使用这些平台的时候,我们最好能够用其所长,避其所短。

这也是一个业界的应用。

为什么拥有那么多的模块,那么多的平台的原因。

你可能想说那橙色不漏也可以处理数据啊,那没错,但是他并不擅长大规模分布式的并行数据处理。

在并行数据处理的能力上,TESFLLW很难和动辄拥有几百上千个基点的spark相比。

所以在面对海量数据的时候,如果我们能够利用spark进行数据清洗、数据预处理、特征提取的话,最好的方案就是让spark发挥它的长处承担繁重。

但是相对简单的样本和特征处理的工作为TNRFLO减轻负担。

好了,既然我们决定用spark进行样本和特征的处理,那下一个问题又来了,我们能从movie length的数据集中抽取出什么样的特征呢?那这就要用到我们在特征工程篇中学到的关于推荐系统特征以及相关特征处理方法的知识。

如果你记得还不够扎实,我建议你回去先复习一下。

那movie length数据集中可供我们提取特征的表其实有两个,那分别是movies表和rings表。

它们的数据格式,你可以看一下文稿。

接下来我们就按照物品特征、用户特征、场景特征这三大类推荐系统特征的顺序来看,一看我们能够从两张表中提取出什么样的特征。

那物品特征我们在项目里指的就是电影特征了。

从movies表中,我们可以提取出电影的基本信息,包括movie、 ID、 title、 religion和java.那除此之外,我们还可以利用rings表,为每一个电影提取出一些统计类的特征,包括电影的平均评分、评分标准差等等。

接下来是用户特征。

那乍一看从movies和rating表中,除了user ID,我们好像找不到其他可以直接用的用户信息了。

这个时候千万不要忘了我们之前提到过的用户特征,最重要的部分就是历史行为特征。

所以从用户的评分历史中,我们其实可以提取出非常多有价值特征。

比如,我们可以根据表的历史,除了ratmovie表的电影信息提取出用户统计类的特征,包括用户的评分、准征、用户的平均评分,用户评分标准差、用护好评。

电影的发布,年份的均值,用户好评。

电影的发布年份,标准差,用户最喜欢的电影风格,用户好评,电影的ID等等。

那最后是场景特征,我们能用的场景特征不多,就一个,那就是评分的时间戳。

所以我们把它作为我们代表时间场景的特征,放到了特征工程中。

好了,到这儿我们就梳理完所有的所有的可用的特征。

我把它们总结在了文稿的表格里,供你参考。

那用spark来提取这些特征的总体实现会比较琐碎,所以我就不把全部的代码贴在文稿里了。

你可以参考spark recess项目中的feature engine for reck model对象,那里面包含了所有特征征程的代代码。

这里我只讲几个有代表性的统计性特征的处理方法。

那计算统计型特征的典型方法就是利用spark中的group by操作作用评分,按照照spmoe i征的代象,然后用用greate操作来计算一些统计性特征。

那我在文稿中给出了一段演示代码。

在这段代码中,我们就可以分别使用count,内置具合函数来统计电影评价次数,用everyage函数来统计评分均值,以及使用SDD def函数来计算评分的标准差。

但特征处理的具体过程我们就讲完了。

不过这里我还想多跟你分享一些我的经验。

那一般来说我们不会人为的预设哪个特征有用,哪个特征无用,而是让模型自己去判断。

如果一个特征的加入,没有提升模型效果,我们再去除这个特征。

就像我刚才虽然提取了不少特征,但并不是每一个模型都会使用到全部的特征。

我会根据模型结构、模型效果有针对性的部分使用。

它们在接下来的特程中,我们还会详细探讨不同模型对这些特征的具体使用方法。

那特征提取之后,我们就到了训练模型的步骤了。

那为了训练模型,我们还需做一件事情,那就是生成模型所需的训练样本。

这里我们就要明确两件事情,一是我们的训练样本从哪里来?二是样本的标签是什么?这两件事情都跟我们训练模型的目标有关系。

对于一个推荐模型来说,它的根本任务就是预测一个用户u对一个物品i在场景c下的喜好分数。

所以在训练的时候,我们要为模型生成一组包含UIC的特征和最终真实得分的样本。

那在spirl access中,这样的样本就是根据评分数据,readings联合用户和物品的特征得来的那其中用户特征和物品特征都需要我们提前生成好,然后让他们与readings数据进行交往后生成最终的训练样本。

具体的实现,我也在feature engine for rock model中实现。

好了,你可以先参考我在文稿中给出的关键代码。

这样一来我们就解决了第一个关键问题。

那接着我们来看第二个关键问题,也就是样本的标签是什么?那对于movie length数据集来说,用户对于电影的评分是最直接的标签数据。

因为它就是我们想要预测的用户对于电影的评价。

那所以reinings表中零到五评评数据据,然可以作为样本的标签。

但对于很多业界应用来说,我们基本上不可能拿到他们的评分数据。

更多的会是点击观看购买这些隐性的反馈数据。

所以业界更多会使用CTR预估这种二分类模型去解决推荐问题啊,为了让我们的实践过程更接近真实的应用场景,我也对movie lance数据集进行了进一步的处理。

具体来说,就是把评分大于等于三点五的样本标签标识为一,以为喜欢评分小于三点五的样本标签置为零。

以为不喜欢。

以为一来,我们可以完全把推荐问题转换为CTR预估问题。

那训练模型所需要的样本我们已经得到了。

但是在处理训练样本的时候,还有一个问题我们一定要注意,那就是引入未来信息的问题,它也是我们在实际工作中经常会遇到的问题。

接下来我就跟你详细来聊一聊,那什么叫做未来信息呢?那简单来讲,如果我们在t时刻进行模型的预测,那t加一时刻以及之后的信息就是未来的信息。

那这个问问题在模型线上服务的时候是根本不存在的。

因为未来的事情还没有发生,我们不可能知道。

但是在离线训练的时候,我们就很容易犯这样的错误。

那比如说我们利用t时刻的样本进行训练,但是使用了全量的样本生成特征。

那这些特征就肯定包含了t加一时刻的未来信息。

这就是一个典型的引入未来信息的错误例子啊,这样说可能还有点抽象。

我们用刚才讲过的特征。

举个例子,刚才我们说到有一个用户特征叫做用户平均评分。

我们通过用户评论过的电影评分取均值来得到它。

那假设一个用户今年评论过三部电影,分别是十一月一日评价的电影,a评分为三分。

十一月二日评价电影b评分为四分。

十一月三日评价电影c评分为五分。

如果让你利用电影b这条评价记录生成样本,样本中的user averagating这个特征的值应该取多少呢?那有的同学会说啊,应该去评价过的电影评分的均值啊,对吧?那就是三加四加五除以三等于四分,应该取四分,对吧?那这就错了,因为在样本b发生的时候,样本c还没有产生,它属于未来信息。

你怎么能把c的评分也加进去呢?而且样本b的评分也不应该加进去,因为user every rating指的是历史评分的均值。

B的评分是我们要预估的值,也不可以加到历史的评分中去。

所以正确答案是三分,我们只能考虑电影a的评分。

因此,在处理历史行为相关的特征的时候,我们一定要考虑未来信息的问题。

类似的还有用户评分总数,用户评分标准差,用户最喜欢的电影风格、用户好评、电影ID等一系列的特征。

那在spark中,我们应该如何处理这些跟历史行为相关的特征呢?这就需要用到window函数了。

那比如说我们在生成user average rating这个特征的时候,就使用了文稿中的代码进行生成。

那结合代码我们可以看到代表中有个有一个叫做over window partition by user ID的操作。

它的意思是说,在做rating平均这个操作的时候,我们不要对user ID下面所有的评分去均值,而是要创建一个滑动窗口。

因为这个用户下面的评分按照时间排序,再让这个滑动窗口一一滑动滑动窗口的位置始终在当前rating的前一个rating的位置。

那这样我们在对滑动窗口内的分数做平均,但不会引入未来信息了。

类似的操作,我使用在了所有与历史行为有关的特征中。

你也可以在项目中的源码中看到好了,到这儿我们特征和训练样本处理的部分就都讲完了。

但还有一个问题,我们需要解决就是特征和训练存储问题。

因为训练样本是供离线训练使用的,而线上模型推断则要使用线上特征。

那好在特征数据库ready已经为我们提供了解决方法。

我们把用户特征和物品特征分别存入REDI.那线场推断的时候,再把所需的用户特征和物品特征分别取出map成模型所需的特征向量就可以了。

那feature engine for reck model中的以了那exsave user features to REDI函数给出了详细的EEIS操作。

我把其中的关键代码放在了文稿里。

我们可以看到代码中使用了REDIS的一个新的操作h set,那它的作用是把一个map存入RE lies,那这样有做有什么好处呢?那对于这里的用户特征来说,map中存储的就是特征的键值。

对,又因为这个map本身就是user ID这个键的value它的值,所以每个user ID就拥有了一组用户特征。

这样一来,我们就可以在推荐服务器的内部,通过user ID来取出所有对应的用户特征了。

当然,物品特征的存储方式是一样的。

到这里,我们完成了所有特征工程相关的准备工作,为之后的模型训练做好了充足的准备。

接下来我们再带你对今天的重点内容做个总结。

那这节课我们明白了,选择read a spark作为特征和样本处理的平台的源。

好,再说一遍,这节课我们明白了选择spark作为特征和样本处理的平台的原因,那就是spark更擅长海量数据的分布式处理,可以为tenence flow减轻数据处理的负担。

在选择具体特征的过程中,我们遵循了物品特征、用户特征、场景特征、这三大类特征分类的方式啊,根据movie lenth的rings表和movies表完成了特征的抽取。

而在样本处理的过程中,我们选用评分和根据评分生成的好评差评标识作为样本标签,并且还根据reinings表的每条数据,通过联合物品和用户数据生成训练样本。

那在训练样本的生成中,我们要特别的关注未来信息的问题。

解决的办法就是利用spark中的window函数滑动生成历史行为相关的特征。

最后,我们利用REDS的h side操作,把线上推断用的特征存储到REDS.那这些重点内容我也都总结在了一张表格里,放在了文稿中,你可以去看一看。

好了,那我们趁热打铁,再来看一道思考题。

为了避免引入未来信息,咱们课程中讲到了基于user id d的window函数的方法。

那你觉得还有哪些方法也能避免引入未来信息吗?好,欢迎把你的思考和答案写在留言区。

如果你觉得今天的内容对你有帮助,也欢迎你分享给你的朋友或同事。

我们下节课见。