项目开源地址:
https://atomgit.com/chen_jixi/cnn-mnist-playground

华为云码道:
AtomGit - 全球开发者的开源社区,开源代码托管平台

前言

前面几篇文章中,我已经用华为云码道 Agent 完成了一个 OpenCV Vision Lab 项目。

从基础图像处理,到 Canny、Harris、SIFT,再到多组参数对比,整个项目已经比较完整。

但 OpenCV 项目有一个特点:

很多算法本身不需要训练模型。

只要安装好 OpenCV,就可以直接调用对应算法完成处理。

所以这一次,我想继续往前走一步。

从传统计算机视觉,正式进入:

PyTorch + CNN + 深度学习。

这次准备开发一个:

Neural Network Playground——CNN 手写数字识别与可视化实验平台。

目标很简单:

用户在网页中手写一个 0~9 的数字,后端使用真实 CNN 模型进行推理,然后返回:

  • 最终预测数字
  • 预测置信度
  • 0~9 十个类别的概率分布
  • 模型基本信息

也就是说,这次不再只是“调用算法”。

而是:

前端输入 → 图像预处理 → CNN 模型 → Softmax 概率 → Web 可视化。


一、这次项目和 OpenCV 最大的区别是什么?

OpenCV 项目里,我们主要处理的是:

图片
↓
算法
↓
结果

而 CNN 项目多了一整套模型流程:

MNIST 数据集
↓
训练 CNN
↓
保存模型权重
↓
Web 加载模型
↓
用户手写数字
↓
图像预处理
↓
模型推理
↓
返回 0~9 概率

这意味着项目复杂度明显高了一层。

因为除了 Web 页面以外,还需要真正解决:

  • PyTorch 环境
  • CNN 模型定义
  • MNIST 数据集
  • DataLoader
  • 模型训练
  • 模型权重保存
  • 权重加载
  • 图像预处理
  • 推理
  • 概率输出

所以这次我给码道的要求也比较明确:

不允许使用随机结果、硬编码数字或者假模型代替 CNN。

必须是真实的 PyTorch 模型。


二、我只给需求,让 Agent 自己设计项目结构

这次我仍然没有把每个文件应该怎么写都规定死。

我只明确了几个核心要求:

开发一个 CNN 手写数字识别平台。

要求:
- PyTorch
- MNIST
- 真实 CNN 模型
- Web 手写画布
- 0~9 概率分布
- 模型信息
- 独立训练脚本
- 模型权重保存
- FastAPI 或其他合适 Web 框架
- README
- 测试
- Git 提交与推送

和 OpenCV 第一篇一样,我更想观察的是:

Agent 会怎么拆这个项目。

而不是我提前告诉它所有实现细节。


三、Agent 最终选择了什么技术方案?

Agent 最终给出的方案是:

后端

  • Python
  • FastAPI

深度学习

  • PyTorch
  • torchvision
  • MNIST

CNN

  • 2 个卷积层
  • 全连接层
  • 输出 10 个类别

前端

  • HTML
  • CSS
  • JavaScript

测试

  • pytest

最终项目结构大致如下:

cnn-mnist-playground/
├── README.md
├── requirements.txt
├── .gitignore
├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── model.py
│   ├── predict.py
│   └── weights/
│       └── .gitkeep
├── scripts/
│   └── train_model.py
├── static/
│   ├── index.html
│   ├── style.css
│   └── app.js
└── tests/
    ├── __init__.py
    ├── test_model.py
    └── test_api.py

这一套结构我觉得比较合理。

训练、模型、推理、Web 和测试都拆开了,没有全部堆在一个 Python 文件里。


四、真正遇到的第一个问题:PyTorch 环境

这次开发并不是一次顺利到底。

Agent 最开始尝试在云端环境中安装:

torch
torchvision

但是深度学习环境和 OpenCV 不一样。

PyTorch 本身体积就比较大,而且还会涉及 CUDA、cuDNN 等依赖。

因此云端环境安装过程并不稳定。

最后我调整了开发方式:

Agent 负责完成项目代码和工程结构,本地机器负责真正安装 PyTorch、训练模型和运行项目。

这个方式反而更接近真实开发。

因为实际深度学习项目中:

开发环境和训练环境本来就不一定是同一个。


五、本地安装 PyTorch

项目推送到 AtomGit 以后,我把代码克隆到本地。

先创建独立 Conda 环境:

conda create -n cnnmnist python=3.10 -y
conda activate cnnmnist

然后安装项目依赖:

pip install -r requirements.txt

这次本地安装成功。

可以看到 PyTorch、torchvision、FastAPI、uvicorn、pytest 等依赖都正常安装。

接下来确认 CUDA:

python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'CPU')"

如果 CUDA 可用,就可以直接使用 GPU 训练。


六、CNN 模型并不是假的

项目中真正定义了一个用于 MNIST 的卷积神经网络。

核心思路是:

