SSFTT(Spectral-Spatial Feature Tokenization Transformer)是一种专门用于高光谱图像分类的深度学习模型。这个架构巧妙地将3D卷积、2D卷积与Transformer相结合,能够同时捕捉光谱和空间特征。下面我将从代码实现角度详细解析这个网络。
SSFTT网络的核心思想可以概括为四个关键步骤:
在代码中,这对应SSFTTnet.py文件中的SSFTTnet类。让我们先看模型初始化的关键参数:
python复制class SSFTTnet(nn.Module):
def __init__(self, band, classes, num_tokens=4):
super(SSFTTnet, self).__init__()
# 光谱-空间特征提取
self.conv3d_1 = nn.Sequential(
nn.Conv3d(1, 8, (7, 3, 3), padding=(0, 1, 1)),
nn.BatchNorm3d(8),
nn.ReLU()
)
# 空间特征提取
self.conv2d = nn.Sequential(
nn.Conv2d(224, 64, (3, 3)),
nn.BatchNorm2d(64),
nn.ReLU()
)
# Tokenizer
self.token_wA = nn.Parameter(torch.empty(64, 64))
# Transformer编码器
self.transformer = Transformer(dim=64, depth=2, heads=8, dim_head=64, mlp_dim=128)
# 分类头
self.cls_token = nn.Parameter(torch.randn(1, 1, 64))
self.mlp_head = nn.Linear(64, classes)
理解维度变化是掌握模型的关键。让我们跟踪一个典型输入(64,1,30,13,13)在各层的变换:
提示:实际调试时可以在forward函数中添加print语句,实时查看各层维度变化。这是理解复杂模型最有效的方法之一。
3D卷积的设计是SSFTT的第一个关键点:
python复制nn.Conv3d(1, 8, (7, 3, 3), padding=(0, 1, 1))
这个卷积核在三个维度上的含义:
为什么需要先3D再2D?
Tokenizer是将特征图转换为token的关键模块,对应论文中的公式(3):
python复制# 代码实现
wa = rearrange(self.token_wA, 'd h -> h d') # 64,64
a = torch.einsum('bnd,h->bnh', x, wa) # 64,81,64
a = a.softmax(dim=1) # 64,81,64
tokens = torch.einsum('bnh,bnd->bhd', a, x) # 64,4,64
数学表达式:
T = softmax(XWₐ)ᵀX
其中:
选择softmax的原因:
Transformer部分采用标准实现,但针对高光谱数据做了调整:
python复制class Transformer(nn.Module):
def __init__(self, dim, depth, heads, dim_head, mlp_dim):
super().__init__()
self.layers = nn.ModuleList([
nn.Sequential(
PreNorm(dim, Attention(dim, heads, dim_head)),
PreNorm(dim, FeedForward(dim, mlp_dim))
) for _ in range(depth)
])
关键参数选择:
IP_train.py中的完整流程:
关键代码片段:
python复制# PCA降维
pca = PCA(n_components=30)
data = pca.fit_transform(data.reshape(-1, bands)).reshape(h, w, 30)
# Patch提取
patches = []
for i in range(h):
for j in range(w):
patch = data[max(0,i-6):min(h,i+7), max(0,j-6):min(w,j+7)]
patch = np.pad(patch, ((6-i, i+7-h), (6-j, j+7-w), (0,0)), 'constant')
patches.append(patch)
训练使用标准交叉熵损失和Adam优化器:
python复制criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001, weight_decay=0.0001)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=20, gamma=0.9)
关键训练技巧:
get_cls_map.py将预测结果转换为分类图:
python复制def generate_cls_map(model, data_loader, device):
model.eval()
cls_map = np.zeros((h, w))
with torch.no_grad():
for batch in data_loader:
inputs, positions = batch
outputs = model(inputs.to(device))
preds = outputs.argmax(dim=1)
for i, pos in enumerate(positions):
cls_map[pos[0], pos[1]] = preds[i].item()
return cls_map
可视化技巧:
常用评估指标实现:
python复制def evaluate(model, data_loader, device):
model.eval()
total_correct = 0
total_samples = 0
conf_matrix = np.zeros((classes, classes))
with torch.no_grad():
for inputs, labels in data_loader:
outputs = model(inputs.to(device))
preds = outputs.argmax(dim=1)
total_correct += (preds == labels.to(device)).sum().item()
total_samples += labels.size(0)
for t, p in zip(labels, preds):
conf_matrix[t, p] += 1
accuracy = total_correct / total_samples
return accuracy, conf_matrix
梯度消失/爆炸
python复制torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
过拟合
python复制self.dropout = nn.Dropout(0.5) # 在Transformer中添加
类别不平衡
python复制weights = torch.tensor([...], device=device) # 每个类别的权重
criterion = nn.CrossEntropyLoss(weight=weights)
特征可视化
学习率测试
python复制lr_finder = LRFinder(model, optimizer, criterion)
lr_finder.range_test(train_loader, end_lr=1, num_iter=100)
lr_finder.plot()
激活统计
python复制print(f'Layer {name}: mean={activation.mean().item():.4f}, std={activation.std().item():.4f}')
混合精度训练
python复制scaler = torch.cuda.amp.GradScaler()
with torch.cuda.amp.autocast():
outputs = model(inputs)
loss = criterion(outputs, labels)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
模型轻量化
多尺度特征融合
时序信息利用
不确定性建模
在实际项目中,我发现模型的性能很大程度上取决于数据预处理的质量。特别是PCA降维的维度选择和patch大小的设置需要反复实验。另一个关键点是初始学习率的设置,使用学习率范围测试可以显著减少调参时间。