ARTICLE DETAIL

资讯详情

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

从零搭建AI工程能力:先跑通最小闭环,再谈优化

从零搭建AI工程能力:先跑通最小闭环,再谈优化 1. 从零搭建AI工程能力为什么大多数人卡在第一步就放弃了“ai-engineering-from-scratch”这个标题我第一次看到的时候脑子里蹦出来的不是某个具体框架或者工具而是一个很现实的问题一个完全没有AI工程背景的人到底该怎么从零开始把“能跑通一个AI应用”这件事真正落地我见过太多人在这条路上折戟。不是因为他们不够聪明恰恰相反很多人是太聪明了——一上来就想搞明白Transformer的注意力机制数学推导或者花两周时间纠结该学TensorFlow还是PyTorch结果三个月过去了连一个最简单的文本分类服务都没部署起来。这种“从零开始”的路径选择本身就是最大的坑。所谓AI工程和AI研究是两码事。研究关注的是“这个模型能不能在某个指标上提升0.5个点”工程关注的是“这个模型能不能在明天早上八点之前稳定地跑在服务器上响应时间控制在200毫秒以内而且成本不能超过预算”。这两个目标的差异决定了从零开始学AI工程的路径应该和学AI研究完全不同。我自己的经验是AI工程能力的构建核心不在于你懂多少算法原理而在于你能不能把数据、模型、服务这三件事串起来形成一个可运行、可观测、可迭代的闭环。这个闭环哪怕再简陋哪怕只是用一个现成的API加一个Flask接口只要它跑通了你就已经跨过了最难的那道门槛。这篇文章想聊的就是怎么用最务实的方式从零开始搭建这套能力。不追求高大上的架构不堆砌花哨的工具链只关注一件事怎么让你在最短的时间内拥有一个真正能用的AI工程基础。适合那些有基本编程能力、但对AI工程还没有系统认知的开发者也适合那些在传统软件领域做了几年、想往AI方向转型的工程师。2. 先搞清楚AI工程到底在工程什么2.1 模型不是核心数据管道才是很多人对AI工程的理解有一个根本性的偏差以为核心工作是“搞模型”。实际上在一个真实的AI应用里模型可能只占整个系统复杂度的20%剩下80%的精力都花在数据管道、服务架构、监控告警、版本管理这些事情上。我参与过一个文本分类的项目模型本身用的是现成的预训练模型做微调训练代码不到200行。但围绕这个模型的数据处理管道写了将近3000行代码。为什么因为真实场景下的数据太脏了。用户输入的文本里混杂着各种特殊符号、编码错误、超长文本、空值还有各种边界情况。这些东西不处理干净模型再好也白搭。从零开始搭建AI工程能力第一件要做的事情就是建立“数据优先”的思维。具体来说你需要掌握这几个核心环节数据采集与清洗怎么从各种来源获取数据怎么处理缺失值、异常值、重复值数据版本管理每次训练用的数据是哪个版本怎么保证可复现特征工程管道原始数据怎么转化成模型能吃的格式这个转化过程怎么自动化数据质量监控上线之后怎么发现数据分布发生了变化这些环节听起来不酷但它们是AI工程的地基。地基不牢上面盖什么都会塌。2.2 训练只是冰山一角推理服务才是日常另一个常见的认知误区是以为模型训练完了就万事大吉。实际上训练只是整个生命周期里很短的一环。模型上线之后的推理服务才是真正考验工程能力的地方。推理服务要考虑的问题包括并发请求怎么处理、响应延迟怎么控制、模型怎么加载和卸载、显存怎么管理、服务怎么扩容缩容、故障怎么自动恢复。这些问题每一个都比训练本身更贴近生产环境。我刚开始做AI工程的时候写了一个很简单的模型服务单进程单线程本地测试跑得好好的。结果一上线并发量稍微上来一点服务直接卡死。后来才知道模型推理是计算密集型任务必须用异步或者多进程的方式来处理并发请求。这种经验不亲自踩一次坑是学不会的。2.3 从零开始的正确姿势先跑通再优化说了这么多那到底该怎么从零开始我的建议是先跑通一个最小闭环再逐步优化。最小闭环包括一份干净的数据、一个能用的模型、一个能接收请求并返回结果的接口。这三样东西串起来哪怕再简陋你就已经有了一个AI应用的雏形。然后在这个基础上逐步加入监控、日志、版本管理、自动化测试这些工程化的东西。这个顺序很重要。很多人反过来做先花大量时间搭建完美的架构结果迟迟跑不通一个完整的流程最后热情耗尽不了了之。先跑通再优化不仅能让你快速获得正反馈还能让你在实际运行中发现真正的问题在哪里。3. 环境搭建别在工具选择上浪费超过一天3.1 Python环境管理的正确打开方式AI工程离不开Python但Python的环境管理是出了名的让人头疼。我见过太多人在这一步卡住各种版本冲突、依赖不兼容折腾好几天。我的建议很直接用conda或者uv来管理环境不要用系统自带的Python也不要在全局环境里装包。具体操作上每个项目建一个独立的环境环境名就用项目名简单好记。# 用conda创建环境 conda create -n ai-engineering python3.11 conda activate ai-engineering # 或者用uv速度更快 uv venv ai-engineering source ai-engineering/bin/activate为什么强调环境隔离因为AI领域的依赖包更新极快不同项目对同一个包的不同版本要求经常冲突。没有环境隔离你迟早会遇到“装了这个包那个包就挂了”的情况。提示环境建好之后第一时间把依赖导出到requirements.txt或者pyproject.toml里。不要等到项目做完了再补那时候你根本记不清装了哪些包。3.2 开发工具链的最小集合工具选择上我的原则是够用就行别追求大而全。以下是我认为从零开始时必须配置好的几样东西工具类型推荐选择用途说明代码编辑器VS Code插件生态丰富对Python和Jupyter支持好版本控制Git代码和配置的版本管理必须实验跟踪MLflow或WB记录每次训练的参数和指标数据版本DVC大数据文件的版本管理服务框架FastAPI轻量、异步支持好、自动生成文档这个列表里前两个是必须的后面三个可以随着项目复杂度提升逐步引入。不要一上来就把所有工具都装上那样只会增加学习负担。3.3 硬件资源的现实考量说到硬件很多人会纠结要不要买显卡。我的建议是刚开始的时候不要买。用云端的按需实例或者Google Colab这类免费资源就够了。为什么因为从零开始阶段你大部分时间花在写代码、调管道、搭服务上真正需要大规模算力的训练任务很少。等到你确实需要长期训练模型了再考虑买卡或者租长期实例。那时候你对显存需求、训练时长这些参数也有了实际感知能做出更合理的决策。如果确实需要本地GPU一张消费级显卡比如RTX 4060 Ti 16GB对于入门阶段来说完全够用。显存比算力更重要因为显存不够模型根本加载不进去算力再强也没用。4. 数据管道AI工程里最脏最累但最重要的活4.1 数据清洗的常见坑与处理策略数据清洗这件事说起来简单做起来全是细节。我总结了几类最常见的问题和处理方法编码问题。中文文本里经常混着GBK、UTF-8、Latin-1各种编码直接读进来就是乱码。处理方法是统一转成UTF-8遇到无法解码的字符用errorsreplace参数替换掉。超长文本。模型对输入长度是有限制的超长文本必须截断。但截断策略有讲究是从头截、从尾截还是取中间我的经验是对于分类任务取头部加尾部拼接的效果通常比单纯截断好。特殊符号。HTML标签、Markdown标记、各种控制字符这些都需要清理。但要注意有些符号是有意义的比如代码里的缩进、数学公式里的符号不能一刀切全删掉。类别不平衡。真实数据里各类别的样本数量往往差异很大。处理方法包括过采样、欠采样、数据增强、调整损失函数权重等。具体用哪种要看数据量和任务特点。import re def clean_text(text): # 统一编码 if isinstance(text, bytes): text text.decode(utf-8, errorsreplace) # 去除HTML标签 text re.sub(r[^], , text) # 去除多余空白 text re.sub(r\s, , text).strip() # 截断超长文本 if len(text) 512: text text[:256] text[-256:] return text这段代码看起来简单但每一条规则背后都是踩过坑之后总结出来的。比如截断策略我试过只取头部结果发现很多文本的关键信息在结尾试过只取尾部又发现开头有重要的上下文。最后取头尾拼接效果最稳定。4.2 数据版本管理别让“上次那个数据”成为谜题数据版本管理是很多人忽略的环节直到有一天你发现模型效果突然下降了却找不到原因——因为你不记得上次训练用的是哪份数据。DVC是解决这个问题的好工具。它的核心思路是把大数据文件存在别的地方比如对象存储Git里只存一个指针文件。这样既不会让Git仓库变得巨大又能保证每次实验的数据可追溯。# 初始化DVC dvc init # 添加数据文件 dvc add data/training_data.csv # 提交指针文件到Git git add data/training_data.csv.dvc .gitignore git commit -m add training data v1每次数据更新都通过DVC生成新的版本并在Git里记录对应的commit。这样任何时候你都能精确地回到某个实验对应的数据版本。4.3 特征管道的自动化与可复现特征工程最怕的是什么是训练时和推理时的处理逻辑不一致。训练的时候用了一套代码做特征推理的时候又写了一套结果两边对不上模型效果大打折扣。解决办法是把特征处理逻辑封装成一个独立的模块训练和推理都调用同一个模块。这个模块的输入是原始数据输出是模型能吃的特征向量。class FeaturePipeline: def __init__(self, config): self.config config def transform(self, raw_data): # 所有特征处理逻辑都在这里 features self._extract_features(raw_data) features self._normalize(features) return features def _extract_features(self, raw_data): # 具体特征提取逻辑 pass def _normalize(self, features): # 归一化逻辑 pass这个类在训练脚本里被调用在推理服务里也被调用。只要保证输入数据格式一致输出就必然一致。这个设计模式看起来简单但能避免大量“训练推理不一致”的诡异问题。5. 模型训练与实验管理让每次尝试都有迹可循5.1 实验跟踪别再用Excel记结果了我刚开始做实验的时候用Excel记录每次训练的参数和指标。跑了不到20组实验Excel就乱成一锅粥了这行是学习率0.001的结果那行是0.0001的结果但忘了记batch size是多少完全没法对比。后来改用MLflow每次训练自动记录参数、指标、模型文件还能在Web界面里直观对比不同实验的结果。这个工具的学习成本很低基本上一小时就能上手。import mlflow mlflow.set_experiment(text-classification) with mlflow.start_run(): mlflow.log_param(learning_rate, 0.001) mlflow.log_param(batch_size, 32) # 训练过程... mlflow.log_metric(accuracy, 0.92) mlflow.log_metric(f1_score, 0.91) mlflow.pytorch.log_model(model, model)关键是要养成习惯每次训练都开一个run把所有相关参数和结果都记进去。这样一周之后回头看你能清楚地知道哪组参数效果最好而不是靠模糊的记忆。5.2 训练脚本的工程化改造从零开始的训练脚本往往是一个大文件从数据加载到模型定义到训练循环全写在一起。这种脚本跑一次两次还行实验多了就完全没法维护。我的做法是拆成几个模块数据加载模块、模型定义模块、训练循环模块、评估模块。每个模块有清晰的输入输出接口可以独立测试和替换。# train.py from data import load_data from model import create_model from trainer import Trainer def main(config): train_data, val_data load_data(config.data_path) model create_model(config.model_name) trainer Trainer(model, config) trainer.train(train_data, val_data) if __name__ __main__: config load_config(config.yaml) main(config)这样拆分的另一个好处是当你想换一个模型试试的时候只需要改config里的model_name其他代码都不用动。实验效率会高很多。5.3 小规模快速验证别一上来就全量训练这是我很想强调的一点不要一上来就用全量数据训练。先用小样本比如1%的数据跑通整个流程确认代码没问题、指标在合理范围内再用全量数据跑。为什么因为全量训练可能要好几个小时如果代码有bug这几个小时就白费了。用小样本跑几分钟就能发现问题迭代速度会快很多。我通常会准备三档数据规模tiny100条、small1000条、full全部。tiny用来调试代码small用来快速验证模型效果full用来出最终结果。这个习惯能帮你节省大量时间。6. 模型部署与服务化让模型真正被人用起来6.1 从脚本到服务FastAPI的最小实践模型训练好了怎么让别人用最简单的办法是写一个FastAPI服务接收HTTP请求返回模型预测结果。from fastapi import FastAPI from pydantic import BaseModel import torch app FastAPI() class PredictRequest(BaseModel): text: str class PredictResponse(BaseModel): label: str confidence: float model None app.on_event(startup) def load_model(): global model model torch.load(model.pt) model.eval() app.post(/predict, response_modelPredictResponse) def predict(request: PredictRequest): features preprocess(request.text) with torch.no_grad(): output model(features) label postprocess(output) return PredictResponse(labellabel, confidence0.95)这个服务虽然简单但已经包含了生产环境需要的基本要素模型在启动时加载一次、请求处理是独立的、返回结构化的响应。6.2 性能优化的几个关键点服务跑起来之后下一步是优化性能。几个最有效的优化手段批处理。单个请求推理一次太浪费把多个请求攒成一批一起推理吞吐量能提升好几倍。但要注意延迟和吞吐的权衡批处理会增大单个请求的响应时间。模型量化。把FP32的模型转成INT8模型体积缩小4倍推理速度提升2-3倍精度损失通常在1%以内。对于大多数应用场景这个 trade-off 是值得的。异步处理。FastAPI支持async/await对于IO密集型的操作比如读写数据库用异步能显著提升并发能力。但模型推理本身是CPU/GPU密集型的异步帮助不大需要用多进程或者专门的推理服务器。缓存。对于重复的输入直接返回缓存结果避免重复推理。缓存的key可以用输入文本的hash值。6.3 监控与日志上线只是开始服务上线之后你必须知道它运行得怎么样。最基本的监控指标包括请求量、响应时间、错误率、模型推理耗时、GPU利用率。日志方面每次请求的输入、输出、耗时都要记录下来。这些日志不仅能帮你排查问题还能作为后续模型迭代的数据来源。import logging import time logger logging.getLogger(__name__) app.post(/predict) def predict(request: PredictRequest): start time.time() result model_inference(request.text) elapsed time.time() - start logger.info({ input: request.text[:100], output: result.label, latency_ms: elapsed * 1000 }) return result注意日志里不要记录完整的用户输入特别是涉及隐私的数据。记录前100个字符或者hash值就够了。7. 迭代闭环让系统自己越跑越好7.1 数据回流把生产环境的数据变成训练数据一个设计良好的AI系统应该能把生产环境产生的数据自动回流到训练管道里。用户的实际输入、模型的预测结果、用户的反馈如果有的话这些都是宝贵的训练数据。具体做法是在服务层加一个异步任务把每次请求的输入输出写到数据存储里。然后定期比如每周把这些数据拉出来做清洗和标注加入到训练集里。这个闭环建立起来之后你的模型就能持续进化而不是上线之后就一成不变。7.2 A/B测试用数据说话别拍脑袋新模型训练好了怎么知道它比旧模型好直接全量替换风险太大万一新模型在某些场景下表现更差呢A/B测试是标准做法把流量分成两组一组用旧模型一组用新模型跑一段时间后对比两组的核心指标。如果新模型显著更好再全量切换。实现上可以在服务层加一个简单的分流逻辑import random def route_request(request): if random.random() 0.1: return new_model_predict(request) else: return old_model_predict(request)10%的流量走新模型90%走旧模型。等积累足够的数据之后再做统计检验。7.3 持续学习什么时候该重新训练模型不是训练一次就一劳永逸的。数据分布会变化用户行为会变化旧模型的效果会逐渐下降。你需要设定一个触发重新训练的条件。常见的触发条件包括模型效果指标连续下降超过阈值、数据分布发生显著变化、积累了足够多的新标注数据、业务需求发生了变化。我通常会设置一个定时任务每周检查一次模型效果。如果发现指标下降超过5%就自动触发重新训练流程。这个流程包括拉取最新数据、清洗、训练、评估、如果比当前模型好就部署。这套闭环建立起来之后AI工程的工作就从“手动救火”变成了“自动运行”。你只需要定期检查一下系统状态处理一些异常情况就行了。8. 一些踩坑之后的真心话做AI工程这几年踩过的坑比写过的代码还多。有几个教训是我想特别分享的不要追求一步到位。我见过太多项目一开始就想搭建完美的架构结果三个月过去了连个demo都没跑通。先跑通最小闭环再逐步优化这个顺序不能反。数据质量比模型大小重要。一个在干净数据上训练的小模型效果往往比在脏数据上训练的大模型好。花时间清洗数据比花时间调模型参数回报率高得多。监控比训练重要。模型上线之后如果没有监控你根本不知道它运行得怎么样。等用户投诉了才发现问题那就太晚了。版本管理要贯穿始终。代码版本、数据版本、模型版本三者必须一一对应。否则出了问题你连回滚到哪个版本都不知道。简单方案优先。能用规则解决的不要上模型能用小模型解决的不要上大模型能用现成API解决的不要自己训练。每增加一个复杂度就增加一份维护成本。最后说一个我自己的习惯每次开始一个新项目我都会先写一个README里面写清楚这个项目要解决什么问题、输入输出是什么、怎么运行、怎么测试。这个README在项目进行过程中不断更新最后就成了最好的文档。这个习惯看起来不起眼但能帮你理清思路也能让接手的人快速上手。
返回列表