2D CNN для классификации данных 3D МРТ в оттенках серого, возможная проблема с маркировкой данных
Я пытаюсь запустить двоичную классификацию черно-белых данных 3D МРТ. Я использую 2D-свертки из-за отсутствия каналов, присущих черно-белым данным. Я добавил измерение, чтобы выровнять размерность, и, по сути, глубина этих данных действует как измерение партии. Я использую подвыборку данных, 20 файлов каждый 189 на 233 на 197. Просто в качестве краткой справки.
У меня есть файл csv с кучей информации, включая данные меток для каждого файла, который я пытался извлечь, в соответствии с приведенным ниже кодом.
import numpy as np
import glob
import os
import tensorflow as tf
import pandas as pd
import glob
import SimpleITK as sitk
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from keras.preprocessing.image import ImageDataGenerator
from keras.utils import plot_model
from keras.utils import to_categorical
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from google.colab import drive
drive.mount('/content/gdrive')
datapath = ('/content/gdrive/My Drive/DirectoryTest/All Data/')
patients = os.listdir(datapath)
labels_df = pd.read_csv('/content/Data_Index.csv', index_col = 0 )
labelset = []
for i in patients:
label = labels_df.loc[i, 'Group']
if label is 'AD':
np.char.replace(label, ['AD'], [0])
if label is 'CN':
np.char.replace(label, ['CN'], [1])
labelset.append(label)
label_encoder = LabelEncoder()
labelset = label_encoder.fit_transform(labelset)
labelset = np_utils.to_categorical(labelset, num_classes= 2)
FullDataSet = []
for i in patients:
a = sitk.ReadImage(datapath + i)
b = sitk.GetArrayFromImage(a)
c = np.reshape(b, (189,233,197, 1))
FullDataSet.append(c)
training_data, testing_data, training_labels, testing_labels = train_test_split(FullDataSet, labelset, train_size=0.70,test_size=0.30)
dataset_train = tf.data.Dataset.from_tensor_slices((training_data, training_labels))
dataset_test = tf.data.Dataset.from_tensor_slices((testing_data, testing_labels))
CNN_model = tf.keras.Sequential(
[
#tf.keras.layers.Input(shape=(189, 233, 197, 1), batch_size=2),
#tf.keras.layers.Reshape((197, 233, 189, 1)),
tf.keras.layers.Conv2D(kernel_size=(7, 7), data_format='channels_last', filters=64, activation='relu',
padding='same', strides=( 3, 3), input_shape=( 233, 197, 1)),
#tf.keras.layers.BatchNormalization(center=True, scale=False),
tf.keras.layers.MaxPool2D(pool_size=(3, 3), padding='same'),
tf.keras.layers.Dropout(0.20),
tf.keras.layers.Conv2D(kernel_size=( 7, 7), filters=128, activation='relu', padding='same', strides=( 3, 3)),
#tf.keras.layers.BatchNormalization(center=True, scale=False),
tf.keras.layers.MaxPool2D(pool_size=(3, 3), padding='same'),
tf.keras.layers.Dropout(0.20),
tf.keras.layers.Conv2D(kernel_size=( 7, 7), filters=256, activation='relu', padding='same', strides=( 3, 3)),
#tf.keras.layers.BatchNormalization(center=True, scale=False),
tf.keras.layers.MaxPool2D(pool_size=(3, 3), padding = 'same'),
tf.keras.layers.Dropout(0.20),
# last activation could be either sigmoid or softmax, need to look into this more. Sig for binary output, Soft for multi output
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(256, activation='relu'),
tf.keras.layers.Dense(64, activation='relu'),
tf.keras.layers.Dropout(0.20),
tf.keras.layers.Dense(2, activation='softmax')
])
# Compile the model
CNN_model.compile(optimizer=tf.keras.optimizers.Adam(lr=0.00001), loss='binary_crossentropy', metrics=['accuracy'])
# print model layers
CNN_model.summary()
CNN_history = CNN_model.fit(dataset_train, epochs=10, validation_data=dataset_test)
Когда я подхожу к модели, я получаю следующую ошибку:
Epoch 1/10
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-35-a8b210ec2e72> in <module>()
1 #running of the model
2 #CNN_history = CNN_model.fit(dataset_train, epochs=100, validation_data =dataset_test, validation_steps=1)
----> 3 CNN_history = CNN_model.fit(dataset_train, epochs=10, validation_data=dataset_test)
4
5
10 frames
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/func_graph.py in wrapper(*args, **kwargs)
971 except Exception as e: # pylint:disable=broad-except
972 if hasattr(e, "ag_error_metadata"):
--> 973 raise e.ag_error_metadata.to_exception(e)
974 else:
975 raise
ValueError: in user code:
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py:806 train_function *
return step_function(self, iterator)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py:796 step_function **
outputs = model.distribute_strategy.run(run_step, args=(data,))
/usr/local/lib/python3.6/dist-packages/tensorflow/python/distribute/distribute_lib.py:1211 run
return self._extended.call_for_each_replica(fn, args=args, kwargs=kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/distribute/distribute_lib.py:2585 call_for_each_replica
return self._call_for_each_replica(fn, args, kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/distribute/distribute_lib.py:2945 _call_for_each_replica
return fn(*args, **kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py:789 run_step **
outputs = model.train_step(data)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training.py:749 train_step
y, y_pred, sample_weight, regularization_losses=self.losses)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/compile_utils.py:204 __call__
loss_value = loss_obj(y_t, y_p, sample_weight=sw)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/losses.py:149 __call__
losses = ag_call(y_true, y_pred)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/losses.py:253 call **
return ag_fn(y_true, y_pred, **self._fn_kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/util/dispatch.py:201 wrapper
return target(*args, **kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/losses.py:1605 binary_crossentropy
K.binary_crossentropy(y_true, y_pred, from_logits=from_logits), axis=-1)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/util/dispatch.py:201 wrapper
return target(*args, **kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/backend.py:4829 binary_crossentropy
bce = target * math_ops.log(output + epsilon())
/usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:1141 binary_op_wrapper
raise e
/usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:1125 binary_op_wrapper
return func(x, y, name=name)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:1457 _mul_dispatch
return multiply(x, y, name=name)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/util/dispatch.py:201 wrapper
return target(*args, **kwargs)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/math_ops.py:509 multiply
return gen_math_ops.mul(x, y, name)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/ops/gen_math_ops.py:6176 mul
"Mul", x=x, y=y, name=name)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/op_def_library.py:744 _apply_op_helper
attrs=attr_protos, op_def=op_def)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/func_graph.py:593 _create_op_internal
compute_device)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/ops.py:3485 _create_op_internal
op_def=op_def)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/ops.py:1975 __init__
control_input_ops, op_def)
/usr/local/lib/python3.6/dist-packages/tensorflow/python/framework/ops.py:1815 _create_c_op
raise ValueError(str(e))
ValueError: Dimensions must be equal, but are 2 and 189 for '{{node binary_crossentropy/mul}} = Mul[T=DT_FLOAT](ExpandDims, binary_crossentropy/Log)' with input shapes: [2,1], [189,2].
Я знаю, что цифра 2 в [189,2] связана с последним слоем softmax, но я не знаю, что делать с этой информацией и куда идти дальше. Любая помощь будет оценена, спасибо!
1 ответ
Вот несколько комментариев относительно вашего кода, надеюсь, они будут полезны.
Использовать Conv3D
а также MaxPool3D
Если вы имеете дело с 3D-изображениями, вам почти наверняка следует использовать Conv3D
вместо того Conv2D
, а также MaxPool3D
вместо того MaxPool2D
. Вот пример (с использованием случайных данных), который я только что протестировал, и, похоже, он работает нормально:
import numpy as np
import tensorflow as tf
from tensorflow import keras
train_size = 20
val_size = 5
X_train = np.random.random([train_size, 189, 233, 197]).astype(np.float32)
X_valid = np.random.random([val_size, 189, 233, 197]).astype(np.float32)
y_train = np.random.randint(2, size=train_size).astype(np.float32)
y_valid = np.random.randint(2, size=val_size).astype(np.float32)
CNN_model = keras.Sequential([
keras.layers.Reshape([189, 233, 197, 1], input_shape=[189, 233, 197]),
keras.layers.Conv3D(kernel_size=(7, 7, 7), filters=32, activation='relu',
padding='same', strides=(3, 3, 3)),
#keras.layers.BatchNormalization(),
keras.layers.MaxPool3D(pool_size=(3, 3, 3), padding='same'),
keras.layers.Dropout(0.20),
keras.layers.Conv3D(kernel_size=(5, 5, 5), filters=64, activation='relu',
padding='same', strides=(3, 3, 3)),
#keras.layers.BatchNormalization(),
keras.layers.MaxPool3D(pool_size=(2, 2, 2), padding='same'),
keras.layers.Dropout(0.20),
keras.layers.Conv3D(kernel_size=(3, 3, 3), filters=128, activation='relu',
padding='same', strides=(1, 1, 1)),
#keras.layers.BatchNormalization(),
keras.layers.MaxPool3D(pool_size=(2, 2, 2), padding='same'),
keras.layers.Dropout(0.20),
keras.layers.Flatten(),
keras.layers.Dense(256, activation='relu'),
keras.layers.Dense(64, activation='relu'),
keras.layers.Dropout(0.20),
keras.layers.Dense(1, activation='sigmoid')
])
# Compile the model
CNN_model.compile(optimizer=keras.optimizers.Adam(lr=0.00001),
loss='binary_crossentropy',
metrics=['accuracy'])
# print model layers
CNN_model.summary()
CNN_history = CNN_model.fit(X_train, y_train, epochs=10,
validation_data=[X_valid, y_valid])
Не изменяйте форму для изменения размеров
Что касается этих двух закомментированных строк:
#tf.keras.layers.Input(shape=(189, 233, 197, 1), batch_size=2),
#tf.keras.layers.Reshape((197, 233, 189, 1)),
Изменение формы изображения 189x233x197x1 на 197x233x189x1 не будет работать должным образом. Он полностью перетасует пиксели, что значительно усложнит задачу. Это похоже на преобразование изображения 2x3 в изображение 3x2:
>>> img = np.array([[1,2,3],[4,5,6]])
>>> np.reshape(img, [3, 2])
array([[1, 2],
[3, 4],
[5, 6]])
Обратите внимание, что это не то же самое, что вращение изображения: пиксели полностью перемешаны.
Вы хотите использовать tf.keras.layers.Permute()
вместо этого вот так:
CNN_model = tf.keras.Sequential([
tf.keras.layers.Permute((3, 2, 1, 4), input_shape=(189, 233, 197, 1)),
...
])
Поскольку эти закомментированные строки были неправильными, я подозреваю, что следующая строка также может быть неправильной:
c = np.reshape(b, (189,233,197, 1))
Я не знаю формы b
, поэтому убедитесь, что он совместим с этим np.reshape()
операция. Например, если его форма[189, 233, 197]
, хорошо. Но если это[197, 233, 189]
, например, вам нужно будет переставить размеры перед изменением формы:
b_permuted = np.transpose(b, [2, 1, 0]) # permute dims
c = np.reshape(b_permuted, [189, 233, 197, 1]) # then add the channels dim
В np.transpose()
функция аналогична использованию Permute()
, за исключением того, что измерения индексируются 0 вместо 1.
Это может быть еще сложнее. Например, если 3D-изображения хранятся как большие 2D-изображения, содержащие более мелкие 2D-срезы, расположенные рядом, то формаb
может быть что-то вроде [189*197, 233]
. В этом случае вам нужно будет сделать что-то вроде этого:
b_reshaped = np.reshape(b, [189, 197, 233, 1])
c = np.transpose(b_reshaped, [0, 2, 1, 3])
Надеюсь, эти примеры достаточно ясны.
Использовать tf.keras
не keras
Существует несколько реализаций API Keras. Один из нихkeras
пакет, который представляет собой "мультибэкэнд" Keras (который устанавливается с помощью pip install keras
). Другойtf.keras
и он поставляется с TensorFlow. Кажется, ваша программа использует оба. Вы должны полностью избегать этого, это вызовет странные проблемы.
from keras.utils import plot_model # this is multibackend Keras
...
CNN_model = tf.keras.Sequential(...) # this is tf.keras
Я настоятельно рекомендую вам удалить multibackend keras, чтобы избежать ошибок такого типа: pip uninstall keras
. Затем исправьте импорт, добавив префиксtensorflow.
, например:
from tensorflow.keras.models import Sequential
from tensorflow.keras.utils import to_categorical # note: not from np_utils
...
Не использовать to_categorical()
для двоичной классификации
Для двоичной классификации метки должны быть просто одномерным массивом, содержащим нули и единицы, например np.array([1., 0., 0., 1., 1.])
. Код можно довольно упростить:
labelset = []
for i in patients:
label = labels_df.loc[i, 'Group']
if label == 'AD': # use `==` instead of `is` to compare strings
labelset.append(0.)
elif label == 'CN':
labelset.append(1.)
else:
raise "Oops, unknown label" # I recommend testing possible failure cases
labelset = np.array(labelset)
Важно отметить, что для двоичной классификации вы должны использовать один нейрон в выходном слое, а также использовать "sigmoid"
функция активации (не "softmax"
, который используется для мультиклассовой классификации):
CNN_model = tf.keras.Sequential([
...
tf.keras.layers.Dense(1, activation='sigmoid')
])
Незначительный комментарий
- Вам не нужно указывать оба
train_size
иtest_size
при звонкеtrain_test_split()
.
Удачи!