news 2026/1/29 22:05:02

卷积神经网络的引入4 —— 局部扰动与空间结构破坏下的鲁棒性验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
卷积神经网络的引入4 —— 局部扰动与空间结构破坏下的鲁棒性验证

在中等复杂度数据集(CIFAR-10)上,差距迅速拉大

到这里,一个重要问题浮现:

CNN 的优势到底来自“更大的数据集”还是来自“图像的空间结构”?

换句话说:是否即便不换更大的数据集,只要破坏空间结构,MLP 就会更吃亏?

本篇将从这一关键视角展开实验。

一、实验目标

本篇希望进一步回答:

🧪 1. 如果我们“破坏图像的局部结构”,MLP 与 CNN 谁更稳健?

🧪 2. 当图像遭遇“局部遮挡”“随机噪声”“随机擦除”等扰动,CNN 是否仍能保持较强泛化能力?

🧪 3. 既然 MLP 完全依赖全局平铺输入,空间破坏是否会对 MLP 造成毁灭性影响?

二、实验策略(不再更换数据集,而是更换扰动方式)

我们继续使用 CIFAR-10 —— 数据集本身不变。

但重点修改“输入图像”,通过注入不同类别的空间扰动,让模型面临真正的对抗性挑战。

本文采用一类典型扰动:

🔶 1. Random Erasing(随机擦除局部区域)

模拟遮挡:

随机挖掉图片中的一小块(如 16×16)

广泛用于训练 CNN 的增强策略

CNN 可通过周边区域补偿

MLP 因完全 flatten,会失去空间结构 → 特征被破坏

三、实验步骤

使用 CIFAR-10

训练 MLP 与 CNN(结构与上一章一致)

每种扰动分别训练 10 epoch,记录:

训练集精度

验证集精度

收敛速度

使用折线图展示 MLP vs CNN 的精度差异

四、核心实验代码(加入图像扰动增强)

以下为结构化 Demo,方便直接复现实验。

# -*- coding: utf-8 -*-

# 卷积神经网络的引入4 —— 空间扰动下的 MLP 与 CNN 鲁棒性对比实验

# Author: zhchoice

# Date: 2025-11-XX

import torch

import torchvision

import torch.nn as nn

import torch.nn.functional as F

from torch.utils.data import DataLoader

from torchvision import datasets, transforms

import matplotlib.pyplot as plt

import random

device = 'mps' if torch.backends.mps.is_available() else 'cpu'

# =============================

# 1️⃣ 扰动类型选择(重点)

# =============================

# 可选: none / erasing / gaussian / cutout

AUG_TYPE = 'erasing'

print(f"===> 使用扰动方式:{AUG_TYPE}")

# =============================

# 2️⃣ 定义扰动函数(核心部分)

# =============================

def add_gaussian_noise(img, mean=0.0, std=0.1):

noise = torch.randn_like(img) * std + mean

return torch.clamp(img + noise, -1, 1)

def cutout(img, size=16):

_, h, w = img.size()

y = random.randint(size, h - size)

x = random.randint(size, w - size)

img[:, y-size:y+size, x-size:x+size] = 0

return img

class CutoutTransform:

"""可在 transforms 中使用的 cutout 封装"""

def __call__(self, img):

return cutout(img)

class GaussianNoiseTransform:

"""高斯噪声封装"""

def __call__(self, img):

return add_gaussian_noise(img)

# =============================

# 3️⃣ 图像增强 pipeline 设置

# =============================

train_transforms = [

transforms.ToTensor(),

transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5)),

]

if AUG_TYPE == 'erasing':

train_transforms.append(transforms.RandomErasing(p=1.0, scale=(0.1,0.2)))

elif AUG_TYPE == 'gaussian':

train_transforms.append(GaussianNoiseTransform())

elif AUG_TYPE == 'cutout':

train_transforms.append(CutoutTransform())

train_transform = transforms.Compose(train_transforms)

test_transform = transforms.Compose([

transforms.ToTensor(),

transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))

])

# =============================

# 4️⃣ CIFAR10 数据加载

# =============================

trainset = datasets.CIFAR10('./data', train=True, download=True, transform=train_transform)

testset = datasets.CIFAR10('./data', train=False, download=True, transform=test_transform)

