反欺诈因素的计算。Yandex报告

反欺诈是一项服务,用于发现和消除利用其他公共Yandex服务的案例。三年前,我们开始设计一个平台,使您可以快速轻松地在公司的任何位置部署反欺诈。任务的复杂性在于,许多服务需要最严格的速度,可靠性和质量保证。其中一些对大量数据进行操作。反欺诈团队对系统的灵活性,易于支持以及机器学习所基于的因素的表达能力感兴趣。





反欺诈头Andrey Popov Nox_andry介绍了我们如何能够满足所有这些相互矛盾的要求。该报告的中心主题是一个模型,用于计算数据流上的复杂因素并确保系统容错能力。Andrey还简要介绍了我们目前正在开发的下一个甚至更快的反欺诈迭代。



反欺诈团队从根本上解决了二进制分类问题。因此,该报告不仅会吸引反欺诈专家,而且还会吸引那些制作各种需要大量数据的快速,可靠和灵活因素的系统的人。



-嗨,我叫安德烈。我在Yandex工作,负责反欺诈的开发。有人告诉我人们更喜欢使用“功能”一词,因此在整个报告中我都会提到它,但是标题和引言保持不变,只是“因素”一词。



什么是反欺诈?



反欺诈是什么?它是一个保护用户免受服务负面影响的系统。负面影响是指故意采取的措施,这些措施可能会使服务质量下降,从而使用户体验恶化。这些可能是相当简单的解析器和机器人,会使我们的统计数据恶化,也可能是故意复杂的欺诈活动。当然,第二个更难定义,也更有趣。



反欺诈与什么作斗争?几个例子。



例如,模仿用户动作。这是由我们称为“黑色SEO”的人完成的-那些不想提高网站质量和网站内容的人。相反,他们编写了前往Yandex搜索的机器人,请在其网站上单击。他们希望自己的网站能以这种方式上升。以防万一,我提醒您,这些行为与用户协议相抵触,并可能导致Yandex采取严厉制裁措施。







或者,例如,作弊评论。可以从放置塑料窗户的地图组织上看到这样的评论。她自己为此评论付费。



顶级反欺诈体系结构如下所示:某些原始事件集像黑盒子一样落入反欺诈系统本身。在其出口处,将生成标记事件。



Yandex提供许多服务。所有这些人,特别是大型的人,以一种或另一种方式面临着不同类型的欺诈。搜索,市场,地图以及其他数十种内容。



我们两三年前在哪里?每个团队都尽其所能在欺诈的冲击下生存下来。她组建了反欺诈团队,但系统运行并不总是很好,因此与分析师进行互动时不太方便。最重要的是,它们彼此之间的集成度很差。



我想告诉您我们如何通过创建一个平台来解决此问题。







为什么我们需要一个平台?重用经验和数据。将经验和数据集中在一处,使您可以更快,更好地应对大型攻击-它们通常是跨服务的。



统一工具包。人们拥有习惯的工具。显然是连接速度。如果我们启动了当前受到主动攻击的新服务,那么我们必须快速将高质量的反欺诈与其连接起来。



可以说,我们在这方面不是唯一的。所有大公司都面临类似的问题。与我们交流的每个人都将创建他们的单一平台。



我会告诉您一些有关我们如何对欺诈进行分类的信息。







这可能是一个离线系统,需要花费数小时,数天和繁重的离线过程:例如,复杂的集群或复杂的重新培训。我几乎不会在报告的这一部分上作任何论述。在几分钟内,就有了一个接近实时的部分。这是一种中庸的手段,她反应敏捷,方法繁重。首先,我将重点放在她身上。但同样重要的是,在此阶段我们正在使用上一阶段的数据。



在需要快速响应的地方,也需要在线部分,即使在我们收到事件并将其传递给用户之前,清除欺诈也至关重要。在这里,我们再次重用更高级别的数据和机器学习算法。



我将讨论这个统一平台的工作原理,用于描述功能和与系统交互的语言,关于我们提高速度的途径,也就是从第二阶段到第三阶段的过渡。



我几乎不会涉及ML方法本身。基本上,我将讨论创建功能的平台,然后在培训中使用这些功能。



谁可能对此感兴趣?显然,对于那些撰写反欺诈或打击诈骗者的人。ML还认为,对于那些刚刚开始数据流并阅读功能的用户。由于我们已经建立了一个相当通用的系统,因此您可能会对其中的一些感兴趣。



