复现顶刊RSE的3D-CNN
AI在遥感领域的应用已经成熟,并且大大推动了传统遥感的创新和发展。根据最近发表在《Remote Sensing of Environment》上的一篇文章,研究人员利用三维卷积神经网络(3D-CNN)结合机载高光谱和LiDAR数据,对树种进行了精确分类。这种方法不仅显著提高了树种分类的准确性,而且展示了AI技术在处理复杂高光谱数据方面的强大能力。
通过3D-CNN,研究人员能够自动提取数据中的空间和光谱特征,这比传统的机器学习方法如支持向量机和随机森林更加高效。研究结果表明,3D-CNN模型在区分针叶树种方面尤其有效,总体准确率达到了87%。此外,这种方法还可以生成详细的树种分布图,有助于可持续林业和生物多样性保护。
由此可见,AI技术在遥感中的应用不仅提升了数据处理效率,还带来了新的研究思路和创新方法。这样的研究成果为我们提供了新的视角,让我们对遥感技术的未来充满期待。赶紧复现这个idea,看看效果如何吧!
数据
通常日常生活中遇到的图像包含 3 个波段,即 RGB。卫星图像包含三个以上的波段,下图显示了一个包含 R行、C列和 B个波段的卫星图像立方体。sentinel-2 具有不同波长和分辨率的沿海气溶胶、近红外 (NIR)、短波红外 (SWIR) 和 RGB 波段组成。
![alt](https://img-blog.csdnimg.cn/img_convert/ed5b319ea10bc3e552f879e1e049493e.png)
以Sundarbans地区为例(是世界上最大的红树林区之一),特别适合做分类任务。
![alt](https://img-blog.csdnimg.cn/img_convert/09ead4da3dad4f0a83b61a81682a8b00.png)
上面的地图显示了 Sundarbans 地区。我们使用 2020 年 1 月 27 日Sentinel-2卫星数据。
波段信息如图:
Band No | Name | Cental Wavelenth(micro meters) | Resolution(meters) | |
---|---|---|---|---|
1 | Coastal aerosol | 0.443 | 60 | |
2 | Blue | 0.490 | 10 | |
3 | Green | 0.560 | 10 | |
4 | Red | 0.665 | 10 | |
5 | Vegetation Red Edge | 0.405 | 20 | |
6 | Vegetation Red Edge | 0.705 | 20 | |
7 | Vegetation Red Edge | 0.740 | 20 | |
8 | NIR | 0.842 | 10 | |
8A | Vegetation Red Edge | 0.965 | 20 | |
9 | Water Vapour | 0.945 | 60 | |
10 | SWIR-Cirrus | 1.375 | 60 | |
11 | SWIR | 1.610 | 20 | |
12 | SWIR | 2.190 | 20 |
接下来读取数据
rasterio
并使用 方法将它们堆叠成一个 n 维数组numpy.stack()
。堆叠后的结果数据具有形状 (12, 954, 298)。使用scipy.io包中的loadmat方法读取卫星图像的地面实况。地面实况有 6 个类别,包括水、植物、树木、裸地等
from glob import glob
import numpy as np
from scipy.io import loadmat
import rasterio as rio
S_sentinel_bands = glob("/content/drive/MyDrive/Satellite_data/sundarbans_data/*B?*.tiff")
S_sentinel_bands.sort()
l = []
for i in S_sentinel_bands:
with rio.open(i, 'r') as f:
l.append(f.read(1))
# Data
arr_st = np.stack(l)
# Ground Truth
y_data = loadmat('Sundarbands_gt.mat')['gt']
这些 Sundarbans 数据有多个波段,包含从可见光到红外的数据。因此,肉眼很难看到这些数据。借助 RGB 合成图像,可以更轻松地有效理解这些数据。
要绘制 RGB 合成图像,使用红色、绿色和蓝色波段(波段 4、3 和 2),我们可以可视化合成图像。
使用EarthPy包中的方法plot_rgb
对卫星图像的合成图像进行可视化。
ep.plot_rgb(
arr_st,
rgb=(3, 2, 1),
stretch=True,
str_clip=0.02,
figsize=(12, 16),
# title="RGB Composite Image with Stretch Applied",
)
plt.show()
# Visualize Groundtruth
ep.plot_bands(y_data,
cmap=ListedColormap(['darkgreen', 'green', 'black',
'#CA6F1E', 'navy', 'forestgreen']))
plt.show()
![alt](https://img-blog.csdnimg.cn/img_convert/acc74e506f18fe8a55ac62f823136c5f.png)
数据包含 12 个波段。让我们使用 EarhPy 包可视化每个波段。该方法plot_bands()
采用波段自定义标题
ep.plot_bands(arr_st,
cmap = 'gist_earth',
figsize = (20, 12),
cols = 6,
cbar = False)
plt.show()
![image-20240808234310950](https://img-blog.csdnimg.cn/img_convert/c3aaf1d502087050ac50b3906496929e.jpeg)
3D CNN
3D CNN 需要三维数据作为输入,因此我们需要将卫星图像分解成块,每个块都有一个类。块的类标签已定义为块中心像素的类。每个块都有尺寸 (W、W、B)。其中 W 和 B 是窗口大小和波段数。下图描绘了卫星图像中的块。
![image-20240808234333109](https://img-blog.csdnimg.cn/img_convert/cad2d42875f8a4462b36cb7be2e1fa1a.png)
让我们通过对数据应用主成分分析 (PCA) 来创建 Sundarbans 卫星图像的三维块。以下代码用于创建实现 PCA、创建 3D 块以及将数据按 7:3 的比例拆分为训练数据和测试数据
def applyPCA(X, numComponents=75):
newX = np.reshape(X, (-1, X.shape[2]))
pca = PCA(n_components=numComponents, whiten=True)
newX = pca.fit_transform(newX)
newX = np.reshape(newX, (X.shape[0],X.shape[1], numComponents))
return newX, pca
def padWithZeros(X, margin=2):
newX = np.zeros((X.shape[0] + 2 * margin, X.shape[1] + 2* margin, X.shape[2]))
x_offset = margin
y_offset = margin
newX[x_offset:X.shape[0] + x_offset, y_offset:X.shape[1] + y_offset, :] = X
return newX
def createImageCubes(X, y, windowSize=5, removeZeroLabels = False):
margin = int((windowSize - 1) / 2)
zeroPaddedX = padWithZeros(X, margin=margin)
# split patches
patchesData = np.zeros((X.shape[0] * X.shape[1], windowSize, windowSize, X.shape[2]))
patchesLabels = np.zeros((X.shape[0] * X.shape[1]))
patchIndex = 0
for r in range(margin, zeroPaddedX.shape[0] - margin):
for c in range(margin, zeroPaddedX.shape[1] - margin):
patch = zeroPaddedX[r - margin:r + margin + 1, c - margin:c + margin + 1]
patchesData[patchIndex, :, :, :] = patch
patchesLabels[patchIndex] = y[r-margin, c-margin]
patchIndex = patchIndex + 1
if removeZeroLabels:
patchesData = patchesData[patchesLabels>0,:,:,:]
patchesLabels = patchesLabels[patchesLabels>0]
patchesLabels -= 1
return patchesData, patchesLabels
def splitTrainTestSet(X, y, testRatio, randomState=42):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=testRatio, random_state=randomState, stratify=y)
return X_train, X_test, y_train, y_test
## GLOBAL VARIABLES
dataset = 'SB'
test_size = 0.30
windowSize = 15
MODEL_NAME = 'Sundarbans'
path = '/content/drive/MyDrive/Sundarbans/'
X_data = np.moveaxis(arr_st, 0, -1)
y_data = loadmat('Sundarbands_gt.mat')['gt']
# Apply PCA
K = 5
X,pca = applyPCA(X_data,numComponents=K)
print(f'Data After PCA: {X.shape}')
# Create 3D Patches
X, y = createImageCubes(X, y_data, windowSize=windowSize)
print(f'Patch size: {X.shape}')
# Split train and test
X_train, X_test, y_train, y_test = splitTrainTestSet(X, y, testRatio = test_size)
X_train = X_train.reshape(-1, windowSize, windowSize, K, 1)
X_test = X_test.reshape(-1, windowSize, windowSize, K, 1)
# One Hot Encoding
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
print(f'Train: {X_train.shape}\nTest: {X_test.shape}\nTrain Labels: {y_train.shape}\nTest Labels: {y_test.shape}')
让我们构建一个具有多个层的三维 CNN,例如卷积层、Dropout 层和密集层。以下代码用于使用 TensorFlow 创建用于土地覆盖分类的 3D-CNN。
S = windowSize
L = K
output_units = y_train.shape[1]
## input layer
input_layer = Input((S, S, L, 1))
## convolutional layers
conv_layer1 = Conv3D(filters=16, kernel_size=(2, 2, 3), activation='relu')(input_layer)
conv_layer2 = Conv3D(filters=32, kernel_size=(2, 2, 3), activation='relu')(conv_layer1)
conv2d_shape = conv_layer2.shape
conv_layer3 = Reshape((conv2d_shape[1], conv2d_shape[2], conv2d_shape[3]*conv2d_shape[4]))(conv_layer2)
conv_layer4 = Conv2D(filters=64, kernel_size=(2,2), activation='relu')(conv_layer3)
flatten_layer = Flatten()(conv_layer4)
## fully connected layers
dense_layer1 = Dense(128, activation='relu')(flatten_layer)
dense_layer1 = Dropout(0.4)(dense_layer1)
dense_layer2 = Dense(64, activation='relu')(dense_layer1)
dense_layer2 = Dropout(0.4)(dense_layer2)
dense_layer3 = Dense(20, activation='relu')(dense_layer2)
dense_layer3 = Dropout(0.4)(dense_layer3)
output_layer = Dense(units=output_units, activation='softmax')(dense_layer3)
# define the model with input layer and output layer
model = Model(name = dataset+'_Model' , inputs=input_layer, outputs=output_layer)
model.summary()
3D-CNN 模型共有 1,204,098 个可训练参数。下图显示了开发的 3D-CNN 模型的摘要。
![alt](https://img-blog.csdnimg.cn/img_convert/11028d314d10b6abf01fd937616f2920.png)
训练模型
使用了Adam优化器、分类交叉熵、准确度作为指标
以下代码用于编译和训练模型。
EarlyStopping:减少神经网络过度拟合的一种方法是使用 Early Stopping。如果模型没有真正学到任何东西,Early Stopping 会终止训练过程
ModelCheckpoint:此回调将在每次成功执行 epoch 后将您的模型作为检查点文件(hdf5 or h5
格式)保存到电脑
TensorBoard:此回调在每epoch训练后记录
# Compile
model.compile(optimizer = 'adam', loss = 'categorical_crossentropy',
metrics = ['accuracy'])
# Callbacks
logdir = path+"logs/" +model.name+'_'+datetime.now().strftime("%d:%m:%Y-%H:%M:%S")
tensorboard_callback = TensorBoard(log_dir=logdir)
es = EarlyStopping(monitor = 'val_loss',
min_delta = 0,
patience = 1,
verbose = 1,
restore_best_weights = True)
checkpoint = ModelCheckpoint(filepath = 'Pavia_University_Model.h5',
monitor = 'val_loss',
mode ='min',
save_best_only = True,
verbose = 1)
# Fit
history = model.fit(x=X_train, y=y_train,
batch_size=1024*6, epochs=6,
validation_data=(X_test, y_test), callbacks = [tensorboard_callback, es, checkpoint])
训练的准确率和损失图如下所示
import pandas as pd
history = pd.DataFrame(history.history)
plt.figure(figsize = (12, 6))
plt.plot(range(len(history['accuracy'].values.tolist())), history['accuracy'].values.tolist(), label = 'Train_Accuracy')
plt.plot(range(len(history['loss'].values.tolist())), history['loss'].values.tolist(), label = 'Train_Loss')
plt.plot(range(len(history['val_accuracy'].values.tolist())), history['val_accuracy'].values.tolist(), label = 'Test_Accuracy')
plt.plot(range(len(history['val_loss'].values.tolist())), history['val_loss'].values.tolist(), label = 'Test_Loss')
plt.xlabel('Epochs')
plt.ylabel('Value')
plt.legend()
plt.show()
![alt](https://img-blog.csdnimg.cn/img_convert/a6fcbf6252661731479b16bc04f030b0.png)
结果
训练后的 CNN 模型准确率为95.60%,让我们看一下混淆矩阵
pred = model.predict(X_test, batch_size=1204*6, verbose=1)
plt.figure(figsize = (10,7))
classes = [f'Class-{i}' for i in range(1, 7)]
mat = confusion_matrix(np.argmax(y_test, 1),
np.argmax(pred, 1))
df_cm = pd.DataFrame(mat, index = classes, columns = classes)
sns.heatmap(df_cm, annot=True, fmt='d')
plt.show()
![alt](https://img-blog.csdnimg.cn/img_convert/afeae98b69f78f3a4c4851007f0d1900.png)
分类报告显示准确率以及类别精度、召回率、f1 分数和支持度。输出如下所示:
# Classification Report
print(classification_report(np.argmax(y_test, 1),
np.argmax(pred, 1),
target_names = [f'Class-{i}' for i in range(1, 7)]))
![alt](https://img-blog.csdnimg.cn/img_convert/aea5f657826e3592c3634da0e1893d11.png)
最后,让我们可视化 Sundarbans 卫星的分类图。以下代码用于创建分类图。该方法perdict
用于预测整个数据的标签,并将其重塑为原始大小,并使用该方法进行可视化plot_bands
。
pred_t = model.predict(X.reshape(-1, windowSize, windowSize, K, 1),
batch_size=1204*6, verbose=1)
# Visualize Groundtruth
ep.plot_bands(np.argmax(pred_t, axis=1).reshape(954, 298),
cmap=ListedColormap(['darkgreen', 'green', 'black',
'#CA6F1E', 'navy', 'forestgreen']))
plt.show()
![alt](https://img-blog.csdnimg.cn/img_convert/dccf6942d69fc5d163ce11b13bda5a0d.png)
结论
回顾本文基本复现了RSE的方法和各种图,准确度优于原文章的同时,也提供了一种新的方法。收藏点在看学起来,你也能发表一区TOP论文。