前言
其实我是在阅读上一篇我所整理的 HSIC 论文的时候发现这篇论文的,但是实际上 SSRN 的提出是比 HybridSN 的提出早了差不多两年,但是我这个“但是”用的不太恰当,因为这两个网络的本质不太一样(只是大家都是基于 CNN 的 HSI 分类论文),我想表达的只是说我整理 HSIC 论文的时间顺序就变得有点奇怪。
Z. Zhong, J. Li, Z. Luo and M. Chapman, "Spectral–Spatial Residual Network for Hyperspectral Image Classification: A 3-D Deep Learning Framework," in IEEE Transactions on Geoscience and Remote Sensing, vol. 56, no. 2, pp. 847-858, Feb. 2018, doi: 10.1109/TGRS.2017.2755542.
- The source code can be found at https://github.com/zilongzhong/SSRN.
- Date of Publication: 06 October 2017
作者受何凯明大神 ResNet 的启发,提出了 SSRN 实际上效果也是非常不错的,甚至和两年后比较火的 HybridSN 不相上下,SSRN 的成功也证明了残差连接在高光谱图像分类的可行性。
论文简读
这篇论文有十几页,其实可以看得出内容还是非常饱满的。整体内容可以分为四大部分:介绍、SSRN 网络结构细节阐述、实验结果与分析、总结。作者的 Introduction 写的很不错,我读着有种在听故事的感觉,很精炼很有逻辑性,全程无废话;SSRN 网络结构的讲解也是非常清晰。下面简单说说每个部分。
Introduction
作者用高光谱图像分类究竟在分类什么作为 Introduction 部分的第一句话,引出本文的主题。
"Classifying every pixel with a certain land-cover type is the cornerstone of hyperspectral image analysis, which spans a broad range of applications, including image segmentation, object recognition, land-cover mapping, and anomaly detection."
这也是我在整理上一篇论文就讲到的事情,高光谱图像分类就是对图像中每一个像素进行地物分类,而高光谱图像分类其实是其他高光谱图像处理任务的基石,包括:包括图像分割、物体识别、土地覆盖制图和异常检测。
接下来作者就开始一系列 introduce 一些东西:由高光谱图像的丰富数据(光谱信息与空间信息)入手,回顾传统分类方法,再由传统方法的不足引出深度学习方法在 HSIC 的应用,然后由所提出的深度学习模型中 SAE 和 DBN 的不足引出 CNN 的可行性,接着引出 3D-CNN ,最后再以 "inspired by ResNet" 带出这篇论文所提出的网络:SSRN。所以我才说整个 Introduction 想讲故事一样,不会很生硬,并且在时间线上也很好地给我们讲述了整个 HSIC 的发展历程。
SSRN 的 idea 基于 3D-CNN 和 residual connection(残差连接),但是作者也说到在 Going Deeper with Contextual CNN for Hyperspectral Image Classification 这篇文章中也有相似地将残差学习与卷积结合的情况,只是这篇论文所提出的方法不能够区分光谱特征和空间特征导致最后的分类结果不佳,而 SSRN 则是设计单独的光谱残差块和空间残差以提取丰富的光谱和空间特征。此外 SSRN 在每一层卷积之后都加了一层 BatchNorm 加快收敛速度。最后作者明确指出这篇文章旨在研究 SSRN 在 HSI 数据集上的泛化能力。

PROPOSED FRAMEWORK
在介绍具体网络结构之前,作者定义了数据集划分的具体操作,以及划分的各部分功能。如下图所示:

可以看出整个数据集划分为三组:training、validation、testing
- training 组:训练组的目标是更新 SSRN 的参数,直到可以给真实标签进行高精度预测
- validation 组:有点像训练过程中的测试组,就是为了验证训练过程中的那些“中间模型”(就是还未能对真实标签进行高精度预测的模型们)的准确率
- test 组:评估最佳训练模型,度量模型泛化能力(或者说评估最终模型的测试集)
接着就是具体介绍模型结构,有三个核心点:
- Three-Dimensional Convolutional Layer With Batch Normalization
- Spectral and Spatial Residual Blocks
- Spectral–Spatial Residual Network
这三个点有点逐层递进的感觉,整体结构就是 base 在 3D-CNN + BN 上,但是 CNN 层数太多反而分类精度下降,是因为层数增多,反向传播回来的梯度越变越小了,于是加上了 Residual Connection。而针对 HSI 数据的特殊性,分别加入光谱残差块和空间残差块更好地去提取光谱特征和空间特征。最后整个 SSRN 的网络就是光谱卷积层(光谱残差块)- 空间卷积层(空间残差块)- 平均池化层 - 全连接层(分类)。
光谱残差块、空间残差块、拿 Indian Pines 作为示例的 SSRN 网络图如下所示:



这个部分的最后,作者说经过这么一个流程(卷积、残差连接、平均池化、全连接)之后 SSRN 的可训练参数已经远大于三个 HSI 数据集中的可用训练数据,所以作者在思考过拟合的这个问题上添加了 BN 和 Dropout 来作为正则化项有无的探讨,这在后面实验部分会展开说说。
RESULTS AND DISCUSSION
👉 本论文使用了 Indian Pines、Kennedy Space Center (KSC)、Pavia University scene 这三个高光谱数据集,三个数据集剔除一些无用波段后具体信息如下:
数据集 | 空间维度 | 光谱波段 | 地物类别数 |
---|---|---|---|
Indian Pines(IN) | $145 \times 145$ | 200 | 16 |
University of Pavia(UP) | $610 \times 340$ | 103 | 9 |
Kennedy Space Center (KSC) | $512 \times 614$ | 176 | 19 |
训练的方法是在这三个数据集中随机选择训练数据进行 10 次实验。具体来说三个数据集的训练组、验证组及测试组的比例如下:
数据集 | 训练组 | 验证组 | 测试组 |
---|---|---|---|
Indian Pines(IN) | 20% | 10% | 70% |
University of Pavia(UP) | 20% | 10% | 70% |
Kennedy Space Center (KSC) | 10% | 10% | 80% |



👉 对于 SSRN 中的配置:
训练之前对输入的 HSI 数据做预处理,标准化为具有单位方差的平均值。(进行了减去均值除以方差的预处理)
一些超参数配置:
- batch_size:16
- optimizer::RMSProp
- epoch:200
- lr:{0.01, 0.003, .0.001, 0.0003, 0.0001, 0.00003} 这里采用多个学习率进行 200 个 epoch 的训练从而在分类效果中找出每个数据集对应的最佳学习率
- kernel number(卷积核数):{8, 16, 24, 32} 同理也是采用多个卷积核数进行训练从而在分类效果中找出每个数据集对应的最佳卷积核数
👉 如上面所说,作者在过拟合这个问题上分别对没有 BN 和 Dropout,有 BN,有 Dropout,有 BN 和 Dropout做了实验,实验结果如下图:

可以看出单纯比较 BN 与 Dropout, BN 在平均总体分类效果比 Dropout 好,精度更高。而在正则化有无这个问题来探讨,则是同时使用 BN 和 Dropout 时 SSRN 表现最好。
👉 作者还对输入 SSRN 的小立方体的 spatial size 做了实验
分别采用了 $3 \times 3$、 $5 \times 5$、 $7 \times 7$、 $9 \times 9$、 $11 \times 11$ 的 spatial size,从而找出适合各个数据集最适合的 input spatial size。

从表中可以看出,有种 spatial size 越大越好的感觉。分类精度随着输入小立方体的 spatial size 增大而增加。实际上,spatial size 在大到一定程度时分类精度会趋于平稳,想象一下,spatial size 如果就是输入数据集本身的 spatial size,那就毫无意义,所以其实不是越大越好,实际实验中,作者会选择一个相对稳定的值,固定了输入小立方体的 spatial size,为了在不同的分类方法中作比较。
👉 对比实验
有了上面的实验铺垫和常规超参数配置,作者就拿 SSRN 和其他 HSIC 经典方法开始做对比实验。评价指标还是 OA、KA 和 KA,对于这几个指标的含义,不在这里赘述,在上一篇整理的论文中已经有所提及。
- "compared the SSRN with the kernel SVM and state-of-the-art deep learning models, such as SAE and 3-D CNN."
- "we also tested the networks that only contain the spectral feature learning part (SPC) and the ones that only contain spatial feature learning part (SPA)."
- Moreover, we evaluated the longer versions of 3-D CNN (denote as CNNL) generated from the SPA models without skip connections to study the effect of the designed spatial residual architecture on the decreasing-accuracy phenomenon .
为了更公平的对比,设置相同的输入体积大小为 $7 \times 7 \times bands$,分别别随机选择 20%、20% 和 10% 标记的 3-D HSI 立方体作为 IN、KSC 和 UP 数据集的训练组。
结果分析:下面的结果就是 "classification results of different methods for different dataset."



- SSRN 表现最猛,在众多对比方法中脱颖而出,无论是在哪个 HSI 数据集上都达到了最高的分类精度并且最小的标准差;
- 所有基于深度学习的方法都比 SVM 要强;
- 基于 CNN、CNNL、和 SPA 的对比,可以看出空间残差块的加入确实可以减缓精度下降的问题。SSRN 又比 SPA 要强,是因为 SSRN 除了有空间残差块还有光谱残差块,即光谱特征得到补充;
- 最后,作者还有一个发现就是说,在 Indian Pines 中选取的训练集中其实有两个类的样本数是很少的,但是 SSRN 在面对这种小样本时还保持着很高的分类精度。 "These results validated the robustness of the designed models in the face of difficult conditions".
👉 眼见为实
下面是几种分类方法在不同数据集上分类结果的可视化显示:


最后还是想表明 SSRN 分类效果最佳,另外 SPC 的分类图中出现了很多噪点,SPA 这种现象要好一点,但是还是存在。
👉 测试 SSRN 对于不同数量训练样本的鲁棒性和泛化性
随机选择 了 5%、10%、15%、15% 和 20% 的标记样本作为 IN 和 KSC 数据集的训练数据以及 4%、6%、8% 和 10% 用于 UP 数据集来测试 SSRN 和其他方法在这样的条件下的 OA,结果如下:

从上面的图看出来,无论是比较少的训练样本还是较多的训练样本,SVM 真的输的很彻底啊,但是 SSRN 依然保持着很高的分类准确率。
👉 进一步验证残差块缓解精度下降现象的有效性
使用数量不同的 residual block ,数量分别为 2~5 个 residual block(spectral block + spatial block),如下图所示,使用上述 SSRN 的基本超参数配置,并且分别设置
- 1 spectral block + 1 spatial block
- 2 spectral block + 1 spatial block
- 2 spectral block + 2 spatial block
- 3 spectral block + 2 spatial block
- 2 spectral block + 3 spatial block

从图中的结果可以看出 OA 在这几个不同数量 spectral block 和 spatial block 组合中,好像并没有太大差异。也就是说不管这个 residual block 组成的网络是有多深或者多浅,分类精度是可以忽略不计的。但是看到这里我有点不太能理解为什么做不同数量组合的 residual block 就可以验证证残差块缓解精度下降现象的有效性问题。后面再次读了一下作者对于这个实验的分析:
"In Fig. 11, the overall classification accuracy differences between the deeper SSRNs and their shallow-layer counterparts are negligible. Therefore, in contrast to the obvious accuracy-decreasing effects reported in [17] and [22], the consistent HSI classification performance of SSRNs with varying layers demonstrated that the residual connections mitigate the decreasing-accuracy effects in other deep learning models."
这段话所提到的 [17] 和 [22] 分别是:


后来我想到一个可以勉强解释的原因(不知道对不对),就是比如说 [17] 或者 [22] 提出的方法都是没有残差块的,但是 SSRN 中包含了残差块并且缓解了精度下降的问题,但是这里有一个严谨的讨论:你 SSRN 这里随便加了两个 residual block 就说 residual block 可以缓解精度下降问题,你就归结为加 residual block 可以缓解精度下降,实际上要这个结论成立,你就只要做更加多的 residual block 数量测试,从而证明只要加了 residual block 就可以使缓解精度下降,不管里面的 residual block 是多少。这个问题更加形象表述可以说,我吃了两个汉堡就能长胖,那可不可以归结到我吃了汉堡就能长胖?你要试试你吃一个汉堡、三个汉堡或者更多汉堡是不是都会长胖,最后才能证明你吃汉堡就能长胖。(例子不太恰当,但是我所表达的就是这个意思)
👉 衡量 SSRN 的计算效率
基本上每个方法的提出都会有这么一个小实验。基于同样的硬件条件和基本超参数配置下,通过计算训练和测试时间来衡量每个方法的计算效率:

从表中可以看出 SSRN 所用训练时间和测试时间都是这几种方法中最长,而 SPC(就是只有光谱残差块) 齐次。因为光谱残差块保留了丰富的特征同时还要保持空间大小不变,自然需要更高的计算能力,那么所需要的时间也就更长。
CONCLUSION
在这个部分,作者对所提出的 SSRN 做了概括性总结与升华。我觉得比较重要的一个点就是 SSRN 在样本数量比较少上也能达到比较高的分类精度,这其实有点回应到我上次整理的 HybridSN 这篇论文中埋下的一个伏笔。SSRN 在小样本上的确是有优势的,得益于 spectral residual block 和 spatial residual block 这两个 residual block。基于3D-CNN 再有 residual connection 的加持,将浅层特征向后传递,实现浅层特征和深层特征的融合,最后经过平均池化层,再经过全连接层进行分类,每层卷积之后又有 BatchNorm,而且又有 Dropout 的点缀,使得 SSRN 成为非常经典和优秀的 HSI 分类方法,以至于后面的提出的网络都会拿 SSRN 来对比。
代码复现
- 作者有给出实现的代码,但是用 Tensorflow 实现的,👉 Tensorflow-SSRN;
- 而在作者的 2021 年新一篇论文中:SSTN,作者顺带给出了 SSTN 和 SSRN 的 Pytorch 实现版本,好贴心。👉 :Pytorch-SSRN ,自行服用
如果想直接跑代码的话,拿 Indian Pines 数据集为例子,将主目录下 train_In.py
文件代码的第 55 行中 parser.add_argument('--model', type=str, default='SSTN', help='select network to train')
的 SSTN
改为 SSRN
就可以使用 SSRN 模型进行训练。
我们对 SSRN 网络中的细节部分进行剖析,我们将 Spectral Feature Learning 和 Spatial Feature Learning 放在一条直线去看(以 Indian Pines 数据集为例子):


