☰
Jupyter Notebook AI图像分类实战:从环境配置到可交付源码
2026/10/8 9:40:24 网站建设 项目流程

简介:这是一份基于 Jupyter Notebook 编写的人工智能理论与应用实战源码合集,覆盖机器学习、深度学习与自然语言处理三大方向,适合零基础入门和希望查漏补缺的进阶开发者。内容由浅入深,先学必备数学基础(线性代数、概率论、统计学),再经机器学习算法原理与推导、竞赛优胜解决方案,过渡到卷积神经网络、循环神经网络等深度学习模型,最后落地到自然语言处理通用框架 BERT 项目实战;各模块均配有详细注释、可复现数据集和可视化图表,保证每个知识点都能动手验证。案例部分则包含手写线性回归、NLP 处理实例、建筑能源利用率预测等贴近实际的任务,能够展示从数据准备到模型分析的完整流程。压缩包共 802 个文件,以61个IPython笔记本、Python脚本和Markdown文档为核心,辅以668张PNG图像、多个CSV/DTA数据集等,合计约67.47MB;图像多用于模型结构和结果展示,笔记本则支持交互式运行,降低了理解门槛。目前已有265人学习下载,无论是自学充电、竞赛备赛还是课堂教学,都能从完整路径中快速获取从理论到实战的参考价值。

1. 这个标题到底想交付什么:不是演示文稿,是一套能跑通、能改、能答辩的 AI 教学工程

“基于 Jupyter Notebook 的 AI 理论及应用实战设计源码”这句话,在课程设计、实训周、毕业设计里出现的频率极高。它指向的不是一个算法比赛项目,也不是一篇论文复现,而是一份既要讲清 AI 理论、又要给出可用代码的交付物。说得直白点:你要交的是一个别人打开 Notebook 就能看到“损失函数下降曲线”、跑完能出识别结果、答辩时能讲清楚“为什么用交叉熵不用均方差”的完整工程。

很多人在这个标题上翻车,不是因为算法不会写,而是把 Jupyter Notebook 用成了草稿纸:代码块乱序、依赖靠 pip 现场装、结果图没保存、换个环境就崩。而真正合格的实战设计源码,应该是“过程可见、结果可复现、代码可改”。这篇笔记我按自己带实训项目的习惯,从环境准备、理论到代码的映射、工程化封装、常见踩坑到进阶收尾,把一条能直接照着走的路径拆给你。内容偏图像分类这条主线,因为它是 AI 理论落地最直观的载体,理论点够多、训练周期短、演示效果好。你拿到后把数据集和网络结构换掉,就能迁移到文本、时序等其他方向。

2. 先把环境变成可复现的:Jupyter Notebook 安装、内核绑定与目录规划

2.1 用 conda 建独立环境:Python 版本、pip 安装与 ipykernel 注册

Jupyter Notebook 本身只是一个交互壳,真正的计算环境由 kernel 提供。很多“在我电脑上能跑,到你这儿就崩”的冲突,九成出在 kernel 与虚拟环境错位上。常见做法是用 conda 为项目单独建环境,再把这个环境注册成 Notebook 的 kernel。

conda create -n ai-lab python=3.10 -y conda activate ai-lab pip install jupyter notebook ipykernel python -m ipykernel install --user --name ai-lab --display-name "AI-Lab(Python3.10)"

这里几个参数值得说明。Python 3.10 是目前 PyTorch/TensorFlow 都支持得很好的版本,避开 3.12 可能遇到的算子兼容问题;ipykernel install --user是把这个环境注册到当前用户级别的 Jupyter kernel 列表里,--name是 kernel 的内部标识,--display-name是 Notebook 界面里显示的名字。注册完成后,每次打开 Notebook 都要在右上角 Kernel 菜单里手动切换到 “AI-Lab” 这个 kernel,否则你 conda 环境里装的包在 Notebook 里根本看不到。

还有个很容易忽略的点:pip install jupyter notebook这一步别省。很多教材默认你已经有了 Notebook,但实战项目里你需要的往往是和虚拟环境绑定的独立 Jupyter。如果不装,直接在系统级 Jupyter 里切 kernel 也能用,但依赖隔离就不彻底了。隔离不彻底的后果是:系统里某个旧版本的 numpy 会悄悄把你在环境里新装的 numpy 覆盖掉,训练出来的 loss 曲线开始震荡——这种问题排查起来非常耗时。

