Привет, Хабр! Меня зовут Родион Уколов, я занимаюсь искусственным интеллектом в компании Friflex. Мы помогаем компаниям внедрять модели машинного обучения и развиваем свои цифровые продукты.

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

Внимание — все, что вам нужно

GPT — это Generative Pre-trained Transformer, что дословно переводится как «генеративный предобученный трансформер». Почему генеративный и предварительно обученный примерно понятно — все слышали про искусственный интеллект, который учится на больших данных и создает тексты, картинки, код и многое другое. Почему трансформер — уже не так очевидно. Но ответ простой: так называется архитектура GPT. 

Transformer — по-настоящему революционная архитектура. Она впервые появилась на страницах культовой статьи Attention Is All You Need («Внимание — все, что вам нужно»), которую написали Ашиш Васвани и его коллеги из Google. Они предложили сетевую архитектуру без рекурсии и сверток, основанную исключительно на механизмах внимания. До этого в основе популярных моделей преобразования последовательностей чаще всего были сложные рекуррентные или сверточные нейросети. 

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

Transformer может применять механизм внимания параллельно несколько раз с разными наборами параметров — это называется multi-head attention, дословно — многоголовое внимание. Каждый слой смотрит на предложение по-своему. Например, один обращает внимание на смысл каждого слова, другой учитывает, как слова связаны между собой, а третий — какие эмоции передает фраза. 

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

GPT использует Transformer, чтобы предсказывать, какое слово будет следующим в тексте. 

Обучение GPT

GPT-модель до обучения — это мощный, но пустой механизм. Он как огромная библиотека со сложной архитектурой, в которой есть множеством полок (слоев нейронной сети) и мест для книг (параметров), а самих книг нет. 

Чтобы разобраться, как обучать GPT, мы создадим базовую структуру в TensorFlow. Она не будет столь же масштабной и детализированной, как полноценные GPT c миллионами параметров, но поможет понять структуру и процесс создания модели. 

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

(1) Keras — высокоуровневый API. Он дает простой и понятный интерфейс для разработки и обучения моделей. 

(2) Тензоры — многомерные массивы. TensorFlow использует их, чтобы хранить данные: числа, строки или булевы значения.

(3) Слои — основные строительные блоки нейронных сетей. Keras предоставляет множество готовых слоев. Например, полносвязный слой Dense, слой для работы с текстами Embedding и слой для механизмов внимания MultiHeadAttention

Теперь подготовим окружение. 

import tensorflow as tf
from tensorflow.keras.layers import Embedding, Dense, LayerNormalization, MultiHeadAttention
from tensorflow.keras.models import Sequential

Создадим GPT-блок. 

class GPTBlock(tf.keras.layers.Layer):
    def __init__(self, embed_dim, num_heads):
        super(GPTBlock, self).__init__()
        self.att = MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)
        self.ln1 = LayerNormalization(epsilon=1e-6)
        self.ln2 = LayerNormalization(epsilon=1e-6)
        self.ffn = tf.keras.Sequential([
            Dense(embed_dim, activation="relu"), 
            Dense(embed_dim)
        ])

    def call(self, inputs):
        attn_output = self.att(inputs, inputs)
        out1 = self.ln1(inputs + attn_output)
        ffn_output = self.ffn(out1)
        return self.ln2(out1 + ffn_output)

И соберем модель. 

def build_gpt(vocab_size, max_length, embed_dim=256, num_heads=8, num_layers=2):
    model = Sequential()
    model.add(Embedding(input_dim=vocab_size, output_dim=embed_dim, input_length=max_length))
    
    for _ in range(num_layers):
        model.add(GPTBlock(embed_dim=embed_dim, num_heads=num_heads))
        
    model.add(Dense(vocab_size, activation='softmax'))
    return model

vocab_size = 10000  # Примерный размер словаря
max_length = 40  # Максимальная длина последовательности
model = build_gpt(vocab_size, max_length)

Наша упрощенная GPT-модель начинается со слоя embedding. Он преобразует индексы слов в плотные вектора с фиксированным размером. За embedding следуют блоки GPT. В каждом из них есть механизмы многослойного внимания MultiHeadAttention, полносвязные слои Dense и два слоя нормализации LayerNormalization.

Завершает модель выходной полносвязный слой Dense с функцией активации softmax. Он предсказывает вероятность следующего слова в последовательности для каждого слова из словаря. 

