chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:45:52 +08:00
commit aa6c9c0720
43 changed files with 5178 additions and 0 deletions
@@ -0,0 +1,282 @@
{
"cells": [
{
"cell_type": "code",
"metadata": {},
"source": [
"# code by Tae Hwan Jung(Jeff Jung) @graykode, Derek Miller @dmmiller612\n",
"# Reference : https://github.com/jadore801120/attention-is-all-you-need-pytorch\n",
"# https://github.com/JayParks/transformer\n",
"import numpy as np\n",
"import torch\n",
"import torch.nn as nn\n",
"import torch.optim as optim\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# S: Symbol that shows starting of decoding input\n",
"# E: Symbol that shows starting of decoding output\n",
"# P: Symbol that will fill in blank sequence if current batch data size is short than time steps\n",
"\n",
"def make_batch():\n",
" input_batch = [[src_vocab[n] for n in sentences[0].split()]]\n",
" output_batch = [[tgt_vocab[n] for n in sentences[1].split()]]\n",
" target_batch = [[tgt_vocab[n] for n in sentences[2].split()]]\n",
" return torch.LongTensor(input_batch), torch.LongTensor(output_batch), torch.LongTensor(target_batch)\n",
"\n",
"def get_sinusoid_encoding_table(n_position, d_model):\n",
" def cal_angle(position, hid_idx):\n",
" return position / np.power(10000, 2 * (hid_idx // 2) / d_model)\n",
" def get_posi_angle_vec(position):\n",
" return [cal_angle(position, hid_j) for hid_j in range(d_model)]\n",
"\n",
" sinusoid_table = np.array([get_posi_angle_vec(pos_i) for pos_i in range(n_position)])\n",
" sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i\n",
" sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1\n",
" return torch.FloatTensor(sinusoid_table)\n",
"\n",
"def get_attn_pad_mask(seq_q, seq_k):\n",
" # print(seq_q)\n",
" batch_size, len_q = seq_q.size()\n",
" batch_size, len_k = seq_k.size()\n",
" # eq(zero) is PAD token\n",
" pad_attn_mask = seq_k.data.eq(0).unsqueeze(1) # batch_size x 1 x len_k(=len_q), one is masking\n",
" return pad_attn_mask.expand(batch_size, len_q, len_k) # batch_size x len_q x len_k\n",
"\n",
"def get_attn_subsequent_mask(seq):\n",
" attn_shape = [seq.size(0), seq.size(1), seq.size(1)]\n",
" subsequent_mask = np.triu(np.ones(attn_shape), k=1)\n",
" subsequent_mask = torch.from_numpy(subsequent_mask).byte()\n",
" return subsequent_mask\n",
"\n",
"class ScaledDotProductAttention(nn.Module):\n",
" def __init__(self):\n",
" super(ScaledDotProductAttention, self).__init__()\n",
"\n",
" def forward(self, Q, K, V, attn_mask):\n",
" scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) # scores : [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]\n",
" scores.masked_fill_(attn_mask, -1e9) # Fills elements of self tensor with value where mask is one.\n",
" attn = nn.Softmax(dim=-1)(scores)\n",
" context = torch.matmul(attn, V)\n",
" return context, attn\n",
"\n",
"class MultiHeadAttention(nn.Module):\n",
" def __init__(self):\n",
" super(MultiHeadAttention, self).__init__()\n",
" self.W_Q = nn.Linear(d_model, d_k * n_heads)\n",
" self.W_K = nn.Linear(d_model, d_k * n_heads)\n",
" self.W_V = nn.Linear(d_model, d_v * n_heads)\n",
" self.linear = nn.Linear(n_heads * d_v, d_model)\n",
" self.layer_norm = nn.LayerNorm(d_model)\n",
"\n",
" def forward(self, Q, K, V, attn_mask):\n",
" # q: [batch_size x len_q x d_model], k: [batch_size x len_k x d_model], v: [batch_size x len_k x d_model]\n",
" residual, batch_size = Q, Q.size(0)\n",
" # (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W)\n",
" q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2) # q_s: [batch_size x n_heads x len_q x d_k]\n",
" k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2) # k_s: [batch_size x n_heads x len_k x d_k]\n",
" v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2) # v_s: [batch_size x n_heads x len_k x d_v]\n",
"\n",
" attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1) # attn_mask : [batch_size x n_heads x len_q x len_k]\n",
"\n",
" # context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]\n",
" context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)\n",
" context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) # context: [batch_size x len_q x n_heads * d_v]\n",
" output = self.linear(context)\n",
" return self.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]\n",
"\n",
"class PoswiseFeedForwardNet(nn.Module):\n",
" def __init__(self):\n",
" super(PoswiseFeedForwardNet, self).__init__()\n",
" self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)\n",
" self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)\n",
" self.layer_norm = nn.LayerNorm(d_model)\n",
"\n",
" def forward(self, inputs):\n",
" residual = inputs # inputs : [batch_size, len_q, d_model]\n",
" output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))\n",
" output = self.conv2(output).transpose(1, 2)\n",
" return self.layer_norm(output + residual)\n",
"\n",
"class EncoderLayer(nn.Module):\n",
" def __init__(self):\n",
" super(EncoderLayer, self).__init__()\n",
" self.enc_self_attn = MultiHeadAttention()\n",
" self.pos_ffn = PoswiseFeedForwardNet()\n",
"\n",
" def forward(self, enc_inputs, enc_self_attn_mask):\n",
" enc_outputs, attn = self.enc_self_attn(enc_inputs, enc_inputs, enc_inputs, enc_self_attn_mask) # enc_inputs to same Q,K,V\n",
" enc_outputs = self.pos_ffn(enc_outputs) # enc_outputs: [batch_size x len_q x d_model]\n",
" return enc_outputs, attn\n",
"\n",
"class DecoderLayer(nn.Module):\n",
" def __init__(self):\n",
" super(DecoderLayer, self).__init__()\n",
" self.dec_self_attn = MultiHeadAttention()\n",
" self.dec_enc_attn = MultiHeadAttention()\n",
" self.pos_ffn = PoswiseFeedForwardNet()\n",
"\n",
" def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask):\n",
" dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, dec_inputs, dec_self_attn_mask)\n",
" dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)\n",
" dec_outputs = self.pos_ffn(dec_outputs)\n",
" return dec_outputs, dec_self_attn, dec_enc_attn\n",
"\n",
"class Encoder(nn.Module):\n",
" def __init__(self):\n",
" super(Encoder, self).__init__()\n",
" self.src_emb = nn.Embedding(src_vocab_size, d_model)\n",
" self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(src_len+1, d_model),freeze=True)\n",
" self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)])\n",
"\n",
" def forward(self, enc_inputs): # enc_inputs : [batch_size x source_len]\n",
" enc_outputs = self.src_emb(enc_inputs) + self.pos_emb(torch.LongTensor([[1,2,3,4,0]]))\n",
" enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)\n",
" enc_self_attns = []\n",
" for layer in self.layers:\n",
" enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)\n",
" enc_self_attns.append(enc_self_attn)\n",
" return enc_outputs, enc_self_attns\n",
"\n",
"class Decoder(nn.Module):\n",
" def __init__(self):\n",
" super(Decoder, self).__init__()\n",
" self.tgt_emb = nn.Embedding(tgt_vocab_size, d_model)\n",
" self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(tgt_len+1, d_model),freeze=True)\n",
" self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])\n",
"\n",
" def forward(self, dec_inputs, enc_inputs, enc_outputs): # dec_inputs : [batch_size x target_len]\n",
" dec_outputs = self.tgt_emb(dec_inputs) + self.pos_emb(torch.LongTensor([[5,1,2,3,4]]))\n",
" dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)\n",
" dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs)\n",
" dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask + dec_self_attn_subsequent_mask), 0)\n",
"\n",
" dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)\n",
"\n",
" dec_self_attns, dec_enc_attns = [], []\n",
" for layer in self.layers:\n",
" dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)\n",
" dec_self_attns.append(dec_self_attn)\n",
" dec_enc_attns.append(dec_enc_attn)\n",
" return dec_outputs, dec_self_attns, dec_enc_attns\n",
"\n",
"class Transformer(nn.Module):\n",
" def __init__(self):\n",
" super(Transformer, self).__init__()\n",
" self.encoder = Encoder()\n",
" self.decoder = Decoder()\n",
" self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False)\n",
" def forward(self, enc_inputs, dec_inputs):\n",
" enc_outputs, enc_self_attns = self.encoder(enc_inputs)\n",
" dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)\n",
" dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x src_vocab_size x tgt_vocab_size]\n",
" return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns\n",
"\n",
"def greedy_decoder(model, enc_input, start_symbol):\n",
" \"\"\"\n",
" For simplicity, a Greedy Decoder is Beam search when K=1. This is necessary for inference as we don't know the\n",
" target sequence input. Therefore we try to generate the target input word by word, then feed it into the transformer.\n",
" Starting Reference: http://nlp.seas.harvard.edu/2018/04/03/attention.html#greedy-decoding\n",
" :param model: Transformer Model\n",
" :param enc_input: The encoder input\n",
" :param start_symbol: The start symbol. In this example it is 'S' which corresponds to index 4\n",
" :return: The target input\n",
" \"\"\"\n",
" enc_outputs, enc_self_attns = model.encoder(enc_input)\n",
" dec_input = torch.zeros(1, 5).type_as(enc_input.data)\n",
" next_symbol = start_symbol\n",
" for i in range(0, 5):\n",
" dec_input[0][i] = next_symbol\n",
" dec_outputs, _, _ = model.decoder(dec_input, enc_input, enc_outputs)\n",
" projected = model.projection(dec_outputs)\n",
" prob = projected.squeeze(0).max(dim=-1, keepdim=False)[1]\n",
" next_word = prob.data[i]\n",
" next_symbol = next_word.item()\n",
" return dec_input\n",
"\n",
"def showgraph(attn):\n",
" attn = attn[-1].squeeze(0)[0]\n",
" attn = attn.squeeze(0).data.numpy()\n",
" fig = plt.figure(figsize=(n_heads, n_heads)) # [n_heads, n_heads]\n",
" ax = fig.add_subplot(1, 1, 1)\n",
" ax.matshow(attn, cmap='viridis')\n",
" ax.set_xticklabels(['']+sentences[0].split(), fontdict={'fontsize': 14}, rotation=90)\n",
" ax.set_yticklabels(['']+sentences[2].split(), fontdict={'fontsize': 14})\n",
" plt.show()\n",
"\n",
"if __name__ == '__main__':\n",
" sentences = ['ich mochte ein bier P', 'S i want a beer', 'i want a beer E']\n",
" # Transformer Parameters\n",
" # Padding Should be Zero index\n",
" src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}\n",
" src_vocab_size = len(src_vocab)\n",
"\n",
" tgt_vocab = {'P': 0, 'i': 1, 'want': 2, 'a': 3, 'beer': 4, 'S': 5, 'E': 6}\n",
" number_dict = {i: w for i, w in enumerate(tgt_vocab)}\n",
" tgt_vocab_size = len(tgt_vocab)\n",
"\n",
" src_len = 5 # length of source\n",
" tgt_len = 5 # length of target\n",
"\n",
" d_model = 512 # Embedding Size\n",
" d_ff = 2048 # FeedForward dimension\n",
" d_k = d_v = 64 # dimension of K(=Q), V\n",
" n_layers = 6 # number of Encoder of Decoder Layer\n",
" n_heads = 8 # number of heads in Multi-Head Attention\n",
"\n",
" model = Transformer()\n",
"\n",
" criterion = nn.CrossEntropyLoss()\n",
" optimizer = optim.Adam(model.parameters(), lr=0.001)\n",
"\n",
" enc_inputs, dec_inputs, target_batch = make_batch()\n",
"\n",
" for epoch in range(20):\n",
" optimizer.zero_grad()\n",
" outputs, enc_self_attns, dec_self_attns, dec_enc_attns = model(enc_inputs, dec_inputs)\n",
" loss = criterion(outputs, target_batch.contiguous().view(-1))\n",
" print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))\n",
" loss.backward()\n",
" optimizer.step()\n",
"\n",
" # Test\n",
" greedy_dec_input = greedy_decoder(model, enc_inputs, start_symbol=tgt_vocab[\"S\"])\n",
" predict, _, _, _ = model(enc_inputs, greedy_dec_input)\n",
" predict = predict.data.max(1, keepdim=True)[1]\n",
" print(sentences[0], '->', [number_dict[n.item()] for n in predict.squeeze()])\n",
"\n",
" print('first head of last state enc_self_attns')\n",
" showgraph(enc_self_attns)\n",
"\n",
" print('first head of last state dec_self_attns')\n",
" showgraph(dec_self_attns)\n",
"\n",
" print('first head of last state dec_enc_attns')\n",
" showgraph(dec_enc_attns)"
],
"outputs": [],
"execution_count": null
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
@@ -0,0 +1,249 @@
# %%
# code by Tae Hwan Jung(Jeff Jung) @graykode, Derek Miller @dmmiller612
# Reference : https://github.com/jadore801120/attention-is-all-you-need-pytorch
# https://github.com/JayParks/transformer
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# S: Symbol that shows starting of decoding input
# E: Symbol that shows starting of decoding output
# P: Symbol that will fill in blank sequence if current batch data size is short than time steps
def make_batch():
input_batch = [[src_vocab[n] for n in sentences[0].split()]]
output_batch = [[tgt_vocab[n] for n in sentences[1].split()]]
target_batch = [[tgt_vocab[n] for n in sentences[2].split()]]
return torch.LongTensor(input_batch), torch.LongTensor(output_batch), torch.LongTensor(target_batch)
def get_sinusoid_encoding_table(n_position, d_model):
def cal_angle(position, hid_idx):
return position / np.power(10000, 2 * (hid_idx // 2) / d_model)
def get_posi_angle_vec(position):
return [cal_angle(position, hid_j) for hid_j in range(d_model)]
sinusoid_table = np.array([get_posi_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)
def get_attn_pad_mask(seq_q, seq_k):
# print(seq_q)
batch_size, len_q = seq_q.size()
batch_size, len_k = seq_k.size()
# eq(zero) is PAD token
pad_attn_mask = seq_k.data.eq(0).unsqueeze(1) # batch_size x 1 x len_k(=len_q), one is masking
return pad_attn_mask.expand(batch_size, len_q, len_k) # batch_size x len_q x len_k
def get_attn_subsequent_mask(seq):
attn_shape = [seq.size(0), seq.size(1), seq.size(1)]
subsequent_mask = np.triu(np.ones(attn_shape), k=1)
subsequent_mask = torch.from_numpy(subsequent_mask).byte()
return subsequent_mask
class ScaledDotProductAttention(nn.Module):
def __init__(self):
super(ScaledDotProductAttention, self).__init__()
def forward(self, Q, K, V, attn_mask):
scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) # scores : [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]
scores.masked_fill_(attn_mask, -1e9) # Fills elements of self tensor with value where mask is one.
attn = nn.Softmax(dim=-1)(scores)
context = torch.matmul(attn, V)
return context, attn
class MultiHeadAttention(nn.Module):
def __init__(self):
super(MultiHeadAttention, self).__init__()
self.W_Q = nn.Linear(d_model, d_k * n_heads)
self.W_K = nn.Linear(d_model, d_k * n_heads)
self.W_V = nn.Linear(d_model, d_v * n_heads)
self.linear = nn.Linear(n_heads * d_v, d_model)
self.layer_norm = nn.LayerNorm(d_model)
def forward(self, Q, K, V, attn_mask):
# q: [batch_size x len_q x d_model], k: [batch_size x len_k x d_model], v: [batch_size x len_k x d_model]
residual, batch_size = Q, Q.size(0)
# (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W)
q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2) # q_s: [batch_size x n_heads x len_q x d_k]
k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2) # k_s: [batch_size x n_heads x len_k x d_k]
v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2) # v_s: [batch_size x n_heads x len_k x d_v]
attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1) # attn_mask : [batch_size x n_heads x len_q x len_k]
# context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]
context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)
context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) # context: [batch_size x len_q x n_heads * d_v]
output = self.linear(context)
return self.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]
class PoswiseFeedForwardNet(nn.Module):
def __init__(self):
super(PoswiseFeedForwardNet, self).__init__()
self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
self.layer_norm = nn.LayerNorm(d_model)
def forward(self, inputs):
residual = inputs # inputs : [batch_size, len_q, d_model]
output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))
output = self.conv2(output).transpose(1, 2)
return self.layer_norm(output + residual)
class EncoderLayer(nn.Module):
def __init__(self):
super(EncoderLayer, self).__init__()
self.enc_self_attn = MultiHeadAttention()
self.pos_ffn = PoswiseFeedForwardNet()
def forward(self, enc_inputs, enc_self_attn_mask):
enc_outputs, attn = self.enc_self_attn(enc_inputs, enc_inputs, enc_inputs, enc_self_attn_mask) # enc_inputs to same Q,K,V
enc_outputs = self.pos_ffn(enc_outputs) # enc_outputs: [batch_size x len_q x d_model]
return enc_outputs, attn
class DecoderLayer(nn.Module):
def __init__(self):
super(DecoderLayer, self).__init__()
self.dec_self_attn = MultiHeadAttention()
self.dec_enc_attn = MultiHeadAttention()
self.pos_ffn = PoswiseFeedForwardNet()
def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask):
dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, dec_inputs, dec_self_attn_mask)
dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)
dec_outputs = self.pos_ffn(dec_outputs)
return dec_outputs, dec_self_attn, dec_enc_attn
class Encoder(nn.Module):
def __init__(self):
super(Encoder, self).__init__()
self.src_emb = nn.Embedding(src_vocab_size, d_model)
self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(src_len+1, d_model),freeze=True)
self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)])
def forward(self, enc_inputs): # enc_inputs : [batch_size x source_len]
enc_outputs = self.src_emb(enc_inputs) + self.pos_emb(torch.LongTensor([[1,2,3,4,0]]))
enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)
enc_self_attns = []
for layer in self.layers:
enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)
enc_self_attns.append(enc_self_attn)
return enc_outputs, enc_self_attns
class Decoder(nn.Module):
def __init__(self):
super(Decoder, self).__init__()
self.tgt_emb = nn.Embedding(tgt_vocab_size, d_model)
self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(tgt_len+1, d_model),freeze=True)
self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])
def forward(self, dec_inputs, enc_inputs, enc_outputs): # dec_inputs : [batch_size x target_len]
dec_outputs = self.tgt_emb(dec_inputs) + self.pos_emb(torch.LongTensor([[5,1,2,3,4]]))
dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)
dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs)
dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask + dec_self_attn_subsequent_mask), 0)
dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)
dec_self_attns, dec_enc_attns = [], []
for layer in self.layers:
dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)
dec_self_attns.append(dec_self_attn)
dec_enc_attns.append(dec_enc_attn)
return dec_outputs, dec_self_attns, dec_enc_attns
class Transformer(nn.Module):
def __init__(self):
super(Transformer, self).__init__()
self.encoder = Encoder()
self.decoder = Decoder()
self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False)
def forward(self, enc_inputs, dec_inputs):
enc_outputs, enc_self_attns = self.encoder(enc_inputs)
dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)
dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x src_vocab_size x tgt_vocab_size]
return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns
def greedy_decoder(model, enc_input, start_symbol):
"""
For simplicity, a Greedy Decoder is Beam search when K=1. This is necessary for inference as we don't know the
target sequence input. Therefore we try to generate the target input word by word, then feed it into the transformer.
Starting Reference: http://nlp.seas.harvard.edu/2018/04/03/attention.html#greedy-decoding
:param model: Transformer Model
:param enc_input: The encoder input
:param start_symbol: The start symbol. In this example it is 'S' which corresponds to index 4
:return: The target input
"""
enc_outputs, enc_self_attns = model.encoder(enc_input)
dec_input = torch.zeros(1, 5).type_as(enc_input.data)
next_symbol = start_symbol
for i in range(0, 5):
dec_input[0][i] = next_symbol
dec_outputs, _, _ = model.decoder(dec_input, enc_input, enc_outputs)
projected = model.projection(dec_outputs)
prob = projected.squeeze(0).max(dim=-1, keepdim=False)[1]
next_word = prob.data[i]
next_symbol = next_word.item()
return dec_input
def showgraph(attn):
attn = attn[-1].squeeze(0)[0]
attn = attn.squeeze(0).data.numpy()
fig = plt.figure(figsize=(n_heads, n_heads)) # [n_heads, n_heads]
ax = fig.add_subplot(1, 1, 1)
ax.matshow(attn, cmap='viridis')
ax.set_xticklabels(['']+sentences[0].split(), fontdict={'fontsize': 14}, rotation=90)
ax.set_yticklabels(['']+sentences[2].split(), fontdict={'fontsize': 14})
plt.show()
if __name__ == '__main__':
sentences = ['ich mochte ein bier P', 'S i want a beer', 'i want a beer E']
# Transformer Parameters
# Padding Should be Zero index
src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}
src_vocab_size = len(src_vocab)
tgt_vocab = {'P': 0, 'i': 1, 'want': 2, 'a': 3, 'beer': 4, 'S': 5, 'E': 6}
number_dict = {i: w for i, w in enumerate(tgt_vocab)}
tgt_vocab_size = len(tgt_vocab)
src_len = 5 # length of source
tgt_len = 5 # length of target
d_model = 512 # Embedding Size
d_ff = 2048 # FeedForward dimension
d_k = d_v = 64 # dimension of K(=Q), V
n_layers = 6 # number of Encoder of Decoder Layer
n_heads = 8 # number of heads in Multi-Head Attention
model = Transformer()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
enc_inputs, dec_inputs, target_batch = make_batch()
for epoch in range(20):
optimizer.zero_grad()
outputs, enc_self_attns, dec_self_attns, dec_enc_attns = model(enc_inputs, dec_inputs)
loss = criterion(outputs, target_batch.contiguous().view(-1))
print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
loss.backward()
optimizer.step()
# Test
greedy_dec_input = greedy_decoder(model, enc_inputs, start_symbol=tgt_vocab["S"])
predict, _, _, _ = model(enc_inputs, greedy_dec_input)
predict = predict.data.max(1, keepdim=True)[1]
print(sentences[0], '->', [number_dict[n.item()] for n in predict.squeeze()])
print('first head of last state enc_self_attns')
showgraph(enc_self_attns)
print('first head of last state dec_self_attns')
showgraph(dec_self_attns)
print('first head of last state dec_enc_attns')
showgraph(dec_enc_attns)
+259
View File
@@ -0,0 +1,259 @@
{
"cells": [
{
"cell_type": "code",
"metadata": {},
"source": [
"# code by Tae Hwan Jung(Jeff Jung) @graykode, Derek Miller @dmmiller612\n",
"# Reference : https://github.com/jadore801120/attention-is-all-you-need-pytorch\n",
"# https://github.com/JayParks/transformer\n",
"import numpy as np\n",
"import torch\n",
"import torch.nn as nn\n",
"import torch.optim as optim\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# S: Symbol that shows starting of decoding input\n",
"# E: Symbol that shows starting of decoding output\n",
"# P: Symbol that will fill in blank sequence if current batch data size is short than time steps\n",
"\n",
"def make_batch(sentences):\n",
" input_batch = [[src_vocab[n] for n in sentences[0].split()]]\n",
" output_batch = [[tgt_vocab[n] for n in sentences[1].split()]]\n",
" target_batch = [[tgt_vocab[n] for n in sentences[2].split()]]\n",
" return torch.LongTensor(input_batch), torch.LongTensor(output_batch), torch.LongTensor(target_batch)\n",
"\n",
"def get_sinusoid_encoding_table(n_position, d_model):\n",
" def cal_angle(position, hid_idx):\n",
" return position / np.power(10000, 2 * (hid_idx // 2) / d_model)\n",
" def get_posi_angle_vec(position):\n",
" return [cal_angle(position, hid_j) for hid_j in range(d_model)]\n",
"\n",
" sinusoid_table = np.array([get_posi_angle_vec(pos_i) for pos_i in range(n_position)])\n",
" sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i\n",
" sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1\n",
" return torch.FloatTensor(sinusoid_table)\n",
"\n",
"def get_attn_pad_mask(seq_q, seq_k):\n",
" batch_size, len_q = seq_q.size()\n",
" batch_size, len_k = seq_k.size()\n",
" # eq(zero) is PAD token\n",
" pad_attn_mask = seq_k.data.eq(0).unsqueeze(1) # batch_size x 1 x len_k(=len_q), one is masking\n",
" return pad_attn_mask.expand(batch_size, len_q, len_k) # batch_size x len_q x len_k\n",
"\n",
"def get_attn_subsequent_mask(seq):\n",
" attn_shape = [seq.size(0), seq.size(1), seq.size(1)]\n",
" subsequent_mask = np.triu(np.ones(attn_shape), k=1)\n",
" subsequent_mask = torch.from_numpy(subsequent_mask).byte()\n",
" return subsequent_mask\n",
"\n",
"class ScaledDotProductAttention(nn.Module):\n",
" def __init__(self):\n",
" super(ScaledDotProductAttention, self).__init__()\n",
"\n",
" def forward(self, Q, K, V, attn_mask):\n",
" scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) # scores : [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]\n",
" scores.masked_fill_(attn_mask, -1e9) # Fills elements of self tensor with value where mask is one.\n",
" attn = nn.Softmax(dim=-1)(scores)\n",
" context = torch.matmul(attn, V)\n",
" return context, attn\n",
"\n",
"class MultiHeadAttention(nn.Module):\n",
" def __init__(self):\n",
" super(MultiHeadAttention, self).__init__()\n",
" self.W_Q = nn.Linear(d_model, d_k * n_heads)\n",
" self.W_K = nn.Linear(d_model, d_k * n_heads)\n",
" self.W_V = nn.Linear(d_model, d_v * n_heads)\n",
" self.linear = nn.Linear(n_heads * d_v, d_model)\n",
" self.layer_norm = nn.LayerNorm(d_model)\n",
"\n",
" def forward(self, Q, K, V, attn_mask):\n",
" # q: [batch_size x len_q x d_model], k: [batch_size x len_k x d_model], v: [batch_size x len_k x d_model]\n",
" residual, batch_size = Q, Q.size(0)\n",
" # (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W)\n",
" q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2) # q_s: [batch_size x n_heads x len_q x d_k]\n",
" k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2) # k_s: [batch_size x n_heads x len_k x d_k]\n",
" v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2) # v_s: [batch_size x n_heads x len_k x d_v]\n",
"\n",
" attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1) # attn_mask : [batch_size x n_heads x len_q x len_k]\n",
"\n",
" # context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]\n",
" context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)\n",
" context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) # context: [batch_size x len_q x n_heads * d_v]\n",
" output = self.linear(context)\n",
" return self.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]\n",
"\n",
"class PoswiseFeedForwardNet(nn.Module):\n",
" def __init__(self):\n",
" super(PoswiseFeedForwardNet, self).__init__()\n",
" self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)\n",
" self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)\n",
" self.layer_norm = nn.LayerNorm(d_model)\n",
"\n",
" def forward(self, inputs):\n",
" residual = inputs # inputs : [batch_size, len_q, d_model]\n",
" output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))\n",
" output = self.conv2(output).transpose(1, 2)\n",
" return self.layer_norm(output + residual)\n",
"\n",
"class EncoderLayer(nn.Module):\n",
" def __init__(self):\n",
" super(EncoderLayer, self).__init__()\n",
" self.enc_self_attn = MultiHeadAttention()\n",
" self.pos_ffn = PoswiseFeedForwardNet()\n",
"\n",
" def forward(self, enc_inputs, enc_self_attn_mask):\n",
" enc_outputs, attn = self.enc_self_attn(enc_inputs, enc_inputs, enc_inputs, enc_self_attn_mask) # enc_inputs to same Q,K,V\n",
" enc_outputs = self.pos_ffn(enc_outputs) # enc_outputs: [batch_size x len_q x d_model]\n",
" return enc_outputs, attn\n",
"\n",
"class DecoderLayer(nn.Module):\n",
" def __init__(self):\n",
" super(DecoderLayer, self).__init__()\n",
" self.dec_self_attn = MultiHeadAttention()\n",
" self.dec_enc_attn = MultiHeadAttention()\n",
" self.pos_ffn = PoswiseFeedForwardNet()\n",
"\n",
" def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask):\n",
" dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, dec_inputs, dec_self_attn_mask)\n",
" dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)\n",
" dec_outputs = self.pos_ffn(dec_outputs)\n",
" return dec_outputs, dec_self_attn, dec_enc_attn\n",
"\n",
"class Encoder(nn.Module):\n",
" def __init__(self):\n",
" super(Encoder, self).__init__()\n",
" self.src_emb = nn.Embedding(src_vocab_size, d_model)\n",
" self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(src_len+1, d_model),freeze=True)\n",
" self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)])\n",
"\n",
" def forward(self, enc_inputs): # enc_inputs : [batch_size x source_len]\n",
" enc_outputs = self.src_emb(enc_inputs) + self.pos_emb(torch.LongTensor([[1,2,3,4,0]]))\n",
" enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)\n",
" enc_self_attns = []\n",
" for layer in self.layers:\n",
" enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)\n",
" enc_self_attns.append(enc_self_attn)\n",
" return enc_outputs, enc_self_attns\n",
"\n",
"class Decoder(nn.Module):\n",
" def __init__(self):\n",
" super(Decoder, self).__init__()\n",
" self.tgt_emb = nn.Embedding(tgt_vocab_size, d_model)\n",
" self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(tgt_len+1, d_model),freeze=True)\n",
" self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])\n",
"\n",
" def forward(self, dec_inputs, enc_inputs, enc_outputs): # dec_inputs : [batch_size x target_len]\n",
" dec_outputs = self.tgt_emb(dec_inputs) + self.pos_emb(torch.LongTensor([[5,1,2,3,4]]))\n",
" dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)\n",
" dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs)\n",
" dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask + dec_self_attn_subsequent_mask), 0)\n",
"\n",
" dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)\n",
"\n",
" dec_self_attns, dec_enc_attns = [], []\n",
" for layer in self.layers:\n",
" dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)\n",
" dec_self_attns.append(dec_self_attn)\n",
" dec_enc_attns.append(dec_enc_attn)\n",
" return dec_outputs, dec_self_attns, dec_enc_attns\n",
"\n",
"class Transformer(nn.Module):\n",
" def __init__(self):\n",
" super(Transformer, self).__init__()\n",
" self.encoder = Encoder()\n",
" self.decoder = Decoder()\n",
" self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False)\n",
" def forward(self, enc_inputs, dec_inputs):\n",
" enc_outputs, enc_self_attns = self.encoder(enc_inputs)\n",
" dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)\n",
" dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x src_vocab_size x tgt_vocab_size]\n",
" return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns\n",
"\n",
"def showgraph(attn):\n",
" attn = attn[-1].squeeze(0)[0]\n",
" attn = attn.squeeze(0).data.numpy()\n",
" fig = plt.figure(figsize=(n_heads, n_heads)) # [n_heads, n_heads]\n",
" ax = fig.add_subplot(1, 1, 1)\n",
" ax.matshow(attn, cmap='viridis')\n",
" ax.set_xticklabels(['']+sentences[0].split(), fontdict={'fontsize': 14}, rotation=90)\n",
" ax.set_yticklabels(['']+sentences[2].split(), fontdict={'fontsize': 14})\n",
" plt.show()\n",
"\n",
"if __name__ == '__main__':\n",
" sentences = ['ich mochte ein bier P', 'S i want a beer', 'i want a beer E']\n",
"\n",
" # Transformer Parameters\n",
" # Padding Should be Zero\n",
" src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}\n",
" src_vocab_size = len(src_vocab)\n",
"\n",
" tgt_vocab = {'P': 0, 'i': 1, 'want': 2, 'a': 3, 'beer': 4, 'S': 5, 'E': 6}\n",
" number_dict = {i: w for i, w in enumerate(tgt_vocab)}\n",
" tgt_vocab_size = len(tgt_vocab)\n",
"\n",
" src_len = 5 # length of source\n",
" tgt_len = 5 # length of target\n",
"\n",
" d_model = 512 # Embedding Size\n",
" d_ff = 2048 # FeedForward dimension\n",
" d_k = d_v = 64 # dimension of K(=Q), V\n",
" n_layers = 6 # number of Encoder of Decoder Layer\n",
" n_heads = 8 # number of heads in Multi-Head Attention\n",
"\n",
" model = Transformer()\n",
"\n",
" criterion = nn.CrossEntropyLoss()\n",
" optimizer = optim.Adam(model.parameters(), lr=0.001)\n",
"\n",
" enc_inputs, dec_inputs, target_batch = make_batch(sentences)\n",
"\n",
" for epoch in range(20):\n",
" optimizer.zero_grad()\n",
" outputs, enc_self_attns, dec_self_attns, dec_enc_attns = model(enc_inputs, dec_inputs)\n",
" loss = criterion(outputs, target_batch.contiguous().view(-1))\n",
" print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))\n",
" loss.backward()\n",
" optimizer.step()\n",
"\n",
" # Test\n",
" predict, _, _, _ = model(enc_inputs, dec_inputs)\n",
" predict = predict.data.max(1, keepdim=True)[1]\n",
" print(sentences[0], '->', [number_dict[n.item()] for n in predict.squeeze()])\n",
"\n",
" print('first head of last state enc_self_attns')\n",
" showgraph(enc_self_attns)\n",
"\n",
" print('first head of last state dec_self_attns')\n",
" showgraph(dec_self_attns)\n",
"\n",
" print('first head of last state dec_enc_attns')\n",
" showgraph(dec_enc_attns)"
],
"outputs": [],
"execution_count": null
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
+226
View File
@@ -0,0 +1,226 @@
# %%
# code by Tae Hwan Jung(Jeff Jung) @graykode, Derek Miller @dmmiller612
# Reference : https://github.com/jadore801120/attention-is-all-you-need-pytorch
# https://github.com/JayParks/transformer
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
# S: Symbol that shows starting of decoding input
# E: Symbol that shows starting of decoding output
# P: Symbol that will fill in blank sequence if current batch data size is short than time steps
def make_batch(sentences):
input_batch = [[src_vocab[n] for n in sentences[0].split()]]
output_batch = [[tgt_vocab[n] for n in sentences[1].split()]]
target_batch = [[tgt_vocab[n] for n in sentences[2].split()]]
return torch.LongTensor(input_batch), torch.LongTensor(output_batch), torch.LongTensor(target_batch)
def get_sinusoid_encoding_table(n_position, d_model):
def cal_angle(position, hid_idx):
return position / np.power(10000, 2 * (hid_idx // 2) / d_model)
def get_posi_angle_vec(position):
return [cal_angle(position, hid_j) for hid_j in range(d_model)]
sinusoid_table = np.array([get_posi_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)
def get_attn_pad_mask(seq_q, seq_k):
batch_size, len_q = seq_q.size()
batch_size, len_k = seq_k.size()
# eq(zero) is PAD token
pad_attn_mask = seq_k.data.eq(0).unsqueeze(1) # batch_size x 1 x len_k(=len_q), one is masking
return pad_attn_mask.expand(batch_size, len_q, len_k) # batch_size x len_q x len_k
def get_attn_subsequent_mask(seq):
attn_shape = [seq.size(0), seq.size(1), seq.size(1)]
subsequent_mask = np.triu(np.ones(attn_shape), k=1)
subsequent_mask = torch.from_numpy(subsequent_mask).byte()
return subsequent_mask
class ScaledDotProductAttention(nn.Module):
def __init__(self):
super(ScaledDotProductAttention, self).__init__()
def forward(self, Q, K, V, attn_mask):
scores = torch.matmul(Q, K.transpose(-1, -2)) / np.sqrt(d_k) # scores : [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]
scores.masked_fill_(attn_mask, -1e9) # Fills elements of self tensor with value where mask is one.
attn = nn.Softmax(dim=-1)(scores)
context = torch.matmul(attn, V)
return context, attn
class MultiHeadAttention(nn.Module):
def __init__(self):
super(MultiHeadAttention, self).__init__()
self.W_Q = nn.Linear(d_model, d_k * n_heads)
self.W_K = nn.Linear(d_model, d_k * n_heads)
self.W_V = nn.Linear(d_model, d_v * n_heads)
self.linear = nn.Linear(n_heads * d_v, d_model)
self.layer_norm = nn.LayerNorm(d_model)
def forward(self, Q, K, V, attn_mask):
# q: [batch_size x len_q x d_model], k: [batch_size x len_k x d_model], v: [batch_size x len_k x d_model]
residual, batch_size = Q, Q.size(0)
# (B, S, D) -proj-> (B, S, D) -split-> (B, S, H, W) -trans-> (B, H, S, W)
q_s = self.W_Q(Q).view(batch_size, -1, n_heads, d_k).transpose(1,2) # q_s: [batch_size x n_heads x len_q x d_k]
k_s = self.W_K(K).view(batch_size, -1, n_heads, d_k).transpose(1,2) # k_s: [batch_size x n_heads x len_k x d_k]
v_s = self.W_V(V).view(batch_size, -1, n_heads, d_v).transpose(1,2) # v_s: [batch_size x n_heads x len_k x d_v]
attn_mask = attn_mask.unsqueeze(1).repeat(1, n_heads, 1, 1) # attn_mask : [batch_size x n_heads x len_q x len_k]
# context: [batch_size x n_heads x len_q x d_v], attn: [batch_size x n_heads x len_q(=len_k) x len_k(=len_q)]
context, attn = ScaledDotProductAttention()(q_s, k_s, v_s, attn_mask)
context = context.transpose(1, 2).contiguous().view(batch_size, -1, n_heads * d_v) # context: [batch_size x len_q x n_heads * d_v]
output = self.linear(context)
return self.layer_norm(output + residual), attn # output: [batch_size x len_q x d_model]
class PoswiseFeedForwardNet(nn.Module):
def __init__(self):
super(PoswiseFeedForwardNet, self).__init__()
self.conv1 = nn.Conv1d(in_channels=d_model, out_channels=d_ff, kernel_size=1)
self.conv2 = nn.Conv1d(in_channels=d_ff, out_channels=d_model, kernel_size=1)
self.layer_norm = nn.LayerNorm(d_model)
def forward(self, inputs):
residual = inputs # inputs : [batch_size, len_q, d_model]
output = nn.ReLU()(self.conv1(inputs.transpose(1, 2)))
output = self.conv2(output).transpose(1, 2)
return self.layer_norm(output + residual)
class EncoderLayer(nn.Module):
def __init__(self):
super(EncoderLayer, self).__init__()
self.enc_self_attn = MultiHeadAttention()
self.pos_ffn = PoswiseFeedForwardNet()
def forward(self, enc_inputs, enc_self_attn_mask):
enc_outputs, attn = self.enc_self_attn(enc_inputs, enc_inputs, enc_inputs, enc_self_attn_mask) # enc_inputs to same Q,K,V
enc_outputs = self.pos_ffn(enc_outputs) # enc_outputs: [batch_size x len_q x d_model]
return enc_outputs, attn
class DecoderLayer(nn.Module):
def __init__(self):
super(DecoderLayer, self).__init__()
self.dec_self_attn = MultiHeadAttention()
self.dec_enc_attn = MultiHeadAttention()
self.pos_ffn = PoswiseFeedForwardNet()
def forward(self, dec_inputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask):
dec_outputs, dec_self_attn = self.dec_self_attn(dec_inputs, dec_inputs, dec_inputs, dec_self_attn_mask)
dec_outputs, dec_enc_attn = self.dec_enc_attn(dec_outputs, enc_outputs, enc_outputs, dec_enc_attn_mask)
dec_outputs = self.pos_ffn(dec_outputs)
return dec_outputs, dec_self_attn, dec_enc_attn
class Encoder(nn.Module):
def __init__(self):
super(Encoder, self).__init__()
self.src_emb = nn.Embedding(src_vocab_size, d_model)
self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(src_len+1, d_model),freeze=True)
self.layers = nn.ModuleList([EncoderLayer() for _ in range(n_layers)])
def forward(self, enc_inputs): # enc_inputs : [batch_size x source_len]
enc_outputs = self.src_emb(enc_inputs) + self.pos_emb(torch.LongTensor([[1,2,3,4,0]]))
enc_self_attn_mask = get_attn_pad_mask(enc_inputs, enc_inputs)
enc_self_attns = []
for layer in self.layers:
enc_outputs, enc_self_attn = layer(enc_outputs, enc_self_attn_mask)
enc_self_attns.append(enc_self_attn)
return enc_outputs, enc_self_attns
class Decoder(nn.Module):
def __init__(self):
super(Decoder, self).__init__()
self.tgt_emb = nn.Embedding(tgt_vocab_size, d_model)
self.pos_emb = nn.Embedding.from_pretrained(get_sinusoid_encoding_table(tgt_len+1, d_model),freeze=True)
self.layers = nn.ModuleList([DecoderLayer() for _ in range(n_layers)])
def forward(self, dec_inputs, enc_inputs, enc_outputs): # dec_inputs : [batch_size x target_len]
dec_outputs = self.tgt_emb(dec_inputs) + self.pos_emb(torch.LongTensor([[5,1,2,3,4]]))
dec_self_attn_pad_mask = get_attn_pad_mask(dec_inputs, dec_inputs)
dec_self_attn_subsequent_mask = get_attn_subsequent_mask(dec_inputs)
dec_self_attn_mask = torch.gt((dec_self_attn_pad_mask + dec_self_attn_subsequent_mask), 0)
dec_enc_attn_mask = get_attn_pad_mask(dec_inputs, enc_inputs)
dec_self_attns, dec_enc_attns = [], []
for layer in self.layers:
dec_outputs, dec_self_attn, dec_enc_attn = layer(dec_outputs, enc_outputs, dec_self_attn_mask, dec_enc_attn_mask)
dec_self_attns.append(dec_self_attn)
dec_enc_attns.append(dec_enc_attn)
return dec_outputs, dec_self_attns, dec_enc_attns
class Transformer(nn.Module):
def __init__(self):
super(Transformer, self).__init__()
self.encoder = Encoder()
self.decoder = Decoder()
self.projection = nn.Linear(d_model, tgt_vocab_size, bias=False)
def forward(self, enc_inputs, dec_inputs):
enc_outputs, enc_self_attns = self.encoder(enc_inputs)
dec_outputs, dec_self_attns, dec_enc_attns = self.decoder(dec_inputs, enc_inputs, enc_outputs)
dec_logits = self.projection(dec_outputs) # dec_logits : [batch_size x src_vocab_size x tgt_vocab_size]
return dec_logits.view(-1, dec_logits.size(-1)), enc_self_attns, dec_self_attns, dec_enc_attns
def showgraph(attn):
attn = attn[-1].squeeze(0)[0]
attn = attn.squeeze(0).data.numpy()
fig = plt.figure(figsize=(n_heads, n_heads)) # [n_heads, n_heads]
ax = fig.add_subplot(1, 1, 1)
ax.matshow(attn, cmap='viridis')
ax.set_xticklabels(['']+sentences[0].split(), fontdict={'fontsize': 14}, rotation=90)
ax.set_yticklabels(['']+sentences[2].split(), fontdict={'fontsize': 14})
plt.show()
if __name__ == '__main__':
sentences = ['ich mochte ein bier P', 'S i want a beer', 'i want a beer E']
# Transformer Parameters
# Padding Should be Zero
src_vocab = {'P': 0, 'ich': 1, 'mochte': 2, 'ein': 3, 'bier': 4}
src_vocab_size = len(src_vocab)
tgt_vocab = {'P': 0, 'i': 1, 'want': 2, 'a': 3, 'beer': 4, 'S': 5, 'E': 6}
number_dict = {i: w for i, w in enumerate(tgt_vocab)}
tgt_vocab_size = len(tgt_vocab)
src_len = 5 # length of source
tgt_len = 5 # length of target
d_model = 512 # Embedding Size
d_ff = 2048 # FeedForward dimension
d_k = d_v = 64 # dimension of K(=Q), V
n_layers = 6 # number of Encoder of Decoder Layer
n_heads = 8 # number of heads in Multi-Head Attention
model = Transformer()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
enc_inputs, dec_inputs, target_batch = make_batch(sentences)
for epoch in range(20):
optimizer.zero_grad()
outputs, enc_self_attns, dec_self_attns, dec_enc_attns = model(enc_inputs, dec_inputs)
loss = criterion(outputs, target_batch.contiguous().view(-1))
print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
loss.backward()
optimizer.step()
# Test
predict, _, _, _ = model(enc_inputs, dec_inputs)
predict = predict.data.max(1, keepdim=True)[1]
print(sentences[0], '->', [number_dict[n.item()] for n in predict.squeeze()])
print('first head of last state enc_self_attns')
showgraph(enc_self_attns)
print('first head of last state dec_self_attns')
showgraph(dec_self_attns)
print('first head of last state dec_enc_attns')
showgraph(dec_enc_attns)