
1. 从零搭建AI工程能力为什么大多数人卡在第一步就放弃了如果你最近在技术社区里频繁看到ai-engineering-from-scratch这个说法不用怀疑它不是什么新框架或者新工具的名字而是一种越来越被认可的学习路径——从零开始自己动手把AI工程化的整套链路搭一遍。我身边不少做后端、做数据、甚至做前端的兄弟都在聊这个方向但真正动手的人少坚持下来的人更少。原因很简单大部分人一上来就去啃Transformer的论文或者直接pip install一堆库跑个demo结果发现自己只是调了别人的API底层发生了什么完全不清楚遇到问题也不知道从哪查起。这篇文章想做的事情很明确把从零构建AI工程能力这件事拆开揉碎告诉你每一步到底在做什么、为什么要这么做、以及我在实际操作中踩过哪些坑。不管你是刚入行的新手还是已经有一定工程经验但想补齐AI这块短板的老手都能从里面找到可以直接上手的东西。我不会只给你一堆概念而是会把每个环节的选型逻辑、参数含义、常见故障和排查思路都讲清楚让你看完就能自己搭一套出来。先说一个我自己的判断AI工程和传统软件工程最大的区别不在于算法有多难而在于不确定性管理。传统后端你写个接口输入A必然输出B测试用例写死了就行。但AI系统里同样的输入可能因为模型版本、随机种子、硬件差异、甚至batch size的不同而给出不一样的结果。所以从零构建AI工程能力核心不是学会调某个库而是建立一套能驾驭这种不确定性的工程方法论。这也是为什么我坚持认为光看教程不动手是永远学不会的。接下来的内容会按照一条完整的工程链路来展开从环境搭建和依赖管理开始到数据处理管线的设计再到模型训练与推理的工程化封装最后是部署、监控和迭代。每一块我都会给出具体的操作步骤和背后的思考逻辑同时穿插一些只有真正动过手的人才会知道的细节。2. 环境搭建与依赖管理别让版本冲突吃掉你三天时间2.1 为什么虚拟环境不是可选项而是必选项我见过太多人在这件事上翻车。你兴冲冲地装了个最新版的深度学习框架跑通了第一个demo然后想再装另一个库做数据处理结果发现两个库对底层数值计算库的版本要求冲突整个环境直接崩掉。更麻烦的是你之前跑通的代码现在也跑不了了因为依赖被覆盖了。这种情况在AI工程里特别常见因为AI生态的库更新极快而且相互之间的依赖关系非常复杂。所以第一步不管你用什么语言、什么框架先把环境隔离做好。Python的话我推荐用conda来管理环境不是因为它比venv好多少而是因为conda能同时管理Python包和非Python的二进制依赖比如CUDA相关的库。这一点在AI场景下太重要了因为很多深度学习框架需要特定版本的GPU计算库用pip装经常出问题。具体操作上我会给每个项目建一个独立的环境命名带上项目缩写和日期比如aiefs-202501。创建的时候明确指定Python版本不要用默认的。为什么因为不同Python版本对某些库的支持不一样而且有些老项目就是跑在3.8上你升到3.12可能一堆兼容性问题。命令很简单conda create -n aiefs-202501 python3.10 conda activate aiefs-202501创建完之后第一件事不是急着装库而是先装一个pip的升级版然后配置好国内镜像源。这个不用我多说了速度差距是数量级的。但我要提醒一点镜像源不要配太多选一个稳定的就行配多了反而容易出现包版本不一致的问题。2.2 依赖锁定从在我机器上能跑到在哪都能跑环境建好之后装库的时候一定要养成一个习惯每装一个关键库就记录它的精确版本号。不要用pip install numpy这种不带版本号的命令因为今天装的是1.26明天可能就是2.0了而2.0的API变化可能让你的代码直接报错。我的做法是维护一个requirements.in文件里面只写顶层依赖和版本范围然后用pip-compile生成一个锁定的requirements.txt里面包含所有间接依赖的精确版本。这样别人拿到你的项目直接pip install -r requirements.txt就能复现出一模一样的环境。这个习惯在团队协作里尤其重要能省掉无数扯皮的时间。还有一个细节AI项目里经常需要装GPU版本的框架比如PyTorch的CUDA版本。这时候一定要去官网查清楚对应的CUDA版本和驱动要求不要凭感觉装。我踩过的坑是装了个CUDA 12的PyTorch结果服务器驱动只支持到11.8跑起来直接报错排查了半天才发现是驱动不匹配。所以装之前先跑一下nvidia-smi看看驱动版本然后去框架官网的兼容性表格里对一下这个步骤花不了五分钟但能省你半天。2.3 目录结构一开始就规划好后面少重构很多人觉得目录结构不重要先写起来再说。但AI项目和普通项目不一样它涉及数据、代码、模型、配置、日志、实验记录等多个维度如果一开始不规划好后面文件一多就乱成一锅粥。我推荐一个经过实战检验的结构project/ ├── configs/ # 配置文件按实验分组 ├── data/ # 数据目录原始数据和处理后数据分开 │ ├── raw/ │ └── processed/ ├── src/ # 源代码 │ ├── data/ # 数据处理模块 │ ├── models/ # 模型定义 │ ├── train/ # 训练脚本 │ └── inference/ # 推理服务 ├── experiments/ # 实验记录每次训练一个子目录 ├── notebooks/ # 探索性分析不进入生产 ├── tests/ # 测试用例 └── requirements.txt这个结构的关键在于把配置、代码、数据、实验记录彻底分开。配置用YAML或者JSON管理不要硬编码在代码里实验记录每次训练自动生成一个带时间戳的目录里面存模型权重、日志、评估指标。这样做的好处是当你需要复现三个月前的一次实验时直接找到对应的目录就行不用去翻代码历史。3. 数据处理管线AI工程里最脏最累但最不能省的一环3.1 数据质量决定模型上限但大多数人只关注模型结构我刚开始做AI项目的时候也犯过这个错误花大量时间调模型结构、试不同的超参数但数据就是随便清洗一下就用。结果就是模型在训练集上表现很好一到真实场景就拉胯。后来才明白模型结构决定的是你能多接近数据的上限而数据质量决定的是这个上限本身有多高。垃圾数据喂进去再牛的模型也学不出好东西。所以从零构建AI工程能力数据处理这块必须下功夫。具体来说数据管线要解决几个核心问题数据从哪里来、怎么清洗、怎么标注、怎么划分训练验证测试集、怎么在训练时高效读取。每一个环节都有坑。先说数据来源。实际项目里数据往往来自多个渠道数据库导出、日志文件、第三方API、人工采集等等。这些数据的格式、编码、字段定义可能都不一样。我的做法是先写一个数据探查脚本把每个来源的数据抽样看一遍统计字段类型、缺失率、取值范围、分布情况。这个脚本不需要多复杂用pandas写几十行就行但能帮你快速发现数据里的明显问题比如某个字段全是空值、某个类别占比超过90%等等。3.2 清洗与标注那些文档里不会写的实操细节数据清洗这块我总结了一个原则先做规则能处理的再做需要模型处理的最后做需要人工处理的。顺序不能反因为人工成本最高模型处理有误差规则处理最可靠。规则处理包括去重、格式统一、异常值过滤、缺失值填充。这里有个细节去重的时候不要只看完全相同的行还要考虑近似重复。比如文本数据里两句话只差一个标点符号这种在语义上是重复的。我一般会用哈希或者简单的相似度计算来识别。异常值过滤要结合业务理解比如年龄字段出现200岁那肯定是错的但收入字段出现一个特别高的值可能是真实的不能随便删。标注这块如果是自己标一定要先定好标注规范然后标一批做一致性检查。我见过太多项目因为标注标准不统一导致模型学出来的东西自相矛盾。如果是外包标注那更要做好质量抽检一般抽10%到20%来验证如果准确率低于95%这批数据就得返工。还有一个容易被忽略的点数据划分不能随机分。很多人直接用train_test_split随机划分这在某些场景下会导致数据泄漏。比如时间序列数据你必须按时间划分不能用未来的数据预测过去。再比如同一个用户的多条记录必须保证同一个用户的数据只出现在一个集合里否则模型会学到用户特有的模式导致评估结果虚高。3.3 高效数据读取别让IO成为训练瓶颈数据处理好之后训练时的读取效率直接影响你的迭代速度。我见过一个项目模型本身训练一个epoch只要十分钟但数据加载花了四十分钟整个训练过程被IO拖垮。解决这个问题有几个层次。最基础的是用框架自带的数据集类比如PyTorch的Dataset和DataLoader把数据预处理放在__getitem__里利用多进程并行加载。但要注意num_workers不是越大越好设太大了反而会因为进程间通信开销导致速度下降。一般从4开始试逐步增加观察GPU利用率找到那个让GPU不再等数据的临界值。进阶一点的做法是预先把数据转换成二进制格式比如TFRecord、LMDB或者HDF5。这样读取的时候不需要解析文本直接读二进制速度快很多。特别是对于图像数据把JPEG解码提前做好存成numpy数组或者二进制训练时直接读能省掉大量CPU计算。再进一步如果数据量特别大单机存不下那就需要考虑分布式存储和流式读取。但这个属于比较后期的优化了一开始不用过度设计。我的建议是先把管线跑通再根据实际瓶颈来优化不要一上来就搞一套复杂的分布式方案结果发现数据量根本没那么大。4. 模型训练工程化从脚本到可复现的实验系统4.1 训练脚本的模块化拆分很多人写训练脚本就是从头到尾一个文件几百行堆在一起。这种写法在跑单个实验的时候没问题但一旦你要对比不同配置、不同模型、不同数据版本就会非常痛苦。因为你改一个参数就得复制一份文件改来改去最后自己都分不清哪个文件对应哪个实验。正确的做法是把训练脚本拆成几个独立的模块数据加载模块、模型定义模块、训练循环模块、评估模块、配置解析模块。每个模块只负责一件事通过配置文件来组装。这样你想换模型只需要改配置里的模型名称想换数据只需要改数据路径想调超参数只需要改配置里的数值。代码本身不用动。具体来说我会用一个train.py作为入口它做的事情很简单解析命令行参数、加载配置文件、初始化数据加载器、初始化模型、初始化优化器和学习率调度器、然后进入训练循环。训练循环里每个epoch做三件事训练、验证、记录指标。记录指标这块一定要用结构化的方式比如存成JSON或者CSV不要只打印到终端。因为后面你要做实验对比的时候需要从这些记录里提取数据。4.2 随机种子与确定性让实验结果可复现AI实验最让人头疼的问题之一就是不可复现。同样的代码今天跑和明天跑结果不一样换台机器又不一样。这背后的原因有很多随机初始化、数据打乱顺序、GPU的并行计算非确定性、甚至不同版本的库实现细节不同。要解决这个问题首先要固定随机种子。Python的random、numpy的np.random、框架自己的随机模块都要设置种子。但光设种子还不够因为GPU上的某些操作本身就是非确定性的。比如CUDA的某些卷积算法为了速度会使用非确定性的实现。如果你需要完全可复现就得设置torch.use_deterministic_algorithms(True)但这会牺牲一些性能。我的建议是在实验阶段尽量保证可复现方便你对比不同方案。但到了生产环境性能优先可以接受一定程度的非确定性。另外记录实验的时候除了最终的指标还要把随机种子、库版本、硬件信息都记下来这样出了问题才能追溯。还有一个细节数据加载器的shuffle参数。训练时通常要打乱数据但如果你用了多进程加载每个进程的打乱方式可能不一样。要保证可复现需要设置worker_init_fn来给每个worker设置不同的种子同时保证整体顺序是确定的。4.3 实验管理与超参数搜索当你开始跑多个实验的时候就需要一套实验管理机制。最简单的做法是用一个表格记录每次实验的配置和结果但手动记录容易出错也容易漏。好一点的做法是用工具来自动化管理比如MLflow、Weights Biases或者TensorBoard。这些工具能自动记录配置、指标、甚至模型权重还能做可视化对比。我个人的习惯是小规模实验用TensorBoard就够了轻量、本地、不依赖网络。大规模的超参数搜索用专门的工具比如Optuna或者Ray Tune。这些工具支持多种搜索策略比如网格搜索、随机搜索、贝叶斯优化能帮你更高效地找到好的超参数组合。但我要提醒一点不要一上来就搞大规模超参数搜索。先把基线跑通确认数据和代码没问题然后再逐步调参。我见过有人一上来就开几百个实验结果因为数据里有个bug所有实验都白跑了。正确的顺序是先跑通一个小实验验证整个链路没问题再扩大规模。超参数搜索的时候要分清楚哪些参数重要、哪些不重要。一般来说学习率和batch size是最重要的优先调这两个。网络层数、隐藏单元数、dropout率这些次之。调参的时候一次只调一个维度不要同时改多个参数否则你分不清是哪个参数起了作用。5. 推理服务化把模型变成别人能用的接口5.1 模型导出与格式选择训练好的模型不能直接扔给业务方用因为训练框架和推理环境往往不一样。你需要把模型导出成一种通用的格式比如ONNX、TorchScript或者SavedModel。选择哪种格式取决于你的部署环境如果推理端也是Python那TorchScript或者SavedModel都行如果需要跨语言、跨平台ONNX的兼容性最好。导出的时候有几个坑要注意。首先是动态维度的问题。训练时batch size可能是固定的但推理时batch size是变化的。导出的时候要把batch维度设为动态的否则推理时换个batch size就报错。其次是算子兼容性。有些自定义算子或者框架特有的操作导出到ONNX可能不支持需要你重写或者用标准算子替代。导出之后一定要做数值验证确保导出前后的输出一致误差在可接受范围内。还有一个实际问题是模型大小。如果模型很大导出后的文件可能几百兆甚至几个G部署和传输都不方便。这时候可以考虑量化或者剪枝把模型压缩到可接受的范围内。量化就是把浮点参数转成低精度整数能大幅减小模型体积和加速推理但会带来一定的精度损失。剪枝是去掉模型中不重要的连接也能减小模型。这两种技术都有现成的工具支持但需要根据具体场景调优。5.2 推理服务的性能优化模型部署成服务之后性能是核心指标。用户不会接受一个请求等好几秒的接口。推理性能优化有几个方向批处理、缓存、异步、硬件加速。批处理是把多个请求合并成一个batch一起推理能充分利用GPU的并行能力。但批处理会引入延迟因为你要等凑够一个batch才能处理。所以需要根据实际流量来权衡流量大且对延迟不敏感的场景可以用较大的batch流量小或者延迟敏感的场景batch要小甚至不用。缓存是针对重复请求的优化。如果同样的输入反复出现可以把结果缓存起来直接返回不用重新推理。这在某些场景下效果很好比如推荐系统里热门商品的预测结果。但缓存要注意失效策略模型更新后缓存要清掉。异步处理是把推理请求放到队列里由后台worker处理前端立即返回一个任务ID用户过一会儿再来查结果。这种方式适合耗时较长的推理任务比如视频处理、大文档分析。硬件加速方面除了GPU还可以考虑专用的推理芯片比如某些针对深度学习优化的加速卡。但这些通常需要专门的编译和部署流程成本也更高适合大规模部署的场景。5.3 服务监控与灰度发布推理服务上线之后监控是必不可少的。你需要关注几个核心指标请求量、延迟、错误率、资源利用率。请求量和延迟反映服务的负载情况错误率反映服务的健康度资源利用率帮你判断是否需要扩容。除了这些通用指标AI服务还需要监控模型层面的指标。比如输入数据的分布是否发生了变化预测结果的分布是否偏移这些是模型退化的早期信号。我一般会定期抽样一批请求把输入和输出存下来做分布对比。如果发现明显偏移就要考虑重新训练模型。灰度发布是降低上线风险的有效手段。新模型不要一下子全量替换先切一小部分流量过去观察一段时间。如果各项指标都正常再逐步扩大比例。如果发现问题立即回滚。这个过程需要服务端支持流量切分一般用网关或者服务网格来实现。还有一个容易被忽略的点模型版本管理。线上可能同时跑着多个版本的模型你需要清楚地知道每个请求用的是哪个版本出了问题能快速定位。我的做法是给每个模型版本打一个唯一的标签请求日志里记录这个标签这样排查问题的时候一目了然。6. 迭代闭环让系统自己越跑越好6.1 数据回流与持续训练AI系统上线不是终点而是起点。真实场景的数据分布会随着时间变化模型的效果会逐渐下降。所以你需要建立一个数据回流机制把线上推理的输入和用户反馈收集起来定期用来更新模型。数据回流的关键是反馈信号的获取。有些场景下反馈很直接比如用户点击了推荐的商品这就是正反馈用户划走了就是负反馈。但有些场景下反馈很稀疏或者有延迟比如医疗诊断你可能要等几个月才知道诊断是否正确。这种情况下就需要设计一些代理指标或者用主动学习的方式挑出那些模型最不确定的样本让人工标注。收集到新数据之后不要直接全量重新训练而是要先做数据质量检查然后和旧数据混合用增量学习或者微调的方式更新模型。全量重训成本高而且可能引入灾难性遗忘把之前学好的东西忘了。微调的时候学习率要设小一点只更新部分层或者用LoRA这类参数高效的方法。6.2 A/B测试与效果评估新模型上线之前一定要做A/B测试。把用户随机分成两组一组用旧模型一组用新模型对比核心业务指标。这里的关键是指标的选择。不要只看模型层面的准确率、召回率要看业务层面的转化率、留存率、收入。模型指标好不代表业务指标好这两者之间有时候差距很大。A/B测试的样本量要足够否则统计上不显著。具体需要多少样本可以用功效分析来估算。测试周期也要足够长覆盖完整的使用周期避免周期性因素干扰。比如工作日和周末的用户行为可能不一样只测一天就下结论是不靠谱的。如果A/B测试结果不显著不要急着下结论说新模型没用。可能是样本量不够也可能是指标选得不对还可能是新模型在某些细分人群上效果好但被整体平均掉了。这时候要做分层分析看看不同人群、不同场景下的表现差异。6.3 技术债与系统重构AI系统跑起来之后会积累很多技术债。比如数据处理脚本越写越乱、模型代码复制粘贴、配置文件散落各处、实验记录不完整等等。这些债在早期不影响运行但时间长了会严重拖慢迭代速度。我的经验是每个季度留出专门的时间来还技术债。把那些临时方案重构成正式模块把重复代码抽成公共库把散落的配置统一管理把缺失的文档补上。这个过程不会直接产生业务价值但能让后续的迭代快很多。重构的时候要注意保持行为一致。AI系统的行为很难完全测试覆盖所以重构前后要做充分的对比验证。我的做法是重构前先跑一批固定的测试用例把输出存下来重构后再跑一遍对比输出是否一致。如果有差异要逐个分析原因确认是预期的改进还是引入的bug。还有一个建议把常用的工具和流程沉淀成内部库或者模板。比如数据清洗的常用函数、模型训练的脚手架、部署的配置文件模板。这样新项目启动的时候不用从零开始直接复用能省很多时间。这些沉淀也是团队能力积累的体现不会因为人员流动而丢失。7. 一些踩坑之后的真心话写了这么多最后分享几个我在从零构建AI工程能力过程中体会最深的点。第一个是不要追求一步到位。我刚开始的时候总想搭一个完美的系统结果花了很多时间在设计上真正跑起来的代码没几行。后来才明白AI工程是一个迭代的过程先跑通一个最简单的版本然后根据实际遇到的问题逐步改进。完美主义在工程领域是要不得的。第二个是日志和记录比你想的重要。训练的时候多打点日志把关键信息都记下来。推理的时候把请求和响应存下来。这些数据在排查问题的时候是无价之宝。我吃过亏有一次线上模型效果突然下降但因为没存请求日志完全不知道发生了什么只能盲猜。第三个是测试不能省。AI系统的测试和传统软件不一样但同样重要。数据管线的测试、模型导出的测试、推理服务的测试都要写。特别是数据管线一个隐蔽的bug可能让你训练出来的模型完全不可用而且很难发现。第四个是保持学习但不要盲目追新。AI领域每天都有新论文、新框架、新工具但你的精力是有限的。先把基础的东西搞扎实把一套技术栈用熟再考虑引入新的东西。我见过太多人今天学这个明天学那个最后什么都没学精。从零构建AI工程能力是一条长路但每一步都算数。你搭的每一个环境、写的每一个数据处理脚本、调的每一个模型、部署的每一个服务都在帮你积累真正的工程直觉。这种直觉是看多少教程都换不来的。所以别想太多动手就对了。