ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零搭建AI工程能力:数据管道与评估体系先行,避开调包侠陷阱

从零搭建AI工程能力:数据管道与评估体系先行,避开调包侠陷阱 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并且锁定版本号。不要写numpy1.20这种要写numpy1.24.3。另外CUDA版本、cuDNN版本、PyTorch版本之间的兼容性一定要查清楚这三个东西版本不匹配是新手最常见的坑。# 创建虚拟环境 python -m venv ai-env source ai-env/bin/activate # 安装依赖并锁定版本 pip install torch2.1.0 torchvision0.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_modelResponse) def predict(req: Request): with torch.no_grad(): inputs tokenizer(req.text, return_tensorspt) outputs model(**inputs) probs torch.softmax(outputs.logits, dim-1) conf, pred torch.max(probs, dim-1) return Response(labelid2label[pred.item()], confidenceconf.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存放Dockerfilescripts存放各种脚本。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, encodingutf-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_labelsconfig[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_direxperiments/exp001, num_train_epochsconfig[training][epochs], per_device_train_batch_sizeconfig[training][batch_size], learning_rateconfig[training][learning_rate], warmup_ratioconfig[training][warmup_ratio], seedconfig[training][seed], evaluation_strategyepoch, save_strategyepoch, load_best_model_at_endTrue, metric_for_best_modelf1 ) trainer Trainer( modelmodel, argstraining_args, train_datasettrain_dataset, eval_datasetval_dataset, compute_metricscompute_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_dictTrue ) 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 Runtime20%-40%0低模型蒸馏50%-70%1%-3%中TensorRT60%-80%0.5%-2%中批处理取决于batch size0低5.4 数据漂移怎么检测和处理数据漂移的检测方法有很多我常用的是PSIPopulation Stability Index和KL散度。PSI小于0.1说明分布稳定0.1到0.25说明有轻微漂移大于0.25说明有显著漂移。检测到漂移之后处理方式取决于漂移的原因。如果是季节性波动可以等一段时间看是否恢复。如果是趋势性变化需要重新训练模型。如果是突发性变化需要排查上游数据源。# PSI计算示例 import numpy as np def calculate_psi(expected, actual, buckets10): 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()显存OOMbatch size太大、模型太大查看显存占用减小batch size、梯度累积、混合精度服务启动失败依赖缺失、端口占用查看日志补依赖、换端口6. 我踩过的坑和给你的一些实在建议6.1 别在数据上偷懒我早期最大的教训就是在数据上偷懒。觉得数据清洗麻烦随便搞搞就扔进模型训练。结果就是模型效果怎么调都上不去最后回头一看数据里一堆脏样本、重复样本、标注错误的样本。把数据清理干净之后同样的模型结构F1直接涨了5个点。所以我的建议是在数据上花多少时间都值得。数据清洗、数据标注、数据版本管理这些工作看起来不产生直接价值但它们决定了你模型效果的上限。6.2 实验记录要养成习惯另一个我反复强调的事情是实验记录。我见过太多人跑完实验不记录过两天就忘了这个结果是用什么配置跑出来的。我的习惯是每次实验都写一段备注记录改了什么、为什么改、结果如何、下一步计划。这个习惯让我在后期迭代的时候效率高了很多不用反复试之前试过的配置。6.3 评估体系要尽早建评估体系一定要尽早建不要等模型训练完了才想起来要评估。评估体系建得越早你后面迭代的速度越快。而且评估体系要尽可能自动化每次训练完自动跑评估、自动生成报告、自动和基线对比。这样才能形成闭环让每次迭代都有明确的依据。6.4 部署和监控不是终点很多人觉得模型上线就完事了其实上线只是开始。线上环境比实验室环境复杂得多数据分布会变、流量会波动、硬件会出故障。没有监控的AI系统就像没有仪表盘的汽车你根本不知道它什么时候会出问题。所以监控一定要做而且要做得细。输入分布、输出分布、置信度分布、延迟、错误率这些指标都要监控。6.5 保持简单最后一条建议是保持简单。我见过很多团队一上来就搞复杂的微服务架构、搞分布式训练、搞自动机器学习。结果系统复杂度上去了维护成本也上去了但实际效果并没有提升多少。对于大多数项目来说一个简单的FastAPI服务加一个PostgreSQL数据库就够了。等流量真的上来了再考虑更复杂的方案。过早优化是万恶之源这句话在AI工程领域同样适用。这个内容后续还可以这样扩展如果你做的是计算机视觉项目数据管道和评估体系会有所不同比如需要处理图像增强、标注格式转换、mAP计算等。如果你做的是推荐系统评估体系会更复杂需要处理A/B测试、离线在线一致性等问题。但核心思路是一样的先把数据和评估搭好再搞模型和部署。这个顺序不能乱。
返回列表