TensorFlow.js: создайте свою собственную «обучаемую машину»; использование трансферного обучения с TensorFlow.js

1. Прежде чем начать

За последние несколько лет использование модели TensorFlow.js выросло в геометрической прогрессии, и многие разработчики JavaScript теперь стремятся взять существующие современные модели и переобучить их для работы с пользовательскими данными, уникальными для их отрасли. Процесс взятия существующей модели (часто называемой базовой моделью) и ее использования в аналогичной, но другой области известен как трансферное обучение.

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

В этой лаборатории кода показано, как создать веб-приложение с чистого листа, воссоздавая популярный веб-сайт Google « Обучаемая машина ». Веб-сайт позволяет вам создать функциональное веб-приложение, которое любой пользователь сможет использовать для распознавания пользовательского объекта с помощью всего лишь нескольких примеров изображений со своей веб-камеры. Веб-сайт намеренно сделан минимальным, чтобы вы могли сосредоточиться на аспектах машинного обучения в этой лаборатории кода. Однако, как и в случае с исходным веб-сайтом Teachable Machine, здесь есть много возможностей применить свой существующий опыт веб-разработчика для улучшения UX.

Предварительные условия

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

  • Для этой лабораторной работы предполагается базовое знакомство с TensorFlow.js, HTML5, CSS и JavaScript.

Если вы новичок в Tensorflow.js, рассмотрите возможность сначала пройти этот бесплатный курс с нуля до героя , который не предполагает никакого опыта работы с машинным обучением или TensorFlow.js и научит вас всему, что вам нужно знать, небольшими шагами.

Что вы узнаете

  • Что такое TensorFlow.js и почему вам следует использовать его в своем следующем веб-приложении.
  • Как создать упрощенную веб-страницу HTML/CSS/JS, повторяющую взаимодействие с пользователем Teachable Machine.
  • Как использовать TensorFlow.js для загрузки предварительно обученной базовой модели, в частности MobileNet, для создания функций изображения, которые можно использовать в трансферном обучении.
  • Как собрать данные с веб-камеры пользователя для нескольких классов данных, которые вы хотите распознать.
  • Как создать и определить многослойный перцептрон, который принимает особенности изображения и учится с их помощью классифицировать новые объекты.

Давайте займемся взломом...

Что вам понадобится

  • Для отслеживания предпочтительнее использовать учетную запись Glitch.com, или вы можете использовать среду веб-сервиса, которую вам удобно редактировать и запускать самостоятельно.

2. Что такое TensorFlow.js?

54e81d02971f53e8.png

TensorFlow.js — это библиотека машинного обучения с открытым исходным кодом , которая может работать везде, где доступен JavaScript. Он основан на оригинальной библиотеке TensorFlow, написанной на Python , и призван воссоздать этот опыт разработки и набор API-интерфейсов для экосистемы JavaScript.

Где его можно использовать?

Благодаря переносимости JavaScript теперь вы можете писать на одном языке и с легкостью выполнять машинное обучение на всех следующих платформах:

  • Клиентская часть в веб-браузере с использованием ванильного JavaScript.
  • Серверная часть и даже устройства IoT, такие как Raspberry Pi, использующие Node.js.
  • Настольные приложения, использующие Electron
  • Нативные мобильные приложения с использованием React Native

TensorFlow.js также поддерживает несколько бэкэндов в каждой из этих сред (фактические аппаратные среды, в которых он может выполняться, например, ЦП или WebGL. «Бэкэнд» в этом контексте не означает среду на стороне сервера — бэкэнд для выполнения). может быть, например, на стороне клиента в WebGL), чтобы обеспечить совместимость, а также обеспечить быструю работу. В настоящее время TensorFlow.js поддерживает:

  • Выполнение WebGL на видеокарте устройства (GPU) — это самый быстрый способ запуска больших моделей (размером более 3 МБ) с ускорением GPU.
  • Выполнение веб-сборки (WASM) на ЦП — для повышения производительности ЦП на всех устройствах, включая, например, мобильные телефоны старшего поколения. Это лучше подходит для моделей меньшего размера (размером менее 3 МБ), которые на самом деле могут работать на процессоре быстрее с WASM, чем с WebGL, из-за затрат на загрузку контента в графический процессор.
  • Выполнение ЦП — резервный вариант, если ни одна из других сред недоступна. Это самый медленный из трех, но он всегда рядом.

Примечание. Вы можете выбрать принудительное использование одного из этих бэкэндов, если знаете, на каком устройстве вы будете выполняться, или вы можете просто позволить TensorFlow.js решить за вас, если вы не укажете это.

Суперспособности клиентской стороны

Запуск TensorFlow.js в веб-браузере на клиентском компьютере может дать несколько преимуществ, которые стоит учитывать.

Конфиденциальность

Вы можете обучать и классифицировать данные на клиентском компьютере, даже не отправляя данные на сторонний веб-сервер. Могут быть случаи, когда это может быть требованием соблюдения местных законов, таких как, например, GDPR, или при обработке любых данных, которые пользователь может захотеть сохранить на своем компьютере и не отправлять третьей стороне.

Скорость

Поскольку вам не нужно отправлять данные на удаленный сервер, вывод (классификация данных) может выполняться быстрее. Более того, у вас есть прямой доступ к датчикам устройства, таким как камера, микрофон, GPS, акселерометр и т. д., если пользователь предоставит вам доступ.

Охват и масштабирование

Одним щелчком мыши любой человек в мире может щелкнуть ссылку, которую вы ему отправляете, открыть веб-страницу в своем браузере и использовать то, что вы создали. Нет необходимости в сложной настройке Linux на стороне сервера с драйверами CUDA и многим другим, просто используйте систему машинного обучения.

Расходы

Отсутствие серверов означает, что единственное, за что вам нужно платить, — это CDN для размещения ваших файлов HTML, CSS, JS и моделей. Стоимость CDN намного дешевле, чем круглосуточная работа сервера (возможно, с подключенной видеокартой).

Возможности серверной части

Использование реализации TensorFlow.js на Node.js обеспечивает следующие функции.

