К ГЛУБОКОМУ РЕЛЯЦИОННОМУ ОБУЧЕНИЮ
Помимо трансформеров с PyNeuraLogic
Демонстрация возможностей нейросимволического программирования
За последние несколько лет мы наблюдаем рост числа моделей на основе Transformer¹ с успешным применением во многих областях, таких как обработка естественного языка или компьютерное зрение. В этой статье мы рассмотрим краткий, объяснимый и расширяемый способ выражения моделей глубокого обучения, в частности преобразователей, в виде гибридной архитектуры, то есть путем объединения глубокого обучения с символическим искусственным интеллектом. Для этого мы будем реализовывать модели в нейросимволическом фреймворке Python под названием PyNeuraLogic(автор является соавтором фреймворка).
«Мы не можем создавать полноценные когнитивные модели адекватным автоматизированным способом без триумвирата гибридной архитектуры, обширных предварительных знаний и сложных методов рассуждений».
— Гэри Маркус²
Сочетание символического представления с глубоким обучением заполняет пробелы в текущих моделях глубокого обучения, такие как нестандартная объяснимость или отсутствующие методы рассуждений. Возможно, увеличение количества параметров — не самый разумный подход к достижению желаемых результатов, точно так же, как увеличение количества мегапикселей камеры не обязательно приводит к получению более качественных фотографий.
Фреймворк PyNeuraLogic основан на логическом программировании с изюминкой — логические программы содержат дифференцируемые параметры. Фреймворк хорошо подходит для небольших структурированных данных, таких как молекулы, и сложных моделей, таких как преобразователи и графовые нейронные сети. С другой стороны, PyNeuraLogic — не лучший выбор для нереляционных и больших тензорных данных.
Ключевым компонентом фреймворка является дифференцируемая логическая программа, которую мы называем шаблоном. Шаблон состоит из логических правил, которые абстрактно определяют структуру нейронных сетей — мы можем думать о шаблоне как о плане архитектуры модели. Затем шаблон применяется к каждому экземпляру входных данных, чтобы создать (путем заземления и нейтрализации) нейронную сеть, уникальную для входной выборки. Этот процесс полностью отличается от других фреймворков с предопределенной архитектурой, которые не могут приспосабливаться к различным входным образцам. Для более подробного ознакомления с фреймворком вы можете посмотреть, например, предыдущую статью о PyNeuralogic с точки зрения Graph Neural Networks.
Символические Трансформеры
Обычно мы склонны реализовывать модели глубокого обучения как тензорные операции над входными токенами, объединенными в один большой тензор. Это имеет смысл, потому что фреймворки и аппаратное обеспечение глубокого обучения (например, графические процессоры) обычно оптимизированы для обработки больших тензоров, а не нескольких тензоров разных форм и размеров. Преобразователи не являются исключением, и обычно представления отдельных векторов токенов объединяются в одну большую матрицу и представляют модель как операции над такими матрицами. Тем не менее, такие реализации скрывают, как отдельные входные токены соотносятся друг с другом, что можно продемонстрировать в механизме внимания Transformer.
Механизм внимания
Механизм внимания лежит в основе всех моделей Transformer. В частности, его классическая версия использует так называемое скалярное произведение с несколькими головками. Разложим масштабированное скалярное произведение внимания с одной головой (для наглядности) в простую логическую программу.
Цель внимания состоит в том, чтобы решить, на каких частях входных данных должна сосредоточиться сеть. Внимание делает это, вычисляя взвешенную сумму значений V, где веса представляют совместимость входных ключей K и запросов Q. В этой конкретной версии веса вычисляются с помощью функции softmax скалярного произведения запросов Q и ключей K, деленного на квадратный корень из размерности входного вектора признаков д_к.
(R.weights(V.I, V.J) <= (R.d_k, R.k(V.J).T, R.q(V.I))) | [F.product, F.softmax_agg(agg_terms=[V.J])], (R.attention(V.I) <= (R.weights(V.I, V.J), R.v(V.J)) | [F.product]
В PyNeuraLogic мы можем полностью захватить механизм внимания с помощью приведенных выше логических правил. Первое правило выражает вычисление весов — оно вычисляет произведение обратного квадратного корня размерности с транспонированным j-м вектором ключа и i-м вектором запроса. Затем мы объединяем все результаты для данного i и всех возможных j с помощью softmax.
Затем второе правило вычисляет произведение между этим вектором весов и соответствующим j-м вектором значений и суммирует результаты по различным j для каждого соответствующего i. -й токен.
Маскировка внимания
Во время обучения и оценки мы обычно ограничиваем токены ввода, на которые могут обращать внимание. Например, мы хотим запретить токенам смотреть вперед и следить за предстоящими словами. Популярные фреймворки, такие как PyTorch, реализуют это с помощью маскирования, то есть путем присвоения подмножеству элементов масштабированного результата скалярного произведения некоторого очень низкого отрицательного числа. Эти числа заставляют функцию softmax назначать ноль в качестве веса соответствующей пары токенов.
(R.weights(V.I, V.J) <= ( R.d_k, R.k(V.J).T, R.q(V.I), R.special.leq(V.J, V.I) )) | [F.product, F.softmax_agg(agg_terms=[V.J])],
С нашим символическим представлением мы можем реализовать это, просто добавив одно отношение тела, служащее ограничением. При расчете весов мы ограничиваем индекс j, чтобы он был меньше или равен индексу i. В отличие от маскирования, мы вычисляем только необходимые масштабированные скалярные произведения.
Помимо стандартной агрегации внимания
Конечно, символическая «маскировка» может быть совершенно произвольной. Большинство из нас слышали о GPT-3⁴ (или его приложениях, таких как ChatGPT), основанном на Sparse Transformers. типы головок внимания:
- Тот, который обслуживает только предыдущие n токены(0≤ i− j≤ n)
- Тот, который посещает только каждый n-й предыдущий токен ((i− j) % n = 0)
Реализации обоих типов головок снова требуют лишь незначительных изменений (например, для n = 5).
(R.weights(V.I, V.J) <= ( R.d_k, R.k(V.J).T, R.q(V.I), R.special.leq(V.D, 5), R.special.sub(V.I, V.J, V.D), )) | [F.product, F.softmax_agg(agg_terms=[V.J])], (R.weights(V.I, V.J) <= ( R.d_k, R.k(V.J).T, R.q(V.I), R.special.mod(V.D, 5, 0), R.special.sub(V.I, V.J, V.D), )) | [F.product, F.softmax_agg(agg_terms=[V.J])],
Мы можем пойти еще дальше и обобщить внимание для графоподобных (реляционных) входных данных, как в реляционном внимании.⁶ Этот тип внимания работает с графами, где узлы обращают внимание только на своих соседей (узлы, соединенные ребром). Затем запросы Q, ключи K и значения V представляют собой вложения ребер, суммированные с вложениями векторов узлов.
(R.weights(V.I, V.J) <= (R.d_k, R.k(V.I, V.J).T, R.q(V.I, V.J))) | [F.product, F.softmax_agg(agg_terms=[V.J])], (R.attention(V.I) <= (R.weights(V.I, V.J), R.v(V.I, V.J)) | [F.product], R.q(V.I, V.J) <= (R.n(V.I)[W_qn], R.e(V.I, V.J)[W_qe]), R.k(V.I, V.J) <= (R.n(V.J)[W_kn], R.e(V.I, V.J)[W_ke]), R.v(V.I, V.J) <= (R.n(V.J)[W_vn], R.e(V.I, V.J)[W_ve]),
Этот тип внимания в нашем случае снова почти такой же, как ранее показанное масштабированное скалярное произведение внимания. Единственная разница заключается в добавлении дополнительных терминов для захвата ребер. Подача графа в качестве входных данных в механизм внимания кажется вполне естественной, что не совсем удивительно, учитывая, что Преобразователь — это разновидность Графовой нейронной сети, воздействующей на полносвязные графы (когда не применяется маскирование). В традиционном тензорном представлении это не так очевидно.
Кодер-трансформер
Теперь, когда мы продемонстрировали реализацию механизма Attention, недостающие части для создания всего блока преобразователя кодирования относительно просты.
Вложения
Мы уже видели в реляционном внимании, как можно реализовать вложения. Для традиционного Трансформера вложения будут очень похожими. Мы проецируем входной вектор на три вектора встраивания — ключи, запросы и значения.
R.q(V.I) <= R.input(V.I)[W_q], R.k(V.I) <= R.input(V.I)[W_k], R.v(V.I) <= R.input(V.I)[W_v],
Пропуск соединений, нормализация и сеть с прямой связью
Вложения запросов суммируются с выводом внимания через соединение с пропуском. Полученный вектор затем нормализуется и передается в многослойный персептрон (MLP).
(R.norm1(V.I) <= (R.attention(V.I), R.q(V.I))) | [F.norm],
Для MLP мы реализуем полносвязную нейронную сеть с двумя скрытыми слоями, которые можно элегантно выразить одним логическим правилом.
(R.mlp(V.I)[W_2] <= (R.norm(V.I)[W_1])) | [F.relu],
После этого последнее пропущенное соединение с нормализацией идентично предыдущему.
(R.norm2(V.I) <= (R.mlp(V.I), R.norm1(V.I))) | [F.norm],
Собираем все вместе
Мы собрали все необходимые детали для создания кодировщика Transformer. В декодере используются те же компоненты; следовательно, его реализация будет аналогичной. Давайте объединим все блоки в одну дифференцируемую логическую программу, которую можно встроить в скрипт Python и скомпилировать в нейронные сети с помощью PyNeuraLogic.
R.q(V.I) <= R.input(V.I)[W_q], R.k(V.I) <= R.input(V.I)[W_k], R.v(V.I) <= R.input(V.I)[W_v], R.d_k[1 / math.sqrt(embed_dim)], (R.weights(V.I, V.J) <= (R.d_k, R.k(V.J).T, R.q(V.I))) | [F.product, F.softmax_agg(agg_terms=[V.J])], (R.attention(V.I) <= (R.weights(V.I, V.J), R.v(V.J)) | [F.product], (R.norm1(V.I) <= (R.attention(V.I), R.q(V.I))) | [F.norm], (R.mlp(V.I)[W_2] <= (R.norm(V.I)[W_1])) | [F.relu], (R.norm2(V.I) <= (R.mlp(V.I), R.norm1(V.I))) | [F.norm],
Заключение
В этой статье мы проанализировали архитектуру Transformer и продемонстрировали ее реализацию в нейросимволическом фреймворке под названием PyNeuraLogic. Благодаря этому подходу мы смогли реализовать различные типы трансформеров с небольшими изменениями в коде, демонстрируя, как каждый может быстро изменить и разработать новую архитектуру трансформеров. Это также указывает на безошибочное сходство различных версий Трансформеров и Трансформеров с GNN.
[1]: Васвани А., Шазир Н., Пармар Н., Ушкорейт Дж., Джонс Л., Гомес А., Кайзер Л. и Полосухин И.. (2017). Внимание — это все, что вам нужно.
[2]: Маркус, Г.. (2020). Следующее десятилетие ИИ: четыре шага к надежному искусственному интеллекту.
[3]: Густав Шоурек, Филип Железный и Ондржей Кужелка (2021). Помимо графовых нейронных сетей с поднятыми реляционными нейронными сетями. Машинное обучение, 110(7), 1695–1738.
[4]: Браун Т., Манн Б., Райдер Н., Суббиа М., Каплан Дж., Дхаривал П., Нилакантан А., Шьям П., Састри Г., Аскелл , А., Агарвал, С., Герберт-Восс, А., Крюгер, Г., Хениган, Т., Чайлд, Р., Рамеш, А., Циглер, Д., Ву, Дж., Винтер, К. , Гессе К., Чен М., Сиглер Э., Литвин М., Грей С., Чесс Б., Кларк Дж., Бернер К., МакКэндлиш С., Рэдфорд А. , Суцкевер, И., и Амодей, Д.. (2020). Языковые модели — это ученики с небольшим количеством шансов.
[5]: Чайлд Р., Грей С., Рэдфорд А. и Суцкевер И.. (2019). Генерация длинных последовательностей с разреженными трансформаторами.
[6]: Диао, К., и Лойнд, Р.. (2022). Реляционное внимание: обобщение преобразователей для графоструктурированных задач.
Автор хотел бы поблагодарить Gustav Šír за корректуру этой статьи и предоставление ценных отзывов. Если вы хотите узнать больше о сочетании логики с глубоким обучением, ознакомьтесь с серией статей Густава.