Теперь можно перейти к обучению. Ну почти. Сначала подготовим данные — у нас это будут тексты Шекспира. Их нужно преобразовать в числовые данные, которые модель будет обрабатывать. 

import numpy as np
import tensorflow as tf

# Загрузка текста
path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://cs.stanford.edu/people/karpathy/char-rnn/shakespear.txt')
text = open(path_to_file, 'rb').read().decode(encoding='utf-8')

# Создание словаря символов
vocab = sorted(set(text))
char2idx = {u:i for i, u in enumerate(vocab)}
idx2char = np.array(vocab)

# Преобразование текста в числовой формат
text_as_int = np.array([char2idx[c] for c in text])

# Создание обучающих примеров и целей
seq_length = 100
examples_per_epoch = len(text)//(seq_length+1)
char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)

sequences = char_dataset.batch(seq_length+1, drop_remainder=True)

def split_input_target(chunk):
    input_text = chunk[:-1]
    target_text = chunk[1:]
    return input_text, target_text

dataset = sequences.map(split_input_target)

# Создание батчей для обучения
BATCH_SIZE = 64
BUFFER_SIZE = 10000

dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)

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

# Параметры модели
vocab_size = len(vocab)  # размер словаря
max_length = 40  # максимальная длина последовательности
embed_dim = 256  # размерность векторов вложения
num_heads = 8  # количество "голов" в механизме внимания
num_layers = 2  # количество слоёв

model = build_gpt(vocab_size, max_length, embed_dim, num_heads, num_layers)

Скомпилируем модель: укажем оптимизатор, функцию потерь и метрики для отслеживания. 

model.compile(optimizer='adam', loss=tf.losses.SparseCategoricalCrossentropy(from_logits=True), metrics=['accuracy'])

(1) Оптимизатор adam — один из самых популярных. Он помогает адаптировать скорость обучения для каждого параметра модели. 

(2) Функция потерь sparse_categorical_crossentropy подходит для задач классификации с несколькими классами. Она измеряет расхождение между прогнозом модели и истинным распределением классов. 

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

Наконец, обучение. 

EPOCHS = 10

history = model.fit(dataset, epochs=EPOCHS)

Эпоха — это один проход по всему обучающему набору данных. Вот что происходит каждую эпоху:

(1) Прямое распространение: модель получает входные данные, проводит их через все слои и выдает предсказания.

(2) Вычисление ошибки: функция потерь помогает сравнивать предсказания с истинными метками.

(3) Обратное распространение: на основе ошибок модель обновляет свои веса, чтобы минимизировать эти ошибки в будущих предсказаниях.

(4) Повторение: процесс повторяется для каждой партии входных данных.

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

Что еще почитать про обучение GPT

Могу посоветовать несколько фундаментальных статей: 

Language Models are Few-Shot Learners

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

Pathways Language Model: Scaling to 500B Parameters and Beyond

Статья рассказывает про архитектуру, обучение и возможности 500-миллиардной языковой модели. 

Training Compute-Optimal Large Language Models

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

Controllable Text Generation with GPT-3

Статья исследует, как управлять генерацией текста GPT-3, чтобы получать точный результат. 

Комментарии (3)


  1. ilye
    17.04.2024 14:08

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


    1. TrustGreen
      17.04.2024 14:08
      +1

      Доброго времени суток!

      Говоря о примере, хочу отметить, что данная статья является туториалом к обучению модели GPT, поэтому задача получения ответа от модели, а также её дальнейшее переобучение с другими параметрами ложится на плечи энтузиастов-читателей данной статьи. Если желаете и Вам интересно, соберите модель у себя и опробуйте её!

      Касаемо того, что модель выводит. Во время работы над данной статьёй нам удалось обучить модель, которая вполне осмысленно писала текст на английском языке. Это точно не был "бессмысленный набор символов" и "несвязанные слова". Присутствовала определенная структура предложения, характерная для английских текстов. Быть может, мы когда-нибудь затронем это в будущих статьях, посвященных ИИ.


      1. ilye
        17.04.2024 14:08

        Запустил ваш "туториал"

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

        После 1000 эпох аккуратность 45%

        Строка на входе

        From fairest creatures we desire increase,
        That thereby beauty's rose might never die,
        But as th

        Строка на выходе
        oeueaonne ehaee nhre eai a ene andee ne
        aahenhahe e e ae nrh seaeue aeneehad e ea n
        aaurhaneahe