系统要求是什么?其中有很多,其中一些是:



  • 大数据流。我们在五分钟内处理了数亿个事件。
  • 完全可配置的功能。
  • .
  • , - exactly-once- , . — , , , , .
  • , , .


此外,我将分别向您介绍这些要点。



由于出于安全原因,我无法谈论真实的服务,因此让我们介绍一种新的Yandex服务。实际上,不,算了,这是我想出的虚构服务,用于展示示例。让它成为人们拥有所有现有书籍数据库的服务。他们进来,给评分从1到10,攻击者想影响最终评分,以便购买他们的书。



当然,与真实服务的所有巧合都是随机的。首先让我们考虑一下接近实时的版本,因为在这里,第一个近似值并不是特别需要在线。



大数据



Yandex具有解决大数据问题的经典方法:使用MapReduce。我们使用自己的MapReduce实现(称为YT)。顺便说一句,马克西姆·阿赫梅多夫今晚有一个关于她故事。您可以使用您的实现或像Hadoop这样的开源实现。



为什么我们不立即使用在线版本?并非总是需要它,它会使过去的重新计算复杂化。如果我们添加了新算法,新功能,我们通常希望重新计算过去的数据,以更改其结论。使用重量级方法比较困难-我认为这很清楚。出于多种原因,在线版本对资源的要求可能更高。



如果我们使用MapReduce,则会得到类似的信息。我们使用一种迷你批处理,即,将批处理分成尽可能小的块。在这种情况下,是一分钟。但是那些使用MapReduce的人知道,小于这个大小,可能系统本身的开销已经太大了。按照惯例,它将在一分钟内无法处理。



接下来,我们在这组批次上运行化简集,并获得标记的批次。







在我们的任务中,通常需要计算特征的确切值。例如,如果我们要计算用户上个月阅读过的确切书籍数量,则我们将为每个批次计算该值,并且必须将所有收集的统计信息存储在一个位置。然后从中删除旧值并添加新值。



为什么不使用粗略计数方法?简短的答案:我们也使用它们,但有时在反欺诈问题中,一定间隔内的准确值非常重要。例如,对于某些方法,阅读的两本书和三本书之间的差异可能非常明显。



因此,我们需要一个大数据历史记录,用于存储这些统计数据。







让我们尝试“正面”。我们有一分钟的时间,讲述一个古老的故事。我们将其放入Reduce输入中,并输出更新的历史记录和标记的日志数据。



对于那些使用MapReduce的人来说,您可能知道这可能效果很差。如果历史记录可以比批处理本身大数百倍,甚至数千倍,那么这种处理可以与历史记录的大小成比例,而不是与批处理的大小成比例。







让我们用一些键值存储代替它。这也是我们自己的实现,即键值存储,但是它将数据存储在内存中。可能最接近的类似物是某种Redis。但是我们在这里有了一点优势:键值存储的实现与MapReduce及其运行所在的MapReduce集群紧密集成。事实证明,它们之间具有便利的事务性和便利的数据传输。



但是通常的方案是,在该Reduce的每个工作中,我们将转到该键值存储,更新数据并在对它作出判断后将其写回。



我们最终遇到了一个故事,该故事只处理我们需要的键并易于扩展。



可配置功能



关于我们如何配置功能的一些知识。简单的计数器通常是不够的。要搜索诈骗者,您需要各种功能,需要一个智能且方便的系统来对其进行配置。



让我们将其分为三个步骤:



  • 提取,我们从日志中提取给定键的数据。
  • 合并,我们将这些数据与历史记录中的统计信息合并。
  • 构建,我们在其中形成功能的最终价值。


例如,让我们计算用户阅读的侦探故事的百分比。







如果用户阅读太多侦探故事,则他会太可疑。尚不清楚他能指望什么。然后,提取就是删除用户在该批次中阅读的侦探数量。合并-将所有侦探从批次中的所有数据中收集一个月。而构建是一定数量的。



然后,我们对他读过的所有书的价值都做同样的事情,最后我们进行了除法运算。



如果我们要计算不同的值(例如,用户阅读的不同作者的数量)怎么办?











然后,我们可以得出该批次中用户已阅读的不同作者的数量。此外,存储一些结构,以便在用户阅读作者时将它们与作者联系起来。因此,如果我们再次在用户处遇到该作者,那么我们这次将进行更新。如果我们需要删除旧事件,我们知道要删除什么。要计算最终功能,我们只需计算其中的键数即可。







但是在嘈杂的信号中,这些特征不足以进行一次切割,因此我们需要一种用于粘合其连接的系统,将这些特征从不同的切割中粘合出来。