Полная поддержка CUDA

На стороне сервера для ускорения видеокарты необходимо установить драйверы NVIDIA CUDA, чтобы TensorFlow мог работать с видеокартой (в отличие от браузера, который использует WebGL — установка не требуется). Однако при полной поддержке CUDA вы можете полностью использовать возможности видеокарты более низкого уровня, что приводит к сокращению времени обучения и вывода. Производительность находится на одном уровне с реализацией Python TensorFlow, поскольку они оба используют один и тот же бэкэнд C++.

Размер модели

Что касается передовых моделей, полученных в ходе исследований, вы можете работать с очень большими моделями, возможно, размером в гигабайты. Эти модели в настоящее время невозможно запустить в веб-браузере из-за ограничений использования памяти для каждой вкладки браузера. Для запуска этих более крупных моделей вы можете использовать Node.js на своем собственном сервере с аппаратными характеристиками, необходимыми для эффективного запуска такой модели.

Интернет вещей

Node.js поддерживается на популярных одноплатных компьютерах, таких как Raspberry Pi , что, в свою очередь, означает, что вы можете выполнять модели TensorFlow.js и на таких устройствах.

Скорость

Node.js написан на JavaScript, а это означает, что он получает преимущества от своевременной компиляции. Это означает, что вы часто можете увидеть прирост производительности при использовании Node.js, поскольку он будет оптимизирован во время выполнения, особенно для любой предварительной обработки, которую вы можете выполнять. Отличный пример этого можно увидеть в этом тематическом исследовании , которое показывает, как компания Hugging Face использовала Node.js для двукратного повышения производительности своей модели обработки естественного языка.

Теперь вы понимаете основы TensorFlow.js, где он может работать, а также некоторые его преимущества, давайте начнем делать с ним полезные вещи!

3. Передача обучения

Что такое трансферное обучение?

Трансферное обучение предполагает использование уже полученных знаний для изучения другой, но похожей вещи.

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

e28070392cd4afb9.png

В зависимости от того, где вы находитесь, есть вероятность, что вы раньше не видели этот тип дерева.

Тем не менее, если я попрошу вас сказать мне, есть ли ивы на новом изображении ниже, вы, вероятно, сможете заметить их довольно быстро, даже несмотря на то, что они находятся под другим углом и немного отличаются от исходного, который я вам показывал.

d9073a0d5df27222.png

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

Аналогично, если у вас есть модель машинного обучения, которая уже обучена в определенной области, например распознавание изображений, вы можете повторно использовать ее для выполнения другой, но связанной задачи.

Вы можете сделать то же самое с помощью продвинутой модели, такой как MobileNet, которая является очень популярной исследовательской моделью и может выполнять распознавание изображений на 1000 различных типах объектов. От собак до автомобилей — оно обучалось на огромном наборе данных, известном как ImageNet , который содержит миллионы помеченных изображений.

На этой анимации вы можете увидеть огромное количество слоев в этой модели MobileNet V1:

7d4e1e35c1a89715.gif

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

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

baf4e3d434576106.png

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

369a8a9041c6917d.png

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

На диаграмме выше эта гипотетическая модель была обучена на цифрах, поэтому, возможно, то, что мы узнали о цифрах, можно применить и к таким буквам, как a, b и c.

Итак, теперь вы можете добавить новую классификационную главу, которая вместо этого пытается предсказать a, b или c, как показано:

db97e5e60ae73bbd.png

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

Этот процесс известен как трансферное обучение и именно это делает Teachable Machine за кулисами.

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

Но как получить доступ к частям модели? Чтобы узнать, перейдите в следующий раздел.

4. TensorFlow Hub — базовые модели

Найдите подходящую базовую модель для использования

Для более продвинутых и популярных исследовательских моделей, таких как MobileNet, вы можете перейти в хаб TensorFlow , а затем отфильтровать модели, подходящие для TensorFlow.js, которые используют архитектуру MobileNet v3, чтобы найти результаты, подобные показанным здесь:

c5dc1420c6238c14.png

Обратите внимание, что некоторые из этих результатов относятся к типу «классификация изображений» (подробно указано в левом верхнем углу каждого результата карточки модели), а другие относятся к типу «вектор признаков изображения».

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

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

Следующее, что нужно проверить для конкретной интересующей базовой модели, — это формат TensorFlow.js, в котором выпущена модель. Если вы откроете страницу одной из этих моделей MobileNet v3 с вектором функций, вы увидите в документации JS, что она имеет форму графовой модели, основанной на фрагменте кода примера в документации, который использует tf.loadGraphModel() .

f97d903d2e46924b.png

Также следует отметить, что если вы найдете модель в формате слоев вместо формата графика, вы можете выбрать, какие слои заморозить, а какие разморозить для обучения. Это может оказаться очень полезным при создании модели для новой задачи, которую часто называют «моделью переноса». Однако на данный момент для этого руководства вы будете использовать тип графовой модели по умолчанию, в качестве которого развертывается большинство моделей TF Hub. Чтобы узнать больше о работе с моделями слоев, ознакомьтесь с курсом TensorFlow.js с нуля до героя .

Преимущества трансферного обучения

Каковы преимущества использования трансферного обучения вместо обучения всей архитектуры модели с нуля?

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

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

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

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

Хорошо! Теперь, когда вы знаете суть трансферного обучения, пришло время создать свою собственную версию обучаемой машины. Давайте начнем!

5. Настройтесь на кодирование

Что вам понадобится

  • Современный веб-браузер.
  • Базовые знания HTML, CSS, JavaScript и Chrome DevTools (просмотр вывода консоли).

Давайте займемся кодированием

Шаблонные шаблоны для начала были созданы для Glitch.com или Codepen.io . Вы можете просто клонировать любой шаблон в качестве базового состояния для этой лаборатории кода всего одним щелчком мыши.

В Glitch нажмите кнопку « remix this», чтобы создать его форк и создать новый набор файлов, которые вы сможете редактировать.

Альтернативно, в Codepen нажмите « вилка» в правом нижнем углу экрана.

