TensorFlow.js запускает машинное обучение в браузере

TensorFlow

[перевод] сБоюсь 1-solve.byte IMG.com/to S-talent-i-he 2…

В этом посте объясняется, как запускать машинное обучение в браузере с помощью TensorFlow.js. Он охватывает API TensorFlow.js для обучения моделей, передачи обучения и функций прогнозирования. Здесь представлена ​​демонстрация приложения, которая помогает прогнозировать время ожидания выполнения бизнес-отчета.

Я реализовал приложение, которое включает использование API TensorFlow.js. Сначала я познакомлю вас с его возможностями, а затем углублюсь в детали реализации. Приложение реализует вариант использования прогнозирования времени выполнения бизнес-отчета (на JavaScript), как описано в моей предыдущей статье.Отчет о прогнозировании времени выполнения с помощью Keras и TensorFlowЭто объясняется в . Для обучения модели я использовал 50 эпох (данные в пакетах по 10), а скорость обучения была установлена ​​​​на 0,001. Нейронная сеть основана на двух слоях обработки и одном слое вывода. Процесс обучения модели выполняется в браузере:

Приложение развернуто и доступно здесь:regressiontfjs-node.herokuapp.com/

Обученная модель может прогнозировать ожидаемое время ожидания выполнения бизнес-отчета. Реализация сделана на основе пояснений и материалов этой книги -«Глубокое обучение с помощью JavaScript»и пример множественной регрессии -«Бостонское жилье».

Особенность

Когда вы открываете приложение в первый раз, вам необходимо обучить модель. После обучения модели она будет сохранена локальноindexeddb. После повторного открытия браузера модель изменится сindexeddbостается доступным, вы можете использовать его повторно или обучить новую модель (ранее сохраненные модели будут заменены). После завершения процесса обучения он выведет набор параметров, показывающих, как работает модель:

  • Базовые потери: это среднеквадратическая ошибка, рассчитанная на основе данных обучения. Значение квадратного корня: 237 секунд. Это означает, что если мы всегда предполагаем среднее значение прогнозов, диапазон ошибок составит 237 секунд.
  • Среднее время: среднее время, рассчитанное для обучающих данных.
  • Окончательная потеря поезда: потеря, рассчитанная во время обучения.
  • Окончательная потеря набора проверки: потеря, вычисленная во время обучения путем проверки
  • Потеря тестового набора: потеря, рассчитанная для данных обучения для данных обучения.
  • Прогноз выключен на (сек): значение квадратного корня потерь тестового набора. Это указывает на способность модели ошибаться. Эта модель хорошо тренируется с ошибкой всего 4,5 секунды, что лучше, чем базовая потеря.

Запуск функции прогнозирования для следующих данных — результат 514 секунд:

Измените временной интервал на PM, и расчетное время увеличится до 573 секунд. Это означает, что модель правильно обучена на обучающих данных, сообщая о более длительном времени выполнения во второй половине дня:

Увеличьте количество параметров. Чем больше параметров, тем меньше данных можно обработать — отчет должен работать быстрее. Модель подтверждает это предсказанием, которое вернет более быстрое время:

Изменим идентификатор отчета и количество параметров. Примерное время наблюдения:

Теперь мы проведем перенос обучения, переобучив модель с новой целью (в этом примере я использую только одну строку данных для простоты, но я рекомендую переобучить модели, для которых требуется несколько строк данных). вызвать метод поезда - он должен работать очень быстро. Выпадение тестового набора может быть хуже, чем на основе исходного обучения (это упражнение подходит), потому что мы используем данные предыдущего тестового набора, которые не связаны напрямую с новой целью. Новая цель — 400 секунд, идентификатор отчета = 1 (при условии, что время конкретного пользователя отличается от времени обучения, и этот пользователь может обновить модель). Результат переобучения (когда обучаем поверх существующей модели - трансферное обучение):

После переобучения модели запустите прогнозы с теми же параметрами, что и раньше — вы увидите, что прогнозы будут скорректированы в соответствии с целями, которые мы использовали для переобучения:

Теперь измените идентификатор отчета на идентификатор, который использовался изначально, и измените количество параметров отчета и временной интервал на исходные значения. Вы увидите, что теперь мы прогнозируем относительно более короткое время из-за недавней переобучения модели, ориентированной на более короткое время:

Попробуйте изменить параметры и посмотрите на результаты.

Практичный

Структура приложения очень проста. Вся логика вappController.jsреализовано в.UIсуществуетindex.htmlреализовано в. Веб-приложение черезOracle JavaScriptбиблиотека —Oracle JETосуществленный. Чтобы запустить приложение локально, выполните следующие два шага:

  • Установить с помощью NPMOracle JET:npm install -g @ oracle / ojet-cli
  • Заходим в приложение и запускаем:ojet restore
  • Запустите приложение:ojet服务