例如,让我们介绍这样的削减方法:用户,作者和体裁。







让我们计算一些困难。例如,平均作者忠诚度。忠诚度是指阅读作者的用户-他们几乎只阅读作者。而且,该平均值对于阅读该文档的用户所阅读的作者的平均数量而言非常低。



这可能是潜在的信号。当然,他可以表示作者就是这样:他周围只有粉丝,每个读他的人都只会读他。但这也可能意味着作者本人正在试图欺骗该系统,并创建这些假冒的用户,这些用户本应阅读该系统。



让我们尝试计算一下。我们来计算一下一个功能,该功能可以统计长时间内不同作者的数量。例如,这里的第二个和第三个值对我们来说似乎很可疑,它们太少了。







然后,我们计算在较大间隔内关联的作者的平均值。然后平均值再次很低:3.由于某种原因,该作者对我们似乎很怀疑。







我们可以将其返回给用户,以了解该特定用户与作者之间的联系,这对我们来说似乎很可疑。



显然,这本身不能成为明确的准则,即应该对用户进行过滤或类似的操作。但这可能是我们可以使用的信号之一。



如何在MapReduce范例中做到这一点?让我们进行几个连续的归约及其间的依赖关系。







我们得到了减少的图表。它影响我们计算特征的切片,通常允许哪些连接,以及消耗的资源数量:显然,减少的次数越多,资源越多。和延迟,吞吐量。



让我们构造一个这样的图。







也就是说,我们将减少量分为两个阶段。在第一阶段,我们将并行计算不同部分(我们的用户,作者和体裁)的不同缩减。我们需要某种第二阶段,我们将从这些不同的归约中收集特征并接受最终判决。



对于下一批,我们执行相同的操作。此外,我们将每个批次的第一阶段与过去的第一阶段相关,并将第二阶段与过去的第二阶段相关。



在这里重要的是,我们没有这种依赖关系:







也就是说,我们实际上得到了一条传送带。即,下一批的第一阶段可以与第一批的第二阶段并行工作。



如果我们只有两个阶段,我们如何在上面给出的三个阶段进行统计呢?很简单。我们可以在批处理N的第一阶段读取第一个值。在批处理的第一阶段的







第二个值是N + 1,并且必须在批处理N + 1的第二个阶段读取最终值。因此,在第一阶段和第二阶段之间的过渡期间,对于N + 1批次,可能会有不太准确的统计信息。但是通常这足以进行此类计算。







拥有所有这些东西,您可以从多维数据集构建更复杂的功能。例如,书籍的当前评分与用户的平均评分之间的偏差。或对书进行正面或负面评价的用户比例。也可疑。或对不同书籍拥有超过N个评分的用户的平均书籍评分。从某些角度来看,这也许是一个更准确和公平的评估。







此外,我们称之为事件之间的关系。通常,重复记录会出现在发送给我们的日志或数据中。这些可以是技术事件,也可以是机器人行为。我们还发现了这些重复项。或者,例如,一些相关事件。假设您的系统显示图书推荐,而用户单击这些推荐。为了确保不会影响排名的最终统计数据,我们需要确保如果我们过滤印象,那么还必须过滤对当前推荐的点击。



但是由于我们的流程可能会不均匀,因此请首先单击一下,我们必须将其推迟到看到节目并接受基于该节目的判决之后。



功能描述语言



我将简单介绍一下用于描述所有这些内容的语言。







您不必阅读它,例如。我们从三个主要部分开始。首先是对历史上通常是任意类型的数据单元的描述。







这是某种功能,可以为空。







还有某种规则。我们怎么称呼规则?这是这些功能及其他条件的一组条件。我们有三个单独的文件。



问题在于,这里的一连串动作分布在不同的文件上。大量分析师需要使用我们的系统。他们不舒服。



事实证明该语言非常必要:当我们描述需要计算的内容时,我们描述如何计算数据,而不是声明性的。这也不是很方便,很容易犯错并且进入门槛很高。新来的人来了,但是他们根本不了解如何使用它。



解决方案-让我们制作自己的DSL。它更清楚地描述了我们的情况,对新人来说更容易,更高级。我们从SQLAlchemy,C#Linq等中获得了灵感。



我将提供一些与以上示例相似的示例。







阅读的侦探小说百分比。我们计算阅读的书数,即按用户分组。我们为此条件添加了过滤条件,如果要计算最终百分比,则只需计算等级即可。一切都很简单,清晰和直观。







