突破深度模型线上耗时瓶颈,我们做了什么?

news2024/11/19 22:32:04

广告投放是深度模型应用较为普遍的场景之一,虽然深度模型能够提升业务效果,但往往也会付出更加高额的耗时开销。滴滴现今 DSP(Demand-Side Platform) 业务场景中,耗时问题已然成为限制模型发挥的魔咒,为了打破魔咒,我们探索了一套解决方案,可以让深度模型极大限度摆脱耗时困扰。

原理概述

背景

DSP 先前的线上深度模型基于 CPU + Tensorflow Feature Column 的方式实现,借助 Tensorflow 框架和 Feature Column 的结构化数据处理能力以快速构建深度模型。该方式带来便利的同时,也会牺牲推理性能,随着业务策略迭代日趋深入,性能问题也愈发凸显。

DSP 参竞链路有着严格的耗时要求,策略模型筛选素材并给出最终结果的 P99 耗时需要控制在 40ms 内。当前精排模型 P99 耗时已经超过 30ms,超过 75% 的计算都消耗在精排阶段,其导致 DSP 链路上各个阶段都难以开展优化。

DSP 系统各部分理想情况下的耗时:

6a7058e06bb483457b3787d42742e3a1.png

在排除网络开销和模型本身问题后,我们发现耗时主要消耗在计算上,因此尝试切换 GPU 进行计算以提高计算性能。单纯的计算场景下 GPU 的确拥有更高的性能,但是用于 Tensorflow 模型推理,耗时却不降反增,经过深入分析,我们发现问题出在了 Feature Column 上。

由于硬件架构差异,Feature Column 特征处理无法完全在 GPU 上进行,部分特征处理过程会被转到 CPU 上,设备之间切换反而会影响整体性能, 其中部分字符串转换操作还会涉及到内存复制与分配,尤为耗时。

降低耗时的核心是分离模型计算和特征处理,为此,我们提出了一套特征外置方案来解决这个问题,并开发了一套同时支持在线和离线的特征处理组件 EzFeaFly(Easy Feature Fly)。

基本原理

当前架构下,DSP 算法能力由两套服务共同支持,策略端服务承接业务系统的预估请求,获取特征后调用模型推理服务进行预估。

为了剥离特征处理部分,我们在 Tensorflow 中彻底舍弃了 Feature Column。策略端服务获取特征后直接利用 EzFeaFly 处理特征,随后传入模型推理服务。传入特征经过插件转换成 Tensor 之后直接喂入模型进行推理计算。

c5185a1885b0f9c559fb76b75268a7f2.png

新的方案在保持整体系统架构不变的情况下,优化了整体各方资源的利用效率:

  • 解耦特征处理与计算:合理分配计算资源,特征处理并发提效,模型计算利用 GPU 提效;

  • 特征处理行为一致性:线上和离线使用同一套工具处理特征,处理结果的一致性有保障。

离在线流程

整套特征外置方案不仅只考虑线上推理提效,还包括了离线模型训练和部署,其详细流程如下图所示:

6857773228624c46bbc83acedcdb67e5.png

离线模型训练:

  • EzFeaFly 工具可编译生成 bin 文件,通过预先设置的特征处理配置,可直接用于处理特征;

  • 利用 Spark + bin + 配置文件 的方式,可实现批量处理大规模样本,最终生成 TFRecords 落盘 HDFS;

  • 模型训练时直接读取 HDFS 上的 TFRecords,训练好的模型可直接部署上线提供推理服务。

在线推理:

  • 离线训练模型使用的特征配置可直接同步线上进行统一管理;

  • 策略端服务基于同步的配置获取并使用部署线上的 EzFeaFly 工具处理特征;

  • 处理后的特征可直接传入模型推理服务获取推理结果 。

使用效果

特征外置方案已经在 DSP 中得到全面应用,整体性能提升巨大,机器成本和耗时均显著下降:

  • 机器实例数量大大减少,成本大约节省 40%

  • 相同 QPS 压力下,推理耗时降低 70% ~ 80%(左图);

  • 考虑到特征处理过程中 EzFeaFly 自身的耗时,对比模型请求预估全链路整体耗时,新方案仍有 60%+耗时下降(右图)。

da7202f0b6c28a4eaba9f3bf0b959da3.png

得益于耗时优化,模型策略的想象空间也变得更大:DSP 线上可以使用更多特征、迭代更为复杂的模型,并且也为级联模型其他阶段提供了更大的优化空间。

