一、背景介绍

本文中重点介绍 NLP 模型开发平台在百分点舆情洞察系统 (MediaForce) 中的设计和实践。MediaForce 是一款面向政企客户,提供信息监测、智能分析等多功能的一款 SaaS 产品。从 2014 年发展至今,客户标准化的建立以及数据资产的积累,为开展自动化和智能化打下了坚实基础。对内要提高生产和运营效率,缩短行为结果的反馈时间;对外要提供个性化服务,提高客户亲密度。

舆情信息是通过关键词检索来获取对应的相关数据, 在基于 BM25、TF-IDF 等传统信息检索机制下,只是考虑关键词和文档的匹配程度,忽略了文档主题、查询理解、搜索意图等因素,致使召回文档与客户诉求相差较大。另一方面,在客户定制化场景下,需要人工对客户数据进行标签处理,这是一个极其费时费力的过程。

在一个 NLP 模型开发任务中,一般包括如下三个大模块:

在早期,主要是围绕和重复这三个模块来支持业务。在业务规模小时,人工方式保证了工作的灵活与创新突破,但是随着业务模式的成熟与增长,逐渐凸显出人工方式的局限性,主要体现在如下几个方面:

(1)NLP 模型开发任务的增多,无疑增加开发人员的维护工作,尤其是在算法迭代更新、模型版本管理等方面,将是灾难性质的。

(2)业务人员是核心业务的把控者,但是由于模型学习门槛相对较高,使其参与度大大降低。

而 NLP 模型开发平台的构建不仅能解决以上问题,也更将聚焦算法工程师模型开发和基准验证,使分工更加明确、全民参与。集数据管理、模型全生命周期管理、计算资源和存储资源统一管理等特性,力求达到以下目标:

(1)复用性:通用算法集成、算法管理、避免重复造轮子。从脚本开发到可视化操作,专注于算法效果提升和模块复用。

(2)易用性:即便是运营 (业务) 人员,也可以定制私有业务模型,真正实现业务赋能。依据自己的个性化诉求可进行数据标注、模型训练、效果评估、模型发布等操作。

(3)扩展性:算力资源可扩展、模型算法框架 (TF、Pytorch、H2o) 可扩展、语言 (Java、Python、R) 可扩展。

二、NLP 模型开发工具栈回顾

在传统软件开发中,我们需要对程序的行为进行硬编码,而在 NLP 机器学习模型开发中,我们将大量内容留给机器去学习数据,开发流程上有着本质的不同,如下图所示:

许多传统软件工程工具可用于开发和服务于机器学习任务,但是由于机器学习的特殊性,也往往需要自己的工具。比如:Git 通过逐行比较差异来进行版本控制,适用于大多数软件开发,但是不适合对数据集或模型检查点进行版本控制。在 2012 年随着深度学习的兴起,机器学习工具栈种类和数量爆炸式增长,包括 All-in-one(一站式机器学习平台):Polyaxon、MLFlow 等,Modeling&Training(模型开发、训练):PyTorch、Colab、JAX 等,Serving(发布、监控、A/B Test):Seldon、Datatron 等。下图表明 MLOps 每种类型的工具数目:

对机器学习工具栈进行细化包括:标注、监控、版本管理、实验追踪、CI/CD 等,详细内容不再赘述,详情参照下图:

可以看到机器学习工具栈种类和数量目前是极其繁多的,有些是面向 OSS 的,有些是商业收费的。下图主要举例在不同种类的工具栈上的产品:

三、NLP 模型开发平台构建

1. AI 训练模型的基本流程简介

(1)分析业务需求:在正式启动训练模型之前,需要有效分析和拆解业务需求,明确模型类型如何选择。

(2)采集、收集、预处理数据:尽可能采集与真实业务场景一致的数据,并覆盖可能有的各种数据情况。

(3)标注数据 :按照规则定义进行数据标签处理。如果是一些分类标签,可以在线下直接标注;如果是一些实体标注、关系标注就需要对应一套在线标注工具进行高效处理。

(4)训练模型:训练模型阶段可以将已有标注好的数据基于已经确定的初步模型类型,选择算法进行训练。

(5)效果评估:训练后的模型在正式集成之前,需要评估模型效果是否可用。需要详细的模型评估报告,以及在线可视化上传数据进行模型效果评估,并且在灰度环境进行业务验证。

(6)模型部署:当确认模型效果可用后,可以将模型部署至生产环境中。同时要支持多版本管理、AutoScale 等功能。

2. 整体架构

(1)分布式存储包括 NFS、HDFS、CEPH。HDFS 是存储原始数据以及样本特征,NFS 是存储训练后的模型文件,CEPH 是 K8S 集群的文件分布式存储系统。

(2)底层计算资源分为 CPU 集群和 GPU 集群,高性能 CPU 集群主要用于部署和训练传统的机器学习模型,GPU 集群则用来部署和训练深度 (迁移) 学习模型。

