Обучение и развертывание модели TensorFlow на Arduino

Я собираюсь обучить игрушечный алгоритм и развернуть его для вывода на Arduino Nano 33 BLE Sense. Я пытаюсь создать и протестировать оболочку, используя как можно меньше компонентов, чтобы впоследствии ее можно было улучшить.

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

Обучение модели

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

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

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

Затем я строю свою модель. Мне нужен только один вход (лошадиные силы), и я создаю очень простую модель с одним скрытым слоем. У меня также есть только один выходной нейрон для предсказания MPG.

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

Преобразование модели

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

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

  • float32 в float16, это может уменьшить размер модели вдвое и значительно ускорить вывод на некотором оборудовании, это означает, что параметры имеют значение float16, а вывод выполняется float32
  • Параметры int8, модель использует смешанные вычисления, если они доступны
  • Параметры и активации int8, выполняемые только с целочисленными операциями

Удобное дерево решений и дополнительная деталь здесь.

Мы хотим квантовать последнее, int8 все с принудительными операциями только с целыми числами. Для этого мы должны создать репрезентативный набор данных. Это необходимо, потому что для эффективного преобразования 8-битных значений требуется линейное преобразование в действительные числа. Для весов это можно сделать автоматически, потому что TensorFlow может вычислить диапазон для каждого слоя на основе обученных значений. Для активаций это сложнее, потому что из параметров не очевидно, каков диапазон выходных сигналов каждого слоя. Если произвольно выбранный диапазон слишком мал, значения будут обрезаны до минимума или максимума, если диапазон слишком велик, точность будет потеряна.

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

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

Фактически мы сохраняем здесь две модели, одну, которая просто конвертируется в формат TFLite, но сохраняет веса и активации как float32, поэтому мы можем увидеть, как это влияет на точность. Затем модель сохраняется с квантованием, применяя только int (потому что она может вернуться к float32 для неподдерживаемых операций, если не указано). Предоставляется репрезентативный набор данных, модель преобразована и сохранена.

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

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

Размер и точность

Теперь, когда у нас есть обученные, преобразованные модели и некоторая точность выборки, насколько сильно это изменилось с точки зрения размера модели и какого-либо влияния на точность?

Что касается точности, мы видим, что она минимальна, модель TFLite и неконвертированная модель идентичны, чего и следовало ожидать, поскольку квантование не происходило. Квантованная модель минимально менее точна с MSE 23,871 до MSE 24,809 для неквантованной модели. Поскольку это очень маленькая и простая модель, квантование также не привело к значительному уменьшению размера модели, сэкономив только 228 байт. Больше места будет сэкономлено для моделей с большим количеством слоев и нейронов.

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

Вывод этого шестнадцатеричного дампа можно скопировать непосредственно в файл model.ccp нашей программы Arduino. И контент, и g_model_len должны быть добавлены и соответствовать, как показано ниже;

Программа Arduino

Репо, содержащее mpgModel.ino.

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

Все интересное происходит в autoTest.ino. Это настраивает нашу среду и циклически перехватывает входные данные и вызывает модель. Но сначала нам нужно импортировать нужные нам операции.

Эта модель не требует большого количества операций, другие будут иметь вложенные пространства имен и вызывать определенные элементы архитектуры, такие как максимальное объединение и слои свертки. KTensorArenaSize - это объем памяти, выделенной модели. На сегодняшний день нет очень элегантного способа оценить это, кроме метода проб и ошибок; уменьшая количество, пока модель не рухнет.

Настройка модели

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

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

Вызов модели

Более интересный момент - это вызов модели в нашем цикле.

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

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

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