Этот очень простой скелет предоставляет вам следующие файлы:

  • HTML-страница (index.html)
  • Таблица стилей (style.css)
  • Файл для написания нашего кода JavaScript (script.js)

Для вашего удобства в HTML-файл добавлен импорт библиотеки TensorFlow.js. Это выглядит так:

index.html

<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>

Альтернатива: используйте предпочитаемый вами веб-редактор или работайте локально.

Если вы хотите загрузить код и работать локально или в другом онлайн-редакторе, просто создайте 3 файла, указанные выше, в одном каталоге, скопируйте и вставьте код из нашего шаблона Glitch в каждый из них.

6. HTML-шаблон приложения

С чего мне начать?

Для всех прототипов требуются базовые HTML-шаблоны, на которых вы можете визуализировать свои результаты. Настройте это сейчас. Вы собираетесь добавить:

  • Заголовок страницы.
  • Немного описательного текста.
  • Статусный абзац.
  • Видео для трансляции с веб-камеры, когда оно будет готово.
  • Несколько кнопок для запуска камеры, сбора данных или сброса настроек.
  • Импорт файлов TensorFlow.js и JS, которые вы напишете позже.

Откройте index.html и вставьте в существующий код следующее, чтобы настроить вышеуказанные функции:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Transfer Learning - TensorFlow.js</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Import the webpage's stylesheet -->
    <link rel="stylesheet" href="/style.css">
  </head>  
  <body>
    <h1>Make your own "Teachable Machine" using Transfer Learning with MobileNet v3 in TensorFlow.js using saved graph model from TFHub.</h1>
    
    <p id="status">Awaiting TF.js load</p>
    
    <video id="webcam" autoplay muted></video>
    
    <button id="enableCam">Enable Webcam</button>
    <button class="dataCollector" data-1hot="0" data-name="Class 1">Gather Class 1 Data</button>
    <button class="dataCollector" data-1hot="1" data-name="Class 2">Gather Class 2 Data</button>
    <button id="train">Train &amp; Predict!</button>
    <button id="reset">Reset</button>

    <!-- Import TensorFlow.js library -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js" type="text/javascript"></script>

    <!-- Import the page's JavaScript to do some stuff -->
    <script type="module" src="/script.js"></script>
  </body>
</html>

Сломай

Давайте разберем часть приведенного выше HTML-кода, чтобы выделить некоторые ключевые моменты, которые вы добавили.

  • Вы добавили тег <h1> для заголовка страницы вместе с тегом <p> с идентификатором «статус», куда вы будете печатать информацию, поскольку для просмотра результатов вы используете разные части системы.
  • Вы добавили элемент <video> с идентификатором «веб-камера», для которого позже вы будете отображать поток веб-камеры.
  • Вы добавили 5 элементов <button> . Первый, с идентификатором «enableCam», включает камеру. Следующие две кнопки имеют класс dataCollector, который позволяет собирать примеры изображений для объектов, которые вы хотите распознать. Код, который вы напишете позже, будет разработан таким образом, что вы сможете добавить любое количество этих кнопок, и они будут автоматически работать по назначению.

Обратите внимание, что эти кнопки также имеют специальный определяемый пользователем атрибут data-1hot с целочисленным значением, начинающимся с 0 для первого класса. Это числовой индекс, который вы будете использовать для представления данных определенного класса. Индекс будет использоваться для правильного кодирования выходных классов с помощью числового представления вместо строки, поскольку модели ML могут работать только с числами.

Существует также атрибут data-name, который содержит удобочитаемое имя, которое вы хотите использовать для этого класса, что позволяет предоставить пользователю более значимое имя вместо числового значения индекса из горячей кодировки 1.

Наконец, у вас есть кнопка обучения и сброса, позволяющая начать процесс обучения после сбора данных или соответственно сбросить приложение.

  • Вы также добавили 2 импорта <script> . Один для TensorFlow.js, а другой для script.js, который вы вскоре определите.

7. Добавьте стиль

Значения элемента по умолчанию

Добавьте стили для только что добавленных HTML-элементов, чтобы обеспечить их правильное отображение. Вот несколько стилей, которые правильно добавляются для правильного расположения и размера элементов. Ничего особенного. Вы, конечно, могли бы добавить к этому позже, чтобы сделать UX еще лучше, как вы видели в обучающем видео о машине.

стиль.css

body {
  font-family: helvetica, arial, sans-serif;
  margin: 2em;
}

h1 {
  font-style: italic;
  color: #FF6F00;
}


video {
  clear: both;
  display: block;
  margin: 10px;
  background: #000000;
  width: 640px;
  height: 480px;
}

button {
  padding: 10px;
  float: left;
  margin: 5px 3px 5px 10px;
}

.removed {
  display: none;
}

#status {
  font-size:150%;
}

Большой! Это все, что вам нужно. Если вы просмотрите результат прямо сейчас, он должен выглядеть примерно так:

81909685d7566dcb.png

8. JavaScript: ключевые константы и слушатели

Определите ключевые константы

Сначала добавьте несколько ключевых констант, которые вы будете использовать в приложении. Начните с замены содержимого script.js этими константами:

скрипт.js

const STATUS = document.getElementById('status');
const VIDEO = document.getElementById('webcam');
const ENABLE_CAM_BUTTON = document.getElementById('enableCam');
const RESET_BUTTON = document.getElementById('reset');
const TRAIN_BUTTON = document.getElementById('train');
const MOBILE_NET_INPUT_WIDTH = 224;
const MOBILE_NET_INPUT_HEIGHT = 224;
const STOP_DATA_GATHER = -1;
const CLASS_NAMES = [];

