Repository: suvojit-0x55aa/A2S2K-ResNet
Branch: master
Commit: 0bba673d6343
Files: 24
Total size: 157.7 KB
Directory structure:
gitextract_ts7231nk/
├── A2S2KResNet/
│ ├── A2S2KResNet.py
│ ├── Utils.py
│ ├── geniter.py
│ └── record.py
├── ContextualNet/
│ ├── ContextualNet.py
│ ├── Utils.py
│ ├── geniter.py
│ └── record.py
├── PyResNet/
│ ├── PyResNet.py
│ ├── Utils.py
│ ├── geniter.py
│ └── record.py
├── README.md
├── ResNet/
│ ├── ResNet.py
│ ├── Utils.py
│ ├── geniter.py
│ └── record.py
├── SSRN/
│ ├── SSRN.py
│ ├── Utils.py
│ ├── geniter.py
│ └── record.py
├── convert_report_to_csv.py
├── environment.yml
└── setup_script.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: A2S2KResNet/A2S2KResNet.py
================================================
#!/usr/bin/env python
# coding: utf-8
# # Imports
import argparse
import collections
import math
import time
import numpy as np
import scipy.io as sio
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from sklearn import metrics, preprocessing
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix
import geniter
import record
import torch_optimizer as optim2
import Utils
from torchsummary import summary
# # Setting Params
parser = argparse.ArgumentParser(description='Training for HSI')
parser.add_argument(
'-d', '--dataset', dest='dataset', default='IN', help="Name of dataset.")
parser.add_argument(
'-o',
'--optimizer',
dest='optimizer',
default='adam',
help="Name of optimizer.")
parser.add_argument(
'-e', '--epoch', type=int, dest='epoch', default=200, help="No of epoch")
parser.add_argument(
'-i', '--iter', type=int, dest='iter', default=3, help="No of iter")
parser.add_argument(
'-p', '--patch', type=int, dest='patch', default=4, help="Length of patch")
parser.add_argument(
'-k',
'--kernel',
type=int,
dest='kernel',
default=24,
help="Length of kernel")
parser.add_argument(
'-vs',
'--valid_split',
type=float,
dest='valid_split',
default=0.9,
help="Percentage of validation split.")
args = parser.parse_args()
PARAM_DATASET = args.dataset # UP,IN,SV, KSC
PARAM_EPOCH = args.epoch
PARAM_ITER = args.iter
PATCH_SIZE = args.patch
PARAM_VAL = args.valid_split
PARAM_OPTIM = args.optimizer
PARAM_KERNEL_SIZE = args.kernel
# # Data Loading
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# for Monte Carlo runs
seeds = [1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341]
ensemble = 1
global Dataset # UP,IN,SV, KSC
dataset = PARAM_DATASET # input('Please input the name of Dataset(IN, UP, SV, KSC):')
Dataset = dataset.upper()
def load_dataset(Dataset, split=0.9):
data_path = '../dataset/'
if Dataset == 'IN':
mat_data = sio.loadmat(data_path + 'Indian_pines_corrected.mat')
mat_gt = sio.loadmat(data_path + 'Indian_pines_gt.mat')
data_hsi = mat_data['indian_pines_corrected']
gt_hsi = mat_gt['indian_pines_gt']
K = 200
TOTAL_SIZE = 10249
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'UP':
uPavia = sio.loadmat(data_path + 'PaviaU.mat')
gt_uPavia = sio.loadmat(data_path + 'PaviaU_gt.mat')
data_hsi = uPavia['paviaU']
gt_hsi = gt_uPavia['paviaU_gt']
K = 103
TOTAL_SIZE = 42776
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'SV':
SV = sio.loadmat(data_path + 'Salinas_corrected.mat')
gt_SV = sio.loadmat(data_path + 'Salinas_gt.mat')
data_hsi = SV['salinas_corrected']
gt_hsi = gt_SV['salinas_gt']
K = 15
TOTAL_SIZE = 54129
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'KSC':
SV = sio.loadmat(data_path + 'KSC.mat')
gt_SV = sio.loadmat(data_path + 'KSC_gt.mat')
data_hsi = SV['KSC']
gt_hsi = gt_SV['KSC_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 5211
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
shapeor = data_hsi.shape
data_hsi = data_hsi.reshape(-1, data_hsi.shape[-1])
data_hsi = PCA(n_components=K).fit_transform(data_hsi)
shapeor = np.array(shapeor)
shapeor[-1] = K
data_hsi = data_hsi.reshape(shapeor)
return data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT
# # Pytorch Data Loader Creation
data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT = load_dataset(
Dataset, PARAM_VAL)
print(data_hsi.shape)
image_x, image_y, BAND = data_hsi.shape
data = data_hsi.reshape(
np.prod(data_hsi.shape[:2]), np.prod(data_hsi.shape[2:]))
gt = gt_hsi.reshape(np.prod(gt_hsi.shape[:2]), )
CLASSES_NUM = max(gt)
print('The class numbers of the HSI data is:', CLASSES_NUM)
print('-----Importing Setting Parameters-----')
ITER = PARAM_ITER
PATCH_LENGTH = PATCH_SIZE
lr, num_epochs, batch_size = 0.001, 200, 32
loss = torch.nn.CrossEntropyLoss()
img_rows = 2 * PATCH_LENGTH + 1
img_cols = 2 * PATCH_LENGTH + 1
img_channels = data_hsi.shape[2]
INPUT_DIMENSION = data_hsi.shape[2]
ALL_SIZE = data_hsi.shape[0] * data_hsi.shape[1]
VAL_SIZE = int(TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
KAPPA = []
OA = []
AA = []
TRAINING_TIME = []
TESTING_TIME = []
ELEMENT_ACC = np.zeros((ITER, CLASSES_NUM))
data = preprocessing.scale(data)
data_ = data.reshape(data_hsi.shape[0], data_hsi.shape[1], data_hsi.shape[2])
whole_data = data_
padded_data = np.lib.pad(
whole_data, ((PATCH_LENGTH, PATCH_LENGTH), (PATCH_LENGTH, PATCH_LENGTH),
(0, 0)),
'constant',
constant_values=0)
# # Model
class ChannelSELayer3D(nn.Module):
"""
3D extension of Squeeze-and-Excitation (SE) block described in:
*Hu et al., Squeeze-and-Excitation Networks, arXiv:1709.01507*
*Zhu et al., AnatomyNet, arXiv:arXiv:1808.05238*
"""
def __init__(self, num_channels, reduction_ratio=2):
"""
:param num_channels: No of input channels
:param reduction_ratio: By how much should the num_channels should be reduced
"""
super(ChannelSELayer3D, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool3d(1)
num_channels_reduced = num_channels // reduction_ratio
self.reduction_ratio = reduction_ratio
self.fc1 = nn.Linear(num_channels, num_channels_reduced, bias=True)
self.fc2 = nn.Linear(num_channels_reduced, num_channels, bias=True)
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()
def forward(self, input_tensor):
"""
:param input_tensor: X, shape = (batch_size, num_channels, D, H, W)
:return: output tensor
"""
batch_size, num_channels, D, H, W = input_tensor.size()
# Average along each channel
squeeze_tensor = self.avg_pool(input_tensor)
# channel excitation
fc_out_1 = self.relu(
self.fc1(squeeze_tensor.view(batch_size, num_channels)))
fc_out_2 = self.sigmoid(self.fc2(fc_out_1))
output_tensor = torch.mul(
input_tensor, fc_out_2.view(batch_size, num_channels, 1, 1, 1))
return output_tensor
class SpatialSELayer3D(nn.Module):
"""
3D extension of SE block -- squeezing spatially and exciting channel-wise described in:
*Roy et al., Concurrent Spatial and Channel Squeeze & Excitation in Fully Convolutional Networks, MICCAI 2018*
"""
def __init__(self, num_channels):
"""
:param num_channels: No of input channels
"""
super(SpatialSELayer3D, self).__init__()
self.conv = nn.Conv3d(num_channels, 1, 1)
self.sigmoid = nn.Sigmoid()
def forward(self, input_tensor, weights=None):
"""
:param weights: weights for few shot learning
:param input_tensor: X, shape = (batch_size, num_channels, D, H, W)
:return: output_tensor
"""
# channel squeeze
batch_size, channel, D, H, W = input_tensor.size()
if weights:
weights = weights.view(1, channel, 1, 1)
out = F.conv2d(input_tensor, weights)
else:
out = self.conv(input_tensor)
squeeze_tensor = self.sigmoid(out)
# spatial excitation
output_tensor = torch.mul(input_tensor,
squeeze_tensor.view(batch_size, 1, D, H, W))
return output_tensor
class ChannelSpatialSELayer3D(nn.Module):
"""
3D extension of concurrent spatial and channel squeeze & excitation:
*Roy et al., Concurrent Spatial and Channel Squeeze & Excitation in Fully Convolutional Networks, arXiv:1803.02579*
"""
def __init__(self, num_channels, reduction_ratio=2):
"""
:param num_channels: No of input channels
:param reduction_ratio: By how much should the num_channels should be reduced
"""
super(ChannelSpatialSELayer3D, self).__init__()
self.cSE = ChannelSELayer3D(num_channels, reduction_ratio)
self.sSE = SpatialSELayer3D(num_channels)
def forward(self, input_tensor):
"""
:param input_tensor: X, shape = (batch_size, num_channels, D, H, W)
:return: output_tensor
"""
output_tensor = torch.max(
self.cSE(input_tensor), self.sSE(input_tensor))
return output_tensor
class ProjectExciteLayer(nn.Module):
"""
Project & Excite Module, specifically designed for 3D inputs
*quote*
"""
def __init__(self, num_channels, reduction_ratio=2):
"""
:param num_channels: No of input channels
:param reduction_ratio: By how much should the num_channels should be reduced
"""
super(ProjectExciteLayer, self).__init__()
num_channels_reduced = num_channels // reduction_ratio
self.reduction_ratio = reduction_ratio
self.relu = nn.ReLU()
self.conv_c = nn.Conv3d(
in_channels=num_channels,
out_channels=num_channels_reduced,
kernel_size=1,
stride=1)
self.conv_cT = nn.Conv3d(
in_channels=num_channels_reduced,
out_channels=num_channels,
kernel_size=1,
stride=1)
self.sigmoid = nn.Sigmoid()
def forward(self, input_tensor):
"""
:param input_tensor: X, shape = (batch_size, num_channels, D, H, W)
:return: output tensor
"""
batch_size, num_channels, D, H, W = input_tensor.size()
# Average along channels and different axes
squeeze_tensor_w = F.adaptive_avg_pool3d(input_tensor, (1, 1, W))
squeeze_tensor_h = F.adaptive_avg_pool3d(input_tensor, (1, H, 1))
squeeze_tensor_d = F.adaptive_avg_pool3d(input_tensor, (D, 1, 1))
# tile tensors to original size and add:
final_squeeze_tensor = sum([
squeeze_tensor_w.view(batch_size, num_channels, 1, 1, W),
squeeze_tensor_h.view(batch_size, num_channels, 1, H, 1),
squeeze_tensor_d.view(batch_size, num_channels, D, 1, 1)
])
# Excitation:
final_squeeze_tensor = self.sigmoid(
self.conv_cT(self.relu(self.conv_c(final_squeeze_tensor))))
output_tensor = torch.mul(input_tensor, final_squeeze_tensor)
return output_tensor
class eca_layer(nn.Module):
"""Constructs a ECA module.
Args:
channel: Number of channels of the input feature map
k_size: Adaptive selection of kernel size
"""
def __init__(self, channel, k_size=3):
super(eca_layer, self).__init__()
self.avg_pool = nn.AdaptiveAvgPool3d(1)
self.conv = nn.Conv2d(
1, 1, kernel_size=k_size, padding=(k_size - 1) // 2, bias=False)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
# x: input features with shape [b, c, h, w]
b, c, h, w, t = x.size()
# feature descriptor on the global spatial information
# 24, 1, 1, 1
y = self.avg_pool(x)
# Two different branches of ECA module
y = self.conv(y.squeeze(-1).transpose(-1, -3)).transpose(
-1, -3).unsqueeze(-1)
# Multi-scale information fusion
y = self.sigmoid(y)
return x * y.expand_as(x)
class Residual(nn.Module): # pytorch
def __init__(
self,
in_channels,
out_channels,
kernel_size,
padding,
use_1x1conv=False,
stride=1,
start_block=False,
end_block=False,
):
super(Residual, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv3d(
in_channels,
out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride), nn.ReLU())
self.conv2 = nn.Conv3d(
out_channels,
out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride)
if use_1x1conv:
self.conv3 = nn.Conv3d(
in_channels, out_channels, kernel_size=1, stride=stride)
else:
self.conv3 = None
if not start_block:
self.bn0 = nn.BatchNorm3d(in_channels)
self.bn1 = nn.BatchNorm3d(out_channels)
self.bn2 = nn.BatchNorm3d(out_channels)
if start_block:
self.bn2 = nn.BatchNorm3d(out_channels)
if end_block:
self.bn2 = nn.BatchNorm3d(out_channels)
# ECA Attention Layer
self.ecalayer = eca_layer(out_channels)
# start and end block initialization
self.start_block = start_block
self.end_block = end_block
def forward(self, X):
identity = X
if self.start_block:
out = self.conv1(X)
else:
out = self.bn0(X)
out = F.relu(out)
out = self.conv1(out)
out = self.bn1(out)
out = F.relu(out)
out = self.conv2(out)
if self.start_block:
out = self.bn2(out)
out = self.ecalayer(out)
out += identity
if self.end_block:
out = self.bn2(out)
out = F.relu(out)
return out
class S3KAIResNet(nn.Module):
def __init__(self, band, classes, reduction):
super(S3KAIResNet, self).__init__()
self.name = 'SSRN'
self.conv1x1 = nn.Conv3d(
in_channels=1,
out_channels=PARAM_KERNEL_SIZE,
kernel_size=(1, 1, 7),
stride=(1, 1, 2),
padding=0)
self.conv3x3 = nn.Conv3d(
in_channels=1,
out_channels=PARAM_KERNEL_SIZE,
kernel_size=(3, 3, 7),
stride=(1, 1, 2),
padding=(1, 1, 0))
self.batch_norm1x1 = nn.Sequential(
nn.BatchNorm3d(
PARAM_KERNEL_SIZE, eps=0.001, momentum=0.1,
affine=True), # 0.1
nn.ReLU(inplace=True))
self.batch_norm3x3 = nn.Sequential(
nn.BatchNorm3d(
PARAM_KERNEL_SIZE, eps=0.001, momentum=0.1,
affine=True), # 0.1
nn.ReLU(inplace=True))
self.pool = nn.AdaptiveAvgPool3d(1)
self.conv_se = nn.Sequential(
nn.Conv3d(
PARAM_KERNEL_SIZE, band // reduction, 1, padding=0, bias=True),
nn.ReLU(inplace=True))
self.conv_ex = nn.Conv3d(
band // reduction, PARAM_KERNEL_SIZE, 1, padding=0, bias=True)
self.softmax = nn.Softmax(dim=1)
self.res_net1 = Residual(
PARAM_KERNEL_SIZE,
PARAM_KERNEL_SIZE, (1, 1, 7), (0, 0, 3),
start_block=True)
self.res_net2 = Residual(PARAM_KERNEL_SIZE, PARAM_KERNEL_SIZE,
(1, 1, 7), (0, 0, 3))
self.res_net3 = Residual(PARAM_KERNEL_SIZE, PARAM_KERNEL_SIZE,
(3, 3, 1), (1, 1, 0))
self.res_net4 = Residual(
PARAM_KERNEL_SIZE,
PARAM_KERNEL_SIZE, (3, 3, 1), (1, 1, 0),
end_block=True)
kernel_3d = math.ceil((band - 6) / 2)
# print(kernel_3d)
self.conv2 = nn.Conv3d(
in_channels=PARAM_KERNEL_SIZE,
out_channels=128,
padding=(0, 0, 0),
kernel_size=(1, 1, kernel_3d),
stride=(1, 1, 1))
self.batch_norm2 = nn.Sequential(
nn.BatchNorm3d(128, eps=0.001, momentum=0.1, affine=True), # 0.1
nn.ReLU(inplace=True))
self.conv3 = nn.Conv3d(
in_channels=1,
out_channels=PARAM_KERNEL_SIZE,
padding=(0, 0, 0),
kernel_size=(3, 3, 128),
stride=(1, 1, 1))
self.batch_norm3 = nn.Sequential(
nn.BatchNorm3d(
PARAM_KERNEL_SIZE, eps=0.001, momentum=0.1,
affine=True), # 0.1
nn.ReLU(inplace=True))
self.avg_pooling = nn.AvgPool3d(kernel_size=(5, 5, 1))
self.full_connection = nn.Sequential(
nn.Linear(PARAM_KERNEL_SIZE, classes)
# nn.Softmax()
)
def forward(self, X):
x_1x1 = self.conv1x1(X)
x_1x1 = self.batch_norm1x1(x_1x1).unsqueeze(dim=1)
x_3x3 = self.conv3x3(X)
x_3x3 = self.batch_norm3x3(x_3x3).unsqueeze(dim=1)
x1 = torch.cat([x_3x3, x_1x1], dim=1)
U = torch.sum(x1, dim=1)
S = self.pool(U)
Z = self.conv_se(S)
attention_vector = torch.cat(
[
self.conv_ex(Z).unsqueeze(dim=1),
self.conv_ex(Z).unsqueeze(dim=1)
],
dim=1)
attention_vector = self.softmax(attention_vector)
V = (x1 * attention_vector).sum(dim=1)
x2 = self.res_net1(V)
x2 = self.res_net2(x2)
x2 = self.batch_norm2(self.conv2(x2))
x2 = x2.permute(0, 4, 2, 3, 1)
x2 = self.batch_norm3(self.conv3(x2))
x3 = self.res_net3(x2)
x3 = self.res_net4(x3)
x4 = self.avg_pooling(x3)
x4 = x4.view(x4.size(0), -1)
return self.full_connection(x4)
model = S3KAIResNet(BAND, CLASSES_NUM, 2).cuda()
summary(model, input_data=(1, img_rows, img_cols, BAND), verbose=1)
def train(net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs,
early_stopping=True,
early_num=20):
loss_list = [100]
early_epoch = 0
net = net.to(device)
print("training on ", device)
start = time.time()
train_loss_list = []
valida_loss_list = []
train_acc_list = []
valida_acc_list = []
for epoch in range(epochs):
train_acc_sum, n = 0.0, 0
time_epoch = time.time()
lr_adjust = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer, 15, eta_min=0.0, last_epoch=-1)
for X, y in train_iter:
batch_count, train_l_sum = 0, 0
X = X.to(device)
y = y.to(device)
y_hat = net(X)
l = loss(y_hat, y.long())
optimizer.zero_grad()
l.backward()
optimizer.step()
train_l_sum += l.cpu().item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
lr_adjust.step()
valida_acc, valida_loss = record.evaluate_accuracy(
valida_iter, net, loss, device)
loss_list.append(valida_loss)
train_loss_list.append(train_l_sum) # / batch_count)
train_acc_list.append(train_acc_sum / n)
valida_loss_list.append(valida_loss)
valida_acc_list.append(valida_acc)
print(
'epoch %d, train loss %.6f, train acc %.3f, valida loss %.6f, valida acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
valida_loss, valida_acc, time.time() - time_epoch))
PATH = "./net_DBA.pt"
if early_stopping and loss_list[-2] < loss_list[-1]:
if early_epoch == 0:
torch.save(net.state_dict(), PATH)
early_epoch += 1
loss_list[-1] = loss_list[-2]
if early_epoch == early_num:
net.load_state_dict(torch.load(PATH))
break
else:
early_epoch = 0
print('epoch %d, loss %.4f, train acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
time.time() - start))
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def select(groundTruth): #divide dataset into train and test datasets
labels_loc = {}
train = {}
test = {}
m = max(groundTruth)
#amount = [3, 41, 29, 7, 14, 20, 2, 15, 3, 36, 64, 22, 4, 28, 10, 2]
#amount = [43, 1387, 801, 230, 469, 710, 26, 463, 17, 936, 2391, 571, 201, 1237, 376, 91]
if Dataset == 'IN':
amount = [
35, 1011, 581, 167, 344, 515, 19, 327, 12, 683, 1700, 418, 138,
876, 274, 69
] #IP 20%
#amount = [6, 144, 84, 24, 50, 75, 3, 49, 2, 97, 247, 62, 22, 130, 38, 10] #IP 20%
if Dataset == 'UP':
amount = [5297, 14974, 1648, 2424, 1076, 4026, 1046, 2950, 755] #UP
if Dataset == 'KSC':
amount = [
530, 165, 176, 170, 110, 161, 80, 299, 377, 283, 296, 341, 654
] #KSC
for i in range(m):
indices = [
j for j, x in enumerate(groundTruth.ravel().tolist()) if x == i + 1
]
np.random.shuffle(indices)
labels_loc[i] = indices
nb_val = int(amount[i])
train[i] = indices[:-nb_val]
test[i] = indices[-nb_val:]
# whole_indices = []
train_indices = []
test_indices = []
for i in range(m):
# whole_indices += labels_loc[i]
train_indices += train[i]
test_indices += test[i]
np.random.shuffle(train_indices)
np.random.shuffle(test_indices)
return train_indices, test_indices
# # Training
for index_iter in range(ITER):
print('iter:', index_iter)
#define the model
net = S3KAIResNet(BAND, CLASSES_NUM, 2)
if PARAM_OPTIM == 'diffgrad':
optimizer = optim2.DiffGrad(
net.parameters(),
lr=lr,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0) # weight_decay=0.0001)
if PARAM_OPTIM == 'adam':
optimizer = optim.Adam(
net.parameters(),
lr=1e-3,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0)
time_1 = int(time.time())
np.random.seed(seeds[index_iter])
# train_indices, test_indices = select(gt)
train_indices, test_indices = sampling(VALIDATION_SPLIT, gt)
_, total_indices = sampling(1, gt)
TRAIN_SIZE = len(train_indices)
print('Train size: ', TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
print('Test size: ', TEST_SIZE)
VAL_SIZE = int(TRAIN_SIZE)
print('Validation size: ', VAL_SIZE)
print('-----Selecting Small Pieces from the Original Cube Data-----')
train_iter, valida_iter, test_iter, all_iter = geniter.generate_iter(
TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE,
total_indices, VAL_SIZE, whole_data, PATCH_LENGTH, padded_data,
INPUT_DIMENSION, 16, gt) #batchsize in 1
tic1 = time.time()
train(
net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs=PARAM_EPOCH)
toc1 = time.time()
pred_test = []
tic2 = time.time()
with torch.no_grad():
for X, y in test_iter:
# print('Shape of X', X.shape, 'Shape of y', y.shape)
# X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
y_hat = net(X)
pred_test.extend(np.array(net(X).cpu().argmax(axis=1)))
toc2 = time.time()
collections.Counter(pred_test)
gt_test = gt[test_indices] - 1
overall_acc = metrics.accuracy_score(pred_test, gt_test[:-VAL_SIZE])
confusion_matrix = metrics.confusion_matrix(pred_test, gt_test[:-VAL_SIZE])
each_acc, average_acc = record.aa_and_each_accuracy(confusion_matrix)
kappa = metrics.cohen_kappa_score(pred_test, gt_test[:-VAL_SIZE])
torch.save(
net.state_dict(), "./models/S3KAIResNetpatch_" + str(img_rows) + '_' +
Dataset + '_split_' + str(VALIDATION_SPLIT) + '_lr_' + str(lr) +
PARAM_OPTIM + '_kernel_' + str(PARAM_KERNEL_SIZE) + str(
round(overall_acc, 3)) + '.pt')
KAPPA.append(kappa)
OA.append(overall_acc)
AA.append(average_acc)
TRAINING_TIME.append(toc1 - tic1)
TESTING_TIME.append(toc2 - tic2)
ELEMENT_ACC[index_iter, :] = each_acc
# # Map, Records
print("--------" + " Training Finished-----------")
record.record_output(
OA, AA, KAPPA, ELEMENT_ACC, TRAINING_TIME, TESTING_TIME,
'./report/' + 'S3KAIResNetpatch:' + str(img_rows) + '_' + Dataset + 'split'
+ str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM + '_kernel_' +
str(PARAM_KERNEL_SIZE) + '.txt')
Utils.generate_png(
all_iter, net, gt_hsi, Dataset, device, total_indices,
'./classification_maps/' + 'S3KAIResNetpatch:' + str(img_rows) + '_' +
Dataset + 'split' + str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM +
'_kernel_' + str(PARAM_KERNEL_SIZE))
================================================
FILE: A2S2KResNet/Utils.py
================================================
import numpy as np
from sklearn import metrics, preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, cohen_kappa_score
from operator import truediv
import matplotlib.pyplot as plt
import scipy.io as sio
import os
import spectral
import torch
import cv2
from operator import truediv
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def set_figsize(figsize=(3.5, 2.5)):
display.set_matplotlib_formats('svg')
plt.rcParams['figure.figsize'] = figsize
def classification_map(map, ground_truth, dpi, save_path):
fig = plt.figure(frameon=False)
fig.set_size_inches(ground_truth.shape[1] * 2.0 / dpi,
ground_truth.shape[0] * 2.0 / dpi)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
fig.add_axes(ax)
ax.imshow(map)
fig.savefig(save_path, dpi=dpi)
return 0
def list_to_colormap(x_list):
y = np.zeros((x_list.shape[0], 3))
for index, item in enumerate(x_list):
if item == 0:
y[index] = np.array([255, 0, 0]) / 255.
if item == 1:
y[index] = np.array([0, 255, 0]) / 255.
if item == 2:
y[index] = np.array([0, 0, 255]) / 255.
if item == 3:
y[index] = np.array([255, 255, 0]) / 255.
if item == 4:
y[index] = np.array([0, 255, 255]) / 255.
if item == 5:
y[index] = np.array([255, 0, 255]) / 255.
if item == 6:
y[index] = np.array([192, 192, 192]) / 255.
if item == 7:
y[index] = np.array([128, 128, 128]) / 255.
if item == 8:
y[index] = np.array([128, 0, 0]) / 255.
if item == 9:
y[index] = np.array([128, 128, 0]) / 255.
if item == 10:
y[index] = np.array([0, 128, 0]) / 255.
if item == 11:
y[index] = np.array([128, 0, 128]) / 255.
if item == 12:
y[index] = np.array([0, 128, 128]) / 255.
if item == 13:
y[index] = np.array([0, 0, 128]) / 255.
if item == 14:
y[index] = np.array([255, 165, 0]) / 255.
if item == 15:
y[index] = np.array([255, 215, 0]) / 255.
if item == 16:
y[index] = np.array([0, 0, 0]) / 255.
if item == 17:
y[index] = np.array([215, 255, 0]) / 255.
if item == 18:
y[index] = np.array([0, 255, 215]) / 255.
if item == -1:
y[index] = np.array([0, 0, 0]) / 255.
return y
def generate_png(all_iter, net, gt_hsi, Dataset, device, total_indices, path):
pred_test = []
for X, y in all_iter:
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
pred_test.extend(net(X).cpu().argmax(axis=1).detach().numpy())
gt = gt_hsi.flatten()
x_label = np.zeros(gt.shape)
for i in range(len(gt)):
if gt[i] == 0:
gt[i] = 17
x_label[i] = 16
gt = gt[:] - 1
x_label[total_indices] = pred_test
x = np.ravel(x_label)
y_list = list_to_colormap(x)
y_gt = list_to_colormap(gt)
y_re = np.reshape(y_list, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
gt_re = np.reshape(y_gt, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
classification_map(y_re, gt_hsi, 300,
path + '.png')
classification_map(gt_re, gt_hsi, 300,
path + '_gt.png')
print('------Get classification maps successful-------')
================================================
FILE: A2S2KResNet/geniter.py
================================================
import torch
import numpy as np
import torch.utils.data as Data
def index_assignment(index, row, col, pad_length):
new_assign = {}
for counter, value in enumerate(index):
assign_0 = value // col + pad_length
assign_1 = value % col + pad_length
new_assign[counter] = [assign_0, assign_1]
return new_assign
def select_patch(matrix, pos_row, pos_col, ex_len):
selected_rows = matrix[range(pos_row-ex_len, pos_row+ex_len+1)]
selected_patch = selected_rows[:, range(pos_col-ex_len, pos_col+ex_len+1)]
return selected_patch
def select_small_cubic(data_size, data_indices, whole_data, patch_length, padded_data, dimension):
small_cubic_data = np.zeros((data_size, 2 * patch_length + 1, 2 * patch_length + 1, dimension))
data_assign = index_assignment(data_indices, whole_data.shape[0], whole_data.shape[1], patch_length)
for i in range(len(data_assign)):
small_cubic_data[i] = select_patch(padded_data, data_assign[i][0], data_assign[i][1], patch_length)
return small_cubic_data
def generate_iter(TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE, total_indices, VAL_SIZE,
whole_data, PATCH_LENGTH, padded_data, INPUT_DIMENSION, batch_size, gt):
gt_all = gt[total_indices] - 1
y_train = gt[train_indices] - 1
y_test = gt[test_indices] - 1
all_data = select_small_cubic(TOTAL_SIZE, total_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
train_data = select_small_cubic(TRAIN_SIZE, train_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
print(train_data.shape)
test_data = select_small_cubic(TEST_SIZE, test_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
x_train = train_data.reshape(train_data.shape[0], train_data.shape[1], train_data.shape[2], INPUT_DIMENSION)
x_test_all = test_data.reshape(test_data.shape[0], test_data.shape[1], test_data.shape[2], INPUT_DIMENSION)
x_val = x_test_all[-VAL_SIZE:]
y_val = y_test[-VAL_SIZE:]
x_test = x_test_all[:-VAL_SIZE]
y_test = y_test[:-VAL_SIZE]
x1_tensor_train = torch.from_numpy(x_train).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_train = torch.from_numpy(y_train).type(torch.FloatTensor)
torch_dataset_train = Data.TensorDataset(x1_tensor_train, y1_tensor_train)
x1_tensor_valida = torch.from_numpy(x_val).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_valida = torch.from_numpy(y_val).type(torch.FloatTensor)
torch_dataset_valida = Data.TensorDataset(x1_tensor_valida, y1_tensor_valida)
x1_tensor_test = torch.from_numpy(x_test).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_test = torch.from_numpy(y_test).type(torch.FloatTensor)
torch_dataset_test = Data.TensorDataset(x1_tensor_test,y1_tensor_test)
all_data.reshape(all_data.shape[0], all_data.shape[1], all_data.shape[2], INPUT_DIMENSION)
all_tensor_data = torch.from_numpy(all_data).type(torch.FloatTensor).unsqueeze(1)
all_tensor_data_label = torch.from_numpy(gt_all).type(torch.FloatTensor)
torch_dataset_all = Data.TensorDataset(all_tensor_data, all_tensor_data_label)
train_iter = Data.DataLoader(
dataset=torch_dataset_train, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
valiada_iter = Data.DataLoader(
dataset=torch_dataset_valida, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
test_iter = Data.DataLoader(
dataset=torch_dataset_test, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
all_iter = Data.DataLoader(
dataset=torch_dataset_all, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
return train_iter, valiada_iter, test_iter, all_iter #, y_test
================================================
FILE: A2S2KResNet/record.py
================================================
import numpy as np
import torch
from operator import truediv
def evaluate_accuracy(data_iter, net, loss, device):
acc_sum, n = 0.0, 0
with torch.no_grad():
for X, y in data_iter:
test_l_sum, test_num = 0, 0
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
net.eval()
y_hat = net(X)
l = loss(y_hat, y.long())
acc_sum += (y_hat.argmax(dim=1) == y.to(device)).float().sum().cpu().item()
test_l_sum += l
test_num += 1
net.train()
n += y.shape[0]
return [acc_sum / n, test_l_sum] # / test_num]
def aa_and_each_accuracy(confusion_matrix):
list_diag = np.diag(confusion_matrix)
list_raw_sum = np.sum(confusion_matrix, axis=1)
each_acc = np.nan_to_num(truediv(list_diag, list_raw_sum))
average_acc = np.mean(each_acc)
return each_acc, average_acc
def record_output(oa_ae, aa_ae, kappa_ae, element_acc_ae, training_time_ae, testing_time_ae, path):
f = open(path, 'a')
sentence0 = 'OAs for each iteration are:' + str(oa_ae) + '\n'
f.write(sentence0)
sentence1 = 'AAs for each iteration are:' + str(aa_ae) + '\n'
f.write(sentence1)
sentence2 = 'KAPPAs for each iteration are:' + str(kappa_ae) + '\n' + '\n'
f.write(sentence2)
sentence3 = 'mean_OA ± std_OA is: ' + str(np.mean(oa_ae)) + ' ± ' + str(np.std(oa_ae)) + '\n'
f.write(sentence3)
sentence4 = 'mean_AA ± std_AA is: ' + str(np.mean(aa_ae)) + ' ± ' + str(np.std(aa_ae)) + '\n'
f.write(sentence4)
sentence5 = 'mean_KAPPA ± std_KAPPA is: ' + str(np.mean(kappa_ae)) + ' ± ' + str(np.std(kappa_ae)) + '\n' + '\n'
f.write(sentence5)
sentence6 = 'Total average Training time is: ' + str(np.sum(training_time_ae)) + '\n'
f.write(sentence6)
sentence7 = 'Total average Testing time is: ' + str(np.sum(testing_time_ae)) + '\n' + '\n'
f.write(sentence7)
element_mean = np.mean(element_acc_ae, axis=0)
element_std = np.std(element_acc_ae, axis=0)
sentence8 = "Mean of all elements in confusion matrix: " + str(element_mean) + '\n'
f.write(sentence8)
sentence9 = "Standard deviation of all elements in confusion matrix: " + str(element_std) + '\n'
f.write(sentence9)
f.close()
================================================
FILE: ContextualNet/ContextualNet.py
================================================
#!/usr/bin/env python
# coding: utf-8
# # Imports
import argparse
import collections
import math
import time
import numpy as np
import scipy.io as sio
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from sklearn import metrics, preprocessing
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix
import geniter
import record
import torch_optimizer as optim2
import Utils
from torchsummary import summary
# # Setting Params
parser = argparse.ArgumentParser(description='Training for HSI')
parser.add_argument(
'-d', '--dataset', dest='dataset', default='IN', help="Name of dataset.")
parser.add_argument(
'-o',
'--optimizer',
dest='optimizer',
default='adam',
help="Name of optimizer.")
parser.add_argument(
'-e', '--epoch', type=int, dest='epoch', default=200, help="No of epoch")
parser.add_argument(
'-i', '--iter', type=int, dest='iter', default=3, help="No of iter")
parser.add_argument(
'-p', '--patch', type=int, dest='patch', default=4, help="Length of patch")
parser.add_argument(
'-vs',
'--valid_split',
type=float,
dest='valid_split',
default=0.9,
help="Percentage of validation split.")
args = parser.parse_args()
PARAM_DATASET = args.dataset # UP,IN,SV, KSC
PARAM_EPOCH = args.epoch
PARAM_ITER = args.iter
PATCH_SIZE = args.patch
PARAM_VAL = args.valid_split
PARAM_OPTIM = args.optimizer
# # Data Loading
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# for Monte Carlo runs
seeds = [1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341]
ensemble = 1
global Dataset # UP,IN,SV, KSC
dataset = PARAM_DATASET #input('Please input the name of Dataset(IN, UP, SV, KSC):')
Dataset = dataset.upper()
def load_dataset(Dataset, split=0.9):
data_path = '../dataset/'
if Dataset == 'IN':
mat_data = sio.loadmat(data_path + 'Indian_pines_corrected.mat')
mat_gt = sio.loadmat(data_path + 'Indian_pines_gt.mat')
data_hsi = mat_data['indian_pines_corrected']
gt_hsi = mat_gt['indian_pines_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 10249
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'UP':
uPavia = sio.loadmat(data_path + 'PaviaU.mat')
gt_uPavia = sio.loadmat(data_path + 'PaviaU_gt.mat')
data_hsi = uPavia['paviaU']
gt_hsi = gt_uPavia['paviaU_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 42776
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'SV':
SV = sio.loadmat(data_path + 'Salinas_corrected.mat')
gt_SV = sio.loadmat(data_path + 'Salinas_gt.mat')
data_hsi = SV['salinas_corrected']
gt_hsi = gt_SV['salinas_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 54129
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'KSC':
SV = sio.loadmat(data_path + 'KSC.mat')
gt_SV = sio.loadmat(data_path + 'KSC_gt.mat')
data_hsi = SV['KSC']
gt_hsi = gt_SV['KSC_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 5211
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
shapeor = data_hsi.shape
data_hsi = data_hsi.reshape(-1, data_hsi.shape[-1])
data_hsi = PCA(n_components=K).fit_transform(data_hsi)
shapeor = np.array(shapeor)
shapeor[-1] = K
data_hsi = data_hsi.reshape(shapeor)
return data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT
# # Pytorch Data Loader Creation
data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT = load_dataset(
Dataset, PARAM_VAL)
print(data_hsi.shape)
image_x, image_y, BAND = data_hsi.shape
data = data_hsi.reshape(
np.prod(data_hsi.shape[:2]), np.prod(data_hsi.shape[2:]))
gt = gt_hsi.reshape(np.prod(gt_hsi.shape[:2]), )
CLASSES_NUM = max(gt)
print('The class numbers of the HSI data is:', CLASSES_NUM)
print('-----Importing Setting Parameters-----')
ITER = PARAM_ITER
PATCH_LENGTH = PATCH_SIZE
lr, num_epochs, batch_size = 0.001, 200, 32
loss = torch.nn.CrossEntropyLoss()
img_rows = 2 * PATCH_LENGTH + 1
img_cols = 2 * PATCH_LENGTH + 1
img_channels = data_hsi.shape[2]
INPUT_DIMENSION = data_hsi.shape[2]
ALL_SIZE = data_hsi.shape[0] * data_hsi.shape[1]
VAL_SIZE = int(TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
KAPPA = []
OA = []
AA = []
TRAINING_TIME = []
TESTING_TIME = []
ELEMENT_ACC = np.zeros((ITER, CLASSES_NUM))
data = preprocessing.scale(data)
data_ = data.reshape(data_hsi.shape[0], data_hsi.shape[1], data_hsi.shape[2])
whole_data = data_
padded_data = np.lib.pad(
whole_data, ((PATCH_LENGTH, PATCH_LENGTH), (PATCH_LENGTH, PATCH_LENGTH),
(0, 0)),
'constant',
constant_values=0)
# # Model
class LeeEtAl(nn.Module):
"""
CONTEXTUAL DEEP CNN BASED HYPERSPECTRAL CLASSIFICATION
Hyungtae Lee and Heesung Kwon
IGARSS 2016
"""
@staticmethod
def weight_init(m):
if isinstance(m, nn.Linear) or isinstance(m, nn.Conv3d):
init.kaiming_uniform_(m.weight)
init.zeros_(m.bias)
def __init__(self, in_channels, n_classes):
super(LeeEtAl, self).__init__()
# The first convolutional layer applied to the input hyperspectral
# image uses an inception module that locally convolves the input
# image with two convolutional filters with different sizes
# (1x1xB and 3x3xB where B is the number of spectral bands)
self.conv_3x3 = nn.Conv3d(
1, 128, (3, 3, in_channels), stride=(1, 1, 2), padding=(1, 1, 0))
self.conv_1x1 = nn.Conv3d(
1, 128, (1, 1, in_channels), stride=(1, 1, 1), padding=0)
self.name = 'LeeEtAl'
# We use two modules from the residual learning approach
# Residual block 1
self.conv1 = nn.Conv2d(256, 128, (1, 1))
self.conv2 = nn.Conv2d(128, 128, (1, 1))
self.conv3 = nn.Conv2d(128, 128, (1, 1))
# Residual block 2
self.conv4 = nn.Conv2d(128, 128, (1, 1))
self.conv5 = nn.Conv2d(128, 128, (1, 1))
# The layer combination in the last three convolutional layers
# is the same as the fully connected layers of Alexnet
self.conv6 = nn.Conv2d(128, 128, (1, 1))
self.conv7 = nn.Conv2d(128, 128, (1, 1))
self.conv8 = nn.Conv2d(128, n_classes, (9, 9))
self.lrn1 = nn.LocalResponseNorm(256)
self.lrn2 = nn.LocalResponseNorm(128)
# The 7 th and 8 th convolutional layers have dropout in training
self.dropout = nn.Dropout(p=0.5)
self.apply(self.weight_init)
def forward(self, x):
# Inception module
x_3x3 = self.conv_3x3(x)
x_1x1 = self.conv_1x1(x)
x = torch.cat([x_3x3, x_1x1], dim=1)
# Remove the third dimension of the tensor
x = torch.squeeze(x)
# Local Response Normalization
x = F.relu(self.lrn1(x))
# First convolution
x = self.conv1(x)
# Local Response Normalization
x = F.relu(self.lrn2(x))
# First residual block
x_res = F.relu(self.conv2(x))
x_res = self.conv3(x_res)
x = F.relu(x + x_res)
# Second residual block
x_res = F.relu(self.conv4(x))
x_res = self.conv5(x_res)
x = F.relu(x + x_res)
x = F.relu(self.conv6(x))
x = self.dropout(x)
x = F.relu(self.conv7(x))
x = self.dropout(x)
x = self.conv8(x)
x = x.squeeze(2).squeeze(2)
return x
model = LeeEtAl(BAND, CLASSES_NUM).cuda()
summary(model, input_data=(1, img_rows, img_cols, BAND), verbose=1)
# # Plotting
def train(net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs,
early_stopping=True,
early_num=20):
loss_list = [100]
early_epoch = 0
net = net.to(device)
print("training on ", device)
start = time.time()
train_loss_list = []
valida_loss_list = []
train_acc_list = []
valida_acc_list = []
for epoch in range(epochs):
train_acc_sum, n = 0.0, 0
time_epoch = time.time()
lr_adjust = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer, 15, eta_min=0.0, last_epoch=-1)
for X, y in train_iter:
batch_count, train_l_sum = 0, 0
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
y_hat = net(X)
# print('y_hat', y_hat)
# print('y', y)
l = loss(y_hat, y.long())
optimizer.zero_grad()
l.backward()
optimizer.step()
train_l_sum += l.cpu().item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
lr_adjust.step()
valida_acc, valida_loss = record.evaluate_accuracy(
valida_iter, net, loss, device)
loss_list.append(valida_loss)
train_loss_list.append(train_l_sum) # / batch_count)
train_acc_list.append(train_acc_sum / n)
valida_loss_list.append(valida_loss)
valida_acc_list.append(valida_acc)
print(
'epoch %d, train loss %.6f, train acc %.3f, valida loss %.6f, valida acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
valida_loss, valida_acc, time.time() - time_epoch))
PATH = "./net_DBA.pt"
# if loss_list[-1] <= 0.01 and valida_acc >= 0.95:
# torch.save(net.state_dict(), PATH)
# break
if early_stopping and loss_list[-2] < loss_list[-1]:
if early_epoch == 0: # and valida_acc > 0.9:
torch.save(net.state_dict(), PATH)
early_epoch += 1
loss_list[-1] = loss_list[-2]
if early_epoch == early_num:
net.load_state_dict(torch.load(PATH))
break
else:
early_epoch = 0
print('epoch %d, loss %.4f, train acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
time.time() - start))
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def select(groundTruth): #divide dataset into train and test datasets
labels_loc = {}
train = {}
test = {}
m = max(groundTruth)
#amount = [3, 41, 29, 7, 14, 20, 2, 15, 3, 36, 64, 22, 4, 28, 10, 2]
#amount = [43, 1387, 801, 230, 469, 710, 26, 463, 17, 936, 2391, 571, 201, 1237, 376, 91]
if Dataset == 'IN':
amount = [
35, 1011, 581, 167, 344, 515, 19, 327, 12, 683, 1700, 418, 138,
876, 274, 69
] #IP 20%
#amount = [6, 144, 84, 24, 50, 75, 3, 49, 2, 97, 247, 62, 22, 130, 38, 10] #IP 20%
if Dataset == 'UP':
amount = [5297, 14974, 1648, 2424, 1076, 4026, 1046, 2950, 755] #UP
if Dataset == 'KSC':
amount = [
530, 165, 176, 170, 110, 161, 80, 299, 377, 283, 296, 341, 654
] #KSC
for i in range(m):
indices = [
j for j, x in enumerate(groundTruth.ravel().tolist()) if x == i + 1
]
np.random.shuffle(indices)
labels_loc[i] = indices
nb_val = int(amount[i])
train[i] = indices[:-nb_val]
test[i] = indices[-nb_val:]
# whole_indices = []
train_indices = []
test_indices = []
for i in range(m):
# whole_indices += labels_loc[i]
train_indices += train[i]
test_indices += test[i]
np.random.shuffle(train_indices)
np.random.shuffle(test_indices)
return train_indices, test_indices
# # Training
for index_iter in range(ITER):
print('iter:', index_iter)
#define the model
#net = pResNet(32, 48, CLASSES_NUM, BAND, 2, 16, bottleneck=True)
#net = resnet20(num_classes=CLASSES_NUM)
net = LeeEtAl(BAND, CLASSES_NUM)
if PARAM_OPTIM == 'diffgrad':
optimizer = optim2.DiffGrad(
net.parameters(),
lr=lr,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0) # weight_decay=0.0001)
if PARAM_OPTIM == 'adam':
optimizer = optim.Adam(
net.parameters(),
lr=1e-3,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0)
time_1 = int(time.time())
np.random.seed(seeds[index_iter])
# train_indices, test_indices = select(gt)
train_indices, test_indices = sampling(VALIDATION_SPLIT, gt)
_, total_indices = sampling(1, gt)
TRAIN_SIZE = len(train_indices)
print('Train size: ', TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
print('Test size: ', TEST_SIZE)
VAL_SIZE = int(TRAIN_SIZE)
print('Validation size: ', VAL_SIZE)
print('-----Selecting Small Pieces from the Original Cube Data-----')
train_iter, valida_iter, test_iter, all_iter = geniter.generate_iter(
TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE,
total_indices, VAL_SIZE, whole_data, PATCH_LENGTH, padded_data,
INPUT_DIMENSION, 16, gt) #batchsize in 1
tic1 = time.time()
train(
net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs=PARAM_EPOCH)
toc1 = time.time()
pred_test = []
tic2 = time.time()
with torch.no_grad():
for X, y in test_iter:
#print('Shape of X',X.shape)
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
y_hat = net(X)
pred_test.extend(np.array(net(X).cpu().argmax(axis=1)))
toc2 = time.time()
collections.Counter(pred_test)
gt_test = gt[test_indices] - 1
overall_acc = metrics.accuracy_score(pred_test, gt_test[:-VAL_SIZE])
confusion_matrix = metrics.confusion_matrix(pred_test, gt_test[:-VAL_SIZE])
each_acc, average_acc = record.aa_and_each_accuracy(confusion_matrix)
kappa = metrics.cohen_kappa_score(pred_test, gt_test[:-VAL_SIZE])
torch.save(
net.state_dict(),
"./models/" + 'ContextualNet' + str(round(overall_acc, 3)) + '.pt')
KAPPA.append(kappa)
OA.append(overall_acc)
AA.append(average_acc)
TRAINING_TIME.append(toc1 - tic1)
TESTING_TIME.append(toc2 - tic2)
ELEMENT_ACC[index_iter, :] = each_acc
# # Map, Records
print("--------" + " Training Finished-----------")
record.record_output(
OA, AA, KAPPA, ELEMENT_ACC, TRAINING_TIME, TESTING_TIME,
'./report/' + 'ContextualNetpatch:' + str(img_rows) + '_' + Dataset +
'split' + str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM + '.txt')
Utils.generate_png(
all_iter, net, gt_hsi, Dataset, device, total_indices,
'./classification_maps/' + 'ContextualNetpatch:' + str(img_rows) + '_' +
Dataset + 'split' + str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM)
================================================
FILE: ContextualNet/Utils.py
================================================
import numpy as np
from sklearn import metrics, preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, cohen_kappa_score
from operator import truediv
import matplotlib.pyplot as plt
import scipy.io as sio
import os
import spectral
import torch
import cv2
from operator import truediv
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def set_figsize(figsize=(3.5, 2.5)):
display.set_matplotlib_formats('svg')
plt.rcParams['figure.figsize'] = figsize
def classification_map(map, ground_truth, dpi, save_path):
fig = plt.figure(frameon=False)
fig.set_size_inches(ground_truth.shape[1] * 2.0 / dpi,
ground_truth.shape[0] * 2.0 / dpi)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
fig.add_axes(ax)
ax.imshow(map)
fig.savefig(save_path, dpi=dpi)
return 0
def list_to_colormap(x_list):
y = np.zeros((x_list.shape[0], 3))
for index, item in enumerate(x_list):
if item == 0:
y[index] = np.array([255, 0, 0]) / 255.
if item == 1:
y[index] = np.array([0, 255, 0]) / 255.
if item == 2:
y[index] = np.array([0, 0, 255]) / 255.
if item == 3:
y[index] = np.array([255, 255, 0]) / 255.
if item == 4:
y[index] = np.array([0, 255, 255]) / 255.
if item == 5:
y[index] = np.array([255, 0, 255]) / 255.
if item == 6:
y[index] = np.array([192, 192, 192]) / 255.
if item == 7:
y[index] = np.array([128, 128, 128]) / 255.
if item == 8:
y[index] = np.array([128, 0, 0]) / 255.
if item == 9:
y[index] = np.array([128, 128, 0]) / 255.
if item == 10:
y[index] = np.array([0, 128, 0]) / 255.
if item == 11:
y[index] = np.array([128, 0, 128]) / 255.
if item == 12:
y[index] = np.array([0, 128, 128]) / 255.
if item == 13:
y[index] = np.array([0, 0, 128]) / 255.
if item == 14:
y[index] = np.array([255, 165, 0]) / 255.
if item == 15:
y[index] = np.array([255, 215, 0]) / 255.
if item == 16:
y[index] = np.array([0, 0, 0]) / 255.
if item == 17:
y[index] = np.array([215, 255, 0]) / 255.
if item == 18:
y[index] = np.array([0, 255, 215]) / 255.
if item == -1:
y[index] = np.array([0, 0, 0]) / 255.
return y
def generate_png(all_iter, net, gt_hsi, Dataset, device, total_indices, path):
pred_test = []
for X, y in all_iter:
# X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
pred_test.extend(net(X).cpu().argmax(axis=1).detach().numpy())
gt = gt_hsi.flatten()
x_label = np.zeros(gt.shape)
for i in range(len(gt)):
if gt[i] == 0:
gt[i] = 17
x_label[i] = 16
gt = gt[:] - 1
x_label[total_indices] = pred_test
x = np.ravel(x_label)
y_list = list_to_colormap(x)
y_gt = list_to_colormap(gt)
y_re = np.reshape(y_list, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
gt_re = np.reshape(y_gt, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
classification_map(y_re, gt_hsi, 300,
path + '.png')
classification_map(gt_re, gt_hsi, 300,
path + '_gt.png')
print('------Get classification maps successful-------')
================================================
FILE: ContextualNet/geniter.py
================================================
import torch
import numpy as np
import torch.utils.data as Data
def index_assignment(index, row, col, pad_length):
new_assign = {}
for counter, value in enumerate(index):
assign_0 = value // col + pad_length
assign_1 = value % col + pad_length
new_assign[counter] = [assign_0, assign_1]
return new_assign
def select_patch(matrix, pos_row, pos_col, ex_len):
selected_rows = matrix[range(pos_row-ex_len, pos_row+ex_len+1)]
selected_patch = selected_rows[:, range(pos_col-ex_len, pos_col+ex_len+1)]
return selected_patch
def select_small_cubic(data_size, data_indices, whole_data, patch_length, padded_data, dimension):
small_cubic_data = np.zeros((data_size, 2 * patch_length + 1, 2 * patch_length + 1, dimension))
data_assign = index_assignment(data_indices, whole_data.shape[0], whole_data.shape[1], patch_length)
for i in range(len(data_assign)):
small_cubic_data[i] = select_patch(padded_data, data_assign[i][0], data_assign[i][1], patch_length)
return small_cubic_data
def generate_iter(TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE, total_indices, VAL_SIZE,
whole_data, PATCH_LENGTH, padded_data, INPUT_DIMENSION, batch_size, gt):
gt_all = gt[total_indices] - 1
y_train = gt[train_indices] - 1
y_test = gt[test_indices] - 1
all_data = select_small_cubic(TOTAL_SIZE, total_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
train_data = select_small_cubic(TRAIN_SIZE, train_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
print(train_data.shape)
test_data = select_small_cubic(TEST_SIZE, test_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
x_train = train_data.reshape(train_data.shape[0], train_data.shape[1], train_data.shape[2], INPUT_DIMENSION)
x_test_all = test_data.reshape(test_data.shape[0], test_data.shape[1], test_data.shape[2], INPUT_DIMENSION)
x_val = x_test_all[-VAL_SIZE:]
y_val = y_test[-VAL_SIZE:]
x_test = x_test_all[:-VAL_SIZE]
y_test = y_test[:-VAL_SIZE]
x1_tensor_train = torch.from_numpy(x_train).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_train = torch.from_numpy(y_train).type(torch.FloatTensor)
torch_dataset_train = Data.TensorDataset(x1_tensor_train, y1_tensor_train)
x1_tensor_valida = torch.from_numpy(x_val).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_valida = torch.from_numpy(y_val).type(torch.FloatTensor)
torch_dataset_valida = Data.TensorDataset(x1_tensor_valida, y1_tensor_valida)
x1_tensor_test = torch.from_numpy(x_test).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_test = torch.from_numpy(y_test).type(torch.FloatTensor)
torch_dataset_test = Data.TensorDataset(x1_tensor_test,y1_tensor_test)
all_data.reshape(all_data.shape[0], all_data.shape[1], all_data.shape[2], INPUT_DIMENSION)
all_tensor_data = torch.from_numpy(all_data).type(torch.FloatTensor).unsqueeze(1)
all_tensor_data_label = torch.from_numpy(gt_all).type(torch.FloatTensor)
torch_dataset_all = Data.TensorDataset(all_tensor_data, all_tensor_data_label)
train_iter = Data.DataLoader(
dataset=torch_dataset_train, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
valiada_iter = Data.DataLoader(
dataset=torch_dataset_valida, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
test_iter = Data.DataLoader(
dataset=torch_dataset_test, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
all_iter = Data.DataLoader(
dataset=torch_dataset_all, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
return train_iter, valiada_iter, test_iter, all_iter #, y_test
================================================
FILE: ContextualNet/record.py
================================================
import numpy as np
import torch
from operator import truediv
def evaluate_accuracy(data_iter, net, loss, device):
acc_sum, n = 0.0, 0
with torch.no_grad():
for X, y in data_iter:
test_l_sum, test_num = 0, 0
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
net.eval()
y_hat = net(X)
l = loss(y_hat, y.long())
acc_sum += (y_hat.argmax(dim=1) == y.to(device)).float().sum().cpu().item()
test_l_sum += l
test_num += 1
net.train()
n += y.shape[0]
return [acc_sum / n, test_l_sum] # / test_num]
def aa_and_each_accuracy(confusion_matrix):
list_diag = np.diag(confusion_matrix)
list_raw_sum = np.sum(confusion_matrix, axis=1)
each_acc = np.nan_to_num(truediv(list_diag, list_raw_sum))
average_acc = np.mean(each_acc)
return each_acc, average_acc
def record_output(oa_ae, aa_ae, kappa_ae, element_acc_ae, training_time_ae, testing_time_ae, path):
f = open(path, 'a')
sentence0 = 'OAs for each iteration are:' + str(oa_ae) + '\n'
f.write(sentence0)
sentence1 = 'AAs for each iteration are:' + str(aa_ae) + '\n'
f.write(sentence1)
sentence2 = 'KAPPAs for each iteration are:' + str(kappa_ae) + '\n' + '\n'
f.write(sentence2)
sentence3 = 'mean_OA ± std_OA is: ' + str(np.mean(oa_ae)) + ' ± ' + str(np.std(oa_ae)) + '\n'
f.write(sentence3)
sentence4 = 'mean_AA ± std_AA is: ' + str(np.mean(aa_ae)) + ' ± ' + str(np.std(aa_ae)) + '\n'
f.write(sentence4)
sentence5 = 'mean_KAPPA ± std_KAPPA is: ' + str(np.mean(kappa_ae)) + ' ± ' + str(np.std(kappa_ae)) + '\n' + '\n'
f.write(sentence5)
sentence6 = 'Total average Training time is: ' + str(np.sum(training_time_ae)) + '\n'
f.write(sentence6)
sentence7 = 'Total average Testing time is: ' + str(np.sum(testing_time_ae)) + '\n' + '\n'
f.write(sentence7)
element_mean = np.mean(element_acc_ae, axis=0)
element_std = np.std(element_acc_ae, axis=0)
sentence8 = "Mean of all elements in confusion matrix: " + str(element_mean) + '\n'
f.write(sentence8)
sentence9 = "Standard deviation of all elements in confusion matrix: " + str(element_std) + '\n'
f.write(sentence9)
f.close()
================================================
FILE: PyResNet/PyResNet.py
================================================
#!/usr/bin/env python
# coding: utf-8
# # Imports
import argparse
import collections
import math
import time
import numpy as np
import scipy.io as sio
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from sklearn import metrics, preprocessing
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix
import geniter
import record
import torch_optimizer as optim2
import Utils
from torchsummary import summary
# # Setting Params
parser = argparse.ArgumentParser(description='Training for HSI')
parser.add_argument(
'-d', '--dataset', dest='dataset', default='IN', help="Name of dataset.")
parser.add_argument(
'-o',
'--optimizer',
dest='optimizer',
default='adam',
help="Name of optimizer.")
parser.add_argument(
'-e', '--epoch', type=int, dest='epoch', default=200, help="No of epoch")
parser.add_argument(
'-i', '--iter', type=int, dest='iter', default=3, help="No of iter")
parser.add_argument(
'-p', '--patch', type=int, dest='patch', default=4, help="Length of patch")
parser.add_argument(
'-vs',
'--valid_split',
type=float,
dest='valid_split',
default=0.9,
help="Percentage of validation split.")
args = parser.parse_args()
PARAM_DATASET = args.dataset # UP,IN,SV, KSC
PARAM_EPOCH = args.epoch
PARAM_ITER = args.iter
PATCH_SIZE = args.patch
PARAM_VAL = args.valid_split
PARAM_OPTIM = args.optimizer
# # Data Loading
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# for Monte Carlo runs
seeds = [1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341]
ensemble = 1
global Dataset # UP,IN,SV, KSC
dataset = PARAM_DATASET #input('Please input the name of Dataset(IN, UP, SV, KSC):')
Dataset = dataset.upper()
def load_dataset(Dataset, split=0.9):
data_path = '../dataset/'
if Dataset == 'IN':
mat_data = sio.loadmat(data_path + 'Indian_pines_corrected.mat')
mat_gt = sio.loadmat(data_path + 'Indian_pines_gt.mat')
data_hsi = mat_data['indian_pines_corrected']
gt_hsi = mat_gt['indian_pines_gt']
K = 200
TOTAL_SIZE = 10249
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'UP':
uPavia = sio.loadmat(data_path + 'PaviaU.mat')
gt_uPavia = sio.loadmat(data_path + 'PaviaU_gt.mat')
data_hsi = uPavia['paviaU']
gt_hsi = gt_uPavia['paviaU_gt']
K = 103
TOTAL_SIZE = 42776
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'SV':
SV = sio.loadmat(data_path + 'Salinas_corrected.mat')
gt_SV = sio.loadmat(data_path + 'Salinas_gt.mat')
data_hsi = SV['salinas_corrected']
gt_hsi = gt_SV['salinas_gt']
K = 15
TOTAL_SIZE = 54129
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'KSC':
SV = sio.loadmat(data_path + 'KSC.mat')
gt_SV = sio.loadmat(data_path + 'KSC_gt.mat')
data_hsi = SV['KSC']
gt_hsi = gt_SV['KSC_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 5211
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
shapeor = data_hsi.shape
data_hsi = data_hsi.reshape(-1, data_hsi.shape[-1])
data_hsi = PCA(n_components=K).fit_transform(data_hsi)
shapeor = np.array(shapeor)
shapeor[-1] = K
data_hsi = data_hsi.reshape(shapeor)
return data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT
# # Pytorch Data Loader Creation
data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT = load_dataset(
Dataset, PARAM_VAL)
print(data_hsi.shape)
image_x, image_y, BAND = data_hsi.shape
data = data_hsi.reshape(
np.prod(data_hsi.shape[:2]), np.prod(data_hsi.shape[2:]))
gt = gt_hsi.reshape(np.prod(gt_hsi.shape[:2]), )
CLASSES_NUM = max(gt)
print('The class numbers of the HSI data is:', CLASSES_NUM)
print('-----Importing Setting Parameters-----')
ITER = PARAM_ITER
PATCH_LENGTH = PATCH_SIZE
lr, num_epochs, batch_size = 0.001, 200, 32
loss = torch.nn.CrossEntropyLoss()
img_rows = 2 * PATCH_LENGTH + 1
img_cols = 2 * PATCH_LENGTH + 1
img_channels = data_hsi.shape[2]
INPUT_DIMENSION = data_hsi.shape[2]
ALL_SIZE = data_hsi.shape[0] * data_hsi.shape[1]
VAL_SIZE = int(TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
KAPPA = []
OA = []
AA = []
TRAINING_TIME = []
TESTING_TIME = []
ELEMENT_ACC = np.zeros((ITER, CLASSES_NUM))
data = preprocessing.scale(data)
data_ = data.reshape(data_hsi.shape[0], data_hsi.shape[1], data_hsi.shape[2])
whole_data = data_
padded_data = np.lib.pad(
whole_data, ((PATCH_LENGTH, PATCH_LENGTH), (PATCH_LENGTH, PATCH_LENGTH),
(0, 0)),
'constant',
constant_values=0)
# # Model
def make_conv_bn_relu(in_channels,
out_channels,
kernel_size=3,
stride=1,
padding=1,
groups=1):
return [
nn.Conv2d(
in_channels,
out_channels,
kernel_size=kernel_size,
stride=stride,
padding=padding,
groups=groups,
bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
]
def make_linear_bn_relu(in_channels, out_channels):
return [
nn.Linear(in_channels, out_channels, bias=False),
nn.BatchNorm1d(out_channels),
nn.ReLU(inplace=True),
]
def make_max_flat(out):
flat = F.adaptive_max_pool2d(
out, output_size=1)
flat = flat.view(flat.size(0), -1)
return flat
def make_avg_flat(out):
flat = F.adaptive_avg_pool2d(out, output_size=1)
flat = flat.view(flat.size(0), -1)
return flat
class BasicBlock(nn.Module):
expansion = 1
def __init__(self, inplanes, planes, stride=1, downsample=None):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(
inplanes,
planes,
kernel_size=3,
stride=stride,
padding=1,
bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(
planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
residual = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
residual = self.downsample(x)
out += residual
out = self.relu(out)
return out
class PyResNet(nn.Module):
def __init__(self, block, layers, in_shape=(3, 256, 256), num_classes=17):
self.inplanes = 64
super(PyResNet, self).__init__()
in_channels, height, width = in_shape
self.conv1 = nn.Conv2d(
in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.layer1 = self.make_layer(block, 64, layers[0])
self.layer2 = self.make_layer(block, 128, layers[1], stride=2)
self.layer3 = self.make_layer(block, 256, layers[2], stride=2)
self.layer4 = self.make_layer(block, 512, layers[3], stride=2)
self.fc2 = nn.Sequential(
*make_linear_bn_relu(128 * block.expansion, 512),
nn.Linear(512, num_classes),
)
self.fc3 = nn.Sequential(
*make_linear_bn_relu(256 * block.expansion, 512),
nn.Linear(512, num_classes),
)
self.fc4 = nn.Sequential(
*make_linear_bn_relu(512 * block.expansion, 512),
nn.Linear(512, num_classes),
)
# self.fc = nn.Sequential(
# *make_linear_bn_relu((128+256+512) * block.expansion, 1024),
# nn.Linear(1024, num_classes)
# )
#
for m in self.modules():
if isinstance(m, nn.Conv2d):
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
m.weight.data.normal_(0, math.sqrt(2. / n))
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zero_()
def make_layer(self, block, planes, blocks, stride=1):
downsample = None
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
nn.Conv2d(
self.inplanes,
planes * block.expansion,
kernel_size=1,
stride=stride,
bias=False),
nn.BatchNorm2d(planes * block.expansion),
)
layers = []
layers.append(block(self.inplanes, planes, stride, downsample))
self.inplanes = planes * block.expansion
for i in range(1, blocks):
layers.append(block(self.inplanes, planes))
return nn.Sequential(*layers)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = F.max_pool2d(x, kernel_size=3, stride=2, padding=1)
x = self.layer1(x) # 64, 64x64
x = self.layer2(x) #128, 32x32
flat2 = make_max_flat(x) ##make_avg_flat
x = self.layer3(x) #256, 16x16
flat3 = make_max_flat(x)
x = self.layer4(x) #512, 8x8
flat4 = make_max_flat(x)
x = self.fc2(flat2) + self.fc3(flat3) + self.fc4(flat4)
logit = x
return logit
def PyResNet34(pretrained=None, **kwargs):
"""Not Pretrained"""
if pretrained:
raise NotImplementedError()
model = PyResNet(BasicBlock, [3, 4, 6, 3], **kwargs)
return model
model = PyResNet34(
in_shape=(BAND, img_rows, img_cols), num_classes=CLASSES_NUM).cuda()
summary(model, input_data=(BAND, img_rows, img_cols), verbose=1)
# # Plotting
def train(net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs,
early_stopping=True,
early_num=20):
loss_list = [100]
early_epoch = 0
net = net.to(device)
print("training on ", device)
start = time.time()
train_loss_list = []
valida_loss_list = []
train_acc_list = []
valida_acc_list = []
for epoch in range(epochs):
train_acc_sum, n = 0.0, 0
time_epoch = time.time()
lr_adjust = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer, 15, eta_min=0.0, last_epoch=-1)
for X, y in train_iter:
batch_count, train_l_sum = 0, 0
X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
y_hat = net(X)
l = loss(y_hat, y.long())
optimizer.zero_grad()
l.backward()
optimizer.step()
train_l_sum += l.cpu().item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
lr_adjust.step()
valida_acc, valida_loss = record.evaluate_accuracy(
valida_iter, net, loss, device)
loss_list.append(valida_loss)
train_loss_list.append(train_l_sum) # / batch_count)
train_acc_list.append(train_acc_sum / n)
valida_loss_list.append(valida_loss)
valida_acc_list.append(valida_acc)
print(
'epoch %d, train loss %.6f, train acc %.3f, valida loss %.6f, valida acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
valida_loss, valida_acc, time.time() - time_epoch))
PATH = "./net_DBA.pt"
if early_stopping and loss_list[-2] < loss_list[-1]:
if early_epoch == 0:
torch.save(net.state_dict(), PATH)
early_epoch += 1
loss_list[-1] = loss_list[-2]
if early_epoch == early_num:
net.load_state_dict(torch.load(PATH))
break
else:
early_epoch = 0
print('epoch %d, loss %.4f, train acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
time.time() - start))
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def select(groundTruth): #divide dataset into train and test datasets
labels_loc = {}
train = {}
test = {}
m = max(groundTruth)
#amount = [3, 41, 29, 7, 14, 20, 2, 15, 3, 36, 64, 22, 4, 28, 10, 2]
#amount = [43, 1387, 801, 230, 469, 710, 26, 463, 17, 936, 2391, 571, 201, 1237, 376, 91]
if Dataset == 'IN':
amount = [
35, 1011, 581, 167, 344, 515, 19, 327, 12, 683, 1700, 418, 138,
876, 274, 69
] #IP 20%
#amount = [6, 144, 84, 24, 50, 75, 3, 49, 2, 97, 247, 62, 22, 130, 38, 10] #IP 20%
if Dataset == 'UP':
amount = [5297, 14974, 1648, 2424, 1076, 4026, 1046, 2950, 755] #UP
if Dataset == 'KSC':
amount = [
530, 165, 176, 170, 110, 161, 80, 299, 377, 283, 296, 341, 654
] #KSC
for i in range(m):
indices = [
j for j, x in enumerate(groundTruth.ravel().tolist()) if x == i + 1
]
np.random.shuffle(indices)
labels_loc[i] = indices
nb_val = int(amount[i])
train[i] = indices[:-nb_val]
test[i] = indices[-nb_val:]
train_indices = []
test_indices = []
for i in range(m):
train_indices += train[i]
test_indices += test[i]
np.random.shuffle(train_indices)
np.random.shuffle(test_indices)
return train_indices, test_indices
# # Training
for index_iter in range(ITER):
print('iter:', index_iter)
net = PyResNet34(
in_shape=(BAND, img_rows, img_cols), num_classes=CLASSES_NUM)
if PARAM_OPTIM == 'diffgrad':
optimizer = optim2.DiffGrad(
net.parameters(),
lr=lr,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0) # weight_decay=0.0001)
if PARAM_OPTIM == 'adam':
optimizer = optim.Adam(
net.parameters(),
lr=1e-3,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0)
time_1 = int(time.time())
np.random.seed(seeds[index_iter])
# train_indices, test_indices = select(gt)
train_indices, test_indices = sampling(VALIDATION_SPLIT, gt)
_, total_indices = sampling(1, gt)
TRAIN_SIZE = len(train_indices)
print('Train size: ', TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
print('Test size: ', TEST_SIZE)
VAL_SIZE = int(TRAIN_SIZE)
print('Validation size: ', VAL_SIZE)
print('-----Selecting Small Pieces from the Original Cube Data-----')
train_iter, valida_iter, test_iter, all_iter = geniter.generate_iter(
TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE,
total_indices, VAL_SIZE, whole_data, PATCH_LENGTH, padded_data,
INPUT_DIMENSION, 16, gt) #batchsize in 1
tic1 = time.time()
train(
net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs=PARAM_EPOCH)
toc1 = time.time()
pred_test = []
tic2 = time.time()
with torch.no_grad():
for X, y in test_iter:
# print('Shape of X',X.shape)
X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
y_hat = net(X)
pred_test.extend(np.array(net(X).cpu().argmax(axis=1)))
toc2 = time.time()
collections.Counter(pred_test)
gt_test = gt[test_indices] - 1
overall_acc = metrics.accuracy_score(pred_test, gt_test[:-VAL_SIZE])
confusion_matrix = metrics.confusion_matrix(pred_test, gt_test[:-VAL_SIZE])
each_acc, average_acc = record.aa_and_each_accuracy(confusion_matrix)
kappa = metrics.cohen_kappa_score(pred_test, gt_test[:-VAL_SIZE])
torch.save(net.state_dict(),
"./models/" + 'PyResnet34' + str(round(overall_acc, 3)) + '.pt')
KAPPA.append(kappa)
OA.append(overall_acc)
AA.append(average_acc)
TRAINING_TIME.append(toc1 - tic1)
TESTING_TIME.append(toc2 - tic2)
ELEMENT_ACC[index_iter, :] = each_acc
# # Map, Records
print("--------" + " Training Finished-----------")
record.record_output(
OA, AA, KAPPA, ELEMENT_ACC, TRAINING_TIME, TESTING_TIME,
'./report/' + 'PyResnet34patch:' + str(img_rows) + '_' + Dataset + 'split'
+ str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM + '.txt')
Utils.generate_png(
all_iter, net, gt_hsi, Dataset, device, total_indices,
'./classification_maps/' + 'PyResnet34patch:' + str(img_rows) + '_' +
Dataset + 'split' + str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM)
================================================
FILE: PyResNet/Utils.py
================================================
import numpy as np
from sklearn import metrics, preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, cohen_kappa_score
from operator import truediv
import matplotlib.pyplot as plt
import scipy.io as sio
import os
import spectral
import torch
import cv2
from operator import truediv
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def set_figsize(figsize=(3.5, 2.5)):
display.set_matplotlib_formats('svg')
plt.rcParams['figure.figsize'] = figsize
def classification_map(map, ground_truth, dpi, save_path):
fig = plt.figure(frameon=False)
fig.set_size_inches(ground_truth.shape[1] * 2.0 / dpi,
ground_truth.shape[0] * 2.0 / dpi)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
fig.add_axes(ax)
ax.imshow(map)
fig.savefig(save_path, dpi=dpi)
return 0
def list_to_colormap(x_list):
y = np.zeros((x_list.shape[0], 3))
for index, item in enumerate(x_list):
if item == 0:
y[index] = np.array([255, 0, 0]) / 255.
if item == 1:
y[index] = np.array([0, 255, 0]) / 255.
if item == 2:
y[index] = np.array([0, 0, 255]) / 255.
if item == 3:
y[index] = np.array([255, 255, 0]) / 255.
if item == 4:
y[index] = np.array([0, 255, 255]) / 255.
if item == 5:
y[index] = np.array([255, 0, 255]) / 255.
if item == 6:
y[index] = np.array([192, 192, 192]) / 255.
if item == 7:
y[index] = np.array([128, 128, 128]) / 255.
if item == 8:
y[index] = np.array([128, 0, 0]) / 255.
if item == 9:
y[index] = np.array([128, 128, 0]) / 255.
if item == 10:
y[index] = np.array([0, 128, 0]) / 255.
if item == 11:
y[index] = np.array([128, 0, 128]) / 255.
if item == 12:
y[index] = np.array([0, 128, 128]) / 255.
if item == 13:
y[index] = np.array([0, 0, 128]) / 255.
if item == 14:
y[index] = np.array([255, 165, 0]) / 255.
if item == 15:
y[index] = np.array([255, 215, 0]) / 255.
if item == 16:
y[index] = np.array([0, 0, 0]) / 255.
if item == 17:
y[index] = np.array([215, 255, 0]) / 255.
if item == 18:
y[index] = np.array([0, 255, 215]) / 255.
if item == -1:
y[index] = np.array([0, 0, 0]) / 255.
return y
def generate_png(all_iter, net, gt_hsi, Dataset, device, total_indices, path):
pred_test = []
for X, y in all_iter:
X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
pred_test.extend(net(X).cpu().argmax(axis=1).detach().numpy())
gt = gt_hsi.flatten()
x_label = np.zeros(gt.shape)
for i in range(len(gt)):
if gt[i] == 0:
gt[i] = 17
x_label[i] = 16
gt = gt[:] - 1
x_label[total_indices] = pred_test
x = np.ravel(x_label)
y_list = list_to_colormap(x)
y_gt = list_to_colormap(gt)
y_re = np.reshape(y_list, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
gt_re = np.reshape(y_gt, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
classification_map(y_re, gt_hsi, 300,
path + '.png')
classification_map(gt_re, gt_hsi, 300,
path + '_gt.png')
print('------Get classification maps successful-------')
================================================
FILE: PyResNet/geniter.py
================================================
import torch
import numpy as np
import torch.utils.data as Data
def index_assignment(index, row, col, pad_length):
new_assign = {}
for counter, value in enumerate(index):
assign_0 = value // col + pad_length
assign_1 = value % col + pad_length
new_assign[counter] = [assign_0, assign_1]
return new_assign
def select_patch(matrix, pos_row, pos_col, ex_len):
selected_rows = matrix[range(pos_row-ex_len, pos_row+ex_len+1)]
selected_patch = selected_rows[:, range(pos_col-ex_len, pos_col+ex_len+1)]
return selected_patch
def select_small_cubic(data_size, data_indices, whole_data, patch_length, padded_data, dimension):
small_cubic_data = np.zeros((data_size, 2 * patch_length + 1, 2 * patch_length + 1, dimension))
data_assign = index_assignment(data_indices, whole_data.shape[0], whole_data.shape[1], patch_length)
for i in range(len(data_assign)):
small_cubic_data[i] = select_patch(padded_data, data_assign[i][0], data_assign[i][1], patch_length)
return small_cubic_data
def generate_iter(TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE, total_indices, VAL_SIZE,
whole_data, PATCH_LENGTH, padded_data, INPUT_DIMENSION, batch_size, gt):
gt_all = gt[total_indices] - 1
y_train = gt[train_indices] - 1
y_test = gt[test_indices] - 1
all_data = select_small_cubic(TOTAL_SIZE, total_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
train_data = select_small_cubic(TRAIN_SIZE, train_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
print(train_data.shape)
test_data = select_small_cubic(TEST_SIZE, test_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
x_train = train_data.reshape(train_data.shape[0], train_data.shape[1], train_data.shape[2], INPUT_DIMENSION)
x_test_all = test_data.reshape(test_data.shape[0], test_data.shape[1], test_data.shape[2], INPUT_DIMENSION)
x_val = x_test_all[-VAL_SIZE:]
y_val = y_test[-VAL_SIZE:]
x_test = x_test_all[:-VAL_SIZE]
y_test = y_test[:-VAL_SIZE]
x1_tensor_train = torch.from_numpy(x_train).type(torch.FloatTensor)#.unsqueeze(1)
y1_tensor_train = torch.from_numpy(y_train).type(torch.FloatTensor)
torch_dataset_train = Data.TensorDataset(x1_tensor_train, y1_tensor_train)
x1_tensor_valida = torch.from_numpy(x_val).type(torch.FloatTensor)#.unsqueeze(1)
y1_tensor_valida = torch.from_numpy(y_val).type(torch.FloatTensor)
torch_dataset_valida = Data.TensorDataset(x1_tensor_valida, y1_tensor_valida)
x1_tensor_test = torch.from_numpy(x_test).type(torch.FloatTensor)#.unsqueeze(1)
y1_tensor_test = torch.from_numpy(y_test).type(torch.FloatTensor)
torch_dataset_test = Data.TensorDataset(x1_tensor_test,y1_tensor_test)
all_data.reshape(all_data.shape[0], all_data.shape[1], all_data.shape[2], INPUT_DIMENSION)
all_tensor_data = torch.from_numpy(all_data).type(torch.FloatTensor)#.unsqueeze(1)
all_tensor_data_label = torch.from_numpy(gt_all).type(torch.FloatTensor)
torch_dataset_all = Data.TensorDataset(all_tensor_data, all_tensor_data_label)
train_iter = Data.DataLoader(
dataset=torch_dataset_train, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
valiada_iter = Data.DataLoader(
dataset=torch_dataset_valida, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
test_iter = Data.DataLoader(
dataset=torch_dataset_test, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
all_iter = Data.DataLoader(
dataset=torch_dataset_all, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
return train_iter, valiada_iter, test_iter, all_iter #, y_test
================================================
FILE: PyResNet/record.py
================================================
import numpy as np
import torch
from operator import truediv
def evaluate_accuracy(data_iter, net, loss, device):
acc_sum, n = 0.0, 0
with torch.no_grad():
for X, y in data_iter:
test_l_sum, test_num = 0, 0
X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
net.eval()
y_hat = net(X)
l = loss(y_hat, y.long())
acc_sum += (y_hat.argmax(dim=1) == y.to(device)).float().sum().cpu().item()
test_l_sum += l
test_num += 1
net.train()
n += y.shape[0]
return [acc_sum / n, test_l_sum] # / test_num]
def aa_and_each_accuracy(confusion_matrix):
list_diag = np.diag(confusion_matrix)
list_raw_sum = np.sum(confusion_matrix, axis=1)
each_acc = np.nan_to_num(truediv(list_diag, list_raw_sum))
average_acc = np.mean(each_acc)
return each_acc, average_acc
def record_output(oa_ae, aa_ae, kappa_ae, element_acc_ae, training_time_ae, testing_time_ae, path):
f = open(path, 'a')
sentence0 = 'OAs for each iteration are:' + str(oa_ae) + '\n'
f.write(sentence0)
sentence1 = 'AAs for each iteration are:' + str(aa_ae) + '\n'
f.write(sentence1)
sentence2 = 'KAPPAs for each iteration are:' + str(kappa_ae) + '\n' + '\n'
f.write(sentence2)
sentence3 = 'mean_OA ± std_OA is: ' + str(np.mean(oa_ae)) + ' ± ' + str(np.std(oa_ae)) + '\n'
f.write(sentence3)
sentence4 = 'mean_AA ± std_AA is: ' + str(np.mean(aa_ae)) + ' ± ' + str(np.std(aa_ae)) + '\n'
f.write(sentence4)
sentence5 = 'mean_KAPPA ± std_KAPPA is: ' + str(np.mean(kappa_ae)) + ' ± ' + str(np.std(kappa_ae)) + '\n' + '\n'
f.write(sentence5)
sentence6 = 'Total average Training time is: ' + str(np.sum(training_time_ae)) + '\n'
f.write(sentence6)
sentence7 = 'Total average Testing time is: ' + str(np.sum(testing_time_ae)) + '\n' + '\n'
f.write(sentence7)
element_mean = np.mean(element_acc_ae, axis=0)
element_std = np.std(element_acc_ae, axis=0)
sentence8 = "Mean of all elements in confusion matrix: " + str(element_mean) + '\n'
f.write(sentence8)
sentence9 = "Standard deviation of all elements in confusion matrix: " + str(element_std) + '\n'
f.write(sentence9)
f.close()
================================================
FILE: README.md
================================================
[](https://paperswithcode.com/sota/hyperspectral-image-classification-on-kennedy?p=attention-based-adaptive-spectral-spatial)
[](https://paperswithcode.com/sota/hyperspectral-image-classification-on-pavia?p=attention-based-adaptive-spectral-spatial)
[](https://paperswithcode.com/sota/hyperspectral-image-classification-on-indian?p=attention-based-adaptive-spectral-spatial)
# Attention-Based Adaptive Spectral-Spatial Kernel ResNet for Hyperspectral Image Classification
This repository is the official implementation of [Attention-Based Adaptive Spectral-Spatial Kernel ResNet for Hyperspectral Image Classification](https://ieeexplore.ieee.org/document/9306920).
[](https://colab.research.google.com/drive/1x2CYfaUXNjX4yDMLCvoVFqAMZXZwwVgS)
>📋 Abstract:
Hyperspectral images (HSIs) provide rich spectral-spatial information with stacked hundreds of contiguous narrowbands. Due to the existence of noise and band correlation, the selection of informative spectral-spatial kernel features poses a challenge. This is often addressed by using convolutional neural networks (CNNs) with receptive field (RF) having fixed sizes. However, these solutions cannot enable neurons to effectively adjust RF sizes and cross-channel dependencies when forward and backward propagations are used to optimize the network. In this article, we present an attention-based adaptive spectral-spatial kernel improved residual network (A²S²K-ResNet) with spectral attention to capture discriminative spectral-spatial features for HSI classification in an end-to-end training fashion. In particular, the proposed network learns selective 3-D convolutional kernels to jointly extract spectral-spatial features using improved 3-D ResBlocks and adopts an efficient feature recalibration (EFR) mechanism to boost the classification performance. Extensive experiments are performed on three well-known hyperspectral data sets, i.e., IP, KSC, and UP, and the proposed A²S²K-ResNet can provide better classification results in terms of overall accuracy (OA), average accuracy (AA), and Kappa compared with the existing methods investigated.
## Requirements
To install requirements:
```setup
conda env create -f environment.yml
```
To download the dataset and setup the folders, run:
```
bash setup_script.sh
```
## Training
To train the model(s) in the paper, run this command in the A2S2KResNet folder:
```train
python A2S2KResNet.py -d -e 200 -i 3 -p 3 -vs 0.9 -o adam
```
## Results
Our model achieves the following performance on 10% of datasets:
### [India Pines](http://www.ehu.eus/ccwintco/uploads/6/67/Indian_pines_corrected.mat) dataset
| Model name | OA |
| ------------------ |---------------- |
| A2S2K-ResNet | 98.66 ± 0.004 % |
### [Kennedy Space Center](http://www.ehu.es/ccwintco/uploads/2/26/KSC.mat) dataset
| Model name | OA |
| ------------------ |---------------- |
| A2S2K-ResNet | 99.34 ± 0.001 % |
### [University of Pavia](http://www.ehu.eus/ccwintco/uploads/e/ee/PaviaU.mat) dataset
| Model name | OA |
| ------------------ |---------------- |
| A2S2K-ResNet | 99.85 ± 0.001 % |
For deatiled results refer to Table IV-VII of our paper.
## Citation
If you use A2S2K-ResNet code in your research, we would appreciate a citation to the original paper:
```
@article{roy2020attention,
title={Attention-based adaptive spectral-spatial kernel resnet for hyperspectral image classification},
author={Swalpa Kumar Roy, and Suvojit Manna, and Tiecheng Song, and Lorenzo Bruzzone},
journal={IEEE Transactions on Geoscience and Remote Sensing},
volume={59},
no.={9},
pp.={7831-7843},
year={2021},
publisher={IEEE}
}
```
================================================
FILE: ResNet/ResNet.py
================================================
#!/usr/bin/env python
# coding: utf-8
# # Imports
import argparse
import collections
import math
import time
import numpy as np
import scipy.io as sio
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from sklearn import metrics, preprocessing
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix
import geniter
import record
import torch_optimizer as optim2
import Utils
from torchsummary import summary
# # Setting Params
parser = argparse.ArgumentParser(description='Training for HSI')
parser.add_argument(
'-d', '--dataset', dest='dataset', default='IN', help="Name of dataset.")
parser.add_argument(
'-o',
'--optimizer',
dest='optimizer',
default='adam',
help="Name of optimizer.")
parser.add_argument(
'-e', '--epoch', type=int, dest='epoch', default=200, help="No of epoch")
parser.add_argument(
'-i', '--iter', type=int, dest='iter', default=3, help="No of iter")
parser.add_argument(
'-p', '--patch', type=int, dest='patch', default=4, help="Length of patch")
parser.add_argument(
'-vs',
'--valid_split',
type=float,
dest='valid_split',
default=0.9,
help="Percentage of validation split.")
args = parser.parse_args()
PARAM_DATASET = args.dataset # UP,IN,SV, KSC
PARAM_EPOCH = args.epoch
PARAM_ITER = args.iter
PATCH_SIZE = args.patch
PARAM_VAL = args.valid_split
PARAM_OPTIM = args.optimizer
# # Data Loading
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# for Monte Carlo runs
seeds = [1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341]
ensemble = 1
global Dataset # UP,IN,SV, KSC
dataset = PARAM_DATASET #input('Please input the name of Dataset(IN, UP, SV, KSC):')
Dataset = dataset.upper()
def load_dataset(Dataset, split=0.9):
data_path = '../dataset/'
if Dataset == 'IN':
mat_data = sio.loadmat(data_path + 'Indian_pines_corrected.mat')
mat_gt = sio.loadmat(data_path + 'Indian_pines_gt.mat')
data_hsi = mat_data['indian_pines_corrected']
gt_hsi = mat_gt['indian_pines_gt']
K = 200
TOTAL_SIZE = 10249
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'UP':
uPavia = sio.loadmat(data_path + 'PaviaU.mat')
gt_uPavia = sio.loadmat(data_path + 'PaviaU_gt.mat')
data_hsi = uPavia['paviaU']
gt_hsi = gt_uPavia['paviaU_gt']
K = 103
TOTAL_SIZE = 42776
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'SV':
SV = sio.loadmat(data_path + 'Salinas_corrected.mat')
gt_SV = sio.loadmat(data_path + 'Salinas_gt.mat')
data_hsi = SV['salinas_corrected']
gt_hsi = gt_SV['salinas_gt']
K = 15
TOTAL_SIZE = 54129
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'KSC':
SV = sio.loadmat(data_path + 'KSC.mat')
gt_SV = sio.loadmat(data_path + 'KSC_gt.mat')
data_hsi = SV['KSC']
gt_hsi = gt_SV['KSC_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 5211
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
shapeor = data_hsi.shape
data_hsi = data_hsi.reshape(-1, data_hsi.shape[-1])
data_hsi = PCA(n_components=K).fit_transform(data_hsi)
shapeor = np.array(shapeor)
shapeor[-1] = K
data_hsi = data_hsi.reshape(shapeor)
return data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT
# # Pytorch Data Loader Creation
data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT = load_dataset(
Dataset, PARAM_VAL)
print(data_hsi.shape)
image_x, image_y, BAND = data_hsi.shape
data = data_hsi.reshape(
np.prod(data_hsi.shape[:2]), np.prod(data_hsi.shape[2:]))
gt = gt_hsi.reshape(np.prod(gt_hsi.shape[:2]), )
CLASSES_NUM = max(gt)
print('The class numbers of the HSI data is:', CLASSES_NUM)
print('-----Importing Setting Parameters-----')
ITER = PARAM_ITER
PATCH_LENGTH = PATCH_SIZE
lr, num_epochs, batch_size = 0.001, 200, 32
loss = torch.nn.CrossEntropyLoss()
img_rows = 2 * PATCH_LENGTH + 1
img_cols = 2 * PATCH_LENGTH + 1
img_channels = data_hsi.shape[2]
INPUT_DIMENSION = data_hsi.shape[2]
ALL_SIZE = data_hsi.shape[0] * data_hsi.shape[1]
VAL_SIZE = int(TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
KAPPA = []
OA = []
AA = []
TRAINING_TIME = []
TESTING_TIME = []
ELEMENT_ACC = np.zeros((ITER, CLASSES_NUM))
data = preprocessing.scale(data)
data_ = data.reshape(data_hsi.shape[0], data_hsi.shape[1], data_hsi.shape[2])
whole_data = data_
padded_data = np.lib.pad(
whole_data, ((PATCH_LENGTH, PATCH_LENGTH), (PATCH_LENGTH, PATCH_LENGTH),
(0, 0)),
'constant',
constant_values=0)
# # Model
def conv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):
"""3x3 convolution with padding"""
return nn.Conv2d(
in_planes,
out_planes,
kernel_size=3,
stride=stride,
padding=dilation,
groups=groups,
bias=False,
dilation=dilation)
def conv1x1(in_planes, out_planes, stride=1):
"""1x1 convolution"""
return nn.Conv2d(
in_planes, out_planes, kernel_size=1, stride=stride, bias=False)
class BasicBlock(nn.Module):
expansion = 1
def __init__(self,
inplanes,
planes,
stride=1,
downsample=None,
groups=1,
base_width=64,
dilation=1,
norm_layer=None):
super(BasicBlock, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
if groups != 1 or base_width != 64:
raise ValueError(
'BasicBlock only supports groups=1 and base_width=64')
if dilation > 1:
raise NotImplementedError(
"Dilation > 1 not supported in BasicBlock")
# Both self.conv1 and self.downsample layers downsample the input when stride != 1
self.conv1 = conv3x3(inplanes, planes, stride)
self.bn1 = norm_layer(planes)
self.relu = nn.ReLU(inplace=True)
self.conv2 = conv3x3(planes, planes)
self.bn2 = norm_layer(planes)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
class Bottleneck(nn.Module):
# Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)
# while original implementation places the stride at the first 1x1 convolution(self.conv1)
# according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385.
# This variant is also known as ResNet V1.5 and improves accuracy according to
# https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.
expansion = 4
def __init__(self,
inplanes,
planes,
stride=1,
downsample=None,
groups=1,
base_width=64,
dilation=1,
norm_layer=None):
super(Bottleneck, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
width = int(planes * (base_width / 64.)) * groups
# Both self.conv2 and self.downsample layers downsample the input when stride != 1
self.conv1 = conv1x1(inplanes, width)
self.bn1 = norm_layer(width)
self.conv2 = conv3x3(width, width, stride, groups, dilation)
self.bn2 = norm_layer(width)
self.conv3 = conv1x1(width, planes * self.expansion)
self.bn3 = norm_layer(planes * self.expansion)
self.relu = nn.ReLU(inplace=True)
self.downsample = downsample
self.stride = stride
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu(out)
out = self.conv3(out)
out = self.bn3(out)
if self.downsample is not None:
identity = self.downsample(x)
out += identity
out = self.relu(out)
return out
class ResNet(nn.Module):
def __init__(self,
block,
layers,
num_classes=1000,
in_shape=3,
zero_init_residual=False,
groups=1,
width_per_group=64,
replace_stride_with_dilation=None,
norm_layer=None):
super(ResNet, self).__init__()
if norm_layer is None:
norm_layer = nn.BatchNorm2d
self._norm_layer = norm_layer
self.inplanes = 64
self.dilation = 1
if replace_stride_with_dilation is None:
# each element in the tuple indicates if we should replace
# the 2x2 stride with a dilated convolution instead
replace_stride_with_dilation = [False, False, False]
if len(replace_stride_with_dilation) != 3:
raise ValueError("replace_stride_with_dilation should be None "
"or a 3-element tuple, got {}".format(
replace_stride_with_dilation))
self.groups = groups
self.base_width = width_per_group
self.conv1 = nn.Conv2d(
in_shape,
self.inplanes,
kernel_size=7,
stride=2,
padding=3,
bias=False)
self.bn1 = norm_layer(self.inplanes)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.layer1 = self._make_layer(block, 64, layers[0])
self.layer2 = self._make_layer(
block,
128,
layers[1],
stride=2,
dilate=replace_stride_with_dilation[0])
self.layer3 = self._make_layer(
block,
256,
layers[2],
stride=2,
dilate=replace_stride_with_dilation[1])
self.layer4 = self._make_layer(
block,
512,
layers[3],
stride=2,
dilate=replace_stride_with_dilation[2])
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(512 * block.expansion, num_classes)
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.kaiming_normal_(
m.weight, mode='fan_out', nonlinearity='relu')
elif isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
# Zero-initialize the last BN in each residual branch,
# so that the residual branch starts with zeros, and each residual block behaves like an identity.
# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677
if zero_init_residual:
for m in self.modules():
if isinstance(m, Bottleneck):
nn.init.constant_(m.bn3.weight, 0)
elif isinstance(m, BasicBlock):
nn.init.constant_(m.bn2.weight, 0)
def _make_layer(self, block, planes, blocks, stride=1, dilate=False):
norm_layer = self._norm_layer
downsample = None
previous_dilation = self.dilation
if dilate:
self.dilation *= stride
stride = 1
if stride != 1 or self.inplanes != planes * block.expansion:
downsample = nn.Sequential(
conv1x1(self.inplanes, planes * block.expansion, stride),
norm_layer(planes * block.expansion),
)
layers = []
layers.append(
block(self.inplanes, planes, stride, downsample, self.groups,
self.base_width, previous_dilation, norm_layer))
self.inplanes = planes * block.expansion
for _ in range(1, blocks):
layers.append(
block(
self.inplanes,
planes,
groups=self.groups,
base_width=self.base_width,
dilation=self.dilation,
norm_layer=norm_layer))
return nn.Sequential(*layers)
def _forward_impl(self, x):
# See note [TorchScript super()]
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
def forward(self, x):
return self._forward_impl(x)
def ResNet34(in_shape, num_classes):
r"""ResNet-34 model from
`"Deep Residual Learning for Image Recognition" `_
Args:
in_shape (tuple): Shape of input
num_classes (tuple): No of classes
"""
model = ResNet(
BasicBlock, [3, 4, 6, 3],
in_shape=in_shape[0],
num_classes=num_classes)
return model
model = ResNet34(
in_shape=(BAND, img_rows, img_cols), num_classes=CLASSES_NUM).cuda()
summary(model, input_data=(BAND, img_rows, img_cols), verbose=1)
# # Plotting
def train(net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs,
early_stopping=True,
early_num=20):
loss_list = [100]
early_epoch = 0
net = net.to(device)
print("training on ", device)
start = time.time()
train_loss_list = []
valida_loss_list = []
train_acc_list = []
valida_acc_list = []
for epoch in range(epochs):
train_acc_sum, n = 0.0, 0
time_epoch = time.time()
lr_adjust = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer, 15, eta_min=0.0, last_epoch=-1)
for X, y in train_iter:
batch_count, train_l_sum = 0, 0
X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
y_hat = net(X)
# print('y_hat', y_hat)
# print('y', y)
l = loss(y_hat, y.long())
optimizer.zero_grad()
l.backward()
optimizer.step()
train_l_sum += l.cpu().item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
lr_adjust.step()
valida_acc, valida_loss = record.evaluate_accuracy(
valida_iter, net, loss, device)
loss_list.append(valida_loss)
train_loss_list.append(train_l_sum) # / batch_count)
train_acc_list.append(train_acc_sum / n)
valida_loss_list.append(valida_loss)
valida_acc_list.append(valida_acc)
print(
'epoch %d, train loss %.6f, train acc %.3f, valida loss %.6f, valida acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
valida_loss, valida_acc, time.time() - time_epoch))
PATH = "./net_DBA.pt"
if early_stopping and loss_list[-2] < loss_list[-1]:
if early_epoch == 0: # and valida_acc > 0.9:
torch.save(net.state_dict(), PATH)
early_epoch += 1
loss_list[-1] = loss_list[-2]
if early_epoch == early_num:
net.load_state_dict(torch.load(PATH))
break
else:
early_epoch = 0
print('epoch %d, loss %.4f, train acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
time.time() - start))
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def select(groundTruth): #divide dataset into train and test datasets
labels_loc = {}
train = {}
test = {}
m = max(groundTruth)
#amount = [3, 41, 29, 7, 14, 20, 2, 15, 3, 36, 64, 22, 4, 28, 10, 2]
#amount = [43, 1387, 801, 230, 469, 710, 26, 463, 17, 936, 2391, 571, 201, 1237, 376, 91]
if Dataset == 'IN':
amount = [
35, 1011, 581, 167, 344, 515, 19, 327, 12, 683, 1700, 418, 138,
876, 274, 69
] #IP 20%
#amount = [6, 144, 84, 24, 50, 75, 3, 49, 2, 97, 247, 62, 22, 130, 38, 10] #IP 20%
if Dataset == 'UP':
amount = [5297, 14974, 1648, 2424, 1076, 4026, 1046, 2950, 755] #UP
if Dataset == 'KSC':
amount = [
530, 165, 176, 170, 110, 161, 80, 299, 377, 283, 296, 341, 654
] #KSC
for i in range(m):
indices = [
j for j, x in enumerate(groundTruth.ravel().tolist()) if x == i + 1
]
np.random.shuffle(indices)
labels_loc[i] = indices
nb_val = int(amount[i])
train[i] = indices[:-nb_val]
test[i] = indices[-nb_val:]
# whole_indices = []
train_indices = []
test_indices = []
for i in range(m):
# whole_indices += labels_loc[i]
train_indices += train[i]
test_indices += test[i]
np.random.shuffle(train_indices)
np.random.shuffle(test_indices)
return train_indices, test_indices
# # Training
for index_iter in range(ITER):
print('iter:', index_iter)
#define the model
#net = pResNet(32, 48, CLASSES_NUM, BAND, 2, 16, bottleneck=True)
#net = resnet20(num_classes=CLASSES_NUM)
net = ResNet34(
in_shape=(BAND, img_rows, img_cols), num_classes=CLASSES_NUM)
if PARAM_OPTIM == 'diffgrad':
optimizer = optim2.DiffGrad(
net.parameters(),
lr=lr,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0) # weight_decay=0.0001)
if PARAM_OPTIM == 'adam':
optimizer = optim.Adam(
net.parameters(),
lr=1e-3,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0)
time_1 = int(time.time())
np.random.seed(seeds[index_iter])
# train_indices, test_indices = select(gt)
train_indices, test_indices = sampling(VALIDATION_SPLIT, gt)
_, total_indices = sampling(1, gt)
TRAIN_SIZE = len(train_indices)
print('Train size: ', TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
print('Test size: ', TEST_SIZE)
VAL_SIZE = int(TRAIN_SIZE)
print('Validation size: ', VAL_SIZE)
print('-----Selecting Small Pieces from the Original Cube Data-----')
train_iter, valida_iter, test_iter, all_iter = geniter.generate_iter(
TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE,
total_indices, VAL_SIZE, whole_data, PATCH_LENGTH, padded_data,
INPUT_DIMENSION, 16, gt) #batchsize in 1
tic1 = time.time()
train(
net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs=PARAM_EPOCH)
toc1 = time.time()
pred_test = []
tic2 = time.time()
with torch.no_grad():
for X, y in test_iter:
# print('Shape of X',X.shape)
X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
y_hat = net(X)
pred_test.extend(np.array(net(X).cpu().argmax(axis=1)))
toc2 = time.time()
collections.Counter(pred_test)
gt_test = gt[test_indices] - 1
overall_acc = metrics.accuracy_score(pred_test, gt_test[:-VAL_SIZE])
confusion_matrix = metrics.confusion_matrix(pred_test, gt_test[:-VAL_SIZE])
each_acc, average_acc = record.aa_and_each_accuracy(confusion_matrix)
kappa = metrics.cohen_kappa_score(pred_test, gt_test[:-VAL_SIZE])
torch.save(net.state_dict(),
"./models/" + 'Resnet34' + str(round(overall_acc, 3)) + '.pt')
KAPPA.append(kappa)
OA.append(overall_acc)
AA.append(average_acc)
TRAINING_TIME.append(toc1 - tic1)
TESTING_TIME.append(toc2 - tic2)
ELEMENT_ACC[index_iter, :] = each_acc
# # Map, Records
print("--------" + " Training Finished-----------")
record.record_output(
OA, AA, KAPPA, ELEMENT_ACC, TRAINING_TIME, TESTING_TIME,
'./report/' + 'Resnet34patch:' + str(img_rows) + '_' + Dataset + 'split' +
str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM + '.txt')
Utils.generate_png(
all_iter, net, gt_hsi, Dataset, device, total_indices,
'./classification_maps/' + 'Resnet34patch:' + str(img_rows) + '_' + Dataset
+ 'split' + str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM)
================================================
FILE: ResNet/Utils.py
================================================
import numpy as np
from sklearn import metrics, preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, cohen_kappa_score
from operator import truediv
import matplotlib.pyplot as plt
import scipy.io as sio
import os
import spectral
import torch
import cv2
from operator import truediv
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def set_figsize(figsize=(3.5, 2.5)):
display.set_matplotlib_formats('svg')
plt.rcParams['figure.figsize'] = figsize
def classification_map(map, ground_truth, dpi, save_path):
fig = plt.figure(frameon=False)
fig.set_size_inches(ground_truth.shape[1] * 2.0 / dpi,
ground_truth.shape[0] * 2.0 / dpi)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
fig.add_axes(ax)
ax.imshow(map)
fig.savefig(save_path, dpi=dpi)
return 0
def list_to_colormap(x_list):
y = np.zeros((x_list.shape[0], 3))
for index, item in enumerate(x_list):
if item == 0:
y[index] = np.array([255, 0, 0]) / 255.
if item == 1:
y[index] = np.array([0, 255, 0]) / 255.
if item == 2:
y[index] = np.array([0, 0, 255]) / 255.
if item == 3:
y[index] = np.array([255, 255, 0]) / 255.
if item == 4:
y[index] = np.array([0, 255, 255]) / 255.
if item == 5:
y[index] = np.array([255, 0, 255]) / 255.
if item == 6:
y[index] = np.array([192, 192, 192]) / 255.
if item == 7:
y[index] = np.array([128, 128, 128]) / 255.
if item == 8:
y[index] = np.array([128, 0, 0]) / 255.
if item == 9:
y[index] = np.array([128, 128, 0]) / 255.
if item == 10:
y[index] = np.array([0, 128, 0]) / 255.
if item == 11:
y[index] = np.array([128, 0, 128]) / 255.
if item == 12:
y[index] = np.array([0, 128, 128]) / 255.
if item == 13:
y[index] = np.array([0, 0, 128]) / 255.
if item == 14:
y[index] = np.array([255, 165, 0]) / 255.
if item == 15:
y[index] = np.array([255, 215, 0]) / 255.
if item == 16:
y[index] = np.array([0, 0, 0]) / 255.
if item == 17:
y[index] = np.array([215, 255, 0]) / 255.
if item == 18:
y[index] = np.array([0, 255, 215]) / 255.
if item == -1:
y[index] = np.array([0, 0, 0]) / 255.
return y
def generate_png(all_iter, net, gt_hsi, Dataset, device, total_indices, path):
pred_test = []
for X, y in all_iter:
X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
pred_test.extend(net(X).cpu().argmax(axis=1).detach().numpy())
gt = gt_hsi.flatten()
x_label = np.zeros(gt.shape)
for i in range(len(gt)):
if gt[i] == 0:
gt[i] = 17
x_label[i] = 16
gt = gt[:] - 1
x_label[total_indices] = pred_test
x = np.ravel(x_label)
y_list = list_to_colormap(x)
y_gt = list_to_colormap(gt)
y_re = np.reshape(y_list, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
gt_re = np.reshape(y_gt, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
classification_map(y_re, gt_hsi, 300,
path + '.png')
classification_map(gt_re, gt_hsi, 300,
path + '_gt.png')
print('------Get classification maps successful-------')
================================================
FILE: ResNet/geniter.py
================================================
import torch
import numpy as np
import torch.utils.data as Data
def index_assignment(index, row, col, pad_length):
new_assign = {}
for counter, value in enumerate(index):
assign_0 = value // col + pad_length
assign_1 = value % col + pad_length
new_assign[counter] = [assign_0, assign_1]
return new_assign
def select_patch(matrix, pos_row, pos_col, ex_len):
selected_rows = matrix[range(pos_row-ex_len, pos_row+ex_len+1)]
selected_patch = selected_rows[:, range(pos_col-ex_len, pos_col+ex_len+1)]
return selected_patch
def select_small_cubic(data_size, data_indices, whole_data, patch_length, padded_data, dimension):
small_cubic_data = np.zeros((data_size, 2 * patch_length + 1, 2 * patch_length + 1, dimension))
data_assign = index_assignment(data_indices, whole_data.shape[0], whole_data.shape[1], patch_length)
for i in range(len(data_assign)):
small_cubic_data[i] = select_patch(padded_data, data_assign[i][0], data_assign[i][1], patch_length)
return small_cubic_data
def generate_iter(TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE, total_indices, VAL_SIZE,
whole_data, PATCH_LENGTH, padded_data, INPUT_DIMENSION, batch_size, gt):
gt_all = gt[total_indices] - 1
y_train = gt[train_indices] - 1
y_test = gt[test_indices] - 1
all_data = select_small_cubic(TOTAL_SIZE, total_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
train_data = select_small_cubic(TRAIN_SIZE, train_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
print(train_data.shape)
test_data = select_small_cubic(TEST_SIZE, test_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
x_train = train_data.reshape(train_data.shape[0], train_data.shape[1], train_data.shape[2], INPUT_DIMENSION)
x_test_all = test_data.reshape(test_data.shape[0], test_data.shape[1], test_data.shape[2], INPUT_DIMENSION)
x_val = x_test_all[-VAL_SIZE:]
y_val = y_test[-VAL_SIZE:]
x_test = x_test_all[:-VAL_SIZE]
y_test = y_test[:-VAL_SIZE]
x1_tensor_train = torch.from_numpy(x_train).type(torch.FloatTensor)#.unsqueeze(1)
y1_tensor_train = torch.from_numpy(y_train).type(torch.FloatTensor)
torch_dataset_train = Data.TensorDataset(x1_tensor_train, y1_tensor_train)
x1_tensor_valida = torch.from_numpy(x_val).type(torch.FloatTensor)#.unsqueeze(1)
y1_tensor_valida = torch.from_numpy(y_val).type(torch.FloatTensor)
torch_dataset_valida = Data.TensorDataset(x1_tensor_valida, y1_tensor_valida)
x1_tensor_test = torch.from_numpy(x_test).type(torch.FloatTensor)#.unsqueeze(1)
y1_tensor_test = torch.from_numpy(y_test).type(torch.FloatTensor)
torch_dataset_test = Data.TensorDataset(x1_tensor_test,y1_tensor_test)
all_data.reshape(all_data.shape[0], all_data.shape[1], all_data.shape[2], INPUT_DIMENSION)
all_tensor_data = torch.from_numpy(all_data).type(torch.FloatTensor)#.unsqueeze(1)
all_tensor_data_label = torch.from_numpy(gt_all).type(torch.FloatTensor)
torch_dataset_all = Data.TensorDataset(all_tensor_data, all_tensor_data_label)
train_iter = Data.DataLoader(
dataset=torch_dataset_train, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
valiada_iter = Data.DataLoader(
dataset=torch_dataset_valida, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
test_iter = Data.DataLoader(
dataset=torch_dataset_test, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
all_iter = Data.DataLoader(
dataset=torch_dataset_all, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
return train_iter, valiada_iter, test_iter, all_iter #, y_test
================================================
FILE: ResNet/record.py
================================================
import numpy as np
import torch
from operator import truediv
def evaluate_accuracy(data_iter, net, loss, device):
acc_sum, n = 0.0, 0
with torch.no_grad():
for X, y in data_iter:
test_l_sum, test_num = 0, 0
X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
net.eval()
y_hat = net(X)
l = loss(y_hat, y.long())
acc_sum += (y_hat.argmax(dim=1) == y.to(device)).float().sum().cpu().item()
test_l_sum += l
test_num += 1
net.train()
n += y.shape[0]
return [acc_sum / n, test_l_sum] # / test_num]
def aa_and_each_accuracy(confusion_matrix):
list_diag = np.diag(confusion_matrix)
list_raw_sum = np.sum(confusion_matrix, axis=1)
each_acc = np.nan_to_num(truediv(list_diag, list_raw_sum))
average_acc = np.mean(each_acc)
return each_acc, average_acc
def record_output(oa_ae, aa_ae, kappa_ae, element_acc_ae, training_time_ae, testing_time_ae, path):
f = open(path, 'a')
sentence0 = 'OAs for each iteration are:' + str(oa_ae) + '\n'
f.write(sentence0)
sentence1 = 'AAs for each iteration are:' + str(aa_ae) + '\n'
f.write(sentence1)
sentence2 = 'KAPPAs for each iteration are:' + str(kappa_ae) + '\n' + '\n'
f.write(sentence2)
sentence3 = 'mean_OA ± std_OA is: ' + str(np.mean(oa_ae)) + ' ± ' + str(np.std(oa_ae)) + '\n'
f.write(sentence3)
sentence4 = 'mean_AA ± std_AA is: ' + str(np.mean(aa_ae)) + ' ± ' + str(np.std(aa_ae)) + '\n'
f.write(sentence4)
sentence5 = 'mean_KAPPA ± std_KAPPA is: ' + str(np.mean(kappa_ae)) + ' ± ' + str(np.std(kappa_ae)) + '\n' + '\n'
f.write(sentence5)
sentence6 = 'Total average Training time is: ' + str(np.sum(training_time_ae)) + '\n'
f.write(sentence6)
sentence7 = 'Total average Testing time is: ' + str(np.sum(testing_time_ae)) + '\n' + '\n'
f.write(sentence7)
element_mean = np.mean(element_acc_ae, axis=0)
element_std = np.std(element_acc_ae, axis=0)
sentence8 = "Mean of all elements in confusion matrix: " + str(element_mean) + '\n'
f.write(sentence8)
sentence9 = "Standard deviation of all elements in confusion matrix: " + str(element_std) + '\n'
f.write(sentence9)
f.close()
================================================
FILE: SSRN/SSRN.py
================================================
#!/usr/bin/env python
# coding: utf-8
# # Imports
import argparse
import collections
import math
import time
import numpy as np
import scipy.io as sio
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from sklearn import metrics, preprocessing
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix
import geniter
import record
import torch_optimizer as optim2
import Utils
from torchsummary import summary
# # Setting Params
parser = argparse.ArgumentParser(description='Training for HSI')
parser.add_argument(
'-d', '--dataset', dest='dataset', default='IN', help="Name of dataset.")
parser.add_argument(
'-o',
'--optimizer',
dest='optimizer',
default='adam',
help="Name of optimizer.")
parser.add_argument(
'-e', '--epoch', type=int, dest='epoch', default=200, help="No of epoch")
parser.add_argument(
'-i', '--iter', type=int, dest='iter', default=3, help="No of iter")
parser.add_argument(
'-p', '--patch', type=int, dest='patch', default=4, help="Length of patch")
parser.add_argument(
'-vs',
'--valid_split',
type=float,
dest='valid_split',
default=0.9,
help="Percentage of validation split.")
args = parser.parse_args()
PARAM_DATASET = args.dataset # UP,IN,SV, KSC
PARAM_EPOCH = args.epoch
PARAM_ITER = args.iter
PATCH_SIZE = args.patch
PARAM_VAL = args.valid_split
PARAM_OPTIM = args.optimizer
# # Data Loading
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# for Monte Carlo runs
seeds = [1331, 1332, 1333, 1334, 1335, 1336, 1337, 1338, 1339, 1340, 1341]
ensemble = 1
global Dataset # UP,IN,SV, KSC
dataset = PARAM_DATASET #input('Please input the name of Dataset(IN, UP, SV, KSC):')
Dataset = dataset.upper()
def load_dataset(Dataset, split=0.9):
data_path = '../dataset/'
if Dataset == 'IN':
mat_data = sio.loadmat(data_path + 'Indian_pines_corrected.mat')
mat_gt = sio.loadmat(data_path + 'Indian_pines_gt.mat')
data_hsi = mat_data['indian_pines_corrected']
gt_hsi = mat_gt['indian_pines_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 10249
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'UP':
uPavia = sio.loadmat(data_path + 'PaviaU.mat')
gt_uPavia = sio.loadmat(data_path + 'PaviaU_gt.mat')
data_hsi = uPavia['paviaU']
gt_hsi = gt_uPavia['paviaU_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 42776
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'SV':
SV = sio.loadmat(data_path + 'Salinas_corrected.mat')
gt_SV = sio.loadmat(data_path + 'Salinas_gt.mat')
data_hsi = SV['salinas_corrected']
gt_hsi = gt_SV['salinas_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 54129
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
if Dataset == 'KSC':
SV = sio.loadmat(data_path + 'KSC.mat')
gt_SV = sio.loadmat(data_path + 'KSC_gt.mat')
data_hsi = SV['KSC']
gt_hsi = gt_SV['KSC_gt']
K = data_hsi.shape[2]
TOTAL_SIZE = 5211
VALIDATION_SPLIT = split
TRAIN_SIZE = math.ceil(TOTAL_SIZE * VALIDATION_SPLIT)
shapeor = data_hsi.shape
data_hsi = data_hsi.reshape(-1, data_hsi.shape[-1])
data_hsi = PCA(n_components=K).fit_transform(data_hsi)
shapeor = np.array(shapeor)
shapeor[-1] = K
data_hsi = data_hsi.reshape(shapeor)
return data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT
# # Pytorch Data Loader Creation
data_hsi, gt_hsi, TOTAL_SIZE, TRAIN_SIZE, VALIDATION_SPLIT = load_dataset(
Dataset, PARAM_VAL)
print(data_hsi.shape)
image_x, image_y, BAND = data_hsi.shape
data = data_hsi.reshape(
np.prod(data_hsi.shape[:2]), np.prod(data_hsi.shape[2:]))
gt = gt_hsi.reshape(np.prod(gt_hsi.shape[:2]), )
CLASSES_NUM = max(gt)
print('The class numbers of the HSI data is:', CLASSES_NUM)
print('-----Importing Setting Parameters-----')
ITER = PARAM_ITER
PATCH_LENGTH = PATCH_SIZE
lr, num_epochs, batch_size = 0.001, 200, 32
loss = torch.nn.CrossEntropyLoss()
img_rows = 2 * PATCH_LENGTH + 1
img_cols = 2 * PATCH_LENGTH + 1
img_channels = data_hsi.shape[2]
INPUT_DIMENSION = data_hsi.shape[2]
ALL_SIZE = data_hsi.shape[0] * data_hsi.shape[1]
VAL_SIZE = int(TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
KAPPA = []
OA = []
AA = []
TRAINING_TIME = []
TESTING_TIME = []
ELEMENT_ACC = np.zeros((ITER, CLASSES_NUM))
data = preprocessing.scale(data)
data_ = data.reshape(data_hsi.shape[0], data_hsi.shape[1], data_hsi.shape[2])
whole_data = data_
padded_data = np.lib.pad(
whole_data, ((PATCH_LENGTH, PATCH_LENGTH), (PATCH_LENGTH, PATCH_LENGTH),
(0, 0)),
'constant',
constant_values=0)
# # Model
class Residual(nn.Module): # pytorch
def __init__(self,
in_channels,
out_channels,
kernel_size,
padding,
use_1x1conv=False,
stride=1):
super(Residual, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv3d(
in_channels,
out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride), nn.ReLU())
self.conv2 = nn.Conv3d(
out_channels,
out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride)
if use_1x1conv:
self.conv3 = nn.Conv3d(
in_channels, out_channels, kernel_size=1, stride=stride)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm3d(out_channels)
self.bn2 = nn.BatchNorm3d(out_channels)
def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
return F.relu(Y + X)
class SSRN_network(nn.Module):
def __init__(self, band, classes):
super(SSRN_network, self).__init__()
self.name = 'SSRN'
self.conv1 = nn.Conv3d(
in_channels=1,
out_channels=24,
kernel_size=(1, 1, 7),
stride=(1, 1, 2))
self.batch_norm1 = nn.Sequential(
nn.BatchNorm3d(24, eps=0.001, momentum=0.1, affine=True), # 0.1
nn.ReLU(inplace=True))
self.res_net1 = Residual(24, 24, (1, 1, 7), (0, 0, 3))
self.res_net2 = Residual(24, 24, (1, 1, 7), (0, 0, 3))
self.res_net3 = Residual(24, 24, (3, 3, 1), (1, 1, 0))
self.res_net4 = Residual(24, 24, (3, 3, 1), (1, 1, 0))
kernel_3d = math.ceil((band - 6) / 2)
self.conv2 = nn.Conv3d(
in_channels=24,
out_channels=128,
padding=(0, 0, 0),
kernel_size=(1, 1, kernel_3d),
stride=(1, 1, 1))
self.batch_norm2 = nn.Sequential(
nn.BatchNorm3d(128, eps=0.001, momentum=0.1, affine=True), # 0.1
nn.ReLU(inplace=True))
self.conv3 = nn.Conv3d(
in_channels=1,
out_channels=24,
padding=(0, 0, 0),
kernel_size=(3, 3, 128),
stride=(1, 1, 1))
self.batch_norm3 = nn.Sequential(
nn.BatchNorm3d(24, eps=0.001, momentum=0.1, affine=True), # 0.1
nn.ReLU(inplace=True))
self.avg_pooling = nn.AvgPool3d(kernel_size=(5, 5, 1))
self.full_connection = nn.Sequential(
nn.Dropout(p=0.5),
nn.Linear(24, classes) # ,
# nn.Softmax()
)
def forward(self, X):
x1 = self.batch_norm1(self.conv1(X))
# print('x1', x1.shape)
x2 = self.res_net1(x1)
x2 = self.res_net2(x2)
x2 = self.batch_norm2(self.conv2(x2))
x2 = x2.permute(0, 4, 2, 3, 1)
x2 = self.batch_norm3(self.conv3(x2))
x3 = self.res_net3(x2)
x3 = self.res_net4(x3)
x4 = self.avg_pooling(x3)
x4 = x4.view(x4.size(0), -1)
return self.full_connection(x4)
model = SSRN_network(BAND, CLASSES_NUM).cuda()
summary(model, input_data=(1, img_rows, img_cols, BAND), verbose=1)
# # Plotting
def train(net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs,
early_stopping=True,
early_num=20):
loss_list = [100]
early_epoch = 0
net = net.to(device)
print("training on ", device)
start = time.time()
train_loss_list = []
valida_loss_list = []
train_acc_list = []
valida_acc_list = []
for epoch in range(epochs):
train_acc_sum, n = 0.0, 0
time_epoch = time.time()
lr_adjust = torch.optim.lr_scheduler.CosineAnnealingLR(
optimizer, 15, eta_min=0.0, last_epoch=-1)
for X, y in train_iter:
batch_count, train_l_sum = 0, 0
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
y_hat = net(X)
# print('y_hat', y_hat)
# print('y', y)
l = loss(y_hat, y.long())
optimizer.zero_grad()
l.backward()
optimizer.step()
train_l_sum += l.cpu().item()
train_acc_sum += (y_hat.argmax(dim=1) == y).sum().cpu().item()
n += y.shape[0]
batch_count += 1
lr_adjust.step()
valida_acc, valida_loss = record.evaluate_accuracy(
valida_iter, net, loss, device)
loss_list.append(valida_loss)
train_loss_list.append(train_l_sum) # / batch_count)
train_acc_list.append(train_acc_sum / n)
valida_loss_list.append(valida_loss)
valida_acc_list.append(valida_acc)
print(
'epoch %d, train loss %.6f, train acc %.3f, valida loss %.6f, valida acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
valida_loss, valida_acc, time.time() - time_epoch))
PATH = "./net_DBA.pt"
# if loss_list[-1] <= 0.01 and valida_acc >= 0.95:
# torch.save(net.state_dict(), PATH)
# break
if early_stopping and loss_list[-2] < loss_list[-1]:
if early_epoch == 0: # and valida_acc > 0.9:
torch.save(net.state_dict(), PATH)
early_epoch += 1
loss_list[-1] = loss_list[-2]
if early_epoch == early_num:
net.load_state_dict(torch.load(PATH))
break
else:
early_epoch = 0
print('epoch %d, loss %.4f, train acc %.3f, time %.1f sec'
% (epoch + 1, train_l_sum / batch_count, train_acc_sum / n,
time.time() - start))
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def select(groundTruth): #divide dataset into train and test datasets
labels_loc = {}
train = {}
test = {}
m = max(groundTruth)
#amount = [3, 41, 29, 7, 14, 20, 2, 15, 3, 36, 64, 22, 4, 28, 10, 2]
#amount = [43, 1387, 801, 230, 469, 710, 26, 463, 17, 936, 2391, 571, 201, 1237, 376, 91]
if Dataset == 'IN':
amount = [
35, 1011, 581, 167, 344, 515, 19, 327, 12, 683, 1700, 418, 138,
876, 274, 69
] #IP 20%
#amount = [6, 144, 84, 24, 50, 75, 3, 49, 2, 97, 247, 62, 22, 130, 38, 10] #IP 20%
if Dataset == 'UP':
amount = [5297, 14974, 1648, 2424, 1076, 4026, 1046, 2950, 755] #UP
if Dataset == 'KSC':
amount = [
530, 165, 176, 170, 110, 161, 80, 299, 377, 283, 296, 341, 654
] #KSC
for i in range(m):
indices = [
j for j, x in enumerate(groundTruth.ravel().tolist()) if x == i + 1
]
np.random.shuffle(indices)
labels_loc[i] = indices
nb_val = int(amount[i])
train[i] = indices[:-nb_val]
test[i] = indices[-nb_val:]
# whole_indices = []
train_indices = []
test_indices = []
for i in range(m):
# whole_indices += labels_loc[i]
train_indices += train[i]
test_indices += test[i]
np.random.shuffle(train_indices)
np.random.shuffle(test_indices)
return train_indices, test_indices
# # Training
for index_iter in range(ITER):
print('iter:', index_iter)
# define the model
net = SSRN_network(BAND, CLASSES_NUM)
if PARAM_OPTIM == 'diffgrad':
optimizer = optim2.DiffGrad(
net.parameters(),
lr=lr,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0) # weight_decay=0.0001)
if PARAM_OPTIM == 'adam':
optimizer = optim.Adam(
net.parameters(),
lr=1e-3,
betas=(0.9, 0.999),
eps=1e-8,
weight_decay=0)
time_1 = int(time.time())
np.random.seed(seeds[index_iter])
# train_indices, test_indices = select(gt)
train_indices, test_indices = sampling(VALIDATION_SPLIT, gt)
_, total_indices = sampling(1, gt)
TRAIN_SIZE = len(train_indices)
print('Train size: ', TRAIN_SIZE)
TEST_SIZE = TOTAL_SIZE - TRAIN_SIZE
print('Test size: ', TEST_SIZE)
VAL_SIZE = int(TRAIN_SIZE)
print('Validation size: ', VAL_SIZE)
print('-----Selecting Small Pieces from the Original Cube Data-----')
train_iter, valida_iter, test_iter, all_iter = geniter.generate_iter(
TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE,
total_indices, VAL_SIZE, whole_data, PATCH_LENGTH, padded_data,
INPUT_DIMENSION, 16, gt) #batchsize in 1
tic1 = time.time()
train(
net,
train_iter,
valida_iter,
loss,
optimizer,
device,
epochs=PARAM_EPOCH)
toc1 = time.time()
pred_test = []
tic2 = time.time()
with torch.no_grad():
for X, y in test_iter:
X = X.to(device)
net.eval()
y_hat = net(X)
pred_test.extend(np.array(net(X).cpu().argmax(axis=1)))
toc2 = time.time()
collections.Counter(pred_test)
gt_test = gt[test_indices] - 1
overall_acc = metrics.accuracy_score(pred_test, gt_test[:-VAL_SIZE])
confusion_matrix = metrics.confusion_matrix(pred_test, gt_test[:-VAL_SIZE])
each_acc, average_acc = record.aa_and_each_accuracy(confusion_matrix)
kappa = metrics.cohen_kappa_score(pred_test, gt_test[:-VAL_SIZE])
torch.save(net.state_dict(),
"./models/" + 'SSRN' + str(round(overall_acc, 3)) + '.pt')
KAPPA.append(kappa)
OA.append(overall_acc)
AA.append(average_acc)
TRAINING_TIME.append(toc1 - tic1)
TESTING_TIME.append(toc2 - tic2)
ELEMENT_ACC[index_iter, :] = each_acc
# # Map, Records
print("--------" + " Training Finished-----------")
record.record_output(
OA, AA, KAPPA, ELEMENT_ACC, TRAINING_TIME, TESTING_TIME,
'./report/' + 'SSRNpatch:' + str(img_rows) + '_' + Dataset + 'split' +
str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM + '.txt')
Utils.generate_png(
all_iter, net, gt_hsi, Dataset, device, total_indices,
'./classification_maps/' + 'SSRNpatch:' + str(img_rows) + '_' + Dataset +
'split' + str(VALIDATION_SPLIT) + 'lr' + str(lr) + PARAM_OPTIM)
================================================
FILE: SSRN/Utils.py
================================================
import numpy as np
from sklearn import metrics, preprocessing
from sklearn.preprocessing import MinMaxScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report, cohen_kappa_score
from operator import truediv
import matplotlib.pyplot as plt
import scipy.io as sio
import os
import spectral
import torch
import cv2
from operator import truediv
def sampling(proportion, ground_truth):
train = {}
test = {}
labels_loc = {}
m = max(ground_truth)
for i in range(m):
indexes = [
j for j, x in enumerate(ground_truth.ravel().tolist())
if x == i + 1
]
np.random.shuffle(indexes)
labels_loc[i] = indexes
if proportion != 1:
nb_val = max(int((1 - proportion) * len(indexes)), 3)
else:
nb_val = 0
train[i] = indexes[:nb_val]
test[i] = indexes[nb_val:]
train_indexes = []
test_indexes = []
for i in range(m):
train_indexes += train[i]
test_indexes += test[i]
np.random.shuffle(train_indexes)
np.random.shuffle(test_indexes)
return train_indexes, test_indexes
def set_figsize(figsize=(3.5, 2.5)):
display.set_matplotlib_formats('svg')
plt.rcParams['figure.figsize'] = figsize
def classification_map(map, ground_truth, dpi, save_path):
fig = plt.figure(frameon=False)
fig.set_size_inches(ground_truth.shape[1] * 2.0 / dpi,
ground_truth.shape[0] * 2.0 / dpi)
ax = plt.Axes(fig, [0., 0., 1., 1.])
ax.set_axis_off()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
fig.add_axes(ax)
ax.imshow(map)
fig.savefig(save_path, dpi=dpi)
return 0
def list_to_colormap(x_list):
y = np.zeros((x_list.shape[0], 3))
for index, item in enumerate(x_list):
if item == 0:
y[index] = np.array([255, 0, 0]) / 255.
if item == 1:
y[index] = np.array([0, 255, 0]) / 255.
if item == 2:
y[index] = np.array([0, 0, 255]) / 255.
if item == 3:
y[index] = np.array([255, 255, 0]) / 255.
if item == 4:
y[index] = np.array([0, 255, 255]) / 255.
if item == 5:
y[index] = np.array([255, 0, 255]) / 255.
if item == 6:
y[index] = np.array([192, 192, 192]) / 255.
if item == 7:
y[index] = np.array([128, 128, 128]) / 255.
if item == 8:
y[index] = np.array([128, 0, 0]) / 255.
if item == 9:
y[index] = np.array([128, 128, 0]) / 255.
if item == 10:
y[index] = np.array([0, 128, 0]) / 255.
if item == 11:
y[index] = np.array([128, 0, 128]) / 255.
if item == 12:
y[index] = np.array([0, 128, 128]) / 255.
if item == 13:
y[index] = np.array([0, 0, 128]) / 255.
if item == 14:
y[index] = np.array([255, 165, 0]) / 255.
if item == 15:
y[index] = np.array([255, 215, 0]) / 255.
if item == 16:
y[index] = np.array([0, 0, 0]) / 255.
if item == 17:
y[index] = np.array([215, 255, 0]) / 255.
if item == 18:
y[index] = np.array([0, 255, 215]) / 255.
if item == -1:
y[index] = np.array([0, 0, 0]) / 255.
return y
def generate_png(all_iter, net, gt_hsi, Dataset, device, total_indices, path):
pred_test = []
for X, y in all_iter:
# X = X.permute(0, 3, 1, 2)
X = X.to(device)
net.eval()
pred_test.extend(net(X).cpu().argmax(axis=1).detach().numpy())
gt = gt_hsi.flatten()
x_label = np.zeros(gt.shape)
for i in range(len(gt)):
if gt[i] == 0:
gt[i] = 17
x_label[i] = 16
gt = gt[:] - 1
x_label[total_indices] = pred_test
x = np.ravel(x_label)
y_list = list_to_colormap(x)
y_gt = list_to_colormap(gt)
y_re = np.reshape(y_list, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
gt_re = np.reshape(y_gt, (gt_hsi.shape[0], gt_hsi.shape[1], 3))
classification_map(y_re, gt_hsi, 300,
path + '.png')
classification_map(gt_re, gt_hsi, 300,
path + '_gt.png')
print('------Get classification maps successful-------')
================================================
FILE: SSRN/geniter.py
================================================
import torch
import numpy as np
import torch.utils.data as Data
def index_assignment(index, row, col, pad_length):
new_assign = {}
for counter, value in enumerate(index):
assign_0 = value // col + pad_length
assign_1 = value % col + pad_length
new_assign[counter] = [assign_0, assign_1]
return new_assign
def select_patch(matrix, pos_row, pos_col, ex_len):
selected_rows = matrix[range(pos_row-ex_len, pos_row+ex_len+1)]
selected_patch = selected_rows[:, range(pos_col-ex_len, pos_col+ex_len+1)]
return selected_patch
def select_small_cubic(data_size, data_indices, whole_data, patch_length, padded_data, dimension):
small_cubic_data = np.zeros((data_size, 2 * patch_length + 1, 2 * patch_length + 1, dimension))
data_assign = index_assignment(data_indices, whole_data.shape[0], whole_data.shape[1], patch_length)
for i in range(len(data_assign)):
small_cubic_data[i] = select_patch(padded_data, data_assign[i][0], data_assign[i][1], patch_length)
return small_cubic_data
def generate_iter(TRAIN_SIZE, train_indices, TEST_SIZE, test_indices, TOTAL_SIZE, total_indices, VAL_SIZE,
whole_data, PATCH_LENGTH, padded_data, INPUT_DIMENSION, batch_size, gt):
gt_all = gt[total_indices] - 1
y_train = gt[train_indices] - 1
y_test = gt[test_indices] - 1
all_data = select_small_cubic(TOTAL_SIZE, total_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
train_data = select_small_cubic(TRAIN_SIZE, train_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
print(train_data.shape)
test_data = select_small_cubic(TEST_SIZE, test_indices, whole_data,
PATCH_LENGTH, padded_data, INPUT_DIMENSION)
x_train = train_data.reshape(train_data.shape[0], train_data.shape[1], train_data.shape[2], INPUT_DIMENSION)
x_test_all = test_data.reshape(test_data.shape[0], test_data.shape[1], test_data.shape[2], INPUT_DIMENSION)
x_val = x_test_all[-VAL_SIZE:]
y_val = y_test[-VAL_SIZE:]
x_test = x_test_all[:-VAL_SIZE]
y_test = y_test[:-VAL_SIZE]
x1_tensor_train = torch.from_numpy(x_train).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_train = torch.from_numpy(y_train).type(torch.FloatTensor)
torch_dataset_train = Data.TensorDataset(x1_tensor_train, y1_tensor_train)
x1_tensor_valida = torch.from_numpy(x_val).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_valida = torch.from_numpy(y_val).type(torch.FloatTensor)
torch_dataset_valida = Data.TensorDataset(x1_tensor_valida, y1_tensor_valida)
x1_tensor_test = torch.from_numpy(x_test).type(torch.FloatTensor).unsqueeze(1)
y1_tensor_test = torch.from_numpy(y_test).type(torch.FloatTensor)
torch_dataset_test = Data.TensorDataset(x1_tensor_test,y1_tensor_test)
all_data.reshape(all_data.shape[0], all_data.shape[1], all_data.shape[2], INPUT_DIMENSION)
all_tensor_data = torch.from_numpy(all_data).type(torch.FloatTensor).unsqueeze(1)
all_tensor_data_label = torch.from_numpy(gt_all).type(torch.FloatTensor)
torch_dataset_all = Data.TensorDataset(all_tensor_data, all_tensor_data_label)
train_iter = Data.DataLoader(
dataset=torch_dataset_train, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
valiada_iter = Data.DataLoader(
dataset=torch_dataset_valida, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True,
num_workers=0,
)
test_iter = Data.DataLoader(
dataset=torch_dataset_test, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
all_iter = Data.DataLoader(
dataset=torch_dataset_all, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=False,
num_workers=0,
)
return train_iter, valiada_iter, test_iter, all_iter #, y_test
================================================
FILE: SSRN/record.py
================================================
import numpy as np
import torch
from operator import truediv
def evaluate_accuracy(data_iter, net, loss, device):
acc_sum, n = 0.0, 0
with torch.no_grad():
for X, y in data_iter:
test_l_sum, test_num = 0, 0
#X = X.permute(0, 3, 1, 2)
X = X.to(device)
y = y.to(device)
net.eval()
y_hat = net(X)
l = loss(y_hat, y.long())
acc_sum += (y_hat.argmax(dim=1) == y.to(device)).float().sum().cpu().item()
test_l_sum += l
test_num += 1
net.train()
n += y.shape[0]
return [acc_sum / n, test_l_sum] # / test_num]
def aa_and_each_accuracy(confusion_matrix):
list_diag = np.diag(confusion_matrix)
list_raw_sum = np.sum(confusion_matrix, axis=1)
each_acc = np.nan_to_num(truediv(list_diag, list_raw_sum))
average_acc = np.mean(each_acc)
return each_acc, average_acc
def record_output(oa_ae, aa_ae, kappa_ae, element_acc_ae, training_time_ae, testing_time_ae, path):
f = open(path, 'a')
sentence0 = 'OAs for each iteration are:' + str(oa_ae) + '\n'
f.write(sentence0)
sentence1 = 'AAs for each iteration are:' + str(aa_ae) + '\n'
f.write(sentence1)
sentence2 = 'KAPPAs for each iteration are:' + str(kappa_ae) + '\n' + '\n'
f.write(sentence2)
sentence3 = 'mean_OA ± std_OA is: ' + str(np.mean(oa_ae)) + ' ± ' + str(np.std(oa_ae)) + '\n'
f.write(sentence3)
sentence4 = 'mean_AA ± std_AA is: ' + str(np.mean(aa_ae)) + ' ± ' + str(np.std(aa_ae)) + '\n'
f.write(sentence4)
sentence5 = 'mean_KAPPA ± std_KAPPA is: ' + str(np.mean(kappa_ae)) + ' ± ' + str(np.std(kappa_ae)) + '\n' + '\n'
f.write(sentence5)
sentence6 = 'Total average Training time is: ' + str(np.sum(training_time_ae)) + '\n'
f.write(sentence6)
sentence7 = 'Total average Testing time is: ' + str(np.sum(testing_time_ae)) + '\n' + '\n'
f.write(sentence7)
element_mean = np.mean(element_acc_ae, axis=0)
element_std = np.std(element_acc_ae, axis=0)
sentence8 = "Mean of all elements in confusion matrix: " + str(element_mean) + '\n'
f.write(sentence8)
sentence9 = "Standard deviation of all elements in confusion matrix: " + str(element_std) + '\n'
f.write(sentence9)
f.close()
================================================
FILE: convert_report_to_csv.py
================================================
import pandas as pd
import re
from glob import glob
import argparse
import os
def get_data(report):
class_wise_acc_regex = r'\[[\d.\se\-\+(\\n)]*\]'
oa_aa_kappa_regex = r'([-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?\s±\s[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?)'
result = {}
x = re.findall(oa_aa_kappa_regex, report)
result['oa'] = x[0][0]
result['aa'] = x[1][0]
result['kappa'] = x[2][0]
result['oa'] = "{:.2f}".format(
float(result['oa'].split(' ± ')[0]) * 100) + ' ± ' + "{:.3f}".format(
float(result['oa'].split(' ± ')[1]))
result['aa'] = "{:.2f}".format(
float(result['aa'].split(' ± ')[0]) * 100) + ' ± ' + "{:.3f}".format(
float(result['aa'].split(' ± ')[1]))
result['kappa'] = "{:.4f}".format(float(
result['kappa'].split(' ± ')[0])) + ' ± ' + "{:.3f}".format(
float(result['kappa'].split(' ± ')[1]))
x = re.findall(class_wise_acc_regex, report)
result['class_mean'] = x[0][1:-1].split()
result['class_std'] = x[1][1:-1].split()
result['class_wise'] = [
"{:.2f}".format(float(m) * 100) + ' ± ' + "{:.3f}".format(float(n))
for m, n in zip(result['class_mean'], result['class_std'])
]
return result
def main(dataset, search_path, output_file):
all_reports = glob(search_path + '/*' + dataset + '*.txt')
no_of_labels = 0
dataframe_dict = {}
for report in all_reports:
print('Processing...', report)
column_name = os.path.basename(report)[:-4]
with open(report) as f:
report_content = f.read()
result = get_data(report_content)
dataframe_dict[column_name] = result['class_wise'] + [result['oa']] + [
result['aa']
] + [result['kappa']]
no_of_labels = len(result['class_wise'])
label_list = [str(i)
for i in range(1, no_of_labels + 1)] + ['oa', 'aa', 'kappa']
df = pd.DataFrame(dataframe_dict)
df = df.reindex(sorted(df.columns), axis=1)
df.insert(0, 'label', label_list)
print('Saving...', dataset, 'report.')
if output_file is not None:
df.to_csv(output_file, index=False)
else:
if not os.path.exists('csv_reports'):
os.makedirs('csv_reports')
df.to_csv(
os.path.join('csv_reports', dataset + '_report.csv'), index=False)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description='Convert Code Generated Report to CSV.')
parser.add_argument(
'-d',
'--dataset_name',
dest='dataset',
required=True,
help="Name of dataset to search.")
parser.add_argument(
'-r',
'--root_dir',
dest='root_dir',
default='./*/report',
help="Directories to search for the report files.")
parser.add_argument(
'-o',
'--output',
dest='output',
default=None,
help="Output name to save csv.")
args = parser.parse_args()
main(args.dataset, args.root_dir, args.output)
================================================
FILE: environment.yml
================================================
name: ms2kiresnet_env
channels:
- pytorch
- anaconda
- conda-forge
- defaults
dependencies:
- cudatoolkit=10.1.243
- matplotlib=3.1.3
- numpy=1.18.1
- opencv=3.4.2
- pip=20.0.2
- python=3.7.7
- pytorch=1.4.0
- scikit-learn=0.22.1
- scipy=1.4.1
- pip:
- pytorch-ranger==0.1.1
- spectral==0.20
- torch-optimizer==0.0.1a12
- torch-summary==1.2.0
================================================
FILE: setup_script.sh
================================================
DATASET_PATH="./dataset"
CLASS_MAP_PATH="classification_maps"
MODEL_PATH="models"
REPORT_PATH="report"
declare -a NETWORKS=("PyResNet"
"SSRN"
"A2S2KResNet"
"ResNet"
"ContextualNet")
declare -a DATAFILES=("Indian_pines_corrected.mat"
"Indian_pines_gt.mat"
"PaviaU.mat"
"PaviaU_gt.mat"
"KSC.mat"
"KSC_gt.mat"
"Salinas_corrected.mat"
"Salinas_gt.mat")
declare -a DATAURL=("http://www.ehu.eus/ccwintco/uploads/6/67/Indian_pines_corrected.mat"
"http://www.ehu.eus/ccwintco/uploads/c/c4/Indian_pines_gt.mat"
"http://www.ehu.eus/ccwintco/uploads/e/ee/PaviaU.mat"
"http://www.ehu.eus/ccwintco/uploads/5/50/PaviaU_gt.mat"
"http://www.ehu.es/ccwintco/uploads/2/26/KSC.mat"
"http://www.ehu.es/ccwintco/uploads/a/a6/KSC_gt.mat"
"https://github.com/gokriznastic/HybridSN/raw/master/data/Salinas_corrected.mat"
"https://github.com/gokriznastic/HybridSN/raw/master/data/Salinas_gt.mat")
if [ ! -d "$DATASET_PATH" ]; then
# Take action if $DIR exists. #
echo "Creaating ${DATASET_PATH}..."
mkdir "$DATASET_PATH"
fi
length=${#DATAFILES[@]}
for (( i = 0; i < length; i++ ));
do
if [ -f "$DATASET_PATH/${DATAFILES[i]}" ]; then
### Take action if $DIR exists ###
echo "${DATAFILES[i]} File exists..."
else
### Control will jump here if $DIR does NOT exists ###
echo "${DATAFILES[i]} file doesn't exists..."
wget "${DATAURL[i]}" -P "$DATASET_PATH"
fi
done
for net in "${NETWORKS[@]}"
do
if [ ! -d "$net/$CLASS_MAP_PATH" ]; then
# Take action if $DIR exists. #
echo "Creaating $net/$CLASS_MAP_PATH..."
mkdir "$net/$CLASS_MAP_PATH"
fi
if [ ! -d "$net/$MODEL_PATH" ]; then
# Take action if $DIR exists. #
echo "Creaating $net/$MODEL_PATH..."
mkdir "$net/$MODEL_PATH"
fi
if [ ! -d "$net/$REPORT_PATH" ]; then
# Take action if $DIR exists. #
echo "Creaating $net/$REPORT_PATH..."
mkdir "$net/$REPORT_PATH"
fi
done