Twitter机器学习平台的设计与搭建

社交网络日新月异,需要更快,更灵活的技术架构才能满足用户日益丰富的媒体需求,Hybrid App架构具备了快速发布的能力,同时也有更接近Native的流畅体验以及性能;本文整理自腾讯社交平台高级工程师黄俊涛在ArchSummit全球架构师峰会

在数据爆炸的时代,机器学习是有效挖掘信息的途径,怎样顺势而为设计与搭建大规模机器学习平台?本文整理自Twitter机器学习平台组负责人郭晓江在ArchSummit深圳2016的演讲。帮助大家了解Twitter机器学习的发展历史与变迁以及超大规模(日万亿量级请求)的在线机器学习系统的设计。

微公号ID:ArchSummit,关注获取演讲视频及PPT。

主持人:今天准备本场演讲嘉宾介绍前我特意上网搜了一下,顿时在心里特别膜拜。郭晓江原来是清华大学毕业生,后来在斯坦福毕业,他加入Twitter,从事机器学习和广告的研究,这四年他现在是机器学习平台组负责人,搭建了Twitter机器学习平台,所以这一块很有建树,后面的时间交给晓江。

郭晓江:感谢主持人,那么今天的话我会简单介绍一下Twitter机器学习平台的设计与搭建,也希望从规模化机器学习平台的角度来主要讲一些我们在这个过程中所遇到的各种坑,以及我们做的各种的努力,也希望能对大家有一点用处。

咱们今天下午的专题是“大数据”专题,机器学习和大数据是密不可分的。如果我们将数据比作一座金矿,机器学习就是挖掘金矿的工具。俗话说:顺势而为。那么机器学习在近些年来也是发展越来越好,应用越来越广,我认为主要得益于以下几个趋势:

1.    Data Availability

我们可以获得的数据量越来越大,一会在下一张slide中也会讲到如果数据量越大,我们模型的质量会有显著提高;

2.  Computation Power越来越强

比如最近出现的云计算、GPU、TPU等等。在上世纪九十年代其实神经网络的理论就已经有了,也就是深度学习的这些理论已经有了,但是当时并没有火起来,甚至一度被学术界认为这是一个没有前途的方向,就是因为当时这个computation power没有到位。

随着近些年来这些方面的不断提高,使得我们可以训练更复杂的模型。大家都知道如果你没有太多的数据或者computation power不够多,则只能在一个小数据集上做训练,如果模型非常复杂就会出现过度拟合(Overfit)。所以只有当我们把这些问题全都克服之后,我们才可以训练更复杂的模型,得到一些更好的结果;

3.  Development in Algorithms

整个算法的发展,会有无数机器学习的研究者,他们不断地去push the boundary of machine learning。

从大数据和模型的表现关系来看,在十几年前有两个研究者他们将当时几个机器学习比较常用的算法,在一个具体的机器学习的问题上做了一个实验:

这张图的横轴是数据量,即训练数据的数据量,它是一个指数的规模(Scale)。最左边的刻度应该是10万个数据点、100万个数据点和1000万个数据点以此类推;纵轴是模型的表现,即训练出来模型的质量。

大家可以非常清楚地在图中看到,当数据量很小的时候,例如10万个数据点时这几个算法的质量非常差,当数据量逐渐增大的时候,模型的质量显著地提高,而且任何一个算法在大数据量时的表现都比任何一个算法在小数据级的表现下要好很多。当然这是在某一个具体的机器学习问题上面做的实验,但是我觉得它有一定的推广价值。它给我们的启示是:如果机器学习的平台架构不够规模化,只能在小数据级上做训练,哪怕你算法做得再好也是徒劳,不如先解决规模化的问题,先在大数据上能够做这样一个训练,然后在算法上再做提高。

说到Twitter,机器学习在Twitter是非常重要的。我们有内部的研究表明:大概80%的DAU都是直接和机器学习相关产品相关的,90%的营收来源于广告,而广告完全是由机器学习来支持的。我们现在做的机器学习平台支持了Twitter很核心的业务,包括:

·      ads ranking(广告排序);

·      ads targeting;

·      timeline ranking(feed ranking);

·      anti-spam;

·      recommendation;

·      moments ranking;

·      trends

