От принципов до поддержки реальных библиотек.

Привет, я Франсуа Лагунас.

Я занимаюсь машинным обучением, и последние месяцы я работал над использованием разреженных матриц, особенно в Transformers. Недавнее объявление о том, что OpenAI переносит свой разреженный блок инструментов в PyTorch, действительно является большой новостью:

«Мы находимся в процессе написания привязок PyTorch для наших высокооптимизированных ядер с разбиением на блоки и в ближайшие месяцы будем открывать исходный код этих привязок»

Я говорил об этом с выдающейся командой Hugging Face (я один из их первых инвесторов) и хотел поделиться с вами своим волнением!

Что такое разреженная матрица?

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

разреженность матрицы - это доля нулей по отношению к размеру матрицы.

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

Минусы? Конечно, наличие всех этих нулей, вероятно, повлияет на точность / производительность сети. Но в какой степени? Вы можете быть удивлены.

Откуда они?

Первыми исследователями / инженерами, использовавшими разреженные матрицы, были пользователи Конечных элементов.

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

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

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

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

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

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

При обработке изображений все сводится к двум вещам:

Разреженность: преобразование является локальным → каждый выходной пиксель должен зависеть от нескольких соседних входных пикселей.

Инвариантность: преобразование не зависит от положения на изображении.

Затем вы просто добавляете ограничение, что преобразование является линейным: если бы вы представили это преобразование, вы бы получили ОГРОМНУЮ матрицу только с несколькими ненулевыми. Но, конечно, правильный способ сделать это - произвести умножение входного тензора на небольшой набор маленьких матриц (каждый квадрат на изображении выше).

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

Где они полезны?

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

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

Почему объявление OpenAI так важно?

Итак, если все в порядке на разреженных землях, нам всем следует попробовать разреженные матрицы, не так ли?

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

Разработчики PyTorch, например, приложили значительные усилия для поддержки разреженных вычислений. Но все еще существует большой разрыв в производительности между операциями с плотными и разреженными матрицами, что сводит на нет всю цель их использования. Даже использование памяти довольно велико: разреженность должна быть более 80%, чтобы сэкономить место на разреженных матрицах (подробнее об этом в моем следующем посте). Даже базовая сериализация была нарушена до версии 1.4. Причина в том, что базовые библиотеки (например, cuSPARSE) плохо справляются со своей задачей, потому что проблема не подходит для работы графического процессора.

Так что объявление об их разреженных блоках инструментах OpenAI - это очень хорошая новость для тех, кто которые хотят использовать sparse ops, не жертвуя скоростью обучения (и, похоже, некоторые люди уже давно ждали). И речь не идет о нескольких процентах.

«Наши ядра обычно работают на один или два порядка быстрее с точки зрения GFLOPS».

(Хуже всего то, что бумага заключает, что cuBLAS быстрее, чем cuSPARSE даже с очень разреженными матрицами. Как это печально.)

Магическое ключевое слово здесь - «блок». Сложно эффективно реализовать общие вычисления с разреженной матрицей на графических процессорах. Но это станет намного проще, если вы добавите «разумное» ограничение на форму матриц: их ненулевые значения должны быть сгруппированы в небольшие блоки фиксированного размера, что значительно упрощает эффективное распараллеливание обработки графическим процессором. Обычно блоки 8x8, 16x16 или 32x32, 16x16 уже дают очень хорошую производительность, а 32x32 дают немного лучше.

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

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

Заключение

Надеюсь, я убедил вас, что 2020 год будет годом разреженной сети (в нем уже есть два нуля, это знак).

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

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

Больше чтения

Во-первых, вот исследование разреженной производительности PyTorch.

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

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