2.2 目录布局与默认保存路径:一个项目一个根目录,路径只写相对路径

Jupyter Notebook 的默认保存路径通常在用户主目录下的Documents或home文件夹里。多人共用一台机器时,经常出现 notebook 文件散落在各处的情况。实战设计源码讲究可交付,第一步就是把项目固定成一个独立目录:

ai-image-classifier/ ├── notebooks/ # 所有 .ipynb 放在这里 ├── src/ # 被 notebook 调用的 .py 模块 ├── data/ # 数据集(原始数据、划分后的数据) ├── models/ # 训练产出的权重文件 .pth / .h5 ├── reports/ # 图表、导出的 HTML 报告 ├── requirements.txt # 依赖清单 └── README.md # 项目说明

这个结构我用了很多年,核心思想是让 notebook 只做“交互展示”,把可复用的逻辑(数据加载、模型定义、评估函数)放进src/。Notebook 里import sys; sys.path.append('../src')就能调用。路径方面有一条铁律:代码里永远写相对路径,不要写C:\Users\...这种绝对路径。Notebook 的工作目录是它所在文件夹,所以以notebooks/为起点,数据目录写成../data/train就是对的。换机器的时候,整个项目目录拷走,所有路径依然有效。

2.3 固定依赖版本:requirements.txt 要精确到小版本号

训练类项目里,numpy、pandas、torch 这些库的大版本升级经常带来行为变化。pip freeze > requirements.txt输出的是一长串带精确版本号的列表,虽然丑,但可复现性最强。我一般会再手动整理一份精简版,把核心依赖精确固定:

torch==2.1.2 torchvision==0.16.2 numpy==1.26.4 matplotlib==3.8.4 jupyter==1.0.0 ipykernel==6.29.3

这里 torch 和 torchvision 的版本必须配套,2.1.2对应的 torchvision 是0.16.2,混搭会出现torchvision依赖的 C++ 算子找不到的错误。用户在部署时执行pip install -r requirements.txt就能获得和作者几乎一致的环境。这一步看似琐碎,但它直接决定了你的“源码”是能直接跑的项目还是只能看看的代码片段。

3. 从 AI 理论到可运行代码:用图像分类把损失函数、反向传播和训练循环落到 Notebook 里

3.1 数据准备:下载、划分与可视化,验证集千万不能 shuffle

AI 理论里最基础的一条是“训练集/验证集/测试集要划分合理”。图像分类里我一般直接用 torchvision 自带的数据集做演示,比如 CIFAR-10,既有足够的类别数支撑理论讲解,单个样本又小到 CPU 也能训练。

import torch import torchvision import torchvision.transforms as T transform = T.Compose([ T.ToTensor(), T.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) ]) train_set = torchvision.datasets.CIFAR10(root='../data', train=True, download=True, transform=transform) test_set = torchvision.datasets.CIFAR10(root='../data', train=False, download=True, transform=transform) train_set, val_set = torch.utils.data.random_split(train_set, [45000, 5000]) train_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True, num_workers=2) val_loader = torch.utils.data.DataLoader(val_set, batch_size=64, shuffle=False, num_workers=2)

这里的 Normalize 参数是 CIFAR-10 数据集的官方均值与标准差,直接抄官方数值即可,不要自己从数据里现算。训练集shuffle=True是必须的,它能打乱样本顺序,避免模型学到批次间的顺序信息;验证集shuffle=False同样重要,确保每次评估的顺序一致,否则你对比两次验证结果时没法判断精度的变化是来自模型改进还是样本顺序变化。

数据准备这一步,我会让学员额外画一张样本网格图,用matplotlib展示每个类别下的几张图片。这个动作看着简单,实际价值是验证数据加载链路是否通了——如果图片显示出来是花屏或者错位的,后面训练再久也是白费。

3.2 模型与训练:四行关键代码画出损失下降曲线

理论部分要讲清楚“前向传播算损失、反向传播算梯度、优化器更新参数”这三件事。落到代码里,训练循环就是这三件事的循环执行。以 ResNet-18 为例,我习惯把训练循环写成一个可复用的函数:

import torch.nn as nn def train_one_epoch(model, loader, criterion, optimizer, device): model.train() running_loss, correct, total = 0.0, 0, 0 for inputs, labels in loader: inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() * inputs.size(0) _, predicted = torch.max(outputs, 1) total += labels.size(0) correct += (predicted == labels).sum().item() epoch_loss = running_loss / total epoch_acc = correct / total return epoch_loss, epoch_acc

