Список работ

Функции потерь библиотеки Keras

Содержание

Введение

При обучении нейронной сети (НС) выполняется минимизация функции потерь, которая при использовании библиотеки Keras указывается в качестве параметра метода compile класса Model [1], например:

from keras.models import Model
from keras.layers import Input, Dense
a = Input(shape=(32,))
b = Dense(32)(a)
model = Model(inputs = a, outputs = b)
model.compile(loss = keras.losses.mean_squared_error, optimizer = 'adam', metrics = ['accuracy'])

или

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

или

model.compile('adam', 'mse', ['accuracy'])
model.summary()

В случае НС с несколькими выходами для каждого выхода можно указать "свою" функцию потерь, передав методу compile либо словарь, либо список задействованных функций потерь, например:

from keras.models import Model
from keras.layers import Input
from keras.layers import Dense
from keras.layers.recurrent import LSTM
visible = Input(shape = (32,1))
extract = LSTM(10, return_sequences = True)(visible)
output1 = Dense(1, activation='sigmoid')(extract)
output2 = Dense(1, activation='sigmoid')(extract)
model = Model(inputs = visible, outputs = [output1, output2])
model.summary()
model.compile('adam', loss = ['mse', 'hinge'], metrics = ['accuracy'])

Для первого выхода использована функция потерь "Средняя квадратическая ошибка", для второго – "Верхняя граница". В этом случае при обучении минимизируется сумма заданных потерь.
При необходимости может быть задана пользовательская функция, например:

from keras.models import Model
from keras.layers import Input, Dense
from keras import backend as K
# Пользовательская функция потерь "Средняя квадратическая ошибка"
def myMse(y_true, y_pred):
    err = K.mean(K.square(y_pred - y_true))
    return err # Вернет тензор с shape=(?,)
a = Input(shape=(32,))
b = Dense(32)(a)
model = Model(inputs = a, outputs = b)
model.summary()
model.compile(loss = myMse, optimizer = 'adam', metrics = ['accuracy'])

Пользовательская функция потерь принимает параметры тензоры y_true, y_pred – соответственно истинное и предсказанные значения на выходе НС.
Далее рассматриваются функции потерь, определенные в библиотеке Keras.

Функции потерь библиотеки Keras

Перечень функция потерь библиотеки Keras перечислены в табл. 1.

Таблица 1. Функции потерь библиотеки Keras.
В таблице:
yi – прогнозируемое значение;
xi – истинное значение;
n – размер вектора xi (yi).

ФункцияОбозначениеФормула
Средняя квадратическая ошибка /
mean squared error
MSEmse
Средняя абсолютная ошибка /
mean absolute error
MAEmae
Средняя абсолютная процентная ошибка /
mean absolute percentage error
MAPEmape
Средняя квадратическая логарифмическая ошибка /
mean squared logarithmic error
MSLEmsle
Квадрат верхней границы /
squared hinge
SHsh
Верхняя граница / hingeHh
Категориальная верхняя граница /
categorical hinge
CHmax(0, neg – pos + 1), где
ch
Логарифм гиперболического косинуса /
logcosh
LClc
Категориальная перекрестная энтропия /
categorical crossentropy
CCEcce
Разреженная категориальная перекрестная энтропия /
sparse categorical crossentropy
SCCEscce
(σ(yi) – softmax-функция, или номализованная экспонента)
Бинарная перекрестная энтропия /
binary crossentropy
BCEbce
Расстояние Кульбака-Лейблера /
kullback leibler divergence
KLDkld
Пуассон /
Poisson
PSSPoisson
Косинусная близость /
cosine proximity
CPcp

При указании функции потерь можно употреблять следующие псевдонимы:

mse = MSE = mean_squared_error
mae = MAE = mean_absolute_error
mape = MAPE = mean_absolute_percentage_error
msle = MSLE = mean_squared_logarithmic_error
kld = KLD = kullback_leibler_divergence
cosine = cosine_proximity

Программы вычисления функций потерь библиотеки Keras

Приводимые ниже программы (функции) взяты из [2, 3].
Все функции потерь принимают 2D-тензоры с истинным (y_true) и предсказанным сетью значениями (y_pred) и возвращают 1D-тензор, содержащий текущее значение функции потерь.
Все функции потерь, кроме sparse_categorical_crossentropy, работают с категориальным представлением входных данных. Поэтому в программах обучения НС метки классов представляются в виде векторов, длина которых равна числу классов. Например, при обучении НС классификации рукописных цифр метка 5 (соответствует цифре 5 в обучающих и тестовых выборках) представляется в виде следующего вектора:

[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.].

Это обеспечивает следующий код:

import keras
num_classes = 10 # Число классов
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

В приведенном выше коде y_train и y_test – соответственно массивы с метками классов для обучающей и тестовых выборок.
Программы, вычисляющие функции потерь, используют следующие функции: abs, clip, epsilon, l2_normalize, log, max, maximum, mean, softplus, square и sum. Эти функции иллюстрируются следующими примерами.

