Как написать пользовательскую функцию потерь в Keras / Tensorflow, которая использует циклы / итерации со ссылкой numpy код - PullRequest
1 голос
/ 29 мая 2020

Я видел этот вопрос: Реализация пользовательской функции потерь в keras с условием И мне нужно сделать то же самое, но с кодом, который, кажется, нуждается в циклах.

У меня есть собственный numpy функция, которая вычисляет среднее евклидово расстояние от среднего вектора. Я написал это на основе статьи https://arxiv.org/pdf/1801.05365.pdf:

Equation Picture

import numpy as np

def mean_euclid_distance_from_mean_vector(n_vectors):

    dists = []

    for (i, v) in enumerate(n_vectors):
        n_vectors_rest = n_vectors[np.arange(len(n_vectors)) != i]

        print("rest of vectors: ")
        print(n_vectors_rest)

        # calculate mean vector
        mean_rest = n_vectors_rest.mean(axis=0)

        print("mean rest vector")
        print(mean_rest)

        dist = v - mean_rest

        print("dist vector")
        print(dist)
        dists.append(dist)

    # dists is now a matrix of distance vectors (distance from the mean vector)
    dists = np.array(dists)

    print("distance vector matrix")
    print(dists)

    # here we matmult each vector
    # sum them up
    # and divide by the total number of elements
    result = np.sum([np.matmul(d, d) for d in dists]) / dists.size

    return result


features = np.array([
    [1,2,3,4],
    [4,3,2,1]
])

c = mean_euclid_distance_from_mean_vector(features)

print(c)

Однако мне нужна эта функция для работы внутри tensorflow с Керасом. Таким образом, пользовательская лямбда https://www.tensorflow.org/api_docs/python/tf/keras/layers/Lambda

Однако я не уверен, как реализовать вышеизложенное в Keras / Tensorflow, поскольку у него есть циклы, и то, как в статье говорилось о вычислении m_i, похоже, требует циклов, подобных тому, как я реализовал выше.

Для справки, версия этого кода PyTorch находится здесь: https://github.com/PramuPerera/DeepOneClass

1 Ответ

1 голос
/ 29 мая 2020

Учитывая карту функций, например:

features = np.array([
    [1, 2, 3, 4],
    [2, 4, 4, 3],
    [3, 2, 1, 4],
], dtype=np.float64)

, отражающую batch_size из

batch_size = features.shape[0]

и

k = features.shape[1]

У одного есть реализация приведенных выше формул в Tensorflow может быть выражено (прототипировано) как:

dim = (batch_size, features.shape[1])
def zero(i):
    arr = np.ones(dim)
    arr[i] = 0
    return arr


mapper = [zero(i) for i in range(batch_size)]
elems = (features, mapper)
m = (1 / (batch_size - 1)) * tf.map_fn(lambda x: tf.math.reduce_sum(x[0] * x[1], axis=0), elems, dtype=tf.float64)
pairs = tf.map_fn(lambda x: tf.concat(x, axis=0) , tf.stack([features, m], 1), dtype=tf.float64)
compactness_loss = (1 / (batch_size * k)) * tf.map_fn(lambda x: tf.math.reduce_euclidean_norm(x), pairs, dtype=tf.float64)

with tf.Session() as sess:
    print("loss value output is: ", compactness_loss.eval())

Что дает:

loss value output is:  [0.64549722 0.79056942 0.64549722]

Однако для пакета требуется единичная мера, поэтому необходимо ее уменьшить; суммированием всех значений.

Требуемая функция потери компактности à la Tensorflow:

def compactness_loss(actual, features):
    features = Flatten()(features)
    k = 7 * 7 * 512
    dim = (batch_size, k)

    def zero(i):
        z = tf.zeros((1, dim[1]), dtype=tf.dtypes.float32)
        o = tf.ones((1, dim[1]), dtype=tf.dtypes.float32)
        arr = []
        for k in range(dim[0]):
            arr.append(o if k != i else z)
        res = tf.concat(arr, axis=0)
        return res

    masks = [zero(i) for i in range(batch_size)]
    m = (1 / (batch_size - 1)) * tf.map_fn(
        # row-wise summation
        lambda mask: tf.math.reduce_sum(features * mask, axis=0),
        masks,
        dtype=tf.float32,
    )
    dists = features - m
    sqrd_dists = tf.pow(dists, 2)
    red_dists = tf.math.reduce_sum(sqrd_dists, axis=1)
    compact_loss = (1 / (batch_size * k)) * tf.math.reduce_sum(red_dists)
    return compact_loss

Конечно, Flatten() можно было бы переместить обратно в модель для удобства, а k может быть получено непосредственно из карты функций; это отвечает на ваш вопрос. У вас могут возникнуть проблемы с определением ожидаемых значений для модели - карты характеристик из VGG16 (или любой другой архитектуры), обученные, например, для imagenet?

В документе говорится:

В нашей формулировке (показанной на рисунке 2 (e)), начиная с предварительно обученной глубокой модели, мы фиксируем начальные функции (gs) и изучаем (gl) и (h c). На основе выходных данных подсети классификации (h c) оцениваются две потери: потеря компактности и потеря информативности. Эти две потери, представленные в следующих разделах, используются для оценки качества изученной глубинной функции. Мы используем предоставленный одноклассный набор данных для расчета потери компактности. Внешний многоклассовый эталонный набор данных используется для оценки потери информативности. Как показано на рисунке 3, веса gl и h c узнаются в предлагаемом методе посредством обратного распространения из составных потерь. После схождения обучения система, показанная в настройке на рисунке 2 (d), используется для выполнения классификации, где полученная модель используется в качестве предварительно обученной модели.

затем просматривается магистраль «Framework» здесь плюс:

Ale xNet двоичный и VGG16 двоичный (базовый) . Двоичная CNN обучается с использованием образцов Imag eNet и образцов изображений одного класса в качестве двух классов с использованием архитектур Ale xNet и VGG16 соответственно. Тестирование выполняется с использованием классификаторов k-ближайшего соседа, SVM одного класса [43], Isolation Forest [3] и Gaussian Mixture Model [3].

Заставляет меня задаться вопросом, не разумно ли было бы add предложил плотные слои для сетей Secondary и Reference для вывода одного класса (сигмоид) или даже для вывода двоичного класса (с использованием Softmax) и с использованием mean_squared_error в качестве так называемой потери компактности и binary_cross_entropy как потеря дескриптивности.

...