1. Swin Transformer 项目概述
Swin Transformer作为计算机视觉领域近年来最具突破性的架构之一,正在逐步改变传统CNN在图像处理任务中的统治地位。这个基于窗口注意力机制的分层Transformer架构,不仅继承了传统Transformer强大的全局建模能力,还通过局部窗口计算大幅降低了计算复杂度。我在实际工业级图像分类和检测项目中发现,相比传统CNN模型,Swin Transformer在保持同等计算效率的前提下,平均能带来3-5%的准确率提升。
本教程将完整演示如何从零开始训练自己的Swin Transformer模型。不同于大多数教程仅展示标准数据集(如ImageNet)的训练过程,我们将聚焦于自定义数据集的完整处理流程。你将获得包括数据预处理、模型微调、训练策略优化在内的全套解决方案,以及经过工业项目验证的实用技巧。配套提供的源码经过模块化封装,支持一键切换不同规模的Swin变体(Tiny/Small/Base/Large),并包含针对消费级GPU(如RTX 3090)的显存优化方案。
2. 环境配置与数据准备
2.1 开发环境搭建
推荐使用Python 3.8+和PyTorch 1.12+的组合,这是经过大量实验验证的稳定版本搭配。对于GPU支持,务必安装与CUDA版本匹配的PyTorch:
conda create -n swin python=3.8 conda activate swin pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html关键依赖库的版本控制非常重要:
- timm==0.6.7 (提供官方Swin实现)
- opencv-python>=4.5.4 (图像处理)
- albumentations>=1.2.1 (高效数据增强)
注意:避免直接pip install最新版库,某些库的API变更可能导致与Swin代码不兼容。我在2023年Q2的项目中就曾因albumentations的接口变更导致训练异常。
2.2 自定义数据集处理
假设我们收集了一个包含3316张图像、4个类别的工业检测数据集(VOC+YOLO格式),典型目录结构应如下:
custom_dataset/ ├── images/ │ ├── train/ │ │ ├── defect_001.jpg │ │ └── ... │ └── val/ │ ├── normal_003.jpg │ └── ... ├── labels/ │ ├── train/ │ │ ├── defect_001.txt │ │ └── ... │ └── val/ │ ├── normal_003.txt │ └── ... ├── classes.names └── dataset_config.yaml需要实现自定义Dataset类时,关键点在于正确处理图像尺寸与标注的对应关系。Swin Transformer通常使用224x224或384x384的输入尺寸,而原始标注可能是任意分辨率,因此需要比例缩放:
def __getitem__(self, idx): img_path = self.image_paths[idx] label_path = self.label_paths[idx] # 读取时保持原始宽高比进行resize image = cv2.imread(img_path) h, w = image.shape[:2] image = cv2.resize(image, (self.size, self.size)) # 处理YOLO格式标注 with open(label_path) as f: labels = [] for line in f.readlines(): class_id, xc, yc, bw, bh = map(float, line.strip().split()) # 坐标转换到resize后的尺寸 xc = int(xc * self.size / w) yc = int(yc * self.size / h) bw = int(bw * self.size / w) bh = int(bh * self.size / h) labels.append([class_id, xc, yc, bw, bh]) return image, torch.tensor(labels)3. 模型构建与训练策略
3.1 Swin Transformer架构解析
Swin-Tiny作为入门级配置,其层次结构如下表所示:
| Stage | Output Size | Channels | Window Size | Heads | Blocks |
|---|---|---|---|---|---|
| 1 | 56x56 | 96 | 7x7 | 3 | 2 |
| 2 | 28x28 | 192 | 7x7 | 6 | 2 |
| 3 | 14x14 | 384 | 7x7 | 12 | 6 |
| 4 | 7x7 | 768 | 7x7 | 24 | 2 |
在代码实现中,可以通过timm库快速加载预训练模型:
import timm model = timm.create_model( 'swin_tiny_patch4_window7_224', pretrained=True, num_classes=4, # 我们的自定义类别数 drop_rate=0.2, # 工业数据通常需要更强的正则化 img_size=224 )3.2 训练策略优化
针对中小规模数据集(<10k样本),推荐采用以下训练配置:
optimizer = torch.optim.AdamW( model.parameters(), lr=5e-5, weight_decay=0.05 # Swin对weight decay敏感 ) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR( optimizer, T_max=50, # 根据epoch数调整 eta_min=1e-6 ) loss_fn = nn.CrossEntropyLoss( label_smoothing=0.1 # 缓解小数据集过拟合 )关键训练技巧:
- 渐进式热身(Warmup):前5个epoch线性增加学习率
- 混合精度训练:减少显存占用约40%
- 梯度裁剪:阈值设为1.0防止梯度爆炸
4. 实战训练与性能调优
4.1 训练过程监控
使用WandB或TensorBoard记录关键指标:
from torch.utils.tensorboard import SummaryWriter writer = SummaryWriter() for epoch in range(epochs): # 训练循环 for batch_idx, (inputs, targets) in enumerate(train_loader): outputs = model(inputs) loss = loss_fn(outputs, targets) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) optimizer.step() # 记录指标 writer.add_scalar('Loss/train', loss.item(), global_step) writer.add_scalar('LR', optimizer.param_groups[0]['lr'], global_step) global_step += 14.2 显存优化技巧
在24GB显存的RTX 3090上训练Swin-Base的显存优化方案:
- 梯度累积:batch_size=32时,设置accum_steps=4
- 激活检查点:在SwinBlock中启用
from torch.utils.checkpoint import checkpoint_sequential def forward(self, x): if self.use_checkpoint: return checkpoint_sequential(self.blocks, len(self.blocks), x) else: return self.blocks(x) - 混合精度训练:
scaler = torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): outputs = model(inputs) loss = loss_fn(outputs, targets) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()
5. 模型评估与部署
5.1 评估指标实现
除常规的Top-1准确率外,工业场景更关注:
from sklearn.metrics import confusion_matrix, classification_report def evaluate(model, dataloader): model.eval() all_preds = [] all_targets = [] with torch.no_grad(): for inputs, targets in dataloader: outputs = model(inputs) _, preds = torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_targets.extend(targets.cpu().numpy()) print(classification_report( all_targets, all_preds, target_names=class_names )) cm = confusion_matrix(all_targets, all_preds) plt.figure(figsize=(10,8)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues') plt.savefig('confusion_matrix.png')5.2 模型导出与部署
将训练好的模型导出为ONNX格式:
dummy_input = torch.randn(1, 3, 224, 224).to(device) torch.onnx.export( model, dummy_input, "swin_tiny.onnx", input_names=["input"], output_names=["output"], dynamic_axes={ 'input': {0: 'batch_size'}, 'output': {0: 'batch_size'} } )针对不同部署场景的优化建议:
- TensorRT加速:使用FP16精度,推理速度提升2-3倍
- OpenVINO优化:在Intel CPU上获得最佳性能
- 移动端部署:通过量化将模型大小压缩至1/4
6. 常见问题与解决方案
6.1 训练不稳定问题排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| Loss出现NaN | 学习率过高 | 降低初始lr至5e-6 |
| 验证集准确率波动大 | 数据分布不一致 | 检查train/val数据划分 |
| GPU显存不足 | batch_size过大 | 使用梯度累积 |
6.2 小数据集过拟合处理
- 数据增强策略:
from albumentations import ( HorizontalFlip, VerticalFlip, Rotate, RandomBrightnessContrast, HueSaturationValue ) train_transform = A.Compose([ A.RandomResizedCrop(224, 224), A.HorizontalFlip(p=0.5), A.VerticalFlip(p=0.3), A.RandomBrightnessContrast(p=0.2), A.HueSaturationValue(hue_shift_limit=20, sat_shift_limit=30, val_shift_limit=20, p=0.3), A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) - 正则化技巧:
- 增加Dropout率(0.3-0.5)
- 使用Stochastic Depth(随机深度)
- 添加MixUp/CutMix数据混合
6.3 类别不平衡处理
对于工业缺陷检测等长尾分布数据:
class_counts = [1200, 800, 200, 100] # 各类别样本数 class_weights = 1. / torch.tensor(class_counts, dtype=torch.float) class_weights = class_weights / class_weights.sum() loss_fn = nn.CrossEntropyLoss(weight=class_weights.to(device))在实际项目中,结合过采样(Oversampling)和难例挖掘(Hard Negative Mining)效果更佳。