28×28 灰度图
↓
Conv
↓
ReLU
↓
Pooling
↓
Conv
↓
ReLU
↓
Pooling
↓
Flatten
↓
Fully Connected
↓
10 类输出

最终输出:

0 1 2 3 4 5 6 7 8 9

十个类别对应的 logits。

经过 Softmax 后,就可以得到十个数字对应的概率。

也就是说,网页里最终看到的:

预测结果:7
置信度:98%

并不是写死的。

而是真正经过 CNN 前向传播得到的。


七、模型训练和 Web 服务是分开的

这一点我特别要求 Agent 处理得更规范一些。

没有采用:

启动网页时发现没有模型 → 自动训练

这种方式。

而是把训练单独放在:

scripts/train_model.py

本地训练:

python -m scripts.train_model

训练完成后生成:

app/weights/mnist_cnn.pth

然后 Web 服务只负责:

加载权重
↓
接收输入
↓
模型推理
↓
返回结果

这样项目逻辑会清晰很多。

如果权重不存在,系统会直接提示:

模型权重不存在,请先运行 python scripts/train_model.py

而不是偷偷返回一个随机结果。


八、实际案例:自己手写一个数字

模型训练完成以后,启动 Web 服务:

python -m uvicorn app.main:app --reload

浏览器访问:

http://127.0.0.1:8000

页面提供了一个手写画布。

可以直接用鼠标写数字。

例如我手写一个:

7

然后点击:

开始识别

后端会完成:

Canvas
↓
图像数据
↓
灰度处理
↓
28×28
↓
归一化
↓
Tensor
↓
CNN
↓
Softmax

最后返回预测结果。

这个案例和前面的 OpenCV 项目相比,最大的区别就是:

页面背后真的有一个训练过的神经网络。


九、0~9 十个类别概率可视化

除了最终预测数字,我还要求页面展示全部类别概率。

比如:

0   0.1%
1   0.3%
2   0.2%
3   0.5%
4   0.1%
5   0.2%
6   0.1%
7   98.1%
8   0.3%
9   0.1%

这样用户不只能看到:

“模型认为这是 7”

还可以看到:

“它有多确定?”

这个其实很适合初学者理解分类模型。

因为神经网络并不是直接输出一个:

7

而是输出一组分数。

最后通过 Softmax 转换为类别概率。


十、模型信息也直接展示出来

页面还会显示一些基础模型信息,例如:

  • 模型名称
  • 输入尺寸
  • 输出类别
  • 参数量
  • 权重加载状态

这样这个项目不只是一个“数字识别小游戏”。

它更像一个小型神经网络实验平台。

用户可以看到:

模型是什么、输入是什么、输出是什么、最后又是怎么得到预测的。


十一、真实训练环境下的资源占用

这次我是在 RTX 4090 环境下运行。

训练过程中可以通过:

nvidia-smi

查看 GPU 状态。

例如:

GPU: NVIDIA GeForce RTX 4090
显存: 24GB
CUDA: 13.0

不过 MNIST 本身很小。

这种 CNN 即使使用 CPU,也可以比较快完成训练。

所以这个项目并不要求高端显卡。

这一点也比较适合作为深度学习入门项目。


十二、这个项目目前完成了什么?

目前 Neural Network Playground 第一版已经实现:

功能状态
Web 手写画布✅
清空 / 重写✅
PyTorch CNN✅
MNIST 训练脚本✅
模型权重保存✅
模型加载✅
28×28 图像预处理✅
CNN 推理✅
0~9 分类✅
Softmax 概率✅
概率可视化✅
模型信息展示✅
权重缺失提示✅
FastAPI 后端✅
README✅
Git 推送✅

项目地址:

https://atomgit.com/chen_jixi/cnn-mnist-playground


十三、从 OpenCV 到 CNN,最大的变化是什么?

做到这里以后,我觉得区别已经非常明显。

OpenCV 项目主要是:

规则和算法驱动。

CNN 项目则变成:

数据和模型驱动。

OpenCV:

输入
↓
固定算法
↓
输出

CNN:

训练数据
↓
学习参数
↓
模型权重
↓
新输入
↓
模型推理
↓
预测结果

所以项目工程里也多出了:

  • 数据集
  • 训练脚本
  • 模型文件
  • 权重
  • 推理
  • GPU / CPU
  • 模型加载状态

这也是从传统计算机视觉进入深度学习以后,一个很明显的变化。


十四、下一篇准备继续做什么?

现在这个项目已经能识别数字了。

但还有一个很有意思的问题:

CNN 到底看到了什么?

我们能看到最终结果:

7

但是卷积层中间到底提取了什么特征?

下一篇我准备继续让码道在这个项目上加入:

Feature Map 可视化。

把:

原始数字
↓
第一层卷积特征图
↓
第二层卷积特征图
↓
最终分类

直接展示出来。

也就是说,下一篇不只是看:

CNN 预测了什么。

而是进一步看:

CNN 是怎么一步一步“看”这个数字的。

更多推荐