作为搜索的用户,我觉得最关心的是两个方面:一是召回的结果是否符合预期,二是召回结果的排序是否符合预期。OpenSearch作为一个搜索服务提供平台,在这两个方面我们提供了一定机制方便用户定制自己的召回和排序逻辑。将搜索这边积累的算法功能通过这两个窗口开放给用户使用,从而改善用户的搜索体验。根据定制的粒度和开放的算法功能的特点,我觉得可以分成两个阶段:*定制时期和数据驱动时期。在*定制阶段,用户可以组合我们提供的算法功能,定制自己的召回和排序策略。在数据驱动阶段,我们在*定制的基础上,广泛使用用户历史数据(比如查询日志和行为数据)为用户提供更加个性化的搜索体验,并提供一些功能方便用户快速迭代和试错,同时我们提供干预功能,让用户尽快修复badcase,最大限度的让用户参与进来。
*定制时期OpenSearch算法产品化特点
*定制时期对应于OpenSearch 1.0版本,在这个阶段OpenSearch逐步健全了搜索召回和排序定制体系,打通了召回和排序链路。用户在OpenSearch控制台中可以定制自己的排序公式和qp规则,并在查询中指定失效,对于qp中产出的query信息也会影响最终的排序结果。这一时期的算法产品化功能可以用下图描述,下面我们会从不同的方面进行介绍。
排序表达式定制
Ranking Formula
OpenSearch支持两阶段排序,粗排和精排。这两个阶段都支持用户自定义排序表达式,只是考虑到参与粗排的文档数较多,所以我们限制了粗排表达式的长度和可以使用feature的个数。在后端实现上它们是同一套代码,走的是相同的逻辑,我们称之为ranking formula。ranking formula从功能模块上讲可以分成两个部分,qrs插件和searcher scorer插件。qrs插件主要用来做公式的解析和验证,并将解析好的语法树序列化传给searcher。searcher scorer插件是formula主要执行部分,它反序列化公式的语法树,对每个命中的doc进行算分,并返回最终结果。从用户使用过程上讲ranking formula也可以分成2两个部分,公式创建流程和查询生效流程。用户通过控制台创建公式时,控制台会实时将用户的公式提交给后端进行验证,如果验证失败会将具体的错误信息返回给用户,只有验证成功的公式才用户用户保存。公式创建成功之后,用户可以将其设置成默认公式,这样就会自动上线生效,用户也可以在query中通过公式名直接引用公式(比如通过搜索测试页面测试公式效果)。查询过程中也会对用户的公式进行验证,如果验证失败,此次查询还会返回结果,只是不在使用公式进行排序。
目前ranking formula支持的语法规则比较简单,详细可以参考ranking formula语法规则。在ranking formula中,用户可以使用自己的attribute字段,也可以使用一些数学函数或者是我们提供的一些特征函数,通过对这些元素的组合从而定义出自己的排序表达式。
Feature Lib
feature lib是排序表达式的重要组成部分,用户通过我们提供的feature可以获取doc在某个维度的分数,比如文本匹配分,距离分数等。目前feature lib支持的feature主要有3类:一是文本类feature,通过它可以获取doc的文本匹配方面的分数,比如查询词和doc的匹配度,查询词在doc上的紧密度等;二是具有一定特殊功能的feature,比distance可以获取2点之间的距离,tag_match可以用来做query和doc中kv的匹配;三是数学函数,比如normalize函数,decay函数等。我们通过不断丰富我们的feature lib,可以为用户提供很多强大的功能。
查询分析定制
查询分析主要用于query改写,query意图识别等方面,通过查询分析用户可以扩大召回,获取想要的结果。OpenSearch将查询分析中常用的功能通过封装开放给用户,用户在OpenSearch控制台上通过勾选需要的供能即可定制自己的查询分析策略。用户可以通过控制台上线查询分析策略,也可以在查询中指定使用哪个策略。查询分析的用法和支持的功能可以参考查询分析使用手册。
查询分析是DII进行实现的,DII支持链式处理而且支持在查询中指定策略,但是策略要在配置文件中提前定义。对于OpenSearch来说,每个用户定义的策略是各不相同的,但是后端系统配置不可能随时变化。为了能够支持不同的用户使用不同的策略,我们再系统配置上定义了一个完整的chain,并且在查询的过程中动态组链。从上图也可以看到,整个查询分析处理过程不仅仅是我们提供出去的那些功能,还有一些内置的处理逻辑,比如normalize和分词等,这些处理逻辑在DII上都实现为独立的processor。当查询执行时,我们会根据用户选择的功能和内置的处理逻辑重新组链,这样就可以支持不同用户使用不同策略。
对于OpenSearch来说,我们的用户是B类用户,我们接收到的query是用户加工过的query,是带有AND OR等运算符的query。查询分析要处理的只是最终端用户输入的查询词,如何识别出这些查询词是OpenSearch查询分析需要考虑的。我们发现,用户最常用的加工query的逻辑为将终端用户输入的查询拼上一些条件比如AND status:'1',而且这些条件的索引和终端用户的索引往往不是同一个。所以,我们规定用户的查询词前面必须指定要查询的索引,在查询分析时只分析特定索引下面的查询词。因此,我们在查询分析前会将用户的查询parse为语法树,在遍历这颗树的过程中只对特定索引下面的查询词做处理。
多种类型分词器支持
分词在离线索引构建和在线查询,查询分析中起着非常重要的作用,分词结果的好坏会直接影响检索结果和相关性排序。OpenSearch目前支持多种类型的分词器,比如中文基础分词、全拼分词和简拼分词等,详细信息请参考OpenSearch分词器介绍。在产品上,OpenSearch分词器经历了从字段类型与分词器绑定,到索引与分词器绑定两个阶段。在字段类型与分词绑定阶段,我们每新增一个分词器就要新增一种字段类型,而且用户在使用时对于同一个字段如果要使用不同的分词器那么就要将一个字段复制成另一个不同类型的字段。这大大增加了系统的复杂性和用户的使用成本。举个例子,之前我们会有TEXT、SWS_TEXT、NWS_TEXT、MWS_TEXT、ENG_TEXT、SHORT_TEXT,每种字段类型就对应一种分词,用户title字段要使用通用分词和单子分词,那么title要设置成TEXT类型,title_copy要设置成NWS_TEXT类型。为了减少系统的复杂性和用户的使用成本,我们通过优化schema编码结构,将字段类型和分词器解绑,分词器只和索引相关。这样如果用户title要使用通用分词和单字分词时,只需要在设置索引时建立一个title索引,使用title字段分词器为通用分词,建一个title_single索引,使用title字段分词器为单字分词。在不同场景下使用不同的分词器,极大丰富了用户搜索形式和搜索结果的多样性。
数据驱动时期OpenSearch算法产品化的探索
OpenSearch上的用户来自各行各业,每个行业的数据都有其特殊的地方。OpenSearch第一阶段提供的算法和模型是基于全网数据训练出来的,在适配到各个用户时不可避免的会出现一些badcase。面对这些badcase,用户最自然的需求就是希望能对其进行干预。针对我们提供的分词和查询分析,用户希望能够干预分词结果,干预停用词、词权重、同义词、纠错等处理结果。另一方面,随着淘宝千人千面的巨大成功,搜索结果个性化的需求越来越多,用户希望我们能够提供更丰富的feature,帮助他们获取更好的排序效果。同时用户还希望我们能够提供快速试错,快速验证的功能,方便用户快速迭代。从OpenSearch自身来讲,我们返回给用户搜索结果之后获取不到用户的反馈,不知道返回的结果是否满足用户的需求,我们和用户之间缺少反馈机制,不能利用反馈来迭代我们的算法。
根据用户反馈的问题和OpenSearch自身发展计划,我们开始了新一轮的算法产品化探索。首先我们提供了针对badcase的快速干预机制,提供了用户行为数据收集机制,提供AB test和数据报表功能方便用户进行快速迭代和效果评估。同时,我们升级ranking formula为脚本语言,让用户可以定制更加复杂的排序逻辑。利用用户的查询日志和行为数据,结合用户的原始文档为用户提供更加高效的算法功能,丰富我们的feature lib。在训练用户数据的过程中,我们总结了通用的机器学习流程,并沉淀到算法服务平台,可以让高级用户干预模型训练过程。这些功能有的已经支持,有的正在进行中,下面我们详细介绍。
自定义分词
为了方便用户干预分词结果,OpenSearch在产品上支持了自定义分词器。用户的自定义分词器由系统分词器和用户自定义词典组成,分词时会同时加载系统词典和用户词典,从而实现干预分词的目的。在实现上,我们使用alinlp替换了aliws,并基于DII构建了分词服务。在用户索引重建时,如果用户使用了自定义分词(信息保存在用户schema中),对于用户的doc,processor分词时会访问分词服务获取分词结果。在查询过程中,agg会根据应用是否配置了自定义分词来决定是否访问分词服务,对于访问了分词服务的查询会设置对应的index为不分词,这样qrs就不会做二次分词了。自定义分词的实现逻辑可以参考OpenSearch自定义分词服务。
干预服务
随着我们提供的算法功能越来越多,出现badcase之后用户需要干预的地方也会越来越多。干预服务制定了统一的标准和流程,方便的对接各个算法模块,快速实现对某个功能的干预。干预服务本身不涉及都具体的业务逻辑,它只是接收用户的干预词条,并将其推送给对应算法模块实时生效。目前依托干预服务,我们实现了对停用词、词权重、同义词和纠错等功能的干预。干预服务的实现细节可以参考OpenSearch在线干预服务。
多版本的支持
用户在日常使用和功能升级过程中,不可避免的会有索引重建的需求。在OpenSearch之前的实现中,应用在OpenSearch内部只有一个实例,索引重建都是在这个实例上进行的。如果用户的修改有问题(比如schema修改错误)或者数据源用问题,索引重建会直接影响线上的服务。而且索引重建周期比较长,从发现问题到恢复的周期很长,对线上服务的影响会很大。为了减少用户变更对线上服务的影响,方便用户快速的回滚,我们引入了多版本概念,即一个应用在OpenSearch内部可能会对应多个实例。多个实例之间数据隔离,实例之间可以*切换,所以当发现问题后可以秒级切换到老的实例继续提供服务,系统健壮性大大提高。目前OpenSearch对一个应用最多只支持两个版本,OpenSearch多版本可以描述如下图所示。
AB test功能
为了方便用户快速迭代和试错,OpenSearch提供了AB test和实验指标统计功能。借助内网成熟的AB test实验平台hyperspace,OpenSearch搭建了自己的AB test系统,并且为了方便用户的使用做了一些概念的抽象和功能的封装。得益于OpenSearch以前的设计,查询时可以根据公式名或者qp规则名直接引用相关的公式或qp规则。在设计AB test实验时,我们支持用户将粗排和精排公式,查询分析规则甚至是算法模型名称做为独立的实验参数。查询时根据流量分配因子,决定查询是否命中实验,命中实验时用实验的参数替换查询中的参数,这样即可对不同的排序公式和查询规则进行实验对比。OpenSearch AB test功能的架构如下图所示,具体的使用和实现细节请参考ABtest在OpenSearch上的设计与实现。
行为数据收集
OpenSearch将搜索结果返回给用户之后,我们并没有提供一种机制来回收用户的反馈,整个搜索链路还未完全打通。我们不知道我们返回的结果用户是否满意,我们也拿不到终端用户的反馈帮助用户改进其搜索体验。同时,我们希望将主搜这边沉淀多年的算法输出给外网用户使用,这些算法模型的训练都需要用户的行为数据。因此,收集做为算法模型训练基础的行为数据就变的异常重要。目前OpenSearch支持server端行为数据收集,即用户可以将收集到行为数据通过API推送给OpenSearch,至于web端和移动端采集目前还未支持。为了方便内网用户使用,OpenSearch行为数据采集所支持的行为数据格式和内网UT的格式一致。这样内网用户如果需要对接OpenSearch只需要将UT采集的数据转发一份给OpenSearch即可。关于行为数据收集的实现细节请参考OpenSearch数据采集介绍。
排序表达式语言化
ranking formula在实际应用中存在一些问题,首先为了控制粗排和精排开销我们限制了formula的长度,比如粗排限制只能使用4个元素,而且仅限线性运算,二是formula中不支持变量定义,三是不支持循环操作,四是无法直接引用引擎中的数据比如match doc。所以说ranking formula只能基于我们提供的feature用户做简单的定制。我们希望能够将formula发展成一种语言,可以解决上面4个问题。用户使用这个语言可以在线开发自己的算分插件,实时开发、实时测试、实时上线。目前引擎同学已经做了一些工作,OpenSearch需要在此基础上做一些包装提供给用户使用。关于排序表达式语言化的底层实现细节可以参考cava。
算法服务平台建设
第一次试水--类目预测功能
在构建类目预测和行为数据收集等算法产品化基础设施的同时,我们也在考虑将集团内成熟的算法功能开放给用户使用。我们以类目预测为原型开始了第一次试水。类目预测是电商领域一个非常重要的特征,对搜索结果排序有着非常大的影响。我们抽象了类目预测模型训练与生效流程,架构图如下所示:
用户在前端控制台创建其类目预测模型,设置模型训练所需的参数,比如title字段与类目字段,并将其保存下来。离线训练任务可以手动触发,也可以定时触发,整个训练流程由rainbow控制。rainbow首先调用sync service将用户的训练数据导入到odps中,然后调用aflow执行预先定义好的模板进行类目预测模型训练,最后rainbow调用autoumars(qp ops)将训练好的数据切换上线。训练数据除了用户的原始doc外,还需要用户的查询日志和点击日志。Agg会记录用户的查询请求,通过sls将日志同步到odps,通过日志解析job可以得到每天每个应用的查询日志。至于点击数据,我们支持用户通过odps上传自己的点击日志(query:click_count格式),也支持使用通过我们行为数据收集得到的点击日志。
算法服务
在支持类目预测过程中,我们发现类目预测模型训练对用户来说就是个黑盒子。首先用户不知道自己训练数据的质量情况,其用户不知道自己训练数据经过了哪些处理,再次用户无法干预模型的执行,无法调整模型的参数,最后模型训练的效果只能通过AB test进行测试,反馈周期过长,对于一些明显的错误发现的较晚。同时,我们调研了下期要开发的算法功能,比如人气模型和CTR预估等。我们发现这些模型训练(机器学习)其实是有些固定的流程的,比如数据处理,特征工程,模型训练,模型评估等。我们希望能够提供一个算法服务平台,在平台中抽象出机器学习流程,在每个流程中提供多个选项供用户选择。这样对于初级用户可以直接使用我们提供的算法功能,对于高级一些的用户可以通过我们的平台进行特征工程,调整模型训练参数从而对我们提供的算法模型进行定制。算法服务在OpenSearch体系中的位置如上图所示,底层依托于集团强大的算法训练平台,为OpenSearch查询理解、排序和其他搜索外围功能提供模型训练能力。关于算法服务的其他内容请参考OpenSearch算法服务。
小结
OpenSearch从立项之初到现在从未停止过对算法产品化的探索,在机器学习深入人心大行其道的今天,我们才刚刚起步。我们希望通过OpenSearch将淘宝算法多年的积累,以更低的门槛开放给更多的人使用。作为一个平台型的产品,在提供低门槛的算法功能的同时,可定制化是不可或缺的。只有可以定制化,让用户参与进来我们的平台才会更有活力,才能解决不同用户的问题。
推荐与搜索技术钉钉交流群