train_loader = DataLoader(trainset, batch_size=64, shuffle=True)

test_loader = DataLoader(testset, batch_size=256)

# =============================

# 5️⃣ 定义模型(沿用上一章)

# =============================

class MLP(nn.Module):

def __init__(self, input_dim=32*32*3, hidden=1024):

super().__init__()

self.net = nn.Sequential(

nn.Flatten(),

nn.Linear(input_dim, hidden),

nn.ReLU(),

nn.Linear(hidden, 10)

)

def forward(self, x):

return self.net(x)

class CNN(nn.Module):

def __init__(self, in_ch=3):

super().__init__()

self.net = nn.Sequential(

nn.Conv2d(in_ch, 32, 3, padding=1),

nn.BatchNorm2d(32),

nn.ReLU(),

nn.MaxPool2d(2),

nn.Conv2d(32, 64, 3, padding=1),

nn.BatchNorm2d(64),

nn.ReLU(),

nn.MaxPool2d(2),

nn.Conv2d(64, 128, 3, padding=1),

nn.ReLU(),

nn.AdaptiveAvgPool2d(1),

nn.Flatten(),

nn.Linear(128, 10)

)

def forward(self, x):

return self.net(x)

# =============================

# 6️⃣ 训练 & 验证

# =============================

loss_fn = nn.CrossEntropyLoss()

def train_one_epoch(model, loader, opt):

model.train()

tot_loss, tot_correct = 0, 0

for x, y in loader:

x, y = x.to(device), y.to(device)

out = model(x)

loss = loss_fn(out, y)

opt.zero_grad()

loss.backward()

opt.step()

tot_loss += loss.item()

tot_correct += (out.argmax(1) == y).sum().item()

return tot_loss / len(loader), tot_correct / len(loader.dataset)

def evaluate(model, loader):

model.eval()

tot_correct = 0

with torch.no_grad():

for x, y in loader:

x, y = x.to(device), y.to(device)

tot_correct += (model(x).argmax(1) == y).sum().item()

return tot_correct / len(loader.dataset)

# =============================

# 7️⃣ 实验执行

# =============================

mlp = MLP().to(device)

cnn = CNN().to(device)

opt_mlp = torch.optim.Adam(mlp.parameters(), lr=1e-3)

opt_cnn = torch.optim.Adam(cnn.parameters(), lr=1e-3)

epochs = 10

mlp_train, mlp_test = [], []

cnn_train, cnn_test = [], []

for ep in range(epochs):

_, acc_m = train_one_epoch(mlp, train_loader, opt_mlp)

_, acc_c = train_one_epoch(cnn, train_loader, opt_cnn)

val_m = evaluate(mlp, test_loader)

val_c = evaluate(cnn, test_loader)

mlp_train.append(acc_m)

cnn_train.append(acc_c)

mlp_test.append(val_m)

cnn_test.append(val_c)

print(f"[{ep+1}/{epochs}] MLP val={val_m:.3f} | CNN val={val_c:.3f}")

# =============================

# 8️⃣ 画精度曲线

# =============================

plt.figure(figsize=(10,6))

plt.plot(range(1,epochs+1), mlp_train, 'r--o', label='MLP Train')

plt.plot(range(1,epochs+1), mlp_test, 'r-', label='MLP Val')

plt.plot(range(1,epochs+1), cnn_train, 'b--o', label='CNN Train')

plt.plot(range(1,epochs+1), cnn_test, 'b-', label='CNN Val')

plt.title(f"Accuracy Curve under Perturbation: {AUG_TYPE}")

plt.xlabel("Epoch")

plt.ylabel("Accuracy")

plt.grid(True)

plt.legend()

plt.tight_layout()

plt.show()

五、训练结果表现

image

从本次 “Random Erasing(随机擦除)” 的实验结果中,我们能够观察到以下几个清晰结论:

CNN 在局部遮挡下的鲁棒性显著优于 MLP

从第 1 个 epoch 起:

CNN 验证集精度:0.426

MLP 验证集精度:0.437

两者相差不大。但随着训练继续进行,CNN 的表现开始快速提升:

最终 CNN val ≈ 0.660

而 MLP val ≈ 0.505

CNN 的验证精度始终保持 10%~15% 的稳定优势,并且在整个训练过程中曲线平滑、稳步上升。

