学习笔记:OpenCV与PyTorch图像处理全攻略

🧩 第一部分:图像处理与可视化(OpenCV + Matplotlib)

1. 颜色空间的"语言不通"问题
  • 是什么cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 是颜色编码转换器。

  • 有什么用 :OpenCV 内部存储是 BGR (蓝绿红),而 Matplotlib/PIL/网页是 RGB(红绿蓝)。如果不转换,用 plt 显示时颜色会严重失真(蓝色变红色)。

  • 为什么这样设计(易错) :OpenCV 诞生于 90 年代的 C 语言底层优化,硬件早期偏爱 BGR 排列,这形成了历史包袱。易错点 :如果你先用 cv2.imread 读图,直接丢给 plt.imshow,必蓝;务必在显示前 cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

2. Matplotlib 布局与直方图调参
  • plt.subplot(1, 2, 1) :画布分割器。作用:1行2列,激活第1个位置(左半边)。

  • plt.xlim([0, 256]) :X轴锁定。作用 :强制直方图的 X 轴显示 0~255(像素全范围)。易错:图像直方图不设 xlim 时,若像素集中在暗部,坐标轴会自动缩放到局部,导致视觉对比误导。

  • plt.grid(alpha=0.3) :网格辅助线。alpha=0.3 控制半透明,防止喧宾夺主。

3. 路径与布局防重叠
  • os.getcwd() :获取当前 Python 解释器运行的工作目录(非脚本所在目录)。易错 :在 Jupyter 中它返回启动目录;在 PyCharm 中返回项目根目录。写文件路径时建议用 os.path.join(os.getcwd(), 'data') 保证跨平台。

  • plt.tight_layout() :自动调整子图间距,杜绝标题与坐标轴重叠。必须放在 plt.show() 之前


🔢 第二部分:PyTorch 张量核心操作(张量维度与梯度切割)

1. 维度操控(AI模型的"形状规矩")
  • unsqueeze(0) :在第 0 维插入一个维度,大小为 1。作用 :将 [C, H, W] 的单张图变成 [1, C, H, W] 的 Batch。因为 PyTorch 所有网络(如 ResNet)都强制要求输入为 4 维 (N, C, H, W)

  • tensor.dim() :返回张量维度数量(秩)。易错 :区分 size()(形状元组)和 dim()(维度个数,如标量是 0,向量是 1)。

2. 梯度切割三兄弟(防爆显存与防误传)
操作 作用范围 核心本质 典型场景
.item() 仅限标量张量 提取 Python 数值,切断计算图 打印 Loss 数值。
.detach() 单个张量 返回新张量,共享数据内存 ,但 requires_grad=False 提取目标特征图(target)用于 Loss 计算,但防止梯度更新目标。
torch.no_grad() 上下文代码块(缩进内所有代码) 关闭整个 Autograd 引擎,不追踪任何操作,省显存提速。 验证集评估、推理、提取固定特征。
.clone() 单个张量 深拷贝数据内存 ,完全独立(配合 .detach() 使用)。 将 GPU 上的模型输出转为 CPU 图片,防止后续原地操作污染原梯度。

💡 致命易错

  • detach() 只是切断了梯度追踪,依然和原张量共享内存 。如果你修改 detach() 后的数据,原张量也会变(虽然原地操作会报错)。

  • no_grad() 是上下文管理器,必须用 with 缩进。若忘记加,验证集前向传播会构建计算图,导致显存爆炸(OOM)。


🧠 第三部分:模型构建(torch.nn 宇宙全解)

(对应你超长的 nn 模块笔记)

1. 三大核心容器(骨架)
  • nn.Module :所有模型的祖宗。必须继承它,并在 __init__ 定义层,forward 写计算逻辑。

  • nn.Sequential :流水线作业。适合串行结构(如 Conv -> BN -> ReLU)。局限:无法处理残差连接(ResNet)或循环拼接(RNN)。

  • nn.ModuleList / ModuleDict :用于存放多个层,方便 for 循环遍历(如 Transformer 的 12 层解码器)。注意 :它只是个列表,不会自动传递数据 ,你必须手写 for layer in self.layers: x = layer(x)

