技术

数据湖 高性能计算与存储 Linux2.1.13网络源代码学习 《大数据经典论文解读》 三驾马车学习 Spark 内存管理及调优 Yarn学习 从Spark部署模式开始讲源码分析 容器狂占内存资源怎么办? 多角度理解一致性 golang io使用及优化模式 Flink学习 c++学习 学习ebpf go设计哲学 ceph学习 学习mesh kvm虚拟化 学习MQ go编译器以及defer实现 学习go 为什么要有堆栈 汇编语言 计算机组成原理 运行时和库 Prometheus client mysql 事务 mysql 事务的隔离级别 mysql 索引 坏味道 学习分布式 学习网络 学习Linux go堆内存分配 golang 系统调用与阻塞处理 Goroutine 调度过程 重新认识cpu mosn有的没的 负载均衡泛谈 单元测试的新解读 《Redis核心技术与实现》笔记 《Prometheus监控实战》笔记 Prometheus 告警学习 calico源码分析 对容器云平台的理解 Prometheus 源码分析 并发的成本 基础设施优化 hashicorp raft源码学习 docker 架构 mosn细节 与微服务框架整合 Java动态代理 编程范式 并发通信模型 《网络是怎样连接的》笔记 go channel codereview gc分析 jvm 线程实现 go打包机制 go interface及反射 如何学习Kubernetes 《编译原理之美》笔记——后端部分 《编译原理之美》笔记——前端部分 Pilot MCP协议分析 go gc 内存管理玩法汇总 软件机制 istio流量管理 Pilot源码分析 golang io 学习Spring mosn源码浅析 MOSN简介 《datacenter as a computer》笔记 学习JVM Tomcat源码分析 Linux可观测性 学习存储 学计算 Gotty源码分析 kubernetes operator kaggle泰坦尼克问题实践 kubernetes扩缩容 神经网络模型优化 直觉上理解深度学习 如何学习机器学习 TIDB源码分析 什么是云原生 Alibaba Java诊断工具Arthas TIDB存储——TIKV 《Apache Kafka源码分析》——简介 netty中的线程池 guava cache 源码分析 Springboot 启动过程分析 Spring 创建Bean的年代变迁 Linux内存管理 自定义CNI IPAM 共识算法 spring redis 源码分析 kafka实践 spring kafka 源码分析 Linux进程调度 让kafka支持优先级队列 Codis源码分析 Redis源码分析 C语言学习 《趣谈Linux操作系统》笔记 docker和k8s安全访问机制 jvm crash分析 Prometheus 学习 Kubernetes监控 容器日志采集 Kubernetes 控制器模型 容器狂占资源怎么办? Kubernetes资源调度——scheduler 时序性数据库介绍及对比 influxdb入门 maven的基本概念 《Apache Kafka源码分析》——server Kubernetes类型系统 源码分析体会 《数据结构与算法之美》——算法新解 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 ansible学习 Kubernetes源码分析——从kubectl开始 jib源码分析之Step实现 jib源码分析之细节 线程排队 跨主机容器通信 jib源码分析及应用 为容器选择一个合适的entrypoint kubernetes yaml配置 《持续交付36讲》笔记 mybatis学习 程序猿应该知道的 无锁数据结构和算法 CNI——容器网络是如何打通的 为什么很多业务程序猿觉得数据结构和算法没用? 串一串一致性协议 当我在说PaaS时,我在说什么 《数据结构与算法之美》——数据结构笔记 PouchContainer技术分享体会 harbor学习 用groovy 来动态化你的代码 精简代码的利器——lombok 学习 《深入剖析kubernetes》笔记 编程语言那些事儿 rxjava3——背压 rxjava2——线程切换 spring cloud 初识 《深入拆解java 虚拟机》笔记 《how tomcat works》笔记 hystrix 学习 rxjava1——概念 Redis 学习 TIDB 学习 如何分发计算 Storm 学习 AQS1——论文学习 Unsafe Spark Stream 学习 linux vfs轮廓 《自己动手写docker》笔记 java8 实践 中本聪比特币白皮书 细读 区块链泛谈 比特币 大杂烩 总纲——如何学习分布式系统 hbase 泛谈 forkjoin 泛谈 看不见摸不着的cdn是啥 《jdk8 in action》笔记 程序猿视角看网络 bgp初识 calico学习 AQS——粗略的代码分析 我们能用反射做什么 web 跨域问题 《clean code》笔记 《Elasticsearch权威指南》笔记 mockito简介及源码分析 2017软件开发小结—— 从做功能到做系统 《Apache Kafka源码分析》——clients dns隐藏的一个坑 《mysql技术内幕》笔记 log4j学习 为什么netty比较难懂? 递归、回溯、动态规划 apollo client源码分析及看待面向对象设计 学习并发 docker运行java项目的常见问题 OpenTSDB 入门 spring事务小结 分布式事务 javascript应用在哪里 《netty in action》读书笔记 netty对http2协议的解析 ssl证书是什么东西 http那些事 苹果APNs推送框架pushy apple 推送那些事儿 编写java框架的几大利器 java内存模型和jvm内存布局 java exception Linux IO学习 netty内存管理 测试环境docker化实践 netty在框架中的使用套路 Nginx简单使用 《Linux内核设计的艺术》小结 Go并发机制及语言层工具 Linux网络源代码学习——数据包的发送与接收 《docker源码分析》小结 docker namespace和cgroup zookeeper三重奏 数据库的一些知识 Spark 泛谈 链式处理的那些套路 netty回顾 Thrift基本原理与实践(二) Thrift基本原理与实践(一) 回调 异步执行抽象——Executor与Future Docker0.1.0源码分析 java gc Jedis源码分析 深度学习泛谈 Linux网络命令操作 JTA与TCC 换个角度看待设计模式 Scala初识 向Hadoop学习NIO的使用 以新的角度看数据结构 并发控制相关的硬件与内核支持 systemd 简介 quartz 源码分析 基于docker搭建测试环境(二) spring aop 实现原理简述 自己动手写spring(八) 支持AOP 自己动手写spring(七) 类结构设计调整 分析log日志 自己动手写spring(六) 支持FactoryBean 自己动手写spring(九) 总结 自己动手写spring(五) bean的生命周期管理 自己动手写spring(四) 整合xml与注解方式 自己动手写spring(三) 支持注解方式 自己动手写spring(二) 创建一个bean工厂 自己动手写spring(一) 使用digester varnish 简单使用 关于docker image的那点事儿 基于docker搭建测试环境 分布式配置系统 JVM执行 git maven/ant/gradle/make使用 再看tcp kv系统 java nio的多线程扩展 《Concurrency Models》笔记 回头看Spring IOC IntelliJ IDEA使用 Java泛型 vagrant 使用 Go常用的一些库 Python初学 Goroutine 调度模型 虚拟网络 《程序员的自我修养》小结 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件 Go基础 JVM类加载 硬币和扑克牌问题 LRU实现 virtualbox 使用 ThreadLocal小结 docker快速入门

