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

Чтобы наверстать упущенное из части 1 и части 2 этой серии.

Постройте и обучите модель Caffe

Керас был моим фаворитом при выборе фреймворка для глубокого обучения из-за его простоты и элегантности, однако на этот раз мы выберем Caffe, поскольку команда ARM выпустила два полезных скрипта для генерации кода для нас, который был построен для моделей Caffe. Не волнуйтесь, если вы новичок в Caffe, как и я. Структура модели и параметры обучения определены в удобном для понимания текстовом формате.

Установка Caffe может быть сложной задачей, особенно для новичков, поэтому я создаю этот работоспособный блокнот Google Colab с установкой Caffe и включенным кодом руководства.

Модель классификации изображений Caffe определена в файле cifar10_m4_train_test_small.prototxt с графом структуры модели, показанным ниже. Он содержит три сверточных слоя, перемежающихся уровнями активации ReLU и максимального объединения, за которыми следует полностью связанный слой в конце для генерации результата классификации в один из десяти выходных классов.

В файле определения модели cifar10_m4_train_test_small.prototxt

  • Слой с типом «Данные» должен называться «данные», поскольку сценарий генерации кода, который мы будем использовать позже, находит слой по имени. Этот слой создает два «больших двоичных объекта», один из которых data содержит данные изображения, а второй - это label blob, представляющий метки выходного класса.
  • lr_mults - это корректировки скорости обучения для обучаемых параметров слоя. В нашем случае он установит скорость обучения весов такой же, как скорость обучения, заданную решателем во время выполнения, а скорость обучения смещению будет в два раза больше, чем это - обычно это приводит к лучшей скорости сходимости.
  • Полностью связанный слой известен в Caffe как слой InnerProduct.
  • Определения слоев могут включать правила того, включены ли они в определение сети и когда, как показано ниже:

В приведенном выше примере этот слой будет включен только в фазу TRAIN.

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

Наконец, запуск скрипта train_small_colab.sh запустит тренировку, когда она закончится, веса будут сохранены. В нашем случае сценарий запускает два файла решателя, скорость обучения снижается в 10 раз за последние 1000 итераций обучения, как определено во втором файле решателя. Окончательные обученные веса будут сохранены в файле cifar10_small_iter_5000.caffemodel.h5, что означает, что модель была обучена для 5000 итераций. Если вы пришли из Keras или другой среды глубокого обучения, одна итерация здесь не означает, что модель была обучена со всем набором обучающих данных один раз, а означает пакет обучающих данных размером 100, как определено в cifar10_m4_train_test_small.prototxt .

Довольно просто, правда? Для построения и обучения модели Caffe не требуется никакого программирования.

Квантовать модель

Краткие сведения о квантовании,

  • Квантование 32-битных весов с плавающей запятой в 8-битные веса с фиксированной запятой для развертывания уменьшает размер модели в 4 раза,
  • Целочисленные операции с фиксированной запятой выполняются намного быстрее, чем операции с плавающей запятой в типичных микроконтроллерах,
  • Во время вывода модель с квантованными целочисленными весами и смещением не демонстрирует потери производительности (т. Е. Точности).

Поскольку веса фиксируются после тренировки, и мы знаем их минимальный / максимальный диапазон. Они квантуются или дискретизируются до 256 уровней с использованием их диапазонов. Вот быстрая демонстрация квантования весов в числа с фиксированной запятой. Предположим, что вес слоя изначально содержит только 5 чисел с плавающей запятой.

Он выводит,

quantization format: 	 Q5.2
Orginal weights:   [-31.63  -6.54   0.45   0.9   31.  ]
Quantized weights: [-127.   -26.    2.     4.    124. ]
Recovered weights: [-31.75  -6.5    0.5    1.    31.  ]

В этой демонстрации веса квантуются в числовой формат с фиксированной запятой Q5.2, что означает представление числа с плавающей запятой со знаком в 8 битах,

  • один бит в качестве знака (положительный / отрицательный),
  • 5 бит для представления целой части
  • 2 бита для десятичной части.

m и n формата Qm.n обычно можно вычислить с использованием диапазона min / max, как показано в предыдущей демонстрации, но как насчет того, который содержит число выбросов в весах? матрица?

weight = np.array([-31.63, -6.54, 0.45, 0.90, 31, 200])

Если вы повторно запустите предыдущий скрипт с этими новыми значениями весов, восстановленный вес как квантование Q8, -1 будет выглядеть, как показано ниже, не очень хорошо, значения малых весов будут потеряны!

array([-32., -6., 0., 0., 32., 200.])

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

Сценарий nn_quantizer.py берет файл определения модели (cifar10_m4_train_test_small.prototxt) и файл обученной модели (cifar10_small_iter_5000.caffemodel.h5), а затем выполняет три действия итеративно, слой за слоем.

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

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

Сгенерируйте код

Кому нужно писать код, если есть «Генератор кода»? code_gen.py получает параметры квантования и связность сетевого графа из предыдущего шага и генерирует код, состоящий из вызовов функций NN.

В настоящее время он поддерживает следующие уровни: Convolution, InnerProduct (полностью подключен), Pooling (макс / среднее) и ReLu. Он генерирует три файла,

  1. weights.h: веса модели и смещение.
  2. parameter.h: диапазоны квантования значений смещения и выходного смещения, вычисленные из формата Qm, n весов, смещения и активаций,
  3. main.cpp: сетевой код.

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

Развернуть на микроконтроллер

Если структура модели не изменилась, нам нужно только обновить эти данные из weights.h и parameter.h. Это значения смещения и сдвига вывода для замены значений в исходном файле проекта. Если ваш проект основан на официальном примере CMSIS-NN cifar10, как мой, эти значения определены внутри файла arm_nnexamples_cifar10_weights.h.

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

Теперь соберите и запустите его на микроконтроллере!

Заключение и дальнейшие размышления

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

Не забудьте ознакомиться с этим работающим блокнотом Google Colab для этого руководства.

Первоначально опубликовано на www.dlology.com.