#python #image #keras
#python #изображение #keras
Вопрос:
Я обучил свой собственный простой одиночный класс CNN для image datagen с помощью rescale 1. / 255.
Точность при обучении и тестировании выглядит нормально.
Когда я прогнозирую некоторые новые изображения, используя тестовый datagen с масштабированием — результаты довольно хорошие.
Но когда я пытаюсь предсказать по отдельным изображениям — у меня возникают проблемы. Если я использую ручное масштабирование / 255 — результаты очень плохие. Если я пропущу этот шаг и перейду к исходному изображению модели — прогнозирование кажется прекрасным.
Что я делаю не так?
Для меня логично, что если модель обучена на масштабированных данных [0 .. 1] (как и должно быть для нейронных сетей), прогнозирование также должно быть на масштабированных данных. Нет?
Мои базы данных:
train_datagen = ImageDataGenerator(rescale = 1./255,
rotation_range=35,
shear_range = 0.15,
zoom_range = 0.15,
brightness_range=[0.5,1.1],
horizontal_flip = False,
preprocessing_function = apply_mask)
valid_datagen = ImageDataGenerator(rescale = 1./255)
training_set = train_datagen.flow_from_directory(path_train,
target_size = img_size,
batch_size = batch_size,
class_mode = "binary")
valid_set = valid_datagen.flow_from_directory(path_test,
target_size = img_size,
batch_size = batch_size,
class_mode = "binary")
Сеть:
initializer = glorot_normal()
classifier = Sequential()
classifier.add(Conv2D(32, (3, 3), input_shape = (img_size[0], img_size[1], 3), activation = 'relu', kernel_initializer=initializer, padding='same')) #, padding='same'
classifier.add(MaxPooling2D(pool_size = (2, 2)))
classifier.add(Conv2D(64, (3, 3), activation = 'relu', kernel_initializer=initializer))
classifier.add(MaxPooling2D(pool_size=(2, 2)))
classifier.add(Conv2D(64, (3, 3), activation = 'relu', kernel_initializer=initializer))
classifier.add(MaxPooling2D(pool_size=(2, 2)))
classifier.add(Dropout(0.4))
classifier.add(Flatten())
classifier.add(Dense(units = 128, activation = 'relu', kernel_initializer=identity()))
classifier.add(Dense(1))
classifier.add(Activation('sigmoid'))
Обучающая сеть:
history = classifier.fit_generator(training_set,
steps_per_epoch = 1 * int(np.ceil(nb_train_samples / batch_size)),
epochs = 150,
validation_data = valid_set,
validation_steps = 1 * int(np.ceil(nb_validation_samples / batch_size)),
callbacks=[earlyStopping, mcp_save, reduce_lr_loss])
Predct от datagen:
test_generator = ImageDataGenerator(rescale = 1./255)
test_data_generator = test_generator.flow_from_directory(path_test,
target_size = img_size,
batch_size = batch_size,
shuffle = False,
class_mode = "binary")
test_steps_per_epoch = np.math.ceil(test_data_generator.samples / test_data_generator.batch_size)
predictions = model.predict_generator(test_data_generator, steps=test_steps_per_epoch)
Прогнозирование с использованием ручного импорта изображения:
valid_images = [".jpg", ".jpeg", ".png"]
for filename in os.listdir(predict_path):
ext = os.path.splitext(filename)[1]
if ext.lower() not in valid_images:
continue
file = predict_path "/" filename
test_image = tensorflow.keras.preprocessing.image.load_img(file, target_size = img_size)
test_image = tensorflow.keras.preprocessing.image.img_to_array(test_image).astype(np.float32)
test_image /= 255.
test_image = np.expand_dims(test_image, axis = 0)
result = model.predict(test_image)
print(result)
if result[0][0] == 1:
prediction = 'True'
else:
prediction = 'Not True'
print(prediction)
img = Image.open(file)
display(img)
print("n")
print("n")
Более интересно — если я использую нормализацию изображения перед прогнозом — результаты, предсказанные сигмоидом, становятся плавающими [0 .. 1], если я просто комментирую test_image /= 255.
— результаты снова двоичные 0,1.
Ответ №1:
Вы проверяете, равен ли выходной слой ‘1’ или нет. Сигмоид обычно генерирует число от 0 до 1. Это может быть ‘1’ или может быть меньше 1. Вы должны использовать пороговое значение. И пороговое значение может быть 0,5 или любым другим значением, которое может быть определено путем оценки данных. Допустим, ваше пороговое значение равно 0,5. Если выходные данные вашей модели больше 0,5, то это 1, в противном случае считайте это 0.
В вашем коде вы сравниваете с 1. Итак, ваш порог равен 1.0. Вы едва ли получите выходное значение 1 из вашей модели с активацией сигмоида. Установите пороговое значение ниже. Сначала попробуйте с 0.5.
Все остальное кажется прекрасным. Если вы используете масштабированный ввод для обучения, то вам также следует масштабировать ввод перед прогнозированием.
Комментарии:
1. На самом деле нет, я обучаю модель как модель 2 класса — True и False, используя flow_from_directory, у меня есть
0
и1
папки для обучения и тестирования.2. @Oleksii Я думаю, вам следует подробнее изучить это. Нет такой логики, что вы получите тот же результат, что и ввод. Вывод нейрона полностью зависит от функции активации. И вы используете сигмоид. Он всегда выводит число в диапазоне от 0 до 1 в зависимости от входных данных. Не обязательно всегда округлять 1. Вы получите 1 только для крайних случаев. Для такого типа модели обычно мы используем пороговое значение. допустим, ваш порог равен 0,5. Если ваш результат равен 0,5, считайте его 1, в противном случае 0. Но вы установили его равным 1. Таким образом, максимум вашего вывода будет считаться равным 0.
3. Извините, я не заметил, что у вас есть только 1 нейрон в выходном слое. Я обновил свой ответ.
4. Спасибо, да. Но я не понимаю, почему проблема в том, чтобы иметь один выходной нейрон с сигмоидом? У меня есть 2 класса в обучении и тестировании, выходной нейрон с сигмовидной активацией может выводить значения от 0 до 1, где ближе к 0 — это первый класс (flase), ближе к 1 — секундному (true). Я добавил пороговое значение 0.5, заменив
if result[0][0] == 1:
наif np.round(result[0][0],0) == 1:
5. @Oleksii Это другой вопрос. Вы можете взглянуть здесь: quora.com/… Пожалуйста, примите мой ответ, если он действительно решает вашу проблему. Спасибо.
Ответ №2:
Последней активацией должен быть слой ‘ReLU’, а не сигмовидный, теперь сделайте то же самое.
Должно решить вашу проблему, на самом деле слой ‘sigmoid’ выдает дробный вывод между 0-1. Вы сравниваете результаты только с 1. Это проблема. Вместо этого используется ‘relu’
Комментарии:
1. Нет, я прогнозирую 2 класса — это
class 1
или все остальное (class 0
). Итак, сигмоид в порядке.2. Даже с relu все в порядке, проблема с вашим кодом в том, что ваш результат является вероятностью. ваша логика, если prob равно 1, класс равен 1 , неразумна. Вы не знаете, какова граничная вероятность классификации, и вы не должны этого знать, это будет машинное обучение. С другой стороны, используйте это напрямую, если вы хотите придерживаться сигмоида
prediction=model.predict_classes(test_image)
3. Хотя
model.predict_classes
это работает, лучшая практика в этом отношении — преобразовать это в проблему с двумя классами.