☰
AI工程从零开始:从环境搭建到模型部署的完整指南
2026/10/3 4:18:45 网站建设 项目流程

1. 别急着写代码:想清楚"从零开始"到底意味着什么

我见过太多人栽在这一步。打开GitHub看到一个叫ai-engineering-from-scratch的仓库,热血上头,克隆下来就开始跑训练脚本,结果卡在环境依赖上折腾了一整天,最后连数据集长什么样都没看清楚。这几乎是每个刚接触AI工程的初学者都会踩的坑——拿到了仓库地址,却不知道自己究竟要在这个工程里做什么。

先把这个概念拆开:AI工程不是"写模型",而是"用工程化的方法把模型变成可持续运行、可维护、可迭代的系统"。一个合格的AI工程,至少包含四件套:数据管线、训练实验、部署服务、监控运维。绝大多数初学者只盯着"训练"这一环,然后被环境配置、数据清洗、模型存储、接口封装这些问题反复摩擦。

所以如果你想认真做ai-engineering-from-scratch这个项目,我建议你第一步不是看代码,而是画一张图,把下面这几件事理清楚:

  • 这个工程要解决什么问题?分类、生成、还是检索?
  • 数据的来源是什么?原始格式长什么样?需要哪些预处理步骤?
  • 模型选多大、选什么架构?训练需要什么样的GPU资源和显存?
  • 训练完成后怎么提供服务?需要多高的并发和延迟要求?
  • 模型漂移了怎么办?人工反馈的通道在哪里?

只有把这些想明白了,你才知道仓库里哪些代码是核心、哪些是辅助、哪些可以直接跳过不用管。从零开始做AI工程,本质上是从业务问题开始倒推技术方案,而不是从代码开始正推"能跑起来什么"。

2. 环境搭建的坑,远比你想的多得多

2.1 Python环境:从Virtualenv到Conda的取舍

在正式开始跑任何AI项目之前,环境是第一道关卡。你可能会觉得用pip install装几个包有什么难的,但真到了深度学习项目里,坑马上就会冒出来:CUDA版本对不上、PyTorch和TensorFlow冲突、Python 3.10某库不兼容、protobuf版本互相打架……我甚至见过有人在Mac上用pip install tensorflow装上之后发现只能跑CPU,还以为是代码写错了。

我的建议是:如果你不是只做纯Python的原型验证,直接用Conda。不光是虚拟环境隔离,更重要的是它能同时管理Python版本和CUDA依赖,尤其是你用NVIDIA GPU做深度学习训练的时候。Conda环境可以做到每个项目一套独立体系,互不污染。用conda create -n ai-eng python=3.10创建环境,然后按需安装PyTorch或TensorFlow对应的CUDA版本,踩坑概率骤降。

2.2 GPU驱动与CUDA版本的匹配逻辑

这是整个环境搭建过程中最容易让人崩溃的一段。很多人照着网上的教程装了CUDA,结果torch.cuda.is_available()返回False,怎么排查都找不到问题。

核心原因通常是:装了新的CUDA Toolkit,但显卡驱动太老,或者驱动版本支持的最高CUDA版本低于你安装的版本。这里有一个基本匹配逻辑:显卡驱动是底层,CUDA Toolkit是上层,上层不能超过下层的上限。用nvidia-smi查看驱动支持的CUDA版本,然后装一个不高于这个上限的CUDA版本,再用pip装对应编译好的PyTorch版本,基本就不会出错。

另外,我强烈建议在项目根目录写一个environment.yml或者至少写清楚依赖列表版本。不做版本锁定的AI工程,等于给自己埋雷——三个月后你打开之前的项目,发现依赖全变了,代码跑不起来了,那种痛苦相信很多人都有体会。

2.3 Docker:从"能跑"到"可复现"的关键一步