2. 线性层与循环层(NLP/时序核心)
  • nn.Linear(in, out) :全连接层,做 y = xA^T + b

  • nn.RNN / nn.LSTM / nn.GRU :处理序列。输入要求 (seq_len, batch, features)

  • 易错点nn.GRUnn.RNN 默认 batch_first=False。如果你习惯传 (batch, seq, feature),必须设置 batch_first=True,否则形状会错乱。

3. 卷积层(DCGAN 上采样秘技)
  • nn.ConvTranspose2d(转置卷积) :用于上采样(放大图像)。黄金参数kernel_size=4, stride=2, padding=1 实现尺寸完美翻倍(输入 1x1 → 输出 4x4)。

  • nn.PixelShuffle(像素洗牌) :另一种上采样方式,将通道数压缩为空间分辨率。对比:转置卷积易产生"棋盘格伪影",PixelShuffle 更平滑,常被用于超分辨率(ESRGAN)。

4. 激活函数与归一化(稳定训练的根基)
  • 激活选型

    • ReLU :最常用,max(0,x),但易导致神经元"坏死"(负数永远变 0)。

    • LeakyReLU:给负数微小斜率(如 0.01),防坏死。

    • Tanh:输出 -1~1,DCGAN 生成器最后一层强制用 Tanh。

  • 归一化选型

    • BatchNorm :依赖批次(Batch),CV 任务首选。易错 :训练时用 model.train()(统计当前 batch 均方差),验证时必须 model.eval()(使用全局滑动均方差),否则结果崩盘。

    • LayerNorm:不依赖批次,Transformer/NLP 用得多。

5. 损失函数(判分标尺)
损失函数 内部是否带 Softmax 输入要求 结论
nn.CrossEntropyLoss (自带 LogSoftmax) 原始分数(Logits),任意实数 工业界标准,一步到位,防数值溢出。
nn.NLLLoss 必须已是 对数概率log_softmax 输出,负数) 99% 场景不单独使用。若忘了在外面加 log_softmax,Loss 会变成负无穷,训练彻底失败。

⚖️ 第四部分:权重初始化哲学(决定能否收敛)

  • nn.init.normal_(weight, 0.0, 0.02):高斯初始化。DCGAN 的标准做法(均值 0,标准差 0.02)。

  • nn.init.constant_(bias, 0):偏置置零。因为 BatchNorm 自带 β 偏置,卷积层的 bias 可以安全设为 0。

核心对比:Kaiming vs 正交初始化

对比维度 Kaiming 初始化 (kaiming_normal_) 正交初始化 (orthogonal_)
保护对象 保护信号方差(防止梯度消失/爆炸) 保护向量长度(范数)(防止方向扭曲)
最佳搭档 CNN + ReLU(视觉任务绝对王者) RNN / LSTM(处理极长序列) / StyleGAN 映射网络
实战铁律 视觉任务无脑选 Kaiming。 若你的网络深度 > 50 层且全是全连接,正交是救命稻草。

易错:初始化并非越随机越好。标准差设为 0.02 是为了配合 ReLU 的稀疏性;若标准差太大(如 0.1),深层输出会迅速发散为 NaN。


🔄 第五部分:训练机制(从手动求导到自动微分)

1. 底层透视(Numpy 手写 vs PyTorch 自动)
  • np.random.randn:生成标准正态分布(均值 0,方差 1)占位数据。

  • x.dot(w1)(矩阵乘法) :线性变换,改变形状(如 64x1000 → 64x100)。

  • np.maximum(h, 0)(ReLU) :非线性激活,不改变形状,只修剪负数。

  • 手动反向传播 :必须按前向的严格逆序 手写链式法则(先算 grad_w2,再截断 ReLU 梯度,最后算 grad_w1)。极其繁琐且易错

  • PyTorch Autograd :只需 loss.backward(),框架自动构建计算图并填充 .grad易错 :PyTorch 默认累加梯度 (为了支持大 batch 拆分为小 batch 模拟),每次 optimizer.step()必须 optimizer.zero_grad(),否则梯度会叠加爆炸。

