Вопрос или проблема
У меня есть проблема, с которой мне сложно справиться: я не понимаю концепцию, которая приводит к этим результатам. Я использую плотный слой keras для отображения 13 входных признаков на 3 выходные метки. Во время обучения значение потерь (RMSE) для обучающих данных постоянно уменьшается даже после 2000 эпох. Однако значение потерь для валидационных данных больше не снижается после примерно 200 эпох, как вы можете видеть на рисунке.
Теперь то, что я совершенно не понимаю, это как можно объяснить такой результат, учитывая, что обучающие и валидационные данные имеют (почти) одинаковое распределение для всех 13 входных признаков и 3 выходных меток. Они взяты из одного и того же распределения. Чтобы продемонстрировать это, я нарисовал гистограммы для некоторых входных признаков и выходных меток (и функции плотности ядра), как вы можете видеть здесь:
Если вы хотите увидеть гистограммы для всех входных признаков и выходных меток, вы можете найти их здесь (все распределения обучающей, валидационной и тестовой выборок выглядят довольно похоже): https://filetransfer.io/data-package/nHxgDfvF#link
Также я рассчитал корреляцию между каждым из входных признаков и выходными метками для обучающего, валидационного и тестового наборов данных, и значения почти одинаковы для всех комбинаций во всех наборах данных. Если хотите увидеть значения, их можно найти здесь: https://filetransfer.io/data-package/iVruYbLx#link. Это только подчеркивает, что обучающие и валидационные данные имеют одно и то же распределение.
Вот мой код:
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_percentage_error
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.layers import BatchNormalization, Dense, Flatten
from matplotlib import pyplot as plt
#Чтение данных из файлов csv
ANN_input_data_features = pd.read_csv("C:/Users/User1/Desktop/TestDataANN_InputFeatures.csv", sep=';')
ANN_input_data_labels = pd.read_csv("C:/Users/User1/Desktop/TestDataANN_OutputLabels.csv", sep=';')
ANN_input_data_features = ANN_input_data_features.values
ANN_input_data_labels = ANN_input_data_labels.values
# стандартизация входных признаков X и выходных меток Y
scaler_standardized_X = StandardScaler()
ANN_input_data_features = scaler_standardized_X.fit_transform(ANN_input_data_features)
scaler_standardized_Y = StandardScaler()
ANN_input_data_labels = scaler_standardized_Y.fit_transform(ANN_input_data_labels)
#Разделение набора данных на обучающий, валидационный и тестовый
index_X_Train_End = int(0.7 * len(ANN_input_data_features))
index_X_Validation_End = int(0.9 * len(ANN_input_data_features))
X_train = ANN_input_data_features [0: index_X_Train_End]
X_valid = ANN_input_data_features [index_X_Train_End: index_X_Validation_End]
X_test = ANN_input_data_features [index_X_Validation_End:]
Y_train = ANN_input_data_labels [0: index_X_Train_End]
Y_valid = ANN_input_data_labels [index_X_Train_End: index_X_Validation_End]
Y_test = ANN_input_data_labels [index_X_Validation_End:]
#Обучение модели
optimizer_adam = tf.keras.optimizers.Adam(learning_rate= 0.001)
numberOfInputFeatures = len(ANN_input_data_features[0])
numberOfOutputNeurons = len(ANN_input_data_labels[0])
model = keras.Sequential([
Flatten(input_shape=(numberOfInputFeatures,)),
Dense(30, activation='relu'),
#BatchNormalization(axis = 1),
Dense(50, activation='relu'),
#BatchNormalization(axis = 1),
Dense(50, activation='relu'),
#BatchNormalization(axis = 1),
Dense(30, activation='relu'),
keras.layers.Dense(numberOfOutputNeurons)])
entireFolderNameForTheResultsOfTheRun = "C:/Users/User1/Desktop/Training/"
pathOfTheFileForBestModel = entireFolderNameForTheResultsOfTheRun + "bestModelSingleTimeSlotTest.keras"
callbacks = [ keras.callbacks.ModelCheckpoint(pathOfTheFileForBestModel, save_best_only=True) ]
model.compile(loss="mean_squared_error", optimizer=optimizer_adam, metrics=['mean_absolute_percentage_error'])
history = model.fit(X_train, Y_train, epochs=2000, batch_size=10, validation_data=(X_valid, Y_valid), callbacks=callbacks)
# Прогнозирование значений из тестового набора данных
model = keras.models.load_model(pathOfTheFileForBestModel)
Y_pred = model.predict(X_test)
# Обратное преобразование результатов прогнозирования в тестовом наборе данных
Y_test_traInv = scaler_standardized_Y.inverse_transform(Y_test)
Y_pred_traInv = scaler_standardized_Y.inverse_transform(Y_pred)
# Рассчет ошибки в тестовом наборе данных
rms = mean_squared_error(Y_test_traInv, Y_pred_traInv, squared=True)
mape = mean_absolute_percentage_error(Y_test_traInv, Y_pred_traInv)
print("Оценка с помощью тестовых данных")
print("Корень среднего квадратного отклонения: ", rms)
print("Средняя абсолютная ошибка в процентах:", mape)
#Построение графиков результатов обучения
plt.plot(history.history['mean_absolute_percentage_error'])
plt.plot(history.history['val_mean_absolute_percentage_error'])
plt.title('Средняя абсолютная ошибка в процентах')
plt.ylabel('Средняя абсолютная ошибка в процентах')
plt.xlabel('эпоха')
plt.legend(['обучение', 'валидация'], loc="upper left")
plt.show()
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Функция потерь')
plt.ylabel('Средняя квадратная ошибка')
plt.xlabel('эпоха')
plt.legend(['обучение', 'валидация'], loc="upper left")
plt.show()
Вот данные, которые я использую (входные признаки и выходные метки): https://filetransfer.io/data-package/ldU9KENV#link
Я также пробовал использовать пакетную нормализацию, но это привело к более худшим общим результатам. Кроме того, я экспериментировал с количеством слоев, нейронов, размером пакета и т.д., но проблема остается прежней, или результаты становятся значительно хуже.
Теперь мой вопрос: как это можно объяснить. Насколько я понимаю, искусственные нейронные сети настраивают свои веса во время обучения так, чтобы они могли максимально точно объяснить обучающий набор данных. Но если обучающий и валидационный наборы данных имеют одно и то же распределение и ту же корреляцию между входами и выходами, как в моем случае, модель, способная объяснить обучающий набор данных, также должна уметь объяснить валидационный набор данных, что здесь не так.
Напоминание: Поскольку я все еще не получил удовлетворительного ответа, я хотел бы напомнить вам об этом вопросе. Я буду признателен за любой дальнейший ответ по этому вопросу.
Ваша модель переобучается на обучающих данных.
Пожалуйста, используйте Dropout или любую другую технику, чтобы противостоять переобучению.
Вот что я получил с Dropout за 250 эпох
Ссылка на код- Colab scratch NB
Ответ или решение
Когда речь заходит о проблемах с уменьшением валидационной потери в нейронных сетях, это часто связано с явлением, известным как переобучение модели. Давайте более подробно рассмотрим вашу проблему, учитывая все предоставленные данные.
Феномен Переобучения
Несмотря на то, что ваши обучающая и валидационная выборки имеют схожие распределения и корреляции, модель может всё равно переобучаться. Переобучение происходит, когда модель слишком хорошо запоминает детали обучающих данных, но при этом не может обобщать эту информацию на новые, невидимые данные (валидационные), даже если их распределение идентично.
-
Сложность Модели: Ваша модель состоит из нескольких уровней (Dense layers) с достаточно большим количеством нейронов, что позволяет ей изучать сложные паттерны, но также и приводит к риску переобучения. Данный класс задач может потребовать более простую архитектуру.
-
Отсутствие Регуляризации: Использование методов регуляризации, таких как Dropout, может значительно улучшить производительность на валидационных данных за счет уменьшения взаимозависимости между нейронами и, следовательно, переобучения.
-
Объем Обучающих Данных: Если у вас относительно небольшой объем данных, то модель может слишком сильно подстраиваться под него. Попробуйте увеличить объем обучающих данных или использовать аугментацию данных.
-
Уровень Шумности Данных: Даже если распределение похоже, существует вероятность, что валидационные данные содержат элементы, которые не были представлены в обучении. Это может быть как шум в данных, так и другие случайные колебания.
Рекомендации по Устранению Проблемы
-
Регуляризация: Попробуйте добавить слои Dropout между вашими Dense-слоями. Например, можно использовать Dropout с коэффициентом 0.2 или 0.5, чтобы контролировать активность нейронов и не давать модели переобучаться.
-
Снижение Сложности Модели: Упрощение архитектуры может помочь улучшить прогнозирование на валидационной выборке. Например, уменьшите количество нейронов на каждом слое или уберите несколько слоев.
-
Настройка Гиперпараметров: Играйте с гиперпараметрами, такими как скорость обучения, размер пакета и количество эпох. Возможно, вам подойдет меньшая скорость обучения, чтобы избежать скачков во время обучения.
-
Использование Ранней Остановки: Вы можете добавить обратный вызов
EarlyStopping
, который остановит обучение, если валидационная ошибка не будет уменьшаться в течение определенного количества эпох, что поможет избежать переобучения. -
Испытание Других Моделей: Попробуйте использовать более простые модели, такие как линейная регрессия, или более сложные, такие как ансамблевые методы (например, случайный лес), чтобы понять, насколько они могут быть эффективны на ваших данных.
Заключение
Как видно, проблема с валидационной потерей может быть сложной и требовать многогранного подхода к её решению. Применение техники регуляризации разного рода, изменение архитектуры модели и тщательная проверка распределения данных помогут вам добиться лучших результатов. Переобучение — это распространенная проблема, но её можно эффективно решать с помощью перечисленных методов. Удачи в ваших исследованиях и обучении!