(3)资源不同,计算的选型也有差别。机器学习训练使用 Alink 做计算,通过 Yarn 来调度计算资源;深度学习训练使用 K8S 做调度,支持主流的 Pytorch、Tensorflow、PaddlePaddle、H2o 等深度学习框架,目前只是做到单机训练,而模型的部署都是借助 K8S 进行统一发布和管理。

模块对外提供数据标注、模型训练、模型评估、模型管理、模型部署、模型预测等功能,同时平台还抽象出分类、NER、评估、预测等组件。

3. 平台构建实践

平台上层提供了一套标准的可视化操作界面,供业务运营人员使用,平台底层提供全生命周期的模型管理,支持上层应用扩展。

上文主要介绍 NLP 模型开发平台构建的基本流程和整体架构 ,本章节会对技术选型与实践进行展开。

(1)容器管理调度平台选型

主流的容器管理调度平台有三个,分别是 Docker Swarm、Mesos Marathon 和 Kubernetes。但是同时具备调度、亲和 / 反亲和、健康检查、容错、可扩展、服务发现、滚动升级等诸多特性,非 Kubernetes 莫属。同时基于 OSS 的机器学习工具栈大多也都是基于 Kubernetes 而进行的上层开发和应用,像我们熟知的 Kubeflow 等。另一方面深度学习领域通常是用 GPU 来做计算的,而 Kubernetes 对 GPU 卡的调度和资源分配有很好的支持和扩展。比如现在集群有多种类型的 GPU 卡,可以对 GPU 节点打上 label,启动任务配置 nodeSelector 实现卡类型的精准分配。最终我们选择用 K8S 作为平台的容器管理系统。

(2)GPU 资源调度管理

目前较新版本的 docker 是支持 NVIDIA GPU 的 runtime,而不再考虑旧版的 nvidia-docker 或者 nvidia-docker2。其实在 runtime 基础上,是可以直接使用 GPU 来执行深度学习任务的,但是却无法限定 GPU 资源以及异构设备的支持。这里主要给出两种解决方案:

a. Device Plugin

为了能够在 Kubernetes 中管理和调度 GPU, Nvidia 提供了 Nvidia GPU 的 Device Plugin。主要功能如下:

  • 支持 ListAndWatch 接口,上报节点上的 GPU 数量。

  • 支持 Allocate 接口,支持分配 GPU 的行为。

但是这种机制导致 GPU 卡都是独享的。特别是在推理阶段,利用率是很低的。这也是我们采用第二种方式的主要原因。

b. GPU Sharing

GPU Device Plugin 可以实现更好的隔离,确保每个应用程序的 GPU 使用率不受其他应用程序的影响。它非常适合深度学习模型训练场景,但是如果场景是模型开发和模型推断,会造成资源浪费。所以要允许用户表达共享资源的请求,并保证在计划级别不会超额订购 GPU。我们这里尝试 Aliyun 在 GPU Sharing 上的开源实现,如下图所示:

在配置文件中,限定显存大小,这里的单位是 GiB:

执行如下命令:

在 11GiB 的显卡上,GPU 分配状况如下:

(3)网关选型

在使用 Kubernetes 的 Service 时,一个必须要面对和解决问题是:如何从外部(kubernetes 集群之外),访问到 Kuberentes 里创建的 Service?这里最常用的一种方式就是 NodePort。它允许你使用任何一台宿主机 IP 进行访问。这种方式需要事先指明 nodePort 或者随机生成 nodePort,对接口资源较难管理。而使用像 Kong、Nginx、HAProxy 等主流网关所面临的问题是:不是自服务的、不是 Kubernetes 原生的、为 Api 管理而设计,而非微服务。Istio 是微服务的服务网格,旨在将应用层(L7)的可观察性、路由和弹性加入到从服务到服务的流量中。而模型部署需要与 Kubernetes 深度融合,也不需要进行服务间的调用,最后选用 Ambassador 作为最后网关选型。Ambassador 作为一个较新推出的开源微服务网关产品,与 kubernetes 结合得相当好,基于 annotation 或 CRD 的配置方式与 K8S 浑然一体,真正做到了 kubernetes native。下面给出一个实际中的一个例子:

其中 timeout_ms 默认值为 3000,在使用 Cpu 做推理设备时,可能会出现超时的情况,这里依据不同的场景对该值进行微调,以满足使用。同时可以从 Route Table 中查看相应的 URL。

(4)可视化

这里的可视化是指在进行模型训练过程中,需要对模型性能进行评测和调优。这里率先融入 Tensorboard,随后也将百度的 VisualDl 融入进去。在训练过程中启动一个单独的容器,暴露出接口供开发者查阅和分析。

(5)模型部署

在第二章节中,介绍了不同功能的机器学习工具栈。在模型部署中我们使用 Seldon Core 来作为 CD 工具,同时 Seldon Core 也被 Kubeflow 深度集成。Seldon 是一个可在 Kubernetes 上大规模部署机器学习模型的开源平台,将 ML 模型(Tensorflow,Pytorch,H2o 等)或语言包装器(Python,Java 等)转换为生产 REST / GRPC 等微服务。

