Есть ли простой способ расширить существующую функцию активации? Моя пользовательская функция softmax возвращает: операция имеет «Нет» для градиента

Я хочу реализовать попытку ускорить softmax, используя только верхние значения k в векторе.

Для этого я попытался реализовать пользовательскую функцию для тензорного потока для использования в модели:

def softmax_top_k(logits, k=10):
    values, indices = tf.nn.top_k(logits, k, sorted=False)
    softmax = tf.nn.softmax(values)
    logits_shape = tf.shape(logits)
    return_value = tf.sparse_to_dense(indices, logits_shape, softmax)
    return_value = tf.convert_to_tensor(return_value, dtype=logits.dtype, name=logits.name)
    return return_value

Я использую fashion mnist, чтобы проверить, работает ли эта попытка:

fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# normalize the data
train_images = train_images / 255.0
test_images = test_images / 255.0

# split the training data into train and validate arrays (will be used later)
train_images, train_images_validate, train_labels, train_labels_validate = train_test_split(
    train_images, train_labels, test_size=0.2, random_state=133742,
)

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation=tf.nn.relu),
    keras.layers.Dense(10, activation=softmax_top_k)
])


model.compile(
    loss='sparse_categorical_crossentropy',
    optimizer='adam',
    metrics=['accuracy']
)

model.fit(
    train_images, train_labels,
    epochs=10,
    validation_data=(train_images_validate, train_labels_validate),
)

model_without_cnn.compile(
    loss='sparse_categorical_crossentropy',
    optimizer='adam',
    metrics=['accuracy']
)

model_without_cnn.fit(
    train_images, train_labels,
    epochs=10,
    validation_data=(train_images_validate, train_labels_validate),
)

Но во время выполнения возникает ошибка:

ValueError: An operation hasНетfor gradient. Please make sure that all of your ops have a gradient defined (i.e. are differentiable).

Я нашел следующее: (Как создать пользовательскую функцию активации), в которой объясняется, как реализовать полностью пользовательскую функцию активации для tensorflow. Но так как это использует и расширяет softmax, я подумал, что градиент должен быть таким же.

Это моя первая неделя кодирования с помощью python и tensorflow, поэтому у меня пока нет хорошего обзора всех внутренних реализаций.

Есть ли более простой способ расширить softmax до новой функции, а не реализовывать ее с нуля?

Заранее спасибо!


person JtheB    schedule 08.03.2019    source источник


Ответы (1)


Вместо использования разреженных тензоров для создания тензора со «всеми нулями, кроме значений softmaxed top-K», используйте tf.scatter_nd:

import tensorflow as tf

def softmax_top_k(logits, k=10):
    values, indices = tf.nn.top_k(logits, k, sorted=False)
    softmax = tf.nn.softmax(values)
    logits_shape = tf.shape(logits)
    # Assuming that logits is 2D
    rows = tf.tile(tf.expand_dims(tf.range(logits_shape[0]), 1), [1, k])
    scatter_idx = tf.stack([rows, indices], axis=-1)
    return tf.scatter_nd(scatter_idx, softmax, logits_shape)

РЕДАКТИРОВАТЬ: Вот немного более сложная версия для тензоров с произвольным количеством измерений. Однако код по-прежнему требует, чтобы количество измерений было известно во время построения графика.

import tensorflow as tf

def softmax_top_k(logits, k=10):
    values, indices = tf.nn.top_k(logits, k, sorted=False)
    softmax = tf.nn.softmax(values)
    # Make nd indices
    logits_shape = tf.shape(logits)
    dims = [tf.range(logits_shape[i]) for i in range(logits_shape.shape.num_elements() - 1)]
    grid = tf.meshgrid(*dims, tf.range(k), indexing='ij')
    scatter_idx = tf.stack(grid[:-1] + [indices], axis=-1)
    return tf.scatter_nd(scatter_idx, softmax, logits_shape)
person jdehesa    schedule 08.03.2019
comment
Спасибо за быстрый ответ! Что мне нужно, чтобы изменить это, чтобы иметь возможность принимать тензор большего размера, чем 2? Нужно ли мне захватить последний слой, чтобы передать его в 2D-решение? - person JtheB; 12.03.2019
comment
@JtheB Я добавил вариант для произвольного количества измерений. - person jdehesa; 12.03.2019