Можно ли использовать набор гиперспектральных пикселей 1×1 в модели CNN, предназначенной для более обычных наборов данных (CIFAR-10 / MNIST)?

#tensorflow #machine-learning #keras #deep-learning #conv-neural-network

#tensorflow #машинное обучение #keras #глубокое обучение #conv-нейронная сеть

Вопрос:

Я создал рабочую модель CNN в Keras / Tensorflow и успешно использовал наборы данных CIFAR-10 и MNIST для тестирования этой модели. Функциональный код, как показано ниже:

 import keras
from keras.datasets import cifar10
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout, Conv2D, Flatten, MaxPooling2D
from keras.layers.normalization import BatchNormalization

(X_train, y_train), (X_test, y_test) = cifar10.load_data()

#reshape data to fit model
X_train = X_train.reshape(50000,32,32,3)
X_test = X_test.reshape(10000,32,32,3)

y_train = to_categorical(y_train)
y_test = to_categorical(y_test)


# Building the model 

#1st Convolutional Layer
model.add(Conv2D(filters=64, input_shape=(32,32,3), kernel_size=(11,11), strides=(4,4), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'))

#2nd Convolutional Layer
model.add(Conv2D(filters=224, kernel_size=(5, 5), strides=(1,1), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'))

#3rd Convolutional Layer
model.add(Conv2D(filters=288, kernel_size=(3,3), strides=(1,1), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))

#4th Convolutional Layer
model.add(Conv2D(filters=288, kernel_size=(3,3), strides=(1,1), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))

#5th Convolutional Layer
model.add(Conv2D(filters=160, kernel_size=(3,3), strides=(1,1), padding='same'))
model.add(BatchNormalization())
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2,2), strides=(2,2), padding='same'))

model.add(Flatten())

# 1st Fully Connected Layer
model.add(Dense(4096, input_shape=(32,32,3,)))
model.add(BatchNormalization())
model.add(Activation('relu'))
# Add Dropout to prevent overfitting
model.add(Dropout(0.4))

#2nd Fully Connected Layer
model.add(Dense(4096))
model.add(BatchNormalization())
model.add(Activation('relu'))
#Add Dropout
model.add(Dropout(0.4))

#3rd Fully Connected Layer
model.add(Dense(1000))
model.add(BatchNormalization())
model.add(Activation('relu'))
#Add Dropout
model.add(Dropout(0.4))

#Output Layer
model.add(Dense(10))
model.add(BatchNormalization())
model.add(Activation('softmax'))


#compile model using accuracy to measure model performance
opt = keras.optimizers.Adam(learning_rate = 0.0001)
model.compile(optimizer=opt, loss='categorical_crossentropy', 
              metrics=['accuracy'])


#train the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=30)
 

С этого момента, после использования вышеупомянутых наборов данных, я хотел пойти дальше и использовать набор данных с большим количеством каналов, чем представлено в оттенках серого или rgb, отсюда и включение гиперспектрального набора данных. При поиске гиперспектрального набора данных я наткнулся на этот.

Проблема на этом этапе заключалась в том, что этот гиперспектральный набор данных представлял собой одно изображение, причем каждое значение в основной правде относится к каждому пикселю. На этом этапе я переформатировал данные из этого в коллекцию гиперспектральных данных / пикселей.

Исправленное переформатирование кода для набора данных x_train и x_test:

 import keras
import scipy
import numpy as np
import matplotlib.pyplot as plt
from keras.utils import to_categorical
from scipy import io

mydict = scipy.io.loadmat('Indian_pines_corrected.mat')
dataset = np.array(mydict.get('indian_pines_corrected'))


#This is creating the split between x_train and x_test from the original dataset 
# x_train after this code runs will have a shape of (121, 145, 200) 
# x_test after this code runs will have a shape of (24, 145, 200)
x_train = np.zeros((121,145,200), dtype=np.int)
x_test = np.zeros((24,145,200), dtype=np.int)    

xtemp = np.array_split(dataset, [121])
x_train = np.array(xtemp[0])
x_test = np.array(xtemp[1])

# x_train will have a shape of (17545, 200) 
# x_test will have a shape of (3480, 200)
x_train = x_train.reshape(-1, x_train.shape[-1])
x_test = x_test.reshape(-1, x_test.shape[-1])
 

Код переформатирования набора данных истинности для Y_train и Y_test:

 truthDataset = scipy.io.loadmat('Indian_pines_gt.mat')
gTruth = truthDataset.get('indian_pines_gt')

#This is creating the split between Y_train and Y_test from the original dataset 
# Y_train after this code runs will have a shape of (121, 145) 
# Y_test after this code runs will have a shape of (24, 145)

Y_train = np.zeros((121,145), dtype=np.int)
Y_test = np.zeros((24,145), dtype=np.int)    

ytemp = np.array_split(gTruth, [121])
Y_train = np.array(ytemp[0])
Y_test = np.array(ytemp[1])

# Y_train will have a shape of (17545) 
# Y_test will have a shape of (3480)
Y_train = Y_train.reshape(-1)
Y_test = Y_test.reshape(-1)


#17 binary categories ranging from 0-16

#Y_train one-hot encode target column
Y_train = to_categorical(Y_train)

#Y_test one-hot encode target column
Y_test = to_categorical(Y_test, num_classes = 17)
 

Мой мыслительный процесс заключался в том, что, несмотря на то, что исходное изображение разбито на участки 1×1, большое количество каналов, которыми обладает каждый участок с их соответствующими значениями, поможет в категоризации набора данных.

По сути, я хотел бы ввести эти переформатированные данные в свою модель (см. в первом фрагменте кода в этом сообщении), однако я не уверен, что я использую неправильный подход к этому из-за моей неопытности в этой области знаний. Я ожидал ввести форму (1,1,200), т.е. Форма x_train amp; x_test будет (17545,1,1,200) amp; (3480,1,1,200) соответственно.

Ответ №1:

Во-первых, предположим, что используемое вами гиперспектральное изображение предназначено для решения проблемы семантической сегментации, а не для классификации.


Если мы посмотрим, что такое сверточный слой в нейронной сети, вряд ли он будет работать слишком хорошо. Это может сработать, но, вероятно, есть лучшие подходы.

Давайте посмотрим на эту 2D-анимацию свертки (автор Майкл Плотке, лицензированный CC-BY-SA 3.0) : 2D-анимация свертки

Мы можем видеть, что по своей сути операция свертки 2D похожа на применение фильтра определенного размера к области изображения, а затем повторение этой операции для всей области изображения. 2D-свертка часто используется в нейронных сетях при попытке изучить / найти пространственные объекты: т.е. Взаимосвязь между соседними пикселями.

Выдержка из CS231n — Сверточные сети

Когда мы перемещаем фильтр по ширине и высоте входного объема, мы создадим 2-мерную карту активации, которая дает ответы этого фильтра в каждой пространственной позиции. Интуитивно сеть будет изучать фильтры, которые активируются, когда они видят некоторый тип визуального объекта, такого как край определенной ориентации или пятно определенного цвета на первом слое, или, в конечном итоге, целые соты или колесообразные узоры на более высоких уровнях сети.

Используя небольшие участки размером 1×1, вы по существу лишили данные их пространственных размеров. Применение 2D-свертки в этом случае не имеет особого смысла. (Особенно учитывая размер фильтров, используемых в этой архитектуре, например, 11×11 на первом уровне).


Предлагаемые подходы:

  • Поиск большего набора данных с несколькими изображениями, предназначенными для классификации: вероятно, это правильный путь. В задачах, управляемых данными, наиболее важной частью являются данные.
  • Если для вас важна классификация областей этого изображения, вы можете либо использовать более простую сетевую архитектуру и / или методы машинного обучения для ваших пикселей спектральных данных. Это может сработать, но вы все равно теряете пространственные отношения между соседними пикселями.

Комментарии:

1. Оцените подробный ответ, включающий идею семантической сегментации, а также теоретическую разбивку 2d-свертки, это помогло мне понять проблемы, о которых я не подозревал, с которыми столкнулся здесь. Из предложенных вами подходов я более склонен двигаться к первому — ранее я безуспешно пытался получить больший гиперспектральный набор данных, мне нужно будет вернуться к этому, поскольку я хочу сохранить предлагаемую архитектуру в своей модели.

Ответ №2:

Если гиперспектральный набор данных предоставляется вам в виде большого изображения со многими каналами, я полагаю, что классификация каждого пикселя должна зависеть от пикселей вокруг него (в противном случае я бы не форматировал данные как изображение, т. Е. Без структуры сетки). Учитывая это предположение, разбивать входное изображение на части 1×1 не очень хорошая идея, поскольку вы теряете структуру сетки.

Я также полагаю, что порядок каналов произвольный, что подразумевает, что свертка по каналам, вероятно, не имеет смысла (чего вы, однако, не планировали делать в любом случае).

Вместо того, чтобы переформатировать данные так, как вы это делали, вы можете создать модель, которая принимает изображение в качестве входных данных, а также выводит «изображение», содержащее классификации для каждого пикселя. Т.е. Если у вас есть 10 классов и вы принимаете (145, 145, 200) изображение в качестве входных данных, вашмодель будет выводить изображение (145, 145, 10). В этой архитектуре у вас не было бы полностью связанных слоев. Ваш выходной слой также будет сверточным слоем.

Это, однако, означает, что вы не сможете сохранить свою текущую архитектуру. Это связано с тем, что задачи для MNIST / CIFAR10 и вашего гиперспектрального набора данных не совпадают. Для MNIST / CIFAR10 вы хотите классифицировать изображение целиком, в то время как для другого набора данных вы хотите присвоить класс каждому пикселю (при этом, скорее всего, также используя пиксели вокруг каждого пикселя).


Некоторые дополнительные идеи:

  • Если вы хотите превратить задачу классификации пикселей в гиперспектральном наборе данных в задачу классификации для всего изображения, возможно, вы можете переформулировать эту задачу как «классификацию гиперспектрального изображения как класса его центра (или верхнего левого, или нижнего правого, или (21-го, 104-го), иликакой угодно) пиксель «. Чтобы получить данные из вашего одного гиперспектрального изображения, для каждого пикселя я бы сдвинул изображение так, чтобы целевой пиксель находился в нужном месте (например, в центре). Все пиксели, которые «выпадают» за границу, могут быть вставлены с другой стороны изображения.
  • Если вы хотите придерживаться задачи классификации пикселей, но вам нужно больше данных, возможно, разделите одно имеющееся у вас гиперспектральное изображение на множество меньших изображений (например, 10x10x200). Возможно, вы даже захотите использовать изображения разных размеров. Если в вашей модели есть только слои свертки и объединения, и вы следите за сохранением размеров изображения, это должно сработать.