详细设计

 EzFeaFly 功能模块

EzFeaFly 作为特征外置方案的核心组件,其功能设计上融入了如下几点考虑:

  • 多场景:同时支持线上和离线特征处理;

  • 一致性:线上和离线处理特征的行为和结果保持一致;

  • 可扩展:便于后续添加新的特征处理方法。

基于上述考虑,我们自顶向下对功能模块进行了层级划分:

e8b66c3056e4fe8c23721665daf84c89.png

  • Manager :工具外层 wrapper,处理请求、IO、数据等,在线和离线使用不同的 Manager;

  • Extractor :特征处理引擎,在线和离线 Manager 均使用相同的 Extractor 处理特征;

  • Parser :特征配置、依赖关系解析,生成具体的特征处理单元;

  • FeatureBase :特征处理单元,管理特征的基础信息和处理逻辑(即特征算子);

  • Operator :特征算子,特征处理中实际计算模块;

  • Utils :算子处理特征过程中使用的通用基础方法。

特征处理过程

由于在线和离线处理特征样本过程基本一致,并且离线处理过程可以视为线上处理过程的超集。因此,这里以离线为例,通过下图简单表述特征处理过程:

f99538a53388b4c5c526e0cc004c6a74.png

离线处理需要三个文件,其作用和对应的线上方案如下所示:

文件

离线作用

线上方案

feature_sql.sql

特征数据源

直接通过特征服务获取实时特征

parser.conf

告诉工具文件中读取的特征列是什么,label 列是什么,样本 id 列是什么

无需此项,线上根据特征处理配置自动解析需要获取的特征

feature_list.conf

特征处理过程的描述。使用什么算子,算子输入什么,结果如何输出等

离线配置可直接部署到线上使用

特征处理结果与模型输入

考虑到线上传输成本以及模型侧的易用性,我们将 EzFeaFly 处理后的 稀疏向量 转换成了一种 稀疏表示的 Dense 向量,即

  • 本质上数据仍是 Dense Tensor;

  • 其内容为特征的稀疏表示,奇数位表示 index,偶数位表示对应的 value, [ k1, v1, k2, v2, ...]。

因此,构建模型时需要对输入 Tensor 进行拆分,以确保获取到正确的 index 和 value。如下为特征 Tensor 的拆分示意:

f28c7da15eb06b3c54d48b580ee0d264.png

该拆分步骤对应具体的代码如下:

# 特征个数
FEATURE_NUM = 4


# 输入层长度 = FEATURE_NUM * 2,数据类型 tf.float32
inputs = tf.keras.Input(shape=[FEATURE_NUM * 2], dtype=tf.float32, name="feature_inputs")


# 提取全部 index - 减 1 是因为工具提取特征最小 index 为 1
input_index = tf.cast(inputs[:, 0::2], tf.int64) - 1


# 提取全部 value
input_value = tf.cast(inputs[:, 1::2], tf.float32)


# 构建模型
model = tf.keras.Model(inputs=inputs, outputs=[···])

全局 Embedding

EzFeaFly 处理特征产出全局 index,即特征 index 已经包含了特征之间的相对偏移,出于计算性能考虑,可以利用 index 构建全局 embedding 层。

  • 模型内部按照特征展开总长度 m 和 embedding 长度 n,维护一个 m · n 的 embedding 矩阵;

  • 使用时,通过全局 index 向量做 embedding_lookup 即可。

261b3ab66113e31de1e7e3cb600e976a.png

具体使用如下所示,这里对 embedding 做了一层封装,结果会直接返回 [batch_size, fea_num * embedding_size] 的向量。

import tensorflow as tf


class IndexEmbedding(tf.keras.layers.Embedding):


    def __init__(self,
                 dense_fea_dim,
                 embedding_dim,
                 sparse_fea_dim,
                 embeddings_initializer="uniform",
                 embeddings_regularizer=None,
                 activity_regularizer=None,
                 embeddings_constraint=None,
                 mask_zero=False,
                 input_length=None,
                 **kwargs):
        
        super(IndexEmbedding, self).__init__(
            input_dim=dense_fea_dim,
            output_dim=embedding_dim,
            embeddings_initializer=embeddings_initializer,
            embeddings_regularizer=embeddings_regularizer,
            activity_regularizer=activity_regularizer,
            embeddings_constraint=embeddings_constraint,
            mask_zero=mask_zero,
            input_length=input_length,
            **kwargs
        )
        self.sparse_fea_dim = sparse_fea_dim
        self.out_dense_dim = sparse_fea_dim * embedding_dim


    def build(self, input_shape=None):
        name = "index_embedding_{}_{}".format(
            self.input_dim, self.output_dim)


        self.embeddings = self.add_weight(
            shape=(self.input_dim, self.output_dim),
            initializer=self.embeddings_initializer,
            regularizer=self.embeddings_regularizer,
            constraint=self.embeddings_constraint,
            experimental_autocast=False,
            name=name
        )


        self.built = True


    def call(self, feature, **kwargs):
        _embedding = tf.nn.embedding_lookup(self.embeddings, feature)
        embedding = tf.reshape(_embedding, [-1, self.out_dense_dim])


        return embedding




