Построение модели без Layers API

TensorFlow.js предоставляет два основных подхода к построению моделей: с использованием Layers API, напоминающего Keras, и с использованием низкоуровневого API, оперирующего напрямую тензорами и операциями. Работа без Layers API даёт полный контроль над вычислениями, позволяя реализовывать кастомные архитектуры и экспериментальные алгоритмы.


Создание тензоров

Тензор — основная единица данных в TensorFlow.js. Он может быть многомерным массивом любого типа (числовым, логическим и т.д.). Создание тензора выполняется с помощью функции tf.tensor:

const t = tf.tensor([[1, 2], [3, 4]]);
const tFloat = tf.tensor([1.0, 2.0, 3.0], [3], 'float32');

Ключевые параметры:

  • данные — массив или TypedArray;
  • shape — массив, определяющий размерность;
  • dtype — тип данных (float32, int32, bool и др.).

Тензоры поддерживают арифметические операции, матричное умножение и трансформации:

const a = tf.tensor([1, 2, 3]);
const b = tf.tensor([4, 5, 6]);
const sum = a.add(b);       // [5, 7, 9]
const product = a.mul(b);   // [4, 10, 18]

Для сложных моделей важны операции линейной алгебры, такие как matMul и transpose:

const x = tf.tensor2d([[1, 2], [3, 4]]);
const y = tf.tensor2d([[5, 6], [7, 8]]);
const result = tf.matMul(x, y); // [[19, 22], [43, 50]]

Определение параметров модели вручную

Без Layers API все веса и смещения создаются вручную. Весовой коэффициент и смещение задаются как переменные tf.variable, чтобы их можно было обновлять во время обучения:

const w = tf.variable(tf.randomNormal([2, 2]));
const b = tf.variable(tf.zeros([2]));
  • tf.randomNormal — инициализация случайными числами с нормальным распределением.
  • tf.zeros — создание массива с нулями.
  • Переменные можно изменять, используя градиенты и оптимизаторы.

Прямой проход (Forward Pass)

Модель вычисляется через последовательность операций над тензорами. Пример простой линейной модели:

function linearModel(x) {
    return x.matMul(w).add(b);
}

Для нелинейных моделей добавляются активационные функции:

function relu(x) {
    return x.relu();
}

function model(x) {
    const z = x.matMul(w).add(b);
    return relu(z);
}

TensorFlow.js предоставляет встроенные функции активации: relu, sigmoid, tanh, softmax.


Вычисление функции потерь

Функция потерь измеряет расхождение между предсказанными значениями и истинными. Для регрессии часто используется MSE (Mean Squared Error):

function loss(pred, label) {
    return pred.sub(label).square().mean();
}

Для классификации можно использовать кросс-энтропию:

function crossEntropy(pred, label) {
    return tf.losses.softmaxCrossEntropy(label, pred).mean();
}

Обратное распространение и оптимизация

TensorFlow.js позволяет вычислять градиенты автоматически с помощью tf.variable и функции tf.grad или tf.train.Optimizer.

Пример ручного обновления весов с использованием градиента:

const learningRate = 0.01;
const optimizer = tf.train.sgd(learningRate);

for (let i = 0; i < 100; i++) {
    optimizer.minimize(() => loss(model(xTrain), yTrain));
}
  • optimizer.minimize автоматически вычисляет градиенты и обновляет переменные.
  • Можно использовать различные оптимизаторы: sgd, adam, adagrad.

Использование tf.tidy для управления памятью

Все операции с тензорами создают новые объекты, которые занимают GPU/CPU память. tf.tidy позволяет автоматически удалять промежуточные тензоры:

tf.tidy(() => {
    const pred = model(xTrain);
    const currentLoss = loss(pred, yTrain);
    console.log(currentLoss.dataSync());
});

Это особенно важно при обучении на больших данных или при многократных итерациях.


Пример полной тренировки простой модели

// Данные
const xTrain = tf.tensor2d([[1], [2], [3], [4]]);
const yTrain = tf.tensor2d([[2], [4], [6], [8]]);

// Параметры
const w = tf.variable(tf.randomNormal([1, 1]));
const b = tf.variable(tf.zeros([1]));

// Модель
function linear(x) {
    return x.matMul(w).add(b);
}

// Потери
function loss(pred, label) {
    return pred.sub(label).square().mean();
}

// Оптимизатор
const optimizer = tf.train.sgd(0.1);

// Обучение
for (let i = 0; i < 200; i++) {
    tf.tidy(() => {
        optimizer.minimize(() => loss(linear(xTrain), yTrain));
    });
}

// Проверка
linear(tf.tensor2d([[5]])).print();  // Предсказание: примерно 10

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


Работа с батчами и итерациями

Для больших наборов данных обучение проводится батчами. TensorFlow.js позволяет удобно выбирать подмножества:

const batchSize = 2;
for (let i = 0; i < xTrain.shape[0]; i += batchSize) {
    const xBatch = xTrain.slice([i, 0], [batchSize, -1]);
    const yBatch = yTrain.slice([i, 0], [batchSize, -1]);
    optimizer.minimize(() => loss(linear(xBatch), yBatch));
}
  • slice извлекает подмассив тензора.
  • Это позволяет эффективно использовать GPU и избегать переполнения памяти.

Расширение моделей

Без Layers API можно реализовать сложные архитектуры, включая несколько скрытых слоев, рекуррентные сети, свёрточные операции:

function twoLayerNN(x) {
    const h = x.matMul(w1).add(b1).relu();
    return h.matMul(w2).add(b2);
}

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


TensorFlow.js без Layers API представляет собой мощный инструмент для детального контроля над обучением нейронных сетей, предоставляя прямой доступ к тензорам, операциям, градиентам и оптимизаторам. Этот подход идеально подходит для исследования нестандартных архитектур и глубокого понимания механики работы нейросетей.