架构

controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 容器和CPU那些事儿 kubevela源码分析 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群及clusternet学习 AutoML和AutoDL 特征平台 实时训练 分布式链路追踪 helm tensorflow原理——python层分析 如何学习tensorflow 数据并行——allreduce 数据并行——ps 机器学习中的python调用c 机器学习训练框架概述 embedding的原理及实践 tensornet源码分析 大模型训练 X的生成——特征工程 tvm tensorflow原理——core层分析 模型演变 《深度学习推荐系统实战》笔记 keras 和 Estimator tensorflow分布式训练 分布式训练的一些问题 基于Volcano的弹性训练 图神经网络 pytorch弹性分布式训练 从混部到统一调度 RNN pytorch分布式训练 CNN 《动手学深度学习》笔记 pytorch与线性回归 多活 volcano特性源码分析 推理服务 kubebuilder 学习 mpi 学习pytorch client-go学习 tensorflow学习 提高gpu 利用率 GPU与容器的结合 GPU入门 AI云平台梳理 tf-operator源码分析 k8s批处理调度 喜马拉雅容器化实践 Kubernetes 实践 学习rpc BFF openkruise学习 可观察性和监控系统 基于Kubernetes选主及应用 《许式伟的架构课》笔记 Admission Controller 与 Admission Webhook 发布平台系统设计 k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 controller 组件介绍 openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? 《软件设计之美》笔记 mecha 架构学习 Kubernetes events学习及应用 CRI 资源调度泛谈 业务系统设计原则 grpc学习 元编程 以应用为中心 istio学习 下一代微服务Service Mesh 《实现领域驱动设计》笔记 概率论 serverless 泛谈 《架构整洁之道》笔记 处理复杂性 那些年追过的并发 服务器端编程 网络通信协议 架构大杂烩 如何学习架构 《反应式设计模式》笔记 项目的演化特点 反应式架构摸索 函数式编程的设计模式 服务化 ddd反模式——CRUD的败笔 研发效能平台 重新看面向对象设计 业务系统设计的一些体会 函数式编程 《左耳听风》笔记 业务程序猿眼中的微服务管理 DDD实践——CQRS 项目隔离——案例研究 《编程的本质》笔记 系统故障排查汇总及教训 平台支持类系统的几个点 代码腾挪的艺术 abtest 系统设计汇总 《从0开始学架构》笔记 初级权限系统设计 领域驱动理念入门 现有上传协议分析 移动网络下的文件上传要注意的几个问题 推送系统的几个基本问题 做配置中心要想好的几个基本问题 不同层面的异步 分层那些事儿 性能问题分析 当我在说模板引擎的时候,我在说什么 用户认证问题 资源的分配与回收——池 消息/任务队列

标签

controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 容器和CPU那些事儿 kubevela源码分析 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群及clusternet学习 helm 从混部到统一调度 volcano特性源码分析 kubebuilder 学习 client-go学习 tf-operator源码分析 k8s批处理调度 喜马拉雅容器化实践 Kubernetes 实践 openkruise学习 基于Kubernetes选主及应用 Admission Controller 与 Admission Webhook k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 controller 组件介绍 openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? Kubernetes events学习及应用 CRI 资源调度泛谈 如何学习Kubernetes 以应用为中心 kubernetes operator kubernetes扩缩容 serverless 泛谈 自定义CNI IPAM docker和k8s安全访问机制 Kubernetes监控 Kubernetes 控制器模型 Kubernetes资源调度——scheduler Kubernetes类型系统 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 Kubernetes源码分析——从kubectl开始 kubernetes yaml配置 CNI——容器网络是如何打通的 当我在说PaaS时,我在说什么 《深入剖析kubernetes》笔记 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件

特征平台

2022年06月27日

简介

实时特征计算平台架构方法论和实践

整体脉络:管理<uid/itemId,原始值> ==> <uid/itemId,特征值>的来源、计算(转换和编码)、存储、周期性更新,而后基于<uid,itemId,y>,如何关联特征(批量、实时)拼凑为训练样本<x,y>。此外推理的时候 也能根据 uid/itemId 查询特征值。

特征平台干什么

有了特征平台之后,特征和样本生成就分离了,分离后,特征能复用能独立管理,AI工作流会更清楚。

ai 训练有一个将 原始特征转换为 模型特征的过程,这个模型特征分两类

  1. 一类是预计算特征(通过批式/流式引擎预计算好,无论推理和训练都可以直接取用)。批流一体通常解决预计算特征的问题。
    1. 流式计算,是事件驱动的,因此只能得到“当下”的特征
    2. 批处理计算,可以得到“历史时刻”的特征 例如,算法同学想要一个特征“用户最近30分钟的广告点击数量”,首先肯定得离线训练试一下这个特征加入后,是否有效果提升,训练时用过去的数据来生成训练样本,例如基于过去一个月的用户访问日志,拼接特征,来生成样本。对于这个新特征来说,(特征)数据库中并没有过去30天的特征数据。此时,就该执行所谓的backfill,通过批处理把过去30天的特征数据算出来,才能join到样本里去。当算法同学验证完觉得OK了,该上线的时候就用流式计算(用批处理的话,时效性不够)持续的更新特征值,以便推理时使用。在这个场景下,对于同一个特征,既有批处理,也有流处理。像用户年龄 等变化速度慢的特征,定时跑一下批处理就行了,变化速度快的,肯定得上流式计算。
  2. 一类是即时计算特征,比如用户请求的时间。在推理时,一般会实时计算出来;训练时,通过上下文信息计算出来。即时计算特征的问题一般靠编写在Spark 和Web Service中通用的算子来解决

特征平台(Feature Store):序论

  1. 特征(Feature)创建,从各类原始数据,例如日志、记录、表,经过关联、统计、转化、聚集等操作得到的一系列值。
  2. 特征离线存储&消费,是为模型训练阶段服务的。对于广告、个性化推荐等使用过去信息来预测未来信息的算法模型,特征是随时间变化的。例如用户最近1小时浏览商品列表,会随着时间变化而发生变化。进行模型训练时,我们需要预先生成训练数据。以电商场景为例,将一系列被标记的行为关联其对应的特征,即可生成训练数据。为了避免特征穿越问题,训练数据生成时,需要实现样本与特征间的Point-in-time Coreect Join,这要求我们保存特征在不同时刻的版本,并确保特征离线消费实现正确。
  3. 特征在线存储&消费,算法模型部署成在线服务后,在进行推理(inference)时,需要快速取得特征,这要求我们将特征的最新的版本同步到高速缓存中,并提供便捷的查询API(例如按特征组查询),以便满足高并发、低延迟的特征在线消费要求。

除去控制台相关组件,特征平台核心的组件都是与数据相关的:

  1. 特征计算引擎:用于特征抽取、特征工程处理、训练数据生成等过程
  2. 特征存储:Google BigQuery、Hive等数据仓库用于特征离线存储,Redis、Cassandra等高速缓存用于特征在线存储

特征平台的简版的实现:平台只是记录了某个特征从哪个数据源过来,要进行何种计算(用dsl 描述)。训练样本的生成 是触发训练工作流时,启动spark任务拉取 特征转换信息 生成的训练样本。也就是工作流不启动,数据都还是原始形式躺在各个数据源里。推理的时候则是 直接拉到原始数据,用dsl硬算的(之后缓存一下),没有专门的特征存储这个环节。如果不涉及性能和复用,这套方案是没问题的,但如果有n多个业务,每个业务都跑一套完整流程,那就计算量大了。

为什么会有特征平台

推荐模型本质上是一个函数,输入输出都是数字或数值型的向量。离线训练 要根据用户的原始特征(有的地方称为物料) 构建训练样本,在线服务要根据用户的uid/itemId 找到原始特征,并根据原始特征转换为训练特征(也叫实时特征),再调用预测服务得到预测值。

