Keras – Реализация пользовательской функции потерь с несколькими выходами

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

Я пытаюсь воссоздать (в гораздо меньшей версии) систему AlphaGo Zero. Однако у меня возникают проблемы с моделью сети. Функция потерь, которую я должен реализовать, следующая:

$$l = (z – v)^2 – \pi^T log(p) + c ||\theta||^2$$

Где:

  • $z$ — это метка (реальное значение между -1 и 1) одной из двух голов сети, а $v$ — это значение, предсказанное сетью.
  • $\pi$ — это метка распределения вероятности всех действий, а $p$ — это распределение вероятности всех действий, предсказанное сетью.
  • $c$ — это параметр регуляризации L2.

Я передаю сети список каналов (представляющих состояние игры) и массив (такого же размера, что и $\pi$ и $p$), представляющий, какие действия действительно являются действительными (путем установки 1, если действительно, 0 в противном случае).

Как видно, функция потерь использует как целевые значения, так и предсказания сети для вычисления. Но после обширного поиска, при реализации своей пользовательской функции потерь, я могу передавать только параметры y_true и y_pred, хотя у меня есть две “y_true” и две “y_pred”. Я пытался использовать индексацию, чтобы получить эти значения, но я уверен, что это не работает.

Моделирование сети и пользовательская функция потерь находится в коде ниже:

def custom_loss(y_true, y_pred):

    # Я уверен, что это не работает

    output_prob_dist = y_pred[0]
    output_value = y_pred[1] 
    label_prob_dist = y_true[0]
    label_value = y_pred[1]

    mse_loss = K.mean(K.square(label_value - output_value), axis=-1)
    cross_entropy_loss = K.dot(K.transpose(label_prob_dist), output_prob_dist)

    return mse_loss - cross_entropy_loss

def define_model():
    """Реализация модели нейронной сети с использованием Keras + Tensorflow."""
    state_channels = Input(shape = (5,5,6), name="States_Channels_Input")
    valid_actions_dist = Input(shape = (32,), name="Valid_Actions_Input")

    conv = Conv2D(filters=10, kernel_size=2, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="Conv_Layer")(state_channels)
    pool = MaxPooling2D(pool_size=(2, 2), name="Pooling_Layer")(conv)
    flat = Flatten(name="Flatten_Layer")(pool)

    # Объединение разровненных каналов (после пулинга) и действительного действия
    # распределение. Используется только в качестве ввода в голову распределения вероятности.
    merge = concatenate([flat, valid_actions_dist])

    # Распределение вероятности по действиям
    hidden_fc_prob_dist_1 = Dense(100, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="FC_Prob_1")(merge)
    hidden_fc_prob_dist_2 = Dense(100, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="FC_Prob_2")(hidden_fc_prob_dist_1)
    output_prob_dist = Dense(32, kernel_regularizer=regularizers.l2(0.0001), activation='softmax', name="Output_Dist")(hidden_fc_prob_dist_2)
    
    # Значение состояния
    hidden_fc_value_1 = Dense(100, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="FC_Value_1")(flat)
    hidden_fc_value_2 = Dense(100, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="FC_Value_2")(hidden_fc_value_1)
    output_value = Dense(1, kernel_regularizer=regularizers.l2(0.0001), activation='tanh', name="Output_Value")(hidden_fc_value_2)

    model = Model(inputs=[state_channels, valid_actions_dist], outputs=[output_prob_dist, output_value])

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



# В основном методе
model = define_model()
# ...
# Процедура MCTS для сбора данных для ввода в сеть
# ...

x_train = [channels_input, valid_actions_dist_input]
y_train = [dist_probs_label, who_won_label]

model.fit(x_train, y_train, epochs=10)

Вкратце, мой вопрос: как правильно реализовать эту пользовательскую функцию потерь, которая использует как выводы сети, так и метки значений сети?

