Кастомные функции потерь

ConvNetJS — это компактная, полностью клиентская библиотека для построения и обучения нейронных сетей на языке JavaScript. Одним из её ключевых достоинств является гибкость при определении функций потерь, что позволяет адаптировать обучение к специфическим задачам.

Основные принципы функций потерь

Функция потерь (loss function) измеряет расхождение между предсказанным значением сети и ожидаемым результатом. В ConvNetJS каждая функция потерь представлена как отдельный объект с методами для прямого и обратного распространения:

  • forward(V, y) — вычисляет значение потерь на входном векторе (V) относительно правильного ответа (y).
  • backward() — вычисляет градиенты для весов, необходимые для обучения методом обратного распространения.

Стандартные функции потерь

Встроенные функции потерь в ConvNetJS включают:

  • SoftmaxCrossEntropyLoss — для задач классификации.
  • SVMLoss — для задач многоклассовой классификации с использованием SVM-подхода.
  • L2Loss — для регрессии, минимизирует среднеквадратичную ошибку.

Каждая из этих функций реализована как отдельный класс, наследующий базовую структуру с методами forward и backward.

Создание кастомной функции потерь

Кастомные функции потерь создаются путём определения нового класса с обязательными методами forward и backward. Пример структуры кастомной функции потерь:

function CustomLoss() {
    this.loss = 0.0;
    this.grad = null;
}

CustomLoss.prototype.forward = function(V, y) {
    // Пример: взвешенная среднеквадратичная ошибка
    var diff = [];
    var lossSum = 0.0;
    for (var i = 0; i < V.w.length; i++) {
        diff[i] = V.w[i] - y[i];
        lossSum += 0.5 * diff[i] * diff[i]; // 0.5 для удобства при вычислении градиента
    }
    this.loss = lossSum;
    this.diff = diff;
    return this.loss;
};

CustomLoss.prototype.backward = function() {
    this.grad = new convnetjs.Vol(this.diff.length, 1, 1, 0.0);
    for (var i = 0; i < this.diff.length; i++) {
        this.grad.w[i] = this.diff[i]; // градиент L2Loss равен разнице между предсказанием и эталоном
    }
};

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

Особенности интеграции кастомных функций в сеть

Для использования кастомной функции потерь в ConvNetJS необходимо:

  1. Создать экземпляр сети через convnetjs.Net().
  2. Задать параметры обучения через convnetjs.SGDTrainer.
  3. При обучении вместо стандартной функции потерь передавать экземпляр кастомного класса:
var net = new convnetjs.Net();
net.makeLayers([
    {type:'input', out_sx:1, out_sy:1, out_depth:2},
    {type:'fc', num_neurons:10, activation:'relu'},
    {type:'softmax', num_classes:3}
]);

var trainer = new convnetjs.SGDTrainer(net, {
    learning_rate:0.01,
    momentum:0.9,
    batch_size:1,
    l2_decay:0.001
});

var lossLayer = new CustomLoss();

var x = new convnetjs.Vol([0.5, -1.2]);
var y = [1.0, 0.0]; // эталонное значение

var loss = lossLayer.forward(x, y);
lossLayer.backward();

Расширенные возможности кастомных функций

Кастомные функции потерь в ConvNetJS позволяют реализовывать:

  • Взвешенные ошибки — полезно, если некоторые классы встречаются реже и требуют увеличенного влияния на градиент.
  • Комбинированные потери — суммирование нескольких функций потерь, например, L2 для регрессии и cross-entropy для классификации одновременно.
  • Регуляризаторы — интеграция дополнительных членов потерь, например, штраф за большие значения весов, напрямую в forward метод.

Практические рекомендации

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

Пример сложной кастомной функции

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

function SymmetricLoss(alpha) {
    this.alpha = alpha || 0.5;
    this.loss = 0.0;
}

SymmetricLoss.prototype.forward = function(V, y) {
    var loss = 0.0;
    for (var i = 0; i < V.w.length; i++) {
        var diff = V.w[i] - y[i];
        loss += (1-this.alpha) * diff*diff + this.alpha * Math.abs(diff);
    }
    this.loss = loss;
    this.diff = V.w.map((v, i) => (1-this.alpha)*2*(v-y[i]) + this.alpha * Math.sign(v-y[i]));
    return this.loss;
};

SymmetricLoss.prototype.backward = function() {
    this.grad = new convnetjs.Vol(this.diff.length, 1, 1, 0.0);
    for (var i = 0; i < this.diff.length; i++) {
        this.grad.w[i] = this.diff[i];
    }
};

Эта реализация объединяет L1 и L2 потери с настраиваемым коэффициентом, что позволяет гибко регулировать чувствительность к большим выбросам.

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