Батчирование запросов

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

Принцип работы батчирования

В ConvNetJS каждый входной пример представлен в виде объекта Vol — многомерного тензора, содержащего данные и их градиенты. При использовании батчей вместо отдельного Vol для каждого примера формируется массив Vol-объектов. Во время прямого прохода (forward pass) сеть обрабатывает весь батч как единое целое, вычисляя активации для каждого примера параллельно. Аналогично, при обратном проходе (backward pass) градиенты аккумулируются по всем примерам батча.

Батчирование позволяет:

  • Стабилизировать обучение, усредняя градиенты по нескольким примерам.
  • Ускорить вычисления, за счет лучшего использования параллельных операций.
  • Снизить вероятность переобучения, обеспечивая более гладкий градиент.

Создание батчей в ConvNetJS

Для формирования батчей в ConvNetJS используются обычные массивы объектов Vol. Например, если имеется 1000 входных изображений размером 32×32×3, можно разделить их на батчи по 50 элементов:

let batchSize = 50;
for (let i = 0; i < data.length; i += batchSize) {
    let batch = data.slice(i, i + batchSize);
    forwardBatch(batch);
}

Здесь функция forwardBatch реализует обработку каждого батча через сеть, учитывая, что каждый элемент батча — отдельный объект Vol.

Forward pass для батча

В стандартном использовании ConvNetJS прямой проход выполняется методом net.forward(vol), где vol — один пример. Для батчей необходимо выполнить обход массива и собрать результаты в отдельный массив:

function forwardBatch(batch) {
    let outputs = [];
    for (let i = 0; i < batch.length; i++) {
        let out = net.forward(batch[i]);
        outputs.push(out);
    }
    return outputs;
}

Вариант с явным обходом массива прост, но не оптимален для больших батчей. Более продвинутый подход предполагает расширение слоёв сети для поддержки входных тензоров размером [batchSize, depth, width, height], что позволяет реализовать настоящую векторизацию вычислений.

Backward pass и обновление градиентов

Обратный проход для батча требует аккумулирования градиентов по всем примерам:

function backwardBatch(batch, targetBatch) {
    for (let i = 0; i < batch.length; i++) {
        net.backward(batch[i], targetBatch[i]);
    }
}

Каждый вызов backward обновляет внутренние градиенты слоёв сети, которые затем усредняются при применении метода стохастического градиентного спуска (SGD). Для улучшения эффективности можно использовать функции оптимизаторов ConvNetJS, такие как Trainer с указанием batch_size, что автоматически выполняет накопление градиентов.

Использование класса Trainer с батчами

ConvNetJS предоставляет удобный интерфейс через класс Trainer, который позволяет настроить размер батча и параметры обучения:

let trainer = new convnetjs.SGDTrainer(net, {
    method: 'adadelta',
    batch_size: 50,
    l2_decay: 0.001
});

Метод trainer.train(x, y) автоматически обрабатывает батчи, аккумулирует градиенты и выполняет шаг оптимизации. Такой подход упрощает обучение на больших данных и минимизирует ручное управление батчами.

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

  • Выбор размера батча: слишком маленький размер делает градиенты шумными, слишком большой — снижает разнообразие обновлений. Обычно используется диапазон 32–128.
  • Перемешивание данных: перед разбиением на батчи рекомендуется случайным образом перемешивать данные, чтобы избежать корреляций между последовательными примерами.
  • Адаптация слоёв: слои с нормализацией (например, BatchNormalizationLayer) корректно работают с батчами и требуют учета всех примеров в батче при вычислении статистик.

Пример полной итерации обучения с батчами

let batchSize = 64;
let trainer = new convnetjs.SGDTrainer(net, {learning_rate:0.01, batch_size: batchSize});

for (let epoch = 0; epoch < numEpochs; epoch++) {
    shuffle(data, labels); // перемешивание
    for (let i = 0; i < data.length; i += batchSize) {
        let batchData = data.slice(i, i + batchSize);
        let batchLabels = labels.slice(i, i + batchSize);
        for (let j = 0; j < batchData.length; j++) {
            trainer.train(batchData[j], batchLabels[j]);
        }
    }
}

В данном примере trainer автоматически учитывает накопление градиентов по батчу и применяет обновление параметров после каждого батча.


Батчирование запросов в ConvNetJS обеспечивает баланс между скоростью и стабильностью обучения. Понимание механики работы с батчами позволяет эффективно реализовать обучение глубоких сетей даже на относительно слабом железе.