如果我们计算不同作者的数量,那么我们将按用户分组,设置不同的作者。为此,我们可以添加一些条件,例如计算窗口或由于内存限制而限制存储的值数量。结果,我们计算了count,即其中的键数。







或我所说的平均忠诚度。也就是说,同样,我们有一种从上面计算出来的表达式。我们按作者分组并在这些表达式中设置一些平均值。然后,我们将其范围缩小到用户。







为此,我们可以添加一个过滤条件。也就是说,我们的过滤器可以是以下示例:忠诚度不太高,侦探所占的百分比在100中的80之间。



我们在引擎盖下使用什么呢?







在引擎盖下,我们直接使用了70年代最先进的技术,例如Flex,Bison。也许你听到了。他们生成代码。我们的代码文件通过在Flex中生成的词法分析器以及在Bison中生成的解析器进行分析。词法分析器生成语言中的终端符号或单词,解析器生成语法表达式。



由此,我们得到了一个抽象的语法树,我们已经可以使用它进行转换。最后,我们将其转换为系统可以理解的低级文件。



底线是什么?这比乍看之下要复杂得多。需要花费大量资源来思考一些小事情,例如操作优先级,极端情况等。当然,您需要学习罕见的技术,除非您编写编译器,否则这些技术在现实生活中不太可能对您有用。但最后还是值得的。就是说,如果您像我们一样拥有大量经常来自其他团队的分析师,那么最终这将带来显着的优势,因为他们的工作变得更加容易。



可靠性



某些服务需要容错:跨DC和一次处理。违规会导致统计数据和损失(包括金钱损失)出现差异。我们针对MapReduce的解决方案使得我们一次只能在一个集群上读取数据,而在第二个集群上进行同步。







例如,我们在这里的表现如何?有一个领导者,关注者和消息代理。可以认为这是有条件的卡夫卡,尽管这里当然是它自己的实现。







我们将批次交付给两个集群,在同一领导者上发起一组削减,接受最终裁决,更新历史记录并将结果发送回服务到消息代理。



偶尔,我们自然必须进行复制。也就是说,我们收集快照,收集更改-每个批次的更改。我们将两者都同步到第二个集群关注者。而且还提出了一个非常热闹的故事。让我提醒您,历史记录保存在这里。



因此,如果一个DC由于某种原因不可用,我们可以以最小的延迟足够快地切换到第二个群集。



为什么不完全依靠两个集群呢?在两个群集上,外部数据可以有所不同,它们可以由外部服务提供。反正什么是外部数据?这是从更高的层次开始的。即,复杂的聚类等。或者只是数据辅助计算。



我们需要一个商定的解决方案。如果我们使用不同的数据并行计算判决,并定期在两个不同聚类的结果之间进行切换,则它们之间的一致性将急剧下降。而且,当然可以节省资源。由于我们一次只在一个群集上使用CPU资源。



那第二个集群呢?我们工作时,他几乎是闲着。让我们使用他的资源进行全面的预制作。所谓的正式预生产,是指接受相同数据流,可使用相同数据量等的成熟安装。







如果群集不可用,我们会将这些安装从销售改为预生产。因此,我们已有一段时间了,但是没关系。



好处是我们可以在预处理过程中计算更多的功能。为什么还要这样做?因为很明显,如果我们要计算大量功能,则通常不需要计算所有功能。在那里,我们仅计算获得最终判决所需要的内容。



(00:25:12)



但是同时,我们在预处理过程中拥有一种热缓存,它很大,具有各种各样的功能。如果发生攻击,我们可以使用它来解决问题并将这些功能转移到生产环境中。



另外,还有B2B测试的好处。也就是说,我们所有人当然都会首先进行预售。我们会充分比较任何差异,因此,我们不会弄错,我们将推出销售时可能犯错的可能性降到最低。



关于调度程序的一些知识。显然,我们有某种机器在MapReduce中运行任务。这些是某种工人。他们定期将其数据同步到跨DC数据库。这只是他们目前设法计算的状态。







如果某个工作线程不可用,则另一个工作线程会尝试捕获日志并获取状态。







站起来,继续工作。继续在此MapReduce上设置任务。







显然,在覆盖这些任务的情况下,其中一些可能会重新启动。因此,这里有一个非常重要的属性:幂等,即重新启动每个操作而不会产生任何后果的能力。



也就是说,所有代码都必须以这种方式正常工作。







