从零搭建AI工程体系:手写训练循环到部署监控全攻略
我刚把一个叫“ai-engineering-from-scratch”的项目完整走了一遍前后花了几个月时间把从理论推导到工程落地的整个链路都跑通了。如果你也想从零搭建一套自己的AI工程体系不依赖现成的黑盒框架而是亲手把每个环节弄明白这篇文章值得你花半个小时看完。我见过太多人学AI是一路“调包”过来的导入transformers调用一行model.generate()任务完成但遇到模型效果差、显存不够、训练速度慢、部署超时这些问题时完全不知道从哪里下手。这个项目就是反着来的——“from scratch”意味着每一层都要自己搭数据集自己处理、模型自己设计、训练循环自己写、部署自己搞定。整个过程会痛苦但痛苦完之后你对AI工程的理解会比绝大多数“调包侠”深刻得多。这篇文章本质上是我的项目总结也是零基础起步的一套完整指南。无论你是准备入门AI方向的学生、想转行的软件工程师还是已经在做算法但缺乏工程化经验的人都应该能从里面找到可以直接照做的方案以及我踩过的坑和摸索出来的经验。1. 整体设计思路拆解为什么“从零开始”比“直接调框架”更有价值1.1 “from scratch”到底在说什么先说个容易误解的点ai-engineering-from-scratch不是说让你从矩阵乘法开始手写所有东西那叫“从零实现深度学习框架”属于另一个更偏研究的项目。这里说的“从零”是指不跳过AI工程链路中的任何关键环节——数据获取、清洗、特征处理、模型构建、训练调试、评估分析、服务化部署、监控迭代——每个环节都要自己动手配一遍、跑一遍、调一遍。举个例子训练一个文本分类模型常规做法是load_dataset一把梭然后用Trainer几行代码搞定。但在from-scratch的思路下你得自己写数据爬取和清洗脚本自己写tokenizer的构建流程自己实现训练循环里的batch采样、梯度累积、学习率调度自己搭建评估指标的计算逻辑最后还要自己写一个带接口的推理服务。步骤多了但对整个系统的理解完全不一样。这个选择背后的逻辑很简单AI工程的瓶颈从来不是“会不会调用某个库”而是“系统出问题时能不能定位”。当你亲手写了每一个模块你就能在模型效果变差时立刻判断是数据问题、特征问题、超参问题还是代码bug而不是对着报错信息干瞪眼。1.2 核心模块划分与路线规划我建议把整个学习路径拆成五个阶段这个划分也是这个项目的骨架基础底座Python编程、线性代数、概率统计、微积分不需要做到数学系水平但梯度、矩阵运算、概率分布这些概念得有直觉。经典机器学习线性回归、逻辑回归、决策树、SVM、K-Means等经典算法的手动实现理解损失函数、优化目标、正则化这些核心机制。深度学习核心从零实现多层感知机MLP、反向传播、卷积神经网络CNN、循环神经网络RNN、注意力机制Attention并在小型数据集上训练出可用模型。工程化能力数据管道搭建、训练加速、模型调优超参搜索、评估与分析、模型部署与监控。综合实战选一个有业务语义的完整项目把前面所有模块串起来完成一个可以演示、可以解释、可以复现的AI应用。这五个阶段不是割裂的越往后越需要回头复习前面的知识。比如第4阶段做超参搜索你会重新理解第2阶段的偏差-方差权衡第5阶段做模型部署你会发现训练时感觉良好的模型在真实数据上会暴露出各种没有考虑到的脏数据问题。1.3 方案选型背后的取舍逻辑很多人在搭建这类项目时会纠结到底应该用PyTorch还是TensorFlow应该先学scikit-learn还是直接上深度学习框架我自己最终的选择是框架用PyTorch生态活跃、调试直观、和学术前沿接轨紧密写自定义训练循环时控制力最强。传统机器学习部分不跳过直接学深度学习容易导致“只会神经网络”的偏科。实际业务中很多问题用XGBoost或逻辑回归就能解决强行上深度模型反而费时费力且难以解释。数据集优先选择公开、小规模、多领域的比如IMDb影评文本分类、Fashion-MNIST图像分类、UCI的Adult收入预测表格数据。数据集太大会导致训练时间过长打击学习积极性太小又缺乏代表性。这个组合方案的优势在于每一步都有清晰的验证反馈每学完一个模块都能立刻看到效果并且不会在一个环节卡太久。比如Fashion-MNIST用CPU训练一个简单CNN几分钟就能看到准确率从随机水平涨到90%以上这种反馈对于保持动力非常重要。2. 核心知识体系搭建从数学直觉到代码实现2.1 数学基础不需要推导一切但必须理解这些数学是AI的底层语言但“从零开始”不意味着要刷完一整本《凸优化》。我实际用到的数学知识集中在三块线性代数向量、矩阵、矩阵乘法、转置、逆矩阵、特征值。最关键的是理解“矩阵乘法就是线性变换的复合”以及“矩阵的形状决定了数据的流向”。概率统计期望、方差、条件概率、贝叶斯公式、极大似然估计。模型训练的本质就是在做参数估计——最小化损失函数等价于在某种分布假设下的极大似然估计。微积分导数、偏导数、链式法则。反向传播算法就是链式法则的工程实现不理解链式法则就无法真正理解为什么梯度能一层层传回去。另外一个经常被忽略但又极其重要的概念是信息论里的交叉熵。分类模型的损失函数为什么用交叉熵而不是均方误差因为交叉熵和概率分布的KL散度直接相关优化它等于让模型预测分布逼近真实分布而且它的梯度形式在softmax下非常漂亮数值上也很稳定。理解这个能帮你避免在后续调模型时走弯路。注意不要死磕数学证明。我见过太多同学在“特征值分解的几何意义”上卡了两个星期然后彻底放弃。你只需要做到看到公式能说出它是什么意思、在代码里对应的模块是什么、改哪个参数会影响它的值。这个程度就够了证明留给做理论的人。2.2 编程基础与工程习惯少踩坑的底层能力AI工程首先是软件工程很多基础能力不过关的人会在这里吃大亏。我建议至少掌握Python高阶特性装饰器写训练钩子必备、生成器和迭代器大数据集分批加载必备、上下文管理器管理显存和资源必备、类型注解多人协作和代码维护必备。NumPy/Pandas的操作熟练度数据清洗阶段80%的活靠这两个库向量化操作写得好不好直接决定数据处理速度是秒级还是分钟级。Debug和性能分析能力会用pdb设置断点会用cProfile定位瓶颈会用torch.utils.tensorboard可视化训练过程。我踩过的一个典型坑是没有从第一天开始用虚拟环境。真实项目跑了两周之后系统里装了各种版本的numpy、torch、cuda工具包互相冲突最后只能全部删掉重来。现在我的习惯是每个项目都建独立的conda或venv环境并且把依赖版本完整记录到requirements.txt确保任何时候都能重建环境。建议学习过程中始终坚持一个好习惯写代码的时候想着“三个月后的我能不能看懂”。命名规范、注释、README、commit message都要认真这在项目后期——尤其是你回看自己一两周前写的代码时——会省下大量本来用于“回忆自己在干嘛”的时间。2.3 机器学习核心算法的“手动实现”路线这个阶段是“from scratch”的精髓所在。我建议不要直接用scikit-learn而是尝试用NumPy手写以下核心算法线性回归用正规方程和梯度下降两种方式实现理解闭式解与迭代优化的区别。逻辑回归实现sigmoid函数、交叉熵损失、梯度下降更新理解为什么分类问题要用逻辑回归而不是线性回归逼近。K-Means聚类实现初始化、分配、更新中心的三步循环体会EM思想。决策树与随机森林实现信息增益计算和树的递归构建理解剪枝的作用。朴素贝叶斯实现条件概率估计和平滑处理理解生成式模型与判别式模型的区别。手写这些算法的过程会让你把“公式”变成“代码”并真正理解模型是在优化什么。比如逻辑回归的梯度公式里为什么特征是X^T * (y_pred - y)只有你自己从损失函数推导到这一步再写成代码你才会彻底明白这是怎么来的。做完这些基础算法后再把它们和scikit-learn的结果对比一下——如果自己实现的准确率在合理范围内说明你的理解基本到位了。3. 深度学习核心从零手写训练循环到完整工程链路3.1 从零实现一个MLP神经网络这里我以图像分类Fashion-MNIST为例展示从零手写一个多层感知机的核心步骤。第一步是构造数据加载器。不用现成的DataLoader自己写一个class SimpleDataLoader: def __init__(self, X, y, batch_size32, shuffleTrue): self.X X self.y y self.batch_size batch_size self.shuffle shuffle self.indices np.arange(len(X)) def __iter__(self): if self.shuffle: np.random.shuffle(self.indices) for start in range(0, len(self.indices), self.batch_size): idx self.indices[start:start self.batch_size] yield self.X[idx], self.y[idx]这个看似简单的类实际上包含了数据加载的三个核心工程设计批处理、乱序、迭代访问。批处理是为了利用矩阵运算的并行性加速训练乱序是为了避免模型学习到样本顺序中的虚假模式迭代访问保证了大数据集不会一次性占满内存。第二步是定义网络结构和前向传播。以一个三层的MLP为例def init_params(dim_in, hidden_sizes, dim_out): # He初始化,避免梯度消失或爆炸 weights [] biases [] sizes [dim_in] hidden_sizes [dim_out] for i in range(len(sizes) - 1): w np.random.randn(sizes[i], sizes[i1]) * np.sqrt(2.0 / sizes[i]) b np.zeros((1, sizes[i1])) weights.append(w) biases.append(b) return weights, biases def relu(x): return np.maximum(0, x) def softmax(x): exp_x np.exp(x - x.max(axis1, keepdimsTrue)) # 数值稳定性处理 return exp_x / exp_x.sum(axis1, keepdimsTrue) def forward(X, weights, biases): cache [X] a X for i in range(len(weights) - 1): z a weights[i] biases[i] a relu(z) cache.append(a) # 最后一层不加ReLU,直接softmax z a weights[-1] biases[-1] cache.append(z) return z, cache这里的softmax里减最大值是经典的数值稳定性技巧指数函数在输入为较大正数时容易溢出为inf每行减去该行最大值后再取指数数值范围可控且softmax结果不变。第三步是反向传播。这是整个MLP中最考验理解的部分用链式法则从输出层往前逐层计算梯度def backward(y, probs, weights, cache): m y.shape[0] grads_w [] grads_b [] # 输出层梯度: softmax cross entropy 的梯度简化形式 dz probs.copy() dz[range(m), y] - 1 dz / m for i in range(len(weights) - 1, -1, -1): a_prev cache[i] # 当前层的输入(上一层的输出) dw a_prev.T dz db dz.sum(axis0, keepdimsTrue) grads_w.insert(0, dw) grads_b.insert(0, db) if i 0: da dz weights[i].T # 反向传播经过ReLU dz da * (cache[i] 0) return grads_w, grads_b注意到这里实现了交叉熵损失与softmax组合的梯度简化当输出层用softmax且损失用交叉熵时梯度恰好是probs - one_hot(y)这个差值。这是我见过最漂亮的数学简化之一也是理解神经网络训练的关键节点——如果你手写过这段代码后面看任何深度学习框架的源码都会有一种“原来如此”的豁然开朗。3.2 训练循环实现学习率、Batch Size与梯度下降有了前向和反向训练循环本身并不复杂但几个超参数的选择直接决定了模型能不能收敛、收敛多快、效果多好。def train(model, dataloader, epochs20, lr0.1): weights, biases model for epoch in range(epochs): total_loss 0 total_correct 0 total_samples 0 for X_batch, y_batch in dataloader: # 前向 logits, cache forward(X_batch, weights, biases) # 计算损失 probs softmax(logits) loss cross_entropy(probs, y_batch) # 反向传播 grads_w, grads_b backward(y_batch, probs, weights, cache) # 参数更新 for i in range(len(weights)): weights[i] - lr * grads_w[i] biases[i] - lr * grads_b[i] total_loss loss total_correct (probs.argmax(axis1) y_batch).sum() total_samples y_batch.shape[0] acc total_correct / total_samples print(fEpoch {epoch1}/{epochs}, Loss: {total_loss/len(dataloader):.4f}, Acc: {acc:.4f})这里特别值得注意的几点学习率是初学者爱踩的第一个坑。太大损失会震荡甚至发散太小则收敛极慢。我自己的经验是一开始可以用0.1附近的对数量级试探观察损失曲线。如果损失在几个epoch后还涨就降为0.01如果损失下降非常慢就升为0.1。这个试错法比盲猜高效得多。Batch Size的影响容易被低估。小batch如32的梯度噪声大但能起到正则化的作用训练时鲁棒性更好大batch如256梯度稳定但可能陷入尖锐极小值泛化能力反而下降。实践中建议在32-128之间尝试。ReLU的死亡神经元问题是手写MLP时的经典bug如果初始化不当或学习率过大某个神经元的输入恒为负ReLU输出恒为0梯度传不回去这个神经元就“死”了。检查方式很简单训练时打印每层的cache[i]均值如果某个隐藏层的激活值大量为0就要调整初始化方式或降低学习率。从零手写训练循环的价值在于你亲眼看着loss下降、准确率上升每一步都有迹可循。这样后面用现成框架时你才知道那些高层封装正在替你做什么。3.3 CNN与RNN的关键理解为什么需要它们MLP能解决简单分类问题但处理图像和序列数据时力不从心。原因在于CNN解决的是“空间局部性”问题。一张图片的像素有很强的局部相关性——相邻像素往往属于同一物体。MLP把每个像素当作独立特征完全不考虑这种空间结构参数量也爆炸一张256x256的RGB图像输入MLP第一层就有256x256x3196608个输入随便一个隐层就是百万级参数。而CNN通过局部连接和权值共享把参数量降了几个数量级同时天然具备平移等变性。用生活类比来说MLP像是一个必须同时看到整张脸才能认出眼睛在哪的笨方法CNN则像拿着小放大镜在图片上滑动每个局部区域用一个相同的模板去匹配特征。RNN/Transformer解决的是“时序依赖”问题。文本、语音、时间序列数据中前后元素之间存在顺序关系。MLP和CNN默认输入是独立的无法建模这种依赖。RNN通过循环连接把上一步的隐状态传给下一步让网络“记住”过去的信息。虽然在长文本任务上Transformer已经成为主流但我依然建议手写一个简单的RNN或LSTM——因为在手写的过程中你会彻底理解“隐状态”是怎么在时间步之间流动的这对理解Transformer里的QKV机制大有帮助。3.4 从手动实现切换到PyTorch的正确姿势完成手写MLP之后可以切换回PyTorch了。有些人会觉得“既然手写过了直接用PyTorch是不是多此一举”完全不会。切换的意义在于利用成熟的自动求导机制和GPU加速能力把精力从“实现数学公式”转移到“设计模型结构和调试训练过程”上。两者是互补的关系。建议以完全相同的MLP分类任务作为过渡先写出在PyTorch中的等价实现import torch import torch.nn as nn class MLP(nn.Module): def __init__(self, dim_in, hidden_sizes, dim_out): super().__init__() layers [] sizes [dim_in] hidden_sizes [dim_out] for i in range(len(sizes) - 1): layers.append(nn.Linear(sizes[i], sizes[i1])) if i len(sizes) - 2: layers.append(nn.ReLU()) self.net nn.Sequential(*layers) def forward(self, x): return self.net(x) model MLP(784, [256, 128], 10) criterion nn.CrossEntropyLoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3)你会发现框架帮你做掉了70%的样板代码但模型的骨架和之前手写的完全对应。此时用PyTorch自带的数据加载器、可视化工具、Checkpoint保存、分布式训练支持就是真正开始做“AI工程”了而不是做“算法玩具”。4. 数据管道、训练优化与模型部署工程化的关键一环4.1 数据管道搭建训练是“数据一脚踢进去”就完事吗很多人训练模型时直接把所有数据读进内存train_test_split一把梭训练完发现评估指标不错一上线就崩。真实业务场景里数据管道要考虑的问题远不止这些。我自己摸索下来的数据管道标准流程是可视化统计先行先不看具体样本先画分布图、统计缺失值比例、看类型分布对数据有一个整体直觉。清洗规则化把去重、去空、格式统一、异常值处理写成可复用的规则函数而不是临时改脚本。切分数据时注意分布一致性分类任务要保证训练集和测试集的类别分布大致一致时间序列任务要按时间顺序切分不能随机切分否则会因为“未来信息泄漏”得到虚高分数。特征工程显式化对于表格数据数值特征做标准化、类别特征做编码的过程要有迹可循并把这个预处理流程保存下来推理阶段要复用同一套流程。数据增强对于图像任务随机裁剪、翻转、色彩抖动等手段能大幅提升模型的泛化能力对于文本可以在同义词替换、随机删除等方向做一些尝试。我踩过的最大一个坑是训练/测试数据分布不一致。案例是我做了一个新闻分类模型从公开数据集上训练时F1高达0.93但接到真实新闻流后准确率掉到0.7以下。后来排查发现公开数据集的文本经过编辑清洗用词规范和长度分布与实际新闻流完全不一样。解决方法是在数据管道中加入了“领域适配校验”——通过对比训练集和真实样本的词频分布、句子长度分布提前发现问题。4.2 训练优化不只是调参还有这些加速手段当你从“单机单卡小模型”走向“真实规模项目”时训练速度就会成为一个不得不考虑的问题。我总结了几种性价比最高的优化手段混合精度训练用FP16代替FP32存储梯度和中间激活值显存占用几乎减半训练速度在支持的GPU上能快2-3倍。PyTorch里用torch.cuda.amp.autocast()加GradScaler()就能实现代价是写训练循环时多几行代码。梯度累积如果你的GPU显存不够跑大batch比如目标batch size为256但显存只能跑64可以每跑4个batch累积一次梯度再更新效果基本等价于直接用大batch训练。这个技巧在微调大模型时几乎天天用到。数据预读取用DataLoader的num_workers参数把数据读取放到子进程并行执行并用pin_memoryTrue加速数据从CPU到GPU的传输。很多GPU利用率上不去的问题根源就在数据加载太慢——GPU在空转等数据。学习率调度不要一直用固定学习率。常用的schedule如cosine decay或step decay可以在训练后期让loss降到更低。一个简单实用的经验是先通过几次小规模训练找到能让loss快速下降的学习率再用带warmup和decay的调度器在这个学习率附近精细调整。另外要说一个容易忽视的点模型参数量计算和显存预算。以下面的公式大致估算模型参数显存 ≈ 参数数量 × 4字节FP32 训练时的显存占用 ≈ 参数显存 × (参数本身 梯度 优化器状态 中间激活值)Adam优化器每个参数需要额外的两个状态变量一阶矩和二阶矩所以实际训练占用大约是参数量的12-16倍FP32。理解这个能帮你提前判断“这个模型在8G显存上能不能跑”不用等到跑的时候才被OOM打脸。4.3 评估分析准确率不是唯一标准训练完成后的评估环节同样是工程的一部分。很多新手一上来只看准确率Accuracy但实际业务里这个数字往往有很强的误导性。一个比较经典的例子是欺诈检测场景99%的样本都是正常交易如果模型一直预测“正常”准确率就是99%但显然没有任何价值。这种类别不平衡场景应改用精确率Precision、召回率Recall和F1分数作为主要指标。精确率回答的问题是“你预测为欺诈的那些交易里有多少真是欺诈”召回率回答的是“所有真实欺诈交易里你抓住了多少”。这两种指标是“此消彼长”的关系——往往是你越激进地标注“欺诈”召回率越高但精确率就会下降。选择调哪一头取决于业务的成本结构把正常交易误判为欺诈用户会投诉成本高放过欺诈交易公司直接损失钱。这两者的权衡才是评估设置背后的真正逻辑。除了这些常规指标我还强烈建议做两件容易被忽略的事错误样本分析和预测置信度分布分析。把预测错得最离谱的样本挑出来逐个查看原因是标注错误、样本本身模糊、还是模型系统性偏差置信度分析则能帮你发现模型有没有“过度自信”——比如预测概率高达0.95但其实是错的这种样本一旦上线会造成严重的信任问题。4.4 模型部署与监控从训练到线上服务的最后一公里模型部署是这个项目里工程含量最高的部分。我分享一个比较实用的方案栈训练好的PyTorch模型导出为TorchScript或ONNX格式用FastAPI封装推理接口再用Docker容器化部署。一个标准的FastAPI推理服务示例from fastapi import FastAPI from pydantic import BaseModel import torch app FastAPI() model torch.jit.load(model_scripted.pt) model.eval() class TextInput(BaseModel): text: str def preprocess(text: str): # 这里要与训练时的预处理完全一致 tokens tokenize(text) ids vocab.encode(tokens) return torch.tensor([ids]) app.post(/predict) def predict(input: TextInput): input_ids preprocess(input.text) with torch.no_grad(): logits model(input_ids) prob torch.softmax(logits, dim-1).tolist()[0] return {probabilities: prob, prediction: int(torch.argmax(logits))}部署阶段最贵的一课是预处理一致性。训练时你做了清洗、编码、归一化推理时也必须分毫不差地做同样的处理。比如训练时文本先做了小写化、去掉了URL推理时如果没有执行同样操作输入分布和你训练时看到的不一样模型效果就会莫名其妙变差。解决方法是把预处理逻辑封装成同一个函数或类在训练脚本和推理服务里共用。上线之后的监控是整个链路中最容易被忽视的环节。至少要盯几个指标请求量、响应延迟、预测置信度分布、以及预测类别的分布。如果发现线上预测分布和训练时差异越来越大大概率是数据漂移——真实世界的输入已经不再是模型当初学习的分布了。这时候就需要周期性用新数据重新训练或微调模型。我建议在入门阶段至少做到“日志留痕分布对比”这两件事再逐步升级到完整的MLOps流程。5. 实操过程全程记录与常见问题速查5.1 一个完整小项目的实操复盘我把整个项目最终的实践任务定为“IMDb影评情感二分类”目标是构建一个从数据清洗到模型部署的完整链路。整体过程如下阶段一数据准备约1天下载IMDb数据集做文本清洗去HTML标签、去特殊符号、统一小写、构建词汇表、进行序列编码和padding。这一步让我彻底理解了为什么NLP模型需要tokenizer和padding——不同长度的文本不能直接堆成tensor需要统一到同一长度后做mask。阶段二构建模型约2天先用自己写的词向量平均模型把一句话的词向量取平均后过一层线性分类器跑出0.86的准确率。这个简单模型的优势是训练极快且解释性强适合作为baseline。随后构建了一个基于LSTM的模型准确率提升到0.89但训练时间显著增加。阶段三工程化改造约3天加入混合精度训练在GPU上从十几分钟缩短到几分钟、checkpoint断点续训、tensorboard可视化训练曲线、超参数搜索对学习率、隐藏层维度、dropout做了几组对比实验。阶段四部署上线约1天导出TorchScript格式用FastAPI封装接口Docker容器化在本地跑通完整的请求-预测-响应链路。阶段五结果分析与迭代约1天对测试集做了错误样本分析发现模型容易把带有“not good”这类双重否定表达的电影评论判错。改进方式是增加词向量训练语料规模并调整了训练时的样本权重。整个流程下来大约一周时间和那些“用Transformers几行代码跑完”的demo相比慢了很多但每一步我都知道自己做了什么以及为什么效果变好或变差。这种感觉是直接用框架无法获得的。5.2 高频问题排查汇总我把整个项目实施期间遇到的高频问题整理成了一张速查表这些问题几乎99%的人都会遇到至少一两个问题现象常见原因排查与解决训练loss为NaN学习率过大数据中存在NaN梯度爆炸先降低学习率检查数据是否有空值和异常大值尝试梯度裁剪模型不收敛学习率过小/过大特征未标准化网络初始化不当画出loss曲线判断是震荡还是平稳检查数据预处理改用合适的初始化方法过拟合训练准高、测试差模型容量太大训练数据不足缺乏正则化增加Dropout数据增强增大训练数据用早停法训练和测试预处理不一致测试脚本未复用训练时的预处理函数把预处理封装为同一函数训练和推理共用GPU显存OOMbatch太大模型太大存在累积的中间变量减小batch开启梯度累积检查是否有变量没有detach()预测全是同一个类别数据类别极度不平衡模型学到偏置检查类别分布使用加权损失或过采样/欠采样加载模型时shape不匹配训练和推理时输入维度不一致检查预处理是否是同一个流程检查embedding维度是否一致5.3 避坑经验与个人体会最后重点讲几个我在实际项目中踩过的比较深的坑。第一个坑是环境管理混乱。我在项目初期没有严格管理包版本导致一次升级numpy后所有旧脚本全部崩溃最后花了一整天回滚环境。后来我养成了每个项目固定版本快照的习惯把关键依赖精确到小版本号写进requirements.txt并且每次改动依赖都顺手更新。第二个坑是过早复杂化。我曾经在一个简单分类任务上直接上了BERT和分布式训练结果因为基础设施不熟悉花了两周还没跑通后来换成简单的LSTM模型半天搞定效果还更好。经验是永远先用简单方案做到全链路通再考虑用复杂方案优化效果。这条规则后来帮我节省了大量的时间。第三个坑是忽略推理性能。训练时模型跑得慢可以忍但线上推理响应速度直接决定用户体验。我第一次部署模型时没有做任何优化单个请求耗时300毫秒接口压测直接超时。后来通过模型量化从FP32到INT8、batch推理和请求缓存三个手段把延迟降到了50毫秒以下。第四个坑是数据泄漏。有次做特征工程我把整个数据集做了标准化后再切分训练测试集测试集的信息提前混进了训练过程结果测试指标虚高。正确做法是先只对训练集fit标准化器再用同一个标准化器transform测试集。这类问题非常隐蔽一旦发生整个评估结果都不可信。就我个人体会而言这个“从零开始”的项目真正的价值不在于你最终学会了多少行代码而在于你建立了一个“输入-过程-输出”全程可解释的心智模型。遇到问题时你能一层层定位而不是在大海捞针。如果你正在犹豫要不要走这条路我的建议非常明确花一个月的时间老老实实把核心环节都手写一遍、跑一遍这笔投入带来的回报是任何速成课程都替代不了的。后续你完全可以再往多模态、大模型微调、分布式训练这些方向扩展有了这个地基上面盖多高的楼都不会心虚。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →