第三届 Apache Flink 极客挑战赛暨 AAIG CUP 比赛攻略 _ 大浪 813 团队
关联比赛: 第三届 Apache Flink 极客挑战赛暨AAIG CUP——电商推荐“抱大腿”攻击识别
第三届 Apache Flink 极客挑战赛暨 AAIG CUP 比赛攻略_大浪 813 团队
第三届 Apache Flink 极客挑战赛暨 AAIG CUP 自 2021 年 8 月 17 日上线以来已有 4537 个参赛队伍报名。11 月 09 号,大赛复赛结束,我们团队(队伍名称:大浪 813)最终取得了初赛第 7 名,复赛第 8 名的成绩。下面就将我们团队的比赛心得和攻略分享一下,与各位参赛小伙伴共勉。
1.1 赛题说明
1.1.1 赛题背景
随着互联网的发展,网购成为越来越多人的选择,据阿里巴巴财报显示,2020 财年阿里巴巴网站成交总额突破一万亿美元,全球年度活跃消费者达 9.60 亿。
为了满足不同用户的个性化需求,电商平台会根据用户的兴趣爱好推荐合适的商品,从而实现商品排序的千人千面需求。推荐系统常见的召回路径有 U2I(User-Item)、I2I(Item-Item)等。其中,user-to-item 是指通过用户的 profile 信息为用户进行商品的推荐,而 item-to-item 推荐策略则根据用户的商品点击列表为用户推荐关联的商品。
推荐系统的目的是基于不同用户的偏好进行千人千面的推荐。传统的离线推荐系统基于用户历史的行为数据进行加工处理,形成特征样本,然后离线训练模型,并且在线部署进行服务。然而用户的偏好是多元的、用户的行为分布会随着时间而变化,离线的模型无法刻画这种动态的用户偏好,因此需要进行实时的特征更新与模型参数更新,从而能够更好的捕获用户的行为偏好。在推荐场景中,为了更好的提升推荐的时效性与准确性,平台会基于全网的用户行为信息进行实时的 U2I 及 I2I 的更新,并且基于用户最近的行为信息进行相关性的推荐。
为了获取更多的平台流量曝光,将自己的商品展现在更多的消费者面前,部分商家通过 HACK 平台的推荐机制从而增加商品的曝光机会。其中一种典型的手法为“抱大腿”攻击,该方法通过雇佣一批恶意用户协同点击目标商品和爆款商品,从而建立目标商品与爆款商品之间的关联关系,提升目标商品与爆款商品之间的 I2I 关联分。商家通过这种方式诱导用户以爆款的心理预期购买名不符实的商品,不仅损害了消费者的利益,降低其购物体验,还影响了平台和其他商家的信誉,严重扰乱了平台的公平性。因此,我们需要用一个风控系统来过滤掉这些可能的恶意流量,避免它们对推荐系统的模型造成干扰。
由于所有用户行为在输入推荐系统之前,都会首先经过风控系统的过滤,所以如果想要做到推荐系统的实时性,风控系统就必须同样做到实时性。实时拦截此类行为,有助于在保证推荐的时效性的同时,保护实时推荐系统不受恶意攻击影响。
实时风控系统对数据安全的要求较高,如果系统的拦截算法意外泄漏,HACK 平台将得以针对性地加强恶意流量的伪装能力,增大平台监控恶意流量的难度,因此,此类系统有必要部署在加密的可信环境中。
综上所述,为了保障实时推荐系统的准确性,比赛要求选手实现一个保证了数据安全的实时风控系统。本次大赛要求选手基于 Flink,Analytics Zoo/BigDL 等组件,在 Occlum 环境中搭建保护数据安全的 PPML(Privacy Preserving Machine Learning)应用,实现对恶意流量的实时识别。
1.1.2 技术介绍
Apache Flink 是一个在无界和有界数据流上进行状态计算的框架和分布式处理引擎。Flink 已经可以在所有常见的集群环境中运行,并以 in-memory 的速度和任意的规模进行计算。
在 Flink 的基础上,Flink AI Flow 作为兼顾流计算的大数据 + AI 顶层工作流抽象和配套服务,提供了机器学习的端到端解决方案。
Analytics Zoo 及 BigDL 是英特尔®开源的统一大数据分析和 AI 平台,支持分布式 TensorFlow 及 PyTorch 的训练和推理,通过 OpenVINO 工具套件和 DL Boost 指令集等,提升深度学习工作负载的性能。Cluster Serving 是 Analytics Zoo/BigDL 的分布式推理解决方案,可以部署在 Apache Flink 集群上进行分布式运算。
Occlum 是蚂蚁集团基于 Intel SGX 的开源 LibOS,使得 Linux 应用程序在只修改少量代码或者完全不修改代码的情况下运行于 Enclave 安全环境中,保证数据处于加密和强隔离状态,确保数据安全与用户隐私。
1.1.3 数据说明
给定恶意点击、正常点击及对应的“商品”、“用户”相关的属性信息(用户本地调试可以从网上下载),选手实现实时的恶意点击识别分类算法,包括模型训练和模型预测。在大赛评测系统中,系统使用 100 万条数据用于模型训练、10 万条数据用于模型预测。另外,比赛提供给选手 50 万条数据的数据集用于算法的本地调试。
比赛提供如下格式的数据用于训练与预测。所有数据均采用 csv 格式保存在文件中,即以下数据格式的各列之间以逗号分隔。每条数据代表一次用户点击商品的行为,它的特征主要来源于其所关联的用户与商品。
· uuid:每条数据的 id。该 id 在数据集内具有唯一性。
· visit_time:该条行为数据的发生时间。实时预测过程中提供的数据的该值基本是单调递增的。
· user_id:该条数据对应的用户的 id。
· item_id:该条数据对应的商品的 id。
· features:该数据的特征,包含 N 个用空格分隔的浮点数。其中,第 1 ~ M 个数字代表商品的特征,第 M+1 ~ N 个数字代表用户的特征。
· label:值为 0 或 1,代表该数据是否为正常行为数据。
训练数据包括上述所有列的数据,预测数据包括除了 label 之外的所有列。
1.1.4 评分指标
选手提交结果的分数由两方面评分的乘积来决定,两方面分别代表选手提交结果的算法与工程方面的表现。用一个公式表示即如下所示:
score=F1 ∗valid_latency
在算法方面,比赛根据推理结果的 F1 参数来评分,即推理结果的准确率与召回率的调和平均数。
在工程方面,由于比赛模拟实时风控场景,所以比赛对实时推理过程中的延迟做出限制。选手的程序需要为 kafka 中出现的实时数据流提供推理服务,并在数据流的流量不超过给定阈值的情况下,单条数据的延迟不超过 500ms。
选手部署的推理服务需要从 kafka 中读取待推理数据,并将推理结果写入 kafka。数据的延迟的定义即为待推理数据及其推理结果在 kafka 中的时间戳的差值。上述公式中的 valid_latency,即为延迟符合要求的数据占所有数据的比例。延迟超过 500ms 的数据不仅会影响到 valid_latency 的值,进而影响到分数,而且也不会参与 F1 参数的计算过程。
1.1.5 赛题解析
本次赛题注重算法和工程的结合,解答赛题大概要经过以下几个阶段:模型训练、模型评估并选取最优阈值、在线预测。
· 模型训练:大赛给的训练数据都是结构化的,可以直接使用其中的 Feature 字段进行训练。在 大赛 demo 里,构建了一个前向反馈网络进行模型的训练,直接拟合样本的标签;但是如果想取得更好成绩,则必须需要根据 User 和 Item 的时序关系,构造更加有效的动态和组合特征;同时业界有很多成熟的 DNN 模型,选手可以考虑使用更复杂的模型进行训练,或者考虑基于集成学习的方式混合多个模型/策略进行预测;另外,由于实时性 500ms 的要求,过于复杂的模型将引入更大的延迟,因此如何权衡模型的性能与实时性将是模型设计的重要考量点。
· 模型评估并选取最优阈值:由于在线预测使用的是直接判定类别,而不是输出一个概率(这个是非常符合实际业务场景的),因此必须选取一个合理的概率阈值来判定最终类别。由于预测阈值的选取对于模型的线上效果影响特别大,因此一般需要对模型进行评估并确定最优阈值。
· 在线预测并判定类别:为了将训练与预测阶段做到更好的分离,在在线预测阶段,使用的是 cluster serving 的形式,因此预测只需要直接加载训练好的模型,便可以进行预测;在最终输出的时候通过比较当前预测概率与最优阈值的大小,从而确定当前样本的预测类别(是否作弊)
1.2 赛题理解:难点与挑战
由于大赛的评分标准包含了算法的部分(F1)和工程的部分(valid_latency),同时由于大赛最终是以 Docker 镜像方式提交,需要在线完成批量特征构造,模型训练,最优阈值确定,实时特征构造,实时预测等全部流程,因此实际比赛中,工程部分的挑战与算法方面的挑战同样艰巨。因此,我们总结的赛题难点与挑战也包括了算法与工程两大部分。
1.2.1 算法部分:
1 数据分析与特征工程:
也就是如何分析、构造更有效的批量和基于流的动态和组合特征?因为赛题中的 Feature 和 Id 字段都是偏向于静态的特征,同时赛题中没有明显给出 User 和 Item 的动态特征,因此选手需要通过 User 和 Item 的点击时序关系自己建立有效的动态和组合特征。
2 数据处理
数据处理中的难点和挑战包括:
1) 如何有效利用 Unlabel 数据提高模型性能?复赛中 train0.csv 给出的 100 万条数据中,只有 10 万条有 Label,其他数据需要选手自己决断。
2) 如何处理不均衡数据?复赛 train0.csv 中 Label1 的数据与 Label0 的数据比例大致为 2:8。
3 网络结构与训练策略
网络结构与训练策略中的难点与挑战包括:
1) 如何构造更有效的满足实时性要求的网络结构?由于大赛中规定单个数据从 kafka 接收到预测完毕输出到 kafka 之间的耗时必须在 500 毫秒以内,这其中包括了 kafka 输入输出,在线特征构造和预测的全部时间,因此预测的网络结构不能过于复杂,那么如何构造满足实时性要求的网络结构就是一个挑战。
2) 如何选择训练策略?复赛中要求两次预测,因此,到底是训练 1 轮还是训练 2 轮,是全量训练还是增量训练,是否加入预训练就是一个需要权衡和实验的点,因此这也是一个挑战。
4 模型稳定性与预测阈值的选择
1) 神经网络模型的一个挑战就是模型稳定性,那么如何增加模型的稳定性也是本赛的一个挑战。
2) 另外,由于预测阈值的选取对于模型的线上效果影响特别大,因此如何确定预测阶段的概率阈值也是一个挑战。
1.2.2 工程部分:
1 Occlum 中各组件内存分配
如何解决 Occlum 中各组件和各训练阶段的内存不足问题?本次大赛要求从训练到预测的全部流程都需要在 Occlum 中执行,而 Occlum 中的总内存是固定的,按照主办方给出的默认内存配置和大内存配置运行,几乎肯定的是会到处碰到 OutOfMemory 或资源不足的问题,因此如何解决 Occlum 中各组件的内存分配是工程部分遇到的第一个挑战。
2 预测阶段长尾现象:Warmup
如何解决预测初始阶段长尾现象?预测初始阶段的长尾现象指的是预测阶段的初始样本的预测会发生高延迟现象,同时初始样本的高延迟也导致后续样本的预测连带延迟。这是工程部分的第二个挑战。
3 Flink 中实时动态特征的构造
如何在实时性要求下构造基于流的有效的动态特征?如何构造有效的动态特征是算法部分的一个挑战,但是当算法部分解决了这个问题后,就需要在 Flink 实时流中构造动态特征,并且必须满足 500ms 的实时性要求,这也是一个工程挑战。
4 Occlum 内外各组件关键流程可见性
另外,在实际调试中,我们发现 Occlum 内外各组件日志彼此独立,内部运行情况和关键流程的耗时很难分析,同时 Occlum 内的代码很难调试,比如一个 Flink AI Flow 的 bug 就是第一轮训练的日志会被第二轮训练的日志覆盖掉,因此第一轮训练的情况很难通过日志搞清楚,甚至经常遇到第一轮训练成功,第二轮训练 OutOfMemory 导致训练日志中只有 errno 12,而没有其他有用的训练信息。第二个问题就是预测任务只有被真正杀掉或异常退出后,才能看到内部日志,本地调试时很难随时看到预测任务的内部信息。因此怎么能方便调试,方便掌握和分析内部运行情况和关键流程耗时也是工程中的一大挑战。
1.3 方案与实验
针对以上大赛中的难点与挑战,我们做了各种实验,并最终给出了有效的解决方案。
1.3.1 总体架构
下面是我们方案的总体架构。
本次大赛的全部代码都是运行于 Docker 镜像内。Docker 镜像内的组件又分为 Occlum 内的组件和 Occlum 外的组件。Occlum 外的组件包括 Flink AI Flow 和 Kafka,Occlum 内的组件包括训练任务依赖的 TensorFlow 等组件,和预测任务依赖的 Apache Flink 和 Intel Analytics Zoo 等组件。
图中白色背景组件是标准的第三方组件,而绿色的训练任务,橙色的预热任务和紫色的预测任务是我们方案需要编写的组件。我们团队的方案是在 Baseline 基础上加入了橙色的预热任务,预热任务的引入主要是为了解决预测阶段的长尾问题。
Flink AI Flow 负责调度训练任务,预热任务和预测任务的运行,Kafka 负责与预热和预测任务进行数据交互。
下图是我们方案的总体流程,首先是训练任务,训练任务结束后拉起预热任务,预热任务结束后拉起预测任务。
下面是我们方案的具体流程:
1) 训练任务:训练任务首先读取批量数据,通过批量数据构造特征,然后进行训练,训练完毕,对模型进行评估并选择最优预测阈值,最后保存模型和预测阈值。
2) 预热任务:预热任务首先通过 Kafka 读取假的实时流数据,然后在线构造特征,然后通过训练任务保存的模型和预测阈值进行预测,并发送结果数据给 Kafka 输出 Topic。
3) 预测任务:预测任务类似预热任务,首先通过 Kafka 读取真实的实时流数据,然后在线构造特征,然后通过训练任务保存的模型和预测阈值进行预测,并发送最终结果数据给 Kafka 输出 Topic。
预热任务和预测任务只有输入和输出 Kafka 数据部分不同,其他都共用代码。通过预热任务我们最终取得了评分指标中 valid_latency 为 1 的完美结果。
1.3.2 特征工程
下面是我们的特征工程:
特征工程主要包括三个部分:
1) 静态特征:
赛题中的 Feature 和 Id 字段都是偏静态的特征,我们没有过多处理。
2) 动态特征:
赛题中没有给出明显的动态特征,因此各参赛选手需要自行决定自己方案的动态特征。我们方案中的动态特征主要是根据 User 和 Item 的点击时序关系建立的统计特征,比如,对于当前 User 而言,当前 User 在 30 分钟内的点击数(count),点击不同 Item 的个数(UniqueCount),以及点击当前 Item 的点击数。同时对于当前 Item 而言,反之亦然。
另一各动态特征就是 TFIDF 特征,比如 TF:最近 30 分钟内,当前 Item 在当前 User 中的点击数/当前 User 的所有 Item 的点击数;IDF:点击当前 Item 的所有 User 的个数/所有 User 的个数等。
3) 组合特征:
对于组合特征,我们只是简单的引入了 UniqueCount 和 Count 之比,之积,没有更多的精力继续实验。
对于特征工程而言,另一个重要的方面就是时间粒度的选择。我们的做法是从 1 分钟到 1 天中的多种粒度全部实验选取,我们的实验表面,如果使用全部时间粒度的全部动态特征,那么模型的最终效果是最好的。但在在线实时流基础上构造特征时,如果构造全部时间粒度的特征,那么将非常耗时,无法满足实时性 500ms 的要求,因此必须对时间粒度和动态特征进行取舍。
那么怎么取舍呢?如下图所示,最终我们是通过 XGBoost 的特征重要性指标进行了取舍。
1.3.3 数据处理-如何利用 Unlabel 数据提高模型性能
复赛加大了难度,给出的数据中只有 1/10 的数据是有 Label 的,其他是 Unlabel 的,那么怎么利用 Unlabel 数据提高模型性能呢?这个方面,我们做了很多实验。如下:
1) 实验 1:通过数据分析按规则打标。
也就是通过数据分析,我们发现了一些规则,然后利用这些规则打标。下面是数据分析后发现的规则:
Rule: 1 个 UserItem 对如果 Label 为 1,则其以后的相同 UserItem 对的 Label 都为 1
Rule: 基本上每个用户的 Label 不变(变化值约万分之一)
我们根据上面规则打标,打标后的数据量提升很大,但是最终模型效果不好,我们分析这种方案改变了数据分布,使模型更关注于点击量大的用户,损失了多样性
2) 实验 2:通过 Kmeans 聚类算法打标。
通过 Kmeans 聚类算法打标的流程是这样的:通过 Kmeans 算法聚多个类(len(label>=0)/10),如果相同类别中的 Label 数据一致,则将该类别中的 Unlabel 数据打标为一致 Label。
这种方式经过线上测试,感觉最终效果不太稳定,还需要更多实验,但在这个实验过程中,我们又想到了实验 3,因此中断了实验 2。
3) 实验 3:借鉴 GAN 的思想,左右互博打标。
既然使用 Kmeans 做聚类打标,那么是否可以使用神经网络做类似的事情?我们想到了 GAN,即用 Label 数据训练模型,再用模型预测 Unlabel 数据,将其中大概率(0.998)正确数据打标;合并新 Label 数据,继续训练,继续打标。
在这个实验中,我们发现,a)打标轮次不能太多,很容易过拟合; b)打标模型与最终模型相同,导致无法兼顾多样性,因此最终模型的效果并没有大幅提升。因此,我们又想到了实验 4。
查看更多内容,欢迎访问天池技术圈官方地址:第三届Apache Flink 极客挑战赛暨AAIG CUP比赛攻略_大浪813团队_天池技术圈-阿里云天池
评论