到了真的要把项目给别人复现、或者部署到服务器上的时候,Docker绝对绕不开。如果你只是在自己电脑上调代码,Conda就够用;但只要涉及团队协作、服务器部署、或者你想让这个从零开始的项目具备"工程化"的底子,一定要一开始就把Dockerfile写好。

FROM pytorch/pytorch:2.1.0-cuda11.8-cudnn8-runtime WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["python", "train.py"]

这份Dockerfile虽然简单,但它体现了工程化最基本的要求:环境可重复构建。在写Dockerfile时有几个细节需要注意:尽量选择官方发布的带CUDA的镜像作为基础,避免重复装驱动和CUDA的烦恼;把requirements.txt放在源码之前单独COPY,这样能充分利用Docker缓存,后续改代码不会重新安装依赖,构建速度快好几倍。

注意:国内网络环境下,拉取Docker镜像和pip依赖都可能遇到连接问题,建议提前配置好镜像源。这不是小问题,很多人的AI工程就是从"下载依赖失败"这一步开始放弃的。

3. 数据管线的优先级:没数据,模型就是花架子

3.1 先解决"数据从哪来"再到"数据怎么喂"

很多从零开始的AI工程,卡住的核心往往不是模型太复杂,而是根本没有高质量数据。很多人习惯先写好训练代码,然后才去找数据集,最后发现格式对不上、标签质量差、数量不够,又回头改代码,浪费大量时间。

我的建议是"数据先行"。不管你是爬公开数据集、从业务系统导出,还是自己设计标注方案,一定要在写模型代码前先把数据流程跑通:原始数据 → 清洗 → 预处理 → 切分训练/验证/测试集 → 构建DataLoader。这一步走完了,后续训练模型时就会非常顺畅。

3.2 数据清洗的常规操作清单

数据清洗听起来不酷,但它是决定模型效果上限的隐形要素。我总结了一套常规操作,你在项目里直接照着做就可以:

  • 去重。文本数据要处理完全重复的样本,图像数据检查完全相同的图片,这一步看似简单但能有效防止验证集数据泄漏,让模型效果虚高。
  • 处理缺失值。图像中缺样本可以直接丢弃,文本中缺标签可以考虑规则补全,表格数据则可以填充均值或众数,具体策略取决于业务场景。
  • 标准化/归一化。文本转小写、去除停用词;图像做灰度归一化;数值特征缩放到0到1区间。不同模型对此敏感度差异很大,但标准做法一定不会错。
  • 标签分布检查。做一个简单的统计直方图,看类别是否极度不均衡。如果发生严重不均衡,在训练前就想好要不要做重采样,否则后面模型会被多数类带跑偏。

这些步骤不需要都用上重武器,很多情况下其实就是几行Python的事。比如用pandas做标签分布检查:

import pandas as pd df = pd.read_csv("data/labels.csv") value_counts = df["label"].value_counts() print(value_counts) # 输出类别分布比例,判断是否需要重采样 class_ratios = value_counts / len(df) print(class_ratios)

3.3 数据集切分容易被忽视的三个细节

切训练集、验证集、测试集,人人都知道8:1:1,但实际操作里有三个细节特别容易被忽略。

第一,类别比例要保持。如果原始数据集里A类占80%、B类占20%,那你切出来的训练集也保持这个比例,不能切完发现训练集里全是A类。用sklearn的train_test_split时,别忘设置stratify=df["label"]这个参数。

第二,验证集和测试集必须保持"未来感"。如果数据带时间顺序,比如新闻文本、交易记录,一定要按时间切分,不能随机打乱,否则就是让模型用未来的信息预测过去,你得到的是一个看起来95%准确率但在真实场景下崩得一塌糊涂的模型。

第三,切分完固定下来。把切分后的索引存成文件或者设置固定随机种子,保证每次复现时训练集、验证集完全一致。没有这一步,你的实验对比就没有公平性可言——每次训练的数据都不一样,你根本无法判断模型效果提升是因为改进了模型,还是因为换了一批训练数据。

