Внутри GPT — I Понимание генерации текста

Inside GPT - Understanding text generation

Простое объяснение модели, стоящей за ChatGPT

молодой Достоевский знакомится с генеративным искусственным интеллектом, созданным Midjourney

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

За популярной магией ChatGPT скрывается непопулярная логика. Вы вводите запрос в ChatGPT, и он генерирует текст, который, вероятно, похож на ответы человека. Как ему удается понять ваш запрос и создать связные и понятные ответы?

Трансформерные нейронные сети. Архитектура, разработанная для обработки неструктурированных данных в больших объемах, в нашем случае, текста. Когда мы говорим об архитектуре, мы имеем в виду серию математических операций, выполненных в нескольких слоях параллельно. Через эту систему уравнений были внедрены несколько инноваций, которые помогли нам преодолеть долгосуществующие проблемы генерации текста. Проблемы, с которыми мы боролись до 5 лет назад.

Если GPT уже существует 5 лет (действительно, статья о GPT была опубликована в 2018 году), то GPT – это не новость? Почему он стал недавно чрезвычайно популярным? В чем разница между GPT 1, 2, 3, 3.5 (ChatGPT) и 4?

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

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

Выходные значения? В этом смысле это текст, созданный языковой моделью, верно? Да. Тогда, что является входными значениями? Это мой запрос? Да, но не полностью. Так что еще находится за ним?

Прежде чем перейти к различным стратегиям декодирования текста, которые будут темой следующего блог-поста, полезно устранить неоднозначность. Вернемся к фундаментальному вопросу, который мы задали в начале. Как оно понимает человеческий язык?

Generative Pre-trained Transformers. Три слова, которые расшифровывают аббревиатуру GPT. Мы уже коснулись части “Transformers”, что она представляет собой архитектуру, где выполняются сложные вычисления. Но что именно мы вычисляем? Где вы берете эти числа? Это языковая модель, и все, что вам нужно сделать, это написать текст, и вы вводите некоторый текст, как можно вычислить текст?

Данные нейтральны. Все данные одинаковы, будь то текст, звук или изображение.¹

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

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

Представим ситуацию, где в нашем языковом корпусе есть 50 000 токенов (подобно GPT-2, в котором их 50 257). Как мы представим эти единицы после токенизации?

Предложение: "студенты празднуют выпускной вечер с большой вечеринкой"Метки токенов: ['[CLS]', 'студенты', 'празднуют', 'выпускной', 'вечер', 'с', 'большой', 'вечеринкой', '[SEP]']Идентификаторы токенов: tensor([[ 101, 2493, 8439, 1996, 7665, 2007, 1037, 2502, 2283,  102]])

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

Мощность множества в математике имеет значение. 101 и 2493 в качестве представления токенов имеют значение для модели. Потому что помните, что все, что мы делаем, в основном, это умножение и сложение больших блоков чисел. Поэтому умножение числа либо на 101, либо на 2493 имеет значение. Но как мы можем убедиться, что токен, который представлен числом 101, не менее важен, чем 2493, просто потому что мы случайным образом разделили его на токены? Как мы можем закодировать слова, не вызывая фиктивного упорядочивания?

One-hot encoding. Разреженное отображение токенов. One-hot encoding – это техника, при которой каждый токен проецируется как двоичный вектор. Это означает, что вектор содержит только один элемент, равный 1 (“горячий”), а остальные элементы равны 0 (“холодные”).

Рисунок 1: Пример векторного кодирования one-hot

Токены представлены вектором, длина которого соответствует общему числу токенов в нашем корпусе. Проще говоря, если у нас есть 50 тыс. токенов в нашем языке, то каждый токен представлен вектором размером 50 тыс., в котором только один элемент равен 1, а остальные равны 0. Поскольку каждый вектор в этой проекции содержит только один ненулевой элемент, он называется разреженным представлением. Однако, как вы можете подумать, этот подход очень неэффективен. Да, мы удаляем искусственную мощность между идентификаторами токенов, но мы не можем извлекать какую-либо информацию о семантике слов. Мы не можем понять, относится ли слово “party” к празднованию или к политической организации, используя разреженные векторы. Кроме того, представление каждого токена вектором размером 50 тыс. означает, что у нас будет 50 тыс. векторов длиной 50 тыс. Это очень неэффективно с точки зрения потребления памяти и вычислений. К счастью, у нас есть более эффективные решения.