Twitter的机器学习规模也非常大,我们拿广告来举例子,每天在Twitter大概是做10个trillion量级的广告预测,每个模型的weights个数大概是10个million的量级,每个training example大概是有几千到1万个features,每一个数据点上有这么多,整个Feature Space大概是百亿的量级,训练的数据也是TB量级,所以大家可以看到对机器学习平台的挑战是非常大的。

机器学习在Twitter有比较独特的一点是Realtime(实时性),Twitter本身的产品非常的realtime,Twitter is all about realtime,like news、events、videos、trends,比如大家去Twitter上更多地是更新自己的状态,或者是看一些新闻,去了解一些最新的动态;广告商也会根据产品的特点去投放一些广告,他们往往投放的广告持续的时间都非常短,比如就是一个事件,如NBA总决赛,三个小时内做一个广告投放,所以要求我们机器学习的模型就必须根据实时的traffic的情况来不断地做调整和变化。否则,如果我们每天训练和更新一次模型,这样的速度就实在是太慢了,所以我们也是投入非常多精力做了一个规模化的在线学习的系统。你在Twitter上点击任何一个广告,那么在百毫秒的量级的延迟内,我们的模型就会更新。

下面我简单地过一下机器学习在Twitter上几个具体的产品应用。

1.    Ads Ranking

它的具体问题是当你上了Twitter,我后面有1千个或者1万个广告可以展示给你,我到底展示哪个广告给你你最可能感兴趣?因为Twitter采取的是CPC(Cost Per Click) Model,只有你点击了广告,广告商才会给我们钱,如果你只是看了广告,不感兴趣没有点,广告商是不会给我们钱的,所以选取最合适的广告不光是给用户更好的用户体验,同时也是Twitter盈利的关键;

2.  Timeline Ranking(Feed Ranking)

将你的时间轴进行排序,把最好的Tweet能放在比较靠上的位置,这样容易被看得到;

3.  Recommendation

推荐你可能最感兴趣的人;

4.  Anti-Spam

比如抓僵尸粉,或者是Abuse Detection,例如大家在Twitter上骂起来了,要做一些检测并且把它隐藏掉,还有NSFW Detection基本上是鉴别一些黄色图片之类的。

大家也可以看到,机器学习平台面临的挑战其实主要是规模化的挑战。规模化我认为主要是两方面:

·      一方面是组织架构上的规模化,我们作为机器学习平台的组如何更好地去支持这样七八个Twitter的核心产品,并且能够让我们的client team(我们的用户)能够非常快地进行prototype(产品迭代);

·      另一方面是整个系统的规模化:一方面你的离线训练如何能更快,在线预测如何能够更快;还有一方面,当我们的用户把整个pipeline搭建起来之后,他们要不断优化整个pipeline,我们有没有足够的工具支持我们这些用户做到这一点(我说的用户是Twitter内部的这些产品团队)。我们怎么让我们的用户非常快速地进行迭代和实验?

一、组织结构的规模化

我们相信我们的用户真正了解他们具体做的事情、他们的产品和他们的问题,所以我们采取的合作模式是:

·      我们开发各种的工具、很多的框架,去定义feature(特征)、transform(变换)、model(模型)等等的格式,然后把工具交给我们的用户,让他们从特征提取到离线训练、如果这个模型好再推到在线生产环境当中、以至后面持续不断地优化提高,在整个过程中我们希望把每一步都做到足够的简便。同时我们还对于一些新的用户提供一些onboarding的支持;

·      我们的client team负责做特征提取的,因为只有他们了解具体的自己的问题,知道什么样的信号可以对他们的问题有更好的提升。

我们也会把整个特征的共享做到非常好,比如其他team有一个很好的特征,你可以非常快地加入你的模型中进行实验。同时我们的client team他们负责去own和maintain training pipeline和serving runtime,例如on call完全不是我们的事,完全由client team来做;

二、系统的规模化

主要分几个方面:

1.    准备数据,既然要进行模型训练当然要把数据准备好;

2.  离线的训练,会有workflow management;

3.  Online Serving(在线服务),比如模型训练好了,要推到市场环境中去,要可以承受这种high QPS、low latency这些要求,还要做A/B testing,在1%的数据上先做一些实验,然后过一段时间,真正它在实际的traffic上更好的话我们就把它launch到100%。与此同时还做很多工具,来帮助我们的用户更好地去理解他们的数据和模型,以及还有一些工具去做比如参数的扫描、特征的选择等等。

