如何检测业务数据中的异常【Airbnb数据最佳实践】
RussellCloud第一位开发者
Nash Equilibrium 等 75 人赞了该文章
</header>
英文博客原文链接:http://nerds.airbnb.com/anomaly-detection/#rd
小编备注:知乎专栏的编辑器真难用,建议大家去公众号「datasay」查看原文,我翻译以及原创的文章每晚都会更新在那里,点击这里直接进入公众号原文。
前言
(作者在这部分有凑字数嫌疑,可以考虑跳过部分内容)
随着 Airbnb 全球化市场的开拓,Airbnb 鼓励租客通过自己熟悉的支付方式或者当地货币来支付房费,同时房主也可以通过自己喜欢的货币和方式收到租金。
比如说,巴西目前的货币是雷亚尔( Brazilian Real ),巴西人习惯于使用当地的 Boleto 作为最常用的支付方式。这和我们在美国的支付方式完全不同,我们可以想象得到 这个问题在 Airbnb 扩展到它服务的 190 个国家时会有多大。
<figure style="margin: 24px 0px;"></figure>
为此,Airbnb 支付团队推出了自己研发的一流支付平台用来保证支付过程的安全性以及易用性。
支付团队的职责不仅包括帮助用户支付租金,使用礼品卡这些新的支付体验,而且还需要协助财务对账( financial reconciliation )。
由于Airbnb需要支持 190 多个国家,因此支付系统必须支持大量种类的货币。大部分的时间里,支付系统能够正常运行,但偶尔也会出现一些暂歇性的故障(谁家能保证自家系统不出问题呢)。
比如,某类货币无法处理甚至无法访问支付渠道。为了能够尽可能快的捕获故障信息,数据团队构建了一套实时的异常监测系统来完成这件事。
这个异常检测系统可以在最短的时间内很好地帮助产品团队定位问题,这就可以让数据分析师们可以腾出手来做其他工作,比如新的付费方式或者新产品上线的A/B test、定价策略优化或者价格预测以及构建机器学习模型来做个性话推荐(作者这是在凑字数吗?)
为了给大家演示这个异常监测工具的使用过程,我们通过一些模拟数据集来展示模型是怎么工作的。
假设现在是 2020 年的夏天,我们正在运营一家电商网站,提供三件商品:显示屏、键盘和鼠标,而且我们只有两个供货商:Lima 和 Hackberry 。
动机
前面我们提到,异常监测系统主要是为了在时序数据中发现异常数据。其实简单的情况人类可以通过整体的直观图表一眼看出来,但机器无法做到这点,而且大部分的情况并没有这么简单,需要我们去对这些数据进行细分调研才能找到问题的根源。下面我们来看看8月份显示屏的进货数据,如下图所示:
<figure style="margin: 24px 0px;"></figure>
从图中可以看出显示屏的整体进货量很正常,并没有出现什么问题。那么我们根据供货商来源对这个数据进行细分查看,如下图所示:
<figure style="margin: 24px 0px;"></figure>
从图中可以看到,Lima 是我们显示器的主要供货商,但是在2020年8月18日这三天出现了运输问题。Airbnb异常检测系统 会自动检测出这类问题并反馈给另一家供应商 Hackberry。
大家可以看到从整体层面数据来看我们根本发现不了这个问题,但是细分后从更深的维度就可以清晰的检测到问题。
模型
简单回归模型
最直观的模型是把一周的第N天作为虚拟变量(dummy variables)跑一次最小二乘回归。模型公式如下:
<figure style="margin: 24px 0px;"></figure>
其中 y 是我们想要追踪的总量数,t 是时间变量,I_day_i 是 指示变量,代表今天是这一周的第 i 天,e 是误差项。
这个模型非常简单,而且它可以很好地识别前面看到的趋势。然而,这里还有几个缺陷:
增长预测是线性的。如果我们遇到指数型增长,这个模型就很难识别了。
从公式中可以看出,这个模型有一个假设的最大前提:周期时序数据(并且周期刚好是一周),无法处理其它模式的周期数据集。
虚拟变量太多,需要大规模的样本训练集才能达到想要的效果。
即便我们能够观察到我们想要模拟的指标模式然后据此人为地调整模型(比如当我们发现有显著的以年或月为周期的周期模型那我们就可以添加更多的虚拟变量),但这个过程仍然是不具备扩展性的。
Airbnb的工程师则采用自动的方法避免误差并且将这种技术运用到支付以外的场景。
(小编:下面是重点,重点,重点)
快速傅里叶变换模型(FFT)
对时序数据集构建一个基于趋势和周期的模型。模型公式如下:
<figure style="margin: 24px 0px;"></figure>
其中,Y 是度量标准,S 是周期变量,T 代表趋势,e是误差项。
和前面的简单回归模型作对比,我们可以发现这里的 S 代表所有指示变量之和,而 T 则等同于 at+b 。
在这部分,我们将会展示新的方法来发现趋势和周期,这里面也包含了我们在前面的部分中所发现的内容。
我们将使用另两款商品(即键盘和鼠标)的销售数据来展示模型。两种商品的销售数据如下图所示:
<figure style="margin: 24px 0px;"></figure>
如图所示,键盘从2016年9月开始售卖并成为主打商品,而鼠标则从2017年8月才开始引进。接下来我们将对销售数据的趋势以及周期进行建模,一次来挖掘其中偏离平均值的异常数据。
周期性质
为了识别数据中的周期性质,我们将使用(传说中的)快速傅里叶变换(FFT),在简单线性回归模型中,我们已经假设了数据存在以周为单位的周期性质。
但如上所示,在鼠标的销售数据中并没有显著的以周为单位的周期模型,因此如果我们盲目的假设则会因为不必要的虚拟变量造成模型误判。一般来讲,如果我们有不少历史数据的话,快速傅里叶变换是一个用来识别周期的好手段。
对时序数据集应用FFT之后,得到下图:
<figure style="margin: 24px 0px;"></figure>
图中的season_day指的是一段余弦变换( the period for the cosine wave of the transformation)。
在快速傅里叶变化中,我们经常只选择峰值的周期来代表周期性质,其它周期则作为噪音数据处理((具体原理参照FFT原理资料))。
在这个例子里面,键盘的FFT结果中大峰值有 7 和 3.5,小峰值有 45 和 60 。对于鼠标来说,大峰值有 7 ,小峰值有35,60以及80。
把这两种商品的FFT结果合并到一起生成周期图表,如图所示:
<figure style="margin: 24px 0px;"></figure>
如图所示,键盘销售数据呈现周期性的增长,周期是一周。而鼠标销售数据则有两种周期趋势,周期分别为一周和40天。
趋势分析
这里我们将使用滚动中值作为时序数据集的趋势。这个算法的前提假设是在极短时间内增长变化不显著。比如,对某天来讲,我们采用其之前7天的滚动中值作为当天的趋势水平。用中值代替平均值使得算法检测异常数据的结果更加稳定。
举例来说,对于某一两天销量突然增长10%,如果使用中值则基本没有什么变化,如果使用平均值则会对趋势有明显的变化。在这种情况下,我们采用14天的中值作为当天的趋势,如图所示:
<figure style="margin: 24px 0px;"></figure>
误差项
在得到周期性质和趋势以后,我们最后还需要评估误差项。通过误差项我们可以发现时序数据集中的异常点,这里用原始数据减去周期效应和趋势得到误差项,如图所示:
<figure style="margin: 24px 0px;"></figure>
从上图可以看出,误差项中出现了一些毛刺型数据(spikes),这说明时序数据集中出现了异常点。
根据实际情况定出所能容忍的负相关度,即选择偏离中心点多少个标准方差(参考《概率论与数理统计教程》)。这里我们选择偏离4个标准方差来获得一个合理的阈值警报,结果如下图所示:
<figure style="margin: 24px 0px;"></figure>
从上图可以看出,异常监测系统已经可以检测出误差项的大部分毛刺型数据(spikes),预警系统效果非常棒。
这里我们需要注意,其实这里有些点我们在人肉眼 看来觉得是正常的,但实际上是异常数据,这种情况也被系统检测出来了。
经过Airbnb的内部测试,这个模型在异常数据检测方面的功能准确性非常之高。
小编寄语:之前一直想把自己平时的阅读笔记以及相关代码写成博客或者公众号图文记录下来,但一直没有毅力执行,昨天刷到了知乎里面「 单身职场人士如何利用晚上八点到十点这段时间自我提高?」这个问题,我决定好好利用这段时间,因此从今天开始我将会在每天的这个时间写下一篇数据方面的文章(主要涵盖数据采集、数据分析以及数据挖掘方面的内容,也会涉及一些其他公司数据产品实践的内容吧),或取材于自身经历,或见诸于书本,或自撰,或翻译,望与诸君共勉!
75 21 条评论
分享
收藏
75
分享
推荐阅读
[评析:触不到的大数据
王义之发表于影像志](http://zhuanlan.zhihu.com/p/19593819)
[
近况&Airbnb定价算法简单介绍
面包君](http://zhuanlan.zhihu.com/p/21353925)
[
多层数据巧钻取,业务盲点无处可躲~
王小美](http://zhuanlan.zhihu.com/p/25507238)
[
2018年 转化率优化的五个最佳实践
数据观](http://zhuanlan.zhihu.com/p/34745070)
21 条评论
切换为时间排序
写下你的评论...
评论
Nash Equilibrium回复魏鸿鑫 (作者)1 年前受外生冲击的影响还是很明显。比如短期预测交易量,如果业务方恰好在这个星期增加了一倍的红包投放量,这时候用历史数据(哪怕分离出趋势与周期)来预测效果就很糟糕。
赞 查看对话 回复 踩 举报
魏鸿鑫 (作者) 回复Nash Equilibrium1 年前最近看的好几篇博客都是外国的团队讲自己如何应用分析手段牛逼,实际上算法都比较粗浅,不过至少还是应用进去了,或许也是因为对外的博客只提供一些思路,我相信内部肯定有进阶版,也希望大家能够帮忙推荐一些相关的资源,我们做数据产品的思路和算法都缺😂
1 查看对话 回复 踩 举报
以上为精选评论
Nash Equilibrium1 年前看了何明科的文章链接过来,发现airbnb做了STL时序分解能够被说得那么高大上……
赞 回复 踩 举报
魏鸿鑫 (作者) 回复Nash Equilibrium1 年前并不是“被”说~这是Airbnb自己的博客,话说回来,国内对自己的业务数据做时序分解的并没有几家
赞 查看对话 回复 踩 举报
江城雨1 年前居然就是简单的FFT
赞 回复 踩 举报
LittleCc丶1 年前赶脚就是FFT的Outlier分析。
赞 回复 踩 举报
魏鸿鑫 (作者) 回复江城雨1 年前是啊,很简单的分析,这篇文章的主要用意不是说算法有多厉害,最终要刻画的是在业务数据中的应用,说到底是各类分析手段在国内互联网领域还没有深入的应用(想想至少FFT比同比环比要深刻吧)
赞 查看对话 回复 踩 举报
魏鸿鑫 (作者) 回复LittleCc丶1 年前算法很简单,但比起现在国内互联网公司还在用同比环比刻画数据的水平还是高很多了
赞 查看对话 回复 踩 举报
Nash Equilibrium回复魏鸿鑫 (作者)1 年前啊理解偏差,不是说你在吹airbnb,而是感慨他们自己吹自己……国内的确用得不多,以前支付宝里预测余额宝申购赎回量的时候用了STI时序分解,ARIMA预测长期趋势,HoltWinters指数平滑预测季节趋势。除此之外,再也没见到互联网公司这么做了……(我自己懒,另当别论)
赞 查看对话 回复 踩 举报
魏鸿鑫 (作者) 回复Nash Equilibrium1 年前嗯,我最近就是试着用你提的这几个算法为饿了么的业务数据服务,异常检测还好,预测感觉还是效果不咋样
赞 查看对话 回复 踩 举报
Nash Equilibrium回复魏鸿鑫 (作者)1 年前加油,多分享些~
赞 查看对话 回复 踩 举报
魏鸿鑫 (作者) 回复Nash Equilibrium1 年前是的,看看后面有没有可能预测中长期的趋势(去除活动等因素),不过其实整体的预测对我们来说可能用处并不大,预测功能更适合给店家的数据产品中做菜品销量预测之类的,这些都要慢慢试了
赞 查看对话 回复 踩 举报
魏鸿鑫 (作者) 回复Nash Equilibrium1 年前知乎专栏的编辑器太难用了,主要在公众号「数据魔法屋」更新每天的内容
赞 查看对话 回复 踩 举报
酷酷的拖拉机手回复魏鸿鑫 (作者)1 年前我们请了微软帮忙做一个预测系统。对于餐饮行业而言,能做出一套相对准确的预测系统真是太重要了。
赞 查看对话 回复 踩 举报
蓝雪1 年前目前还没有看到过怎么剔除活动因素的预警,很希望有人能分享
赞 回复 踩 举报
魏鸿鑫 (作者) 回复蓝雪1 年前这个需要对活动数据进行单独训练,后面只需要从异常数据结果中剔除活动时间(实际还不如人工剔除吧)
赞 查看对话 回复 踩 举报
郭嘉1 年前season_day是什么
赞 回复 踩 举报
魏鸿鑫 (作者) 回复郭嘉1 年前原文有做解释:the period for the cosine wave of the transformation
赞 查看对话 回复 踩 举报
小霸王1 年前楼主,你这些好文章都是在哪里找到的?
赞 回复 踩 举报
魏鸿鑫 (作者) 回复小霸王1 年前自己挖的😂
赞 查看对话 回复 踩 举报
1 2 下一页
</article>
</main>
<input autocomplete="off" role="combobox" aria-expanded="false" aria-autocomplete="list" aria-activedescendant="AutoComplet-85981-46214-0" id="Popover-85981-32054-toggle" aria-haspopup="true" aria-owns="Popover-85981-32054-content" class="Input" placeholder="选择语言" value="" style="-webkit-box-flex: 1; flex: 1 1 0%; padding: 0px; overflow: hidden; font-family: inherit; font-size: inherit; font-weight: inherit; background: transparent; border: none; outline: none; resize: none; color: rgb(26, 26, 26); height: 24px; line-height: 24px; cursor: inherit;">