这段代码里最核心的是optimizer.zero_grad()这一行。PyTorch 的梯度是累积的,如果不清零,上一次迭代的梯度会叠加到这一次上,导致 loss 曲线上下震荡。逻辑流程是:前向得到outputs→ 与labels算交叉熵损失 →backward()自动求导得到每个参数的梯度 →step()让优化器按梯度更新参数。循环结束后用running_loss / total得到整个 epoch 的平均损失和准确率,这两个值就是画曲线用的原始数据。

模型定义和优化器选择也很讲究。做理论演示我用torchvision.models.resnet18(weights=None)从零初始化,而不是加载预训练权重——虽然预训练权重能显著提高精度,但会让学员误以为“模型自己学得好”,掩盖了训练本身的作用。优化器选 Adam,学习率设1e-3,这是图像分类里最不容易出错的组合:

model = torchvision.models.resnet18(weights=None) model.fc = nn.Linear(512, 10) criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

model.fc = nn.Linear(512, 10)是把 ResNet-18 最后的全连接层从 ImageNet 的 1000 类改成 CIFAR-10 的 10 类。训练时收集每个 epoch 的损失和准确率,最后用 matplotlib 画两条曲线——训练损失和验证损失同图对比,这就是答辩时最有说服力的“理论到实战”证据。

3.3 理论可视化:让梯度、过拟合、数据增强效果变成长在 Notebook 里的图

实战设计源码区别于普通代码的地方,在于它要把抽象理论变成肉眼可见的证据。损失曲线是第一个,另外还有三个我强烈建议加进 Notebook 的图。第一个是数据增强效果对比图:把同一张原图分别经过随机裁剪、随机翻转、颜色抖动后并排展示,讲清楚“增强不是造假,是扩大数据分布”。第二个是过拟合可视化:训练 30 个 epoch 后画出训练损失和验证损失的分离趋势,当两者差距越来越大时,就是活生生的过拟合样本。第三个是卷积核可视化:把第一层卷积权重model.conv1.weight.data取出,归一化后画成 16 张小图拼成的网格。

import matplotlib.pyplot as plt weights = model.conv1.weight.data.cpu().numpy() fig, axes = plt.subplots(4, 4, figsize=(8, 8)) for i, ax in enumerate(axes.flat): if i < weights.shape[0]: kernel = weights[i] kernel = (kernel - kernel.min()) / (kernel.max() - kernel.min()) ax.imshow(kernel.transpose(1, 2, 0)) ax.axis('off') plt.tight_layout() plt.savefig('../reports/conv1_weights.png', dpi=150)

这段代码把卷积核权重归一化到 0-1 区间再显示。第一层卷积核学到的通常是边缘、颜色块等底层特征,图案越清晰说明模型确实在学东西,而不是在瞎猜。plt.savefig这行是关键——Notebook 里直接plt.show()只在交互时能看见,关闭页面就没了。实战设计源码要求结果能沉淀,所以每张产生价值的图都要显式存到reports/目录。我见过太多学员答辩前重新跑一遍训练只为截图,这种苦完全没必要吃。

4. 把 Notebook 工程化:从 .ipynb 到可交付源码的封装步骤

4.1 用 nbconvert 把 Notebook 转成脚本:保留结果、去掉干扰

Notebook 作为交付物有一个天然弱点:代码块执行顺序可以被随意打乱,而 py 脚本是顺序执行的。答辩评审很可能会乱点你的 Notebook,这时候某个 Cell 因为依赖前一个 Cell 的变量而报错,就是灾难现场了。我的习惯是:Notebook 保留给人看,同时用 nbconvert 输出一份干净的 Python 脚本作为后备。

jupyter nbconvert --to script --template lab notebooks/02_train.ipynb --output ../src/train_script.py

nbconvert --to script会把每个代码 Cell 原样导出为.py文件,markdown 说明变成注释。--template lab是实验室风格模板,导出的脚本会附带 Cell 序号标记。这样做的价值是:当 Notebook 执行顺序出问题时,脚本文件是严格的顺序执行版本,可以作为排查依据。另外,在自动化部署场景下,脚本可以直接被 Jenkins 或定时任务调度,Notebook 做不到这一点。

导出后的脚本末尾可以追加一段主入口逻辑:

if __name__ == "__main__": model = build_model() history = run_training(model, epochs=20) plot_history(history)

