Обучение последовательностей

ConvNetJS — это библиотека для построения нейронных сетей на языке JavaScript, включая сверточные и полносвязные сети. Помимо классических моделей для задач классификации изображений, она поддерживает обучение последовательностей с помощью рекуррентных нейронных сетей (RNN) и Long Short-Term Memory (LSTM). Эти модели полезны для обработки текстовых данных, временных рядов, музыкальных последовательностей и любых данных с временной зависимостью.


Создание RNN и LSTM в ConvNetJS

Для работы с последовательными данными используется объект RNN или слой LSTM. Основная идея заключается в передаче состояния сети от одного временного шага к следующему. Это позволяет модели учитывать контекст предыдущих элементов последовательности при обработке текущего.

Пример конфигурации RNN:

var layer_defs = [];
layer_defs.push({type:'input', out_sx:1, out_sy:1, out_depth:vocab_size});
layer_defs.push({type:'rnn', num_neurons:128, activation:'tanh'});
layer_defs.push({type:'softmax', num_classes:vocab_size});

var net = new convnetjs.Net();
net.makeLayers(layer_defs);
  • input — задаёт размер входного вектора. Для текстовых данных это обычно one-hot encoding слов или символов.
  • rnn — слой рекуррентной сети. Параметр num_neurons задаёт количество скрытых нейронов, activation — функцию активации.
  • softmax — слой для классификации, который выдаёт вероятности для каждого возможного следующего элемента последовательности.

Для LSTM слой задаётся аналогично:

layer_defs.push({type:'lstm', num_neurons:128});

LSTM обеспечивает более устойчивое обучение длинных последовательностей за счёт механизма «запоминания» и «забывания» информации.


Подготовка данных для обучения

Последовательные данные необходимо преобразовать в подходящий формат. Обычно последовательность разбивается на шаги:

  1. One-hot кодирование: каждый элемент последовательности представляется в виде вектора длиной vocab_size, где единица ставится в позиции соответствующего символа или слова.
  2. Формирование батчей: сеть обучается на мини-батчах, каждый из которых содержит последовательность фиксированной длины.
  3. Сдвиг последовательностей: для обучения предсказания следующего элемента, целевой вектор (target) сдвигается на один шаг относительно входного (input).

Пример подготовки входных и выходных данных:

function buildDataset(text, vocab) {
    var X = [], Y = [];
    for(var i=0;i<text.length-1;i++){
        var x_vec = new Array(vocab.length).fill(0);
        x_vec[vocab.indexOf(text[i])] = 1;
        X.push(x_vec);

        var y_vec = new Array(vocab.length).fill(0);
        y_vec[vocab.indexOf(text[i+1])] = 1;
        Y.push(y_vec);
    }
    return {X: X, Y: Y};
}

Настройка тренера

ConvNetJS использует объект Trainer для оптимизации весов сети. Для RNN и LSTM рекомендуется использовать стохастический градиентный спуск (SGD) с небольшой скоростью обучения. Параметры тренера включают:

  • method — метод оптимизации (sgd, adadelta, adam).
  • learning_rate — скорость обучения. Для последовательностей обычно берут 0.01–0.001.
  • momentum — ускорение сходимости.
  • batch_size — количество последовательностей в одном обновлении весов.

Пример инициализации тренера:

var trainer = new convnetjs.Trainer(net, {
    method: 'adam',
    learning_rate: 0.001,
    batch_size: 20
});

Процесс обучения

Обучение RNN происходит итеративно по каждому шагу последовательности. На каждом шаге сеть получает текущий элемент, предсказывает следующий, затем вычисляется ошибка и проводится обратное распространение по времени (BPTT — Backpropagation Through Time).

Пример цикла обучения:

for(var iter=0; iter<num_iters; iter++){
    var loss = 0.0;
    for(var i=0; i<X.length; i++){
        loss += trainer.train(X[i], Y[i]);
    }
    if(iter % 100 == 0) console.log('iter', iter, 'loss', loss/X.length);
}
  • train — выполняет прямой и обратный проход, обновляет веса.
  • loss — средняя ошибка на текущем батче, важный показатель для контроля сходимости.

Генерация последовательностей

После обучения сеть может использоваться для генерации новых последовательностей. Для этого:

  1. Выбирается начальный элемент (seed).
  2. Сеть предсказывает распределение вероятностей для следующего шага.
  3. Выбирается элемент согласно вероятностям (например, случайно или берется наиболее вероятный).
  4. Новый элемент подается обратно в сеть как вход следующего шага.

Пример генерации текста:

var seed = X[0];
var generated = [];
for(var t=0; t<100; t++){
    var probs = net.forward(seed);
    var next_idx = sampleIndex(probs);
    generated.push(vocab[next_idx]);

    var next_input = new Array(vocab.length).fill(0);
    next_input[next_idx] = 1;
    seed = next_input;
}
  • sampleIndex — функция случайного выбора индекса с учётом вероятностей.
  • Такой метод позволяет получать разнообразные варианты последовательностей, сохраняя характер статистики обучающих данных.

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

  • Длина последовательности: слишком длинные последовательности увеличивают сложность обучения и могут вызвать затухание градиентов. Обычно используют шаги 25–50 элементов.
  • Размер скрытого слоя: увеличение числа нейронов повышает способность модели запоминать контекст, но требует больше ресурсов.
  • Регуляризация: для борьбы с переобучением можно использовать дропаут в полносвязных слоях перед RNN.
  • Инициализация весов: важно начинать с малых случайных значений, чтобы избежать насыщения активации.
  • Накопление состояния: при генерации длинных последовательностей рекомендуется сохранять внутреннее состояние сети между вызовами forward.

Заключение по функционалу ConvNetJS для последовательностей

ConvNetJS позволяет полностью управлять архитектурой RNN и LSTM, настраивать оптимизаторы, шаги обучения и генерацию новых последовательностей. Несмотря на относительную простоту по сравнению с Python-библиотеками, она обеспечивает полный контроль над процессом обучения и хорошо подходит для экспериментов с обучением текста, музыки или временных рядов непосредственно в браузере или на Node.js.