Давайте разберемся, для чего они нужны:

  • STATUS просто содержит ссылку на тег абзаца, в который вы будете записывать обновления статуса.
  • VIDEO содержит ссылку на HTML-элемент видео, который будет отображать изображение с веб-камеры.
  • ENABLE_CAM_BUTTON , RESET_BUTTON и TRAIN_BUTTON захватывают ссылки DOM на все ключевые кнопки со страницы HTML.
  • MOBILE_NET_INPUT_WIDTH и MOBILE_NET_INPUT_HEIGHT определяют ожидаемую входную ширину и высоту модели MobileNet соответственно. Если вы сохраните это в константе в верхней части файла, если вы решите использовать другую версию позже, вам будет проще обновить значения один раз, вместо того, чтобы заменять их во многих разных местах.
  • STOP_DATA_GATHER имеет значение - 1. Здесь сохраняется значение состояния, чтобы вы знали, когда пользователь перестал нажимать кнопку для сбора данных из канала веб-камеры. Давая этому числу более значимое имя, вы сделаете код более читаемым в дальнейшем.
  • CLASS_NAMES выполняет функцию поиска и содержит удобочитаемые имена возможных предсказаний классов. Этот массив будет заполнен позже.

Хорошо, теперь, когда у вас есть ссылки на ключевые элементы, пришло время связать с ними некоторые прослушиватели событий.

Добавьте прослушиватели ключевых событий

Начните с добавления обработчиков событий щелчка к кнопкам клавиш, как показано:

скрипт.js

ENABLE_CAM_BUTTON.addEventListener('click', enableCam);
TRAIN_BUTTON.addEventListener('click', trainAndPredict);
RESET_BUTTON.addEventListener('click', reset);


function enableCam() {
  // TODO: Fill this out later in the codelab!
}


function trainAndPredict() {
  // TODO: Fill this out later in the codelab!
}


function reset() {
  // TODO: Fill this out later in the codelab!
}

ENABLE_CAM_BUTTON — вызывает функцию EnableCam при нажатии.

TRAIN_BUTTON — вызывает trainAndPredict при нажатии.

RESET_BUTTON - сброс вызовов при нажатии.

Наконец, в этом разделе вы можете найти все кнопки, имеющие класс dataCollector, используя document.querySelectorAll() . Это возвращает массив элементов, найденных в документе, которые соответствуют:

скрипт.js

let dataCollectorButtons = document.querySelectorAll('button.dataCollector');
for (let i = 0; i < dataCollectorButtons.length; i++) {
  dataCollectorButtons[i].addEventListener('mousedown', gatherDataForClass);
  dataCollectorButtons[i].addEventListener('mouseup', gatherDataForClass);
  // Populate the human readable names for classes.
  CLASS_NAMES.push(dataCollectorButtons[i].getAttribute('data-name'));
}


function gatherDataForClass() {
  // TODO: Fill this out later in the codelab!
}

Объяснение кода:

Затем вы перебираете найденные кнопки и связываете с каждой по два прослушивателя событий. Один для «mousedown», другой для «mouseup». Это позволяет вам продолжать записывать сэмплы, пока нажата кнопка, что полезно для сбора данных.

Оба события вызывают функцию gatherDataForClass , которую вы определите позже.

На этом этапе вы также можете перенести найденные удобочитаемые имена классов из имени данных атрибута кнопки HTML в массив CLASS_NAMES .

Затем добавьте несколько переменных для хранения ключевых данных, которые будут использоваться позже.

скрипт.js

let mobilenet = undefined;
let gatherDataState = STOP_DATA_GATHER;
let videoPlaying = false;
let trainingDataInputs = [];
let trainingDataOutputs = [];
let examplesCount = [];
let predict = false;

Давайте пройдемся по ним.

Во-первых, у вас есть переменная mobilenet для хранения загруженной модели мобильной сети. Изначально установите для этого параметра значение undefined.

Далее у вас есть переменная с именем gatherDataState . Если нажата кнопка «dataCollector», вместо этого она изменится на 1 горячий идентификатор этой кнопки, как определено в HTML, поэтому вы знаете, какой класс данных вы собираете в данный момент. Изначально для этого параметра установлено значение STOP_DATA_GATHER , чтобы цикл сбора данных, который вы напишете позже, не собирал никаких данных, когда никакие кнопки не нажимаются.

videoPlaying отслеживает, успешно ли загружен и воспроизводится поток веб-камеры и доступен ли он для использования. Изначально для этого параметра установлено значение false поскольку веб-камера не включится, пока вы не нажмете ENABLE_CAM_BUTTON.

Затем определите два массива: trainingDataInputs и trainingDataOutputs . Они сохраняют собранные значения обучающих данных, когда вы нажимаете кнопки «dataCollector» для входных функций, сгенерированных базовой моделью MobileNet, и выходного класса, выбранного соответственно.

Затем определяется последний массив, examplesCount, для отслеживания количества примеров, содержащихся в каждом классе, после того, как вы начнете их добавлять.

Наконец, у вас есть переменная predict , которая управляет циклом прогнозирования. Изначально для этого параметра установлено значение false . Никакие прогнозы не могут иметь место, пока это не станет true позже.

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

9. Загрузите базовую модель MobileNet.

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

скрипт.js

/**
 * Loads the MobileNet model and warms it up so ready for use.
 **/
async function loadMobileNetFeatureModel() {
  const URL = 
    'https://tfhub.dev/google/tfjs-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/default/1';
  
  mobilenet = await tf.loadGraphModel(URL, {fromTFHub: true});
  STATUS.innerText = 'MobileNet v3 loaded successfully!';
  
  // Warm up the model by passing zeros through it once.
  tf.tidy(function () {
    let answer = mobilenet.predict(tf.zeros([1, MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH, 3]));
    console.log(answer.shape);
  });
}

// Call the function immediately to start loading.
loadMobileNetFeatureModel();

В этом коде вы определяете URL , по которому находится загружаемая модель, из документации TFHub.

Затем вы можете загрузить модель с помощью await tf.loadGraphModel() , не забывая присвоить специальному свойству fromTFHub значение true при загрузке модели с этого веб-сайта Google. Это особый случай только при использовании моделей, размещенных в TF Hub, где необходимо установить это дополнительное свойство.

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

Теперь осталось только прогреть модель. В случае с такими более крупными моделями при первом использовании модели может потребоваться некоторое время, чтобы все настроить. Поэтому полезно передавать в модель нули, чтобы избежать ожидания в будущем, когда время может оказаться более критичным.