import tensorflow as tf
from keras import backend as K
print(K.epsilon()) # 1e-07
sess = tf.Session()
print(sess.run(K.abs([-3.0, -4.0]))) # [3., 4.]
# Натуральный логарифм
print(sess.run(K.log([100.0, 1000.0]))) # [4.6051702 6.9077554]
# Нормализация
print(sess.run(K.l2_normalize([3.0, 4.0]))) # [0.6 0.8] = [3.0/5.0, 4.0/5.0]
#
t = K.square([1.2, 2.0])
print(sess.run(t)) # [1.44 4.] = [1.2**2, 2.0**2]
print(sess.run(K.mean(t))) # 2.72 = (1.44 + 4) / 2
print(sess.run(K.clip(t, 1.0, 5.0))) # [1.44 4.] = [max(1.44, 1.0), min(4.0, 5.0)]
print(sess.run(K.clip(t, 3.5, 5.0))) # [3.5 4.] = [max(1.44, 3.5), min(4.0, 5.0)]
print(sess.run(K.clip(t, 3.5, 3.75))) # [3.5 3.75] = [max(1.44, 3.5), min(4.0, 3.75)]
print(sess.run(K.sum(t))) # 5.44 = 1.44 + 4.0
print(sess.run(K.max(t))) # 4.0 = max(1.44, 4.0)
print(sess.run(K.maximum(t, 3.0))) # [3. 4.] = [max(1.44, 3.0), max(4.0, 3.0)]
print(sess.run(K.softplus(t))) # [1.6526307 4.01815] = [log(1 + exp(1.44)), log(1 + exp(4.0))]
#
t2 = K.exp([1.0, 2.0])
print(sess.run(t2)) # [2.7182817 7.389056]
print(sess.run(K.log(t2))) # [0.99999994 2.]

Функции потерь вычисляются следующими процедурами:

from keras import backend as K
#
# Средняя квадратическая ошибка
def mean_squared_error(y_true, y_pred):
    return K.mean(K.square(y_pred - y_true), axis = -1)
#
# Средняя абсолютная ошибка
def mean_absolute_error(y_true, y_pred):
    return K.mean(K.abs(y_pred - y_true), axis = -1)
#
# Средняя абсолютная процентная ошибка
def mean_absolute_percentage_error(y_true, y_pred):
    diff = K.abs((y_true - y_pred) / K.clip(K.abs(y_true), K.epsilon(), None))
    return 100. * K.mean(diff, axis = -1)
#
# Средняя квадратическая логарифмическая ошибка
def mean_squared_logarithmic_error(y_true, y_pred):
    first_log = K.log(K.clip(y_pred, K.epsilon(), None) + 1.)
    second_log = K.log(K.clip(y_true, K.epsilon(), None) + 1.)
    return K.mean(K.square(first_log - second_log), axis = -1)
#
# Квадрат верхней границы
def squared_hinge(y_true, y_pred):
    return K.mean(K.square(K.maximum(1. - y_true * y_pred, 0.)), axis = -1)
#
# Верхняя граница
def hinge(y_true, y_pred):
    return K.mean(K.maximum(1. - y_true * y_pred, 0.), axis = -1)
#
# Категориальная верхняя граница
def categorical_hinge(y_true, y_pred):
    pos = K.sum(y_true * y_pred, axis=-1)
    neg = K.max((1. - y_true) * y_pred, axis = -1)
    return K.maximum(0., neg - pos + 1.)
#
# Логарифм гиперболического косинуса
def logcosh(y_true, y_pred):
    # Логарифм гиперболического косинуса
    # log(cosh(x)) приблизительно равен (x ** 2) / 2 для небольших значений x
    # и приблизительно равен abs(x) - log(2) для больших значений x
    # Поэтому logcosh работает преимущественно как средняя квадратическая ошибка,
    # но не будет в то же время сильно подвержен случайным большим ошибкам предсказания
    # Возвращает тензор со скалярным значением потери
    def _logcosh(x):
        return x + K.softplus(-2. * x) - K.log(2.)
#
    return K.mean(_logcosh(y_pred - y_true), axis = -1)
# Категориальная перекрестная энтропия
def categorical_crossentropy(y_true, y_pred):
    return K.categorical_crossentropy(y_true, y_pred)
#
# Разреженная категориальная перекрестная энтропия
def sparse_categorical_crossentropy(y_true, y_pred):
    return K.sparse_categorical_crossentropy(y_true, y_pred)
#
# Бинарная перекрестная энтропия
def binary_crossentropy(y_true, y_pred):
    return K.mean(K.binary_crossentropy(y_true, y_pred), axis = -1)
#
# Расстояние Кульбака-Лейблера
def kullback_leibler_divergence(y_true, y_pred):
    y_true = K.clip(y_true, K.epsilon(), 1)
    y_pred = K.clip(y_pred, K.epsilon(), 1)
    return K.sum(y_true * K.log(y_true / y_pred), axis = -1)
#
# Пуассон
def poisson(y_true, y_pred):
    return K.mean(y_pred - y_true * K.log(y_pred + K.epsilon()), axis = -1)