三、准备数据

首先,我们要做的是统一数据格式,定义一个数据格式很简单,但是我觉得意义非常重大,就像秦始皇统一六国以后先统一度量衡,因为只有大家用一样的格式大家才能彼此互相沟通、交流和分享。

举个例子:比如某个产品团队在他们在模型中加了某一种信号或特征,结果特别好,我是做广告的,我想把数据拿过来用,如果数据的格式都不一样,我还得过去去研究你们这个组的数据格式到底是什么样子的,我们怎么转换成我们的格式,有非常非常多时间浪费在这个地方,这是我们希望解决的,Enable feature sharing across teams and make machine-learning platform iteration very easy.

我们的特征向量的格式,其实本质上是feature identifier to feature value mapping,它支持4种dense types:

·      Binary;

·      Continuous;

·      Categorical;

·      Text

2种sparse feature types:

·      SparseBinary;

·      SparseContinuous

为了去优化效率,我们feature identifier是用64位feature id存的,这个feature id是feature name的一个hash。之所以这样去做,是因为如果你在训练的过程或者在你的生产环境中,你去操作很多个string的话,特别费CPU,所以我们采取的方式是使用feature id,而在别的地方存一个feature id to feature name的mapping。比如我们存在数据仓库中的数据都是feature id的,但是每个机器学习的数据集旁边我们都会存一个metadata,就是feature id to feature name的mapping。

说到数据准备,大家可以想象一下:如果让一个数据科学家用语言描述怎么样准备他的数据,往往这个数据科学家在非常短的时间比如1分钟时间之内就可以描述清楚:比如我们把这个production中scribe的数据拿过来,然后和另外一个数据集做join,然后做一些sampling和transform,然后写到persistent storage里面去。

我们开发了一套DataAPI,对机器学习的数据集以及数据集的操作在很高的层次上做的一些抽象,当你用我们这一套API去描述你想对数据进行操作过程时,这个代码就跟你描述出来你要做什么事情和我们期望达到的效果一样的简单,我们希望这使得我们大部分的machine-learning task在训练过程中的数据准备都能够通过20行或者30行代码就搞定。

它是基于Scala的API,一个fluent的interface,并且在整个过程中去保证我们的数据正确,以及刚才我们说的feature id to feature name mapping的metadata是keep consistency。之后我们简单看一小段代码,举一个例子来让大家感受一下,这是用Scala写的,看这个代码的话,其实从代码上你完全就能明白我要做一件怎样的事情:

首先我是从FeatureSource里面读出了我机器学习里的数据集,并存在了tweetTopic这样一个变量里,然后我再从另外一个地方,从我的一个input path里读出另外一个数据集,并且把他filter/sample by 10% randomly,然后 用我给定的discretizer来进行transform,然后把它和我刚才的tweetTopic数据集进行join,它们的join key是tweet id,并且使用的是LeftJoin,最后我再把这个数据集写出去,这样我整个过程就准备好了。

其实读一下代码你会发现整个代码其实是非常好懂的,在写代码的过程其实就像在描述。我们的目标就是希望算法工程师在写这个代码的时候就像描述他们想做的事情一样。比如我们对数据的位置、格式等进行抽象。不管你的数据到底在哪里,比如你的数据可以在hdfs上,可以在database里,可以在很多其他地方,但是在这个API里,其实都抽象在FeatureSource里,然后用read就可以把它读出来,所以用户在使用的时候是不需要操心数据到底存在哪里等等这类事情。

四、Trainer

我们也提供了很多的trainer,让我们的用户把这些trainer进行一定的组合作为他们的offline training pipeline。首先是large scale logistic regression learner,我们有两个解决方案:

1.    Vowpal Wabbit

是John Langford开源的C++ trainer;

2.  Lolly

是Twitter内部开发的基于JVM的online learning trainer,因为Twitter整个stack(技术栈)都是基于JVM的,比如Java、Scala,所以我们开发了这个learner会和Twitter Stack会结合地更好一些。

在discretizer方面我们都比较标准了,像Boosting tree(GBDT、AdaBoost)、Random forest、MDL discretizer等;

在Deep Learning方面,我们是基于torch做的,也有一些Deep Learning的libraries。

五、PredictionEngine

