1. 从零搭建AI工程能力:为什么我劝你别一上来就啃论文
这两年“AI工程”这个词被炒得火热,招聘网站上挂着“AI工程师”的岗位薪资一个比一个高,培训班广告铺天盖地,仿佛只要学了几个框架调用、跑通了几个Demo,就能顺利上岸。但我带了这么多届新人、也面试过不少号称“有AI项目经验”的候选人之后,发现一个很尴尬的现实:大部分人所谓的AI工程能力,其实只是“会调库”。真正让他从零搭一条能跑通、能上线、能维护的AI流水线,立刻就露馅了。
ai-engineering-from-scratch这个标题,我第一次看到的时候就觉得它戳中了痛点。它讲的不是某个具体框架怎么用,也不是某个模型怎么调参,而是一个更底层的问题:如果把你扔到一个空白环境里,不给你现成的脚手架,你该怎么一步步把AI工程能力搭起来?这个问题听起来很虚,但实际上它决定了你到底是“会用工具的人”还是“能造工具的人”。
我写这篇东西,就是想把我自己从零搭建AI工程体系的过程完整拆一遍。从环境准备、数据管道、模型训练、评估体系、部署上线到监控迭代,每一步我都会讲清楚我为什么这么选、踩过哪些坑、有哪些看起来不起眼但特别关键的细节。适合谁看?如果你是刚入行一两年、想从“调包侠”往“工程化”方向走的开发者,或者你是一个小团队的技术负责人、需要从零搭一套AI能力但不知道从哪下手,那这篇内容应该能帮你省下不少试错时间。如果你已经是资深从业者,也欢迎看看我的思路有没有可以借鉴或者吐槽的地方。
2. 整体设计思路:为什么我不建议从模型开始搭
2.1 先想清楚“AI工程”到底在工程什么
很多人一提到AI工程,脑子里第一反应就是模型。选什么架构、用什么预训练权重、学习率怎么设、batch size调多大。这些重要吗?重要。但它们只是整个AI工程体系里的一环,而且往往不是最先要解决的那一环。
我自己的理解是,AI工程的核心不是“训练出一个好模型”,而是“让模型的能力稳定、可重复、可度量地产生业务价值”。这句话听起来有点绕,我拆开说。稳定,意味着你的系统不能今天跑通明天崩;可重复,意味着换一个人、换一台机器,同样的输入能得到同样的输出;可度量,意味着你知道当前系统到底行不行、哪里不行、改进了多少;产生业务价值,意味着你做的这些东西最终要能解决实际问题,而不是在notebook里自嗨。
所以从零搭建AI工程能力,我的建议顺序是:先搭数据管道和评估体系,再搭训练流程,最后搭部署和监控。这个顺序和很多教程是反着来的,大部分教程一上来就教你搭模型、跑训练,数据随便找个现成数据集,评估就看准确率。但实际工作中,数据管道和评估体系才是决定你项目能不能活下去的关键。
2.2 为什么数据管道要放在第一位
我踩过最大的坑,就是早期做项目的时候不重视数据管道,觉得“数据嘛,读进来就行了”。结果做到后面发现,数据格式不统一、标注质量参差不齐、训练集和测试集有泄漏、增量数据没法平滑接入。每次想重新训练模型,都要花大量时间在数据清洗上,而且每次清洗的逻辑还不一样,导致实验结果根本没法对比。
后来我学乖了,不管项目多小,第一步一定是把数据管道搭起来。所谓数据管道,至少包含这几个环节:数据采集、数据清洗、数据标注、数据版本管理、数据切分。每个环节都要有明确的输入输出格式和可重复执行的脚本。这样做的代价是前期会慢一些,但后期迭代速度会快很多。你可以把数据管道想象成厨房里的备菜区,菜洗好切好分装好,炒菜的时候直接拿就行。如果每次炒菜都要现洗现切,那出菜速度永远上不去。
2.3 评估体系为什么比模型本身更重要
另一个我花了很久才想明白的事情是:评估体系比模型本身更重要。原因很简单,没有可靠的评估,你根本不知道模型改进了没有。我见过太多团队,模型换了一版又一版,每次都说“感觉好了一些”,但到底好了多少、在哪些场景下好了、有没有变差的场景,谁也说不清楚。
一个完整的评估体系应该包含:离线评估指标、在线评估指标、人工评估流程、bad case分析机制。离线指标用来快速筛选模型版本,在线指标用来验证真实效果,人工评估用来发现指标覆盖不到的问题,bad case分析用来指导下一步优化方向。这四者缺一不可。而且评估体系要尽可能自动化,每次训练完自动跑评估、自动生成报告、自动和基线对比。这样才能形成“训练-评估-分析-优化”的闭环。
2.4 训练流程和部署监控的定位
训练流程当然重要,但它的定位应该是“在数据和评估都准备好的前提下,快速实验和迭代”。如果你数据管道没搭好、评估体系没建起来,训练流程做得再花哨也是白搭。我一般会把训练流程做成配置化的,把模型结构、超参数、数据版本、训练策略都抽成配置文件,这样换实验的时候只需要改配置,不用改代码。
部署和监控是最后一环,但也是最容易被忽视的一环。很多团队模型训练完就扔给工程团队去部署,结果发现推理延迟太高、显存不够、并发上不去、线上效果和离线差很多。这些问题其实在训练阶段就应该考虑,比如模型大小、推理框架选择、量化策略、服务架构。监控更是如此,上线不是终点,而是起点。你需要监控推理延迟、吞吐量、错误率、输入分布漂移、输出分布漂移等等。没有监控的AI系统就像没有仪表盘的汽车,你根本不知道它什么时候会出问题。
3. 核心细节解析:从零搭建的五个关键环节
3.1 环境与工具链:别小看这一步
从零搭建AI工程能力,第一步是环境准备。这件事听起来简单,但我见过太多人在这上面翻车。最常见的问题是:本地环境能跑,换台机器就跑不了;今天能跑,明天更新了个包就跑不了。根本原因是没有做环境隔离和依赖锁定。
我的做法是,每个项目都用独立的虚拟环境,Python项目用venv或者conda都行,关键是所有依赖必须写进requirements.txt或者environment.yml,并且锁定版本号。不要写numpy>=1.20这种,要写numpy==1.24.3。另外,CUDA版本、cuDNN版本、PyTorch版本之间的兼容性一定要查清楚,这三个东西版本不匹配是新手最常见的坑。
# 创建虚拟环境 python -m venv ai-env source ai-env/bin/activate # 安装依赖并锁定版本 pip install torch==2.1.0 torchvision==0.16.0 pip freeze > requirements.txt除了Python环境,我还建议把Docker用起来。Docker的好处是,你可以把整个运行环境打包成一个镜像,换任何机器都能一键跑起来。对于AI项目来说,Docker还有一个额外的好处:你可以把训练环境和推理环境分开,训练环境装训练需要的包,推理环境只装推理需要的包,这样推理镜像可以做得非常小,部署起来也快。
注意:Docker镜像里不要放数据和模型权重,这些应该通过挂载卷或者对象存储来管理。镜像只放代码和依赖,保持镜像轻量。
工具链方面,我常用的组合是:Git做代码版本管理,DVC做数据和模型版本管理,MLflow做实验跟踪,Hydra做配置管理,Weights & Biases或者TensorBoard做训练可视化。这些工具不是必须全用,但至少要有代码版本管理和实验跟踪,否则你很快会陷入“这个结果是用哪版代码哪个参数跑出来的”这种混乱中。
3.2 数据管道搭建:从原始数据到训练样本
数据管道是整个AI工程体系的地基。我一般会把数据管道分成四层:原始数据层、清洗数据层、标注数据层、训练样本层。每一层都有明确的存储位置和格式规范。
原始数据层存放从各种来源采集到的原始数据,不做任何修改。这一层的原则是“只增不改”,所有原始数据都保留,方便追溯。清洗数据层存放经过格式统一、去重、去噪之后的数据。标注数据层存放经过人工标注或者自动标注之后的数据。训练样本层存放最终用于训练的样本,包括输入和标签。
# 数据清洗示例:统一文本格式 import re import hashlib def clean_text(text): # 去除多余空白 text = re.sub(r'\s+', ' ', text).strip() # 去除特殊字符 text = re.sub(r'[^\w\s\u4e00-\u9fff.,!?;:]', '', text) return text def deduplicate(records): seen = set() unique = [] for r in records: key = hashlib.md5(r['text'].encode()).hexdigest() if key not in seen: seen.add(key) unique.append(r) return unique数据版本管理是很多人忽视的一环。我的做法是,每次数据管道跑完,给输出数据打一个版本号,版本号包含时间戳和配置哈希。这样每次训练的时候,只要记录数据版本号,就能追溯到用的是哪版数据。DVC可以很好地做这件事,它会把大文件存在远程存储里,Git里只存元数据指针。
数据切分也有讲究。训练集、验证集、测试集的切分不能随机切,要根据业务场景来。比如做时间序列预测,就要按时间切分,不能用未来的数据预测过去。做用户行为预测,就要按用户切分,同一个用户的数据不能同时出现在训练集和测试集里。这些细节如果搞错了,离线指标会虚高,上线之后效果会大打折扣。
3.3 模型训练与实验管理:让每次实验都可追溯
模型训练这一块,我的核心原则是“配置化、可追溯、可复现”。配置化意味着模型结构、超参数、数据版本、训练策略都写在配置文件里,代码只负责执行。可追溯意味着每次实验都有唯一的ID,记录所有配置和结果。可复现意味着给定相同的配置和数据,能跑出相同的结果。
# config/train_config.yaml model: name: bert-base-chinese num_labels: 10 dropout: 0.1 data: train_path: data/v1/train.jsonl val_path: data/v1/val.jsonl max_length: 128 training: batch_size: 32 learning_rate: 2e-5 epochs: 5 warmup_ratio: 0.1 seed: 42实验管理我推荐用MLflow或者Weights & Biases。每次训练自动记录配置、指标、模型文件、日志。这样你可以在网页上直观地对比不同实验的结果,不用再手动整理Excel表格。我自己的习惯是,每次实验都写一段简短的备注,说明这次实验改了什么、预期是什么、结果是否符合预期。这个习惯看起来麻烦,但当你跑了上百次实验之后,没有备注你根本记不住每次改了什么。
提示:随机种子一定要固定,并且记录下来。我遇到过好几次实验结果无法复现的情况,最后发现是某个库的随机种子没固定。PyTorch、NumPy、Python内置random都要固定。
训练过程中还有一个容易被忽视的点:检查点管理。不要只保存最后一个epoch的模型,要保存验证集指标最好的那个检查点。同时,检查点文件要包含模型权重、优化器状态、epoch数、最佳指标值,这样断点续训的时候才能完整恢复。
3.4 评估体系搭建:别让模型自欺欺人
评估体系是我认为整个AI工程中最重要、也最容易被做烂的部分。很多团队的评估就是跑一下测试集,看准确率、F1值,然后就没有然后了。这种评估方式的问题在于:它只能告诉你模型在测试集上表现如何,不能告诉你模型在真实场景中表现如何,也不能告诉你模型为什么犯错。
我的评估体系包含四个层次。第一层是离线指标,包括准确率、召回率、F1值、AUC等常规指标,以及分场景、分品类的细分指标。第二层是在线指标,包括点击率、转化率、用户停留时长等业务指标。第三层是人工评估,定期抽样让标注人员评估模型输出质量。第四层是bad case分析,把模型犯错的案例收集起来,归类分析,找出系统性问题和优化方向。
# 评估报告生成示例 def generate_eval_report(predictions, labels, categories): report = {} report['overall'] = compute_metrics(predictions, labels) report['per_category'] = {} for cat in set(categories): mask = [c == cat for c in categories] cat_preds = [p for p, m in zip(predictions, mask) if m] cat_labels = [l for l, m in zip(labels, mask) if m] report['per_category'][cat] = compute_metrics(cat_preds, cat_labels) return report评估体系要尽可能自动化。每次训练完自动跑评估、自动生成报告、自动和基线对比、自动发送通知。这样才能形成闭环。另外,评估集要定期更新,不能一直用同一个测试集。因为模型可能会在测试集上过拟合,或者测试集的分布和真实场景逐渐偏离。
3.5 部署与监控:上线只是开始
模型部署这一块,我的建议是尽量简单。不要一上来就搞复杂的微服务架构,先用最简单的方式把模型跑起来。比如用FastAPI包一个HTTP接口,用Gunicorn做进程管理,用Nginx做反向代理。等流量上来了再考虑更复杂的方案。
# FastAPI推理服务示例 from fastapi import FastAPI from pydantic import BaseModel import torch app = FastAPI() model = torch.load('model.pt') model.eval() class Request(BaseModel): text: str class Response(BaseModel): label: str confidence: float @app.post('/predict', response_model=Response) def predict(req: Request): with torch.no_grad(): inputs = tokenizer(req.text, return_tensors='pt') outputs = model(**inputs) probs = torch.softmax(outputs.logits, dim=-1) conf, pred = torch.max(probs, dim=-1) return Response(label=id2label[pred.item()], confidence=conf.item())监控是部署之后最重要的事情。我一般会监控这几类指标:系统指标(CPU、内存、GPU、延迟、吞吐量)、业务指标(请求量、成功率、错误率)、模型指标(输入分布、输出分布、置信度分布)。输入分布和输出分布的变化特别重要,如果线上输入分布和训练分布偏离太大,模型效果会急剧下降,这就是所谓的“数据漂移”。
注意:监控要设置告警阈值,但不能太敏感。我见过有的团队告警设置得太频繁,每天几百条告警,最后大家都麻木了,真正的故障反而没人处理。告警要分级,P0级故障打电话,P1级发消息,P2级记工单。
4. 实操过程:从零到一搭建一个文本分类系统
4.1 项目初始化与目录结构
说了这么多理论,接下来我以一个实际的文本分类项目为例,完整走一遍从零搭建的过程。这个项目要做的是新闻分类,输入一段新闻文本,输出它属于哪个类别(比如体育、财经、科技、娱乐等)。
首先初始化项目目录。我的习惯是目录结构要清晰,让人一眼就能看出每个文件夹是干什么的。
mkdir ai-engineering-from-scratch cd ai-engineering-from-scratch mkdir -p data/{raw,processed,labeled,splits} mkdir -p src/{data,models,training,evaluation,serving} mkdir -p configs experiments notebooks tests mkdir -p docker scripts这个目录结构里,data存放各层数据,src存放源代码,configs存放配置文件,experiments存放实验记录,notebooks存放探索性分析,tests存放单元测试,docker存放Dockerfile,scripts存放各种脚本。
4.2 数据采集与清洗实操
数据采集这一步,我用的是一个公开的新闻数据集,大概有十万条新闻,包含标题、正文和类别标签。原始数据是JSON格式,但字段名不统一,有些是中文有些是英文,还有一些缺失值。
# src/data/collect.py import json import os def load_raw_data(raw_dir): records = [] for fname in os.listdir(raw_dir): if fname.endswith('.json'): with open(os.path.join(raw_dir, fname), 'r', encoding='utf-8') as f: data = json.load(f) for item in data: records.append({ 'title': item.get('title') or item.get('标题', ''), 'content': item.get('content') or item.get('正文', ''), 'label': item.get('category') or item.get('类别', '') }) return records清洗的时候,我做了几件事:去除HTML标签、去除多余空白、过滤掉正文长度小于50的样本、过滤掉标签为空的样本、去重。去重我用的是标题加正文前100个字符的MD5值作为指纹。
# src/data/clean.py import re import hashlib def clean_record(record): record['title'] = re.sub(r'<[^>]+>', '', record['title']) record['content'] = re.sub(r'<[^>]+>', '', record['content']) record['title'] = re.sub(r'\s+', ' ', record['title']).strip() record['content'] = re.sub(r'\s+', ' ', record['content']).strip() return record def filter_record(record): if len(record['content']) < 50: return False if not record['label']: return False return True def get_fingerprint(record): text = record['title'] + record['content'][:100] return hashlib.md5(text.encode()).hexdigest()清洗完之后,我把数据按8:1:1切分成训练集、验证集、测试集。切分的时候用了分层抽样,保证每个类别的比例在三个集合中一致。
4.3 模型训练实操与参数选择
模型我选的是BERT-base-chinese,这是一个中文预训练模型,在文本分类任务上表现稳定。为什么选BERT而不是其他模型?因为BERT的生态最成熟,文档多、社区活跃、踩坑的人多,遇到问题容易找到解决方案。对于从零搭建的项目来说,成熟稳定比先进重要。
训练参数方面,学习率我设的是2e-5,这是BERT微调的经典学习率。batch size设的是32,因为我的显存只有16G,再大就OOM了。epochs设的是5,因为我在验证集上观察到第3个epoch之后指标就基本不涨了,再训练容易过拟合。warmup比例设的是0.1,这是经验值,能让训练初期更稳定。
# src/training/train.py import torch from transformers import BertTokenizer, BertForSequenceClassification from transformers import Trainer, TrainingArguments def train(config): tokenizer = BertTokenizer.from_pretrained(config['model']['name']) model = BertForSequenceClassification.from_pretrained( config['model']['name'], num_labels=config['model']['num_labels'] ) train_dataset = load_dataset(config['data']['train_path'], tokenizer) val_dataset = load_dataset(config['data']['val_path'], tokenizer) training_args = TrainingArguments( output_dir='experiments/exp001', num_train_epochs=config['training']['epochs'], per_device_train_batch_size=config['training']['batch_size'], learning_rate=config['training']['learning_rate'], warmup_ratio=config['training']['warmup_ratio'], seed=config['training']['seed'], evaluation_strategy='epoch', save_strategy='epoch', load_best_model_at_end=True, metric_for_best_model='f1' ) trainer = Trainer( model=model, args=training_args, train_dataset=train_dataset, eval_dataset=val_dataset, compute_metrics=compute_metrics ) trainer.train() trainer.save_model('experiments/exp001/best_model')训练过程中我用了MLflow做实验跟踪,每次训练自动记录配置、指标、模型文件。这样我可以在MLflow的界面上直观地对比不同实验的结果。
4.4 评估与bad case分析实操
训练完之后,我在测试集上跑评估,生成了详细的评估报告。整体F1值是0.89,看起来还不错。但分品类看,体育类的F1是0.94,财经类是0.91,科技类是0.87,娱乐类是0.84。娱乐类明显偏低。
# src/evaluation/evaluate.py from sklearn.metrics import classification_report, confusion_matrix def evaluate(model, test_dataset, id2label): predictions = [] labels = [] for batch in test_dataset: with torch.no_grad(): outputs = model(**batch) preds = torch.argmax(outputs.logits, dim=-1) predictions.extend(preds.cpu().numpy()) labels.extend(batch['labels'].cpu().numpy()) report = classification_report( labels, predictions, target_names=[id2label[i] for i in range(len(id2label))], output_dict=True ) return report, predictions, labels然后我做了bad case分析,把娱乐类预测错误的样本抽出来看。发现大部分错误是把娱乐新闻预测成了科技新闻,因为很多娱乐新闻涉及短视频平台、直播、网红经济,这些内容和科技新闻有重叠。这是一个典型的类别边界模糊问题,不是模型能力问题,而是标注标准问题。解决方法是重新定义类别边界,或者增加一个“泛娱乐科技”的类别。
4.5 部署上线与监控配置
模型评估通过之后,我用FastAPI包了一个推理服务,用Docker打包,部署到了一台测试服务器上。推理服务支持批量预测和单条预测,单条预测的P99延迟在50ms以内,满足业务需求。
# docker/Dockerfile FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY src/serving/ ./serving/ COPY experiments/exp001/best_model/ ./model/ EXPOSE 8000 CMD ["uvicorn", "serving.main:app", "--host", "0.0.0.0", "--port", "8000"]监控方面,我用Prometheus采集指标,用Grafana做可视化。监控的指标包括:请求量、延迟分布、错误率、输入文本长度分布、输出类别分布、置信度分布。设置了两条告警规则:错误率超过5%告警,输入文本长度分布偏移超过阈值告警。
5. 常见问题与排查技巧实录
5.1 训练不收敛或loss震荡怎么办
这是新手最常见的问题。我遇到过的原因大概有这么几类:学习率太大、batch size太小、数据没有shuffle、标签有问题、模型初始化有问题。排查顺序建议从学习率开始,先把学习率调小一个数量级试试。如果还不行,检查数据有没有shuffle,标签有没有越界或者错误。再不行,检查模型初始化,有时候预训练模型加载失败会静默使用随机初始化,导致完全不收敛。
提示:训练之前一定要做一次小规模过拟合测试。拿10条数据训练,看模型能不能把这10条数据拟合到接近100%准确率。如果连10条数据都拟合不了,说明模型或者训练流程有问题。
5.2 离线指标好但线上效果差怎么办
这个问题太常见了,原因通常有三个:数据泄漏、分布偏移、评估指标和业务指标不一致。数据泄漏是指训练集和测试集有重叠,或者特征里包含了标签信息。分布偏移是指线上数据分布和训练数据分布不一致。评估指标和业务指标不一致是指你优化的指标和业务真正关心的指标不是一回事。
排查方法:先检查数据切分逻辑,确保没有泄漏。然后对比线上输入和训练输入的分布,看有没有明显差异。最后检查评估指标是否合理,比如在不平衡数据集上只看准确率是没有意义的。
5.3 推理延迟太高怎么优化
推理延迟优化有几个方向:模型压缩(量化、剪枝、蒸馏)、推理框架优化(ONNX Runtime、TensorRT)、服务架构优化(批处理、缓存、异步)。我一般先从量化开始,把FP32量化成INT8,延迟能降一半左右,精度损失通常在1%以内。如果还不够,再考虑换推理框架。
| 优化手段 | 延迟降低幅度 | 精度损失 | 实施难度 |
|---|---|---|---|
| INT8量化 | 40%-60% | 0.5%-2% | 低 |
| ONNX Runtime | 20%-40% | 0 | 低 |
| 模型蒸馏 | 50%-70% | 1%-3% | 中 |
| TensorRT | 60%-80% | 0.5%-2% | 中 |
| 批处理 | 取决于batch size | 0 | 低 |
5.4 数据漂移怎么检测和处理
数据漂移的检测方法有很多,我常用的是PSI(Population Stability Index)和KL散度。PSI小于0.1说明分布稳定,0.1到0.25说明有轻微漂移,大于0.25说明有显著漂移。检测到漂移之后,处理方式取决于漂移的原因。如果是季节性波动,可以等一段时间看是否恢复。如果是趋势性变化,需要重新训练模型。如果是突发性变化,需要排查上游数据源。
# PSI计算示例 import numpy as np def calculate_psi(expected, actual, buckets=10): breakpoints = np.percentile(expected, np.linspace(0, 100, buckets + 1)) expected_perc = np.histogram(expected, breakpoints)[0] / len(expected) actual_perc = np.histogram(actual, breakpoints)[0] / len(actual) expected_perc = np.where(expected_perc == 0, 0.0001, expected_perc) actual_perc = np.where(actual_perc == 0, 0.0001, actual_perc) psi = np.sum((actual_perc - expected_perc) * np.log(actual_perc / expected_perc)) return psi5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 训练loss不下降 | 学习率太小、模型初始化问题 | 检查学习率、检查模型参数是否更新 | 调大学习率、重新初始化 |
| 训练loss震荡 | 学习率太大、batch size太小 | 观察loss曲线 | 调小学习率、增大batch size |
| 验证集指标远低于训练集 | 过拟合 | 对比训练和验证指标 | 增加正则化、增加数据、早停 |
| 推理结果和训练结果不一致 | 预处理不一致、模型未切换eval模式 | 对比预处理代码、检查model.training | 统一预处理、调用model.eval() |
| 显存OOM | batch size太大、模型太大 | 查看显存占用 | 减小batch size、梯度累积、混合精度 |
| 服务启动失败 | 依赖缺失、端口占用 | 查看日志 | 补依赖、换端口 |
6. 我踩过的坑和给你的一些实在建议
6.1 别在数据上偷懒
我早期最大的教训就是在数据上偷懒。觉得数据清洗麻烦,随便搞搞就扔进模型训练。结果就是模型效果怎么调都上不去,最后回头一看,数据里一堆脏样本、重复样本、标注错误的样本。把数据清理干净之后,同样的模型结构,F1直接涨了5个点。所以我的建议是,在数据上花多少时间都值得。数据清洗、数据标注、数据版本管理,这些工作看起来不产生直接价值,但它们决定了你模型效果的上限。
6.2 实验记录要养成习惯
另一个我反复强调的事情是实验记录。我见过太多人跑完实验不记录,过两天就忘了这个结果是用什么配置跑出来的。我的习惯是,每次实验都写一段备注,记录改了什么、为什么改、结果如何、下一步计划。这个习惯让我在后期迭代的时候效率高了很多,不用反复试之前试过的配置。
6.3 评估体系要尽早建
评估体系一定要尽早建,不要等模型训练完了才想起来要评估。评估体系建得越早,你后面迭代的速度越快。而且评估体系要尽可能自动化,每次训练完自动跑评估、自动生成报告、自动和基线对比。这样才能形成闭环,让每次迭代都有明确的依据。
6.4 部署和监控不是终点
很多人觉得模型上线就完事了,其实上线只是开始。线上环境比实验室环境复杂得多,数据分布会变、流量会波动、硬件会出故障。没有监控的AI系统就像没有仪表盘的汽车,你根本不知道它什么时候会出问题。所以监控一定要做,而且要做得细。输入分布、输出分布、置信度分布、延迟、错误率,这些指标都要监控。
6.5 保持简单
最后一条建议是保持简单。我见过很多团队,一上来就搞复杂的微服务架构、搞分布式训练、搞自动机器学习。结果系统复杂度上去了,维护成本也上去了,但实际效果并没有提升多少。对于大多数项目来说,一个简单的FastAPI服务加一个PostgreSQL数据库就够了。等流量真的上来了,再考虑更复杂的方案。过早优化是万恶之源,这句话在AI工程领域同样适用。
这个内容后续还可以这样扩展:如果你做的是计算机视觉项目,数据管道和评估体系会有所不同,比如需要处理图像增强、标注格式转换、mAP计算等。如果你做的是推荐系统,评估体系会更复杂,需要处理A/B测试、离线在线一致性等问题。但核心思路是一样的:先把数据和评估搭好,再搞模型和部署。这个顺序不能乱。