Вы можете использовать tf.zeros() завернутый в tf.tidy() чтобы гарантировать правильное удаление тензоров с размером пакета 1 и правильной высотой и шириной, которые вы определили в своих константах в начале. Наконец, вы также указываете цветовые каналы, которых в данном случае 3, поскольку модель ожидает изображения RGB.

Затем запишите результирующую форму тензора, возвращенную с помощью answer.shape() чтобы понять размер элементов изображения, которые создает эта модель.

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

Если вы просмотрите предварительный просмотр в реальном времени прямо сейчас, через несколько секунд вы увидите, что текст статуса изменится с «Ожидание загрузки TF.js» на «MobileNet v3 загружен успешно!» как показано ниже. Прежде чем продолжить, убедитесь, что это работает.

a28b734e190afff.png

Вы также можете проверить вывод консоли, чтобы увидеть размер печати выходных функций, которые создает эта модель. После ввода нулей в модель MobileNet вы увидите напечатанную фигуру [1, 1024] . Первый элемент имеет размер пакета 1, и вы можете видеть, что он фактически возвращает 1024 признака, которые затем можно использовать для классификации новых объектов.

10. Определите голову новой модели.

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

скрипт.js

let model = tf.sequential();
model.add(tf.layers.dense({inputShape: [1024], units: 128, activation: 'relu'}));
model.add(tf.layers.dense({units: CLASS_NAMES.length, activation: 'softmax'}));

model.summary();

// Compile the model with the defined optimizer and specify a loss function to use.
model.compile({
  // Adam changes the learning rate over time which is useful.
  optimizer: 'adam',
  // Use the correct loss function. If 2 classes of data, must use binaryCrossentropy.
  // Else categoricalCrossentropy is used if more than 2 classes.
  loss: (CLASS_NAMES.length === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy', 
  // As this is a classification problem you can record accuracy in the logs too!
  metrics: ['accuracy']  
});

Давайте пройдемся по этому коду. Вы начинаете с определения модели tf.sequential, к которой вы добавите слои модели.

Затем добавьте плотный слой в качестве входного слоя к этой модели. Его входная форма равна 1024 поскольку выходные данные функций MobileNet v3 имеют именно такой размер. Вы обнаружили это на предыдущем шаге после прохождения через модель. Этот слой имеет 128 нейронов, которые используют функцию активации ReLU.

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

Следующий слой, который нужно добавить, — это выходной слой. Количество нейронов должно равняться количеству классов, которые вы пытаетесь предсказать. Для этого вы можете использовать CLASS_NAMES.length чтобы узнать, сколько классов вы планируете классифицировать, что равно количеству кнопок сбора данных, найденных в пользовательском интерфейсе. Поскольку это проблема классификации, вы используете активацию softmax на этом выходном слое, которую необходимо использовать при попытке создать модель для решения задач классификации вместо регрессии.

Теперь напечатайте model.summary() , чтобы вывести на консоль обзор вновь определенной модели.

Наконец, скомпилируйте модель, чтобы она была готова к обучению. Здесь для оптимизатора установлено значение adam , и потеря будет либо binaryCrossentropy , если CLASS_NAMES.length равна 2 , либо будет использоваться categoricalCrossentropy , если нужно классифицировать 3 или более классов. Также запрашиваются показатели точности, чтобы их можно было позже отслеживать в журналах в целях отладки.

В консоли вы должны увидеть что-то вроде этого:

22eaf32286fea4bb.png

Обратите внимание, что здесь имеется более 130 тысяч обучаемых параметров. Но поскольку это простой плотный слой обычных нейронов, он будет обучаться довольно быстро.

В качестве действия, которое следует выполнить после завершения проекта, вы можете попробовать изменить количество нейронов в первом слое, чтобы увидеть, насколько низким вы можете его сделать, сохраняя при этом приличную производительность. Часто при машинном обучении приходится методом проб и ошибок найти оптимальные значения параметров, обеспечивающие наилучший компромисс между использованием ресурсов и скоростью.

11. Включите веб-камеру

Теперь пришло время конкретизировать функцию enableCam() которую вы определили ранее. Добавьте новую функцию с именем hasGetUserMedia() , как показано ниже, а затем замените содержимое ранее определенной функции enableCam() соответствующим кодом ниже.

скрипт.js

function hasGetUserMedia() {
  return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}

function enableCam() {
  if (hasGetUserMedia()) {
    // getUsermedia parameters.
    const constraints = {
      video: true,
      width: 640, 
      height: 480 
    };

    // Activate the webcam stream.
    navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
      VIDEO.srcObject = stream;
      VIDEO.addEventListener('loadeddata', function() {
        videoPlaying = true;
        ENABLE_CAM_BUTTON.classList.add('removed');
      });
    });
  } else {
    console.warn('getUserMedia() is not supported by your browser');
  }
}

Сначала создайте функцию с именем hasGetUserMedia() , чтобы проверить, поддерживает ли браузер getUserMedia() , проверив наличие ключевых свойств API браузера.

В функции enableCam() используйте функцию hasGetUserMedia() которую вы только что определили выше, чтобы проверить, поддерживается ли она. Если это не так, выведите предупреждение на консоль.

Если он поддерживает это, определите некоторые ограничения для вашего вызова getUserMedia() , например, вам нужен только видеопоток и вы предпочитаете, чтобы width видео составляла 640 пикселей, а height480 пикселей. Почему? Что ж, нет особого смысла получать видео большего размера, поскольку его размер необходимо будет изменить до 224 на 224 пикселя, чтобы его можно было передать в модель MobileNet. Вы также можете сэкономить некоторые вычислительные ресурсы, запросив меньшее разрешение. Большинство камер поддерживают разрешение такого размера.

Затем вызовите navigator.mediaDevices.getUserMedia() с constraints подробно описанными выше, а затем дождитесь возврата stream . Как только stream будет возвращен, вы можете заставить свой элемент VIDEO воспроизводить stream , установив его в качестве значения srcObject .

Вам также следует добавить eventListener к элементу VIDEO , чтобы знать, когда stream загрузился и успешно воспроизводится.