# 按照输入结构直接拆分
input_index = tf.cast(feature[:, 0::2], tf.int64) - 1
# input_value = tf.cast(feature[:, 1::2], tf.float32)


# embedding
embedding = IndexEmbedding(
    dense_fea_dim=20, 
    embedding_dim=8,
    sparse_fea_dim=4
)(input_index)

在线部署

在线部署完全是一套流程化的操作,基于策略端服务上下游,用户逻辑上需要执行4步操作:

  • 基于特征服务,注册和上线使用的特征;

  • 基于配置同步服务,同步特征处理配置到线上;

  • 基于模型服务,将离线训练的模型部署到线上;

  • 配置策略 workflow,控制流量进入策略,触发线上服务运行。

d6be9cb179d98ebe85329fa17babf020.png

写在最后

目前,项目取得阶段性成果,降本提效的同时,也为业务带来了正向收益。整个项目得以推进,离不开项目所有成员的共同努力!在此特别感谢团队各位同学的支持,以及密切合作的工程和算法同学,还有所有帮助我们不断完善工具的使用方。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1609980.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构-链表刷题集(长期更新)

文章目录 1. leetcode 2 两数之和1.1 解法一 1. leetcode 2 两数之和 1.1 解法一 题目及其相关实例如下 要做这个题,首先我们要学会模拟竖式的加法,我们知道即使是java基本数据中最大的long类型范围也是有限的,那如果超出范围了我们该怎么办呢,我们就需要用字符串来模拟这个…

【JavaSE】JDK17的一些特性

前言 从springboot3.0开始,已经不⽀持JDK8了 选⽤Java17,概括起来主要有下⾯⼏个主要原因 JDK17是LTS(⻓期⽀持版),可以免费商⽤到2029年。⽽且将前⾯⼏个过渡版(JDK9-JDK16) 去其糟粕,取其精华的版本JDK17…

HarmonyOs开发:导航tabs组件封装与使用

前言 主页的底部导航以及页面顶部的切换导航,无论哪个系统,哪个App,都是最常见的功能之一,虽然说在鸿蒙中有现成的组件tabs可以很快速的实现,但是在使用的时候,依然有几个潜在的问题存在,第一&a…

C++相关概念和易错语法(6)(运算符重载)

1.运算符重载注意事项: (1)多个同一运算符重载可构成函数重载 (2)在成员函数中由于隐含了this指针,外部调用看上去前置和后置不会有任何区别,所以为了区分这个在后置时强制引入参数int&#x…

C++:模板详解

模板详解 1.函数模板1.概念2.语法3.原理4.实例化1.隐式实例化2.显示实例化 5.匹配原则 2.类模板1.格式2.实例化 3.非类型模板参数注意点 4.特化1.概念2.函数模板特化1.前提2.语法说明3.示例 3.类模板特化1.全特化2.偏特化/半特化3.选择顺序 4.按需实例化 5.模板的分离编译1.分离…

玄子Share-计算机网络参考模型

玄子Share-计算机网络参考模型 分层思想 利用七层参考模型,便于在网络通信过程中,快速的分析问题,定位问题并解决问题 将复杂的流程分解为几个功能相对单一的子过程 整个流程更加清晰,复杂问题简单化 更容易发现问题并针对性的…

Labview2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 LabVIEW是一种由美国国家仪器(NI)公司开发的程序开发环境,它显著区别于其他计算机语言,如C和BASIC。传统的计算机语言是基于文本的语言来产生代码,而LabVIEW则采用图形化…

【Python】函数基础(纯干货版)

目录 什么是函数 函数定义 函数的文档说明 局部变量和全局变量 综合案例:模拟实现ATM界面 什么是函数 函数是组织好的,可重复使用的,用于实现特定功能的代码段,将功能封装在函数内,可供随时随地重复利用&#xff…

