В работе с последовательными данными, такими как текст или временные ряды, часто возникает необходимость обработки входов различной длины. Модели нейронных сетей, особенно рекуррентные (RNN, LSTM, GRU) и трансформеры, требуют единообразных размеров входных тензоров. Padding и маскирование служат для решения этой задачи.
Padding — это добавление специальных значений к
последовательностям для приведения их к одинаковой длине. В
TensorFlow.js обычно используют 0 или любое другое
значение, которое не встречается в реальных данных.
Пример последовательностей:
const sequences = [
[1, 2, 3],
[4, 5],
[6]
];
Для выравнивания до длины 3 добавляется нулевой padding:
const paddedSequences = [
[1, 2, 3],
[4, 5, 0],
[6, 0, 0]
];
Основные методы добавления padding в TensorFlow.js:
const tf = require('@tensorflow/tfjs');
const tensor = tf.tensor2d([[1, 2], [3, 4]]);
const padded = tf.pad(tensor, [[0, 1], [0, 2]], 0);
padded.print();
Аргументы [[0, 1], [0, 2]] обозначают добавление
0 в начале и конце каждой размерности:
[top, bottom] для строк, [left, right] для
столбцов.
const tf = require('@tensorflow/tfjs');
const { padSequences } = require('@tensorflow/tfjs-layers/dist/keras_format/utils');
const sequences = [[1, 2, 3], [4, 5], [6]];
const padded = padSequences(sequences, { maxlen: 4, padding: 'pre', value: 0 });
console.log(padded);
Опции padSequences:
maxlen — желаемая длина последовательности.padding — 'pre' (в начало) или
'post' (в конец).truncating — 'pre' или
'post', определяет, с какой стороны обрезать длинные
последовательности.value — значение padding (по умолчанию
0).Маскирование (masking) позволяет модели игнорировать добавленные элементы padding при обучении и вычислении градиентов. Это критично для правильной работы RNN и трансформеров, чтобы не учитывать искусственные нули.
Создание маски в TensorFlow.js:
const tf = require('@tensorflow/tfjs');
const sequences = tf.tensor2d([
[1, 2, 3],
[4, 5, 0],
[6, 0, 0]
]);
const mask = sequences.notEqual(tf.scalar(0));
mask.print();
Результат — булевый тензор:
[[true, true, true],
[true, true, false],
[true, false, false]]
Этот маска-тензор можно передавать слоям, поддерживающим
маскирование, например tf.layers.lstm:
const model = tf.sequential();
model.add(tf.layers.embedding({ inputDim: 10, outputDim: 8, inputLength: 3, maskZero: true }));
model.add(tf.layers.lstm({ units: 16 }));
maskZero: true у Embedding
автоматически создает маску, игнорируя элементы с нулевым padding.Трансформеры требуют строгой маски для attention, чтобы не учитывать padding при вычислении весов внимания:
function createPaddingMask(seq) {
const mask = tf.cast(seq.equal(tf.scalar(0)), 'float32');
return mask.expandDims(1).expandDims(2); // [batch, 1, 1, seq_len]
}
Использование маски в Multi-Head Attention:
const attentionOutput = tf.layers.multiHeadAttention({
numHeads: 8,
keyDim: 64
}).apply([query, key, value, createPaddingMask(inputSequence)]);
1 для padding и 0 для
реальных элементов.maskZero: true в
Embedding.tf.pad с
собственными значениями padding.const tf = require('@tensorflow/tfjs');
const { padSequences } = require('@tensorflow/tfjs-layers/dist/keras_format/utils');
const sequences = [[2, 3, 4], [5, 6], [7]];
// Шаг 1: Padding
const padded = padSequences(sequences, { maxlen: 4, padding: 'post', value: 0 });
// Шаг 2: Создание маски
const tensor = tf.tensor2d(padded);
const mask = tensor.notEqual(tf.scalar(0));
// Шаг 3: Создание модели с маскирующим Embedding
const model = tf.sequential();
model.add(tf.layers.embedding({ inputDim: 10, outputDim: 8, inputLength: 4, maskZero: true }));
model.add(tf.layers.lstm({ units: 16 }));
model.add(tf.layers.dense({ units: 1, activation: 'sigmoid' }));
// Шаг 4: Компиляция и обучение
model.compile({ optimizer: 'adam', loss: 'binaryCrossentropy' });
В этом примере нули, добавленные для выравнивания последовательностей, не влияют на обучение модели, благодаря встроенному маскированию.