Эта статья одновременно публикуется в публичном аккаунте WeChat: А Ли голосует за то, чтобы накормить дом
Управляемое чтение
Это следует рассматривать как обзорную статью.Когда Трансформер почти правил всеми полями, вспоминать Трансформера было немного расплывчато. Тем не менее, статус Трансформера в области глубокого обучения должен быть бесспорным, будь то Берт в области НЛП или Визуальный Трансформер в области CV, мы это видим. Поэтому необходимо иметь глубокое понимание Transformer.В этой статье мы представим структуру Transformer и некоторые детали его реализации в виде обзора.
Рекомендуемые статьи, связанные с серией Берта
Чему учит BERT: узнайте, почему BERT такой сильный
Технология квантования и динамическое квантование Альберта
DistillBert: Берт слишком дорого? Я дешевый и простой в использовании
[Обмен документами] | RoBERTa: Привет, XLNet здесь? Меня избили
Введение в статью XLNet - за спиной Берта
- Ссылка на бумагу:АР Вест V.org/PDF/1706.03…
- Исходный код:GitHub.com/ просто обожаю80112…
Механизм внимания
Преобразователь представляет собой структуру кодирования-декодирования Структура кодировщика и декодера аналогична, и они объединены несколькими идентичными слоями. Каждый уровень кодировщика разделен на два подуровня: уровень внимания и уровень полной связи. Каждый уровень в декодере разделен на три подуровня, которые представляют собой два уровня внимания и один полносвязный уровень.
- Однослойное внимание
Структура слоя внимания показана на рисунке ниже, а метод расчета, этот шаг заключается в вычислении корреляции между Q и K. Благодаря этому вычислению слова, которые более связаны между контекстами, будут вычисляться в соответствии с весом корреляции. В Само-внимании матричный источник QKV является одним и тем же входом.
Использование факела для реализации однослойного внимания происходит следующим образом.
class ScaledDotProductAttention(nn.Module):
''' Scaled Dot-Product Attention '''
de __init__(self, temperature, attn_dropout=0.1):
super().__init__()
self.temperature = temperature
self.dropout = nn.Dropout(attn_dropout)
def forward(self, q, k, v, mask=None):
attn = torch.matmul(q / self.temperature, k.transpose(2, 3))
if mask is not None:
attn = attn.masked_fill(mask == 0, -1e9)
attn = self.dropout(F.softmax(attn, dim=-1))
output = torch.matmul(attn, v)
return output, attn
- MultiHead Attention
Структура многоголового внимания показана на рисунке ниже и представляет собой объединение результатов одноуровневого внимания, которое здесь подробно объясняться не будет.
Подробное объяснение структуры трансформатора
Вышеупомянутая структура внимания В этой главе мы познакомимся со структурой Трансформера. Прежде всего, мы введем встраивание позиций.
Выше мы представили, что Transformer представляет собой структуру кодировщик-декодер, и вход кодировщика включает не только встраивание слов, но и встраивание позиций. Причина добавления встраивания позиции заключается в использовании информации о последовательности входного текста. Расчет встраивания позиций выглядит следующим образом: вложение позиции
class PositionalEncoding(nn.Module):
def __init__(self, d_hid, n_position=200):
super(PositionalEncoding, self).__init__()
# Not a parameter
self.register_buffer('pos_table', self._get_sinusoid_encoding_table(n_position, d_hid))
def _get_sinusoid_encoding_table(self, n_position, d_hid):
''' Sinusoid position encoding table '''
# TODO: make it with torch instead of numpy
def get_position_angle_vec(position):
return [position / np.power(10000, 2 * (hid_j // 2) / d_hid) for hid_j in range(d_hid)]
sinusoid_table = np.array([get_position_angle_vec(pos_i) for pos_i in range(n_position)])
sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i
sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1
return torch.FloatTensor(sinusoid_table).unsqueeze(0)
def forward(self, x):
return x + self.pos_table[:, :x.size(1)].clone().detach()
1. Encoder
Вход кодировщика - встраивание позиций и встраивание слов.После многоголового внимания и полностью подключенной сети результат выводится в декодер.
class EncoderLayer(nn.Module):
''' Compose with two layers '''
def __init__(self, d_model, d_inner, n_head, d_k, d_v, dropout=0.1):
super(EncoderLayer, self).__init__()
self.slf_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout)
self.pos_ffn = PositionwiseFeedForward(d_model, d_inner, dropout=dropout)
def forward(self, enc_input, slf_attn_mask=None):
enc_output, enc_slf_attn = self.slf_attn(
enc_input, enc_input, enc_input, mask=slf_attn_mask)
enc_output = self.pos_ffn(enc_output)
return enc_output, enc_slf_attn
2. Decoder
Декодер имеет двухуровневую структуру внимания, первый уровень — это внимание к себе, а вход второго уровня — это выход кодировщика и выход собственного внимания. Чтобы декодер не мог узнать что-то после текущей позиции, ввод должен быть замаскирован на этапе самоконтроля. Маска второго слоя внимания такая же, как маска кодировщика.
class DecoderLayer(nn.Module):
''' Compose with three layers '''
def __init__(self, d_model, d_inner, n_head, d_k, d_v, dropout=0.1):
super(DecoderLayer, self).__init__()
self.slf_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout)
self.enc_attn = MultiHeadAttention(n_head, d_model, d_k, d_v, dropout=dropout)
self.pos_ffn = PositionwiseFeedForward(d_model, d_inner, dropout=dropout)
def forward(
self, dec_input, enc_output,
slf_attn_mask=None, dec_enc_attn_mask=None):
dec_output, dec_slf_attn = self.slf_attn(
dec_input, dec_input, dec_input, mask=slf_attn_mask)
dec_output, dec_enc_attn = self.enc_attn(
dec_output, enc_output, enc_output, mask=dec_enc_attn_mask)
dec_output = self.pos_ffn(dec_output)
return dec_output, dec_slf_attn, dec_enc_attn
Выводы и размышления
Эта статья представляет собой не столько интерпретацию Transformer, сколько интерпретацию исходного кода Transformer.При чтении статьи есть некоторые места, на которые я только что посмотрел, но когда я действительно смотрю на исходный код, я обнаруживаю, что есть много деталей, которые не были учтены в то время. Может быть, это просто поверхностность на бумаге. Вот некоторые мысли
- Почему ввод декодера должен быть добавлен к исходному вводу?
- Зачем добавлять маску, можно ли ее не указывать?
Reference
- Rush A . The Annotated Transformer[C]// Proceedings of Workshop for NLP Open Source Software (NLP-OSS). 2018.
- Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[J]. arXiv preprint arXiv:1706.03762, 2017.