下面是推理镜像的构建过程,其中 MyModel.py 是预测文件:

其中部分 deployments 描述文件如下:

四、平台应用和成效

NLP 模型开发平台的构建极大地降低模型学习门槛,使业务人员不仅可以参与规则的制定,也可以参与到数据标注、服务发布、效果评估等多个阶段。同时使数据科学家和机器学习工程师能更加专注于模型本身的算法和性能,极大地提高工作效率、简化工作流程。下面举例借助平台在数据相关度、标签处理等方面的成效。

1. 相关度

在过去几十年中,已经实现了各种自动信息检索系统。文档的有效表示是能够检索文档的核心,像矢量空间模型和概率模型都依赖于 TF、IDF、文档长度等特征因素。将文档从文本转换为数字或基于矢量的表示形式,排序功能就需要根据特定的查询的相关性对文档进行排序。其中 Okapi BM25 是 IR 中最著名和使用最广泛的排序算法。传统信息检索方法没有考虑语义信息等诸多因素。而随后的 Bert 在 GLUE 中 IR 相关的基准测试达到最优,其中一部分原因是因为其大量的训练数据。此外基于 Transformer 神经网络架构促进了输入中各个 token 之间的深层关系,从而使模型可以更好地了解输入中存在的关系。在真正应用中,需要考虑查询意图、查询改写、同义词扩展等诸多技巧。下面将阐述在提高检索相关度方面的尝试和方案的演进,以及 NLP 模型开发平台在这方面的成效和应用。

(1)基于查询意图的传统信息检索

舆情中的搜索往往是词或短语,在缺少外部知识的情况下,搜索意图往往无法得知。在使用 Okapi BM25 传统的信息检索方式,只能得到查询关键词与文档相关,而并不符合搜索意图。在当时的架构下,主要是基于 Elasticsearch 的全文检索,以便考虑能否使用 ES 得出一个比较通用的处理框架。Elasticsearch 是基于 Luence 的架构。很多要素都是一脉相承的,例如文档和字段的概念、相关性的模型、各种模式的查询等。流程如下图所示:

这里的意图扩展库其实是对查询关键词进行扩展,例如,关键词为"真功夫",如果你的搜索意图指的是餐饮品牌“真功夫”,那么可以扩展一系列行业相关词:餐饮、门店、优惠券等。联合 Query 一起查询,这里的意图扩展库(扩展相关词)只是贡献权重得分,而不作为检索过滤条件,使用 ES 中 should 语句即可实现。这种机制在一定程度上缓解了数据相关度问题,特别是在垂直领域中,效果甚佳。而一旦涉及跨领域、搜索意图宽泛,就显得无能为力。

(2)基于 Bert 分类模型应用

在以上的实现机制中,都是无监督排序算法的典范,这样的排序算法并不是最优的。在极度简化的情况下,如果标签定义为,某个文档针对某个关键字是否相关,也就是二分标签,训练排序算法的问题就转换成了二分分类(Binary Classification)的问题。这样,任何现成的二分分类器,几乎都可以在不加更改的情况下直接用于训练排序算法,比如经典的“对数几率”分类器或者支持向量机都是很好的选择。这类算法称之为“单点法排序学习”(Pointwise Learning to Rank)。这种机制与我们的应用场景十分吻合,只不过将 Query 上升为话题维度。而像 DSSM 等经典的文本匹配算法解决的是查询串与文档的匹配程度,查询串往往是句子,而不是词语。因此我们将相关度问题转化为二分分类问题,召回阶段使用 Elastcsearch 索引库检索,排序阶段使用分类器对召回的文档进行判定。

在这种机制下,为客户提供了个性化服务。在 NLP 模型开发平台的助力下,进行一站式处理,并且可以实现版本的迭代优化。

2. 离线标签

在一些定制化场景下,需要对离线数据进行标签化处理。这是一个费时费力的过程,并且之前的劳动无法为后续的工作赋能。我们通过标注模块对已有数据进行整合,并且对新标签的样本数据进行标注,从而快速为业务赋能,解放生产力。

以及在实体识别的场景下,可以直接在标注模块进行标注:

五、平台展望

1. 标注功能完善

在文本分类、NER 等基础标注任务的基础上,还需要增加关系标注、seq2seq 等主流任务的支持,以及任务分配、多人协作等特性。

2. 丰富算法模块

在满足基础需求下,还需要增加文本匹配等算法模块,满足更加广泛的应用场景。

3. 打造 Piplines 流水线式 NLP 模型开发平台

模型训练以及模型评估目前是耦合的,不利于组件模块复用,所以要按照细粒度的模块进行独立拆分,再按照 Pipline 方式自由组合,来达到最大利用率。

参考资料

[1] https://huyenchip.com/2020/06/22/mlops.html

[2] https://github.com/AliyunContainerService/gpushare-scheduler-extender

[3] https://docs.seldon.io/projects/seldon-core/en/latest/