刚刚提到Twitter这种实时性是非常非常重要的,所以我开发了一个在线学习的一个引擎,叫PredictionEngine,这是专门为Large scale online SGD learing来做的,我们整个广告包括我们的Feeds Ranking都是用的这个PredictionEngine。

在offline training其实整个PredictionEngine简单地包一层application layer;在online serving的时候,PredictionEngine包一层online service layer,加这个layer去处理一些像RPC等等这方面的东西,它基本的架构是:

·      第一层是Transform,用户可以去定义或者用我们提供的transform来对feature vector(特征向量)进行一定的变换;

·      第二层是Cross,Cross的意思是我可以把我的特征分组,比如分成四五组,然后第一组和第二组所有特征进行Cross,比如在广告上这个好处是可以把advertiser id,即把每个广告商的id分到一组,把其它的features分到第二组,然后第一组和第二组一Cross,其实effectively给每一个广告商一个personalized feature,这是非常有效的;

·      第三层是Logistic Regression;

这个Architecture一方面很方便地让我们进行在线的学习,同时在transform layer和cross layer我们也加进去了足够多的这种nonlinearity(非线性)元素。如果只是简单的logistic regression,那是线性的,效果并没有那么好,于是我们加了transform layer和cross layer会解决一些非线性变换的问题。

那么对PredictionEngine我们做了非常多的优化,在这个地方我会详细地讲:

1.    我们希望减少序列化和反序列化的代价:

第一点是model collocation,model collocation是什么意思呢?就比如在广告的预测中,我们预测的不是一个概率,即用户有多少可能性去点击这个广告,我们可能是预测很多个概率,比如用户可能转发这个tweet的概率,用户点击这个的tweet里面的链接的概率,或者是用户点击了这个链接还购买的概率,或者用户直接把这个广告叉掉的概率。

对于一个用户和广告的这么一个pair,我们会预测很多个概率,比如你要预测5个概率,本来是应该去做5次RPC call的,但是我们会把这五个模型都放在一physical container里面,这样的话一个call过去,我可以在5个模型中都进行计算并把5个prediction都给你返回,这是第一个优化。

第二点是Batch request API,还是拿广告问题举例,对于一个用户我要去评估可能成百上千甚至上万的广告的数量,对于任何一个用户和这个广告的pair,其实用户的特征其实都是一样的,所以有一个Batch的API的话,我可以amortise cost for user feature;

2.  我们希望减少CPU的Cost

也是做了几方面的优化:

·        所有的feature identifier全都是用id而不是feature name;

·        Transform sharing:刚才可以看到PredictionEngine里面,第一步是做transform,由于我们有model collocation可能有五六个模型,但其实可能有些模型他们的tramsform是一样的,所以在这个层面上我们不要做重复的transform,如果不同的model的Transform都是一样的话,我们就把它识别出来并且只做一次;

·        最后是feature cross done on the fly,因为feature cross其实是特征从几百个变到几千个甚至几万个的过程,比如原始特征几百个,cross之后特征数量可能大量增加。如果这时候我们把cross完的feature的再存到我们的内存中去,这个cross就太大了,即使只是对这个cross后的结果扫描一遍的代价都非常地大,所以要做成on the fly的cross。

3.  Training/Serving throughput

在整个在线学习过程之中,它的瓶颈在于最后trainer的模型update,在update模型的时候就要对这个模型加锁。如果不优化的话,只能有一个线程来对整个模型进行更新。如果你的模型特别大,比如我们每一个模型都是上GB(Gigabyte)的,在这个情况下就会严重的影响training throughput。

所以我们的优化会对整个模型进行sharding,比如用多线程。比如有10个线程,每个线程分别负责这个模型的十分之一,算出来整个模型的update的时候把它切成10块,扔到10个queue或buffer里面去,让这10个线程来更新自己相应的模型的那一块,所以只是需要每一块的worker自己更新自己那块的时候对那块进行加锁就可以了;

第二个是把training和prediction分离,因为在线学习的话,我们需要在线去响应很多的请求,如果每一个模型、每一个instance里面都有一个training都在做在线学习其实是很重复的。比如你有1千个instances都在做在线学习,并且都在做实时响应请求,1千个instances里面的training部分是冗余的,所以我们会把training这部分单独拿出来作为training service,定期会把这个模型的更新去放到一个queue里面,然后fanout到所有的predition service instance里面去;

