1. 高光谱图像分类:为什么需要不同维度的卷积神经网络?
如果你刚接触高光谱图像分类,可能会被一堆术语搞晕:1D-CNN、2D-CNN、3D-CNN,听起来像在说维度穿越。别担心,我用一个简单的比喻帮你理解。想象一下,你要识别一片森林里的树木种类。普通相机拍的照片(RGB图像)就像你站在林外看一眼,只能看到树冠的颜色和形状(空间信息)。而高光谱图像呢?它就像你给每棵树都做了个“光谱CT扫描”,不仅能看清树冠,还能知道每片叶子的水分含量、叶绿素浓度(光谱信息)。数据量一下子爆炸了——一个像素点不再只有红绿蓝三个值,而是有几十甚至几百个连续波段的光谱曲线。
这就引出了核心问题:如何处理这个既包含空间纹理(长、宽)又包含光谱细节(波段数)的“数据立方体”?传统机器学习方法(比如SVM)就像只用文字描述来认树,效率低还容易认错。深度学习来了之后,卷积神经网络(CNN)成了主力。但用哪种CNN最合适?这就是1D、2D、3D卷积大显身手的地方。简单说,1D-CNN专注“听声辨物”(只分析每个像素的光谱曲线),2D-CNN擅长“看图识物”(只看空间纹理,但要把光谱压扁或先处理),而3D-CNN则是“全息扫描”(同时捕捉空间和光谱的关联)。我在实际项目中都试过,发现选对维度,效果天差地别。
举个例子,用Pavia University数据集区分沥青路面和屋顶瓦片。1D-CNN可能因为两者光谱曲线相似而混淆;2D-CNN能看出路面纹理更均匀,但若遇到阴影就抓瞎;3D-CNN结合了光谱差异(瓦片在特定波段有吸收峰)和空间模式(瓦片排列整齐),准确率能直接拔高一截。下面我们就拆开看看这三种网络到底怎么玩,以及怎么用代码实现。我会用PaviaU和KSC这两个经典数据集当例子,它们都是公开的,你可以直接跟着做。
2. 1D-CNN:专注光谱维度的“听诊器”
2.1 核心思想与适用场景
1D-CNN,顾名思义,它的卷积核只在一个方向上滑动。在高光谱图像里,这个方向就是光谱维度。你可以把每个像素点想象成一条长长的光谱曲线(比如PaviaU有103个波段,就是103个点的曲线)。1D-CNN的工作,就是拿一个小窗口(比如大小=3的卷积核)在这条曲线上从左到右滑动,学习局部光谱的起伏模式。它完全忽略了像素之间的空间关系,只关心“这个点的光谱签名长什么样”。
这听起来有点“偏科”,但在某些场景下特别管用。比如地物类别主要靠光谱特征区分,像区分水体和植被。水体在近红外波段吸收强,反射率骤降;植被则有明显的“红边”特征。1D-CNN学这些光谱形状是一把好手。我早期做项目时,数据标注很少,空间上下文信息杂乱,用1D-CNN快速跑了个基线,效果比传统SVM好了不少,关键是训练速度飞快,对硬件要求极低。
但它的短板也很明显:对空间变化毫无抵抗力。同一片树林,树顶和树荫下的像素光谱可能差异很大(光照影响),1D-CNN很容易把它们判成两类。而且,像建筑物和道路这种光谱可能相似、主要靠纹理区分的类别,1D-CNN就力不从心了。
2.2 网络结构与代码实战
我们来看一个典型的1D-CNN结构,用Keras实现。输入数据的形状是 (样本数, 波段数, 1)。注意,我们得把空间维度(高和宽)压平,每个像素当成一个独立样本。
from keras.models import Sequential
from keras.layers import Conv1D, MaxPooling1D, Flatten, Dense, BatchNormalization, Dropout
from keras.optimizers import Adam
def build_1d_cnn(input_shape, num_classes):
model = Sequential()
# 第一个卷积层,用32个大小为3的卷积核
model.add(Conv1D(filters=32, kernel_size=3, activation='relu', input_shape=input_shape))
model.add(BatchNormalization()) # 加速训练,稳定收敛
# 第二个卷积层,加深特征提取
model.add(Conv1D(filters=64, kernel_size=3, activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2)) # 降采样,减少计算量
# 第三个卷积层
model.add(Conv1D(filters=128, kernel_size=3, activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
# 展平后接全连接层
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(0.5)) # 防止过拟合
model.add(Dense(num_classes, activation='softmax'))
model.compile(optimizer=Adam(learning_rate=0.001),
loss='categorical_crossentropy',
metrics=['accuracy'])
return model
# 以PaviaU为例,输入形状是(103, 1)
input_shape = (103, 1)
num_classes = 9 # PaviaU有9类
model_1d = build_1d_cnn(input_shape, num_classes)
model_1d.summary() # 打印网络结构
这里有几个我踩过的坑要提


387

被折叠的 条评论
为什么被折叠?



