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

Популярные библиотеки, такие как Tensorflow, работают с использованием CUDA (Compute Unified Device Architecture) для обработки данных на графических процессорах, используя их параллельные вычислительные мощности. Эта работа называется программированием GPGPU (General Purpose GPU). Он адаптирован к моделям глубокого обучения, которые требуют как минимум тысяч арифметических операций.

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

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

ПЛИС раньше не предлагали столь удобного решения, их использование требовало глубокого понимания того, как работает оборудование. Но недавний прогресс сделал их более доступными, и об этом мы поговорим позже.

Обзор

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

  • Графические процессоры, процессоры и CUDA
  • Преимущества и конструкция ПЛИС
  • HDL как метод развертывания ПЛИС
  • HLS как метод развертывания ПЛИС
  • Развертывание FPGA с использованием LeFlow
  • Особенности LeFlow для оптимизации

Почему графические процессоры иногда лучше, чем процессоры?

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

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

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

Чтобы узнать больше об использовании CUDA, посетите Блог разработчиков Nvidia или посмотрите книгу CUDA By Example.

Оборудование нейронной сети

Tensorflow разделен на два раздела: библиотека и среда выполнения.

Библиотека - это создание вычислительного графа (нейронной сети), а среда выполнения - это выполнение его на некоторой аппаратной платформе.

Предпочтительной платформой является графический процессор, однако есть альтернатива: ПЛИС.

Зачем использовать ПЛИС?

FPGA могут создавать схемы с тысячами единиц памяти для вычислений, поэтому они работают аналогично графическим процессорам и их потокам в CUDA. ПЛИС имеют адаптируемую архитектуру, позволяющую проводить дополнительную оптимизацию для увеличения пропускной способности. Таким образом, возможный объем вычислений делает FPGA жизнеспособным решением для графических процессоров.

Сравнительно FPGA имеют более низкое энергопотребление и могут быть оптимальными для встраиваемых приложений. Они также являются общепринятым стандартом для операций, критически важных для безопасности, таких как ADAS (Advanced Driver Assistance Systems) в автомобилестроении.

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

Теперь вы должны задаться вопросом, что такое ПЛИС?

ПЛИС (программируемая вентильная матрица) - это настраиваемое аппаратное устройство. Его можно рассматривать как море плавающих логических ворот. Приходит дизайнер и записывает программу, используя язык описания оборудования (HDL), например Verilog или VHDL. Эта программа определяет, какие соединения выполняются и как они реализуются с использованием цифровых компонентов. Другое слово для HDL - это язык RTL (уровень передачи регистров).

ПЛИС легко обнаружить, ищите крупногабаритный Arduino.

Шучу, они бывают самых разных форм и размеров.

Используя программное обеспечение, аналогичное компилятору, HDL синтезируется (выясняется, какие ворота использовать), затем маршрутизируется (соединяет части вместе) для формирования оптимизированной цифровой схемы. Эти инструменты (HDL, синтез, маршрутизация, временной анализ, тестирование) включены в программный пакет, некоторые из которых включают Xilinx Design Tools и Quartus Prime.

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

Тогда почему бы нам не использовать FPGA?

Для ПЛИС сложнее всего реализовать фреймворки машинного обучения, написанные на языках более высокого уровня, таких как Python. HDL по своей сути не является платформой программирования, это код, написанный для определения аппаратных компонентов, таких как регистры и счетчики. Некоторые языки HDL включают: Verilog, VHDL.

Ниже показан фрагмент кода, используемого для создания последовательного битового детектора.

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

Выполнено? Даже если вы некоторое время смотрите на него, это не очевидно.

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

Вернемся к теме: главное, что нет прямого перевода для преобразования цикла в Python в связку проводов в Verilog.

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

Так должны ли мы придерживаться графических процессоров?

Ну нет, ПЛИС не бесполезны.

Один из способов обойти проблему программирования - использовать инструменты HLS (синтез высокого уровня), такие как LegUp, для создания программ в Verilog для развертывания. Инструменты HLS позволяют дизайнерам избегать написания HDL с нуля и вместо этого использовать более интуитивно понятный, алгоритмический язык программирования (C).

Инструменты HLS абстрагируются от проектирования на аппаратном уровне; аналогично тому, как CUDA автоматически настраивает параллельные блоки и потоки при запуске модели.

Для инструментов HLS требуется код C в качестве ввода, который сопоставляется с LLVM IR (промежуточным представлением) для выполнения. Эти инструменты используются для преобразования процедурных описаний в аппаратную реализацию.