4. 训练实验管理:你以为的"跑通"只是万里长征第一步

4.1 实验记录为什么比代码本身更值钱

到了训练阶段,一个典型的错误是:改一改参数,跑一遍训练,看一眼loss下降没有,然后继续改——没有任何记录。跑了几十次实验后,看到结果最好的是第三天的某一次,但你完全记不清当时用了什么参数、什么数据版本、什么随机种子。

AI工程和写普通软件最大的区别就在于:它的行为是概率性的,实验的复现和追踪极其重要。如果你从零开始做工程化,我建议在第一天就把下面这些东西记录下来:

  • 模型结构的具体配置(隐藏层维度、层数、激活函数、dropout率)
  • 训练超参数(学习率、batch size、epoch数、优化器及参数)
  • 数据版本(用哪份清洗后的数据、预处理脚本的版本)
  • 训练环境(PyTorch版本、CUDA版本、GPU型号)
  • 随机种子(固定种子,保证可复现)
  • 关键指标(train loss、val loss、accuracy、F1等)

这些信息可以先用一个简单的CSV表格记录,也可以直接引入MLflow或者Weights and Biases这类实验管理平台。我对从零开始的项目的建议是:先用CSV手动记录,把习惯养成,等实验数量多了再上平台。

4.2 一个最小可用的训练循环

下面这段代码是一个结构清晰的训练循环框架,你拿到任何入门深度学习项目里都可以直接改改就用:

import torch from torch.utils.data import DataLoader def train_one_epoch(model, dataloader, optimizer, criterion, device): model.train() total_loss = 0.0 correct = 0 total = 0 for inputs, labels in dataloader: inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() total_loss += loss.item() * inputs.size(0) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() avg_loss = total_loss / total accuracy = correct / total return avg_loss, accuracy def validate(model, dataloader, criterion, device): model.eval() total_loss = 0.0 correct = 0 total = 0 with torch.no_grad(): for inputs, labels in dataloader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) loss = criterion(outputs, labels) total_loss += loss.item() * inputs.size(0) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() avg_loss = total_loss / total accuracy = correct / total return avg_loss, accuracy

如果你观察足够仔细,会发现一个关键点:训练时用optimizer.zero_grad()清空梯度,这是初学者最容易忘的。如果忘了,梯度会跨batch累加,模型行为完全不可控。另外model.train()和model.eval()的切换也不能省,它控制着Dropout和BatchNorm在不同阶段的行为差别,省了它,你看到的验证集指标基本没有参考价值。

4.3 从"过拟合一个batch"到"真正收敛"

我这里有一个实战中非常有效的经验,希望尽早传授给你:正式开始大规模训练之前,先取一两个batch的数据,跑一次完整的前向和反向传播,看模型能否把loss降到接近0。这个过程俗称"过拟合一个小batch",它的意义在于用最短的时间确认整条链路是通的——从数据加载到模型前向、从loss计算到梯度回传、从参数更新到指标统计,每一个环节都没有bug。

很多初学者跳过了这一步,直接让模型在全部数据上训练,结果发现36个小时跑完,loss就是不降,再回头排查,发现是数据标签错位了。这时已经浪费了整整一天半的GPU时间。

等小batch过拟合测试通过,再回到完整训练流程上,这就到了策略取舍的地方:学习率设多少合适。一个经验是3e-4到5e-5区间对大多数Transformer类模型来说是一个安全起点;CNN模型可以放宽到1e-3。如果你开启了学习率预热和衰减策略,通常训练表现会更稳定。

4.4 早停法与模型保存:别把GPU烧到最后

训练过程中我比较推荐使用早停法。设置一个耐心值,比如连续5个epoch验证集指标没有提升,就停止训练,把模型恢复到验证集表现最好的那一步。这样做的原因很简单:深度神经网络的训练曲线往往是锯齿状上升的,后期可能出现局部波动、过拟合和指标回退,继续烧GPU除了伤害钱包和耐心,没有多少收益。

