Потеря NAN, точность 0 – Есть идеи, почему? Полный код представлен.

Вопрос или проблема

Я работал над этим в последние несколько дней и не мог разобраться. Публиковал в различных группах, на StackOverflow и т. д., и получил предложения от многих пользователей. Я реализовал эти предложения в коде, представленном ниже, но проблема осталась прежней. Извините за длинный пост, но я хочу быть как можно более ясным. Все соответствующие фрагменты кода показаны ниже:

Настройка путей к изображениям:

imagepaths = []

for root, dirs, files in os.walk(".", topdown=False): 
  for name in files:
    path = os.path.join(root, name)
    if path.endswith("jpg"): # Нам нужны только изображения
      imagepaths.append(path)

Загрузка в массивы, предобработка:

X = [] # Данные изображений
y = [] # Метки

datagen = ImageDataGenerator(rescale=1./255, samplewise_center=True)

# Циклы по imagepaths для загрузки изображений и меток в массивы
for path in imagepaths:
  img = cv2.imread(path) # Читает изображение и возвращает np.array
  img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Преобразует в правильное цветовое пространство (GRAY) # найти rgb 
  img = cv2.resize(img, (75, 75)) # Уменьшаем размер изображения для ускорения обучения
  img = image.img_to_array(img)
  img = datagen.standardize(img)
  X.append(img)

  # Обработка метки в пути к изображению
  category = path.split("\\")[1]
  #print(category)
  split = (category.split("_"))     
  if int(split[0]) == 0:
    label = int(split[1])
  else:
    label = int(split[0])
  y.append(label)

# Превращаем X и y в np.array для ускорения train_test_split

X = np.array(X, dtype="float32") #ORIGINAL uint8
X = X.reshape(len(imagepaths), 75, 75, 1) 
y = np.array(y)
tf.keras.utils.to_categorical(X, num_classes=None, dtype="float32")
tf.keras.utils.to_categorical(y, num_classes=None, dtype="float32")

Создание тестового набора:

ts = 0.3 
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=ts, random_state=42)

Создание модели. Да, я знаю, что она очень маленькая, всего 1 слой, но мне посоветовали начать с основы и постепенно наращивать. Сначала было 5 слоев, но результаты все равно те же.

model = Sequential()

model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(75, 75, 1))) 
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization())
model.add(Dropout(0.5))

model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(26, activation='softmax'))

Компиляция модели. И обучение. Мне сказали, что градиент может взрываться, поэтому посоветовали добавить первую строку с clipnorm..

adam = keras.optimizers.Adam(clipnorm=1.)

model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=['accuracy'])  

model.fit(X_train, y_train, epochs=5, batch_size=32, verbose=1, validation_data=(X_test, y_test)) 

Финальное обучение можно увидеть здесь. Проблемы в том, что потери – NAN, а точность – 0.

Train on 54600 samples, validate on 23400 samples
Epoch 1/5
54600/54600 [==============================] - 14s 265us/step - loss: nan - accuracy: 0.0000e+00 - val_loss: nan - val_accuracy: 0.0000e+00
Epoch 2/5
54600/54600 [==============================] - 15s 269us/step - loss: nan - accuracy: 0.0000e+00 - val_loss: nan - val_accuracy: 0.0000e+00
Epoch 3/5
54600/54600 [==============================] - 15s 273us/step - loss: nan - accuracy: 0.0000e+00 - val_loss: nan - val_accuracy: 0.0000e+00
Epoch 4/5
54600/54600 [==============================] - 15s 267us/step - loss: nan - accuracy: 0.0000e+00 - val_loss: nan - val_accuracy: 0.0000e+00
Epoch 5/5
54600/54600 [==============================] - 14s 263us/step - loss: nan - accuracy: 0.0000e+00 - val_loss: nan - val_accuracy: 0.0000e+00

Вот список вещей, которые я сделал неправильно и мне посоветовали сделать другие:

Я изначально не стандартизировал данные – Поэтому я использовал ImageDataGenerator, масштабировал и стандартизировал его.

Мне посоветовали превратить данные в категориальные, что я и сделал с помощью функции to_categorical (думаю, что сделал это правильно), но не уверен, требуется ли что-то еще.