第三个是弹性负载,比如我们的client端要call我们的prediction service的时候,我们会在client端加一个检测请求延迟,当我们在client端检测到prediction service不堪重负,这个时候我们会动态地减少对prediction service的请求,以保证我们的prediction service是良性和健康地运转的。

因为大家知道每天的流量会有周期变化,比如某些时段流量特别高,某一些时段比如在夜里流量相对比较低。通过弹性负载动态调整的机制,比如等到白天上午十点或者晚上八点特别忙的时候,我们可以做到对每一个用户评估少一点的广告,比如评估2000个广告;如果是到半夜,每一个用户可以评估多一点的广告,如1万个广告。这样动态地去保证CPU的使用率都是在固定的level上。这个Level的制定是要考虑不同数据中心中间的failover,比如数据中心挂了,所有的这些traffic都要failover到某一个数据中心,然后还要留一点余量,所以我们一般CPU的utilization是在40%左右。

4.  Realtime feedback

在线学习很重要一点是feedback一定要及时,但是有一个很不好解决的问题,如果用户他点了这个广告,这是正向的反馈你马上能知道,但是用户没有点这个广告你这事就不能马上知道,说不定用户过五分钟以后才点呢。

常用的解决方式是:我先把这个广告先存起来,然后等十五分钟,看看用户有没有点,如果用户在十五分钟内点了,我们就说这个用户点了,这是一个positive training example,然后把它发到在线学习服务中去;如果用户没有点,这就是negative training example。

这样的问题就是说我们会有十五分钟的延时,这一点是非常不好的,所以我们做了一个优化:每当我们展示一个广告的时候,我们马上给在线学习服务发一个negative training example,当成一个用户没有点击的事件,然后当用户后面真正去点了这个广告的话,那时我们会对这个事情进行一定的修正,这样就保证所有的事件实时性都非常高,是没有延迟的。

5.  Fault tolerance

我们的模型可能有几千个instances,这些instances经常地挂。我们需要每隔一段时间对我们的模型进行一个snapshot,如果某一个instance挂了,另外一个重新启动的时候,它会去把最新最近的model snapshot load进来,然后再开始进行在线学习;

还有一个问题是anomaly traffic detection,因为在线学习十分危险,因为上游数据任何的错误都会马上影响到这个模型的质量。举个例子,比如你有个pipeline,有两个queue,一个queue专门发positive training example,另一个是发negative training example,结果你的positive的queue给挂了,这样在线学习的模型一直只能接到negative training example,于是模型的预测在非常短的时间内整个模型就全乱了,像Twitter这种公司肯定都是有非常严格的on call制度,但是on call不能解决问题,为什么呢?当on call被page的时候,5分钟之后打开电脑去进行干预那个时候就已经晚了,所以我们是需要做anomaly traffic detection做到在线学习当中,如果一旦发现traffic这个构成发生了很严重的变化,我们会马上停止训练。当然还是要page on call啦,然后让on call来进行人工干预解决。

六、Tooling

刚才说的是在线学习,我们还给用户提供很多工具,这些工具是为了帮助我们用户很方便地区对整个模型进行研究或者做一些改进。这个工具叫Auto Hyper-parameter Tuning,就是变量的自动选择。机器学习的模型尤其包括像深度学习模型都有很多的变量。往往大家选变量的时候都是拍脑袋,比如我觉得这个learning-rate应该是多少然后放进去,好一点的呢就暴力搜一下,或者有些就是Random Search;

但是我们基于贝叶斯做了自动的hyper-parameter选择,我们会根据之前不同的parameter setting所跑出来的模型的结果去计算:我下一个parameter选择什么使得在期望意义下我对目标函数的提高会做到最大,而不像无头苍蝇一样到处去搜,而是充分地利用已经模型跑出来的数据和peformance来选择下一步尝试的参数。

其他的tooling,比如:

·      workflow management:就是整个offline的训练,你需要对它进行监测,需要可复现,可以互相地分享;

·      Insight和Interpretation:我们不希望我们的用户用我们的东西是一个黑盒,所以我们也会搞一些tool帮助他们看数据、看模型、去分析特征的权重和贡献等等;

·      Feature selection tool:进行简单地forward/backward 的greedy search。

七、Work in Progress

我们的机器学习也是在不断的探索之中,这是我们努力的一些方向:

1.    最主要的方向是我们要平衡规模化和灵活性的问题。因为规模化和灵活性往往是非常矛盾的,如果你用一些R、Matlab、Scikit-Learn等等一些工具,它们很多东西做得不错,灵活性是可以的,但是在这些工具上要搞规模化,这个事情是非常困难的;

反过来如果要规模化,你的系统要做的非常非常专,要针对某些情况做出很多优化,但是这种情况下就会影响算法的发挥。举个例子,我们的PredictionEngine分三层,第一层Transform、第二层Cross、第三层是Logistic Regression,如果你说我想试一点别的框架和步骤,和这个假设如果不是那么一样的话,可能你就没有办法用我们的这个工具。

所以,规模化和灵活性一直是一个非常冲突和难以平衡的问题,也是大家在不断在这方面做更多的努力,我们也期望用一些torch-based的large scale机器学习, 因为torch在灵活性方面是足够的,如果我们能够解决规模化的问题就会非常好;

2.  我们也会尝试把深度学习的一些东西在广告或者是feeds流上做一些实验,虽然在业界现在成功的并不多,只有Google他们声称在这个方面做得还可以;

3.  为我们的用户提供更好的工具,比如visualization和interactive exploration。

我今天的分享就到这里,谢谢大家!

八、Q&A

主持人:好,谢谢晓江。晓江请留步,今天你讲的这一场非常爆满。我想替大家问你几个问题,第一个问题是,你们做数据和推荐这一块,能直观的给几个数据来度量一下对你们Twitter业务的价值吗?

郭晓江:刚才我可能提到了,Twitter90%的营收来自广告,所有广告都是机器学习支撑的。我四年前进Twitter的时候,广告组的规模的确刚刚起步,当时那一代机器学习的架构也非常简单,模型也非常的简陋。在我们上了大规模的在线学习的东西之后,把整个Twitter的营收提高了大概30%左右,这对于Twitter是几十亿美金的business,30%是一个非常非常可观的数字,一般模型能提高1%、2%就已经非常不错了。

在一些基础架构的革新使得我们可以在大规模的数据上面进行训练,比如模型的复杂度和feature的规模都扩大了好几个数量级,的确会对我们整个business和整个模型的质量都有显著地提高。

主持人:30%的提升,很酷的数字,很困难的动作,所以在场的CTO、架构师加油整,我们数据的价值很大。第二个问题,你提到在架构上踩过很多的坑,这四年来整个过程中,你觉得最难的地方是什么?

郭晓江:因为我们是一个和业务结合蛮紧密的组织,我觉得其实最难的事情是要统一所有组的机器学习的架构。因为在最开始,可能每个组都有自己的一套机器学习的东西,但是如果大家都自己一套东西,互相的share就非常的困难,所以我们花了非常多努力,比如就非常简单的数据特征格式的定义,要推到所有的组都相当困难。

我觉得很多时候这个挑战更多还是来自于非技术的那一块,也就是说服大家用我们的这一套系统。也许因为我做技术,我觉得技术的东西还好,但是非技术的东西要推的话确实会稍微难一些。我们现在大家都用一套机器学习平台,之后我们把学习平台进行一些更新换代,其实基本上代码一般的生命周期也就是两年左右,过两年我们又有一套新的东西来更好地替代,这时候大家都用这一套,我们替代的时候也会方便很多,所以我觉得这是一个很大的挑战吧。

主持人:好,最后一个问题,你们Twitter的这件事情做得很牛,架构也做的很牛,你们有没有更多的文档更多知识能在网上跟大家分享吗?

郭晓江:我们现在并没有太多对外开放的文档,我们也考虑过有一些东西能不能open source。因为机器学习和业务结合实在太紧密了,它和整个公司的技术战略也非常紧密,我要开源这个东西,可能所有依赖的东西都得开源,而且机器学习这个东西更新实在太快了,所以我们花了好多时间,把这套开源了,实际上更好的东西已经出来了,所以暂时我们这方面还没有考虑。

主持人:你们已经开始分享了,期待你们以后有更多分享给我们启发,也希望我们国内出现更多像Twitter一样的公司。谢谢晓江的分享,使我们收获很大。

Twitter机器学习平台的设计与搭建_第1张图片

ArchSummit全球架构师峰会2017深圳站正在筹备中,更多精彩分享内容点击这里。

你可能感兴趣的:(Twitter机器学习平台的设计与搭建)