А-Я Трансформеров Все, что вам нужно знать

А-Я трансформеров все, что вам нужно знать

Все, что вам нужно знать о Трансформаторах и как ими пользоваться

Изображение от автора

Почему еще один учебник по трансформаторам?

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

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

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

В этом руководстве я стремлюсь заполнить этот пробел в знаниях. Руководство, которое даст вам крепкое понимание трансформаторов, погружение в архитектуру и реализацию с нуля.

Я настоятельно рекомендую вам следовать коду на Github:

awesome-ai-tutorials/NLP/007 – Transformers From Scratch at main ·…

Лучшая коллекция учебников по искусственному интеллекту, чтобы сделать вас мастером Data Science! – awesome-ai-tutorials/NLP/007 – Transformers…

github.com

Наслаждайтесь! 🤗

Немного истории сначала:

Многие связывают концепцию механизма внимания с известной статьей “Attention is All You Need” команды Google Brain. Однако это лишь часть истории.

Корни механизма внимания можно отследить еще в более ранней статье titled “Neural Machine Translation by Jointly Learning to Align and Translate”, авторами которой являются Дмитрий Багданов, Кьенхюн Чо и Йошуа Бенджио.

Основной задачей Багданова было решение ограничений Рекуррентных нейронных сетей (РНС). Конкретно, при кодировке длинных предложений векторами с использованием РНС, часто терялась важная информация.

Исходя из аналогий с переводческими упражнениями — где часто перечитывают исходное предложение при переводе — Багданов старался распределить веса между скрытыми состояниями в РНС. Этот подход привел к впечатляющим результатам, которые изображены на следующей диаграмме.

Изображение из Neural machine translation by jointly learning to align and translate

Однако Багданов не был единственным, кто решал эту проблему. Подчерпнув вдохновение из его революционной работы, команда Google Brain выдвинула смелую идею:

“Почему бы не обойти все и сосредоточиться только на механизме внимания?”

Они верили, что не РНС, а именно механизм внимания является главным двигателем успеха.

Эта уверенность достигла своего высшего проявления в их статье, озаглавленной “Attention is All You Need”.

Увлекательно, не так ли?

Архитектура Трансформатора

1. Прежде всего, вложения

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

Вложения, Изображение из статьи, изменено автором

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

Каждое слово проходит процесс преобразования в вектор. Этот процесс преобразования называется «встраивание». Каждый из этих векторов или «вложений» имеет размер d_model = 512.

Что же такое это Встраивание? В его основе лежит линейное отображение (матрица), обозначаемое E. Можно визуализировать его как матрицу размером (d_model, vocab_size), где vocab_size – это размер нашего словаря.

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

Перейдем к написанию кода для этой части:


class Embeddings(nn.Module):
    def __init__(self, d_model, vocab):
        super(Embeddings, self).__init__()
        self.lut = nn.Embedding(vocab, d_model)
        self.d_model = d_model
    
    def forward(self, x):
        return self.lut(x) * math.sqrt(self.d_model)

Примечание: мы умножаем на d_model для целей нормализации (объяснение будет позже)

Примечание 2: Я лично задавался вопросом, используется ли здесь предварительно обученное встраивание или хотя бы начинается с предварительно обученного и его донастраивают. Но нет, встраивание полностью обучается с нуля и инициализируется случайным образом.

Позиционное кодирование

Зачем нам нужно позиционное кодирование?

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

Теперь вы можете задаться вопросом: “Почему бы просто не добавить простое приращение, например, +1 для первого слова, +2 для второго и так далее?” У этого подхода есть несколько проблем:

  1. Многомерность: Каждый токен представлен в 512 измерениях. Простое приращение недостаточно для охвата такого сложного пространства.
  2. Проблемы с нормализацией: Идеально было бы, чтобы наши значения находились в пределах от -1 до 1. Поэтому прямое добавление больших чисел (например, +2000 для длинного текста) станет проблематичным.
  3. Зависимость от длины последовательности: Прямое добавление приращений не является масштабно-независимым. Для длинного текста, где позиция может быть +5000, это число не отражает действительную относительную позицию токена в его ассоциированном предложении. И значение слова больше зависит от его относительной позиции в предложении, чем от его абсолютной позиции в тексте.

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

Имея нашу матрицу размером (seq_len, d_model), наша цель – добавить другую матрицу той же размерности, позиционное кодирование,.

Вот основная концепция:

  1. Для каждого токена авторы предлагают предоставить синусовую координату для попарных измерений (2k) и косинусную координату для (2k+1).
  2. Если мы закрепим позицию токена и будем двигать измерение, мы увидим, что синус/косинус уменьшаются в частоте.
  3. Если мы посмотрим на токен, который находится дальше в тексте, этот феномен происходит быстрее (частота увеличивается).
Изображение из статьи

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

Позиционное Кодирование, Изображение от автора
class PositionalEncoding(nn.Module):    "Реализация функции PE."    def __init__(self, d_model, dropout, max_len=5000):        super(PositionalEncoding, self).__init__()        self.dropout = nn.Dropout(p=dropout)        # Вычисление позиционного кодирования один раз в логарифмическом пространстве.        pe = torch.zeros(max_len, d_model)        position = torch.arange(0, max_len).unsqueeze(1)        div_term = torch.exp(            torch.arange(0, d_model, 2) * -(math.log(10000.0) / d_model)        )        pe[:, 0::2] = torch.sin(position * div_term)        pe[:, 1::2] = torch.cos(position * div_term)        pe = pe.unsqueeze(0)        self.register_buffer("pe", pe)    def forward(self, x):        x = x + self.pe[:, : x.size(1)].requires_grad_(False)        return self.dropout(x)

Механизм Внимания (Одна Голова)

Давайте погрузимся в основную концепцию статьи Google: Механизм Внимания

Общее понимание:

В основе своей механизм внимания является коммуникационным механизмом между векторами/токенами. Он позволяет модели сосредоточиться на определенных частях входных данных при генерации вывода. Можно сказать, что это как прожектор, который освещает определенные части ваших входных данных. Этот “прожектор” может быть ярче на более значимых частях (уделять им больше внимания) и тусклее на менее значимых частях.

Для предложений внимание помогает определить отношения между словами. Некоторые слова тесно связаны друг с другом по смыслу или функции в предложении, в то время как другие – нет. Механизм внимания количественно оценивает эти отношения.

Пример:

Рассмотрим предложение: “Она дала ему свою книгу”.

Если сфокусироваться на слове “свою”, механизм внимания может определить:

  • Оно имеет сильную связь с “книгу”, потому что “свою” указывает на владение “книгой”.
  • Оно имеет связь с “она”, так как “она” и “свою” вероятно относятся к одному и тому же объекту.
  • Оно имеет более слабую связь с другими словами, такими как “дала” или “ему”.

Подробное изучение Механизма Внимания

Масштабированное внимание на основе скалярного произведения, изображение из статьи

Для каждого токена генерируются три вектора:

  1. Запрос (Q):

Понимание: Думайте о запросе как о “вопросе”, который задает каждый токен. Он представляет текущее слово и пытается определить, какие части последовательности являются для него релевантными.

2. Ключ (K):

Понимание: Ключ можно рассматривать как “идентификатор” для каждого слова в последовательности. Когда запрос “задает” свой вопрос, ключ помогает “ответить”, определяя насколько важно каждое слово в последовательности для запроса.

3. Значение (V):

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

Как генерируются Q, K, V?

Генерация Q, K, V, изображение от автора

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

Формула внимания, изображение из статьи

Давайте проиллюстрируем это на примере:

Предположим, у нас есть один запрос, и мы хотим определить результат внимания с помощью K и V:

Генерация Q, K, V, изображение от автора

Теперь вычислим сходства между q1 и ключами:

Скалярное произведение, изображение от автора

Хотя числа 3/2 и 1/8 могут показаться относительно близкими, экспоненциальная природа функции softmax усиливает разницу между ними.

Веса внимания, изображение от автора

Это различие указывает на то, что q1 имеет более яркую связь с k1, чем с k2.

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

Внимание, изображение от автора

Отлично! Повторение этой операции для каждого токена (от q1 до qn) дает коллекцию из n векторов.

На практике эта операция векторизуется в матричное умножение для большей эффективности.

Давайте запишем это:

def attention(query, key, value, mask=None, dropout=None):    "Вычисляет 'Scaled Dot Product Attention'"    d_k = query.size(-1)    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)    if mask is not None:        scores = scores.masked_fill(mask == 0, -1e9)    p_attn = scores.softmax(dim=-1)    if dropout is not None:        p_attn = dropout(p_attn)    return torch.matmul(p_attn, value), p_attn

Многоголовое внимание

В чем проблема с одноголовым вниманием?

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

Чтобы преодолеть эту ограничение, мы представляем многоголовое внимание. Основная идея? Позволим каждому токену задавать несколько вопросов (запросов) одновременно, выполняя процесс внимания параллельно ‘h’ раз. Оригинальный Трансформер использует 8 голов.

Многоголовое внимание, изображение из статьи

Получив результаты от 8 голов, мы объединяем их в матрицу.

Многоголовое внимание, изображение из статьи

Это также просто в реализации, но мы должны быть осторожны с размерностями:

class MultiHeadedAttention(nn.Module):    def __init__(self, h, d_model, dropout=0.1):        "Принимает размер модели и количество голов."        super(MultiHeadedAttention, self).__init__()        assert d_model % h == 0        # Мы предполагаем, что d_v всегда равно d_k        self.d_k = d_model // h        self.h = h        self.linears = clones(nn.Linear(d_model, d_model), 4)        self.attn = None        self.dropout = nn.Dropout(p=dropout)    def forward(self, query, key, value, mask=None):        "Реализует Фигуру 2"        if mask is not None:            # Одна и та же маска применяется ко всем h головам.            mask = mask.unsqueeze(1)        nbatches = query.size(0)        # 1) Произвести линейные проекции батч-пакета из d_model => h x d_k        query, key, value = [            lin(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)            for lin, x in zip(self.linears, (query, key, value))        ]        # 2) Применить внимание ко всем проекционным векторам батч-пакета.        x, self.attn = attention(query, key, value, mask=mask, dropout=self.dropout)        # 3) "Конкат" с использованием view и применение финальной линии.        x = x.transpose(1, 2).contiguous().view(nbatches, -1, self.h * self.d_k)        del query        del key        del value        return self.linears[-1](x)

Теперь вы должны начать понимать, почему Трансформеры настолько мощны, они полностью используют параллелизм.

Сборка частей Трансформера

На высоком уровне, Трансформер – это комбинация 3 элементов: Encoder, Decoder и Generator

Endoder, Decoder, Generator, Изображение из статьи, изменено автором

1. Энкодер

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

2. Декодер

  • Цель: генерировать выходную последовательность, используя закодированную последовательность от Энкодера.
  • Примечание: Декодер в Трансформере отличается от типичного декодера автоэнкодера. В Трансформере декодер рассматривает не только закодированный вывод, но и рассматривает уже сгенерированные токены.

3. Генератор

  • Цель: преобразовать вектор в токен. Он выполняет это, проецируя вектор на размер словаря и затем выбирает наиболее вероятный токен с помощью функции softmax.

Программируем это:

class EncoderDecoder(nn.Module):    """    Стандартная архитектура Encoder-Decoder. Основа для этой и многих других моделей.    """    def __init__(self, encoder, decoder, src_embed, tgt_embed, generator):        super(EncoderDecoder, self).__init__()        self.encoder = encoder        self.decoder = decoder        self.src_embed = src_embed        self.tgt_embed = tgt_embed        self.generator = generator    def forward(self, src, tgt, src_mask, tgt_mask):        "Принимает и обрабатывает маскированные последовательности src и tgt."        return self.decode(self.encode(src, src_mask), src_mask, tgt, tgt_mask)    def encode(self, src, src_mask):        return self.encoder(self.src_embed(src), src_mask)    def decode(self, memory, src_mask, tgt, tgt_mask):        return self.decoder(self.tgt_embed(tgt), memory, src_mask, tgt_mask)class Generator(nn.Module):    "Обычный шаг генерации - линейное преобразование + softmax."    def __init__(self, d_model, vocab):        super(Generator, self).__init__()        self.proj = nn.Linear(d_model, vocab)    def forward(self, x):        return log_softmax(self.proj(x), dim=-1)

Здесь одно замечание: «src» относится к входной последовательности, а «tgt» относится к генерируемой последовательности. Помните, что мы генерируем вывод последовательно, токен за токеном, поэтому нам также необходимо отслеживать целевую последовательность.

Сложение энкодеров

Энкодер Transformer’а не состоит из одного слоя. Фактически, это стек из N слоев. В частности:

  • Энкодер в оригинальной модели Transformer состоит из стека из N=6 одинаковых слоев.

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

  • Блок (1) Механизм самовнимания: Помогает энкодеру сконцентрироваться на разных словах во входе при генерации закодированного представления.
  • Блок (2) Полносвязная нейронная сеть: Маленькая нейронная сеть, применяемая независимо к каждой позиции.