#
# Косинусная близость
def cosine_proximity(y_true, y_pred):
    y_true = K.l2_normalize(y_true, axis = -1)
    y_pred = K.l2_normalize(y_pred, axis = -1)
    return -K.sum(y_true * y_pred, axis = -1)

Детализация вычисления категориальной перекрестной энтропии:

def categorical_crossentropy(y_true, y_pred, from_logits=False, axis=-1):
    # Масштабируем y_pred, чтобы сумма компонентов y_pred была равна 1
    y_pred = y_pred / math_ops.reduce_sum(y_pred, axis, True)
    epsilon_ = _to_tensor(epsilon(), output.dtype.base_dtype)
    y_pred = clip_ops.clip_by_value(y_pred, epsilon_, 1. - epsilon_)
    return -math_ops.reduce_sum(y_true * math_ops.log(y_pred), axis)

Детализация вычисления разреженной перекрестной энтропии:

def sparse_categorical_crossentropy(y_true, y_pred, from_logits=False, axis=-1):
    # Категориальная перекрестная энтропия с целочисленным y_true
    rank = len(y_pred.shape)
    axis = axis % rank
    if axis != rank - 1:
        permutation = list(range(axis)) + list(range(axis + 1, rank)) + [axis]
        y_pred = array_ops.transpose(y_pred, perm=permutation)
    epsilon_ = _to_tensor(epsilon(), y_pred.dtype.base_dtype)
    y_pred = clip_ops.clip_by_value(y_pred, epsilon_, 1 - epsilon_)
    y_pred = math_ops.log(y_pred)
    y_pred_shape = y_pred.shape
    targets = cast(flatten(y_true), 'int64')
    logits = array_ops.reshape(y_pred, [-1, int(y_pred_shape[-1])])
    res = nn.sparse_softmax_cross_entropy_with_logits(labels = targets, logits = logits)
    if len(y_pred_shape) >= 3:
        # Если y_pred содержит timesteps или пространственные измерения, то необходимо выполнить reshape
        return array_ops.reshape(res, array_ops.shape(y_pred)[:-1])
    else:
    return res

Детализация вычисления бинарной перекрестной энтропии:

def binary_crossentropy(y_true, y_pred, from_logits=False):
    # Бинарная перекрестная энтропия между y_pred и y_true
    # Обратное преобразование в логиты: logit(p) = log(p / (1 – p))
    epsilon_ = _to_tensor(epsilon(), y_pred.dtype.base_dtype)
    y_pred = clip_ops.clip_by_value(y_pred, epsilon_, 1 - epsilon_)
    y_pred = math_ops.log(y_pred / (1 - y_pred))
    return nn.sigmoid_cross_entropy_with_logits(labels=y_true, logits=y_pred)

Заключение

Приведенные функции не всегда удовлетворяют пользователей, поэтому имеется много дополнительно разработанных функций потерь [4]. К подобным разработкам, однако, следует приступать, только убедившись, что среди встроенных функций потерь не найдется ни одной подходящей для решаемой задачи.
Результат обучения оценивается по тестовой выборке X_test, y_test. X_test[i] содержит тестовый образец, а y_test[i] – метку, указывающую на класс X_test[i]. Например, при классификации рукописных цифр, заданных в оттенках серого цвета на площадке 28*28 пикселей, образец содержит 28*28 значений в диапазоне [0, 255], метка – это число из диапазона [0, 9], равное числу, которое представляет образец.
Результат обучения характеризует, насколько правильно были приняты решения при выборе архитектуры НС, параметров обучения (размер обучающей порции, скорость обучения, ...), метода оптимизации и его параметров, а также функции потерь.
Результат обучения можно проверить двумя способами:

score = model.evaluate(X_test, y_test, verbose = 0)
print('Потери при тестировании: ', score[0])
print('Точность при тестировании:', score[1])

и

y_pred = model.predict(X_test)
classes = []
for m in y_pred: classes.append(np.argmax(m))
# np.sum(classes == y_test_0) вернет сумму случаев, когда classes[i] = y_test_0[i]
acc = np.sum(classes == y_test_0) / n_test * 100
print("Точность прогнозирования: " + str(acc) + '%')

В приведенном выше коде

y_test_0 = y_test

до преобразования y_test в категориальное представление.

Очевидно, что точность оценки score[1] и точность прогнозирования acc должны совпадать.

Литература

  1. Model class API. [Электронный ресурс] URL: https://keras.io/models/model/#methods (дата обращения: 29.12.2018).
  2. Keras. Built-in loss functions. [Электронный ресурс] URL: https://github.com/keras-team/keras/blob/master/keras/losses.py (дата обращения: 29.12.2018)
  3. Tensorflow/python/keras/backend.py. [Электронный ресурс] URL: https://github.com/tensorflow/tensorflow/blob/r1.12/tensorflow/python/keras/backend.py (дата обращения: 29.12.2018)
  4. Апарнев А. Н. Функции потерь в задаче обучения нейронной сети. [Электронный ресурс] URL: http://100byte.ru/stdntswrks/loss/loss.html (дата обращения: 29.12.2018).

Список работ

Рейтинг@Mail.ru