结论:CNN 对于随机擦除造成的局部结构破坏具有天然抗性,而 MLP 曲线提升缓慢且上限更低。

随机擦除对 MLP 的影响远大于对 CNN 的影响

从曲线可以看到:

MLP 的训练曲线与验证曲线始终存在明显 gap

且验证集精度增长缓慢,后期几乎进入停滞

随机擦除破坏了 MLP flatten 后的全局向量,使模型难以恢复有效特征

而 CNN:

即便被遮挡一部分区域

仍能通过局部卷积核从未被遮挡的区域提取足够信息

验证曲线平滑且稳定上升

这表明:MLP 缺乏空间补偿机制,一旦局部像素被破坏,整体特征都会被扰乱;而 CNN 则具备“局部容错”能力。

CNN 的收敛速度明显快于 MLP

观察前 3 个 epoch:

CNN val:从 0.426 → 0.562 → 0.590

MLP val:从 0.437 → 0.465 → 0.475

CNN 的增长速度更快,曲线也更陡峭。

CNN 在早期就掌握了稳健的局部纹理特征,而 MLP 则需要更长时间才开始学习到有效结构。

随机擦除反而进一步凸显了 CNN 的优势

与上一章(无扰动)相比:

CNN 在 erasing 下精度略降,但仍维持高稳定性与高上限

MLP 在 erasing 下损失明显加剧,最终验证精度也更低

这说明:

扰动越强,MLP 越脆弱;

扰动越强,CNN 越体现其辨识局部结构的能力。

🎯 最终总结(卷4核心结论)

基于本次实验数据可以明确得出:

CNN 的优势不依赖于更大的数据集,而是来源于“空间结构敏感性 + 局部特征补偿能力”。

随机擦除本质上破坏了:

局部纹理

部分语义区域

MLP 由于 flatten 特性:

完全没有空间意识

局部损坏会扰乱整个输入向量

因此验证精度严重受损

CNN 则可以:

靠周边特征补偿缺失的信息

保持稳定学习

在遮挡环境下仍能正确分类

因此:

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/29 2:18:14

FastMCP高级特性之Composition

一、服务组合 使用挂载和导入功能,将多个 FastMCP 服务器合并成一个更大的应用程序。 随着您的 MCP 应用程序不断发展,您可能希望将工具、资源和提示组织到逻辑模块中,或者重用现有的服务器组件。FastMCP 通过两种方法支持组合: i…

作者头像 李华
网站建设 2026/1/29 6:12:26

边缘计算开源项目终极指南:让物联网设备秒变智能终端

边缘计算开源项目终极指南:让物联网设备秒变智能终端 【免费下载链接】Awesome-GitHub-Repo 收集整理 GitHub 上高质量、有趣的开源项目。 项目地址: https://gitcode.com/gh_mirrors/aw/Awesome-GitHub-Repo 还在为物联网设备响应慢、云端延迟而烦恼吗&…

作者头像 李华
网站建设 2026/1/29 1:54:09

ForensicsTool取证工具完整安装配置指南:快速掌握电子数据取证技能

ForensicsTool取证工具完整安装配置指南:快速掌握电子数据取证技能 【免费下载链接】ForensicsTool 简单的取证工具 项目地址: https://gitcode.com/gh_mirrors/fo/ForensicsTool 想要学习电子数据取证技术却苦于没有合适的工具?ForensicsTool开源…

作者头像 李华
网站建设 2026/1/29 3:42:41

DeepSeek-V3 KV缓存技术:让AI对话像翻书一样流畅

DeepSeek-V3 KV缓存技术:让AI对话像翻书一样流畅 【免费下载链接】DeepSeek-V3 项目地址: https://gitcode.com/GitHub_Trending/de/DeepSeek-V3 你是否曾经在与AI助手对话时感到不耐烦?🤔 特别是当对话进行到第五轮、第十轮时&#…

作者头像 李华
网站建设 2026/1/28 2:08:40

SpringBoot进阶教程(八十八)获取图片的宽高

回到顶部 v使用BufferedImage(推荐) 适用于常见图片格式,如果是处理本地文件系统中的图片文件,可以使用 Java 的ImageIO类结合BufferedImage来获取宽高,示例代码如下: 复制代码 /** * 使用BufferedImage(适用于常见图…

作者头像 李华