Слой энкодера, резидуальные соединения и слой нормализации,изображение из статьи, модифицированное автором

Теперь программировать это:

Сначала SublayerConnection:

Мы следуем общей архитектуре, и мы можем заменить “sublayer” на “self-attention” или “FFN”

class SublayerConnection(nn.Module):    """    Резидуальное соединение, за которым следует слой нормализации.    Упрощение кода: нормализация делается первой, а не последней.    """    def __init__(self, size, dropout):        super(SublayerConnection, self).__init__()        self.norm = nn.LayerNorm(size)  # Используем LayerNorm из PyTorch        self.dropout = nn.Dropout(dropout)    def forward(self, x, sublayer):        "Применить резидуальное соединение к любому блоку с одинаковым размером."        return x + self.dropout(sublayer(self.norm(x)))

Теперь мы можем определить полный слой энкодера:

class EncoderLayer(nn.Module):    "Энкодер состоит из самовнимания и прямой связи (определены ниже)"    def __init__(self, size, self_attn, feed_forward, dropout):        super(EncoderLayer, self).__init__()        self.self_attn = self_attn        self.feed_forward = feed_forward        self.sublayer = clones(SublayerConnection(size, dropout), 2)        self.size = size    def forward(self, x, mask):        # самовнимание, блок 1        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, mask))        # прямая связь, блок 2        x = self.sublayer[1](x, self.feed_forward)        return x

Слой энкодера готов, теперь просто соединим их вместе, чтобы сформировать весь энкодер:

def clones(module, N):    "Производит N идентичных слоев."    return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])class Encoder(nn.Module):    "Основной энкодер представляет собой стек из N слоев"    def __init__(self, layer, N):        super(Encoder, self).__init__()        self.layers = clones(layer, N)        self.norm = nn.LayerNorm(layer.size)    def forward(self, x, mask):        "Пропустите вход (и маску) через каждый слой по очереди."        for layer in self.layers:            x = layer(x, mask)        return self.norm(x)

Декодер

Декодер, как и Энкодер, структурирован с несколькими идентичными слоями, расположенными друг на друге. Количество этих слоев обычно составляет 6 в оригинальной модели Трансформера.

В чём отличие Декодера от Энкодера?

В декодер добавляется третий субслой, взаимодействующий с энкодером: это перекрёстное внимание (кросс-аттеншн)

  • Субслой (1) такой же, как в Энкодере. Это механизм само-аттеншн (внимания к самому себе), что означает, что мы генерируем все (Q, K, V) из токенов, поданных на вход Декодеру.
  • Субслой (2) – новый механизм коммуникации: перекрёстное внимание. Он называется так потому, что мы используем вывод из (1) для генерации запросов, и мы используем вывод из Энкодера для генерации ключей и значений (K, V). Иными словами, для генерации предложения нам нужно смотреть как на то, что мы до сих пор сгенерировали с помощью Декодера (само-аттеншн), так и на то, что мы спросили в первую очередь в Энкодере (перекрёстное внимание).
  • Субслой (3) идентичен с Энкодером.
Слой Декодера, само-аттеншн, перекрёстное внимание, Изображение из статьи, измененное автором

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

class DecoderLayer(nn.Module):
    "Декодер состоит из само-аттеншн, перекрёстной аттеншн и прямого прохода (определены ниже)"
    def __init__(self, size, self_attn, src_attn, feed_forward, dropout):
        super(DecoderLayer, self).__init__()
        self.size = size
        self.self_attn = self_attn
        self.src_attn = src_attn
        self.feed_forward = feed_forward
        self.sublayer = clones(SublayerConnection(size, dropout), 3)
    def forward(self, x, memory, src_mask, tgt_mask):
        "Следуйте рисунку 1 (справа) для связей."
        m = memory
        x = self.sublayer[0](x, lambda x: self.self_attn(x, x, x, tgt_mask)) # Новый субслой (перекрёстное внимание)
        x = self.sublayer[1](x, lambda x: self.src_attn(x, m, m, src_mask))
        return self.sublayer[2](x, self.feed_forward)

А теперь мы можем объединить N=6 Слоев Декодера, чтобы создать Декодер:

class Decoder(nn.Module):
    "Обобщенный декодер с N слоями и маскировкой."
    def __init__(self, layer, N):
        super(Decoder, self).__init__()
        self.layers = clones(layer, N)
        self.norm = nn.LayerNorm(layer.size)
    def forward(self, x, memory, src_mask, tgt_mask):
        for layer in self.layers:
            x = layer(x, memory, src_mask, tgt_mask)
        return self.norm(x)