2. 数据管道(DataLoader)
  • shuffle=True :训练集开启,打乱顺序防止模型记忆样本;验证集必须 False,保持可复现性。

  • num_workers :多进程读取。Windows 致命坑 :必须把 DataLoader 放在 if __name__ == '__main__': 下,否则会递归创建进程导致死锁。

3. 优化器选择(LBFGS vs Adam)
  • Adam / SGD:一阶梯度下降,适合大规模非凸问题(深度学习 99% 场景)。

  • LBFGS(拟牛顿法) :二阶方法,收敛极快,但内存占用巨大 ,且必须传入 closure 函数(多次重新计算 Loss)。仅限 :小批量、平滑凸问题(如逻辑回归、传统稀疏编码)。深度学习慎用


🚀 第六部分:模型导出与部署(ONNX + TorchScript)

1. ONNX(AI界的"通用翻译官")
  • 是什么 :开放神经网络交换格式,存储为 .onnx 二进制文件(Protobuf 序列化)。

  • 有什么用:打破 PyTorch 和 TensorFlow 的壁垒。你可以用 PyTorch 训练,导出 ONNX,再用 NVIDIA TensorRT 或 ONNX Runtime 在 C++ 环境下极速推理。

  • 易错 :ONNX 不支持动态控制流 (如 if 或变化长度的循环)以及自定义算子。如果你的模型里有 nn.GRUbatch_first=True,ONNX 导出时可能会产生冗余的 Transpose 节点,需用 opset_version 控制。

2. TorchScript(JIT) vs 普通 torch.save
对比维度 torch.save (研究态) torch.jit.save / torch.jit.load (生产态)
包含内容 仅权重(state_dict 结构(计算图)+ 权重 融为一体
运行时依赖 必须有 原始 Python 类定义(class Net 完全脱离 Python,可在 C++ / Java / iOS 运行
灵活性 极高(支持动态图修改) 冻结为静态图,无法改动
获取对象 nn.Module 子类实例 torch.jit.ScriptModule(不再依赖源码)

易错torch.jit.trace 只能跟踪输入路径下的操作,若模型中有 if data.sum() > 0 这种数据依赖分支,trace 会失效,必须改用 torch.jit.script(编译 Python AST)。


🎯 第七部分:DCGAN 专项细节

  • weights_init 遍历陷阱model.apply(weights_init) 会递归寻找子模块。若用 find('Conv') 字符串判断,漏掉 Linear 全连接层 。若生成器有全连接映射网络,需手动补全 elif isinstance(m, nn.Linear)

  • 转置卷积输出计算公式Output = (Input - 1) * Stride - 2 * Padding + Kernel_Size。套用 k=4, s=2, p=1,输出正好翻倍(例如 1 -> 4 -> 8 -> 16 -> 32 -> 64)。

  • Tanh 输出层 :生成器末尾必须用 Tanh,且 bias=False(因为紧跟着的 BatchNorm 自带 β,再设 bias 会冗余)。易错 :输入噪声通常是 100 维向量,必须 unsqueeze(2).unsqueeze(3) 变成 (batch, 100, 1, 1) 才能喂给转置卷积。


⚠️ 最终"易错急救清单"(金句总结)

  1. 颜色:OpenCV 读图是 BGR,显示用 Matplotlib 记得转 RGB。

  2. 维度 :任何预训练模型输入都必须是 4 维 (N,C,H,W),单张图必须 unsqueeze(0)

  3. 梯度 :验证集推理必须with torch.no_grad(),否则显存泄漏。

  4. 清零optimizer.step() 之前 必须 optimizer.zero_grad()

  5. 模式切换 :含有 BN 或 Dropout 的模型,验证/测试时必须 model.eval(),训练时 model.train()

  6. 损失 :用了 CrossEntropyLoss不要再手动加 Softmax,否则双重激活导致梯度消失。

  7. 导出 :生产部署用 torch.jit.save 摆脱 Python 依赖;跨框架用 ONNX。