智能搜索模型预估框架的建设与实践( 四 )


其中,我们将输入模型的特征名(tf_input_name)和原始特征名(name)做了区分 。这样的话,就可以只在外部编写一次表达式,注册一个公用特征,却能通过在模型的结构体中配置不同Transfomer创造出多个不同的模型预估特征 。这种做法相对节约资源,因为公用特征只需抽取计算一次即可 。

智能搜索模型预估框架的建设与实践

文章插图
 
此外,这一套配置文件也是离线样本生产时使用的特征配置文件,结合统一的OP&Transformer代码逻辑,进一步保证了离线/在线处理的一致性,也简化了上线的过程 。因为只需要在离线状态下配置一次样本生成文件,即可在离线样本生产、在线模型预估两个场景通用 。
4.2 完善预估系统:性能、接口与周边设施
4.2.1 高效的模型预估过程
OP和Transformer构建了框架处理特征的基本能力 。实际开发中,为了实现高性能的预估能力,我们采用了分片纯异步的线程结构,层层Call Back,最大程度将线程资源留给实际计算 。因此,预估服务对机器的要求并不高 。
为了描述清楚整个过程,这里需要明确特征的两种类型:
  1. ContextLevel Feature:全局维度特征,一次模型预估请求中,此类特征是通用的 。比如时间、地理位置、距离、用户信息等等 。这些信息只需计算一次 。
  2. DocLevel Feature:文档维度特征,一次模型预估请求中每个文档的特征不同,需要分别计算 。
一个典型的模型预估请求,如下图所示:
智能搜索模型预估框架的建设与实践

文章插图
 
Augur启动时会加载所有特征的表达式和模型,一个模型预估请求ModelScoreRequest会带来对应的模型名、要打分的文档id(docid)以及一些必要的全局信息Context 。Augur在请求命中模型之后,将模型所用特征构建成一颗树,并区分ContextLevel特征和DocLevel特征 。由于DocLevel特征会依赖ContextLevel特征,故先将ContextLevel特征计算完毕 。
对于Doc维度,由于对每一个Doc都要加载和计算对应的特征,所以在Doc加载阶段会对Doc列表进行分片,并发完成特征的加载,并且各分片在完成特征加载之后就进行打分阶段 。也就是说,打分阶段本身也是分片并发进行的,各分片在最后打分完成后汇总数据,返回给调用方 。期间还会通过异步接口将特征日志上报,方便算法同学进一步迭代 。
在这个过程中,为了使整个流程异步非阻塞,我们要求引用的服务提供异步接口 。若部分服务未提供异步接口,可以将其包装成伪异步 。这一套异步流程使得单机(16c16g)的服务容量提升超过100%,提高了资源的利用率 。
4.2.2 预估的性能及表达式的开销
框架的优势:得益于分布式,纯异步流程,以及在特征OP内部做的各类优化(公用特征 、RPC聚合等),从老框架迁移到Augur后,上千份文档的深度模型预估性能提升了一倍 。
至于大家关心的表达式解析对对于性能的影响其实可以忽略 。因为这个模型预估的耗时瓶颈主要在于原始特征的抽取性能(也就是特征存储的性能)以及预估服务的性能(也就是Serving的性能) 。而 Augur 提供了表达式解析的Benchmark测试用例,可以进行解析性能的验证 。
_I(_I('xxx'))Benchmark              Mode  Cnt  Score   Error  UnitsAbsBenchmarkTest.test  avgt   25  1.644 ± 0.009  ms/op一个两层嵌套的表达式解析10W次的性能是1.6ms左右 。相比于整个预估的时间,以及语言化表达式对于特征迭代效率的提升,这一耗时在当前业务场景下,基本可以忽略不计 。
4.2.3 系统的其他组成部分
一个完善可靠的预估系统,除了“看得见”的高性能预估能力,还需要做好以下几个常被忽略的点: