高光谱图像分类 - SSRN

Spectral–Spatial Residual Network for Hyperspectral Image Classification: A 3-D Deep Learning Framework

目录

前言

其实我是在阅读上一篇我所整理的 HSIC 论文的时候发现这篇论文的,但是实际上 SSRN 的提出是比 HybridSN 的提出早了差不多两年,但是我这个“但是”用的不太恰当,因为这两个网络的本质不太一样(只是大家都是基于 CNN 的 HSI 分类论文),我想表达的只是说我整理 HSIC 论文的时间顺序就变得有点奇怪。

Spectral–Spatial Residual Network for Hyperspectral Image Classification: A 3-D Deep Learning Framework

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.

作者受何凯明大神 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 数据集上的泛化能力。

image.png

PROPOSED FRAMEWORK

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

image.png

可以看出整个数据集划分为三组:training、validation、testing


接着就是具体介绍模型结构,有三个核心点:

这三个点有点逐层递进的感觉,整体结构就是 base 在 3D-CNN + BN 上,但是 CNN 层数太多反而分类精度下降,是因为层数增多,反向传播回来的梯度越变越小了,于是加上了 Residual Connection。而针对 HSI 数据的特殊性,分别加入光谱残差块和空间残差块更好地去提取光谱特征和空间特征。最后整个 SSRN 的网络就是光谱卷积层(光谱残差块)- 空间卷积层(空间残差块)- 平均池化层 - 全连接层(分类)。

光谱残差块、空间残差块、拿 Indian Pines 作为示例的 SSRN 网络图如下所示:

image.png

这个部分的最后,作者说经过这么一个流程(卷积、残差连接、平均池化、全连接)之后 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 数据做预处理,标准化为具有单位方差的平均值。(进行了减去均值除以方差的预处理)

一些超参数配置:

👉 如上面所说,作者在过拟合这个问题上分别对没有 BN 和 Dropout,有 BN,有 Dropout,有 BN 和 Dropout做了实验,实验结果如下图:

image.png

可以看出单纯比较 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。

image.png

从表中可以看出,有种 spatial size 越大越好的感觉。分类精度随着输入小立方体的 spatial size 增大而增加。实际上,spatial size 在大到一定程度时分类精度会趋于平稳,想象一下,spatial size 如果就是输入数据集本身的 spatial size,那就毫无意义,所以其实不是越大越好,实际实验中,作者会选择一个相对稳定的值,固定了输入小立方体的 spatial size,为了在不同的分类方法中作比较。

👉 对比实验

有了上面的实验铺垫和常规超参数配置,作者就拿 SSRN 和其他 HSIC 经典方法开始做对比实验。评价指标还是 OA、KA 和 KA,对于这几个指标的含义,不在这里赘述,在上一篇整理的论文中已经有所提及。

为了更公平的对比,设置相同的输入体积大小为 $7 \times 7 \times bands$,分别别随机选择 20%、20% 和 10% 标记的 3-D HSI 立方体作为 IN、KSC 和 UP 数据集的训练组。

结果分析:下面的结果就是 "classification results of different methods for different dataset."

👉 眼见为实

下面是几种分类方法在不同数据集上分类结果的可视化显示:

image.png
image.png

最后还是想表明 SSRN 分类效果最佳,另外 SPC 的分类图中出现了很多噪点,SPA 这种现象要好一点,但是还是存在。

👉 测试 SSRN 对于不同数量训练样本的鲁棒性和泛化性

随机选择 了 5%、10%、15%、15% 和 20% 的标记样本作为 IN 和 KSC 数据集的训练数据以及 4%、6%、8% 和 10% 用于 UP 数据集来测试 SSRN 和其他方法在这样的条件下的 OA,结果如下:

image.png

从上面的图看出来,无论是比较少的训练样本还是较多的训练样本,SVM 真的输的很彻底啊,但是 SSRN 依然保持着很高的分类准确率。

👉 进一步验证残差块缓解精度下降现象的有效性

使用数量不同的 residual block ,数量分别为 2~5 个 residual block(spectral block + spatial block),如下图所示,使用上述 SSRN 的基本超参数配置,并且分别设置

image.png

从图中的结果可以看出 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 的计算效率

基本上每个方法的提出都会有这么一个小实验。基于同样的硬件条件和基本超参数配置下,通过计算训练和测试时间来衡量每个方法的计算效率:

image.png

从表中可以看出 SSRN 所用训练时间和测试时间都是这几种方法中最长,而 SPC(就是只有光谱残差块) 齐次。因为光谱残差块保留了丰富的特征同时还要保持空间大小不变,自然需要更高的计算能力,那么所需要的时间也就更长。

CONCLUSION

在这个部分,作者对所提出的 SSRN 做了概括性总结与升华。我觉得比较重要的一个点就是 SSRN 在样本数量比较少上也能达到比较高的分类精度,这其实有点回应到我上次整理的 HybridSN 这篇论文中埋下的一个伏笔。SSRN 在小样本上的确是有优势的,得益于 spectral residual block 和 spatial residual block 这两个 residual block。基于3D-CNN 再有 residual connection 的加持,将浅层特征向后传递,实现浅层特征和深层特征的融合,最后经过平均池化层,再经过全连接层进行分类,每层卷积之后又有 BatchNorm,而且又有 Dropout 的点缀,使得 SSRN 成为非常经典和优秀的 HSI 分类方法,以至于后面的提出的网络都会拿 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 数据集为例子):

image.png
9edb9bb1b095cac679ddacbe957e6de.jpg

根据上面的代码以及上面的图,作者的代码在定义 SSRN 的时候分为了 6 层 + 平均池化层 + 全连接层。下面说说每一层对数据做了什么处理(下面忽略了一些激活函数和 BN,实际上是有的,下面只概括卷积核残差相关操作)

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

上面这段话中概括了 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 类看看:

image_ssrn

我从 2021 年作者给出 SSRN 的 Pytorch 实现版本中提取出来了 jupyter 版本,如下:

image.png