Их роль в проектировании ПЛИС показана ниже.

Подробнее о НП LLVM

LLVM - это не аббревиатура, это библиотека, которая создает инструкции, подобные сборке (IR). Эти программы легче обрабатывать инструментам HLS, и их можно использовать для создания синтезируемого кода для ПЛИС.

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

Чтобы узнать больше о LLVM и IR, обратитесь к Dr. Слайды Чисналла .

Назад к проблемам с ПЛИС

Основная проблема заключается в преобразовании программ и переносе библиотек, написанных для Python, на C для работы инструментов HLS. В настоящее время в C нет поддержки Tensorflow, поэтому это решение очень сложно. Очевидно, требование к проектированию и созданию оборудования является большим препятствием для использования ПЛИС в глубоком обучении.

Наш герой LeFlow

LeFlow Toolkit позволяет инженерам проектировать, обучать и тестировать свои модели с использованием Python, а затем развертывать его непосредственно на FPGA для использования. LeFlow упрощает процесс разработки, позволяя инструментам HLS быть совместимыми с Python и Tensorflow , действующий как адаптер.

Программное обеспечение было разработано исследователями из отдела ECE (Электротехника и вычислительная техника) Университета Британской Колумбии: Daniel H. Noronha, Bahar Salehpour и Стивен Дж. Э. Уилсон.

В следующих разделах подробно описано, как LeFlow интегрируется с Tensorflow и FPGA, если вас интересует только реализация пропустите на: Время настройки.

Как LeFlow Toolkit работает с Tensorflow

Компилятор XLA (ускоренная линейная алгебра), созданный для Tensorflow, выводит LLVM IR. LeFlow реструктурирует IR и оптимизирует его для использования с инструментами HLS.

После этого инструменты HLS выполняют всю работу по преобразованию этого IR в программу, развернутую на FPGA, как описано в разделе: Следует ли нам придерживаться графических процессоров?

Модель ввода-вывода LeFlow

LeFlow принимает ИК как вход. Алгоритм 1 - это IR Tensorflow, загружающий два поплавка.

Программа сложна для выполнения и выглядит неаккуратно. LeFlow очистит его и изменит.

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

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

Преобразование IR для оптимального исполнения

LeFlow устанавливает непостоянную нагрузку на регистры ПЛИС. Это обеспечивает доступ к переменным и автоматическое изменение схемы при изменении кода высокого уровня. Изменения заметны в основном в строках 1, 6 и 7.

LeFlow завершает свою работу, как показано обновленным IR алгоритма 2, а остальное обрабатывается инструментом HLS!

Время настройки!

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

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

Разворачивать или не разворачивать

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

Это выглядит так же, как обычный цикл for, но на аппаратном уровне добавлено больше компонентов для выполнения большего количества вычислений за такт (или итерацию цикла). Подробнее о развертывании см. В Руководстве пользователя Keil.

int sum = 0;
for(int i = 0; i < 10; i+=2)
     sum += a[i]; sum += a[i+1];

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

При этом используется преимущество параллелизма FPGA, чтобы работать больше как GPU, очевидно, в этом примере циклы были сокращены на 13%.

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

Разрежьте это с помощью разделов памяти

Для HLS, используемого в конвейере LeFlow, требуется двухпортовая RAM (оперативная память) для хранения значений.

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

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

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

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

В (а) нет разделов памяти, поэтому один элемент из каждого массива загружается, умножается и сохраняется за цикл. Процесс продолжается и занимает восемь циклов до завершения.

В (b) массивы циклически разделены на две отдельные памяти. Два элемента из каждого массива загружаются, умножаются и сохраняются за цикл. Более крупные фрагменты указывают на то, что процессы происходят одновременно, хотя и в разных частях оборудования. Это сокращает его до шести циклов.

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

Использование LeFlow в Python

После того, как LeFlow настроен правильно, все, что ему нужно для работы без каких-либо дополнительных настроек (например, развертывания), - это строка выбора устройства:

with tf.device("device:XLA_CPU:0")

Это указывает на то, что XLA-компилятор будет использоваться для генерации LLVM и запуска процесса преобразования LeFlow.

Пробуем!

Теперь вы эксперт в понимании того, как работает LeFlow, и, возможно, вам подходят FPGA.

На Github есть множество примеров LeFlow и его конкретной установки. Дэниел Холанда (один из соавторов) среди прочего имеет исходный код для распознавания цифр MNIST, так что возьмите FPGA и попробуйте!