Как только Steam загрузится, вы можете установить для videoPlaying значение true и удалить ENABLE_CAM_BUTTON чтобы предотвратить повторное нажатие на него, установив для его класса значение « removed ».

Теперь запустите свой код, нажмите кнопку включения камеры и разрешите доступ к веб-камере. Если вы делаете это впервые, вы должны увидеть себя в элементе видео на странице, как показано ниже:

b378eb1affa9b883.png

Хорошо, теперь пришло время добавить функцию для обработки нажатий кнопок dataCollector .

12. Обработчик событий кнопки сбора данных

Теперь пришло время заполнить пустую на данный момент функцию gatherDataForClass(). Это то, что вы назначили в качестве функции обработчика событий для кнопок dataCollector в начале лаборатории кода.

скрипт.js

/**
 * Handle Data Gather for button mouseup/mousedown.
 **/
function gatherDataForClass() {
  let classNumber = parseInt(this.getAttribute('data-1hot'));
  gatherDataState = (gatherDataState === STOP_DATA_GATHER) ? classNumber : STOP_DATA_GATHER;
  dataGatherLoop();
}

Сначала проверьте атрибут data-1hot на нажатой в данный момент кнопке, вызвав this.getAttribute() с именем атрибута, в данном случае data-1hot в качестве параметра. Поскольку это строка, вы можете затем использовать parseInt() чтобы привести ее к целому числу и присвоить этот результат переменной с именем classNumber.

Затем соответствующим образом установите переменную gatherDataState . Если текущий gatherDataState равен STOP_DATA_GATHER (которому вы установили значение -1), это означает, что вы в настоящее время не собираете никаких данных и произошло событие mousedown . Установите gatherDataState чтобы стать classNumber вы только что нашли.

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

Наконец, начните вызов dataGatherLoop(), который фактически выполняет запись данных класса.

13. Сбор данных

Теперь определите функцию dataGatherLoop() . Эта функция отвечает за выборку изображений из видео веб -камеры, передача их через модель Mobilenet и захват выходов этой модели (1024 векторов функций).

Затем он хранит их вместе с идентификатором gatherDataState кнопки, которая в настоящее время нажимается, поэтому вы знаете, какой класс представляет эти данные.

Давайте пройдем через это:

script.js

function dataGatherLoop() {
  if (videoPlaying && gatherDataState !== STOP_DATA_GATHER) {
    let imageFeatures = tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
      let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor, [MOBILE_NET_INPUT_HEIGHT, 
          MOBILE_NET_INPUT_WIDTH], true);
      let normalizedTensorFrame = resizedTensorFrame.div(255);
      return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
    });

    trainingDataInputs.push(imageFeatures);
    trainingDataOutputs.push(gatherDataState);
    
    // Intialize array index element if currently undefined.
    if (examplesCount[gatherDataState] === undefined) {
      examplesCount[gatherDataState] = 0;
    }
    examplesCount[gatherDataState]++;

    STATUS.innerText = '';
    for (let n = 0; n < CLASS_NAMES.length; n++) {
      STATUS.innerText += CLASS_NAMES[n] + ' data count: ' + examplesCount[n] + '. ';
    }
    window.requestAnimationFrame(dataGatherLoop);
  }
}

Вы собираетесь продолжить выполнение этой функции только в том случае, если videoPlaying верно, то есть веб -камера активна, а gatherDataState не равна STOP_DATA_GATHER , и в настоящее время нажимается кнопка для сбора данных класса.

Затем оберните свой код в tf.tidy() чтобы утилизировать любые созданные тензоры в следующем коде. Результат этого выполнения кода tf.tidy() сохраняется в переменной, называемой imageFeatures .

Теперь вы можете взять кадр VIDEO веб -камеры с помощью tf.browser.fromPixels() . Полученный тензор, содержащий данные изображения, хранится в переменной, называемой videoFrameAsTensor .

Затем, измените размер переменной videoFrameAsTensor , чтобы иметь правильную форму для ввода модели Mobilenet. Используйте вызов tf.image.resizeBilinear() с тензором, который вы хотите изменить в качестве первого параметра, а затем форму, которая определяет новую высоту и ширину, как определено константы, которые вы уже создали ранее. Наконец, установите выравнивание углов в True, передавая третий параметр, чтобы избежать каких -либо проблем выравнивания при изменении размера. Результат этого изменения размера хранится в переменной, называемой resizedTensorFrame .

Обратите внимание, что это примитивное изменение размера растягивает изображение, так как размер вашего изображения веб -камеры составляет 640 на 480 пикселей, а модели нуждается в квадратном изображении 224 на 224 пикселей.

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

Затем нормализуйте данные изображения. Данные изображения всегда находятся в диапазоне от 0 до 255 при использовании tf.browser.frompixels() , поэтому вы можете просто разделить resizedtensorframe на 255, чтобы убедиться, что все значения находятся от 0 до 1, а это то, что модель Mobilenet ожидает в качестве входных данных.

Наконец, в разделе tf.tidy() кода протолкните этот нормализованный тензор через загруженную модель, вызывая mobilenet.predict() , к которой вы передаете расширенную версию normalizedTensorFrame используя expandDims() , так что она является партией. 1, поскольку модель ожидает партии входов для обработки.

После того, как результат вернется, вы можете немедленно вызвать squeeze() на этом возвращенном результате, чтобы раздавить его обратно к 1D -тензору, который вы затем возвращаете и присваиваете переменной imageFeatures , которая захватывает результат от tf.tidy() .

Теперь, когда у вас есть imageFeatures из модели Mobilenet, вы можете записать их, натолкнув их на массив trainingDataInputs , который вы определили ранее.

Вы также можете записать, что этот вход представляет, подтолкнув текущий gatherDataState в массив trainingDataOutputs .

Обратите внимание, что переменная gatherDataState была бы установлена ​​на численном идентификаторе текущего класса, в котором вы записываете данные, когда кнопка нажимала в ранее определенной функции gatherDataForClass() .