代理IP对网络爬虫有什么影响?

代理IP对网络爬虫的影响深远且多方面,主要体现在以下几个方面: 第一点,代理IP能有效防止爬虫IP被封禁:在爬虫工作过程中,如果频繁访问同一目标网站,很容易被该网站的服务器识别为恶意行为,导致…

Pytest精通指南(22)钩子函数-重复执行(pytest-repeat)

文章目录 前言应用场景插件安装参数分析使用方式一:命令行使用方式二:配置文件使用方式三:装饰器 前言 pytest框架中的**重复测试(pytest-repeat)**插件的用途是允许在运行测试用例时进行多次循环,以更全面…

阿里二面凉了,难蹦。。。

分享一位同学阿里巴巴的后端面经,共有 2 面,第一面很顺利过了,可惜挂在第二面。 这两面的知识点范围,我帮大家罗列一下: 网络:TCP、HTTP mysql:索引应用、索引结构、隔离级别、最左匹配 redis…

流程控制:goto语句,模拟switch语句

示例&#xff1a; /*** brief how about goto-switch? show you here.* author wenxuanpei* email 15873152445163.com(query for any question here)*/ #define _CRT_SECURE_NO_WARNINGS//support c-library in Microsoft-Visual-Studio #include <stdio.h>static …

循环购模式:创新消费返利,引领电商新潮流

大家好&#xff0c;我是吴军&#xff0c;今天我将与大家探讨一种别具一格的商业模式——循环购模式。或许您曾经听说过消费满额赠送现金的活动&#xff0c;甚至每天都能累积并提取部分现金。您可能会好奇&#xff0c;商家为何如此慷慨地“回馈”消费者&#xff1f;这背后其实蕴…

QT C++ sqlite 对多个数据库的操作

//本文描述&#xff0c;QT 对多数据库的操作。 //你可能会想&#xff0c;多数据库的操作时&#xff0c;查询语句怎么知道是哪个数据库。 //QT提供了这样一种构造函数 QSqlQuery(const QSqlDatabase &db) //指定数据库 //在QT6.2.4 MSVC2019调试通过。 //效果见下图&am…

刷题 替换数字

题干 给定一个字符串 s&#xff0c;它包含小写字母和数字字符&#xff0c;请编写一个函数&#xff0c;将字符串中的字母字符保持不变&#xff0c;而将每个数字字符替换为number。 例如&#xff0c;对于输入字符串 "a1b2c3"&#xff0c;函数应该将其转换为 "an…

关于超出表示范围的数据类型转化

目录 背景&#xff1a; 问题分析&#xff1a; 参数异常分析&#xff1a; 分析文件原始值&#xff1a; 分析数据类型转换 代码分析&#xff1a; 结论&#xff1a; 参考资料&#xff1a; 背景&#xff1a; 在Ubuntu环境下进行项目开发时&#xff0c;调试时程序总是进入断…

小型企业网络优化加速方案

随着数字化经济蓬勃发展&#xff0c;小型企业的网络基础设施变得尤为重要。在这一浪潮中&#xff0c;建立一个稳定、高效的企业网络成为支撑业务发展的关键。本文将深入研究针对小型企业设计的网络优化加速方案&#xff0c;助力企业主了解如何规划和实施适合自身业务需求的网络…

车载诊断系统应用方案选型,ESP8266方案让成本降低了35%,销售数据提升47%

车载诊断系统简称OBD&#xff0c;这个系统随时监控发动机的运行状况和尾气后处理系统的工作状态&#xff0c;一旦发现有可能引起排放超标的情况&#xff0c;会马上发出警示。当系统出现故障时&#xff0c;故障灯(MIL)或检查发动机(Check Engine)警告灯亮&#xff0c;同时OBD系统…

使用Python进行容器编排Docker Compose与Kubernetes的比较

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 随着容器化技术的普及&#xff0c;容器编排成为了管理和部署容器化应用程序的重要环节。在容…

尺取法知识点讲解

一、固定长度的情况&#xff1a; 最小和(sum) 输入N个数的数列&#xff0c;所有相邻的M个数的和共有N-M1个&#xff0c;求其中的最小值。 输入格式 第1行&#xff0c;2个整数N&#xff0c;M&#xff0c;范围在[3…100000]&#xff0c;N>M。 第2行&#xff0c;有N个正…