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 ================================================ [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/attention-based-adaptive-spectral-spatial/hyperspectral-image-classification-on-kennedy)](https://paperswithcode.com/sota/hyperspectral-image-classification-on-kennedy?p=attention-based-adaptive-spectral-spatial) [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/attention-based-adaptive-spectral-spatial/hyperspectral-image-classification-on-pavia)](https://paperswithcode.com/sota/hyperspectral-image-classification-on-pavia?p=attention-based-adaptive-spectral-spatial) [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/attention-based-adaptive-spectral-spatial/hyperspectral-image-classification-on-indian)](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). [![Open A2S2K-ResNet in Colab](https://colab.research.google.com/assets/colab-badge.svg)](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