На этом этапе вы также можете увеличить количество примеров, которые у вас есть для данного класса. Чтобы сделать это, сначала проверьте, был ли индекс в массиве examplesCount был инициализирован до или нет. Если он не определен, установите его на 0 для инициализации счетчика для численного идентификатора данного класса, а затем вы можете увеличить examplesCount для текущего gatherDataState .

Теперь обновите текст элемента STATUS на веб -странице, чтобы показать текущий счет для каждого класса, как они запечатлены. Для этого проберите массив CLASS_NAMES и распечатайте человеческое читаемое имя в сочетании с количеством данных в одном и том же индексе в examplesCount .

Наконец, вызов window.requestAnimationFrame() с dataGatherLoop пройденным в качестве параметра, чтобы снова вызвать эту функцию. Это будет продолжать выставлять кадры из видео до тех пор, пока mouseup кнопки не будут обнаружены, и gatherDataState установлен в STOP_DATA_GATHER, и в этот момент цикл сбора данных закончится.

Если вы запускаете свой код сейчас, вы сможете нажать кнопку «Включить камеру», ожидать загрузки веб -камеры, а затем нажимать и удерживать каждую из кнопок сбора данных, чтобы собрать примеры для каждого класса данных. Здесь вы видите, как я собираю данные для моего мобильного телефона и моей руки соответственно.

541051644A45131F.GIF

Вы должны увидеть обновленный текст состояния, поскольку он хранит все тензоры в памяти, как показано в захвате экрана выше.

14. Тренируйте и предсказывайте

Следующим шагом является внедрение кода для вашей в настоящее время функции пустого trainAndPredict() , где происходит обучение передачи. Давайте посмотрим на код:

script.js

async function trainAndPredict() {
  predict = false;
  tf.util.shuffleCombo(trainingDataInputs, trainingDataOutputs);
  let outputsAsTensor = tf.tensor1d(trainingDataOutputs, 'int32');
  let oneHotOutputs = tf.oneHot(outputsAsTensor, CLASS_NAMES.length);
  let inputsAsTensor = tf.stack(trainingDataInputs);
  
  let results = await model.fit(inputsAsTensor, oneHotOutputs, {shuffle: true, batchSize: 5, epochs: 10, 
      callbacks: {onEpochEnd: logProgress} });
  
  outputsAsTensor.dispose();
  oneHotOutputs.dispose();
  inputsAsTensor.dispose();
  predict = true;
  predictLoop();
}

function logProgress(epoch, logs) {
  console.log('Data for epoch ' + epoch, logs);
}

Во -первых, убедитесь, что вы остановите любые текущие прогнозы, которые происходят, установив predict для false .

Затем перетасовывайте свои входные и выходные массивы с помощью tf.util.shuffleCombo() чтобы убедиться, что заказ не вызывает проблем при обучении.

Преобразуйте свой выходной массив, trainingDataOutputs, чтобы стать Tensor1D типа Int32, чтобы он был готов использовать в одном горячем кодировании . Это хранится в переменной с именем outputsAsTensor .

Используйте функцию tf.oneHot() с этой переменной outputsAsTensor , а также максимальное количество классов для кодирования, что является просто CLASS_NAMES.length . Ваши единственные горячие выходы теперь хранятся в новом тензоре под названием oneHotOutputs .

Обратите внимание, что в настоящее время trainingDataInputs - это множество записанных тензоров. Чтобы использовать их для обучения, вам нужно будет преобразовать множество тензоров, чтобы стать обычным 2D -тензором.

Для этого есть отличная функция в библиотеке Tensorflow.js под названием tf.stack() ,

который берет массив тензоров и складывает их вместе, чтобы создать тензор более высокого размера в качестве вывода. В этом случае возвращается тензор 2D, это партия из 1 размерных входов, каждый из которых имеет длину по 1024, содержащие записанные функции, которые вам нужны для обучения.

Далее, await model.fit() чтобы обучить индивидуальную модель. Здесь вы передаете свою переменную inputsAsTensor вместе с oneHotOutputs , чтобы представлять учебные данные для использования, например, входных и целевых выходов соответственно. В объекте Configuration для 3 -го параметра установите shuffle на true , используйте batchSize 5 , с epochs , установленными на 10 , а затем укажите callback для onEpochEnd для функции logProgress , которую вы определите в ближайшее время.

Наконец, вы можете избавиться от созданных тензоров по мере обучения модели. Затем вы можете установить predict в true чтобы позволить прогнозам снова иметь место, а затем вызвать функцию predictLoop() , чтобы начать прогнозирование живых изображений веб -камеры.

Вы также можете определить функцию logProcess() для регистрации состояния обучения, которая используется в model.fit() выше и печатает результаты для консоли после каждого раунда обучения.

Ты почти там! Время добавить функцию predictLoop() для прогнозирования.

Основная петля предсказания

Здесь вы реализуете основной цикл прогнозирования, который выпускает кадры из веб -камеры и непрерывно предсказывает, что находится в каждом кадре с режимами в режиме реального времени, приводит к браузеру.

Давайте проверим код:

script.js

function predictLoop() {
  if (predict) {
    tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO).div(255);
      let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor,[MOBILE_NET_INPUT_HEIGHT, 
          MOBILE_NET_INPUT_WIDTH], true);

      let imageFeatures = mobilenet.predict(resizedTensorFrame.expandDims());
      let prediction = model.predict(imageFeatures).squeeze();
      let highestIndex = prediction.argMax().arraySync();
      let predictionArray = prediction.arraySync();

      STATUS.innerText = 'Prediction: ' + CLASS_NAMES[highestIndex] + ' with ' + Math.floor(predictionArray[highestIndex] * 100) + '% confidence';
    });

    window.requestAnimationFrame(predictLoop);
  }
}

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

Затем вы можете получить функции изображения для текущего изображения, как вы это сделали в функции dataGatherLoop() . По сути, вы получаете кадр с веб -камеры, используя tf.browser.from pixels() , нормализовать ее, изменить размер до размера 224 на 224 пикселей, а затем передаете эти данные через модель Mobilenet, чтобы получить полученные функции изображения.

