Add installation guides for OpenClaw and uv package manager
This commit is contained in:
@@ -0,0 +1,649 @@
|
||||
# 从全连接层到卷积:深度学习中的空间智慧
|
||||
|
||||
## 引言
|
||||
|
||||
想象一下,当你在看一张照片时,你的眼睛会自动聚焦在局部区域——边缘、纹理、形状——然后将这些局部信息整合起来识别整体内容。卷积神经网络(CNN)正是模仿了这种视觉系统的运作方式。让我带你从数学原理出发,理解为什么卷积层比全连接层更适合处理图像数据。
|
||||
|
||||
## 一、全连接层的困境
|
||||
|
||||
### 1.1 表格数据的成功
|
||||
|
||||
在之前的章节中,我们已经看到多层感知机(MLP)在处理表格数据时表现出色:
|
||||
|
||||
```python
|
||||
import torch
|
||||
from torch import nn
|
||||
|
||||
# 典型的MLP处理表格数据
|
||||
class MLP(nn.Module):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.flatten = nn.Flatten()
|
||||
# 隐藏层:学习特征交互
|
||||
self.hidden = nn.Linear(784, 256)
|
||||
self.relu = nn.ReLU()
|
||||
self.output = nn.Linear(256, 10)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.flatten(x)
|
||||
x = self.relu(self.hidden(x))
|
||||
return self.output(x)
|
||||
```
|
||||
|
||||
对于表格数据,每个样本是一行特征,我们需要学习特征之间的交互关系,MLP 正是为此而生。
|
||||
|
||||
### 1.2 高维图像的灾难
|
||||
|
||||
然而,当面对高维图像数据时,全连接层的参数数量会爆炸式增长:
|
||||
|
||||
```python
|
||||
# 假设:200×200 像素的灰度图像
|
||||
input_size = 200 * 200 # = 40,000
|
||||
hidden_size = 1000
|
||||
|
||||
# 全连接层参数数量
|
||||
params_fc = input_size * hidden_size # 40,000,000 参数!
|
||||
print(f"全连接层参数: {params_fc:,}")
|
||||
|
||||
# 对于彩色图像(RGB三通道)
|
||||
input_rgb = 200 * 200 * 3 # = 120,000
|
||||
params_rgb = input_rgb * hidden_size # 120,000,000 参数!
|
||||
print(f"RGB图像全连接层参数: {params_rgb:,}")
|
||||
```
|
||||
|
||||
**问题本质**:对于 200×200 的灰度图像,仅一个全连接层就需要 4000 万个参数!这对于训练和存储都是噩梦。
|
||||
|
||||
### 1.3 缺乏结构先验
|
||||
|
||||
全连接层假设所有输入之间都是平等交互的:
|
||||
|
||||
```
|
||||
全连接层视角:
|
||||
输入层: [x₁] ──┬── [h₁]
|
||||
│
|
||||
[x₂] ──┬── [h₂]
|
||||
│ │ ... 每个输入都连接到每个输出
|
||||
[x₃] ──┴── [h₃]
|
||||
```
|
||||
|
||||
但图像有天然的空间结构:
|
||||
- 相邻像素通常高度相关
|
||||
- 图像特征可以出现在任何位置
|
||||
- 局部区域比全局组合更有意义
|
||||
|
||||
## 二、空间先验:视觉智能的核心
|
||||
|
||||
### 2.1 沃尔玛在哪里?
|
||||
|
||||
让我们玩一个思想实验。想象在沃尔玛游戏中:
|
||||
|
||||
> 游戏包含混乱的场景,沃尔玛藏在各种位置,我们需要找出他。
|
||||
|
||||
关键洞察:**沃尔玛的样子不取决于他藏在哪里**。无论他出现在左上角还是右下角,我们都能认出他。
|
||||
|
||||
这启发了两个核心原则:
|
||||
|
||||
| 原则 | 含义 | 数学表达 |
|
||||
|------|------|----------|
|
||||
| **平移不变性** | 无论物体在图像何处,检测器应该以相同方式工作 | V(i,j,a,b) = V(a,b)(不依赖位置) |
|
||||
| **局部性** | 只看局部区域,不需要关注远处像素 | V(a,b) = 0 当 \|a\| > Δ 或 \|b\| > Δ |
|
||||
|
||||
### 2.2 数学形式化
|
||||
|
||||
让我们从全连接层的数学表示开始,逐步推导出卷积层。
|
||||
|
||||
#### 原始全连接层表示
|
||||
|
||||
对于输入图像 X ∈ R^(h×w),隐藏表示 H ∈ R^(h×w):
|
||||
|
||||
```
|
||||
H(i,j) = U(i,j) + Σₖ Σₗ W(i,j,k,l) · X(k,l)
|
||||
```
|
||||
|
||||
其中 W 是四阶权重张量,参数数量为 h × w × h × w。
|
||||
|
||||
#### 应用平移不变性
|
||||
|
||||
根据平移不变性原则,权重不应依赖位置 (i,j):
|
||||
|
||||
```
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ H(i,j) = u + Σₐ Σᵦ V(a,b) · X(i+a,j+b) ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
这已经是我们熟悉的卷积操作了!
|
||||
|
||||
#### 应用局部性约束
|
||||
|
||||
进一步限制只访问局部区域(窗口大小为 2Δ+1):
|
||||
|
||||
```
|
||||
╔═══════════════════════════════════════════════════════════════════╗
|
||||
║ H(i,j) = u + Σₐ₌₋Δ^Δ Σᵦ₌₋Δ^Δ V(a,b) · X(i+a,j+b) ║
|
||||
╚═══════════════════════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
**这就是卷积层!** 从数十亿参数减少到几百个参数。
|
||||
|
||||
```python
|
||||
# 直观理解:卷积操作就是"滑动窗口加权求和"
|
||||
def conv2d_manual(image, kernel, stride=1, padding=0):
|
||||
"""
|
||||
手动实现2D卷积
|
||||
image: 输入图像 (H, W)
|
||||
kernel: 卷积核 (K, K),学习参数
|
||||
"""
|
||||
# 添加padding
|
||||
if padding > 0:
|
||||
image = np.pad(image, padding, mode='constant')
|
||||
|
||||
h, w = image.shape
|
||||
k = kernel.shape[0]
|
||||
out_h = (h - k) // stride + 1
|
||||
out_w = (w - k) // stride + 1
|
||||
|
||||
output = np.zeros((out_h, out_w))
|
||||
|
||||
for i in range(0, out_h * stride, stride):
|
||||
for j in range(0, out_w * stride, stride):
|
||||
# 取局部区域并加权求和
|
||||
region = image[i:i+k, j:j+k]
|
||||
output[i//stride, j//stride] = np.sum(region * kernel)
|
||||
|
||||
return output
|
||||
```
|
||||
|
||||
## 三、卷积的数学本质
|
||||
|
||||
### 3.1 一维卷积
|
||||
|
||||
在数学中,两个函数 f, g: R → R 的卷积定义为:
|
||||
|
||||
```
|
||||
(f * g)(x) = ∫ f(z) · g(x-z) dz
|
||||
```
|
||||
|
||||
**直观理解**:将函数 g "翻转"后,在位置 x 处与 f 的重叠程度。
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def convolution_1d(f, g):
|
||||
"""
|
||||
计算一维卷积 (f * g)
|
||||
积分 -> 求和,翻转操作通过索引实现
|
||||
"""
|
||||
n = len(f)
|
||||
result = np.zeros(n)
|
||||
|
||||
for i in range(n):
|
||||
for a in range(n):
|
||||
if 0 <= i - a < n:
|
||||
result[i] += f[a] * g[i - a] # g(index - a) 实现翻转
|
||||
|
||||
return result
|
||||
|
||||
# 示例信号
|
||||
t = np.linspace(0, 1, 100)
|
||||
f = np.sin(2 * np.pi * 5 * t) # 5Hz正弦波
|
||||
g = np.exp(-10 * t) # 指数衰减
|
||||
|
||||
# 计算卷积
|
||||
conv_result = convolution_1d(f, g)
|
||||
```
|
||||
|
||||
### 3.2 二维卷积
|
||||
|
||||
对于图像(2D 信号),卷积推广为:
|
||||
|
||||
```
|
||||
(f * g)(i, j) = Σₐ Σᵦ f(a,b) · g(i-a, j-b)
|
||||
```
|
||||
|
||||
在深度学习中,我们通常使用**互相关**(cross-correlation):
|
||||
|
||||
```
|
||||
(f ★ g)(i, j) = Σₐ Σᵦ f(i+a, j+b) · g(a,b)
|
||||
```
|
||||
|
||||
PyTorch 的 `nn.Conv2d` 实际上计算的是互相关,但为了简洁,通常直接称为卷积。
|
||||
|
||||
```python
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
# PyTorch 的卷积层
|
||||
conv = nn.Conv2d(
|
||||
in_channels=3, # 输入通道数(RGB图像=3)
|
||||
out_channels=16, # 输出通道数(特征图数量)
|
||||
kernel_size=3, # 卷积核大小 3×3
|
||||
stride=1, # 步长
|
||||
padding=1 # 填充,保持尺寸
|
||||
)
|
||||
|
||||
# 输入: (batch_size, channels, height, width)
|
||||
x = torch.randn(1, 3, 224, 224)
|
||||
y = conv(x)
|
||||
print(f"输入尺寸: {x.shape}")
|
||||
print(f"输出尺寸: {y.shape}") # [1, 16, 224, 224]
|
||||
```
|
||||
|
||||
## 四、沃尔玛检测器:卷积的直观理解
|
||||
|
||||
让我们通过一个具体例子理解卷积如何工作:
|
||||
|
||||
```python
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# 创建一个小图像
|
||||
image = torch.zeros(7, 7)
|
||||
# 模拟一个简单的形状(十字)
|
||||
image[3, 2:5] = 1
|
||||
image[2:5, 3] = 1
|
||||
|
||||
# 定义卷积核(检测垂直边缘)
|
||||
vertical_edge = torch.tensor([
|
||||
[-1, 0, 1],
|
||||
[-2, 0, 2],
|
||||
[-1, 0, 1]
|
||||
], dtype=torch.float32).unsqueeze(0)
|
||||
|
||||
# 应用卷积
|
||||
output = F.conv2d(
|
||||
image.unsqueeze(0).unsqueeze(0), # 添加batch和channel维度
|
||||
vertical_edge.unsqueeze(0),
|
||||
stride=1,
|
||||
padding=1
|
||||
)
|
||||
|
||||
print("卷积核(检测垂直边缘):")
|
||||
print(vertical_edge[0])
|
||||
print("\n卷积输出(边缘响应):")
|
||||
print(output[0, 0])
|
||||
```
|
||||
|
||||
### 4.1 卷积核的直观解释
|
||||
|
||||
| 卷积核类型 | 核矩阵 | 作用 |
|
||||
|-----------|--------|------|
|
||||
| 锐化 | `[[0,-1,0], [-1,5,-1], [0,-1,0]]` | 增强细节 |
|
||||
| 模糊 | `1/9 * [[1,1,1], [1,1,1], [1,1,1]]` | 平滑噪声 |
|
||||
| 垂直边缘 | `[[-1,0,1], [-2,0,2], [-1,0,1]]` | 检测垂直边缘 |
|
||||
| 水平边缘 | `[[-1,-2,-1], [0,0,0], [1,2,1]]` | 检测水平边缘 |
|
||||
|
||||
## 五、通道:多维特征的表示
|
||||
|
||||
### 5.1 为什么需要通道?
|
||||
|
||||
现实世界的图像是三维的(高度 × 宽度 × 通道):
|
||||
|
||||
```python
|
||||
# RGB图像:张量形状 (3, H, W)
|
||||
rgb_image = torch.randn(3, 224, 224)
|
||||
|
||||
# 多通道卷积层
|
||||
conv_multi_channel = nn.Conv2d(
|
||||
in_channels=3, # 输入3通道(RGB)
|
||||
out_channels=16, # 输出16通道(特征图)
|
||||
kernel_size=3
|
||||
)
|
||||
|
||||
# 卷积核形状: (16, 3, 3, 3)
|
||||
# 16个输出通道,每个通道对应3个输入通道的3×3卷积核
|
||||
print(f"卷积核权重形状: {conv_multi_channel.weight.shape}")
|
||||
```
|
||||
|
||||
### 5.2 多通道卷积公式
|
||||
|
||||
对于多通道输入 X ∈ R^(h×w×c_in) 和多通道输出:
|
||||
|
||||
```
|
||||
H(i,j,d) = u(d) + Σₐ₌₋Δ^Δ Σᵦ₌₋Δ^Δ Σ꜀ V(a,b,c,d) · X(i+a,j+b,c)
|
||||
```
|
||||
|
||||
其中:
|
||||
- c:输入通道索引
|
||||
- d:输出通道索引
|
||||
- V ∈ R^((2Δ+1)×(2Δ+1)×c_in×c_out)
|
||||
|
||||
### 5.3 通道的语义意义
|
||||
|
||||
每一层的通道可以看作是对图像不同特征的响应:
|
||||
|
||||
```python
|
||||
# 可视化不同通道学到的特征
|
||||
def visualize_channels(conv_layer, input_image):
|
||||
"""
|
||||
假设输入是一张包含多种元素的图像
|
||||
不同通道可能专门响应不同特征:
|
||||
- 通道0: 检测边缘
|
||||
- 通道1: 检测纹理
|
||||
- 通道2: 检测颜色
|
||||
- ...
|
||||
"""
|
||||
with torch.no_grad():
|
||||
activations = conv_layer(input_image)
|
||||
|
||||
# activations: (batch, channels, H, W)
|
||||
# 每个通道都是对输入的空间响应图
|
||||
return activations
|
||||
```
|
||||
|
||||
## 六、从数学到实现
|
||||
|
||||
### 6.1 PyTorch 卷积层详解
|
||||
|
||||
```python
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
|
||||
class ConvLayerDemo(nn.Module):
|
||||
"""
|
||||
演示卷积层的各个参数
|
||||
"""
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# 基础卷积
|
||||
self.conv1 = nn.Conv2d(
|
||||
in_channels=3, # RGB图像
|
||||
out_channels=64, # 输出64个特征图
|
||||
kernel_size=3, # 3×3卷积核
|
||||
stride=1, # 步长1
|
||||
padding=1, # 周围填充1像素
|
||||
padding_mode='zeros' # 填充方式
|
||||
)
|
||||
|
||||
# 大卷积核(感受野更大)
|
||||
self.conv_large = nn.Conv2d(64, 64, kernel_size=7, padding=3)
|
||||
|
||||
# 空洞卷积(扩大感受野)
|
||||
self.dilated_conv = nn.Conv2d(
|
||||
64, 64, kernel_size=3,
|
||||
padding=2, dilation=2 # 空洞率2
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.conv1(x)
|
||||
x = torch.relu(x)
|
||||
return x
|
||||
|
||||
# 测试不同配置
|
||||
model = ConvLayerDemo()
|
||||
x = torch.randn(1, 3, 224, 224)
|
||||
print(f"输入: {x.shape}")
|
||||
|
||||
# 查看卷积层参数数量
|
||||
total_params = sum(p.numel() for p in model.parameters())
|
||||
print(f"总参数量: {total_params:,}")
|
||||
```
|
||||
|
||||
### 6.2 感受野的概念
|
||||
|
||||
**感受野**(Receptive Field):输出特征图上的一个像素能看到多大的输入区域。
|
||||
|
||||
```python
|
||||
def compute_receptive_field(layer_config):
|
||||
"""
|
||||
计算感受野
|
||||
公式: RF = RF_prev + (kernel_size - 1) * product_of_strides
|
||||
"""
|
||||
rf = 1
|
||||
stride_product = 1
|
||||
|
||||
for kernel_size, stride in layer_config:
|
||||
rf = rf + (kernel_size - 1) * stride_product
|
||||
stride_product *= stride
|
||||
|
||||
return rf
|
||||
|
||||
# 示例:三个3×3卷积层的感受野
|
||||
# 第一层: RF = 1 + (3-1) * 1 = 3
|
||||
# 第二层: RF = 3 + (3-1) * 1 = 5
|
||||
# 第三层: RF = 5 + (3-1) * 1 = 7
|
||||
|
||||
print(f"三层3×3卷积的感受野: {compute_receptive_field([(3,1), (3,1), (3,1)])}")
|
||||
print(f"一层7×7卷积的感受野: {compute_receptive_field([(7,1)])}")
|
||||
```
|
||||
|
||||
## 七、全连接层 vs 卷积层
|
||||
|
||||
### 7.1 参数对比
|
||||
|
||||
| 方面 | 全连接层 | 卷积层 |
|
||||
|------|----------|--------|
|
||||
| 参数数量 | H_in × H_out | C_out × C_in × K × K |
|
||||
| 权重共享 | 无 | 有(整个图像共享同一卷积核) |
|
||||
| 空间结构 | 忽略 | 保留 |
|
||||
| 平移不变性 | 无 | 有 |
|
||||
| 适用数据 | 表格数据 | 图像、音频、序列 |
|
||||
|
||||
```python
|
||||
# 参数数量对比
|
||||
def compare_parameters():
|
||||
# 假设输入: 224×224×3
|
||||
h_in, w_in, c_in = 224, 224, 3
|
||||
|
||||
# 全连接层(flatten后)
|
||||
fc_params = h_in * w_in * c_in * 512
|
||||
print(f"全连接层参数: {fc_params:,}")
|
||||
|
||||
# 卷积层
|
||||
conv_params = 64 * c_in * 3 * 3
|
||||
print(f"卷积层参数: {conv_params:,}")
|
||||
|
||||
print(f"\n参数减少比例: {fc_params / conv_params:.1f}x")
|
||||
|
||||
return fc_params, conv_params
|
||||
|
||||
compare_parameters()
|
||||
```
|
||||
|
||||
### 7.2 何时使用哪种层?
|
||||
|
||||
```python
|
||||
class HybridNet(nn.Module):
|
||||
"""
|
||||
现代网络通常混合使用卷积层和全连接层
|
||||
"""
|
||||
def __init__(self, num_classes=1000):
|
||||
super().__init__()
|
||||
|
||||
# 卷积层:处理图像,提取特征
|
||||
self.features = nn.Sequential(
|
||||
nn.Conv2d(3, 64, 3, padding=1),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2d(2), # 空间尺寸减半
|
||||
|
||||
nn.Conv2d(64, 128, 3, padding=1),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2d(2),
|
||||
|
||||
nn.Conv2d(128, 256, 3, padding=1),
|
||||
nn.ReLU(),
|
||||
nn.AdaptiveAvgPool2d(1) # 全局平均池化
|
||||
)
|
||||
|
||||
# 全连接层:分类(只在最后使用)
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Dropout(0.5),
|
||||
nn.Linear(256, num_classes)
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
x = self.features(x)
|
||||
x = torch.flatten(x, 1) # 展平
|
||||
x = self.classifier(x)
|
||||
return x
|
||||
```
|
||||
|
||||
## 八、归纳偏置:CNN 的先验知识
|
||||
|
||||
### 8.1 什么是归纳偏置?
|
||||
|
||||
**归纳偏置**:学习算法对数据分布的先验假设。
|
||||
|
||||
卷积神经网络包含以下归纳偏置:
|
||||
|
||||
1. **平移不变性**:特征检测器对位置不敏感
|
||||
2. **局部性**:只关注局部区域
|
||||
3. **层次化表示**:从边缘到纹理到物体部件到完整物体
|
||||
|
||||
### 8.2 偏置的双刃剑
|
||||
|
||||
```python
|
||||
# 偏置的好处:样本效率高
|
||||
# 因为有先验知识,网络不需要从头学习所有东西
|
||||
|
||||
# 偏置的代价:灵活性降低
|
||||
# 如果数据不满足假设,性能可能下降
|
||||
|
||||
# 例子:ImageNet上的ResNet vs Transformer
|
||||
# CNN在自然图像上表现好
|
||||
# Transformer在小样本、分布外数据上可能更鲁棒
|
||||
```
|
||||
|
||||
### 8.3 何时 CNN 可能不够好?
|
||||
|
||||
| 场景 | 问题 | 解决方案 |
|
||||
|------|------|----------|
|
||||
| 旋转/缩放变换 | 平移不变但非旋转不变 | 数据增强、STN |
|
||||
| 长距离依赖 | 局部性限制 | attention mechanism |
|
||||
| 非结构化输入 | 无空间结构 | MLP-Mixer、Transformer |
|
||||
|
||||
## 九、代码实践:亲手实现卷积层
|
||||
|
||||
### 9.1 从零理解卷积
|
||||
|
||||
```python
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import numpy as np
|
||||
|
||||
def my_conv2d(image, kernel, stride=1, padding=0):
|
||||
"""
|
||||
纯Python实现2D卷积
|
||||
image: (H, W) numpy数组
|
||||
kernel: (K, K) numpy数组
|
||||
"""
|
||||
if padding > 0:
|
||||
image = np.pad(image, padding, mode='constant')
|
||||
|
||||
h, w = image.shape
|
||||
k = kernel.shape[0]
|
||||
out_h = (h - k) // stride + 1
|
||||
out_w = (w - k) // stride + 1
|
||||
|
||||
output = np.zeros((out_h, out_w))
|
||||
|
||||
for i in range(0, out_h * stride, stride):
|
||||
for j in range(0, out_w * stride, stride):
|
||||
# 取局部区域,加权求和
|
||||
output[i//stride, j//stride] = np.sum(
|
||||
image[i:i+k, j:j+k] * kernel
|
||||
)
|
||||
|
||||
return output
|
||||
|
||||
# 测试
|
||||
image = np.random.randn(5, 5)
|
||||
kernel = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]]) # 边缘检测
|
||||
|
||||
result = my_conv2d(image, kernel, padding=1)
|
||||
print("自定义卷积结果:\n", result)
|
||||
|
||||
# 与PyTorch对比
|
||||
t_image = torch.tensor(image, dtype=torch.float32).unsqueeze(0).unsqueeze(0)
|
||||
t_kernel = torch.tensor(kernel, dtype=torch.float32).unsqueeze(0).unsqueeze(0)
|
||||
t_result = torch.nn.functional.conv2d(t_image, t_kernel, padding=1)
|
||||
print("\nPyTorch卷积结果:\n", t_result[0, 0].numpy())
|
||||
```
|
||||
|
||||
### 9.2 可视化卷积操作
|
||||
|
||||
```python
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
def visualize_convolution():
|
||||
"""可视化卷积操作"""
|
||||
|
||||
# 创建测试图像(棋盘格)
|
||||
image = np.zeros((8, 8))
|
||||
image[::2, ::2] = 1
|
||||
image[1::2, 1::2] = 1
|
||||
|
||||
# 定义不同的卷积核
|
||||
kernels = {
|
||||
'原图': np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]),
|
||||
'水平边缘': 0.25 * np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]]),
|
||||
'垂直边缘': 0.25 * np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]]),
|
||||
'锐化': np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
|
||||
}
|
||||
|
||||
fig, axes = plt.subplots(1, len(kernels), figsize=(15, 4))
|
||||
|
||||
for ax, (name, kernel) in zip(axes, kernels.items()):
|
||||
result = my_conv2d(image, kernel, padding=1)
|
||||
ax.imshow(result, cmap='gray')
|
||||
ax.set_title(name)
|
||||
ax.axis('off')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig('convolution_demo.png', dpi=150)
|
||||
print("已保存卷积演示图")
|
||||
|
||||
visualize_convolution()
|
||||
```
|
||||
|
||||
## 十、总结与展望
|
||||
|
||||
### 10.1 核心要点
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 从全连接层到卷积 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 问题:全连接层处理图像 → 参数爆炸,忽略空间结构 │
|
||||
│ ↓ │
|
||||
│ 解决方案:引入空间先验 │
|
||||
│ ↓ │
|
||||
│ 平移不变性 + 局部性 → 卷积层 │
|
||||
│ ↓ │
|
||||
│ 结果:参数减少 × 空间感知 ✓ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 10.2 CNN 的能力与局限
|
||||
|
||||
**CNN 擅长的任务**:
|
||||
- 图像分类(ResNet、EfficientNet)
|
||||
- 目标检测(YOLO、Faster R-CNN)
|
||||
- 语义分割(U-Net、DeepLab)
|
||||
- 图像生成(DCGAN、Pix2Pix)
|
||||
|
||||
**需要额外技术的情况**:
|
||||
- 旋转/尺度变化 → 数据增强、STN
|
||||
- 长距离依赖 → attention、non-local
|
||||
- 3D 数据 → 3D CNN、PointNet
|
||||
|
||||
### 10.3 继续学习
|
||||
|
||||
卷积层只是开始,后续你将学习:
|
||||
|
||||
1. **卷积层变体**:空洞卷积、转置卷积、分组卷积
|
||||
2. **经典架构**:LeNet、AlexNet、VGG、ResNet
|
||||
3. **现代模块**:残差连接、注意力机制、特征金字塔
|
||||
4. **应用领域**:目标检测、语义分割、实例分割
|
||||
|
||||
## 参考资源
|
||||
|
||||
- D2L 中文版:卷积神经网络
|
||||
https://zh-v2.d2l.ai/chapter_convolutional-neural-networks/index.html
|
||||
- PyTorch Conv2d 文档
|
||||
https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html
|
||||
Reference in New Issue
Block a user