На данном этапе мы поняли примерно 90% того, что представляет собой Трансформер. Осталось еще несколько деталей:

Детали модели Трансформера:

Постановка отступов:

  • В типичном трансформере есть максимальная длина для последовательностей (например, “max_len=5000”). Это определяет самую длинную последовательность, с которой модель может справиться.
  • Однако реальные предложения могут иметь разные длины. Чтобы обработать более короткие предложения, мы используем отступы (padding).
  • Отступ – это добавление специальных “токенов отступа”, чтобы все последовательности в пакете имели одинаковую длину.
Отступы, изображение от автора

Маскировка

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

Два сценария маскировки:

  • Маскировка исхода (src_masking): Поскольку мы добавили токены отступа к последовательностям, мы не хотим, чтобы модель обращала внимание на эти бессмысленные токены. Поэтому мы их маскируем.
  • Маскировка при прогнозировании (tgt_masking) или маскировка вперёд/последовательная маскировка: В декодере, при последовательном генерировании токенов, каждый токен должен быть под влиянием только предыдущих токенов, а не будущих. Например, при генерации пятого слова в предложении, оно не должно знать о шестом слове. Это обеспечивает последовательное генерирование токенов.
Causal Masking / Look-Ahead Masking, изображение автора

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

Маскирование, трюк в софтмаксе, изображение автора

FFN: Feed Forward Network

  • Слой “Feed Forward” на диаграмме Трансформера может быть немного вводящим в заблуждение. Это не только одна операция, а последовательность из них.
  • FFN состоит из двух линейных слоев. Интересно то, что входные данные, которые могут иметь размерность d_model=512, сначала преобразуются в более высокую размерность d_ff=2048, а затем отображаются обратно в их исходную размерность (d_model=512).
  • Это можно представить в виде данных, которые “расширяются” в середине операции, прежде чем быть “сжатыми” обратно до исходного размера.
Изображение из статьи, измененное автором

Это легко реализовать в коде:

class PositionwiseFeedForward(nn.Module):    "Реализует уравнение FFN."    def __init__(self, d_model, d_ff, dropout=0.1):        super(PositionwiseFeedForward, self).__init__()        self.w_1 = nn.Linear(d_model, d_ff)        self.w_2 = nn.Linear(d_ff, d_model)        self.dropout = nn.Dropout(dropout)    def forward(self, x):        return self.w_2(self.dropout(self.w_1(x).relu()))

Вывод

Беспрецедентный успех и популярность модели Трансформера можно объяснить несколькими ключевыми факторами:

  1. Гибкость. Трансформеры могут работать с любой последовательностью векторов. Эти векторы могут быть вложениями для слов. Легко преобразовать это в Computer Vision, преобразовав изображение в различные патчи и развернув патч в вектор. Или даже в звуке, мы можем разбить аудио на различные части и векторизовать их.
  2. Общность: С минимальным индуктивным смещением Трансформер свободен для захвата сложных и тонких паттернов в данных, что позволяет ему учиться и обобщаться лучше.
  3. Скорость и эффективность: Используя огромные вычислительные возможности графических процессоров, Трансформеры разработаны для параллельной обработки.

Спасибо за чтение! Прежде чем уйти:

Вы можете запустить эксперименты с моим репозиторием Transformer GitHub.

Для получения больше потрясающих учебников проверьте мою компиляцию учебников по AI на GitHub

GitHub — FrancoisPorcher/awesome-ai-tutorials: Лучшая коллекция учебников по AI, чтобы сделать вас…

The best collection of AI tutorials to make you a boss of Data Science! — GitHub …

github.com

Вы должны получать мои статьи в своем почтовом ящике. Подпишитесь здесь.

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

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

Ссылки

Чтобы продвинуться дальше

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

  • Позиционное кодирование: были сделаны значительные улучшения, вам может быть интересно посмотреть на “Относительное позиционное кодирование” и “Поворотное позиционное вложение (RoPE)”
  • Нормализация слоев и различия с пакетной нормализацией, нормализацией групп
  • Остаточные соединения и их влияние на сглаживание градиента
  • Улучшения, внесенные в BERT (Roberta, ELECTRA, Camembert)
  • Дистилляция больших моделей в меньшие модели
  • Применение Трансформеров в других областях (главным образом визуальной и звуковой)
  • Связь между Трансформерами и Графовыми Нейронными Сетями