Векторные представления. Плотное представление токенов. Токенизированные единицы проходят через слой векторного представления, где каждый токен преобразуется в непрерывное векторное представление фиксированного размера. Например, в случае GPT 3 каждый токен представлен вектором из 768 чисел. Эти числа назначаются случайным образом и затем обучаются моделью после просмотра большого количества данных (обучение).

Метка токена: "party"
Токен: 2283
Длина вектора представления: 768
Форма тензора представления: ([1, 10, 768])
Вектор представления: tensor([ 2.9950e-01, -2.3271e-01,  3.1800e-01, -1.2017e-01, -3.0701e-01,        -6.1967e-01,  2.7525e-01,  3.4051e-01, -8.3757e-01, -1.2975e-02,        -2.0752e-01, -2.5624e-01,  3.5545e-01,  2.1002e-01,  2.7588e-02,        -1.2303e-01,  5.9052e-01, -1.1794e-01,  4.2682e-02,  7.9062e-01,         2.2610e-01,  9.2405e-02, -3.2584e-01,  7.4268e-01,  4.1670e-01,        -7.9906e-02,  3.6215e-01,  4.6919e-01,  7.8014e-02, -6.4713e-01,         4.9873e-02, -8.9567e-02, -7.7649e-02,  3.1117e-01, -6.7861e-02,        -9.7275e-01,  9.4126e-02,  4.4848e-01,  1.5413e-01,  3.5430e-01,         3.6865e-02, -7.5635e-01,  5.5526e-01,  1.8341e-02,  1.3527e-01,        -6.6653e-01,  9.7280e-01, -6.6816e-02,  1.0383e-01,  3.9125e-02,        -2.2133e-01,  1.5785e-01, -1.8400e-01,  3.4476e-01,  1.6725e-01,        -2.6855e-01, -6.8380e-01, -1.8720e-01, -3.5997e-01, -1.5782e-01,         3.5001e-01,  2.4083e-01, -4.4515e-01, -7.2435e-01, -2.5413e-01,         2.3536e-01,  2.8430e-01,  5.7878e-01, -7.4840e-01,  1.5779e-01,        -1.7003e-01,  3.9774e-01, -1.5828e-01, -5.0969e-01, -4.7879e-01,        -1.6672e-01,  7.3282e-01, -1.2093e-01,  6.9689e-02, -3.1715e-01,        -7.4038e-02,  2.9851e-01,  5.7611e-01,  1.0658e+00, -1.9357e-01,         1.3133e-01,  1.0120e-01, -5.2478e-01,  1.5248e-01,  6.2976e-01,        -4.5310e-01,  2.9950e-01, -5.6907e-02, -2.2957e-01, -1.7587e-02,        -1.9266e-01,  2.8820e-02,  3.9966e-03,  2.0535e-01,  3.6137e-01,         1.7169e-01,  1.0535e-01,  1.4280e-01,  8.4879e-01, -9.0673e-01,                                            …                                             …                                             …                           ])

Выше приведен пример вектора вложения слова “вечеринка”.

Теперь у нас есть векторы размером 50,000×786, что по сравнению с one-hot кодированием размером 50,000×50,000 значительно более эффективно.

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

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

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

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# Пример векторов вложения для "кошка", "собака", "машина" и "банан"
embedding_cat = np.array([0.5, 0.3, -0.1, 0.9])
embedding_dog = np.array([0.6, 0.4, -0.2, 0.8])
embedding_car = np.array([0.5, 0.3, -0.1, 0.9])
embedding_banana = np.array([0.1, -0.8, 0.2, 0.4])

Используя приведенные выше векторы, давайте вычислим оценку сходства с помощью косинусной меры сходства. Человеческая логика найдет слова “собака” и “кошка” более связанными друг с другом, чем слова “банан” и “машина”. Можем ли мы ожидать, что математика сможет смоделировать нашу логику?

# Вычисление косинусного сходства
similarity = cosine_similarity([embedding_cat], [embedding_dog])[0][0]
print(f"Косинусное сходство между 'кошка' и 'собака': {similarity:.4f}")
# Вычисление косинусного сходства
similarity_2 = cosine_similarity([embedding_car], [embedding_banana])[0][0]
print(f"Косинусное сходство между 'машина' и 'банан': {similarity:.4f}")

Мы видим, что у слов “кошка” и “собака” очень высокая оценка сходства, в то время как у слов “машина” и “банан” она очень низкая. Теперь представьте себе векторы вложения длиной 768 вместо 4 для каждого из 50000 токенов в нашем языковом корпусе. Вот как мы можем найти слова, которые связаны друг с другом.

Теперь давайте рассмотрим два предложения ниже, которые имеют более высокую семантическую сложность.

"студенты празднуют выпуск с большой вечеринкой""заместитель лидера высоко уважаем в партии"

Слово “вечеринка” в первом и втором предложениях несет разные значения. Как крупные языковые модели способны отобразить разницу между “вечеринкой” как политической организацией и “вечеринкой” как праздником?

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

Self-attention. Решение снова было предложено нейронными сетями трансформера. Мы генерируем новый набор весов, которые называются матрицами запроса, ключа и значения. Эти веса учатся представлять векторы вложения токенов как новый набор вложений. Как? Просто, взяв взвешенное среднее исходных вложений. Каждый токен “обращается” к каждому другому токену (включая себя) во входном предложении и вычисляет набор весов внимания или, другими словами, новые так называемые “контекстные вложения”.

Все, что делает это действительно, – это отображает важность слов во входном предложении, присваивая новый набор чисел (веса внимания), которые вычисляются с использованием вложений токенов.

Рисунок 2: Веса внимания токена в разных контекстах (вид внимания BertViz)

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

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

иллюстрация матриц запроса, ключа и значения и их конечных вероятностей (Softmax)

Визуализация показывает векторы q и k в виде вертикальных полосок, где насыщенность каждой полоски отражает ее величину. Связи между токенами означают веса, определенные вниманием, указывая, что вектор q для “party” наиболее существенно соотносится с вектором k для “is”, “deputy” и “respected”.

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

Еще один важный трюк, введенный в трансформеры, заключается в добавлении позиционных кодировок к векторным эмбеддингам. Просто потому, что мы хотим учесть информацию о позиции слова. Это повышает вероятность более точного предсказания следующего токена в контексте истинного предложения. Это важная информация, потому что часто перестановка слов полностью меняет контекст. Например, предложения “Тим преследовал облака всю свою жизнь” и “облака преследовали Тима всю жизнь” совершенно разные по сути.

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

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

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

Ссылки:

A. Васвани, Н. Шазеер, Н. Пармар, Дж. Ушкорейт, Л. Джонс, А. Н. Гомес, Л. Кайзер и И. Полосухин, “Внимание – это все, что вам нужно”, в Advances in Neural Information Processing Systems 30 (NIPS 2017), 2017.

J. Виг, “Многомасштабная визуализация внимания в модели трансформера”, В протоколах 57-го ежегодного собрания Ассоциации вычислительной лингвистики: Демонстрации систем, c. 37–42, Флоренция, Италия, Ассоциация вычислительной лингвистики, 2019.

L. Тансталл, Л. фон Верра и Т. Вольф, “Обработка естественного языка с помощью трансформеров, пересмотренное издание”, O’Reilly Media, Inc., Май 2022, ISBN: 9781098136796.

1 – Блог Ленивого Программиста