Модели обучения

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

В TensorFlow.js есть два способа обучения модели машинного обучения:

  1. используя API слоев с LayersModel.fit() или LayersModel.fitDataset() .
  2. используя Core API с Optimizer.minimize() .

Сначала мы рассмотрим Layers API, который представляет собой API более высокого уровня для построения и обучения моделей. Затем мы покажем, как обучить ту же модель с помощью Core API.

Введение

Модель машинного обучения — это функция с обучаемыми параметрами, которая сопоставляет входные данные с желаемыми выходными данными. Оптимальные параметры получаются путем обучения модели на данных.

Обучение включает в себя несколько этапов:

  • Получение пакета данных в модель.
  • Попросите модель сделать прогноз.
  • Сравнение этого прогноза с «истинным» значением.
  • Принятие решения о том, насколько сильно нужно изменить каждый параметр, чтобы модель могла лучше прогнозировать будущее для этой партии.

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

Параметры модели

Давайте определим простую двухслойную модель с помощью Layers API:

const model = tf.sequential({
 layers: [
   tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
   tf.layers.dense({units: 10, activation: 'softmax'}),
 ]
});

Внутри моделей есть параметры (часто называемые весами ), которые можно изучить путем обучения на данных. Давайте напечатаем названия гирь, связанных с этой моделью, и их формы:

model.weights.forEach(w => {
 console.log(w.name, w.shape);
});

Мы получаем следующий результат:

> dense_Dense1/kernel [784, 32]
> dense_Dense1/bias [32]
> dense_Dense2/kernel [32, 10]
> dense_Dense2/bias [10]

Всего утяжелителей 4, по 2 на каждый плотный слой. Это ожидаемо, поскольку плотные слои представляют собой функцию, которая отображает входной тензор x в выходной тензор y с помощью уравнения y = Ax + b где A (ядро) и b (смещение) являются параметрами плотного слоя.

ПРИМЕЧАНИЕ. По умолчанию плотные слои включают смещение, но вы можете исключить его, указав {useBias: false} в параметрах при создании плотного слоя.

model.summary() — полезный метод, если вы хотите получить обзор вашей модели и увидеть общее количество параметров:

Слой (тип) Выходная форма Парам #
плотный_Dense1 (Плотный) [ноль, 32] 25120
плотный_Dense2 (Плотный) [ноль, 10] 330
Всего параметров: 25450
Обучаемые параметры: 25450
Необучаемые параметры: 0

Каждый вес в модели определяется объектом Variable . В TensorFlow.js Variable представляет собой Tensor с плавающей запятой с одним дополнительным методом assign() , используемым для обновления ее значений. API слоев автоматически инициализирует веса, используя лучшие практики. Для демонстрации мы могли бы перезаписать веса, вызвав метод assign() для базовых переменных:

model.weights.forEach(w => {
  const newVals = tf.randomNormal(w.shape);
  // w.val is an instance of tf.Variable
  w.val.assign(newVals);
});

Оптимизатор, потери и метрика

Прежде чем приступать к каким-либо тренировкам, вам необходимо определиться с тремя вещами:

  1. Оптимизатор . Задача оптимизатора — решить, насколько изменить каждый параметр модели с учетом текущего прогноза модели. При использовании Layers API вы можете предоставить либо строковый идентификатор существующего оптимизатора (например, 'sgd' или 'adam' ), либо экземпляр класса Optimizer .
  2. Функция потерь . Цель, которую модель попытается минимизировать. Его цель — дать одно числовое значение того, «насколько ошибочным» было предсказание модели. Потери рассчитываются для каждого пакета данных, чтобы модель могла обновить свои веса. При использовании Layers API вы можете предоставить либо строковый идентификатор существующей функции потерь (например, 'categoricalCrossentropy' ), либо любую функцию, которая принимает прогнозируемое и истинное значение и возвращает потерю. См. список доступных потерь в нашей документации API.
  3. Список метрик. Подобно потерям, метрики вычисляют одно число, суммирующее, насколько хорошо работает наша модель. Метрики обычно вычисляются для всех данных в конце каждой эпохи. По крайней мере, мы хотим следить за тем, чтобы наши потери со временем снижались. Однако нам часто нужен более удобный для человека показатель, такой как точность. При использовании API слоев вы можете предоставить либо строковый идентификатор существующей метрики (например, 'accuracy' ), либо любую функцию, которая принимает прогнозируемое и истинное значение и возвращает оценку. См. список доступных метрик в нашей документации API.

Приняв решение, скомпилируйте LayersModel , вызвав model.compile() с предоставленными параметрами:

model.compile({
  optimizer: 'sgd',
  loss: 'categoricalCrossentropy',
  metrics: ['accuracy']
});

Во время компиляции модель выполнит некоторую проверку, чтобы убедиться, что выбранные вами параметры совместимы друг с другом.

Обучение

Есть два способа обучения LayersModel :

  • Использование model.fit() и предоставление данных в виде одного большого тензора.
  • Использование model.fitDataset() и предоставление данных через объект Dataset .

модель.подгонка()