这样既保留了 Notebook 的交互性,又让交付物有了可命令行执行的能力,本质上解决的是“代码能不能在脱离交互环境后仍然跑通”的问题。很多课程设计源码卡在评审这一关,就是因为评审想当场跑一遍代码,但发现 Notebook 里一堆 Cell 依赖手动操作。

4.2 检查点与结果目录:训练产出要有后悔药

训练类的源码如果跑一半崩了要重新开始,是让人最崩溃的场景。PyTorch 的ModelCheckpoint机制可以实现按 epoch 保存权重,但为了减少依赖,我一般直接手写一个简单的保存逻辑:

best_acc = 0.0 for epoch in range(epochs): train_loss, train_acc = train_one_epoch(...) val_loss, val_acc = evaluate(...) if val_acc > best_acc: best_acc = val_acc torch.save({ 'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'best_acc': best_acc }, '../models/best_model.pth')

这里保存的是“验证集准确率最高的那一次”的权重,而不是最后一次训练的权重。这个细节很关键:训练后期模型可能过拟合,最后一次权重在验证集上的表现反而不如中间某个时刻。torch.save字典格式的好处是,除了模型参数,还把 epoch 数、优化器状态、当时的准确率都存了下来。有了检查点,训练中断后可以这样恢复:

checkpoint = torch.load('../models/best_model.pth') model.load_state_dict(checkpoint['model_state_dict']) optimizer.load_state_dict(checkpoint['optimizer_state_dict'])

每次训练结束还会额外存一份last_model.pth,逻辑是“即使没达到最优,也要保住最后一次的成果”。这个习惯在实战里救过我很多次:有时候最佳模型在验证集上表现好,但在测试集上反而差,这时候能回退到最后一个 epoch 的权重重新评估,就是一种后悔药。

4.3 交付清单:README、依赖锁定、数据说明,让源码真正能复现

源码交付不只是代码本身。我收尾一个实战项目时,一般会花 20 分钟整理三样东西:README 文档、数据说明、复现步骤。README 里写清楚“这个项目做什么、目录结构是什么、环境怎么搭、训练命令是什么、预期精度是多少”。数据说明里写清楚数据集来源、训练/验证/测试划分比例、类别数量。复现步骤要精确到命令级别:

conda create -n ai-lab python=3.10 -y conda activate ai-lab pip install -r requirements.txt jupyter notebook notebooks/01_data_explore.ipynb

这份 README 本身就是减少答疑成本的最佳工具。学员拿着项目跑不通时,第一步永远是看 README 而不是看代码。如果 README 里写了“预期在 20 个 epoch 后验证准确率约 75%”,对方跑出 73% 就知道是正常波动,而不是跑过来问你“为什么我的结果和你不完全一样”。源码交付的本质是把“你脑子里的操作流程”变成“别人照着也能走的路径”,这一步做得越细,后续被问的问题越少。

5. 避坑 / 常见问题排查:Notebook 训练跑不通的 6 个翻车点

5.1 现象:Notebook 里 import torch 报 ModuleNotFoundError

这是最高频的翻车点。原因几乎都是 kernel 没切换到 conda 环境。你在终端conda activate ai-lab后装好了 torch,但 Notebook 里用的还是系统默认的 Python 内核。解决方法是执行python -m ipykernel install --user --name ai-lab重新注册内核,然后在内核菜单里手动切换。这个错误非常容易误导人,因为你在终端里import torch明明是成功的,容易让人怀疑是 Notebook 本身坏了。

5.2 现象:训练一半报 FileNotFoundError: No such file or directory

原因基本是路径写死成了绝对路径,或者相对路径的基准不对。Notebook 的当前目录是它自身所在的文件夹,不是项目根目录。我的做法是统一用../data/...这种基于 notebook 位置的相对路径,并且保证所有 notebook 都在同一层目录。如果跨机器拷贝后路径失效,优先检查是不是目录层级变了。

5.3 现象:验证集准确率一直停留在随机水平(CIFAR-10 大约 10%)

原因通常是数据预处理或标签处理出了问题。最常见的是 Normalize 的均值和标准差写错,导致输入分布严重偏离;其次是random_split后没有设置shuffle=True导致训练样本顺序固定,模型学到了某种顺序偏差。排查方法是用测试集做一次“过拟合测试”:只取 50 个样本训练 20 个 epoch,如果 loss 能降到接近 0,说明代码链路是通的,问题出在数据或超参数;如果降到 0 都难,说明模型或数据处理有 bug。这个排查思路比逐行看代码高效得多。

5.4 现象:训练过程中显存不断增长直至 OOM

原因有两个方向。第一是每次迭代都创建了新的计算图,最常见的是在循环里不小心把loss.item()写成了loss,导致梯度图一直被持有;第二是验证集评估时忘记with torch.no_grad(),验证过程也在累积梯度。解决方法是:训练循环里只在需要时保留张量,评估代码统一用with torch.no_grad():包裹。另外每隔几个 step 调用torch.cuda.empty_cache()治标不治本,真正要做的是防止计算图被意外保留。

5.5 现象:matplotlib 画的图在 Notebook 里不显示

原因是内核缺了%matplotlib inline魔术命令。这个命令让 matplotlib 的图直接嵌入到 Notebook 输出里,而不是弹出一个新窗口。解决方法是把%matplotlib inline放在 Notebook 第一个代码 Cell 里。还有变体问题:图能显示但不是上次训练的结果,这是因为没有执行plt.savefig前的所有代码块。要避免这个,建议养成“图直接存文件、用plt.close()关闭画布”的习惯——不关闭画布,后续绘图会把多张图画叠在同一张图上,输出错乱。

5.6 现象:换一台电脑训练结果不可复现,精度差 5 个百分点以上

原因是随机性没有被固定。PyTorch 里至少要固定三个地方:Python 随机种子、numpy 随机种子、PyTorch 随机种子,CUDA 相关的还有torch.backends.cudnn.deterministic = True。固定后,代码、数据、超参数相同的情况下训练结果能保持一致。这个问题在答辩现场遇到会很尴尬——你演示的结果跟文档里写的不一样,很难解释清楚。

6. 把实验变成作品:W&B 日志、nbconvert 报告导出与确定性训练

到这一步,你的 Notebook 已经能完整跑通训练、出图、保存权重、交付源码了,但离一个“作品”还差两步:可观测性和报告形态。

第一步是接入 Weights & Biases(W&B)做实验日志。W&B 的价值不只在云上记录曲线,更在于自动生成实验对比表——不同学习率、不同网络结构的训练结果放在同一张表里,答辩时用这张表讲“我对比了三个超参数的组合,得出以下结论”,说服力远大于单条 loss 曲线。接入方式极简:

import wandb wandb.init(project="ai-image-classifier", name="resnet18-lr1e3") wandb.log({"train_loss": train_loss, "val_acc": val_acc, "epoch": epoch})

wandb.log每个 epoch 调用一次,数据和曲线会实时同步到云端面板。对于有“源码必须能离线跑”要求的场景,不用 W&B 也无妨,但至少要在 Notebook 里用一个字典结构把每次实验的配置和结果记录下来,最后json.dump到reports/experiments.json,效果类似。

第二步是把 notebook 批量导出成评审可读的静态报告。nbconvert --to html会保留代码、执行结果和 markdown 排版,导出后是一份完整的实验报告,不需要安装任何环境就能用浏览器打开。这一步是“低成本交付感”的关键一笔:

jupyter nbconvert --to html notebooks/02_train.ipynb --output-dir reports/

最后说确定性训练。虽然我在避坑章节里提过固定随机种子,但这里强调一个进阶用法:固定种子不能只在训练脚本入口写一行全局torch.manual_seed(41),因为不同 PyTorch 版本对同一随机种的算子执行顺序有差异。所以要做到真正的可复现,要把 seed 设置和模型构建放进同一个函数里,并且把 PyTorch 版本号、CUDA 版本号一并记录到实验日志中。这样 “复现不了” 的锅就不在代码上。

我自己做项目时吃过这样的亏:为一门课程设计调好了所有参数,效果很好,但记录实验时漏了 torch 版本号,三个月后别人用新版 torch 跑,结果对不上,最后排查到算子差异上。从那以后,我每个 notebook 的第一个 Cell 固定放环境信息打印:

import torch, sys print("Python:", sys.version) print("PyTorch:", torch.__version__) print("CUDA:", torch.version.cuda if torch.cuda.is_available() else "CPU")

这不仅能让你的“实战设计”看起来更像正规工程,也会让你在每次打开 Notebook 时第一时间确认自己没跑错环境。希望这篇笔记帮你把这个标题变成一个真正经得起推敲的作品。

本文还有配套的精品资源,点击获取

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

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

立即咨询