Уменьшить сложность модели. Я это сделал, уменьшив до одного слоя для отладки.

Возможный взрывающийся градиент – поэтому я изменил оптимизатор adam с clipnorm = 1

ФОН: Эта модель обучается и распознает 26 букв алфавита. Я знаю, что набор данных в порядке, потому что, когда я использую его для обучения модели для 10 букв за раз (например, A-J), все работает хорошо. Проблема возникает только, когда я перехожу от 10 до 26. Да, я пытался изменить плотный слой на 26 в оригинальном коде, но это не сработало.

Я смотрел на это и пробовал все последние два дня…

КАКАЯ-ЛИБО ПОМОЩЬ БУДЕТ ОЧЕНЬ ЦЕННА

Я думаю, что здесь может быть несколько ошибок:

  1. Нет необходимости преобразовывать X в категориальные, только ваши метки.
    Таким образом, эта строка не нужна: tf.keras.utils.to_categorical(X, num_classes=None, dtype="float32")
  2. to_categorical не работает на месте, поэтому вам нужно будет переназначить его на y. Измените на: y = tf.keras.utils.to_categorical(y)
  3. После того, как вы преобразуете их в категориальные, ваша функция потерь должна быть 'categorical_crossentropy'

Прежде всего, я бы посоветовал вам использовать datagen.flow_from_directory для загрузки набора данных. Также ваша модель стала слишком простой, попробуйте добавить как минимум 1-2 дополнительных свёрточных слоя.

Для вашей проблемы это может быть случай взрывающегося градиента. Попробуйте уменьшить скорость обучения и добавить нормализацию партии между слоями. Возможно, попробуйте SGD с моментумом или оптимизатор RMSprop, это иногда помогает.

Ответ или решение

Проблема, которую вы описываете—появление NaN в потере и 0% точности во время обучения вашей модели—может быть вызвана несколькими факторами, связанными с подготовкой данных, архитектурой модели и параметрами обучения. Рассмотрим подробнее потенциальные источники ошибок и пути их решения.

1. Ошибки в предварительной обработке данных

a. Преобразование меток

Вы верно заметили, что переход к категориальным меткам необходимо осуществлять только для целевых переменных (y), а не для входных данных (X). Вместо:

tf.keras.utils.to_categorical(X, num_classes=None, dtype="float32")

Следует использовать:

y = tf.keras.utils.to_categorical(y)

После этой операции убедитесь, что ваша функция потерь изменена на 'categorical_crossentropy', поскольку вы работаете с категориальными метками.

b. Нормализация данных

Вы уже добавили стандартное преобразование изображений с помощью ImageDataGenerator. Однако важно убедиться, что данные действительно нормализованы. Проверьте, правильно ли вы обрабатываете изображение, перед тем как передать его в модель. Иногда использование cv2.imread() может давать неожиданные результаты, например, если изображение не существует или имеет некорректный формат.

2. Обработка модели

a. Архитектура модели

Ваше уменьшение сложности модели до одного слоя может привести к недостаточному обучению, особенно с 26 классами. Вернитесь к более сложной архитектуре, например:

model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(75, 75, 1)))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization())
# Добавьте еще один сверточный слой
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dense(26, activation='softmax'))

Это должно помочь модели извлекать более сложные функции из данных.

3. Параметры оптимизации

a. Выбор оптимизатора и его параметры

Рассмотрите возможность использования более низкой скорости обучения. Попробуйте следующие параметры для оптимизатора Adam:

adam = keras.optimizers.Adam(learning_rate=0.0001, clipnorm=1.)

Также можно попробовать другие оптимизаторы, такие как SGD с моментумом или RMSprop, которые могут лучше справляться с нестабильными градиентами.

4. Проблемы с данными

a. Проверьте целостность данных

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

b. Использование flow_from_directory

Если ваша текущая структура данных позволяет, рассмотрите использование метода flow_from_directory из ImageDataGenerator, который может автоматически обрабатывать изображения и метки:

datagen = ImageDataGenerator(rescale=1./255)
train_generator = datagen.flow_from_directory(
    'data/train',
    target_size=(75, 75),
    color_mode='grayscale',
    class_mode='categorical',
    batch_size=32)

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...