Если ваш набор данных помещается в основную память и доступен в виде одного тензора, вы можете обучить модель, вызвав метод fit() :

// Generate dummy data.
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);

function onBatchEnd(batch, logs) {
  console.log('Accuracy', logs.acc);
}

// Train for 5 epochs with batch size of 32.
model.fit(data, labels, {
   epochs: 5,
   batchSize: 32,
   callbacks: {onBatchEnd}
 }).then(info => {
   console.log('Final accuracy', info.history.acc);
 });

Под капотом model.fit() может многое для нас сделать:

  • Разделяет данные на обучающий и проверочный набор и использует проверочный набор для измерения прогресса во время обучения.
  • Перемешивает данные, но только после разделения. На всякий случай вам следует предварительно перетасовать данные перед их передачей в fit() .
  • Разбивает большой тензор данных на меньшие тензоры размера batchSize.
  • ВызываетOptimizer.minimize optimizer.minimize() при вычислении потерь модели по отношению к пакету данных.
  • Он может уведомлять вас о начале и конце каждой эпохи или пакета. В нашем случае мы получаем уведомление в конце каждого пакета с помощью опции callbacks.onBatchEnd . Другие варианты включают: onTrainBegin , onTrainEnd , onEpochBegin , onEpochEnd и onBatchBegin .
  • Он уступает основному потоку, чтобы гарантировать своевременную обработку задач, поставленных в очередь в цикле событий JS.

Для получения дополнительной информации см. документацию fit() . Обратите внимание: если вы решите использовать Core API, вам придется реализовать эту логику самостоятельно.

модель.fitDataset()

Если ваши данные не полностью помещаются в памяти или передаются в потоковом режиме, вы можете обучить модель, вызвав fitDataset() , который принимает объект Dataset . Вот тот же обучающий код, но с набором данных, который обертывает функцию-генератор:

function* data() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomNormal([784]);
 }
}

function* labels() {
 for (let i = 0; i < 100; i++) {
   // Generate one sample at a time.
   yield tf.randomUniform([10]);
 }
}

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// We zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

// Train the model for 5 epochs.
model.fitDataset(ds, {epochs: 5}).then(info => {
 console.log('Accuracy', info.history.acc);
});

Дополнительную информацию о наборах данных см. в документации model.fitDataset() .

Прогнозирование новых данных

После обучения модели вы можете вызвать model.predict() , чтобы сделать прогнозы на основе невидимых данных:

// Predict 3 random samples.
const prediction = model.predict(tf.randomNormal([3, 784]));
prediction.print();

Основной API

Ранее мы упоминали, что существует два способа обучения модели машинного обучения в TensorFlow.js.

Общее практическое правило — сначала попытаться использовать Layers API, поскольку он создан по образцу широко распространенного Keras API. Layers API также предлагает различные готовые решения, такие как инициализация веса, сериализация модели, обучение мониторингу, переносимость и проверка безопасности.

Вы можете использовать Core API всякий раз, когда:

  • Вам нужна максимальная гибкость и контроль.
  • И вам не нужна сериализация, или вы можете реализовать свою собственную логику сериализации.

Дополнительные сведения об этом API см. в разделе «Основной API» руководства «Модели и слои» .

Та же модель, что и выше, написанная с использованием Core API, выглядит следующим образом:

// The weights and biases for the two dense layers.
const w1 = tf.variable(tf.randomNormal([784, 32]));
const b1 = tf.variable(tf.randomNormal([32]));
const w2 = tf.variable(tf.randomNormal([32, 10]));
const b2 = tf.variable(tf.randomNormal([10]));

function model(x) {
  return x.matMul(w1).add(b1).relu().matMul(w2).add(b2);
}

В дополнение к API слоев, API данных также бесперебойно работает с Core API. Давайте повторно используем набор данных, который мы определили ранее в разделе model.fitDataset() , который выполняет за нас перетасовку и пакетную обработку:

const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// Zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);

Давайте обучим модель:

const optimizer = tf.train.sgd(0.1 /* learningRate */);
// Train for 5 epochs.
for (let epoch = 0; epoch < 5; epoch++) {
  await ds.forEachAsync(({xs, ys}) => {
    optimizer.minimize(() => {
      const predYs = model(xs);
      const loss = tf.losses.softmaxCrossEntropy(ys, predYs);
      loss.data().then(l => console.log('Loss', l));
      return loss;
    });
  });
  console.log('Epoch', epoch);
}

Приведенный выше код представляет собой стандартный рецепт обучения модели с помощью Core API:

  • Цикл по количеству эпох.
  • Внутри каждой эпохи перебирайте пакеты данных. При использовании Dataset dataset.forEachAsync() — это удобный способ перебирать пакеты.
  • Для каждого пакета вызовитеOptimer.minimize optimizer.minimize(f) , который выполняет f и минимизирует его выходные данные путем вычисления градиентов по отношению к четырем переменным, которые мы определили ранее.
  • f вычисляет потери. Он вызывает одну из предопределенных функций потерь, используя прогноз модели и истинное значение.