Вопрос или проблема
Я создаю нейронную сеть в tensorflow и мне нужно минимизировать следующую функцию потерь:
$\frac{max(y,p)}{min(y,p)}$ где $y$ обозначает истинное значение, а $p$ предсказанное значение. Поскольку функция потерь недифференцируема, это становится проблемой при использовании градиентного спуска.
Обновление, теперь я пытаюсь реализовать:
$p > y \rightarrow loss = log(p)-log(y)$
$p < y \rightarrow loss = log(y)-log(p)$
$p = y \rightarrow loss = log(y)-log(y) = log(p)-log(p)$
Вот мой код:
def custom_loss(y_true, pred):
if pred > y_true:
custom_loss = K.log(pred) - K.log(y_true)
elif pred < y_true:
custom_loss = K.log(y_true) - K.log(pred)
else:
custom_loss = K.log(pred)-K.log(pred)
return custom_loss
if __name__ == '__main__':
run = 1
X = np.load("vectors_normal_2way.npy")
with open("target2.pickle", "rb") as file:
target_dict = pickle.load(file)
target_strings = [*target_dict]
Y = np.array([])
for target_value in target_strings:
Y = np.append(Y, target_dict.get(target_value))
for i in range(run):
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size = 0.2)
model = get_model()
start = time.time()
model.fit(X_train, Y_train, verbose = 1, epochs = 1, batch_size = 32)
end_time = time.time()-start
pred = model.predict(X_test)[:, 0]
Но я получаю следующую ошибку:
tensorflow.python.framework.errors_impl.InvalidArgumentError: Второй ввод должен быть скаляром, но имеет форму [32]
[[{{node gradient_tape/custom_loss/cond/StatelessIf/gradient_tape/custom_loss/weighted_loss/Mul/_17}}]] [Op:__inference_train_function_1040]
Код может выполняться, когда я ставлю batch_size = 1
Функция потерь вычисляет ошибку на всех данных, которые она получает. Для нейронных сетей это среднее значение по мини-пакету. Ваш код может обрабатывать только скаляры (поэтому работает при batch=1), он должен обрабатывать векторы.
K
похоже, что вы используете tf.Keras. Документация дает примеры пользовательских функций потерь. Полезно наследоваться от tf.keras.losses.Loss
, который ожидает метод call()
, содержащий логику для вычисления потерь с использованием y_true, y_pred.
class CustomLoss(Loss):
def call(self, y_true, y_pred):
pass
Реализация такой функции потерь может не привести вас к желаемому результату, потому что она кажется очень разрывной, а одно из требований градиентного спуска для функции потерь – это ее непрерывность. Я бы протестировал сеть на синтетическом задании, чтобы проверить функцию потерь.
.
Ответ или решение
Создание настраиваемой функции потерь для нейронной сети, особенно такой, которая включает в себя как min(y, p)
, так и max(y, p)
, требует внимательного подхода в связи с особенностями, характеристиками и ограничениями методов оптимизации, часто используемых в глубоких сетях, например, градиентного спуска. В рассматриваемом случае, цель – минимизировать выражение $\frac{max(y,p)}{min(y,p)}$, что, по сути, представляет собой отношение максимального и минимального значений между истинным значением y
и прогнозируемым значением p
.
Теория
Ключевым инструментом в машинном обучении является функция потерь, которая определяет, насколько хорошо, или наоборот, плохо модель выполняет свою задачу прогнозирования. Она, в конечном счете, является тем компасом, который направляет процесс обучения в правильное русло. В общем случае она должна быть:
-
Дифференцируемой: Это необходимо для применения градиентного спуска, который используется для минимизации функции потерь. Отсутствие дифференцируемости может привести к ошибкам или проблемам с сходимостью.
-
Континуальной: Континуальность позволяет избежать резких скачков в значениях функции потерь, что может привести к неустойчивому обучению.
Пример
Представленная функция: $\frac{max(y,p)}{min(y,p)}$ не является дифференцируемой во всех точках, особенно в местах разрывов, что требует осторожного подхода к её оптимизации. В первоначальной попытке, отмеченной в вопросе пользователя, используется логарифмическое выражение для дифференциации условий p > y
, p < y
и p = y
. Однако, текущая реализация была представлена с ошибкой из-за использования недифференцируемой функции в векторном контексте.
Применение
Для корректного вычисления предлагаем следующие шаги:
- Реализация настраиваемой функции потерь: Используйте класс
tf.keras.losses.Loss
для создания настраиваемой функции. Это позволяет инкапсулировать необходимую логику в пределах методаcall()
, который будет вызван во время обучения модели.
import tensorflow as tf
from tensorflow.keras.losses import Loss
class CustomLoss(Loss):
def call(self, y_true, y_pred):
difference = y_pred - y_true
log_difference = tf.math.log(tf.abs(difference) + 1e-10) # добавляем небольшую величину для стабильности
condition = tf.greater(y_pred, y_true)
loss = tf.where(condition, log_difference, -log_difference)
return tf.reduce_mean(loss)
-
Обработка батчей: Вместо обработки скаляров необходимо обеспечить работу с векторами значений, что обеспечит возможность параллельной обработки данных в батче. В приведённом примере используется векторизация операций с применением функций библиотеки TensorFlow.
-
Синтетическое тестирование: Подобная функция потерь может привести к сложным ландшафтам оптимизации, которые могут вызвать проблемы с сходимостью. Проведение тестов моделей с такими функциями потерь на синтетических данных позволяет выявить проблемы на ранних стадиях и скорректировать подход, до того как модель будет задействована на более сложных реальных данных.
Заключение
Реализация функции потерь, подобной описанной, требует понимания нюансов её поведения и влияния на процесс оптимизации. Стоит также учесть необходимость в предобработке данных, регуляризации и иных методах андологичного снижения сложности моделей, чтобы любые проблемы с сходимостью могли минимизироваться.
При успешной реализации и тестировании выбранного метода, подобная настраиваемая функция потерь может существенно улучшить качество моделей для специфических задач прогнозирования, где стандартные функции потерь могут не подходить в достаточной степени.