这次我们来看一门课而不是一个“一键部署的工具”。哥伦比亚大学的公开课“计算机视觉第一原理”First Principles of Computer Vision第十二讲主题是神经网络。这一讲在整门课里的位置很关键前几讲还在讲成像、特征提取、传统几何视觉到这一讲开始进入神经网络后续的卷积网络、视觉识别任务都会建立在它的基础上。如果你学过图像处理也看过不少深度学习实战但总感觉“特征提取、分类器、神经网络”这些概念之间是断开的那这一讲正好可以把链路补上。很多同学学计算机视觉会直接跳到“怎么调 API”“怎么微调大模型”这本身没问题。但一旦遇到模型效果不好、需要改进网络结构或设计损失函数缺少原理支撑会非常被动。这门课的价值就在于它从“第一原理”出发把神经网络是怎么被设计出来、为什么能工作、梯度是怎么传下去的讲清楚。第十二讲不是让你从零手写一个深度学习框架而是帮你把前馈神经网络、激活函数、损失函数、反向传播这些基础扎牢再往卷积神经网络和更复杂的视觉模型走就会顺很多。这篇文章会围绕第十二讲做两件事第一整理这一讲的关键知识脉络给出明确的学习路线第二提供一套直接能跑的 PyTorch 实验方案从多层感知机到 MNIST 手写数字识别再到一个简单 CNN 对照让概念落到代码上。内容偏学习笔记和实验指南适合准备系统学习计算机视觉的本科生、研究生以及想补齐深度学习基础的工程师。1. 核心能力速览项目说明课程名称计算机视觉第一原理First Principles of Computer Vision第十二讲课程来源哥伦比亚大学公开课本讲主题神经网络基础前馈网络、激活函数、损失函数、反向传播、梯度下降内容范围从传统特征分类器过渡到神经网络为卷积神经网络打基础适合人群计算机视觉入门者、准备系统学 CV 基础的研究生/工程师先修知识线性代数、微积分、概率基础Python 基础实验环境要求不强制 GPUCPU 即可跑 MNIST 小模型Windows/macOS/Linux 均可建议工具Python、NumPy、PyTorch、Jupyter Notebook学习方式视频 讲义 代码复现结合版权边界课程资源版权归哥伦比亚大学学习用途需合法获取公开分享笔记需注明来源从表格可以看到这一讲对硬件几乎没有什么门槛。学习神经网络原理和跑最基础的手写数字识别实验用普通笔记本的 CPU 就能完成。如果已经有一块支持 CUDA 的显卡训练速度会更快但并不影响理解核心概念。2. 适用场景与使用边界先明确这个课程适合谁。最直接的一类读者是“学过图像处理和机器学习基础但还没系统学过深度学习”的计算机视觉学习者。传统视觉流程通常是 SIFT/HOG 等特征 SVM/随机森林分类器而神经网络会把“特征提取”这件事也交给网络自己完成。如果你在做毕业设计、课题入门或者在准备面试时被问到“卷积神经网络为什么比全连接网络更适合图像”这一讲的帮助会非常大。另一类适合的是“做过不少深度学习项目但基本都是调用现成模型”的工程师。这类人通常知道怎么用 ResNet、怎么训练 YOLO但对反向传播、梯度消失、激活函数选择背后的原因并不完全清楚。第十二讲的价值是把这些基础概念重新梳理一遍查漏补缺。需要提醒的是这不是一门“零基础 Python 入门课”。如果你完全没写过代码也没有线性代数和基础微积分基础建议先补一下相关基础知识再看否则看到矩阵运算、求导和梯度更新时容易卡住。使用边界也要说清楚。这门课是大学公开课视频、讲义、作业等资源版权归哥伦比亚大学所有学习时要从官方或授权渠道获取。后续做实验时MNIST、CIFAR-10 这类公开数据集有各自的数据许可通常可以用于学术研究和学习但如果在商业项目中直接使用需要确认数据集的许可条款。另外如果后续把网络用于人脸识别、声音克隆、图像生成等方向涉及真实人物肖像、隐私或版权素材时必须确认已获得合法授权不能拿来随意训练或发布。3. 学习环境准备与前置条件这一讲的环境准备非常简单。学习过程中需要三样东西Python 环境、PyTorch、一个 Notebook 或脚本运行环境。如果本机已经装过 Anaconda可以直接创建一个虚拟环境。没有的话用 venv 也可以。下面是一个通用安装过程# 创建并激活虚拟环境Windows 用 activate python -m venv cv_env # Linux/macOS source cv_env/bin/activate # Windows cv_env\Scripts\activate # 安装基础依赖 pip install numpy matplotlib torch torchvision jupyterTorch 和 TorchVision 的版本不需要追求最新优先选择当前稳定版本即可。如果本机有 NVIDIA 显卡想用 GPU 训练需要确认显卡驱动和 CUDA 版本匹配。PyTorch 官网会根据 CUDA 版本生成对应的安装命令建议直接去官网选择合适版本不要随便复制老教程里的命令。如果不想配置本机环境也可以用 Google Colab 等在线 Notebook 服务MNIST 小实验在免费额度内能跑完。CUDA 是否可用可以用下面的 Python 代码验证import torch print(PyTorch:, torch.__version__) print(CUDA available:, torch.cuda.is_available()) print(Device count:, torch.cuda.device_count())如果输出CUDA available: False说明当前环境无法使用 GPU但 CPU 跑 MNIST 多层感知机也完全可行不要因此卡住。磁盘空间方面MNIST 数据集本身只有几 MB学习阶段不需要担心占用。需要担心的是同时安装多个 PyTorch 版本或下载大型预训练模型那些才会占较多空间。4. 课程内容结构与学习路线第十二讲的内容如果拆开看实际上是从“传统视觉的痛点”一路推到“神经网络的基本训练方式”。不建议直接跳着看建议按下面这条线走。4.1 从传统特征到神经网络传统计算机视觉识别一张图像里的物体通常需要两步第一步是设计特征比如颜色直方图、纹理特征、SIFT 特征点第二步是把特征送到分类器里做分类。这里的核心问题是特征需要人来设计而“到底什么特征最好”很难回答。神经网络改变的不是分类器这一步而是让网络在训练过程中自己从数据里学习特征。第十二讲的第一层铺垫就是把这种“手工设计特征”和“数据驱动特征学习”的区别讲明白。理解这一点后面再看卷积网络里的卷积核就不会觉得它们是随机初始化的黑盒而是从数据里学出来的特征模板。4.2 神经元、感知机与激活函数神经网络的基本单元是“神经元”。一个神经元做的事情很朴素把输入做加权求和加上偏置再通过一个非线性激活函数输出。写成数学形式就是output activation(W * x b)其中W是权重b是偏置activation是激活函数。单个神经元能解决线性可分问题但现实里的视觉任务几乎没有线性可分的。要让网络具备非线性表达能力就需要在每层之间插入激活函数。第十二讲会重点讲几个常见激活函数Sigmoid、Tanh、ReLU。Sigmoid 输出范围是 0 到 1适合作为二分类输出但容易出现梯度饱和Tanh 输出范围是 -1 到 1均值为 0比 Sigmoid 更容易优化ReLU 是目前最常用的隐藏层激活函数计算简单且能缓解梯度消失问题。学习时要关注的不只是函数长什么样还包括它们的导数。因为反向传播本质上是链式求导激活函数的导数决定了梯度能不能顺利传到前面的层。4.3 前馈神经网络与层结构把很多个神经元按层组织起来就是前馈神经网络也叫多层感知机。最典型的结构是输入层、若干隐藏层、输出层。输入层接收数据隐藏层负责非线性变换输出层给出预测结果。第十二讲里的神经网络部分主线就是多层感知机。理解前馈网络时有两个关键问题需要想清楚。第一个是“为什么需要隐藏层”。没有隐藏层的单层感知机只能画一条直线解决不了 XOR 这类线性不可分问题加入一个隐藏层配合非线性激活函数就能逼近很多复杂函数。第二个是“加多少层、每层多少个神经元”。这个问题没有理论上的万能答案更多靠实验和任务约束。课程会给一些直观解释但真正的感觉要通过代码实验来建立。4.4 损失函数与梯度下降网络输出之后要判断输出和真实标签差多少就需要损失函数。第十二讲会讨论两种常见损失函数均方误差MSE和交叉熵Cross-Entropy。对分类任务来说交叉熵通常比均方误差更合适因为它在概率分布上衡量差异梯度变化也更符合分类场景。对回归任务均方误差更直观。有了损失函数训练目标就是找到一组参数让损失值尽量小。梯度下降是最核心的优化思想计算损失对每个参数的梯度然后沿着梯度的反方向更新参数。学习率决定更新步长太大容易震荡太小收敛太慢。第十二讲不会花大量篇幅讲优化器但会把梯度下降的基本形式讲清楚。后续要学 Adam、SGD 带动量等优化器也都是在这个思想上做改进。4.5 反向传播反向传播是神经网络训练的“发动机”。计算图从输入到输出是前向传播而反向传播是从损失函数出发利用链式法则把梯度从输出层逐层传回输入层。每一层都算出损失对当前层权重的梯度然后交给优化器做更新。第十二讲里会逐步推导反向传播的公式。学习时不要只背公式建议自己拿一个只有两三层的小网络从手写数字识别任务出发先手动算一遍中间变量的梯度再用 PyTorch 的loss.backward()验证结果。这样能把“反向传播只是链式法则”这句话真正变成自己的直觉。4.6 向卷积神经网络过渡多层感知机处理图像时最常见的做法是把图片展平成一维向量再把向量送入全连接层。这种方式有两个明显问题一是参数量巨大一张 28×28 的 MNIST 图片变成 784 维向量第一层全连接层如果有 256 个神经元就接近 20 万个参数二是丢失了图像的空间结构相邻像素的关系、边缘和纹理信息在展平过程中会变得混乱。卷积神经网络解决这两个问题的思路是保留图像的二维结构用局部连接和权值共享降低参数数量。第十二讲作为神经网络模块的一环会指出这条通路但不会一下子展开太多卷积细节。学习到这里最好已经清楚全连接网络和卷积网络的区别再进入下一讲就会轻松很多。5. 动手验证从公式到代码只看公式容易飘动手跑一个最小实验才能真正理解。这里给出三个由浅入深的实验多层感知机结构、MNIST 手写数字识别、简单 CNN 对照。代码基于 PyTorch不需要额外模型文件数据集由 TorchVision 自动下载。5.1 先理解输入输出结构先写一个最简单的多层感知机。这个模型接收 28×28 的灰度图输入维度 784隐藏层 128 个神经元输出维度 10。代码里的nn.Flatten()会把图像从[batch, 1, 28, 28]变成[batch, 784]。import torch import torch.nn as nn class MLP(nn.Module): def __init__(self, input_dim784, hidden_dim128, num_classes10): super().__init__() self.net nn.Sequential( nn.Flatten(), nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, num_classes) ) def forward(self, x): return self.net(x) model MLP() print(model)输出应该能看到四个组件Flatten、Linear、ReLU、Linear。运行后如果报错优先检查 PyTorch 是否安装成功。5.2 MNIST 手写数字识别完整训练脚本把模型定义和训练逻辑放在一起能直接跑通一个完整实验。import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from torchvision import datasets, transforms device cuda if torch.cuda.is_available() else cpu transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_set datasets.MNIST(./data, trainTrue, downloadTrue, transformtransform) test_set datasets.MNIST(./data, trainFalse, downloadTrue, transformtransform) train_loader DataLoader(train_set, batch_size64, shuffleTrue) test_loader DataLoader(test_set, batch_size256) class MLP(nn.Module): def __init__(self, input_dim784, hidden_dim128, num_classes10): super().__init__() self.net nn.Sequential( nn.Flatten(), nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, num_classes) ) def forward(self, x): return self.net(x) model MLP().to(device) optimizer optim.Adam(model.parameters(), lr1e-3) criterion nn.CrossEntropyLoss() for epoch in range(3): model.train() for images, labels in train_loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) optimizer.zero_grad() loss.backward() optimizer.step() model.eval() correct 0 total 0 with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) preds model(images).argmax(dim1) correct (preds labels).sum().item() total labels.size(0) print(fepoch {epoch 1}, test accuracy: {correct / total:.4f})如果按默认超参数完整训练 3 轮MNIST 测试集准确率通常可以达到 97% 左右。实际数值会受随机种子、数据下载状态和硬件影响不要因为一次结果偏高或偏低就怀疑代码。训练时注意观察 loss 变化如果 loss 一直不下降优先检查学习率是不是设置得过大或过小。5.3 换成简单 CNN 对照把 MLP 替换成一个小型 CNN观察卷积结构对图像任务的影响。class CNN(nn.Module): def __init__(self): super().__init__() self.features nn.Sequential( nn.Conv2d(1, 32, kernel_size3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Conv2d(32, 64, kernel_size3, padding1), nn.ReLU(), nn.MaxPool2d(2) ) self.classifier nn.Sequential( nn.Flatten(), nn.Linear(64 * 7 * 7, 10) ) def forward(self, x): return self.classifier(self.features(x)) model CNN().to(device)训练时只需把上一节脚本里的MLP()替换成CNN()。CNN 参数量和计算量更大训练速度会比 MLP 慢一些但通常准确率会更高。这一组对照实验非常值得做它用同一个数据集、同一套训练代码直接展示了“网络结构对效果的影响”。6. 模型接口与批量推理第十二讲本身不涉及部署 API但很多读者学完神经网络后会想把训练好的模型接到自己的工具里。这里给一个通用的模型导出和推理服务示例方便理解“训练”和“推理”之间的衔接。学习阶段不需要立刻做但提前知道如何把模型变成可调用接口会有帮助。训练完成后可以用下面的方式保存模型权重torch.save(model.state_dict(), mnist_mlp.pth)后面写一个 FastAPI 推理服务对外暴露 HTTP 接口。注意这是一个通用模板实际部署时要把模型类放在同一文件并确保输入数据格式正确。from fastapi import FastAPI from pydantic import BaseModel import torch import torch.nn as nn import uvicorn class MLP(nn.Module): def __init__(self, input_dim784, hidden_dim128, num_classes10): super().__init__() self.net nn.Sequential( nn.Flatten(), nn.Linear(input_dim, hidden_dim), nn.ReLU(), nn.Linear(hidden_dim, num_classes) ) def forward(self, x): return self.net(x) app FastAPI() class Item(BaseModel): data: list # 28x28 像素值列表长度 784 model MLP() model.load_state_dict(torch.load(mnist_mlp.pth, map_locationcpu)) model.eval() app.post(/predict) def predict(item: Item): x torch.tensor(item.data, dtypetorch.float32).reshape(1, 784) with torch.no_grad(): pred model(x).argmax(dim1).item() return {pred: pred} if __name__ __main__: uvicorn.run(app, host127.0.0.1, port8000)启动服务后可以用 curl 测试curl -X POST http://127.0.0.1:8000/predict \ -H Content-Type: application/json \ -d {data: [0.0, 0.1, ...]}批量推理更简单的做法是用DataLoader读取一个目录下的图片循环调用模型。实际工程里建议加日志、异常捕获和失败重试机制防止某一张图片格式异常导致整个批量任务中断。如果要在生产环境部署还建议用 ONNX Runtime 或 TensorRT 做加速但学习阶段不需要过早投入这些。7. 训练资源占用与性能观察跑神经网络训练时多观察资源占用能帮你判断模型是否适合当前硬件。对于 MNIST 这种小任务CPU 训练已经足够但如果你想在更大数据集上做实验就需要关注 GPU 显存和内存占用。观察显存占用可以用命令nvidia-smi -l 1如果 PyTorch 在 GPU 上运行还可以在代码里查看当前显存占用if torch.cuda.is_available(): print(torch.cuda.memory_allocated() / 1024**2, MB)资源占用和训练速度受几个因素影响。batch_size越大单次前向和反向传播处理的样本越多显存占用越高图片分辨率越高特征图尺寸越大显存开销越大模型网络层越深、通道数越多参数量和计算量越大。训练轮数epoch本身不影响单步显存占用但影响总训练时间。如果显存不足最简单的办法是调小batch_size比如从 64 改成 32 或 16。还可以降低输入分辨率但这可能会影响准确率。更进阶的做法是使用混合精度训练PyTorch 的torch.amp可以在支持相关硬件时降低显存占用并加速训练但学习阶段不必急着用。CPU 训练时瓶颈通常在计算而不是显存。观察 CPU 占用可以用任务管理器或top命令。如果分析后发现训练太慢优先考虑减小网络规模或换用更小的数据集做验证而不是直接把学习率调大。8. 常见问题与排查方法学习这一讲时最容易遇到的问题集中在环境、数据和训练效果三方面。下面是一张常见问题排查表。问题现象可能原因排查方式解决方案安装 PyTorch 失败网络原因或源不稳定查看 pip 报错信息使用国内镜像源或离线安装包CUDA 不可用驱动和 PyTorch 版本不匹配运行torch.cuda.is_available()到 PyTorch 官网选择匹配 CUDA 的安装命令MNIST 数据集下载失败网络无法访问数据集源查看下载日志手动下载数据集并放到./data对应目录loss 不下降学习率不合适、数据未归一化打印前几个 batch 的 loss调整学习率使用 ToTensor 和 Normalize显存不足batch_size 过大查看nvidia-smi显存占用减小 batch_size 或使用 CPU 训练测试准确率低模型结构简单或训练轮数不足观察训练集准确率增加隐藏层宽度、训练轮数或改用 CNN模型推理时端口被占用8080 或 8000 端口已有服务查看端口占用进程改用其他端口或关闭占用进程这里想特别强调“数据归一化”这个问题。很多初学者在 MNIST 上直接输入 0 到 255 的原始像素导致 loss 下降很慢。正确做法是先ToTensor()把像素缩放到 0 到 1再Normalize用数据集的均值和标准差做标准化。这一步对训练稳定性的影响通常比网络结构更大。9. 最佳实践与学习建议学完第十二讲不建议立刻去追最新的大模型或复杂网络结构先把基础动作练扎实。下面几条是这套学习路线里最值得注意的实践建议。第一先手动推导一遍反向传播。找一个人工神经网络输入层 2 个节点、隐藏层 3 个节点、输出层 1 个节点自己用求导公式算一遍梯度然后和 PyTorch 的backward()结果对比。做一次这样的练习比反复看十遍课件都更有用。第二写实验时固定随机种子。神经网络初始化是随机的如果不固定随机种子每次训练出来的准确率会有波动难以判断某个改动是否真的有效。在代码开头加一行torch.manual_seed(42)如果有 GPU还需要加torch.cuda.manual_seed_all(42)第三建立清晰的实验目录结构。建议把数据、模型、Notebook、日志分开避免所有东西堆在一个文件夹里。下面是推荐结构cv_learning/ ├── data/ # 数据集 ├── models/ # 模型权重 ├── notebooks/ # Jupyter Notebook ├── scripts/ # 训练脚本 ├── logs/ # 训练日志 └── outputs/ # 输出结果第四不要只看准确率这一个指标。训练时记录 loss 的变化观察训练集和测试集准确率的差距。如果训练集准确率高、测试集准确率低说明模型过拟合如果两个都低说明模型容量或训练方式有问题。第十二讲学完你可以先不做复杂实验但一定要养成记录训练过程的习惯。第五注意版权和数据合规。课程资源、公开数据集、开源代码都有各自的许可协议。学习用途一般没有问题但如果有一天你要把实验成果发布成博客、论文或商业产品务必备注来源并确认是否满足许可要求。涉及人脸、声音、私人图片的数据必须先获得明确授权再使用。10. 总结与下一步这一讲最值得掌握的是“手工设计特征到数据驱动特征学习”的转变以及前馈神经网络的基本训练框架。具体来说就是神经元和感知机的形式、激活函数的作用、损失函数和梯度下降的关系、反向传播如何把梯度逐层传回去。这套框架不只在第十二讲有用后面学卷积神经网络、循环神经网络、Transformer本质上都还在这个框架里。最容易踩的坑有两个。一个是只跑通代码但没推导公式导致后续调参时没有方向感另一个是没有控制超参数和随机种子实验结果不可复现很难判断某个改动到底有没有效果。建议学完这一讲后先把 MLP 和 CNN 在 MNIST 上的对照实验做完整再进入卷积神经网络的系统学习。下一步可以看两类内容一类是卷积神经网络的详细原理包括卷积层、池化层、感受野和经典结构另一类是更实用的训练技巧比如数据增强、正则化、学习率调度、优化器选择。手边可以准备 CIFAR-10 数据集它的图像尺寸比 MNIST 大类别也更多用来验证“模型结构对视觉任务的影响”会更明显。基础打牢之后再去看目标检测、图像分割、视觉生成等方向就不容易觉得全是黑盒了。