根据上面的代码以及上面的图,作者的代码在定义 SSRN 的时候分为了 6 层 + 平均池化层 + 全连接层。下面说说每一层对数据做了什么处理(下面忽略了一些激活函数和 BN,实际上是有的,下面只概括卷积核残差相关操作)
- 数据
x
输入到第一层 SPCModuleIN,经过一次三维卷积,得到x1
; - 进入第二层(第一个光谱残差块),先经过一次三维卷积 →
x1_1
,再经过一次三维卷积 →x1_2
,然后将x1+x1_2
,得到x2
,这一步就是 short connect; - 进入第三层(第二个光谱残差块),先经过一次三维卷积 →
x2_1
,再经过一次三维卷积 →x2_2
,然后将x2+x2_2
,得到x3
,这一步就是 short connect; - 进入第四层 SPAModuleIN,经过一次三维卷积,得到
x4
; - 进入第五层(第一个空间残差块),先经过一次二维卷积 →
x4_1
,再经过一次二维卷积 →x4_2
,然后将x4+x4_2
,得到x5
,这一步就是 short connect; - 进入第六层(第二个空间残差块),先经过一次二维卷积 →
x5_1
,再经过一次二维卷积 →x5_2
,然后将x5+x5_2
,得到x6
,这一步就是 short connect; - 然后就进入平均池化层;
- 最后进入全连接层进行分类。
We take the IN data set, the 3-D samples of which have the size of 7 × 7 × 200, as an example to explain the designed SSRN
Spectral Feature Learning Part
- The spectral feature learning section includes two convolutional layers and two spectral residual blocks. In the first convolutional layer, 24 1 × 1 × 7 spectral kernels with a subsampling stride of (1, 1, 2) convolves the input HSI volume to generate 24 7 × 7 × 97 feature cubes.
- Then, two consecutive spectral residual blocks, which contain four convolutional layers and two identity mappings, use 24 1 × 1 × 7 vector kernels at each convolutional layers to learn deep spectral representation.
- In the spectral residual blocks, all convolutional layers use padding to keep the sizes of output feature cubes the same as input.
- Following the spectral residual blocks, the last convolutional layer in this learning section, which includes 128 1 × 1 × 97 spectral kernels for keeping discriminative spectral features, convolves the 24 7 × 7 feature tensors to produce a 7 × 7 feature volume as input for spatial feature learning section.
Spatial Feature Learning Part
- The section comprises a 3-D convolutional layer and two spatial residual blocks. The first convolutional layer in this section reduces the spatial size of input feature cubes and extract low-level spatial features with 24 3 × 3 × 128 spatial kernels, resulting an output 5 × 5 × 24 feature tensor.
- Then, similar to their spectral counterparts, the two spatial residual blocks learn deep spatial representation with four convolutional layers, all of which use 24 3×3×24 spatial kernels and keep the sizes of feature cubes unchanged.
Average Pooling Layer and FC Layer
- After the above two feature learning sections, an average pooling layer (POOL) transforms the extracted 5 × 5 × 24 spectral–spatial feature volume to a 1 × 1 × 24 feature vector.
- Next, an FC layer adapts the SSRN to HSI data set according to the number of land-cover categories and generates an output vector $\hat{\boldsymbol{y}}=\left[\hat{y}_{1}, \hat{y}_{2}, \ldots, \hat{y}_{L}\right]$
上面这段话中概括了 SPCModuleIN、ResSPC、SPAModuleIN、ResSPA 的一些卷积核的参数以及步长、填充等等,不再赘述。具体代码如下,这里只给出 SSRN 的网络定义,其他直接看作者的源代码。
import scipy.io as sio
import numpy as np
from sklearn import preprocessing
import torch
import torch.nn as nn
import math
from torch.utils.data import Dataset,DataLoader
import torch.optim as optim
from torch.autograd import Variable
import torch.nn.functional as F
# Spectral Learning 部分的一个卷积层
class SPCModuleIN(nn.Module):
def __init__(self, in_channels, out_channels, bias=True):
super(SPCModuleIN, self).__init__()
self.s1 = nn.Conv3d(in_channels, out_channels, kernel_size=(7,1,1), stride=(2,1,1), bias=False)
def forward(self, input):
input = input.unsqueeze(1)
out = self.s1(input)
return out.squeeze(1)
# 光谱残差块
class ResSPC(nn.Module):
def __init__(self, in_channels, out_channels, bias=True):
super(ResSPC, self).__init__()
self.spc1 = nn.Sequential(nn.Conv3d(in_channels, in_channels, kernel_size=(7,1,1), padding=(3,0,0), bias=False),
nn.LeakyReLU(inplace=True),
nn.BatchNorm3d(in_channels),)
self.spc2 = nn.Sequential(nn.Conv3d(in_channels, in_channels, kernel_size=(7,1,1), padding=(3,0,0), bias=False),
nn.LeakyReLU(inplace=True),)
self.bn2 = nn.BatchNorm3d(out_channels)
def forward(self, input):
out = self.spc1(input)
out = self.bn2(self.spc2(out))
return F.leaky_relu(out + input)
# Spatial Feature Learning 部分的第一个卷积层
class SPAModuleIN(nn.Module):
def __init__(self, in_channels, out_channels, k=49, bias=True):
super(SPAModuleIN, self).__init__()
self.s1 = nn.Conv3d(in_channels, out_channels, kernel_size=(k,3,3), bias=False)
def forward(self, input):
out = self.s1(input)
out = out.squeeze(2)
return out
# 空间残差块
class ResSPA(nn.Module):
def __init__(self, in_channels, out_channels, bias=True):
super(ResSPA, self).__init__()
self.spa1 = nn.Sequential(nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1),
nn.LeakyReLU(inplace=True),
nn.BatchNorm2d(in_channels),)
self.spa2 = nn.Sequential(nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
nn.LeakyReLU(inplace=True),)
self.bn2 = nn.BatchNorm2d(out_channels)
def forward(self, input):
out = self.spa1(input)
out = self.bn2(self.spa2(out))
return F.leaky_relu(out + input)
# SSRN 网络
class SSRN(nn.Module):
def __init__(self, num_classes=9, k=49):
super(SSRN, self).__init__()
self.layer1 = SPCModuleIN(1, 24)
self.layer2 = ResSPC(24,24)
self.layer3 = ResSPC(24,24)
self.layer4 = SPAModuleIN(24, 24, k=k)
self.bn4 = nn.BatchNorm2d(24)
self.layer5 = ResSPA(24, 24)
self.layer6 = ResSPA(24, 24)
self.fc = nn.Linear(24, num_classes)
def forward(self, x):
x = F.leaky_relu(self.layer1(x)) #self.bn1(F.leaky_relu(self.layer1(x)))
x = self.layer2(x)
x = self.layer3(x)
x = self.bn4(F.leaky_relu(self.layer4(x)))
x = self.layer5(x)
x = self.layer6(x)
x = F.avg_pool2d(x, x.size()[-1])
x = self.fc(x.squeeze())
return x
# 测试一下定义的 SSRN 网络通不通 (batch_size, frames, height, width, channels)
x = torch.randn(1, 200, 7, 7)
net = SSRN(num_classes=16, k=97)
print(net)
y = net(x)
print(y.shape)
打印 SSRN 类看看:

我从 2021 年作者给出 SSRN 的 Pytorch 实现版本中提取出来了 jupyter 版本,如下:
- 有个问题待解决,就是从 ResSPC 模块 进入到 SPAModuleIN 模块的时候,其实还有一次卷积,可是在作者实现的代码中把这一次卷积去掉了
- 另外作者使用的优化器从论文中的 RMSProp 变成了 Adam 优化器,明显 Adam 效果更好
- 此外,作者在论文中的 SPAModuleIN 所用到的卷积核大小是:$3 \times 3 \times 128$,实际上应该是作者笔误,而应该是 $3 \times 3 \times 28$