Поскольку вы смешиваете y_true[0] с y_pred[0] и y_true[1] с y_pred[1], вы можете рассмотреть возможность использования разных потерь для каждого из них и использования loss={'Output_Dist': custom_loss, 'Output_Value': losses.MSE} при компиляции. Внутри он добавит результат каждого из них в финальную потерю.

def custom_loss(y_true, y_pred):
    return -K.dot(K.transpose(y_true), y_pred)

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

Реализация пользовательской функции потерь с несколькими выходами в Keras

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

Проблема

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

Решение

Рекомендуется разделить расчеты потерь для каждого выходного слоя (например, для распределения вероятностей и для значения) и затем объединить их для общей функции потерь. Вы можете это сделать, передав разные функции потерь для каждого выхода в методе compile.

Вот как можно переписать вашу функцию потерь и код для компиляции модели:

import keras.backend as K
from keras import regularizers
from keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, concatenate
from keras.models import Model

def custom_loss_value(y_true, y_pred):
    z = y_true[1]
    v = y_pred[1]
    mse_loss = K.mean(K.square(z - v), axis=-1)
    return mse_loss

def custom_loss_dist(y_true, y_pred):
    pi = y_true[0]
    p = y_pred[0]
    # Применяем mask к y_true для получения действительных действий
    valid_actions_mask = K.cast(K.equal(pi, 1), K.floatx())  # Маска для действительных действий
    cross_entropy_loss = -K.sum(valid_actions_mask * K.log(p + K.epsilon()), axis=-1) 
    return cross_entropy_loss

def define_model():
    state_channels = Input(shape=(5, 5, 6), name="States_Channels_Input")
    valid_actions_dist = Input(shape=(32,), name="Valid_Actions_Input")

    conv = Conv2D(filters=10, kernel_size=2, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="Conv_Layer")(state_channels)
    pool = MaxPooling2D(pool_size=(2, 2), name="Pooling_Layer")(conv)
    flat = Flatten(name="Flatten_Layer")(pool)

    merge = concatenate([flat, valid_actions_dist])

    # Вероятностное распределение действий
    hidden_fc_prob_dist_1 = Dense(100, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="FC_Prob_1")(merge)
    output_prob_dist = Dense(32, kernel_regularizer=regularizers.l2(0.0001), activation='softmax', name="Output_Dist")(hidden_fc_prob_dist_1)

    # Значение состояния
    hidden_fc_value_1 = Dense(100, kernel_regularizer=regularizers.l2(0.0001), activation='relu', name="FC_Value_1")(flat)
    output_value = Dense(1, kernel_regularizer=regularizers.l2(0.0001), activation='tanh', name="Output_Value")(hidden_fc_value_1)

    model = Model(inputs=[state_channels, valid_actions_dist], outputs=[output_prob_dist, output_value])

    # Обратите внимание на разные функции потерь для каждого выхода
    model.compile(optimizer='adam', loss={'Output_Dist': custom_loss_dist, 'Output_Value': custom_loss_value}, metrics=['accuracy'])

    return model

# В основном методе
model = define_model()

# Предполагается, что данные для обучения формируются заранее:
# x_train = [channels_input, valid_actions_dist_input]
# y_train = [dist_probs_label, who_won_label]

model.fit(x_train, y_train, epochs=10)

Объяснение кода

  1. Модульные потери: Мы определили две отдельные функции потерь: custom_loss_value использует MSE для оценки ошибки между предсказанным значением и истинным значением, в то время как custom_loss_dist рассчитывает кросс-энтропию для распределения вероятностей, учитывая только разрешенные действия.

  2. Маскирование: В custom_loss_dist добавлено маскирование для выявления действительных действий, что позволяет исключить недопустимые действия из расчета потерь.

  3. Компиляция модели: При компиляции модели разные функции потерь назначаются для разных выходов, что позволяет корректно вычислить итоговую потерю модели.

Заключение

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

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

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