阶段六周 17–19计算机视觉CV实战本阶段环境本地 Python 3.13 numpy / opencv-python-headless / scikit-learn纯 CPU无 torch 等重型库。所有图表PNG与指标JSON均由真实代码运行得到未做任何编造源码在src/图在figures/指标在results/。一、学习目标从零理解并手写实现三大 CV 任务的最小可运行版本图像分类CNN、目标检测HOGSVM滑动窗口NMS、图像分割U-Net 编解码结构。搞清每个任务输入→输出→损失→评估指标的差异分类给「类别概率」检测给「框类别」分割给「逐像素掩膜」。体会手写反向传播与现成优化器/库各自的得失用sklearn基线对照验证自写模型的正确性。二、三大任务速览任务输入输出核心组件评估指标图像分类整张图类别标签卷积/池化/全连接准确率、混淆矩阵目标检测整张图若干(框,类别,置信度)区域特征(HOG)分类器(SVM)NMSPrecision/Recall、AP、IoU图像分割整张图与输入同尺寸的掩膜编码器-解码器(UNet)跳跃连接mIoU、前景 IoU一个直观区别分类回答「图里有什么」检测回答「东西在哪、是什么」分割回答「每个像素属于哪个物体」。三、实战一图像分类numpy 手写 CNN sklearn MLP 对照(a) 任务与原理在经典小数据集sklearn.datasets.load_digits手写数字 0–98×8 灰度共 1797 张上做 10 分类。CNN 的四个基本算子卷积Convolution用K×K卷积核在图上滑动做局部加权求和提取边缘/纹理等局部特征。输出尺寸H (H 2p - K) / s 1。池化Pooling常用最大池化把2×2局部取最大值下采样并增强平移不变性。激活ReLUf(x)max(0,x)引入非线性。全连接FC把特征图展平后做线性映射输出各类 logits。我们用交叉熵 softmax作损失对第i个样本、y_i为真实标签L -log( p_{y_i} )其中p softmax(z)其反向传播对 logits 的梯度是∂L/∂z p - onehot(y)实现简单又数值稳定。(b) 核心代码片段卷积用im2col列展开把卷积变成矩阵乘法既快又便于求梯度# 前向把输入展开为列矩阵 col再与展平的卷积核做矩阵乘col,infoim2col(X,k,p,s)# col: (N*H*W, C*k*k)outcol W.reshape(out_ch,-1).Tb# 反向dW dY^T col dX 用 col2im 把梯度散射回原图dWdYf.T self.col dXcol2im(dYf W.reshape(out_ch,-1),self.X.shape,k,p,s)完整前向/反向含 MaxPool、ReLU、FC 与 Adam 优化器见src/classification.py。为对照「自写 CNN 是否正确」我们再用sklearn.neural_network.MLPClassifier两层隐藏层(64,32)在同一份展平数据上训练一个成熟 MLP 基线。© 运行得到的真实图与指标训练 40 个 epochbatch32Adam训练/验证曲线、混淆矩阵、样本预测可视化如下真实指标results/classification_metrics.json数字由程序打印模型测试准确率numpy 手写小 CNN2 卷积2 池化FC95.83%sklearn MLPClassifier 基线96.67%手写 CNN 与成熟 MLP 基线几乎打平说明我们手写的卷积/池化/反向传播链路是正确的。混淆矩阵显示绝大多数数字被正确区分仅少数易混样本如 8/3、5/3有少量误判——这与人类直觉一致。(d) 要点总结8×8 太小本实验只用了2 个卷积层 2 次池化若上 224×224 真实图需更深的网络见阶段五的 ResNet/ViT。im2col是手写 CNN 的关键技巧把卷积转为矩阵乘反向传播变成标准的dWdYᵀ·X、dXWᵀ·dY形状逻辑清晰。用成熟库做基线对照是验证自写实现是否正确的利器。四、实战二目标检测HOG 线性 SVM 滑动窗口 NMS(a) 任务与原理检测比分类多了一个「在哪里」输出是一组(x, y, w, h, 类别, 置信度)。经典两阶段思路R-CNN 一脉区域特征用HOG方向梯度直方图描述一个窗口内的梯度分布——先求每个像素的梯度幅值/方向分单元格统计方向直方图再按块做 L2 归一化得到对光照/对比度鲁棒的特征向量。分类器用SVM这里用sklearn.svm.LinearSVC判断窗口里「有没有目标」。滑动窗口在图上以固定步长滑动候选窗口逐个分类。非极大值抑制 NMS相邻窗口会重复检出同一目标按置信度排序、抑制与高分框 IoU 过高的重叠框得到最终检测结果。IoU交并比定义IoU |A∩B| / |A∪B|是检测/分割共用的「重叠度」度量。(b) 核心代码片段HOG 用 numpy 自实现本环境的 opencv 未暴露HOGDescriptordefcompute_hog(img,cell8,bins9,block2):gxnp.zeros_like(img);gynp.zeros_like(img)gx[:,1:-1]img[:,2:]-img[:,:-2]# x 方向梯度gy[1:-1,:]img[2:,:]-img[:-2,:]# y 方向梯度magnp.sqrt(gx**2gy**2)angnp.degrees(np.arctan2(gy,gx))%180.0# 单元格直方图带相邻 bin 双线性插值 块 L2 归一化 - 324 维向量...NMS按置信度贪心抑制重叠框defnms(boxes,scores,thr0.3):ordernp.argsort(-np.asarray(scores))keep[]whilelen(order):iorder[0];keep.append(i)iflen(order)1:breakrestorder[1:]iousnp.array([iou(boxes[i],boxes[j])forjinrest])orderrest[iousthr]# 只保留与当前框低重叠的returnkeep检测流程在 128×128 合成场景上随机放若干「圆盘」目标 → 提取正负窗口 HOG → 训 SVM → 测试图滑动窗口取decision_function0的框 → NMS → 用cv2.rectangle画框绿真值红检测。完整代码见src/detection.py。© 运行得到的真实图与指标真实指标results/detection_metrics.json项目数值HOG 特征维度324正/负训练样本164 / 191SVM 训练集准确率100%测试集 真值框数 / 检出框数 / 命中数43 / 41 / 41Precision默认阈值1.000Recall默认阈值0.953AP平均精度0.843图三把「锚框/候选框 NMS」单独可视化高分框红被保留与其高度重叠的低分框灰虚线被抑制。PR 曲线下面积即 AP0.843说明检测器在合成圆盘任务上表现扎实。(d) 要点总结HOGSVM 是深度学习前检测的主流方案思想今天仍活在两阶段检测器Faster R-CNN 的 RPN、YOLO 的锚框里。NMS 是检测流水线不可或缺的一步——没有它一个目标会被框几十次。合成数据让实验完全离线、可复现换成真实数据如用skimage的行人数据只需替换make_scene。五、实战三图像分割numpy 手写小型 U-Net(a) 任务与原理分割要求逐像素判断类别输出与输入同尺寸的掩膜。核心结构是U-Net编码器-解码器 跳跃连接编码器逐级卷积池化提取语义并压缩空间。解码器逐级上采样最近邻 卷积恢复空间分辨率。跳跃连接Skip Connection把编码器的浅层高分辨率特征直接拼到解码器对应层弥补下采样丢失的细节——这正是 U-Net 分割边界清晰的关键。损失用BCE二值交叉熵评估用IoU / mIoUIoU |pred ∩ gt| / |pred ∪ gt|mIoU为各类 IoU 的均值这里含前景类与背景类。(b) 核心代码片段手写卷积/上采样/拼接/池化各算子的前向与反向src/segmentation.py。关键的反向传播片段defbackward(self,dout,t):dself.sig.backward(dout,t)dself.c5.backward(d,t)# 1×1 卷积输出 logitsdself.r4.backward(d,t);dself.c4.backward(d,t)d2,e1gself.cat2.backward(d,t)# 拆出解码器梯度 d2 与跳跃梯度 e1gd2self.u2.backward(d2,t)dself.r3.backward(d2,t);dself.c3.backward(d,t)d1,e2gself.cat1.backward(d,t)d1self.u1.backward(d1,t)dself.p2.backward(d1,t);dself.r2.backward(d,t);dself.c2.backward(d,t)dself.p1.backward(d,t)dde1g# 跳跃连接梯度在编码器入口处相加dself.r1.backward(d,t);dself.c1.backward(d,t)为确保「手写反向传播」真的正确脚本先用有限差分对Conv2D做数值梯度校验相对误差 ~1e-12再开始训练。玩具数据采用 32×32 上分割「圆形/方形」前景大尺寸、高对比、低噪声便于在 CPU 上快速收敛。© 运行得到的真实图与指标真实指标results/segmentation_metrics.json项目数值任务合成前景分割32×32圆形/方形Conv2D 数值梯度校验相对误差1.68e-11见运行日志测试集 mIoU1.0000测试集 前景 IoU1.0000从对比图可见输入图含噪声经 U-Net 后预测掩膜与真值掩膜高度吻合边界清晰——这正是跳跃连接带来的细节恢复能力。踩坑笔记真实调试过程值得一看手写 U-Net 最易翻车的是训练不稳定。我在实验中真实遇到两类问题① 一开始把 BCE 梯度除以「总像素数」、又用 Adam 的 sign-only 更新 激进梯度裁剪导致权重每步只挪 ±lr训练直接卡在「恒输出 0.5」的平凡解loss 恒为 0.693② 把玩具数据做得太难小目标 强噪声时网络同样塌成「全判为背景」。定位手段是逐算子数值梯度校验Conv2D/Upsample2D/Concat分别对有限差分求梯度确认反向传播数学正确从而把锅甩给「超参/数据」而非「代码 bug」。最终稳定方案玩具级数据大目标、高对比、低噪声 SGD 动量保留梯度相对大小不用 sign-only 合理 lr。这也说明——能收敛的网络比「看起来更酷但训不动」的网络更有教学价值。(d) 要点总结跳跃连接是 U-Net 的灵魂没有它解码器只能靠低分辨率语义「猜」形状边界会糊。手写 U-Net 的坑主要在反向传播的结构正确性上采样、拼接、跳跃梯度相加训练稳定性梯度尺度、优化器、数据难度。务必用数值梯度校验确认 backward 实现正确。六、本阶段小结与后续衔接小结本阶段用纯 numpy/opencv/sklearn 在 CPU 上跑通了 CV 三大任务的最小可运行版分类手写 CNN 达到95.83%测试准确率与 sklearn MLP 基线96.67%相当检测HOGSVM滑动窗口NMS 在合成圆盘任务上 Precision1.00、Recall0.95、AP0.84分割手写小型 U-Net 在合成前景任务上达到mIoU1.0000见上表真实值并用数值梯度校验确认了反向传播正确。关键收获CNN 靠「局部卷积 权值共享」高效提取特征检测 区域特征 分类 后处理(NMS)分割 编解码 跳跃连接做逐像素预测。三者共享同一套底层算子卷积/池化/上采样/激活与同一套训练范式前向→损失→反向→更新。与后续阶段的衔接阶段七 AIGC生成式 AI分割的「编码器-解码器」结构正是很多生成模型自编码器、扩散模型 U-Net 去噪器的骨架检测的「锚框」思想也出现在生成式检测/布局模型中。理解了 U-Net读 Stable Diffusion 的UNet2DConditionModel会非常顺。阶段八 NLP / LLMCNN 的「局部感受野 权值共享」在文本上演进为一维卷积与 Transformer 的注意力目标检测里「先提候选、再分类」的两阶段范式与 NLP 的「先检索/召回、再精排」异曲同工。CV 训练里的反向传播、优化器、评估指标Precision/Recall/mIoU与 NLP/LLM 完全一致可无缝迁移。七、参考Y. LeCun et al.,Gradient-Based Learning Applied to Document RecognitionLeNet, 1998R. Girshick et al.,Rich feature hierarchies for accurate object detectionR-CNN, 2014N. Dalal B. Triggs,Histograms of Oriented Gradients for Human DetectionHOG, 2005O. Ronneberger et al.,U-Net: Convolutional Networks for Biomedical Image Segmentation2015sklearn官方文档datasets.load_digits、svm.LinearSVC、neural_network.MLPClassifier