appController.jsСлушатель определен в . Этот прослушиватель вызывается при загрузке приложения и отвечает за загрузку данных, преобразование их в тензор и вычисление базовой линии. данные через вспомогательные модулиdata.jsнагрузка. я используюPapaparseбиблиотека для разбораCSV. Исходный набор данных основан на четырех столбцах:

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

var features = results['data'].map(report => ({
 report_params: parseFloat(report.report_params),
 report_1: parseFloat(report.report_id) === 1 ? 1 : 0,
 report_2: parseFloat(report.report_id) === 2 ? 1 : 0,
 report_3: parseFloat(report.report_id) === 3 ? 1 : 0,
 report_4: parseFloat(report.report_id) === 4 ? 1 : 0,
 report_5: parseFloat(report.report_id) === 5 ? 1 : 0,
 day_morning: parseFloat(report.day_part) === 1 ? 1 : 0,
 day_midday: parseFloat(report.day_part) === 2 ? 1 : 0,
 day_afternoon: parseFloat(report.day_part) === 3 ? 1 : 0,
}));

Такое преобразование данных способствует более точному обучению. Я использую 1200 строк данных для обучения и 300 строк для тестирования. Обязательно перетасуйте свои данные, прежде чем разбивать их на наборы данных для обучения и тестирования. я используюhelperметод, взятый изBoston HousingПРИЛОЖЕНИЕ. использоватьTensorFlow.jsфункцияtensor2dПреобразование массива данных в тензор:

tensors.rawTrainFeatures = tf.tensor2d(dataHelper.trainFeatures);
tensors.trainTarget = tf.tensor2d(dataHelper.trainTarget);
tensors.rawTestFeatures = tf.tensor2d(dataHelper.testFeatures);
tensors.testTarget = tf.tensor2d(dataHelper.testTarget);

TensorFlow.jsМодель состоит из двух слоев обработки и выходного слоя для возврата прогнозируемых значений:

const model = tf.sequential();
model.add(tf.layers.dense({
 inputShape: [dataHelper.trainFeatures[0].length],
 units: 25,
 activation: 'sigmoid',
 kernelInitializer: 'leCunNormal'
}));
model.add(tf.layers.dense({
 units: 25,
 activation: 'sigmoid',
 kernelInitializer: 'leCunNormal'
}));
model.add(tf.layers.dense({ units: 1 }));

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

model.compile({ optimizer: tf.train.sgd(LEARNING_RATE), loss: 'meanSquaredError' });
await model.fit(tensors.trainFeatures, tensors.trainTarget, {
 batchSize: BATCH_SIZE,
 epochs: NUM_EPOCHS,
 shuffle: true,
 validationSplit: 0.2,
 callbacks: {
  onEpochEnd: async (epoch, logs) => {

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

result = model.evaluate(tensors.testFeatures, tensors.testTarget, { batchSize: BATCH_SIZE });
testLoss = result.dataSync()[0];

Наконец, сохраните модель. Существует несколько вариантов сохранения модели, и вы даже можете отправить ее на сервер. В этом примере я сохраняю модель локально в браузере.indexeddbсередина:

await model.save('indexeddb://report-exec-time-model');

После обучения модели мы можем запустить функцию прогнозирования. я получаю отindexeddbСохраненное состояние загружает модель, создает входные тензоры и выполняетTensorFlow.jsпредсказывать:

model = await tf.loadLayersModel('indexeddb://report-exec-time-model');
input = [{
 report_id: parseFloat(self.reportIdP()),
 report_params: self.reportParamsP(),
 day_part: parseFloat(self.reportExecSlotP())
}];
convertInputToTensor(input);
res = model.predict(tensors.inputFeatures);
score = res.dataSync()[0];

Заключительный шаг — перенос обучения, переобучение существующей модели с дополнительными данными (в данном случае данные имеют ту же форму и мы обучаемся для новой цели). Чтобы сделать переобучение более эффективным, мы должны заморозить некоторые слои исходной модели. Таким образом, ранее обученные веса модели не будут затронуты, и обучение модели будет выполняться быстрее. Обычно вы должны тренироваться с несколькими новыми элементами, в этом случае я бы тренировался с одним новым элементом для простоты. Слои модели можно заморозить, установив для обучаемого свойства значение false:

model = await tf.loadLayersModel('indexeddb://report-exec-time-model');
model.layers[0].trainable = false;
model.compile({
 loss: 'meanSquaredError',
 optimizer: tf.train.sgd(LEARNING_RATE)
});
model.summary();
input = [{
 report_id: parseFloat(self.reportId()),
 report_params: self.reportParams(),
 day_part: parseFloat(self.reportExecSlot()),
 exec_time: self.reportExecTime()
}];
convertInputToTensor(input, 'Y');

Запустите функцию, подходящую с новыми данными поверх существующей модели:

await model.fit(tensors.inputFeatures, tensors.inputTarget, {
 batchSize: BATCH_SIZE,
 epochs: NUM_EPOCHS,
 shuffle: true,
 callbacks: {
  onEpochEnd: async (epoch, logs) => {