Оказывается, сделать крошечную модель на самом деле было довольно просто. В итоге я полагался на два метода, оба из которых можно обобщить на другие модели.
1. Безжалостная обрезка отводок и отягощений.
2. Преобразование 32-битных весов с плавающей запятой в 8-битные целые числа посредством квантования.
Стратегии обрезки
Начнем с обрезки. Сверточные нейронные сети обычно содержат миллионы или даже сотни миллионов весов, которые настраиваются во время обучения. Как правило (я сказал общее, не надо), большее количество гирь означает более высокую точность. Но обмен крайне неэффективен.
Стандартная конфигурация Google’s MobileNetV2 имеет 3,47 миллиона весов и занимает 16 МБ места. Архитектура InceptionV3 почти в 6 раз больше: 24 миллиона весов занимают 92 МБ. Несмотря на то, что InceptionV3 содержит более 20 миллионов дополнительных весов, точность классификации в ImageNet всего на 7 процентных пунктов выше, чем в MobileNetV2 (80% против 73%).
Итак, если у нас есть нейронная сеть, мы можем предположить, что большинство весов не так уж и полезно, и удалить их. Но как? Есть три варианта: обрезка на уровне отдельного веса, уровне слоя и уровне блока.
Уровень веса. Как мы видели, подавляющее большинство (например, ›95%) обученных весов в определенных нейронных сетях бесполезны. Если мы сможем определить, какие веса действительно влияют на точность сети, мы сможем сохранить их, а остальные удалить.
Уровень слоя. Гири упаковываются внутри отдельных слоев. Например, двумерные сверточные слои имеют тензор весов, называемый ядром, с определяемой пользователем шириной, высотой и глубиной. Уменьшение размера ядра приводит к уменьшению размера всей сети.
Уровень блока. Слои обычно объединяются в подграфы многократного использования, известные как блоки. ResNets, например, берет свое название от остаточного блока, повторяемого от 10 до 50 раз. Обрезка на уровне блока удаляет несколько слоев и, следовательно, параметров за один разрез.
На практике не существует хороших реализаций разреженных тензорных операций, необходимых для того, чтобы обрезание уровня веса было целесообразным. Надеюсь, в будущем в этой области будет сделано больше. Это оставляет обрезку на уровне слоев и блоков.
Обрезка на практике
Моя любимая техника обрезки слоя - введение множителя ширины в качестве гиперпараметра. Впервые представленный Google в их знаменитой статье MobileNet, он шокирующе прост и эффективен.
Множитель ширины регулирует количество фильтров в каждом сверточном слое на постоянную долю. Для данного слоя и множителя ширины alpha
количество фильтров F
становится alpha * F
. Вот и все!
С помощью этого гиперпараметра мы можем сгенерировать континуум сетей с одинаковой архитектурой, но с разным числом весов. Обучая каждую конфигурацию, мы можем найти компромисс между скоростью и размером модели и ее точностью.
Давайте посмотрим на метод, который создает модель быстрого переноса стиля, похожую на ту, которую описал Джонсон и др., Но на этот раз множитель ширины добавляется как гиперпараметр:
Когда alpha=1.0
, результирующая сеть содержит 1,7 млн весов. Когда alpha=0.5
, мы получаем сеть всего с 424 102 весами.
Вы можете создать несколько довольно маленьких сетей с параметрами небольшой ширины, но есть также довольно много повторяющихся блоков. Я решил подрезать и некоторых из них. На практике я обнаружил, что не могу удалить слишком много. Более глубокие сети давали лучшие результаты даже при фиксированном количестве параметров. В итоге я удалил два из пяти остаточных блоков и уменьшил количество фильтров по умолчанию для каждого слоя до 32. Моя небольшая сеть в итоге выглядела так:
Путем проб и ошибок я обнаружил, что могу добиться хорошей стилизации с помощью вышеупомянутой архитектуры, вплоть до параметра ширины 0,3, оставляя по 9 фильтров на каждом слое. Конечный результат: нейронная сеть всего с 11 868 весами. Все, что меньше 10 000 единиц веса, не тренировалось последовательно и приводило к плохо стилизованным изображениям.
Стоит отметить, что этот метод отсечения применяется до обучения сети. Вы можете добиться большей производительности при выполнении многих задач, итеративно обрезая их во время и после обучения. Однако это довольно сложно, и я нетерпелив. Если вам интересно, вы можете обратиться к этому отличному посту Маттейса Холлеманса для получения дополнительной информации.
Квантование
Последний этап сжатия происходит после того, как сеть обучена. Веса нейронной сети обычно хранятся в виде 64- или 32-битных чисел с плавающей запятой. Процесс квантования отображает каждый из этих весов с плавающей запятой в целое число с меньшей разрядностью. Переход от 32-битных чисел с плавающей запятой к 8-битным целым числам уменьшает размер хранилища в 4 раза. Благодаря потрясающему сообщению Алексиса Крузо я знал, что могу перейти к 8-битному квантованию с небольшим влиянием на стиль.
Квантование теперь поддерживается всеми основными мобильными платформами, включая TensorFlow Mobile, TensorFlow Lite, Core ML и Caffe2Go.
Чтобы перейти от V1 модели, готовой к работе с мобильными устройствами, к модели, готовой к производству, требуется много доработок и тонких настроек. Fritz AI Studio предназначена для оптимизации процессов на протяжении всего жизненного цикла проекта.
Окончательные результаты
Подводя итог, наша крошечная сетевая архитектура имеет 11 868 параметров по сравнению с 1,7 миллиона в исходной модели Джонсона. При преобразовании в Core ML и квантовании окончательный размер составляет всего 17 КБ по сравнению с исходными 1,7 МБ - всего 0,10% от исходного размера. Вот результаты, полученные в «Звездной ночи» Ван Гога. Я вообще-то думаю, что предпочитаю крошечную версию!
Я был немного удивлен, обнаружив, что, несмотря на разницу в размерах в 400 раз, крошечная модель работала только на 50% быстрее на моем iPhone X. Я не уверен, потому ли это потому, что наши вычисления связаны с этой общей архитектурой, или же узкое место в передача изображений в графический процессор для обработки.
Если не верите, то можете скачать и использовать миниатюрную модель самостоятельно. И даже тренируйте самостоятельно! Все есть в репозитории Fritz Style Transfer на GitHub.
Заключение
Подводя итог, я использовал два простых метода, чтобы уменьшить размер нейронной сети передачи стилей на 99,9%. Слои были обрезаны с помощью гиперпараметра простого множителя ширины, а обученные веса были квантованы с 32-битных чисел с плавающей запятой до 8-битных целых чисел.
В будущем мне интересно посмотреть, насколько хорошо эти методы распространяются на другие нейронные сети. Передача стиля проста в том смысле, что «точность» находится в глазах смотрящего. Для более поддающейся количественной оценке задачи, такой как распознавание изображений, мы, вероятно, увидим гораздо худшую производительность после такой резкой обрезки.
Обсудите этот пост в Hacker News и Reddit
Примечание редактора. Heartbeat - это онлайн-публикация и сообщество, созданное авторами и посвященное изучению возникающего пересечения разработки мобильных приложений и машинного обучения. Мы стремимся поддерживать и вдохновлять разработчиков и инженеров из всех слоев общества.
Являясь независимой редакцией, Heartbeat спонсируется и публикуется Fritz AI, платформой машинного обучения, которая помогает разработчикам учить устройства видеть, слышать, ощущать и думать. Мы платим участникам и не продаем рекламу.
Если вы хотите внести свой вклад, переходите к нашему призыву участников. Вы также можете подписаться на наши еженедельные информационные бюллетени (Deep Learning Weekly и Fritz AI Newsletter), присоединяйтесь к нам на » «Slack и подписывайтесь на Fritz AI в Twitter , чтобы узнавать обо всех последних новостях в области мобильного машинного обучения.