如何提高模型效果?

  1. Model-centric: 以调整模型代码、调优模型超参数为主的系统调优策略,在这种策略下,可以认为数据集是固定的 Data-centric:
  2. 与Model-centric相对,以调整数据集为主的系统调优策略,在这种策略下,可以认为模型是固定的(只对数据集作适应性调整) Andrew认为,在搭建模型时,特征生产与模型训练的时间占比应该是8:2,然而目前大部分AI研究的文章(>99%)均发力于模型探索方面。人们很容易认为模型效果不好,是因为模型不好,却忽略了数据集本身对模型效果的巨大影响。如何在工程中实现Data-centric策略?MLOps。

你真的需要特征存储吗?有三种方法可以确保在训练期间所做的预处理在预测期间是相同的

  1. 将预处理代码放在模型中,比如tf 有专门的feature_column库
    1. 优点,不需要额外的基础设施,预处理步骤将自动成为模型的一部分,如果需要在边缘或在另一个云上部署模型,不需要做什么特别的事情。
    2. 缺点,预处理步骤在通过训练数据集的每次迭代中会重复,计算的预处理的步骤越复杂,浪费的时间就越多。另一个缺点是,必须在与ML模型相同的框架中实现预处理代码。例如,如果模型是使用Pytorch编写的,那么预处理也必须使用Pytorch完成。如果预处理代码使用自定义库,会变得非常麻烦。
  2. 使用转换函数,将预处理步骤封装成函数,并将该函数一次性的应用于原始数据。然后对预处理后的数据进行模型训练,这样预处理只进行了一次,可以提高效率。但是必须确保预测代码中也需要调用相同函数。像Tensorflow Extended (TFX)这样的框架提供了转换功能来简化相关操作。一些基于sql的ML框架,比如BigQuery ML,也支持TRANSFORM子句。
  3. 使用特征存储。
    1. 预测时需要动态特征,这种动态特征必须在服务器上计算,这时特性库就发挥了作用。例如动态定价模型的特征之一可能是过去一小时内网站上商品列表的访问数量。请求酒店价格的客户是无法实获取这个特征的,这些信息是在服务器上通过点击流数据的流管道进行实时计算的,所以特征库可以保存实时的计算结果并且提供对外的访问。PS:没看懂,大概是训练时刻t1 “过去一小时内网站上商品列表” 和 推理时刻t2 “过去一小时内网站上商品列表” 不一样,所以要把t1 时刻 “过去一小时内网站上商品列表” 保存下来给推理用
    2. 防止不必要的数据拷贝。例如有一个计算开销很大的特性,并且在多个ML模型中使用。与使用转换函数并将转换后的特性存储在多个ML训练数据集中相比,将它存储在一个集中的存储库中是更加高效的选择。

5年迭代5次,抖音推荐系统演进历程推荐系统最基础的燃料是特征,高效生产基础特征对业务推荐系统的迭代至关重要。PS: 特征分为离线特征和 实时特征,计算、存储特点和难点又有所不同,需要基础特征生产平台统一管理。

  1. 离线特征计算的基本模式都是通过消费 Kafka、BMQ、Hive、HDFS、Abase、RPC 等数据源,基于 Spark、Flink 计算引擎实现特征的计算,而后把特征的结果写入在线、离线存储。各种不同类型的基础特征计算散落在不同的服务中,缺乏业务抽象,带来了较大的运维成本和稳定性问题。
  2. 构建统一的实时特征生产系统面临着较大挑战,主要来自四个方面:
    1. 巨大的业务规模
    2. 较高的特征实时化要求,在以直播、电商、短视频为代表的推荐场景下,为保证推荐效果,实时特征离线生产的时效性需实现常态稳定于分钟级别。
    3. 更好的扩展性和灵活性:随着业务场景不断复杂,特征需求更为灵活多变。从统计、序列、属性类型的特征生产,到需要灵活支持窗口特征、多维特征等,业务方需要特征中台能够支持逐渐衍生而来的新特征类型和需求。
    4. 业务迭代速度快:特征中台提供的面向业务的 DSL 需要足够场景,特征生产链路尽量让业务少写代码,底层的计算引擎、存储引擎对业务完全透明,彻底释放业务计算、存储选型、调优的负担,彻底实现实时基础特征的规模化生产,不断提升特征生产力;

特征平台与样本生成

模型样本构建方案演进之路以推荐系统为例:

  1. t0时刻,端侧向推荐服务发起请求,要求为用户u1提供100个最匹配的商品
  2. t1时刻,服务后端请求召回模型,获得了1000个商品
  3. t2时刻,服务后端请求该1000个商品以及用户u1的特征,并向排序模型发起请求
  4. t3时刻,排序模型完成推理,返回排序后的Top100商品
  5. t4时刻,服务后端返回该100商品,端侧向用户展示商品
  6. t5时刻,实际仅Top10商品向用户曝光并触发了用户点击,曝光和点击日志回传到服务后端
  7. t6时刻,根据曝光和点击日志,构建样本,以对排序模型进行再训练或增量训练