Теперь, однако, вы можете использовать свою недавно обученную модельную головку, чтобы фактически выполнить прогноз, передавая полученные imageFeatures только что обнаруживаемые через функцию predict() . Затем вы можете сжать полученный тензор, чтобы снова сделать его 1 -го размера и назначить его переменной, называемой prediction .

С этим prediction вы можете найти индекс, который имеет наивысшее значение, используя argMax() , а затем преобразовать этот результирующий тензор в массив с использованием arraySync() , чтобы получить базовые данные в JavaScript, чтобы обнаружить позицию наивысшего ценного элемента. Это значение хранится в переменной, называемой highestIndex .

Вы также можете получить фактические оценки достоверности прогнозирования таким же образом, вызывая arraySync() на тензоре prediction напрямую.

Теперь у вас есть все, что вам нужно, чтобы обновить текст STATUS с помощью данных prediction . Чтобы получить человеческую читаемую строку для класса, вы можете просто найти highestIndex в массиве CLASS_NAMES , а затем получить значение доверия из predictionArray . Чтобы сделать его более читаемым в процентах, просто умножьте на 100 и math.floor() результат.

Наконец, вы можете использовать window.requestAnimationFrame() для вызова predictionLoop() снова и снова, как только готовая, чтобы получить классификацию в реальном времени в вашем видеопотоке. Это продолжается до тех пор, пока predict не будет установлен на false если вы решите обучить новую модель с новыми данными.

Что подводит вас к последнему кусочке головоломки. Реализация кнопки сброса.

15. Реализуйте кнопку сброса

Почти завершено! Последняя часть головоломки - реализовать кнопку сброса для начала. Код для вашей функции в настоящее время пустого reset() ниже. Идите вперед и обновите это следующим образом:

script.js

/**
 * Purge data and start over. Note this does not dispose of the loaded 
 * MobileNet model and MLP head tensors as you will need to reuse 
 * them to train a new model.
 **/
function reset() {
  predict = false;
  examplesCount.length = 0;
  for (let i = 0; i < trainingDataInputs.length; i++) {
    trainingDataInputs[i].dispose();
  }
  trainingDataInputs.length = 0;
  trainingDataOutputs.length = 0;
  STATUS.innerText = 'No data collected';
  
  console.log('Tensors in memory: ' + tf.memory().numTensors);
}

Во -первых, остановите любые управляемые циклы прогнозирования, установив predict для false . Затем удалите все содержимое в массиве examplesCount , установив его длину до 0, что является удобным способом очистить все содержимое из массива.

Теперь просмотрите все текущие записанные trainingDataInputs и убедитесь, что вы dispose() каждого тензора, содержащегося в нем, чтобы снова освободить память, поскольку тензоры не очищаются коллекционером мусора JavaScript.

Как только это будет сделано, теперь вы можете безопасно установить длину массива на 0 как на массивах trainingDataInputs , так и trainingDataOutputs , чтобы очистить их.

Наконец, установите текст STATUS на что -то разумное и распечатайте тензоры, оставленные в памяти в качестве проверки здравомыслия.

Обратите внимание, что в памяти все еще будет несколько сотен тензоров, поскольку модель Mobilenet и многослойный персептрон, который вы определили, не утилизируются. Вам нужно будет повторно использовать их с новыми учебными данными, если вы решите тренироваться снова после этого сброса.

16. Попробуем это

Пришло время проверить свою собственную версию обучаемой машины!

Отправляйтесь в Live Preview, включите веб -камеру, соберите не менее 30 образцов для класса 1 для некоторого объекта в вашей комнате, а затем сделайте то же самое для класса 2 для другого объекта, нажмите на поезд и проверьте журнал консоли, чтобы увидеть прогресс. Это должно тренироваться довольно быстро:

BF1AC3CC5B15740.GIF

После обучения покажите объекты в камеру, чтобы получить живые прогнозы, которые будут напечатаны в текстовую область состояния на веб -странице рядом с верхом. Если у вас возникли проблемы, проверьте мой завершенный рабочий код , чтобы увидеть, пропустили ли вы копирование над чем -либо.

17. Поздравляю

Поздравляем! Вы только что закончили свой самый первый пример обучения переноса, используя Tensorflow.js Live в браузере.

Попробуйте, проверьте его на различных объектах, вы можете заметить, что некоторые вещи труднее распознать, чем другие, особенно если они похожи на что -то другое. Возможно, вам придется добавить больше классов или учебных данных, чтобы иметь возможность отделить их отдельно.

Резюме

В этом коделабе вы узнали:

  1. Что такое переносное обучение, и его преимущества по сравнению с обучением полной модели.
  2. Как получить модели для повторного использования из Tensorflow Hub.
  3. Как настроить веб -приложение, подходящее для перевода.
  4. Как загрузить и использовать базовую модель для создания функций изображения.
  5. Как обучить новую главу прогнозирования, которая может распознавать пользовательские объекты из изображений веб -камеры.
  6. Как использовать полученные модели для классификации данных в режиме реального времени.

Что дальше?

Теперь, когда у вас есть рабочая база для начала, с какими креативными идеями вы можете придумать, чтобы расширить эту модель машинного обучения Cowerplate для реального варианта использования, над которым вы можете работать? Может быть, вы могли бы революционизировать отрасль, в которой вы в настоящее время работаете, чтобы помочь людям в моделях вашей компании, чтобы классифицировать вещи, которые важны в их повседневной работе? Возможности безграничны.

Чтобы пойти дальше, рассмотрите возможность взять этот полный курс бесплатно , что показывает вам, как объединить 2 модели, которые у вас есть в этой коделабе в 1 единую модель для эффективности.

Также, если вам больше интересно вокруг теории, стоящей за оригинальным приложением обучаемой машины, ознакомьтесь с этим учебником .

Поделитесь тем, что делаете с нами

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

Не забудьте отметить нас в социальных сетях, используя хэштег #madewithtfjs , чтобы получить шанс, чтобы ваш проект был показан в нашем блоге Tensorflow или даже будущих событиях . Мы хотели бы увидеть, что вы делаете.

Веб -сайты для проверки