ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零搭建AI工程能力:数据管道、特征工程与推理服务实战

从零搭建AI工程能力:数据管道、特征工程与推理服务实战 1. 从零搭建AI工程能力为什么我劝你别一上来就调包这两年AI应用开发的门槛肉眼可见地降低了随便拉个框架、调个API就能跑出一个能对话的Demo。但我带过不少新人也面试过不少号称“做过AI项目”的候选人发现一个很普遍的问题模型能跑起来但一问到数据怎么清洗、特征怎么对齐、推理延迟为什么忽高忽低、线上效果和离线评估为什么对不上基本就卡壳了。这就是典型的“会调包但不懂工程”。ai-engineering-from-scratch这个方向说白了就是要把AI从“实验室玩具”变成“能扛住真实流量和真实脏数据的生产系统”。它涵盖的东西很杂数据管道、特征工程、模型训练与评估、推理服务、监控告警、成本控制甚至包括怎么和产品、后端、运维扯皮。适合谁来参考我认为有三类人一是刚转行做AI应用开发、想补齐工程短板的算法同学二是有后端或数据开发背景、想切入AI方向的工程师三是技术负责人需要判断团队里AI项目的工程成熟度到底在哪个水平。我自己在这个方向上踩过的坑比写过的模型多得多。下面我把从零搭建AI工程能力这件事按我实际带项目和做技术评审的思路拆开讲尽量把每个选择背后的“为什么”说清楚而不是甩一堆工具名让你自己猜。2. 整体设计思路先想清楚“工程”到底在解决什么问题2.1 从Demo到生产中间隔的不是模型精度很多人对AI工程的理解停留在“把模型部署上去”。我做过一个推荐场景的项目离线AUC做到0.82团队都很兴奋结果上线第一周转化率几乎没动。排查了两周才发现离线训练用的特征里有一个“用户最近7天点击次数”而线上实时计算这个特征时因为埋点延迟实际拿到的是“最近7天减2小时”的数据。就这么一个时间窗口的错位模型在线上等于半个瞎子。这件事让我彻底明白AI工程的核心不是模型本身而是保证训练和推理两条链路的数据一致性、可复现性和可观测性。模型只是其中一环数据管道、特征存储、服务编排、监控体系才是真正吃工作量的地方。所以我在设计任何AI工程项目时第一件事不是选模型而是画数据流图标清楚每个特征从产生到消费的完整路径。2.2 分层架构把变化的部分和稳定的部分隔开从零搭建时我习惯把系统分成四层这个分法不是教科书上的是我自己踩坑后总结的数据层原始日志、业务库、第三方数据源的接入和清洗。这一层变化最频繁因为业务字段经常改。特征层特征的定义、计算、存储和版本管理。这一层是AI工程和传统后端最大的区别也是最容易出问题的地方。模型层训练、评估、调参、版本管理。这一层反而相对稳定因为算法迭代有周期。服务层推理接口、流量调度、降级策略、监控告警。这一层直接面对线上压力。为什么要这么分因为每层的变更频率和责任人不同。数据层可能每天改特征层每周改模型层每月改服务层按需改。如果混在一起改一个字段可能要把整个系统重新测一遍。分层之后每层有独立的测试和发布流程出问题时排查范围也小很多。2.3 技术选型的三个原则选型这块我不喜欢直接给工具清单因为团队规模、现有技术栈、预算都不一样。但我有三个原则基本能帮你过滤掉大部分错误选项第一优先选团队已经会用的。如果团队里没人写过Go你非要上Go做推理服务光排环境问题就能耗掉两周。用Python FastAPI虽然性能不是极致但团队上手快后期真遇到瓶颈再换也不迟。第二特征存储和模型服务要解耦。我见过把特征计算逻辑直接写在推理服务里的做法结果训练侧想复用同样的逻辑只能复制粘贴两边一旦不同步就是灾难。特征计算应该独立成服务或库训练和推理都调同一份代码。第三监控先于优化。很多团队一上来就追求低延迟、高吞吐结果线上出了数据漂移都不知道。我的做法是先把关键指标输入分布、输出分布、延迟分位数、错误率的监控搭起来哪怕服务性能一般至少能看见问题。3. 核心细节解析数据管道和特征工程才是重头戏3.1 数据清洗别小看缺失值和异常值真实业务数据有多脏我统计过一个电商场景的用户行为日志缺失率最高的字段达到37%时间戳格式有四种还有约2%的记录时间戳是未来的。如果直接拿这种数据训练模型学到的全是噪声。我的清洗流程通常是这样的先做字段级统计对每个字段算缺失率、唯一值数量、最大最小值、分位数分布。缺失率超过30%的字段先和业务方确认是不是埋点有问题而不是急着填充。对于数值型字段用IQR方法识别异常值但不要直接删而是打上标记让模型自己决定要不要用。时间戳字段一定要做时区统一和未来时间过滤这个坑我踩过不止一次。注意清洗逻辑一定要写成可配置的规则文件而不是硬编码在脚本里。因为业务方随时可能说“这个字段现在有意义了”硬编码的话你得改代码重新发版。3.2 特征计算离线批处理和实时流处理怎么选特征计算有两条路离线批处理比如每天跑一次Spark任务和实时流处理比如Flink消费Kafka。怎么选我的判断标准是特征的时间敏感度。用户画像类的特征比如“用户历史购买品类分布”一天更新一次完全够用走离线批处理成本低、逻辑简单。但像“用户最近5分钟点击序列”这种必须走实时流处理否则推荐结果会严重滞后。这里有个容易被忽略的点离线特征和实时特征的口径必须一致。我见过离线用“自然日”统计实时用“滑动窗口”统计结果同一个特征在训练和推理时分布完全不同。解决办法是维护一份特征定义文档明确每个特征的时间窗口、聚合方式、空值处理规则训练和推理两侧都按这份文档实现并且定期做一致性校验。3.3 特征存储不只是存数据更是管版本特征存储Feature Store这个概念被炒得很热但我觉得它的核心价值就两个训练推理一致性和特征复用。没有特征存储的时候每个算法同学自己写一套特征计算脚本A团队算的“用户活跃度”和B团队算的完全不是一个东西模型效果没法比较。我自己的做法是轻量级的用一张Hive表或Parquet文件存离线特征用Redis或内存数据库存实时特征再加一个元数据服务记录每个特征的定义、负责人、更新频率、最近一次校验时间。不一定要上完整的Feature Store平台但元数据管理不能省。这样当某个特征出问题时能快速定位到是谁改的、什么时候改的、影响了哪些模型。4. 实操过程从零搭一个可用的AI工程流水线4.1 环境准备与目录结构假设你现在要做一个文本分类的AI应用从零开始。我建议的目录结构是这样的project/ ├── data/ # 原始数据和清洗后数据 │ ├── raw/ │ └── processed/ ├── features/ # 特征计算逻辑 │ ├── offline/ # 离线特征 │ └── online/ # 实时特征 ├── models/ # 模型训练和评估 │ ├── train.py │ └── evaluate.py ├── serving/ # 推理服务 │ ├── app.py │ └── config.yaml ├── monitoring/ # 监控配置 │ └── metrics.py └── tests/ # 测试用例 ├── test_features.py └── test_serving.py这个结构的好处是职责清晰。数据、特征、模型、服务各自独立测试用例也按模块划分。我特别强调tests/目录因为AI项目最容易出的问题就是“改了特征计算逻辑忘了同步改测试”导致线上和离线不一致。环境管理用conda或venv都行关键是锁定依赖版本。我吃过亏本地用pandas 1.5跑得好好的服务器上是pandas 1.3某个聚合函数的默认行为不一样结果特征值差了0.1模型效果直接掉两个点。所以requirements.txt里一定要写死版本号比如pandas1.5.3。4.2 数据管道搭建从原始日志到训练样本第一步是数据接入。假设原始数据是JSON格式的日志文件每天一个文件。我用Python写一个简单的ETL脚本import pandas as pd import json from datetime import datetime def load_raw_data(date_str): path fdata/raw/{date_str}.json records [] with open(path, r) as f: for line in f: try: records.append(json.loads(line)) except json.JSONDecodeError: continue # 跳过坏行 return pd.DataFrame(records) def clean_data(df): # 时间戳统一 df[timestamp] pd.to_datetime(df[timestamp], unitms) # 过滤未来时间 now datetime.now() df df[df[timestamp] now] # 缺失值处理 df[text] df[text].fillna() df[label] df[label].fillna(-1) return df这里有个细节坏行不要直接抛异常而是跳过并记录数量。线上日志里偶尔会有截断的JSON如果因为一行坏数据导致整个任务失败运维会来找你麻烦。我通常会在清洗后打印一个统计报告包括总行数、坏行数、各字段缺失率方便快速判断数据质量。第二步是样本构造。文本分类任务需要把原始日志转成(text, label)的形式。如果label来自人工标注还要处理标注不一致的问题。我的做法是同一个样本多人标注时取多数投票如果分歧太大直接丢弃不强行合并。4.3 特征工程实操以文本特征为例文本特征我一般分三块统计特征、语义特征和交互特征。统计特征包括文本长度、词数、标点符号比例、大写字母比例等。这些特征计算简单但对某些任务很有效。比如垃圾文本检测标点符号比例过高往往是个强信号。语义特征用预训练模型提取比如用Sentence-BERT把文本转成768维向量。这里要注意推理成本如果每天有百万级文本用GPU跑BERT可能成本很高。我的做法是先用轻量级模型比如蒸馏后的小模型做粗筛只对疑似样本用大模型提特征。交互特征是指文本和用户、商品等实体的交叉特征。比如“用户历史点击过的文本与当前文本的相似度”。这类特征往往效果最好但计算也最复杂需要维护用户历史行为序列。from sentence_transformers import SentenceTransformer import numpy as np model SentenceTransformer(paraphrase-MiniLM-L6-v2) def extract_semantic_features(texts, batch_size64): embeddings model.encode(texts, batch_sizebatch_size, show_progress_barTrue) return embeddings def extract_statistical_features(df): df[text_len] df[text].str.len() df[word_count] df[text].str.split().str.len() df[punct_ratio] df[text].apply( lambda x: sum(1 for c in x if c in .,!?;:) / max(len(x), 1) ) return df提示语义特征提取很耗时建议把结果缓存到磁盘或特征存储里不要每次训练都重新算。我通常用joblib或parquet格式存读取速度比重新推理快几十倍。4.4 模型训练与评估别只看准确率训练脚本我习惯用sklearn或pytorch但评估指标一定要根据业务来选。文本分类如果类别不平衡准确率会骗人。比如99%的样本是正常文本1%是垃圾文本模型全预测正常也有99%准确率但垃圾文本一个都抓不到。我通常看三个指标AUC排序能力、F1综合精确率和召回率、业务指标比如垃圾文本漏放率。业务指标最重要但往往需要和产品方一起定义。我做过一个内容审核项目产品方最关心的是“漏放率”因为放过一条违规内容的代价远高于误杀一条正常内容。所以模型阈值调得很低宁可错杀。评估还要做交叉验证和时间外测试。随机划分的交叉验证在时序数据上会高估效果因为未来信息泄露到了训练集。我的做法是按时间划分用前80%时间的数据训练后20%时间的数据测试。这样评估出来的效果更接近线上真实表现。4.5 推理服务部署从Flask到FastAPI的取舍推理服务我早期用Flask后来换成了FastAPI。原因很简单FastAPI原生支持异步和Pydantic数据校验对于需要调用多个下游服务特征服务、模型服务的场景异步能显著降低延迟。from fastapi import FastAPI from pydantic import BaseModel import joblib app FastAPI() model joblib.load(models/classifier.pkl) class PredictRequest(BaseModel): text: str user_id: str class PredictResponse(BaseModel): label: int score: float app.post(/predict, response_modelPredictResponse) async def predict(req: PredictRequest): features extract_features(req.text, req.user_id) score model.predict_proba([features])[0][1] label int(score 0.5) return PredictResponse(labellabel, scorescore)部署时要注意模型加载时机。不要在每次请求时加载模型那样延迟会爆炸。应该在服务启动时加载到内存用全局变量或依赖注入的方式共享。另外批处理推理能大幅提升吞吐量。如果上游允许把多个请求攒成一批一起推理GPU利用率会高很多。4.6 监控体系上线只是开始监控我分三层系统层CPU、内存、GPU、网络、服务层QPS、延迟分位数、错误率、数据层输入特征分布、输出分数分布。数据层监控最容易被忽略但最重要。我遇到过一次线上事故某个特征因为上游数据源变更突然全部变成0模型输出分数整体偏移但系统层和服务层监控完全正常。后来加了一个**PSIPopulation Stability Index**监控对比当天特征分布和训练集分布超过阈值就告警。import numpy as np def calculate_psi(expected, actual, buckets10): def scale_range(input_arr, min_val, max_val): input_arr np.clip(input_arr, min_val, max_val) return (input_arr - min_val) / (max_val - min_val) breakpoints np.arange(0, buckets 1) / buckets * 100 expected_percents np.histogram(expected, breakpoints)[0] / len(expected) actual_percents np.histogram(actual, breakpoints)[0] / len(actual) def sub_psi(e_perc, a_perc): if a_perc 0: a_perc 0.0001 if e_perc 0: e_perc 0.0001 return (e_perc - a_perc) * np.log(e_perc / a_perc) psi_value sum(sub_psi(expected_percents[i], actual_percents[i]) for i in range(len(expected_percents))) return psi_valuePSI小于0.1说明分布稳定0.1到0.25之间需要关注超过0.25就要告警了。这个指标我每周都会看一次比看模型准确率还勤。5. 常见问题与排查技巧实录5.1 训练和推理效果不一致怎么查这是AI工程里最经典的问题没有之一。我的排查顺序是检查特征口径训练用的特征和推理用的特征是不是同一份代码算的时间窗口、聚合方式、空值处理是否一致检查数据泄露训练集里有没有包含未来信息比如用全量数据算的统计特征在推理时根本拿不到。检查预处理归一化、标准化、分词器的参数是不是训练时保存的那一套我见过推理时重新fit了一个scaler结果全乱了。检查版本模型文件、特征代码、依赖库版本是否匹配用git log和pip freeze对一遍。我通常会在训练脚本里保存一份特征快照推理时用同样的输入跑一遍对比输出是否一致。如果不一致就逐字段对比很快能定位到问题。5.2 线上延迟突然飙升的排查思路延迟问题我按这个顺序查排查项可能原因验证方法模型推理输入长度突增、批大小变化看输入长度分布和批大小监控特征服务下游数据库慢查询、缓存击穿看特征服务延迟和缓存命中率网络跨机房调用、DNS解析慢看网络延迟分位数资源CPU/GPU争抢、内存不足看系统监控和容器资源限制代码死循环、锁竞争、GC停顿看火焰图和GC日志我遇到过一次延迟飙升最后发现是特征服务里有个for循环在遍历用户历史行为某个用户的历史记录特别长导致单次请求卡了3秒。解决办法是加长度截断和超时控制超过一定长度直接截断超过一定时间直接返回默认特征。5.3 模型效果衰减的应对策略模型上线后效果衰减是必然的因为数据分布会变。我的应对策略分三步短期加监控告警PSI超过阈值就通知。同时准备一个降级模型比如规则模型或旧版本模型效果差但稳定主模型出问题时能顶上。中期建立定期重训机制。我通常每周重训一次用最近三个月的数据。重训后先做离线评估达标了再走灰度发布观察一天再全量。长期做在线学习或增量训练。但这条路坑很多我建议先把定期重训跑稳了再考虑。在线学习对数据质量和监控要求极高搞不好会越学越差。5.4 成本控制的几个实用技巧AI工程不只是技术问题也是成本问题。我总结的几个省钱技巧GPU按需使用训练任务用竞价实例推理服务用CPU如果延迟允许的话。我做过一个文本分类服务CPU推理延迟只比GPU高20ms但成本降了70%。模型蒸馏用大模型教小模型小模型推理快、成本低效果通常能保留90%以上。缓存对重复输入做缓存比如热门商品的推荐结果缓存命中率能到40%。批处理推理请求攒批GPU利用率能从30%提到70%。注意省钱的前提是不影响业务指标。我见过为了省GPU把批大小调得很大结果延迟超标用户流失得不偿失。成本优化一定要在监控体系完善之后做。6. 我个人的一些实操体会带过几个从零开始的AI项目后我最大的体会是AI工程的难点不在AI在工程。模型架构、调参技巧这些网上资料很多学一学就能上手。但数据管道怎么设计、特征怎么管理、服务怎么部署、监控怎么做这些才是真正拉开差距的地方。另一个体会是文档和测试比代码重要。特征定义文档、数据字典、接口契约这些东西写起来枯燥但能省下大量沟通成本。测试用例也是我现在的习惯是每写一个特征计算函数先写测试用例用构造的数据验证输出是否符合预期。这样改逻辑的时候心里有底。最后说一个容易被忽略的点和业务方对齐指标。技术指标AUC、F1和业务指标转化率、留存率之间往往有 gap。我现在的做法是项目启动时就拉业务方一起定义“成功标准”比如“垃圾文本漏放率低于1%”比“F1达到0.9”更直观也更容易在后期评估时达成共识。这个方向后续还可以往自动化特征工程和模型可解释性两个方向扩展。自动化特征工程能减少人工试错成本模型可解释性则能帮助业务方理解模型决策减少上线阻力。这两个方向我都在尝试有新的心得再分享。
返回列表