说说“ai-engineering-from-scratch”这件事。我见过太多人把AI工程理解成“调一下API”“跑通一个notebook”,真正遇到数据垃圾、显存溢出、模型上线后效果飘忽这些事,一下就懵了。“from scratch”这个路线,说白了就是逼着你把AI系统的每一块骨头都亲手摸一遍:数据怎么进来、怎么清洗、怎么划分,模型怎么训练、怎么收敛、怎么保存,上线之后怎么服务、怎么监控、怎么迭代。它能搞定的,不是某个具体算法,而是你对整套AI工程体系的掌控力。如果你已经会用框架跑通demo,却总觉得换个场景就hold不住,这篇文章就是给你准备的。
1. 先聊清楚:这条“with scratch”路线到底要解决什么问题
1.1 为什么我不推荐你直接套框架
很多刚入行的朋友习惯一上来就拉一个HuggingFace的模型,再配一套现成的训练脚本,数据塞进去,loss下降,皆大欢喜。这个流程本身没错,但它掩盖了太多关键细节:你的数据是不是干净的?划分的时候有没有泄漏?学习率为什么是这个值?显存不够到底是代码问题还是数据问题?模型上线之后预测分布变了怎么办?
这些问题的答案,在现成框架里是找不到的。框架帮你把复杂的事情封装好了,同时也把你和真实问题隔开了。我见过不少能熟练调用各种库的开发者,一旦需要自己处理一个非标准格式的数据集,或者把模型部署到低延迟场景,就完全无从下手。原因很简单:他们从来没有从零把一个系统搭起来过,不知道每个环节内部到底发生了什么。
“from scratch”这条路的价值就在这。它不是让你重新发明轮子,而是让你把轮子拆开看一遍,搞清楚它为什么是圆的、辐条怎么受力,然后再把轮子装回去。这个过程会很慢,很痛苦,但走完之后,你对AI工程的理解会是体系化的,而不是点状的。
1.2 路线整体设计:把AI工程拆成四层
我自己的习惯,是把“AI工程”拆成四个大层,每一层都对应一组可以独立验证的技能:
数据工程层:数据获取、清洗、去重、标注校验、划分、数据管线构建。这一层决定了模型的天花板。很多人说“数据和特征决定了机器学习的上限”,这句话放到工程体系里依然成立,只是表现方式变成了:数据质量不过关,后面所有环节都在白费力气。
模型与训练层:模型选型、损失函数设计、优化器配置、训练循环实现、超参数调整、分布式训练、混合精度、断点续训。这一层是大多数人最关注的部分,但也是最容易“照抄参数然后翻车”的部分。
部署与推理层:模型导出、服务化封装、推理优化(量化、批处理、引擎选择)、延迟与吞吐调优、灰度发布。这一层是“能跑demo”到“能上线”之间的天堑,也是AI工程师和算法研究员最明显的分水岭。
评估与迭代层:离线评估指标设计、回归测试集建设、线上监控指标、bad case分析、模型更新流程。这一层决定了系统的长期健康度,也是最容易被忽视、导致系统“越跑越歪”的根源。
这样拆的好处非常明显:每一层都能单独验证,单独出问题单独查,不用等到最后才发现整个系统崩了却不知道崩在哪。我自己在带项目的时候,也严格要求每个阶段必须有可检查的产出物,而不是一路闷头冲到部署。
1.3 这条路线适合谁,不适合谁
说实话,这条路不适合所有人。如果你只是想快速做出一个效果还行的模型,解决眼前的任务,直接用成熟的框架和AutoML工具效率更高,没必要自讨苦吃。但如果你打算在AI工程方向长期深耕,或者你所在的业务场景非常特殊(数据格式怪异、延迟要求苛刻、领域知识复杂),那这条路线几乎是必经之路。
具体来说,我觉得适合三类人。第一类是刚入行一到三年的开发者,想从“会调用”进阶到“会构建”。第二类是算法工程师,想补齐工程短板,让模型不只停留在paper里。第三类是技术负责人,需要评估AI项目可行性、设计技术方案,没有亲手搭过整个链路的人很难做出靠谱的判断。
不适合的人也很清楚:只想要结果不想要过程的、时间非常紧急的、公司已经有成熟AI平台只想用平台的。这些情况下,“from scratch”都是一种时间浪费。懂得判断什么时候该从零搭、什么时候该用现成方案,本身就是工程能力的一部分。
2. 数据工程:最先要过的坎
2.1 数据规模与质量的取舍:多少样本才够
这是被问得最多的问题,但也是最没法用一个数字回答的问题。我问你几个反问题:你做的是什么任务?类别有多少?数据本身的信噪比如何?你对模型精度的期望值是多少?这些条件全都不一样,样本量需求天差地别。
不过可以给出几个粗糙的经验坐标。对于文本分类这种相对简单的任务,类别不太多(几十类以内)的情况下,每类至少要有几百条样本,整体到1万条级别,才能看到一个相对稳定的指标曲线。再往下,比如每类只有几十条,那任何指标波动都可能是噪声,先别急着调模型,先想办法扩充数据。对于生成类任务或者开放域任务,数据量需求则通常再高一到两个数量级。
比样本数量更关键的,是样本质量。我踩过最深的坑是数据里有大量重复和近似重复的样本。比如一个电商评论数据集里,同一个用户反复发布几乎相同的评论,直接把训练集里的重复率拉到30%,模型训练出来之后,在验证集上表现不错,一上真实场景就垮——因为它实际上在背诵高频样本,而不是理解语义模式。清洗之后重复率降到2%,同样一个模型结构,线上效果涨了十几个点。数据清洗不花一分钱GPU,收益却往往比换模型大得多。
还有一个容易忽略的点:标签噪声。模型对标签一致性的敏感程度远高于你对数据“看起来差不多”的容忍度。几个人标注的口径如果不一致,模型学到的东西就会混乱。有条件的话,一定要做标注一致性抽检,哪怕只是抽几十条人工复核,都能发现不少问题。
2.2 预处理与数据泄漏的血泪教训
数据泄漏算是工程里最阴间的问题,因为它不会让你报错,只会让你的指标幽灵般虚高。最常见也最经典的:时间序列任务里用了未来信息。我之前做过一个销量预测项目,把整个时间段的特征都做了全局归一化,结果模型训练指标漂亮得吓人,到了线上预测完全失灵。原因就是归一化的均值和方差是从全量数据里算出来的,包含了未来数据的信息。正确的做法是只用训练集的统计量,并且滚动窗口划分数据,保证验证集的每个时间点都晚于训练集的全部时间点。
文本数据也有类似的坑。比如做文本分类,数据里有文本长度这个统计特征,而测试集构建时又刚好是长度分布的某个子集,模型就可能学到“长文本属于某个类别”这种伪规律。更隐蔽的情况是:你在预处理阶段对整个数据集做了同样的清洗或截断操作,使得不同类别的数据形态产生了结构性差异,模型捕捉到的根本不是语义信号,而是这些人为差异。
处理这类问题的核心思路只有一条:数据划分必须发生在任何有监督信息的预处理之前。先把训练集、验证集、测试集按原始状态切好,再去分别做清洗、归一化、特征工程。我遇到过很多次想图省事先统一清洗再切分的情况,最后都因为奇奇怪怪的指标异常被迫返工。这个顺序问题,值得你一开始就重视。
2.3 数据管线的构建与加载速度
训练过程中的数据加载,往往是工程里最隐形也最拖后腿的部分。我见过不少人训练一个模型,GPU利用率只有百分之三四十,原因竟然是DataLoader在吭哧吭哧地读磁盘、解码图片,数据喂不过来。GPU算得快,数据跟不上,训练时长直接翻倍,这属于典型的“木桶短板”。
PyTorch的DataLoader有几个常用参数值得认真调。num_workers一般建议设成CPU核心数或核心数的一半,太少了数据加载不够快,太多了进程切换开销反而增大。pin_memory=True在训练场景下通常能带来几个点的性能提升,代价是内存占用多一些。shuffle一定要开,除非你的数据本身就是随机顺序,否则模型会学到batch之间的顺序模式。
还有一个容易忽略的点:预处理操作到底是放在数据加载时做还是提前离线做好。如果每个epoch都要对原始数据做同样的重活,比如正则表达式清洗、复杂的文本解析,那就应该把这些结果提前缓存,线上加载时只做轻量的转换。我用过一个很粗暴但有效的方案:把清洗后的数据序列化成二进制格式(比如Arrow或pickle),加载速度能提升五六倍,训练迭代速度快了,实验周期自然就短了。
3. 模型与训练工程:从基线到可收敛
3.1 框架怎么选:PyTorch为主,为什么
框架选型这个话题,很容易变成粉丝论战,但落到实际操作上,我的结论很明确:大部分场景选PyTorch,特殊场景另说。原因不是PyTorch比TensorFlow或者JAX更“高级”,而是它的调试体验和生态完整度更适合作工程迭代。
PyTorch的命令式编程模型,让断点调试变得非常自然。你可以在训练循环的任何位置插入打印、检查梯度、查看中间变量。这种可观测性在从零搭工程时至关重要,因为你会遇到大量“不知道哪里出问题”的时刻,一个能随时停下来看内部状态的框架,能救你很多次。相比之下,静态图框架在性能优化上有优势,但从零开始排错的时候,那层“编译-执行”的转换会让人非常头疼。
生态方面,PyTorch的覆盖面从模型库到部署工具链几乎全打通了,模型训练用PyTorch,导成ONNX,再用Triton或ONNX Runtime做推理,这条链路非常顺畅。还有一点:招聘市场上,熟悉PyTorch的候选人多,团队协作和后续维护都更容易。框架只是工具,选一个你能在项目周期里快速排错、社区资料充足的,就是好选择。
3.2 一组能用很久的训练参数基线
从工程角度,我强烈建议你准备一套“默认参数基线”,所有新项目先跑这套基线,再根据具体情况调整。这样能极大减少重复试错的成本。下面给我自己常用的一套,以Transformer类模型微调场景为例:
- 优化器:AdamW,
lr=2e-5到5e-5起步,小数据集从2e-5开始调 weight_decay=0.01warmup_ratio=0.1,即前10%的训练步数学习率线性上升batch_size=32起步,显存不够用梯度累积等效到32- 混合精度:AMP开启(
torch.cuda.amp,或者直接用autocast和GradScaler) - 梯度裁剪:
max_grad_norm=1.0 - 训练轮数:先从3个epoch开始,观察验证集指标是否还在上升,再决定是否延长
为什么这几个参数是这种搭配?AdamW本身已经能处理很多训练不稳定问题,weight decay提供轻量正则化,防止大模型在数据量不足时过拟合。Warmup是让模型在训练初期不要用太大的学习率猛冲,先用小步幅适应参数空间,再进入主要训练阶段。我见过太多人把warmup忽略掉,结果loss直接起飞,还以为是代码写错了。
混合精度这块多说一句。AMP能显着减少显存占用并加快速度,尤其是在TensorCore支持的GPU上。我第一次在项目里开AMP时非常担心精度损失,实测下来fp16的前向和反向后,再配合loss scaling,绝大多数任务精度损失可以忽略不计。如果你的模型训练速度不够快,先检查有没有开AMP,这个改动通常比调任何超参都立竿见影。
3.3 实验管理:没有记录等于白练
我犯过最蠢的错误之一,是训练完一个模型觉得效果很好,但记不清用了哪组超参数、哪份数据版本。等到要复现、要调优的时候,只能靠回忆和猜。这种情况一次两次还好,多了之后,你根本无法判断实验间的差异到底是来自参数改动还是数据改动。
后来我强制自己用实验管理工具,不管项目规模大小。轻量方案可以用CSV文件记录每次运行的关键信息,稍微正规一点可以用MLflow或者Weights & Biases。我自己的偏好是W&B,它的交互界面方便快速对比多组实验的指标曲线,团队协作时大家都能看到实验结果,减少“各自为政”的混乱。
实验管理不只是“记一下参数”那么简单,核心是记录数据版本和代码版本。模型效果变好或变差,你得能回答“是哪个改动导致的”。我的习惯是把数据集每次清洗后的版本打标签,代码用git提交哈希关联,训练脚本里自动记录这些信息。这样一来,任何一次训练结果都能追溯到完整上下文。这花不了多少时间,但能让你在项目后期省出大量的“考古”时间。
4. 推理部署与线上闭环:让模型真正干活
4.1 服务化:把模型变成一个接口
训练出一个指标不错的模型,这只是第一步。真正让模型创造价值的,是把它封装成一个能响应外部请求的服务。很多初学工程的人会低估这一步的复杂度:模型加载要多久?并发请求来了怎么办?输入数据格式怎么校验?不支持某个输入时该返回什么?
我的经验是先从一个“能用”的版本开始,别过度设计。最常见的方案是用FastAPI包一个HTTP接口,加载模型到内存,接收JSON格式的输入,预处理后丢给模型推理,再把结果包装成JSON返回。这个版本响应速度不会很快,但足够让业务方先联调起来,也让你对整体流程有直观认识。服务化过程中有一个很容易忽略的细节:输入校验和异常处理必须做。线上请求的数据格式千奇百怪,如果模型报错直接导致服务崩溃,那运维事故就来了。在接口层把参数校验、超时控制、异常捕获做好,比什么都重要。
模型加载的时机也值得注意。懒加载(服务启动后再加载模型)会让第一个请求非常慢,容易让调用方误以为服务挂了。我更推荐服务启动时就预加载模型,哪怕让初始化时间多几秒钟,换来线上请求的稳定。如果模型很大,加载一次要几十秒甚至更久,可以考虑常驻内存或预热机制,这比每次冷启动要靠谱得多。
4.2 推理优化:量化、批处理与引擎选择
部署上线之后,你很快会遇到性能问题:并发上来了,响应时间变长,GPU成本居高不下。这时候就该做推理优化了,方向主要有四个:模型压缩、推理引擎、动态批处理、缓存。
量化是见效最快的压缩手段。把fp32模型转成INT8,在精度损失可接受的范围内,推理速度通常能提升两到三倍,显存占用也大幅下降。实操时先用一个小验证集评估量化前后的指标差异,尤其关注业务重点关注的指标(比如分类任务的F1,排序任务的NDCG),如果损失在可接受范围内,直接上量化版本。我见过量化后指标几乎不掉但速度快了两倍还多的案例,也见过量化后直接崩坏的任务,所以评估这步不能省。
推理引擎方面,ONNX Runtime和TensorRT是我最常用的两个。ONNX Runtime胜在通用性好,支持的算子覆盖广,导出后基本都能跑;TensorRT在NVIDIA GPU上性能更极致,但转换过程对模型结构的兼容性要求更高,某些自定义算子会卡住你。工程上我的建议是:先导ONNX用ONNX Runtime跑,如果延迟还不达标,再尝试TensorRT。别一开始就上最复杂的方案。我自己有一次为了追求极致性能在TensorRT上折腾了几天,最后发现ONNX Runtime配合调整批处理策略已经满足了业务需求,前期的折腾纯属过度优化。
动态批处理是提升吞吐的利器。真实业务里请求是零散到达的,如果一个一个推理,GPU利用率很低。动态批处理把一小段时间内的多个请求攒在一起,凑成一个batch统一推理,整体吞吐能翻好几倍。实现的方式可以是在服务层做一个队列,攒够数量或等待超时就触发一次推理。参数上我一般把最大batch设为8到16,最大等待时间设为10到20毫秒。需要注意:这两个值存在矛盾,最大batch设大了容易让延迟升高,等待时间设长了也伤害延迟,具体取值要用实际的请求到达率去压测调整。
换个好懂的类比:训练阶段就像研究一道菜的菜谱,跑通铁锅炒菜;部署阶段则像开餐厅后厨,要同时给很多桌出菜,不能因为某桌客人点单就让其他人干等着。动态批处理、量化和推理引擎,都是从“做出一道好菜”走向“稳定出很多道菜”的手段。
4.3 评估与回归:迭代不翻车的守门人
模型上线不是终点,而是一个新的开始。业务数据会变、用户行为会变,模型的效果一定会随时间衰减。如果每次更新模型都是“凭感觉觉得效果变好了就上”,那系统早晚会翻车。
我的做法是维护一套“金标回归集”。这组数据是精心挑选的、覆盖业务关键场景的样本,标注质量有保障,不参与任何训练。每次要上线新模型前,先在回归集上做完整评估,与线上旧模型对比。如果新模型在回归集上主要指标没有下降、关键bad case有改善,才允许上线。这个流程看似烦琐,但能拦住绝大多数“局部优化、全局劣化”的情况。
线上监控则是另一道防线。除了基础的请求量、错误率、延迟,我更关注几个模型专属指标:预测结果的类别分布漂移(如果线上预测的类别比例和训练时候差很远,说明数据分布变了)、平均置信度变化(置信度普遍下降往往意味着遇到了没见过的输入)、空预测率和兜底分支触发率。这些指标一旦出现趋势性的异常,就要立刻排查,通常业务侧已经发生了变化。
5. 常见问题与排查技巧实录
5.1 训练不收敛的雷区
训练不收敛或者收敛后效果差,是新手遇到最多的现象。我的排查顺序是固定的:先看数据,再看训练配置,最后才怀疑模型结构。
数据层面最常见的是标签错误,尤其人工标注的数据,有时错误率高得惊人。我曾经接到一个“模型怎么调都不涨”的项目,最后抽样检查训练数据,发现标签错误率接近15%,把明显标错的样本修了一批之后,指标直接原地起飞。所以遇到不收敛,我的第一件事永远是随机抽几百条训练样本人工看一遍,核对文本内容和标签是否一致。
训练配置层面,优先级最高的是学习率。学习率太大,loss会剧烈震荡甚至直接爆炸;学习率太小,loss下降很慢像蜗牛爬。排查时画loss曲线最直观,震荡锯齿状多半是学习率偏高,平缓下滑太慢可能需要加大学习率。其次检查是否开了梯度裁剪,以及warmup比例是否合理。最后检查优化器里的weight decay是否设得过大,这也会导致loss降不下去。模型结构的问题一般最后才怀疑,因为随机初始化的模型在数据不太差的情况下总会有些学习信号,完全不下降往往说明前面某一步已经出问题了。
5.2 显存爆了的排查路径
OOM(Out of Memory)是训练阶段最容易遇到的错误之一。很多人第一反应是调小batch size,这确实是最直接的方案,但有时候问题根源不在batch size。
我先说一下排查顺序:先看是不是数据加载阶段把整个数据集都塞进了内存或显存。有人为了方便,把预处理后的数据一次性转成Tensor丢到GPU上,几万条样本一上去就爆了。这个问题常见且隐蔽,改成DataLoader按需加载就能解决。
排除了数据问题之后,再看模型本身。序列模型特别关注序列长度是否被无谓拉长,图片模型关注输入分辨率是否过大。我这里有个经验:先用最小的batch size跑一步试试,如果batch=1依然OOM,那一定是模型或数据加载的问题,而不是batch大小的问题。梯度检查点(gradient checkpointing)是另一招,它对前向传播的中间结果做了“时间换空间”的取舍,能大幅降低显存,代价是训练变慢一些。最后还可以考虑混合精度,它能同时减少显存占用和提升速度,值得优先检查是否已经开启。
5.3 数据管线慢得离谱
GPU在等数据,这是最浪费时间的故障之一。表现特征是:GPU利用率上不去,训练日志里每个step的耗时跳动很大,数据加载的时间明显超过模型计算时间。排查方法也很直接:在训练循环里分别统计数据加载耗时和模型计算耗时,看谁占大头。
数据加载慢的常见原因有这么几个。磁盘IO是瓶颈:数据文件是大量小文件,读取速度极慢,解决办法是把小文件合并成大文件,或者使用内存映射文件。预处理过重:每个epoch都对原始文本做复杂解析,解决办法是把清洗结果缓存成中间格式,加载时只做轻量转换。num_workers设置不合理:太高或太低都影响性能,我通常从等于CPU核心数开始试,做一次简单的profiling就能确定最佳值。
还有一个细节是shuffle的位置。把shuffle放在预处理之前,会导致每次epoch都要先花时间重新打散原始数据;如果在加载器里用随机采样器则更高效。这种问题不致命,但累积起来会让训练周期慢上不少。
5.4 推理延迟忽高忽低
模型上线后延迟抖动,是部署环节最让人头疼的问题之一。P99延迟飙高但平均延迟看起来正常,这种时候千万不能只盯着平均值看。
抖动最常见的来源是动态批处理参数设置不当。最大等待时间设得太长,会导致某些请求在队列里排队过久,P99延迟恶化。解决办法是缩短等待时间,增加最大batch大小,让批处理更激进地“攒够数量就发车”,而不是“干等时间到”。另一个来源是推理引擎的冷启动或预料之外的小批量请求。比如ONNX Runtime第一次推理时要做算子优化和内存分配,所以服务上线后的前几个请求会特别慢。我的经验是在服务启动后做一次“预热推理”,跑一两个假样本,把一次性开销消化掉,之后的延迟就会稳定下来。
还有一个容易被忽略的原因:模型推理时CPU和GPU之间的数据拷贝太频繁。当输入数据是文本、图像这种非Tensor格式时,每次请求都要做序列化和反序列化,这会增加不小的额外开销。解决办法是尽量复用Tensor缓冲区,减少不必要的内存分配和拷贝。我实测过,仅仅优化这部分,P99延迟就能降低20%以上。
5.5 高频问题排查速查表
| 问题现象 | 可能原因 | 优先排查动作 |
|---|---|---|
| loss震荡不降 | 学习率过大、warmup缺失 | 调小学习率,检查warmup比例 |
| loss下降极慢 | 学习率过小、数据太稀疏 | 适当调大学习率,检查数据质量 |
| 训练中OOM | batch过大、数据一次性载入 | 减小batch,检查数据加载方式 |
| GPU利用率低 | 数据加载慢、num_workers不合理 | profiling加载耗时,调workers数量 |
| 验证集指标虚高 | 数据泄漏 | 检查划分时机和归一化方式 |
| 线上效果与离线不符 | 分布漂移、特征缺失 | 检查线上特征覆盖率,监控类别分布 |
| 推理P99偏高 | 批处理等待过长、冷启动 | 调批处理参数,做服务预热 |
| 量化后效果暴跌 | 量化敏感性高、评估集太小 | 换量化方式,用更大验证集评估 |
6. 从“能跑”到“生产级”的进阶扩展
6.1 生产级系统还需要什么
按上面的路线全部走完,你已经有了一条“从零到能跑”的完整链路:数据有了、模型训了、接口上了、监控挂了。但距离一个真正可靠的生产级系统,通常还差三个层面的东西。
第一是特征与数据治理层。模型用到的大多数特征,在业务系统中可能散落在各个数据源里,需要一套统一的特征存储来管理和复用。离线训练用的特征和线上推理用的特征必须来自同一套逻辑并保持一致,否则就会出现离线好、在线差的老问题。这个一致性问题,在从零搭建时能靠“手动同步”糊弄过去,规模一上来就不能这样干了。
第二是模型与实验平台层。模型的版本管理、灰度发布、AB实验分流,都需要平台化支持。上线一个新模型不能直接把旧的覆盖掉,而是要能一键切换、一键回滚。我见过太多团队因为模型回滚困难,线上出了事故还要紧急改代码,这种体验非常痛苦。提前把模型注册和版本管理机制建好,成本很低,收益极高。
第三是可观测性与告警体系。不仅是CPU、内存这些基础监控,更要关注模型语义层面的健康状况:预测分布漂移、关键字bad case的出现频率、请求内容的异常变化。单靠上午看一次dashboard远远不够,设置合理的告警规则,在问题发生的时候第一时间收到通知,才能避免把小问题拖成大事故。这一步不需要特别重的工具,一套定时任务加告警推送就能起步。
6.2 我的三点实操体会
从零搭建AI工程这条路,我走了三遍了。第一遍用各种现成库拼拼凑凑,勉强把流程跑通,但内部原理一知半解;第二遍开始动手写训练循环和推理服务,遇到各种问题后回头查资料补基础,才算真正入门;第三遍才把数据、模型、部署、监控的耦合关系彻底理清,能做到“换一个业务场景也不慌”。
我的第一点体会是:千万别追求第一次就搭出完美的系统。先用最笨的方式、最简单的方式把全链路跑通,哪怕训练慢、代码丑、接口简陋都行。跑通一次之后,你脑子里有了完整的整体图谱,再回头优化每一层,方向感会完全不同。很多人是在第一步“把链路跑通”就卡住了,因为总觉得自己还没准备好、还要再学点东西才可以动手。实际上,动手跑通本身就是最好的学习方式。
第二点体会是:建立“异常先怀疑基础设施”的排查直觉。大多数问题——训练不稳、推理变慢、指标异常——根源往往不在模型本身,而在数据管线、资源分配、服务框架这些不起眼的地方。先排查基础设施,再怀疑模型逻辑,这条路径能节省大量时间。等你在数据管线和部署细节上栽过几次跟头之后,这种直觉自然就建立了。
第三点体会是:文档记录的习惯越早建立越省心。做一个从零开始的工程,你要做的决策比想象中多得多:为什么选这个优化器、为什么这样划分数据、为什么推理用了ONNX Runtime而不是TensorRT。这些决策当时觉得理所当然,三个月后回头看完全找不到理由。哪怕只是花五分钟在代码仓库的README里写一段“本项目的关键决策和原因”,后续交接和自我回顾都会轻松很多。
踩过几次坑之后,你会慢慢建立起一种感觉——面对一个全新的AI项目,脑子里像有一张地图,上面标着从数据到模型到部署的每一站,以及每一站容易翻车的点。这种感觉最珍贵,它不是任何现成框架能教你的,只能靠你自己一步步走下来。如果你正打算上手这条路线,别犹豫,直接开始,第一版再简陋也没关系,动手跑一次完整的链路,比看十篇教程都管用。