样本生成的要求:当生成样本时,特征的时刻要与事件的时刻匹配,不能过早也不能过晚,否则将导致特征穿越。以如上时间线为例,t2时刻发生了实际的模型推理,这一事件在t6时刻转化成训练样本时,关联的应是t2时刻的特征值,即所谓的Point-in-Time Correct。

t6时刻构建样本时,有几种方案

  1. 用户点击流 (也就是y)带有uid/itemId 信息,然后根据uid/itemId 信息向store查询x (这个过程可能会有特征穿越),然后拼凑的样本
    1. 对于只使用批式特征的模型,t6与t2的时间间隔远小于特征更新频率,可以认为方案是可靠的;
    2. 而对于使用了流式特征和即时特征的模型来说,特征更新频率越快,越容易发生特征穿越,不应使用该方案
  2. 为了完全避免特征穿越,需要将t2时刻查询到的特征记录成快照(记录x),生成样本时Join该特征快照(也就是t6 拿到y 之后join x和y)
    1. 离线样本:特征快照写入离线存储,定时通过批处理实现Join,完成样本生成。推荐排序系统单次请求可能对数百个商品进行排序,但最终曝光和点击的商品仅数个,导致特征快照数量级远大于曝光/点击流,导致特征快照数据量大,进而导致Join时计算量大
    2. 特征快照写入Kafka,通过Flink实现双流Join,完成样本生成,用作离线或在线训练。
  3. t6 将曝光/点击流(也就是y)insert到store,延迟数分钟后,再以Update方式写入特征快照x(t2 时刻将特征快照x写入 kafka)。
    1. 特征快照x中,同时保存了批式、流式、即时特征。然而,只有流式、即时特征容易引入特征穿越问题。因此:特征快照中可以只保存流式、即时特征;在样本生成时,再通过二次查询Feature Store补录批式特征
    2. 由于特征快照 x 数量远大于 y,因此采用先insert y ,再用x 更新的的方式,x 和 y 是同一个key,update 过程即 x 和y join 。

特征平台的数据一致性问题,说的就是特征的在线和离线存储的一致性问题。问题的本质就是在线离线有两条pipeline

  1. 砍掉离线pipeline,线上线下消费同一套数据。实时生成的样本积累到一定数据量再进行离线训练。但如果想为模型加入新的特征,就不得等加入新特征的样本积累一定的数据量了。
  2. 线上线下pipeline实现逻辑归一

AI工程化落地的数据和特征的挑战

华为商城特征平台建设实践,特征平台-最佳的AI数据监控落脚点

  1. 特征计算难
    1. 多种数据源组件
    2. 大数据场景下的SQL。
    3. 近线/实时计算
  2. 样本生成难
    1. 规避特征穿越。
  3. 特征、样本管理缺失
    1. 特征血缘
    2. 特征复用
  4. 特征Serving难
    1. 特征时效性保证
    2. 数据一致性保证
    3. 查询性能优化
  5. 特征、样本监控难
    1. Pipeline作业监控
    2. 数据质量监控,业界工具Great Expectations

全栈 FeatureOps:开源机器学习数据库 OpenMLDB举一个性化搜索的例子,比如小李同学,在某个时间点想买洗衣机,去搜索洗衣机,触发了搜索行为以后,后面整个特征计算会做什么?首先进来的实时行为特征,只是这三个原始的特征,就是User ID,date以及他在搜索东西。如果我们只是拿这三个特征去做模型训练和推理,它是达不到一个非常好的模型精度的。此时需要做一个特征工程,所谓的特征工程就是我们从数据库里去进一步的去拉取一些历史数据,比方说我们从交易数据库、商品数据库、用户数据库去拉取一些历史数据,然后组合、计算,得到一些更完整的更有意义的特征。

特征生成难

特征生成:把数据从数据源拉出来,结合AI领域知识,生成特征并存储。数据工程师会大数据不会AI,算法工程师会算法不会工程。因为数据量大,物料转特征要用到spark,而算法人员一般不会使用spark。且特征转换 多样,每个模型都不同。

数据科学家他关注的点是模型的准确度、精确度、质量,但他不太关心模型上线后latency, QPS等等。大部分科学数据科学家做这个过程当中,用的是Python,RSQL这种偏向批处理比较易用的框架,这种面向批处理的这种框架,不能直接上线。所以一般会有一个工程化团队,把科学家做的特征工程的脚本和模型训练的建模方法,翻译成线上的一套东西,因为他们非常关注latency、QPS,他们会用一些高性能数据库,甚至去用c++自己搭建一套特征抽取的服务,打通后面预估服务这条线。因为线上线下是两套系统,由两个团队开发,所以一致性校验是不可避免的。

所以特征生产一般提供Python/SQL/PySpark接口,暴露给算法工程师以SQL的形式来提供计算逻辑,工程人员将SQL 转换为大数据pipeline作业来管理。

数据一致性

离线/在线一致性校验 千亿级模型在离线一致性保障方案详解 未细读

