
“ai-engineering-from-scratch”是我过去一年持续维护的一个个人项目它不是那种开箱即用的框架而是一套从零搭建AI工程能力的完整实践记录。这个项目里没有跳步从Python环境怎么配、CUDA怎么装到第一个神经网络怎么训练、模型怎么打包成服务每一步都有现场笔记和踩坑日志。一句话说清楚它做什么把一个只有编程基础、对机器学习一知半解的人带到能够独立完成“数据准备-模型训练-模型评估-服务部署-监控迭代”全链路工程化交付的能力水平。它适合三类人一是刚入门想系统学AI工程的开发者二是在业务中需要自己训模型的后端或数据工程师三是准备转算法岗但缺少工程实践的学生。我自己就是这三类人的合体。最早学AI时我犯的最大错误就是把大部分时间花在调库上——import torch跑个官方demo觉得自己会了。直到被分到一个真实项目才发现训练脚本能跑和工程能交付之间隔着一条巨大的河。所以这个项目我刻意坚持“从零开始”不是重复造轮子而是确保每个关键环节都理解本质数据为什么需要清洗、模型为什么会梯度爆炸、服务为什么需要做推理优化。1. 项目思路拆解为什么坚持“from-scratch”1.1 黑盒学习模式的陷阱市面上关于AI的教程太多了几乎每个都能让你在半小时内跑通一个图片分类demo。demo跑通的快感很容易让人产生“我已经掌握AI”的错觉。但真实的AI工程几乎从来不给你一份干净的数据集、一个调好参数的网络结构也不会让你在Notebook里慢悠悠地跑实验。真实场景往往是这样的数据存在各种奇怪的库表里特征缺失、标签错乱模型在训练集上表现很好上线后却被业务方吐槽推理服务一上并发就抖模型训好了却不知道该拿什么流程去更新它。这些问题没有一个能靠“跑通demo”解决。所以“from-scratch”真正的含义不是要你重新实现一遍反向传播而是要把AI工程的每个环节从黑盒变成白盒。数据、特征、模型、训练、评估、部署、监控、迭代这八个环节里任何一环出了事整个系统都跑不起来。早期把底子打厚后面才能少踩坑。我在项目里定了一条原则不确定原理的模块不进入下一阶段。比如PyTorch的DataLoader为什么设计成多进程如果答不上来就一直等到搞懂为止。这不是吹毛求疵而是因为AI工程里最贵的成本不是显卡是返工时间。1.2 这个项目解决的三类真实痛点从技术需求来看这个项目主要解决三个问题。第一学习路径碎片化。今天看一篇讲Transformer的文章明天看一个讲模型部署的教程知识之间没有连接起来看完就忘。我把它整合成一条有依赖关系的路线按“数据-模型-训练-部署-运维”五层推进每一层依赖上一层的理解补课的时候能清楚知道自己缺在哪一层。第二实践工具缺失。很多教程止步于模型训练对工程化部署、性能优化、模型版本管理讲得很浅。而真实业务里“跑得通”和“跑得好”是两件事。这个项目把后面这些容易被忽视的部分补了上去每个环节都有可复现的代码和实验记录工具链也保持延伸不会训练完就断掉。第三排查经验无法积累。我见过太多人在训练不收敛、显存溢出、推理延迟高的时候手足无措只能到处搜索搜到的答案又是碎片化的。项目里有一个持续维护的问题排查清单把我真实遇到的每一个坑、排查路径、最终解法都记录在案。这个清单后来成了我效率最高的部分因为AI工程的能力本质上就是面对未知异常的处置能力。1.3 学习路径设计按真实项目生命周期推进我的规划不是按文献综述的方式展开而是按一个真实项目的生命周期来设计。总周期大约四到六个月分五个阶段阶段主题核心产出第一阶段环境与基础工具链能在一台裸机上从零配置好AI开发环境第二阶段数据工程基础完成一套可靠的数据清洗、增强、切分流程第三阶段模型训练与评估训练出可用模型并建立一套评估指标体系第四阶段模型部署优化用推理优化与容器化把模型封装成服务第五阶段监控与迭代实现模型发布、回滚、持续训练的闭环这条路径有两个特点。一是每个阶段都产出一个可验证的成果而不是“学完了”。第一阶段的成果是环境一键配置脚本第二阶段是一份经过完整预处理的数据集和对应的pipeline代码第三阶段是一个在验证集上有明确指标的模型第四阶段是一个能扛住并发请求的推理服务第五阶段是带监控指标的版本迭代流程。二是每个阶段我都强制自己做一次“从零开始”的复现不依赖任何一键安装镜像从英文文档、源码或者官方API逐行搭建。这样做前期确实慢有段时间我光配环境和确认版本就花了两周但正是这段慢让我后来几乎不碰环境兼容性问题。出了问题我能直接从底层定位而不是只能对着报错发愁。2. 核心技术知识体系与技术选型逻辑2.1 算法线与平台线的双线能力模型我对AI工程的理解可以拆成两条主线一条是算法线理解模型原理、损失函数、优化器知道怎么让模型学得动另一条是平台线理解数据管理、训练容器、推理服务、监控告警知道怎么让模型稳定地活在业务里。很多人只盯着算法线把AI工程等同于炼丹。但在真实系统里一个模型能稳定运行六个月甚至更久依靠的更多是平台线的能力。数据漂移了要能发现模型效果衰减了要能定位新版本上线出问题要能第一时间回滚。这些能力看起来不性感却决定了AI项目能不能长期活下去。这个项目两条线都覆盖但顺序上有讲究先用算法线建立基本盘让你理解模型本身再进入平台线把模型放进真实的工程环境。如果一上来就讲平台很容易变成空泛的工具罗列学完还是不知道怎么把模型搬到线上。2.2 必须掌握的五个核心能力模块我把AI工程的知识体系收敛成五个模块每个模块都有明确的验收标准。数据模块。包括数据采集、清洗、标注、增强、切分、版本管理。判断标准是你能不能构造一条pipeline让数据从原始状态自动变成模型可消费的训练集和验证集并且保证可复现。常见误区是数据切分时用随机采样结果训练集和验证集之间信息泄漏模型指标虚高。模型模块。包括常见网络结构、损失函数、优化器、正则化、预训练与微调。你不需要手写每个层的反向传播但至少知道每个组件存在的理由。比如BatchNorm为什么能缓解梯度消失它的行为在训练和推理时有什么不同这些知识在模型导出和生产部署时会直接决定成败。训练模块。包括训练循环、超参数搜索、学习率调度、早停策略、分布式训练基础。这个模块的核心能力是能诊断训练过程中的异常比如loss震荡、梯度消失、过拟合。我建议每个实践者都学会画训练曲线并且养成记录每次实验配置的习惯否则后面优化就是瞎猜。部署模块。包括模型导出、推理优化、接口服务化、容器化、异步推理、批量预测。判断标准是你能把一个训练好的模型封装成稳定的服务并理解每毫秒延迟花在了哪里。运维模块。包括监控、告警、日志、模型版本管理、回滚机制、持续训练。这是AI工程里最容易被忽略的部分也是区分“实验室项目”和“生产项目”的分界线。没有监控模型坏了都不知道没有回滚机制一次模型更新失误就可能让整个业务停摆。2.3 技术栈选型与版本管理策略技术栈的选择决定了后面所有实践的效率。我花了不少时间对比验证最终确定了以Python 3.10 PyTorch 2.x Docker为核心的主导组合。先说运行时选Python没有任何悬念。AI生态里的数据处理、模型训练、服务化工具大多原生支持Python选其他语言意味着你要重新造一堆轮子。但如果你真的想长期做AI工程建议从一开始就在工程规范上向生产看齐用虚拟环境管理依赖用类型标注提升代码可读性用pytest管理测试用例而不是在Notebook里写完就扔。深度学习框架方面我建议优先考虑PyTorch。CV和NLP方向的绝大多数前沿模型都会优先发布PyTorch版本社区资料也最全。TensorFlow在部分生产场景里更成熟但对个人实践来说PyTorch的调试体验和生态丰富度明显更好。配合Hugging Face的transformers库可以很快走到微调大模型的阶段。Docker这块我建议尽早引入而不是等部署阶段再学。把训练脚本和推理服务容器化能一并解决环境复现和依赖隔离的问题。后端服务我常用FastAPI搭建相比Flask它原生支持异步和OpenAPI文档出接口效率高很多。模型中间表示用ONNX导出的模型不依赖特定训练框架部署时更灵活。选型时有个容易被忽略的点版本锁定。AI领域依赖更新极快我吃过不少“昨天还能跑今天升级就挂了”的亏。现在所有关键依赖都要锁版本并且把requirements和conda环境导出文件都放进仓库这是从零开始项目必须养成的第一个工程习惯。我的做法是先用pip freeze生成完整版本清单再手工对照PyTorch官方文档确认CUDA版本对应关系最后在空环境里完整复现一遍这一步能筛掉九成环境问题。3. 完整实操流程环境搭建到模型生产化3.1 从裸机开始搭建AI开发环境我强烈建议让所有AI工程实践都从一台“最小环境”的裸机开始。我用过的典型配置是Linux系统、NVIDIA驱动、CUDA、Miniconda和Python。第一步是确认硬件驱动用nvidia-smi查看GPU和驱动版本它能直接告诉你可以支持的CUDA版本上限这决定了后面版本选择的天花板。# 查看GPU与驱动信息 nvidia-smi # 创建虚拟环境并激活 conda create -n ai-engineering python3.10 -y conda activate ai-engineering # 安装PyTorch根据官方页面选择对应CUDA版本 pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121安装CUDA时不要只看Latest版本要结合PyTorch官方文档确认你需要的版本。我的习惯是先装PyTorch再看它对CUDA的要求然后决定安装哪个CUDA Toolkit避免出现PyTorch要求12.1、你却装了12.4这种版本错位。驱动版本一般保持较新兼容性更好。conda用Miniconda而不是完整Anaconda前者更轻量环境管理足够用。创建虚拟环境时把Python版本固定好我给这个项目选的Python 3.10兼容性和生态环境都稳。然后是工程化配置安装pre-commit统一代码风格配置.gitignore把数据集、模型权重、临时文件排除在版本库之外用dvc或简单的哈希校验管理数据版本。环境搭建阶段会碰到的最大陷阱是用conda install把包和乱七八糟的渠道混装在一起最后四五个不同版本的openblas互相打架。我的做法是尽量统一用pip安装Python包只有在确实没有pip替代时会用到conda并且严格控制channel优先级。另外如果你用的GPU显存比较小一开始就顺手把混合精度训练的依赖项装好后面可以少一次环境变更。3.2 第一个模型项目的完整实现环境就绪之后第一个项目不要上来就挑战多模态我选的是手写数字识别也就是MNIST数据集。这个项目看似简单却能把数据、模型、训练、评估四个关键环节串起来非常适合验证整个工具链。数据处理这一步别直接用别人的封装。我带着自己从零写了一遍数据加载先下载原始图片和标签自己实现按索引文件划分训练集与验证集再做标准化和随机数据增强最后封装成DataLoader。只有手写过数据pipeline你才会理解DataLoader里的shuffle、num_workers、pin_memory这些参数到底在解决什么问题。比如num_workers多进程加载数据是为了不让CPU在预处理这一环拖慢GPU训练pin_memory则是把数据映射到可分页内存减少主机到设备的复制开销。模型部分我选了经典的LeNet结构它足够简单你能逐层看明白而且参数量只有几万个非常适合在一张消费级GPU甚至CPU上快速验证。训练循环里需要配置损失函数、优化器、学习率三个核心要素。我用的是一套被反复验证过的基线配置交叉熵损失函数、Adam优化器、初始学习率1e-3、批次大小64、训练10个epoch。每训练完一个epoch在验证集上算准确率打印loss和acc形成一条清晰的变化曲线。import torch from model import LeNet from data import build_loaders model LeNet() optimizer torch.optim.Adam(model.parameters(), lr1e-3) criterion torch.nn.CrossEntropyLoss() train_loader, val_loader build_loaders(batch_size64) for epoch in range(10): model.train() train_loss, train_correct 0, 0 for images, labels in train_loader: outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() train_loss loss.item() * images.size(0) train_correct (outputs.argmax(1) labels).sum().item() epoch_loss train_loss / len(train_loader.dataset) epoch_acc train_correct / len(train_loader.dataset) val_loss, val_acc evaluate(model, val_loader, criterion) print(fepoch {epoch}: train_loss{epoch_loss:.4f}, train_acc{epoch_acc:.4f}, val_acc{val_acc:.4f})记得我第一次训练时loss下降得很慢图上接近一条水平线。排查下来发现是把学习率设成了1e-5太小了换回1e-3之后曲线立刻正常。这件事给我的启发是训练异常时得先确认参数是否正确而不是立即怀疑模型结构。很多看似玄学的训练问题最后发现都是基础配置或数据处理失误。3.3 训练优化建立自己的实验基线基线模型跑通后第二个关键阶段是优化。这个阶段的重点不是刷分而是学会识别不同优化手段的边际收益以及它们带来的额外成本。首先是数据增强。常规的数字识别任务里随机旋转、缩放、平移、加噪声这几种增强组合就能稳定提升泛化效果。我实测过一组对比不加增强时验证集准确率大概在98.5%加入增强后能稳定到99%以上而代价是训练时间增加了约20%。这提示我们数据增强是性价比很高的投入但也要注意增强强度不能过大否则会引入噪声让模型学不到有效特征。其次是学习率调度。我试过固定学习率、按epoch衰减、余弦退火三种方式固定学习率后期loss容易在最优值附近震荡余弦退火的收敛更稳。推荐从1e-3初始值和CosineAnnealingLR开始尝试。然后是早停策略监控验证集loss连续几个epoch没有改善就提前停止训练可以节省大量算力还能防止过拟合。我建立了一个简单的实验记录表格每次修改都记录关键配置和结果这成为项目后续迭代的基础。实验记录里必须包含数据集hash值、代码版本号、超参数配置、训练时长、验证集指标、随机种子。没有这些信息模型结果就不可复现后面出了任何问题都没法回溯。实验编号数据增强学习率调度验证集准确率训练时长备注exp-001无固定1e-398.52%12min基线exp-002旋转平移固定1e-399.10%15min增强有效exp-003旋转平移cosine99.28%16min收敛更稳exp-004旋转平移噪声cosine99.15%17min噪声过强这组数据说明不是所有优化策略叠加起来都有正向收益。实验记录的意义就在于你能清楚地看到哪一步改进产生了效果哪一步是白费功夫。3.4 模型部署上线从Notebook到REST服务模型训练好之后下一步就是把它部署成真实服务。我选择的路径是PyTorch模型导出ONNX、再用ONNX Runtime做推理、最后用FastAPI封装成REST服务并放入Docker容器。ONNX导出这一步需要注意的细节很多。模型必须先切到eval模式关闭梯度记录然后准备一个符合输入形状的样例张量用torch.onnx.export导出。model.eval() dummy_input torch.randn(1, 1, 28, 28) torch.onnx.export( model, dummy_input, model.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}}, )导出后要验证ONNX模型和原模型在同一输入上的输出是否一致常见的误差来源是动态尺寸处理、算子映射不完整、BatchNorm在训练和推理时的行为差异。我建议用一小组真实数据做全量对比判断最大绝对误差误差超过阈值就说明导出链路有问题。ONNX导出完成后推理函数与API层要彻底分离。推理函数负责加载模型、预处理、后处理API层只负责接收请求、调用推理函数、返回结果。这样后续无论把服务换成gRPC还是离线批量推理都不用重写推理逻辑。FastAPI配合Uvicorn的高性能表现足够支撑中等规模的并发但第一次上线前建议压测。我实测过用locust发100并发请求重点观察P99延迟和显存占用性能瓶颈通常出现在数据预处理或Python端的序列化开销上。Docker镜像我做的是两阶段构建第一阶段安装全部依赖第二阶段只拷贝运行所需的文件这样镜像体积能小三分之一以上部署更快也更容易被镜像仓库接受。FROM python:3.10-slim AS runtime WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY ./src ./src EXPOSE 8000 CMD [uvicorn, src.main:app, --host, 0.0.0.0, --port, 8000]4. 高频问题排查技巧与避坑实录4.1 训练不收敛时的排查路径这是AI工程里最常遇到的状况也是最容易让人病急乱投医的问题。遇到loss不下降我有一套自己的排查序列先看数据、再看模型、后看超参数而不是一上来就换优化器。先确认数据和标签是否对齐。我见过几次loss异常的案例最终原因都是数据切分时标签错位也就是说模型在随机学习错误目标。这个很好验证写一段脚本随机抽样输入和标签人眼确认对应关系。再检查数据是否做了归一化输入范围差异过大会让梯度更新非常不稳定你可能会看到loss在某个值附近反复震荡却降不下去。接着看模型结构是不是连乘的层太多导致梯度爆炸或消失必要时打印各层梯度的范数分布这一步对定位问题非常有帮助。如果某几层的梯度范数是0或者NaN多半是数值稳定性出问题。最后才调整训练配置包括学习率、批次大小、优化器。排查的重要工具是tensorboard或者简单的日志系统。训练时把loss、准确率、学习率、梯度范数、权重范数全部记录下来异常发生时能快速对比最近几次改动。我的经验是训练过程的可观测性投入是AI工程里最被低估的部分。很多所谓“玄学”问题有了完整日志之后都能定位到具体某个环节。4.2 过拟合的边界识别与干预时机训练集loss持续下降验证集loss开始回升这是标准的过拟合信号。处理手段有数据增强、正则化、dropout、早停但我更想说的是判断边界你是什么时候确定该动手的。我的经验是不要等验证集指标明显变差才处理而是把训练曲线的“分歧点”作为干预时机。也就是说当一个时间点之后训练集和验证集指标开始显著背离就该停止继续硬训。这个分歧点出现的epoch次数可以作为模型容量的重要参考如果太早出现说明模型容量超出问题需要考虑减少参数或者增强数据多样性。另外过拟合有时不是单一策略能解决的可能需要同时调整多个维度。比如减少模型层数的同时加强数据增强配合早停策略才能把验证集指标压下来。记录每次改动后的指标变化才能判断哪些策略对你当前的数据集真正有效这就是实验管理的重要性。还有一个容易被忽略的细节数据切分方式。如果训练集和验证集是同分布随机切分的验证集指标会比较稳定如果是按时间或来源切分的验证难度会大得多过拟合的判断也要相应调整。你要先确认自己的切分方式是否和业务场景一致。4.3 显存溢出与训练崩溃的应对显存溢出是最常见也最崩溃的报错之一。英文提示一般是CUDA out of memory。排查时先确认是“全局占用”还是“瞬时峰值”造成的有些时候是其他进程占用了显存nvidia-smi能看到全局占用情况。如果是自己训练进程占用过高先检查batch size和输入分辨率这两个最直接的因素。如果确认是自己的训练进程峰值过高可以从这些方向逐个试减小批次大小、降低输入分辨率、使用混合精度训练、用梯度累积替代批次增大、检查是否有缓存没释放。PyTorch里偶尔还会遇到显存不断增长的问题通常和代码里的引用持有有关注意减小不必要的缓存或显式删除不再需要的大变量必要时用torch.cuda.empty_cache()释放缓存。混合精度训练是一个值得优先采用的优化手段它可以用半精度浮点数存储部分张量降低显存占用同时通过损失缩放保持训练稳定。开启方式很简单PyTorch的torch.cuda.amp包提供了现成的API但需要确认GPU是支持该技术的架构。在推理阶段我还会用推理引擎的显存复用机制进一步降低峰值占用。4.4 推理延迟高的定位与优化模型上线后最常见的另一个问题是推理延迟高到业务方想骂人。排查思路要先分清楚延迟发生在哪一个环节网络传输、API序列化、数据预处理还是模型推理本身。我的排查序列是先在服务日志里埋时间戳分别记录收到请求、完成预处理、完成推理、返回响应的时刻对比就可以定位瓶颈。如果是模型推理本身慢优先考虑量化、算子融合、裁剪或换成更轻量的模型结构模型导出ONNX后用ONNX Runtime推理通常比PyTorch的eager模式快不少。有时候延迟和吞吐是矛盾的为了降低单次延迟你可能想加大并发但加大并发又会增加排队。正确做法是先压测确认服务的最佳并发度然后通过限流或队列保护服务稳定。还有一个容易被忽略的问题是CPU与GPU之间的数据复制尤其是小batch请求场景频繁的拷贝开销可能比推理耗时还大这时可以考虑批处理策略把多个请求聚合后一次性推理。5. 踩坑复盘与后续进阶路线5.1 我踩过的最深的三个坑第一个坑是过分依赖最新依赖库版本。我曾经为了尝鲜把PyTorch从1.12升到2.0结果一个模型在推理时结果和训练时对不上查了两天才发现是一个算子在新版本里改变了默认行为。“锁定版本、升级前先看release note”这条规则我是用两天时间换来的。现在每次升级依赖我都会先在一个独立环境里跑完核心测试用例再决定是否全量更新。第二个坑是轻视数据工程。我做过一个图像分类项目模型结构几乎没怎么换只是在数据清洗环节花了大力气把标签噪声、重复样本、边界异常的样本处理干净之后准确率直接提升了好几个百分点。数据质量的作用往往被严重低估很多人宁愿花一个月调模型结构也不愿意花三天检查数据这是不划算的。第三个坑是只在Notebook里工作。Notebook很适合做探索和分析但它不适合做工程。变量状态不透明、难以测试、难以复用一旦代码量变大维护就是一场灾难。从零开始项目就应该用标准Python工程项目结构来组织代码Notebook只能作为实验记录的辅助工具。5.2 “from-scratch”之后还能往哪走当上面这条从数据到部署的全链路都能独立走通以后你可以沿着几个方向继续深入。一是大语言模型工程化。PyTorch和transformers生态已经让微调一个模型变得不那么遥远但真正工程化涉及数据构造、评估集设计、推理成本控制、幻觉排查等一堆新问题这比单纯训练一个小模型要复杂得多。你可以从开源模型的推理加速开始一点一点把成本打下来。二是MLOps平台化。把训练、部署、监控、回滚这套流程沉淀成平台能力支持多项目复用。可以尝试用开源的MLflow或Kubeflow搭建会对你理解生产级AI基础设施有很大帮助。这时候你再回头看“from-scratch”阶段搭的那些脚本会发现很多都可以抽象成平台模块。三是边缘端部署。模型跑在手机、嵌入式设备或浏览器上涉及量化、剪枝、算子定制和内存管理是性能和资源约束之下最锻炼工程能力的方向。它以另一种方式考验你对模型结构和计算图的理解深度。从我的经验看AI工程的核心能力不是背熟某个框架的API而是能在未知问题面前用系统化的方法一步步缩小排查范围。这种能力没有捷径只有在真实项目里不断踩坑、记录、总结然后下一次更快地定位问题。如果你也准备开始一个“from-scratch”的项目我的建议只有一条选一个你真正感兴趣的场景把它从数据到上线完整地做一遍该走的弯路一步都别省。