Как получить значение тензора tf, вычисляемое в функции потерь в keras train_on_batch, не вычисляя его дважды и не записывая пользовательский цикл?

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

У меня есть модель, и я реализовал пользовательскую функцию потерь что-то вроде:

def custom_loss(labels, predictions):
    global diff
    # фактический код использует декоратор, поэтому глобальных переменных нет
    diff = labels - predictions
    return tf.square(diff)

model.compile(loss=custom_loss, optimizer=opt.RMSprop())

...

model.train_on_batch(input, labels)

#

Как получить diff после того, как я запустил train_on_batch, не вызывая повторного выполнения predict за кулисами (ненужное замедление) и не испортив trainable/batchnorm и т.д. (возможные проблемы)?

Я хочу избежать создания ручного цикла train_op в чистом tensorflow и т.д., отслеживая фазу обучения и прочее. Это мой единственный выбор?

Я использую модуль keras версии tensorflow 1.14

Я решил эту проблему (открыл управление зависимостями и запомнил переменные). В основном, я создаю операцию присваивания diff переменной и с помощью control_dependencies forcing tf выполнять эту операцию каждый раз, когда вычисляется op, таким образом, когда я получаю эту переменную, она не вызывает перерасчет графа.

diff_var = tf.Variable()

def custom_loss(labels, predictions):
    diff = labels - predictions
    diff_var_op = diff_var.assign(diff)
    with tf.control_dependencies([diff_var_op]):
        return tf.square(diff)

тестовый код

import tensorflow as tf
sess = tf.Session()
var1 = tf.Variable(1, dtype=tf.float32)
var2 = tf.Variable(2, dtype=tf.float32)
counter = tf.Variable(1, dtype=tf.float32)
var2_op = tf.square(var2)*counter

diff_var = tf.Variable(10, dtype=tf.float32, trainable=False)

diff = var2_op - var1
diff_var_op = diff_var.assign(diff)

with tf.control_dependencies([diff_var_op]):
    op = tf.square(diff)

sess.run(tf.global_variables_initializer())

print('diff var:', sess.run(diff_var))     #10
print('counter:', sess.run(counter))       #1
print('op:', sess.run(op))                 #9
print('diff var:', sess.run(diff_var))     #3
print('-')
counter_op = tf.assign_add(counter, 1)
print('counter:', sess.run(counter))       #1
print('diff var:', sess.run(diff_var))     #3  # все еще то же самое
print('var2:', sess.run(var2_op))          #4
print('-')
sess.run(counter_op)
print('после counter_op')
print('counter:', sess.run(counter))       #2
print('var2:', sess.run(var2_op))          #8
# все еще то же самое, хотя var2 изменился из-за counter_op
print('diff var:', sess.run(diff_var))     #3
print('op:', sess.run(op))                 #49 # выполняем полный op
print('-')
print('после op')
print('diff var:', sess.run(diff_var))     #7
# переменная изменилась, никаких операций не было

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

Чтобы получить значение тензора diff в функции потерь при использовании train_on_batch в Keras, при этом избегая повторного вычисления, можно воспользоваться механизмами управления зависимостями TensorFlow и переменными для хранения промежуточных значений.

Подход к реализации

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

Одним из эффективных способов решения данной задачи является использование переменных для хранения результатов и контроля их обновления с помощью зависимостей TensorFlow.

Шаги реализации

  1. Создание переменной для хранения diff: Определите переменную TensorFlow, которая будет хранить значение diff, используя trainable=False, чтобы избежать его обучения.

  2. Использование assign для обновления переменной: В функции потерь создайте операцию, которая будет обновлять значение diff и поместите её в блоке control_dependencies. Это гарантирует, что при вычислении потерь значение diff обновится без повторного вызова predictions.

  3. Возврат значения потерь: Возвратите квадрат разности (или любое другое значение по вашему выбору) после обновления переменной.

Пример реализации

Вот пример, как можно эффективно реализовать вашу функцию потерь:

import tensorflow as tf

# Создаем переменную вне функции потерь для хранения разности
diff_var = tf.Variable(0, dtype=tf.float32, trainable=False)

# Определяем пользовательскую функцию потерь
def custom_loss(labels, predictions):
    # Вычисляем разность
    diff = labels - predictions
    # Обновляем переменную с помощью assign, не забывая указать зависимости
    diff_var_op = diff_var.assign(diff)

    with tf.control_dependencies([diff_var_op]):
        # Возвращаем квадрат разности в качестве потерь
        return tf.square(diff)

# Компиляция модели с пользовательской функцией потерь
model.compile(loss=custom_loss, optimizer=tf.keras.optimizers.RMSprop())

# Обучение модели на пакетах данных
model.train_on_batch(input, labels)

# Теперь diff_var будет содержать последнее значение разности
current_diff = diff_var.numpy()  # Получаем значение разности
print("Текущая разность:", current_diff)

Вывод

Данный подход позволяет избежать множественного вычисления predictions, что приводит к увеличению производительности и снижению нагрузки на граф вычислений. Использование управления зависимостями и переменных – это чистое и эффективное решение, которое не требует вручную отслеживать состояние графа или превращаться в сложные циклы обучения. Таким образом, можно сохранить простоту и чистоту кода, придерживаясь стандартных практик Keras и TensorFlow.

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

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

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