有的特征可能在训练过程中t1时刻被转为a(对应模型参数版本v1),但经过一段时间t2时刻(随着样本量的变化)会被转换为b,此时拿着t1 时刻的模型参数去预测t2 就会不准。AI算法模型中的特征穿越问题:原理篇

特征穿越:以电商推荐系统为例,对于一条用户A于9:00时发生的行为记录(训练样本),只能使用9:00之前的信息构建特征(例如过去购买、点击、分享的商品,以及用户的一些属性,包括所在城市、性别等),来训练模型。若使用了9:00之后的信息来构建特征,则会触发典型的特征时间穿越问题,因为特征中包含了未来的信息,这些信息极有可能隐含与标签(Label,即模型预测目标)相关的内容。通过存在特征穿越的数据集训练的模型,部署上线后,由于预测数据不可能发生穿越(构建特征所使用的信息均来自当下或过去),因此与训练数据存在显著差异,导致模型表现差劲。AI算法模型中的特征穿越问题:原理篇

特征服务/feature serving

在线服务可能qps 非常高,一般要用到特征缓存

  1. 缓存原始特征/物料,公司有很多个部门,也就是会有很多个物料服务,此时会有一个物料缓存网关。物料缓存网关的核心功能则为缓存和路由,其根据特征名称路由到不同的物料服务来计算。
  2. 缓存训练特征。预测时实时 将物料转换为训练特征,实时计算就意味着RT增加,特征越多,RT的压力越来越大。降低RT的常见思路为用空间换时间,类似年龄性别等人口属性和用户的长期兴趣是固定的,没有必要每次都计算,可以将某些类型的特征提前计算好并存储到缓存中,线上请求直接拉取对应的特征即可。要区分不同物料的计算频率,我们将物料区分为下面几种更新周期:天级别、分钟级别、实时级别。实时级别的特征还是需要每次请求都实时计算的。此时,物料缓存网关 实际是 物料and特征缓存网关。

整体设计

网易新闻推荐工程优化 - 特征平台篇业内常见的特征平台的核心设计往往为数据表,比如设计用户表和文章表。离线训练时将日志信息、用户表、文章表做join处理以生成样本;在线服务时则有专门的服务加速从数据表中获取内容的过程。

推荐系统(6): 特征平台实践与思考 由于特征种类多样,线上数据的来源也常常五花八门

  1. 比如物品的实时统计特征,可能通过客户端上报实时计算点击数、点赞数、购买数。
  2. 用户画像可能是离线计算,从hive 或hdfs 每天更;
  3. 物品的一些基础属性也可能是通过mysql db表生成。

因此,一个统一的特征平台(包括离线流程),可以:

  1. 统一特征口径: 特征的统计口径唯一,减少沟通成本,通过统一的接入作业,也可以复用模块代码,加快迭代速度
  2. 特征复用: 统一特征接入和上线流程,每个特征唯一id,上线过程复核,减少特征的重复。
  3. 质量监控: 特征覆盖率,分布统一监控,同时收拢出口,可以监控和限制特征的使用频率,及时下线淘汰的特征,减少成本。

特征平台(Feature Store):序论 业界已经有 Feast/Tecton/Databricks Feature Store/LinkedIn Feathr 等

  1. Feature Registry:一般数据库即可,以承载特征元数据,用来描述: 数据从哪里来,经过什么处理,变成什么样子,最后存到哪里去,这一整个流程。
  2. 特征计算/ Feature Pipeline。从各类原始数据,例如日志、记录、表,经过关联、统计、转化、聚集等操作得到的一系列值。这些Pipeline通常是Spark、Flink、Hive、SQL作业,并且批处理作业还需要一个编排系统(例如Apache Airflow)进行管理。PS: 以下工程通过python 等描述后,交给spark 执行
    1. 对于需要从原始数据计算得到的特征,Feathr称之为Anchored Feature,即该Feature是Anchor(锚定)在某个数据源上的。其Feature定义过程如下:
      1. 声明特征数据源,比如使用HDFS上的文件作为数据源
      2. 声明Feature对象,该对象记录了name、feature_type(数据类型)、transform(特征计算逻辑)等信息
      3. 声明FeatureAnchor对象
    2. 对于基于其他特征计算得到的特征,Feathr称之为Derived Feature(衍生特征)。其定义过程与AnchorFeature类似,只是将Source换成input_features。
  3. Offline Store:同时支持SQL数据库/数据仓库和数据湖,包含了特征的所有版本
    1. Feast方案中是一个时序表,包含timestamp/entity/feature 等
    2. shema free: 比如json。但这种格式无论是存储占用还是序列化开销都比较大
    3. Protobuf: pb协议相对通用,在没有复杂嵌套情况下,可以简单定义 feature_id,feature_type,feature_value三个类型,类似于tensorflow example的协议。
    4. Flatbuffer: 。同样是谷歌出品,通过打平+offset的方式,反序列化基本为0。但缺点是构造复杂,不适合嵌套结构。
  4. Online Store:比如Redis,服务于模型推理时的特征在线消费场景。一些公司会用到特征分组:特征分组将相同维度下的多个特征进行聚合,以减少特征Key的数量,避免大量Key读写对KV存储性能造成的影响。还有定期全量刷新 和 增量同步等问题。 业界常见的特征在线存储方式有两种:单一版本存储和多版本存储。
    1. 单一版本存储即覆盖更新,用新数据直接覆盖旧数据,实现简单,对物理存储占用较少,但在数据异常的时候无法快速回滚。
    2. 多版本存储相比前者,增加了版本概念,每一份数据都对应特定版本,虽然物理存储占用较多,但在数据异常的时候可通过版本切换的方式快速回滚,保证线上稳定性。
  5. SDK:比如基于Python开发的SDK,供用户使用

工作流程大概如下

  1. 特征元数据
  2. 特征计算,使用python或dsl 描述特征计算逻辑,交给spark 等处理,处理完成后存储到 Offline Store 中,并同步一份给 Online Store
  3. 模型训练,训练时从 Offline Store 中拼接 训练样本
  4. 推理时,业务方根据SDK 从 Online Store 中拿到某个用户的特征数据,调用serving 服务拿到预估值。

特征监控

  1. 离线天级可以通过抽样全量特征,主要分析来源数据是否异常。
    1. 特征异常值
    2. 特征覆盖率
    3. 特征max min avg 中位数等数据分布
  2. 在线监控主要是从 feature server 端,抽样上报,主要分析特征调用方,调用数据源,调用频率等等。除了用来统计流量来源,也可以及时发现一些过期不用的特征,通过下线存储来达到节约成本的目的。

业务dsl/Feature AS Code

从业务视角提供高度抽象的特征生产 DSL 语言,屏蔽底层计算、存储引擎细节,让业务方聚焦于业务特征定义。业务 DSL 层提供:数据来源、数据格式、数据抽取逻辑、数据生成特征类型、数据输出方式等。

Feast用python 代码来描述上述过程,Feature AS Code 的方式也便于做变更的审批、跟踪。

vivo

vivo推荐中台升级路:机器成本节约75%,迭代周期低至分钟级

  1. 为了打通离线训练与在线推理的特征解析,我们对多个业务进行抽象设计,制定了 vivo 模型特征统一配置规范,VMFC(vivo Model Feature Configration)。
  2. 算法工程师在样本生产时,先在特征仓库勾选形成样本需要的特征集,在创建离线模型训练任务的时候,选择该模型训练与该特征集匹配。最终模型训练完成之后,在模型中心对其一键部署为在线推理服务。
  3. 在线推理服务对外提供统一接口,业务方使用时只需要指定对应的业务与模型代码,配置化推理服务找到该模型的 VMFC,便可以自动查询需要的特征,拼接为模型入参,返回预测结果。

在 vivo 传统的推荐系统架构中,特征的获取、特征的处理、特征的拼接以及推理预测是耦合在推荐工程的代码中的,每次算法实验的迭代,每增加一个特征,甚至是增加一个用于回传的特征,都需要在离线训练和推荐工程端硬编码新增的特征名称。然后把特征处理函数“搬运”过来,显然存在以下难以解决的痛点:

  1. 架构耦合对单机压力愈来愈大:特征获取与模型预测耦合,推荐系统服务器单机运行压力大;
  2. 硬编码迭代效率慢成为工程落地的瓶颈:特征获取与特征处理的逻辑不够灵活,无法应对算法实验的快速迭代需求,成为推荐系统流水线的瓶颈环节;
  3. 特征数据不一致:频繁的多分支多版本算法实验,多人协作带来较大沟通成本;训练的特征集、特征处理函数可能与线上推理预测的不一致,进而导致特征数据不一致;
  4. 特征复用困难:各个业务场景的特征都依赖于数据流算法工程师的经验,对多个团队类似的业务场景,特征的数据和经验不共享,导致增加了特征重复处理、存储等问题。数据孤岛效应明显,成本也很难缩减。

基于上述痛点,为了从根源解决算法迭代效率的问题,创造性的提出了特征仓库,特征集和通用特征服务的三大概念。

  1. 特征仓库,是通过特征工程把 Raw 数据抽取转化为一个特征,在特征平台注册为一个新特征元数据信息(Metadata),描述了这个特征存储的方式,数据类型,长度,默认值等,并且可以对该特征设置多项目共享,达到特征复用的目的。字节跳动基于 Iceberg 的海量特征存储实践
  2. 特征集,是一个虚拟灵活的特征集合概念,按照模型迭代的需求,可以自由从特征仓库上勾选需要的特征元数据,类似购物车的概念,按需动态勾选一个匹配当前模型训练的特征集。如果一个特征已经在平台上注册过,其他业务和场景需要复用,相应的算法工程师只需要申请共享,通过合规审批之后,就可以通过勾选特征集,用于自己的模型训练和在线推理。
  3. 特征服务,把特征获取这个关键步骤直接服务化,与特征管理平台联动的一个可配置化特征服务。在线获取特征的时候,调用通用特征服务的接口,传入特征集的唯一 ID,特征服务根据特征集元数据动态灵活获取需要的特征,一次返回给在线推理工程,通过特征拼接进入 Tensorflow 模型进行 CTR 实时预测。给出推荐结果。