模型保存同样有讲究。我建议每一轮都保存包含训练信息的完整checkpoint:

torch.save({ 'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'valid_loss': best_valid_loss, 'config': model_config, }, f'checkpoints/checkpoint_{epoch:03d}.pt')

这样你在后续任何时候都可以精确恢复训练或者进行模型评估,不用从头再来。只保存model.state_dict()的做法虽然方便加载,但想继续训练或者复盘实验时就会很被动。从工程角度讲,一个checkpoint如果能告诉你"这个模型在哪个epoch、什么数据、什么超参数下训练的",它的价值会超出模型权重本身。

5. 模型部署这"最后一公里",决定项目能否真正落地

5.1 TorchScript、ONNX还是一种简单服务化封装

模型训练完,怎么给别人用,是很多从零开始项目最容易掉链子的一步。你有可能把训练好的模型写成model.pt文件放在Git仓库里就完事了,但其他人想跑起来就得把整个训练代码拉下来,环境全部装一遍,再调用一些模糊不清的函数。这不是工程化的做法。

生产环境里常见的方式有三种:TorchScript、ONNX和纯服务化封装。

TorchScript是PyTorch官方提供的模型序列化格式,好处是它把模型结构和权重打包成单一文件,不依赖原始Python类定义。导出方式很简单:

model.eval() scripted_model = torch.jit.script(model.cpu()) scripted_model.save("models/model_scripted.pt")

ONNX则更进一步,把模型转换成与框架无关的中间格式,可以直接用ONNX Runtime在CPU上达到远超PyTorch原生的推理速度。但ONNX对某些动态结构兼容性不够理想,如果你的模型里包含循环和条件控制,可能需要额外处理。

对初学者来说,我建议的路线是:先不用管TorchScript和ONNX这些繁琐格式,先写一个标准的推理脚本封装,把模型的加载和预测封装成干净的predict(input)接口。这段代码才是工程化的第一步:

import torch from PIL import Image import torchvision.transforms as transforms class ModelInference: def __init__(self, model_path, device="cpu"): self.device = torch.device(device) self.model = torch.jit.load(model_path, map_location=self.device) self.model.eval() self.transform = transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) def predict(self, image_path): image = Image.open(image_path).convert("RGB") tensor = self.transform(image).unsqueeze(0).to(self.device) with torch.no_grad(): output = self.model(tensor) prob = torch.softmax(output, dim=1) return prob

5.2 FastAPI是当前比较稳妥的服务化选择

如果你想把这个推理脚本变成真正的"线上服务",我推荐用FastAPI,它相比Flask有几个更贴合AI项目的特点:原生支持异步、自动生成API文档、基于Pydantic做请求参数校验,尤其适合深度学习模型的在线推理场景。一个最小可用的服务只需要几十行代码:

from fastapi import FastAPI, UploadFile, File import uvicorn import torch app = FastAPI() inferer = ModelInference("models/model_scripted.pt", device="cpu") @app.post("/predict") async def predict(file: UploadFile = File(...)): contents = await file.read() with open("/tmp/upload.jpg", "wb") as f: f.write(contents) probabilities = inferer.predict("/tmp/upload.jpg") top3_indices = torch.topk(probabilities, 3).indices[0].tolist() return {"predictions": [{"class_id": idx, "probability": float(probabilities[0, idx])} for idx in top3_indices]} if __name__ == "__main__": uvicorn.run(app, host="0.0.0.0", port=8000)

这个服务看起来简陋,但它完整地做到了"模型推理对外提供HTTP接口"这件事。你可以先用uvicorn main:app --reload在本地起服务,然后用curl测一下:

curl -X POST "http://localhost:8000/predict" -F "file=@test.jpg"

如果一切正常,返回的JSON里就是模型的预测结果。到了这一步,"模型能跑"和"模型能用"之间的距离就被拉近了。

5.3 部署到云服务器时的几个实操建议

本地跑通只是第一步,真正部署到云服务器的过程中还有几个注意点值得提前准备。

第一个是模型文件的分发和加载路径。不要把大模型文件直接放进Git仓库,Github有100MB的文件大小限制,而且push几十MB的权重文件会造成仓库膨胀,日后拉取代码非常痛苦。正规做法是使用模型专用的对象存储服务(如S3、OSS)或者用Git LFS来管理。

第二个是GPU和CPU之间的选择。很多推理场景其实CPU就够用。特别是用的预训练模型参数量在亿级以下,且没有大量请求并发时,开一个好消息是,即使不上GPU,优化过的CPU推理也能满足一般业务需求。更重要的是,CPU服务器通常便宜得多,从成本角度考虑非常划算。

第三个是服务自动重启和健康检查。用systemd或Docker Compose管理服务进程,让它崩溃后自动拉起。加上一个/health端点,方便负载均衡和监控系统定期检查服务是否存活。

6. 上线的终点是监控:模型漂移和效果衰减防不胜防

如果你以为模型部署上线后就万事大吉,那我只能说你还没经历过真正的AI工程。模型跑在生产环境里,数据分布每天都在变,用户的输入模式和训练集有差异,模型的效果可能在你不知不觉中持续下降——这就是所谓的"模型漂移"问题。它通常发生在两种情况下:一是数据漂移,即输入数据分布发生变化;二是概念漂移,即输入到输出的映射规则发生了变化。

针对这些问题,我建议你从第一天就建立以下三个监控习惯:

  • 输入数据分布监控:对每一批真实请求的特征做统计,和训练集分布对比,发现异常偏离能及时预警。
  • 预测结果质量抽检:人工抽样检查模型输出,记录错误率和置信度分布变化。
  • 延迟和负载监控:记录服务的推理延迟、并发请求量、内存和GPU利用率,确保系统稳定性。

举个很直观的例子:一个在晴天拍摄街景下训练出来的分类模型,部署后遇到连续阴雨天气,输入图片的像素分布发生明显偏移,模型准确率可能从95%直接跌到70%。如果没有数据分布监控,你可能要等用户大量投诉后才发现问题。而有了监控告警,你可以第一时间发现分布偏离,及时收集新数据、重新训练模型,把影响控制在最小范围。

7. 从"跑通一个仓库"到"做出自己的AI工程":最后阶段的路径建议

说实话,ai-engineering-from-scratch这类项目之所以有吸引力,恰恰在于它给了你一个从混沌到清晰的成长路径。但我要提醒你:完全照着仓库跑一遍,你的收获非常有限。真正的成长发生在你"改"这个仓库的那一刻——换一个数据集、加一个新功能、换一种模型架构、修复一个bug、把单机训练改成分布式、把同步推理改成异步队列。每一次改变,都会逼你深入理解一个原本一知半解的环节。

在这里我给你三条可以具体执行的方向:

  • 选一个小而完整的问题场景,比如文本分类或图像分类,从数据采集到服务上线全流程亲手走一遍,不依赖任何端到端的AutoML工具。
  • 把这个项目的某一环彻底做深:例如把数据管线切换到流式处理,或者把模型推理用ONNX Runtime重写,体会不同技术选择之间的差异。
  • 给自己设定一个"交付"标准:Git仓库里有清晰的README、能一键复现的环境配置、完整的实验记录、标准化的模型推理接口。做到这些,你的工程化意识就已经比绝大多数教程搬运工强了。

我在做类似的从零项目时,最大的感受是:AI工程的知识密度其实被远远低估了。很多人觉得"我懂机器学习算法就够了",但实际上数据清洗、依赖管理、训练调度、模型服务化、监控告警、实验追踪,这些工程能力才是真正区分"能做Demo"和"能落地"的分界线。而且这些能力没有任何捷径,只能在项目中一个一个坑踩过来,踩得越多,下一次就越稳。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询