我会讲一些有关一次的知识。我们正在达成一致的判决,这非常重要。我们使用可以提供此类保证的技术,并且自然地,我们会监控所有差异,并将其减少到零。即使看起来已经减少了,但有时也会出现一个非常棘手的问题,我们没有考虑到。



仪器



非常简短地介绍了我们使用的工具。为不同的系统维护多个反欺诈是一项艰巨的任务。我们实际上提供了数十种不同的服务,我们需要一个单一的位置,您可以在这里看到他们当前的工作状态。







这是我们的指挥所,您可以在其中查看我们当前正在使用的集群的状态。您可以在彼此之间切换,发布版本等







。例如,在问题仪表板中,我们可以在一页上立即看到与我们连接的不同服务的所有反欺诈问题。在这里,您可以看到目前我们的图书服务明显存在问题。但是监视将起作用,值班人员将对其进行观察。







我们到底在监视什么?显然,系统延迟非常重要。显然,每个阶段的运行时间,当然还有各个规则的过滤。这是一项业务要求。



出现数百个图形和仪表板。例如,在此仪表板上,您可以看到轮廓已经很糟糕,因为我们有了很大的滞后。



速度



我将向您介绍向在线部分的过渡。这里的问题是整个回路中的滞后可能会达到几分钟。它在MapReduce上的轮廓中。在某些情况下,我们需要禁止,更快地发现欺诈者。



会是什么呢?例如,我们的服务现在可以购买书籍。同时,出现了一种新型的付款欺诈。您需要对此做出更快的反应。出现了一个问题-如何转移整个计划,理想情况下尽可能保留分析人员熟悉的交互语言?让我们尝试将其“转移到前额”。







假设我们有一个平衡器,其中包含来自服务的数据以及一定数量的工作人员,我们将这些数据从这些平衡器分担给这些工作人员。我们在这里使用外部数据,这些数据非常重要,并且包含一系列故事。让我提醒您,每个这样的故事对于不同的还原都是不同的,因为它具有不同的键。



在这样的方案中,可能出现以下问题。







假设我们有两个关于工人的事件。在这种情况下,如果对这些工人进行任何分片,一把钥匙指向不同工人时可能会出现这种情况。在这种情况下,这是作者托尔金,他进了两名工人。



然后,我们将从键值存储中的数据读取到历史记录中的两个工作人员,我们将以不同的方式对其进行更新,并且当我们尝试回写时将出现竞争。



解决方案:让我们假设读取和写入可以分开,并且写入可以稍有延迟。这通常不是很重要。稍微延迟一下,我的意思是这里以秒为单位。这尤其重要,因为我们对键值存储的实现花费的时间比读取的时间更长。



我们将滞后更新统计信息。平均而言,考虑到我们将在计算机上保留缓存状态的事实,这种方法或多或少都很好。







还有一件事。为简单起见,让我们将这些故事合并为一个,并按剪切的类型和键记下来。我们有某种共同的历史。



然后,我们将再次添加平衡器,添加读取器的计算机,可以用任何方式将其分片-例如,仅通过负载即可。他们将简单地读取此数据,接受最终判决并将其返回给平衡器。



在这种情况下,我们需要将这些数据直接发送到其中的一组编写器机器。作家将相应地更新故事。但是这里仍然存在问题,我在上面已经写过。接下来,让我们改变作者的结构。







我们将使它以与历史记录相同的方式进行分片-通过密钥的类型和值。在这种情况下,当其分片与历史相同时,我们将不会遇到我上面提到的问题。



他的任务在这里改变。他不再接受判决。相反,它仅接受来自Reader的更新,将它们混合在一起,然后将其正确地应用于历史记录。



显然,这里需要一个组件,一个协调器,可以在读取器和写入器之间分发这些更新。



当然,这还包括工作人员需要维护最新缓存的事实。结果,我们需要负责数百毫秒,有时甚至更少,并且我们在一秒钟内更新统计信息。一般而言,它运作良好,对于服务来说已经足够。







我们到底得到了什么?分析师开始以所有服务相同的方式更快地完成工作。这提高了所有系统的质量和连接性。您可以在不同服务的反欺诈之间重用数据,新服务可以快速获得高质量的反欺诈。







最后有一些想法。如果您编写类似的内容,请立即考虑分析师对这些系统的支持和可扩展性的便利性。使一切都可配置,您需要它。有时,跨DC和一次准确的属性很难实现,但是可以做到。如果您认为自己已经实现了,请仔细检查。感谢您的关注。



All Articles