Потоковая подача данных через генераторы

Keras.js — это библиотека для запуска моделей, обученных с помощью Keras, непосредственно в браузере с использованием JavaScript и WebGL. Эффективная работа с большими наборами данных требует организации потоковой подачи данных, чтобы избежать переполнения памяти и ускорить обработку. Одним из ключевых инструментов для этого являются генераторы.


Принципы работы генераторов

Генератор в JavaScript — это функция, которая может приостанавливать своё выполнение и возвращать промежуточные значения с помощью ключевого слова yield. В контексте Keras.js генераторы используются для построчной или пакетной подачи данных в модель. Основные преимущества:

  • Эффективное использование памяти: данные подаются небольшими блоками, не загружая всю выборку в память сразу.
  • Поддержка асинхронной обработки: генераторы могут работать с потоковыми источниками данных, такими как файловые API браузера или сетевые запросы.
  • Совместимость с батчевой обработкой: позволяет обрабатывать данные партиями, что необходимо для оптимизации работы GPU через WebGL.

Пример простого генератора:

function* dataGenerator(dataset, batchSize) {
    let index = 0;
    while (index < dataset.length) {
        const batch = dataset.slice(index, index + batchSize);
        yield batch;
        index += batchSize;
    }
}

В этом примере dataset разбивается на партии фиксированного размера batchSize, которые возвращаются последовательно при вызове метода next() генератора.


Интеграция генераторов с Keras.js

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

Пример использования генератора с Keras.js:

const model = new KerasJS.Model({
    filepaths: {
        model: 'model.json',
        weights: 'model_weights.buf',
        metadata: 'model_metadata.json'
    },
    gpu: true
});

async function predictWithGenerator(generator) {
    for (let batch of generator) {
        const inputTensor = new Float32Array(batch.length * inputSize);
        // Преобразование данных в тензор
        batch.forEach((item, i) => {
            inputTensor.set(item, i * inputSize);
        });

        const output = await model.predict({ input: inputTensor });
        console.log('Результат батча:', output);
    }
}

const generator = dataGenerator(dataset, 32);
predictWithGenerator(generator);

Ключевой момент — преобразование партий данных в формат, подходящий для модели: массив Float32Array или ndarray, соответствующий размерности входного слоя.


Асинхронные генераторы для потоковой загрузки данных

Для динамических источников данных, таких как изображения с сервера или медиапотоки, полезно использовать асинхронные генераторы (async function*). Они позволяют выполнять сетевые запросы без блокировки основного потока.

Пример асинхронного генератора для загрузки изображений:

async function* asyncImageGenerator(urls, batchSize) {
    let index = 0;
    while (index < urls.length) {
        const batchUrls = urls.slice(index, index + batchSize);
        const batchData = await Promise.all(
            batchUrls.map(async url => {
                const img = new Image();
                img.src = url;
                await img.decode();
                return preprocessImage(img);
            })
        );
        yield batchData;
        index += batchSize;
    }
}

Функция preprocessImage здесь отвечает за преобразование изображения в массив чисел, нормализованный для подачи в модель. Асинхронные генераторы позволяют начинать обработку первого батча данных ещё до полной загрузки всех изображений, значительно ускоряя работу приложения.


Преобразование потоковых данных в тензоры

Для совместимости с Keras.js все данные из генератора должны быть приведены к тензорам определённой формы. Используется структура ndarray или одномерные/многомерные массивы Float32Array. Важно учитывать:

  • Формат данных: размерность входного слоя модели (например, [batchSize, height, width, channels] для изображений).
  • Нормализация: приведение значений пикселей или других параметров к диапазону [0,1] или [-1,1].
  • Пакетная обработка: каждая партия данных должна соответствовать размеру батча, указанному в генераторе.

Пример преобразования массива изображений в тензор:

function imagesToTensor(batch) {
    const tensor = new Float32Array(batch.length * height * width * channels);
    batch.forEach((img, i) => {
        const flat = img.data; // предполагается, что img.data уже нормализован
        tensor.set(flat, i * height * width * channels);
    });
    return tensor;
}

Преимущества потоковой подачи через генераторы

  1. Снижение нагрузки на память: модель получает данные постепенно, без загрузки всего датасета.
  2. Гибкость источников данных: можно комбинировать локальные файлы, сетевые запросы и реальное время.
  3. Поддержка больших батчей: ускоряет вычисления на GPU, не ограничивая размер всего датасета.
  4. Асинхронная обработка: первый результат может быть получен до полной загрузки всех данных.

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


Практические советы

  • Размер батча подбирать с учётом доступной памяти GPU.
  • Асинхронные генераторы использовать при работе с удалёнными источниками или потоковыми данными.
  • Для больших изображений проводить предварительную ресайзинг и нормализацию перед формированием тензора.
  • Следить за совместимостью размерности входа с моделью Keras.js, иначе вызовы model.predict будут выдавать ошибки.

Потоковая подача данных через генераторы позволяет Keras.js эффективно обрабатывать большие объёмы данных в браузере, обеспечивая гибкость, экономию ресурсов и высокую скорость вычислений.