网易

网易新闻推荐工程优化 - 特征算子篇

对训练样本需要的特征进行汇总分析,并对依赖的原始数据进行梳理,整理出由原始数据到特征样本之间所有的变换、组合关系,我们称之为特征算子。抽象算子之后,我们将进行特征描述和组合。我们选择json作为配置描述语言。

根据特征算子的输入、输出依赖关系将特征算子列表转成特征算子依赖关系图,该图为一个有向无环图。运行时,只需要根据图结点的依赖关系进行遍历计算,即可得到最终的特征数据。

美团

美团外卖特征平台的建设与实践 未全明白,比较全面。

外卖广告大规模深度学习模型工程实践 比较全面。

cube-studio

华为

特征平台本身并不直接负责特征计算、存储的工作,计算方面创建对应的作业,然后把作业交给相应的计算引擎。存储方面只管理数据的元数据,数据本身的存储则是交给云服务来做。

  1. 很多特征平台涉及到了特征存储的格式,说明特征平台负责了特征数据的计算和存储(美团叫 特征共享表),数据仓库 ==> 特征平台(管理特征集等) ==> 训练样本(模型支持的数据格式) ==> 训练。有了特征平台之后,只要根据特征集配置 从特征平台抽取 各个特征组成训练样本即可。那么特征平台是否负责存储 从原始数据转换后的 特征(特征的离线存储)? 这个取决于是 临时进行特征计算快,还是特征计算完成后 每个特征单独存储,然后按需join 拼凑训练样本更快?
    1. 存储。优点是特征转换结果可以复用。缺点是,技术上因为要做point in time join,样本生成会很耗时。可以试着切分任务,比如说一次要生成三个月的样本。那么就把它拆成三个任务,每次只生成一个月的,相当于手动分桶优化。因为join时会有数据膨胀,而且是很夸张的膨胀,所以在计算资源固定的情况下,降低作业的数据量就会有明显提速。另一方面,point in time join的特性,本身就会受益于分桶优化。Point-in-Time Correct Join:Feast Spark版实现
    2. 不存储。 “特征平台”只是描述了 特征从哪里来、从原始数据如何转换,每次训练时要从原始数据抓取开始、特征转换、拼接训练样本。
    3. 实时生成样本,比如一个模型选了 n个特征,根据模型推理服务的请求,另外搞一个数据流根据模型的 特征转换dsl 实时转成样本存起来(推理 也包含一个原始特征 转为样本特征的过程,直接样本特征落盘也ok)。
  2. 平台里的kv 存储,存的是raw 特征 ,还是转换后的特征(也就是特征的在线存储)?
    1. 能预计算的,肯定是算好再存,这样快且简单
    2. 一些不能预计算的,例如组合特征、上下文特征,就只能在查询时,实时计算好再返回了

字节

推荐系统流批一体实践在推荐系统的整个数据处理链路中,流式处理和批式处理都占据着重要的位置。尤其是在特征计算模块,推荐系统需要为用户实时地推荐信息流,保证实时性和准确性,同时也需要进行模型训练以提升推荐准确性。双数据链路的设计带来了诸多问题。

在流式链路中,我们接收用户请求,获得用户的实时在线特征,这些实时在线特征经过实时的流式处理之后,再结合在线特征库,就可以得到一个比较庞大的特征组。随后,将整个特征组输入到在线预测模型中,就可以得到预测的结果,从而实时地为用户推荐信息流。同时,这些特征也会被存入离线存储(如 HDFS)中,后续会利用这些特征进行线下的批式模型训练。对于离线训练来说,存入 HDFS 中的数据,经过批式的 ETL 处理后,输入到离线的模型训练中,训练出的模型可以用于更新在线服务的模型,从而更准确地服务用户。然而,正如上文所述,推荐系统的数据链路分了在线和离线两个体系,所以推荐系统在计算和使用在线特征和离线特征时,需要分别使用两种不同计算引擎和存储进行在、离线特征处理,带来了以下问题:

  1. 对流处理和批处理分别维护两套代码,业务成本过高
  2. 特征回溯难度大
  3. 如何使用历史数据初始化状态难定义
  4. 数据不统一,存储成本高

5年迭代5次,抖音推荐系统演进历程特征生产的链路分为数据源抽取 / 拼接、状态存储、计算三个阶段,Flink SQL 完成特征数据的抽取和流式拼接,Flink State 完成特征计算的中间状态存储。

其它

模型仓库与模型服务

  1. 模型仓库,主要是对离线训练完成的模型进行统一管理,并且提供离线模型的一键部署能力。算法同事在完成模型训练之后,只需要确定该离线模型符合预期并加入模型仓库。
  2. 模型服务,“模型即服务”(Model as a Service), 基于 VMFC 的离线训练得到模型,在模型仓库一键部署成为在线服务,即可对外提供标准的在线预测接口。