К ГЛУБОКОМУ РЕЛЯЦИОННОМУ ОБУЧЕНИЮ

Помимо трансформеров с 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 токены(0ijn)
  • Тот, который посещает только каждый n-й предыдущий токен ((ij) % 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 за корректуру этой статьи и предоставление ценных отзывов. Если вы хотите узнать больше о сочетании логики с глубоким обучением, ознакомьтесь с серией статей Густава.