TensorFlow 2.0 Beta : 上級 Tutorials : テキストとシークエンス :- 言語理解のための Transformer モデル (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 07/08/2019
* 本ページは、TensorFlow の本家サイトの TF 2.0 Beta – Advanced Tutorials – Text and sequences の以下のページを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
テキストとシークエンス :- 言語理解のための Transformer モデル
このチュートリアルはポルトガル語を英語に翻訳するために Transformer モデル を訓練します。これは テキスト生成 と attention の知識を仮定する上級サンプルです。
transformer モデルの背後にある中心的なアイデアは self-attention — そのシークエンスの表現を計算するために入力シークエンスの異なる位置に注目する機能です。Transformer は self-attention 層のスタックを作成して下のセクション Scaled dot product attention and Multi-head attention で説明されます。
transformer モデルは RNN や CNN の代わりに self-attention 層のスタックを使用して可変サイズの入力を処理します。この一般的なアーキテクチャは幾つかの優位点を持ちます :
- それはデータに渡る時間的/空間的関係について何も仮定しません。これはオブジェクトのセット (例えば、StarCraft ユニット) を前処理するために理想的です。
- 層出力は RNN のような直列 (= series) の代わりに、並列に計算できます。
- 距離のある (= distant) 項目は多くの RNN-ステップや畳み込み層を通すことなく互いの出力に影響を与えることができます (例えば Scene Memory Transformer 参照)。
- それは long-range 依存性を学習できます。これは多くのシークエンスタスクにおける挑戦 (的課題) です。
アーキテクチャの不都合な点は :
- 時系列について、時間ステップに対する出力は入力と現在の隠れ状態だけの代わりに履歴全体から計算されます。
- 入力がテキストのような、時間的/空間的関係を持つ場合、何某かの位置的エンコーディングが追加されなければなりません、そうでないのであればモデルは bag of words を効果的に見るでしょう。
このノートブックでモデルを訓練後、ポルトガル語センテンスを入力して英語翻訳を返すことができます。
from __future__ import absolute_import, division, print_function, unicode_literals !pip install -q tensorflow-gpu==2.0.0-beta1 import tensorflow_datasets as tfds import tensorflow as tf import time import numpy as np import matplotlib.pyplot as plt
入力パイプラインをセットアップする
TFDS を使用して TED Talks Open Translation Project から 葡英翻訳データセット をロードします。
このデータセットはおよそ 50000 訓練サンプル、1100 検証サンプルそして 2000 テストサンプルを含みます。
examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True, as_supervised=True) train_examples, val_examples = examples['train'], examples['validation']
Downloading and preparing dataset ted_hrlr_translate (124.94 MiB) to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/0.0.1... HBox(children=(IntProgress(value=1, bar_style='info', description='Dl Completed...', max=1, style=ProgressStyl… HBox(children=(IntProgress(value=1, bar_style='info', description='Dl Size...', max=1, style=ProgressStyle(des… HBox(children=(IntProgress(value=1, bar_style='info', description='Extraction completed...', max=1, style=Prog… HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value=''))) HBox(children=(IntProgress(value=0, description='Shuffling...', max=1, style=ProgressStyle(description_width='… WARNING: Logging before flag parsing goes to stderr. W0628 06:47:58.377724 140112721143552 deprecation.py:323] From /home/kbuilder/.local/lib/python3.5/site-packages/tensorflow_datasets/core/file_format_adapter.py:209: tf_record_iterator (from tensorflow.python.lib.io.tf_record) is deprecated and will be removed in a future version. Instructions for updating: Use eager execution and: `tf.data.TFRecordDataset(path)` HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des… HBox(children=(IntProgress(value=0, description='Writing...', max=51785, style=ProgressStyle(description_width… HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value=''))) HBox(children=(IntProgress(value=0, description='Shuffling...', max=1, style=ProgressStyle(description_width='… HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des… HBox(children=(IntProgress(value=0, description='Writing...', max=1193, style=ProgressStyle(description_width=… HBox(children=(IntProgress(value=1, bar_style='info', max=1), HTML(value=''))) HBox(children=(IntProgress(value=0, description='Shuffling...', max=1, style=ProgressStyle(description_width='… HBox(children=(IntProgress(value=1, bar_style='info', description='Reading...', max=1, style=ProgressStyle(des… HBox(children=(IntProgress(value=0, description='Writing...', max=1803, style=ProgressStyle(description_width=… Dataset ted_hrlr_translate downloaded and prepared to /home/kbuilder/tensorflow_datasets/ted_hrlr_translate/pt_to_en/0.0.1. Subsequent calls will reuse this data.
訓練データセットからカスタム部分単語字句解析器を作成します。
tokenizer_en = tfds.features.text.SubwordTextEncoder.build_from_corpus( (en.numpy() for pt, en in train_examples), target_vocab_size=2**13) tokenizer_pt = tfds.features.text.SubwordTextEncoder.build_from_corpus( (pt.numpy() for pt, en in train_examples), target_vocab_size=2**13)
sample_string = 'Transformer is awesome.' tokenized_string = tokenizer_en.encode(sample_string) print ('Tokenized string is {}'.format(tokenized_string)) original_string = tokenizer_en.decode(tokenized_string) print ('The original string: {}'.format(original_string)) assert original_string == sample_string
Tokenized string is [7915, 1248, 7946, 7194, 13, 2799, 7877] The original string: Transformer is awesome.
字句解析器は単語が辞書にない場合には文字列を部分単語に分割することによりエンコードします。
for ts in tokenized_string: print ('{} ----> {}'.format(ts, tokenizer_en.decode([ts])))
7915 ----> T 1248 ----> ran 7946 ----> s 7194 ----> former 13 ----> is 2799 ----> awesome 7877 ----> .
BUFFER_SIZE = 20000 BATCH_SIZE = 64
入力とターゲットに start と end トークンを追加します。
def encode(lang1, lang2): lang1 = [tokenizer_pt.vocab_size] + tokenizer_pt.encode( lang1.numpy()) + [tokenizer_pt.vocab_size+1] lang2 = [tokenizer_en.vocab_size] + tokenizer_en.encode( lang2.numpy()) + [tokenizer_en.vocab_size+1] return lang1, lang2
Note: この例題を小さくそして比較的高速に保持するため、40 トークンを超える長さを持つサンプルは捨てます。
MAX_LENGTH = 40
def filter_max_length(x, y, max_length=MAX_LENGTH): return tf.logical_and(tf.size(x) <= max_length, tf.size(y) <= max_length)
.map() 内の演算はグラフモードで動作して numpy 属性を持たないグラフ tensor を受け取ります。字句解析器は (それを) 整数にエンコードするために文字列か Unicode シンボルを想定します。こうして、エンコーディングを tf.py_function 内で実行する必要があります、これは文字列値を含む numpy 属性を持つ eager tensor を受け取ります。
def tf_encode(pt, en): return tf.py_function(encode, [pt, en], [tf.int64, tf.int64])
train_dataset = train_examples.map(tf_encode) train_dataset = train_dataset.filter(filter_max_length) # cache the dataset to memory to get a speedup while reading from it. train_dataset = train_dataset.cache() train_dataset = train_dataset.shuffle(BUFFER_SIZE).padded_batch( BATCH_SIZE, padded_shapes=([-1], [-1])) train_dataset = train_dataset.prefetch(tf.data.experimental.AUTOTUNE) val_dataset = val_examples.map(tf_encode) val_dataset = val_dataset.filter(filter_max_length).padded_batch( BATCH_SIZE, padded_shapes=([-1], [-1]))
pt_batch, en_batch = next(iter(val_dataset)) pt_batch, en_batch
W0628 06:50:46.608169 140107050899200 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string W0628 06:50:46.610315 140107050899200 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string W0628 06:50:46.612855 140107034113792 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string W0628 06:50:46.614116 140107034113792 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string W0628 06:50:46.616141 140107034113792 backprop.py:842] The dtype of the watched tensor must be floating (e.g. tf.float32), got tf.string (<tf.Tensor: id=311422, shape=(64, 40), dtype=int64, numpy= array([[8214, 1259, 5, ..., 0, 0, 0], [8214, 299, 13, ..., 0, 0, 0], [8214, 59, 8, ..., 0, 0, 0], ..., [8214, 95, 3, ..., 0, 0, 0], [8214, 5157, 1, ..., 0, 0, 0], [8214, 4479, 7990, ..., 0, 0, 0]])>, <tf.Tensor: id=311423, shape=(64, 40), dtype=int64, numpy= array([[8087, 18, 12, ..., 0, 0, 0], [8087, 634, 30, ..., 0, 0, 0], [8087, 16, 13, ..., 0, 0, 0], ..., [8087, 12, 20, ..., 0, 0, 0], [8087, 17, 4981, ..., 0, 0, 0], [8087, 12, 5453, ..., 0, 0, 0]])>)
位置エンコーディング (= Positional encoding)
このモデルはどのようなリカレンスも畳み込みも含まないので、センテンスの単語の相対位置についての何某かの情報をモデルに与えるために位置エンコーディングが追加されます。
位置エンコーディングベクトルは埋め込みベクトルに追加されます。埋め込みは d-次元空間でトークンを表し、そこでは類似の意味を持つトークンは互いに近くなります。しかし埋め込みはセンテンスの単語の相対位置をエンコードしません。そこで位置エンコーディングの追加後、単語はそれらの意味の類似性とセンテンスにおけるそれらの位置に基づき、d-次元空間で互いに近くなります。
それについて更に学習するために 位置エンコーディング のノートブックを見てください。位置エンコーディングを計算するための公式は次のようなものです :
def get_angles(pos, i, d_model): angle_rates = 1 / np.power(10000, (2 * (i//2)) / np.float32(d_model)) return pos * angle_rates
def positional_encoding(position, d_model): angle_rads = get_angles(np.arange(position)[:, np.newaxis], np.arange(d_model)[np.newaxis, :], d_model) # apply sin to even indices in the array; 2i sines = np.sin(angle_rads[:, 0::2]) # apply cos to odd indices in the array; 2i+1 cosines = np.cos(angle_rads[:, 1::2]) pos_encoding = np.concatenate([sines, cosines], axis=-1) pos_encoding = pos_encoding[np.newaxis, ...] return tf.cast(pos_encoding, dtype=tf.float32)
pos_encoding = positional_encoding(50, 512) print (pos_encoding.shape) plt.pcolormesh(pos_encoding[0], cmap='RdBu') plt.xlabel('Depth') plt.xlim((0, 512)) plt.ylabel('Position') plt.colorbar() plt.show()
(1, 50, 512)
マスキング
シークエンスのバッチのパッド・トークンの総てをマスクします。それはモデルがパディングを入力として扱わないことを確実にします。マスクはパッド値 0 がどこに存在するかを示します: それはそれらの位置では 1 を出力し、そうでなければ 0 を出力します。
def create_padding_mask(seq): seq = tf.cast(tf.math.equal(seq, 0), tf.float32) # add extra dimensions so that we can add the padding # to the attention logits. return seq[:, tf.newaxis, tf.newaxis, :] # (batch_size, 1, 1, seq_len)
x = tf.constant([[7, 6, 0, 0, 1], [1, 2, 3, 0, 0], [0, 0, 0, 4, 5]]) create_padding_mask(x)
<tf.Tensor: id=311439, shape=(3, 1, 1, 5), dtype=float32, numpy= array([[[[0., 0., 1., 1., 0.]]], [[[0., 0., 0., 1., 1.]]], [[[1., 1., 1., 0., 0.]]]], dtype=float32)>
look-ahead mask はシークエンスの future トークンをマスクするために使用されます。換言すれば、マスクはどのエントリが使用されるべきでないかを示します。
これは 3 番目の単語を予測するため、最初と 2 番目の単語だけが使用されることを意味します。同様に 4 番目の単語を予測するためには、最初、2 番目そして 3 番目の単語だけが使用されます等々。
def create_look_ahead_mask(size): mask = 1 - tf.linalg.band_part(tf.ones((size, size)), -1, 0) return mask # (seq_len, seq_len)
x = tf.random.uniform((1, 3)) temp = create_look_ahead_mask(x.shape[1]) temp
<tf.Tensor: id=311455, shape=(3, 3), dtype=float32, numpy= array([[0., 1., 1.], [0., 0., 1.], [0., 0., 0.]], dtype=float32)>
transformer により使用される attention 関数は 3 つの入力を取ります: Q (query), K (key), V (value)。attention 重みを計算するために使用される等式は :
ドット積 attention は depth の平方根の因子でスケールされます。これは、depth の巨大な値のため、ドット積が大きさの点で巨大に増大して (そこでは小さな勾配を持つ) softmax 関数を非常に hard な softmax という結果に押しやるために成されます。
例えば、Q と K が 0 の平均と 1 の分散を持つことを考えます。それらの行列乗算は 0 の平気にと dk の分散を持ちます。こうして、dk の平方根がスケーリングのために使用されます (そして他のどのような数字でもない)、何故ならば Q と K の matmul は 0 の平均と 1 の分散を持つはずですので、gentler softmax を得ます。
マスクは (負の無限大に近い) -1e9 で乗算されます。これが成されるのは、マスクは Q と K のスケールされた行列乗算で総計されて softmax の直前に適用されるからです。目標はこれらのセルをゼロ設定 (= zero out) することで、そして softmax への巨大な負の入力は出力でゼロ近くなります。
def scaled_dot_product_attention(q, k, v, mask): """Calculate the attention weights. q, k, v must have matching leading dimensions. k, v must have matching penultimate dimension, i.e.: seq_len_k = seq_len_v. The mask has different shapes depending on its type(padding or look ahead) but it must be broadcastable for addition. Args: q: query shape == (..., seq_len_q, depth) k: key shape == (..., seq_len_k, depth) v: value shape == (..., seq_len_v, depth_v) mask: Float tensor with shape broadcastable to (..., seq_len_q, seq_len_k). Defaults to None. Returns: output, attention_weights """ matmul_qk = tf.matmul(q, k, transpose_b=True) # (..., seq_len_q, seq_len_k) # scale matmul_qk dk = tf.cast(tf.shape(k)[-1], tf.float32) scaled_attention_logits = matmul_qk / tf.math.sqrt(dk) # add the mask to the scaled tensor. if mask is not None: scaled_attention_logits += (mask * -1e9) # softmax is normalized on the last axis (seq_len_k) so that the scores # add up to 1. attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1) # (..., seq_len_q, seq_len_k) output = tf.matmul(attention_weights, v) # (..., seq_len_q, depth_v) return output, attention_weights
softmax 正規化は K 上で行われるので、その値は Q に与えられる重要性の総量を決定します。
出力は attention 重みと V (値) ベクトルの乗算を表します。これは注目したい単語がそのまま保持されて無関係な単語が追い出される (flushed out) ことを確実にします。
def print_out(q, k, v): temp_out, temp_attn = scaled_dot_product_attention( q, k, v, None) print ('Attention weights are:') print (temp_attn) print ('Output is:') print (temp_out)
np.set_printoptions(suppress=True) temp_k = tf.constant([[10,0,0], [0,10,0], [0,0,10], [0,0,10]], dtype=tf.float32) # (4, 3) temp_v = tf.constant([[ 1,0], [ 10,0], [ 100,5], [1000,6]], dtype=tf.float32) # (4, 2) # This `query` aligns with the second `key`, # so the second `value` is returned. temp_q = tf.constant([[0, 10, 0]], dtype=tf.float32) # (1, 3) print_out(temp_q, temp_k, temp_v)
Attention weights are: tf.Tensor([[0. 1. 0. 0.]], shape=(1, 4), dtype=float32) Output is: tf.Tensor([[10. 0.]], shape=(1, 2), dtype=float32)
# This query aligns with a repeated key (third and fourth), # so all associated values get averaged. temp_q = tf.constant([[0, 0, 10]], dtype=tf.float32) # (1, 3) print_out(temp_q, temp_k, temp_v)
Attention weights are: tf.Tensor([[0. 0. 0.5 0.5]], shape=(1, 4), dtype=float32) Output is: tf.Tensor([[550. 5.5]], shape=(1, 2), dtype=float32)
# This query aligns equally with the first and second key, # so their values get averaged. temp_q = tf.constant([[10, 10, 0]], dtype=tf.float32) # (1, 3) print_out(temp_q, temp_k, temp_v)
Attention weights are: tf.Tensor([[0.5 0.5 0. 0. ]], shape=(1, 4), dtype=float32) Output is: tf.Tensor([[5.5 0. ]], shape=(1, 2), dtype=float32)
総ての問い合わせを一緒に渡します。
temp_q = tf.constant([[0, 0, 10], [0, 10, 0], [10, 10, 0]], dtype=tf.float32) # (3, 3) print_out(temp_q, temp_k, temp_v)
Attention weights are: tf.Tensor( [[0. 0. 0.5 0.5] [0. 1. 0. 0. ] [0.5 0.5 0. 0. ]], shape=(3, 4), dtype=float32) Output is: tf.Tensor( [[550. 5.5] [ 10. 0. ] [ 5.5 0. ]], shape=(3, 2), dtype=float32)
マルチヘッド attention
マルチヘッド attention は 4 つのパートから成ります : * 線形層とヘッドへの分割。* スケールされたドット積 attention。* ヘッドの結合。* 最後の線形層。
各マルチヘッド attention ブロックは 3 つの入力を得ます; Q (query), K (key), V (value)。これらは線形 (Dense) 層を通されてマルチヘッドに分割されます。
上で定義された scaled_dot_product_attention は各ヘッドに適用されます (効率のためにブロードキャストされます)。attention ステップでは適切なマスクが使用されなければなりません。それから各ヘッドのための attention 出力は (tf.transpose と tf.reshape を使用して) 結合されて最後の Dense 層を通されます。
一つの単一の attention ヘッドの代わりに、Q, K と V はマルチヘッドに分割されます、何故ならばそれはモデルに異なる具象空間からの異なる位置における情報に一緒に注意を払うことを可能にするからです。分割の後各ヘッドは削減された次元を持ちますので、総計の計算コストは full 次元を持つ単一ヘッド attention と同じです。
class MultiHeadAttention(tf.keras.layers.Layer): def __init__(self, d_model, num_heads): super(MultiHeadAttention, self).__init__() self.num_heads = num_heads self.d_model = d_model assert d_model % self.num_heads == 0 self.depth = d_model // self.num_heads self.wq = tf.keras.layers.Dense(d_model) self.wk = tf.keras.layers.Dense(d_model) self.wv = tf.keras.layers.Dense(d_model) self.dense = tf.keras.layers.Dense(d_model) def split_heads(self, x, batch_size): """Split the last dimension into (num_heads, depth). Transpose the result such that the shape is (batch_size, num_heads, seq_len, depth) """ x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth)) return tf.transpose(x, perm=[0, 2, 1, 3]) def call(self, v, k, q, mask): batch_size = tf.shape(q)[0] q = self.wq(q) # (batch_size, seq_len, d_model) k = self.wk(k) # (batch_size, seq_len, d_model) v = self.wv(v) # (batch_size, seq_len, d_model) q = self.split_heads(q, batch_size) # (batch_size, num_heads, seq_len_q, depth) k = self.split_heads(k, batch_size) # (batch_size, num_heads, seq_len_k, depth) v = self.split_heads(v, batch_size) # (batch_size, num_heads, seq_len_v, depth) # scaled_attention.shape == (batch_size, num_heads, seq_len_q, depth) # attention_weights.shape == (batch_size, num_heads, seq_len_q, seq_len_k) scaled_attention, attention_weights = scaled_dot_product_attention( q, k, v, mask) scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3]) # (batch_size, seq_len_q, num_heads, depth) concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model)) # (batch_size, seq_len_q, d_model) output = self.dense(concat_attention) # (batch_size, seq_len_q, d_model) return output, attention_weights
試すために MultiHeadAttention 層を作成します。シークエンス y の各位置で、MultiHeadAttention で総ての 8 attention ヘッドをシークエンスの総ての他の位置に渡り実行し、各位置で同じ長さの新しいベクトルを返します。
temp_mha = MultiHeadAttention(d_model=512, num_heads=8) y = tf.random.uniform((1, 60, 512)) # (batch_size, encoder_sequence, d_model) out, attn = temp_mha(y, k=y, q=y, mask=None) out.shape, attn.shape
(TensorShape([1, 60, 512]), TensorShape([1, 8, 60, 60]))
ポイント-wise 順伝播ネットワーク
ポイント-wise 順伝播ネットワークは間に ReLU 活性を持つ 2 つの完全結合層から成ります。
def point_wise_feed_forward_network(d_model, dff): return tf.keras.Sequential([ tf.keras.layers.Dense(dff, activation='relu'), # (batch_size, seq_len, dff) tf.keras.layers.Dense(d_model) # (batch_size, seq_len, d_model) ])
sample_ffn = point_wise_feed_forward_network(512, 2048) sample_ffn(tf.random.uniform((64, 50, 512))).shape
TensorShape([64, 50, 512])
エンコーダとデコーダ
transformer モデルは標準的な sequence to sequence with attention model と同じ一般的なパターンに従います。
- 入力センテンスはシークエンスの各単語/トークンのための出力を生成する N エンコーダ層を通されます。
- デコーダは次の単語を予測するためにエンコーダの出力とそれ自身の入力 (self-attention) に注目します。
エンコーダ層
各エンコーダ層は副層 (= sublayer) から成ります :
- (パディング・マスクを持つ) マルチヘッド attention
- ポイント-wise な順伝播ネットワーク。
これらの副層の各々はその回りに残差接続を持ち層正規化が続きます。残差接続は深層ネットワークにおける勾配消失問題を回避する助けとなります。各副層の出力は LayerNorm(x + Sublayer(x)) です。正規化は d_model (last) 軸上で行われます。transformer には N エンコーダ層があります。
class EncoderLayer(tf.keras.layers.Layer): def __init__(self, d_model, num_heads, dff, rate=0.1): super(EncoderLayer, self).__init__() self.mha = MultiHeadAttention(d_model, num_heads) self.ffn = point_wise_feed_forward_network(d_model, dff) self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6) self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6) self.dropout1 = tf.keras.layers.Dropout(rate) self.dropout2 = tf.keras.layers.Dropout(rate) def call(self, x, training, mask): attn_output, _ = self.mha(x, x, x, mask) # (batch_size, input_seq_len, d_model) attn_output = self.dropout1(attn_output, training=training) out1 = self.layernorm1(x + attn_output) # (batch_size, input_seq_len, d_model) ffn_output = self.ffn(out1) # (batch_size, input_seq_len, d_model) ffn_output = self.dropout2(ffn_output, training=training) out2 = self.layernorm2(out1 + ffn_output) # (batch_size, input_seq_len, d_model) return out2
sample_encoder_layer = EncoderLayer(512, 8, 2048) sample_encoder_layer_output = sample_encoder_layer( tf.random.uniform((64, 43, 512)), False, None) sample_encoder_layer_output.shape # (batch_size, input_seq_len, d_model)
TensorShape([64, 43, 512])
デコーダ層
各デコーダ層は次の副層から成ります :
- (look ahead マスクとパディングマスクを持つ) マスクされたマルチヘッド attention
- (パディングマスクを持つ) マルチヘッド attention。V (value) と K (key) はエンコーダ出力を入力として受け取ります。Q (query) はマスクされたマルチヘッド attention 副層からの出力を受け取ります。
- ポイント wise 順伝播ネットワーク
これらの副層の各々はその回りに残差接続を持ち層正規化が続きます。各副層の出力は LayerNorm(x + Sublayer(x)) です。正規化は d_model (last) 軸上で行われます。
transformer には N デコーダ層があります。
Q がデコーダの最初の attention ブロックからの出力を受け取り、そして K がエンコーダ出力を受け取るとき、attention 重みはエンコーダ出力に基づくデコーダの入力に与えられる重要性を表します。換言すれば、デコーダはエンコーダ出力とそれ自身の出力への self-attending を見ることにより次の単語を予測します。スケールされたドット積 attention セクションの上のデモを見てください。
class DecoderLayer(tf.keras.layers.Layer): def __init__(self, d_model, num_heads, dff, rate=0.1): super(DecoderLayer, self).__init__() self.mha1 = MultiHeadAttention(d_model, num_heads) self.mha2 = MultiHeadAttention(d_model, num_heads) self.ffn = point_wise_feed_forward_network(d_model, dff) self.layernorm1 = tf.keras.layers.LayerNormalization(epsilon=1e-6) self.layernorm2 = tf.keras.layers.LayerNormalization(epsilon=1e-6) self.layernorm3 = tf.keras.layers.LayerNormalization(epsilon=1e-6) self.dropout1 = tf.keras.layers.Dropout(rate) self.dropout2 = tf.keras.layers.Dropout(rate) self.dropout3 = tf.keras.layers.Dropout(rate) def call(self, x, enc_output, training, look_ahead_mask, padding_mask): # enc_output.shape == (batch_size, input_seq_len, d_model) attn1, attn_weights_block1 = self.mha1(x, x, x, look_ahead_mask) # (batch_size, target_seq_len, d_model) attn1 = self.dropout1(attn1, training=training) out1 = self.layernorm1(attn1 + x) attn2, attn_weights_block2 = self.mha2( enc_output, enc_output, out1, padding_mask) # (batch_size, target_seq_len, d_model) attn2 = self.dropout2(attn2, training=training) out2 = self.layernorm2(attn2 + out1) # (batch_size, target_seq_len, d_model) ffn_output = self.ffn(out2) # (batch_size, target_seq_len, d_model) ffn_output = self.dropout3(ffn_output, training=training) out3 = self.layernorm3(ffn_output + out2) # (batch_size, target_seq_len, d_model) return out3, attn_weights_block1, attn_weights_block2
sample_decoder_layer = DecoderLayer(512, 8, 2048) sample_decoder_layer_output, _, _ = sample_decoder_layer( tf.random.uniform((64, 50, 512)), sample_encoder_layer_output, False, None, None) sample_decoder_layer_output.shape # (batch_size, target_seq_len, d_model)
TensorShape([64, 50, 512])
エンコーダ
エンコーダは: 1. 入力埋め込み 2. 位置エンコーディング 3. N エンコーダ層から成ります。
入力は埋め込みを通されます、これは位置エンコーディングにより総計されます。この総計の出力はエンコーダ層への入力です。エンコーダの出力はデコーダへの入力です。
class Encoder(tf.keras.layers.Layer): def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, rate=0.1): super(Encoder, self).__init__() self.d_model = d_model self.num_layers = num_layers self.embedding = tf.keras.layers.Embedding(input_vocab_size, d_model) self.pos_encoding = positional_encoding(input_vocab_size, self.d_model) self.enc_layers = [EncoderLayer(d_model, num_heads, dff, rate) for _ in range(num_layers)] self.dropout = tf.keras.layers.Dropout(rate) def call(self, x, training, mask): seq_len = tf.shape(x)[1] # adding embedding and position encoding. x = self.embedding(x) # (batch_size, input_seq_len, d_model) x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32)) x += self.pos_encoding[:, :seq_len, :] x = self.dropout(x, training=training) for i in range(self.num_layers): x = self.enc_layers[i](x, training, mask) return x # (batch_size, input_seq_len, d_model)
sample_encoder = Encoder(num_layers=2, d_model=512, num_heads=8, dff=2048, input_vocab_size=8500) sample_encoder_output = sample_encoder(tf.random.uniform((64, 62)), training=False, mask=None) print (sample_encoder_output.shape) # (batch_size, input_seq_len, d_model)
(64, 62, 512)
デコーダ
デコーダは: 1. 出力埋め込み 2. 位置エンコーディング 3. N デコーダ層から成ります。
ターゲットは埋め込みを通されます、これは位置エンコーディングにより総計されます。この総計の出力はデコーダ層への入力です。デコーダの出力は最後の線形層への入力です。
class Decoder(tf.keras.layers.Layer): def __init__(self, num_layers, d_model, num_heads, dff, target_vocab_size, rate=0.1): super(Decoder, self).__init__() self.d_model = d_model self.num_layers = num_layers self.embedding = tf.keras.layers.Embedding(target_vocab_size, d_model) self.pos_encoding = positional_encoding(target_vocab_size, self.d_model) self.dec_layers = [DecoderLayer(d_model, num_heads, dff, rate) for _ in range(num_layers)] self.dropout = tf.keras.layers.Dropout(rate) def call(self, x, enc_output, training, look_ahead_mask, padding_mask): seq_len = tf.shape(x)[1] attention_weights = {} x = self.embedding(x) # (batch_size, target_seq_len, d_model) x *= tf.math.sqrt(tf.cast(self.d_model, tf.float32)) x += self.pos_encoding[:, :seq_len, :] x = self.dropout(x, training=training) for i in range(self.num_layers): x, block1, block2 = self.dec_layers[i](x, enc_output, training, look_ahead_mask, padding_mask) attention_weights['decoder_layer{}_block1'.format(i+1)] = block1 attention_weights['decoder_layer{}_block2'.format(i+1)] = block2 # x.shape == (batch_size, target_seq_len, d_model) return x, attention_weights
sample_decoder = Decoder(num_layers=2, d_model=512, num_heads=8, dff=2048, target_vocab_size=8000) output, attn = sample_decoder(tf.random.uniform((64, 26)), enc_output=sample_encoder_output, training=False, look_ahead_mask=None, padding_mask=None) output.shape, attn['decoder_layer2_block2'].shape
(TensorShape([64, 26, 512]), TensorShape([64, 8, 26, 62]))
Transformer を作成する
Transformer はエンコーダ、デコーダと最後の線形層から成ります。デコーダの出力は線形層への入力でその出力が返されます。
class Transformer(tf.keras.Model): def __init__(self, num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, rate=0.1): super(Transformer, self).__init__() self.encoder = Encoder(num_layers, d_model, num_heads, dff, input_vocab_size, rate) self.decoder = Decoder(num_layers, d_model, num_heads, dff, target_vocab_size, rate) self.final_layer = tf.keras.layers.Dense(target_vocab_size) def call(self, inp, tar, training, enc_padding_mask, look_ahead_mask, dec_padding_mask): enc_output = self.encoder(inp, training, enc_padding_mask) # (batch_size, inp_seq_len, d_model) # dec_output.shape == (batch_size, tar_seq_len, d_model) dec_output, attention_weights = self.decoder( tar, enc_output, training, look_ahead_mask, dec_padding_mask) final_output = self.final_layer(dec_output) # (batch_size, tar_seq_len, target_vocab_size) return final_output, attention_weights
sample_transformer = Transformer( num_layers=2, d_model=512, num_heads=8, dff=2048, input_vocab_size=8500, target_vocab_size=8000) temp_input = tf.random.uniform((64, 62)) temp_target = tf.random.uniform((64, 26)) fn_out, _ = sample_transformer(temp_input, temp_target, training=False, enc_padding_mask=None, look_ahead_mask=None, dec_padding_mask=None) fn_out.shape # (batch_size, tar_seq_len, target_vocab_size)
TensorShape([64, 26, 8000])
ハイパーパラメータを設定する
この例題を小さく比較的高速に保つために、num_layers, d_model と dff のための値は減じられます。
transformer の基本モデルで使用された値は ; num_layers=6, d_model = 512, dff = 2048 でした。transformer の総ての他のバージョンについては ペーパー を見てください。
Note: 下の値を変更することにより、多くのタスク上で最先端技術を達成するモデルを得ることができます。
num_layers = 4 d_model = 128 dff = 512 num_heads = 8 input_vocab_size = tokenizer_pt.vocab_size + 2 target_vocab_size = tokenizer_en.vocab_size + 2 dropout_rate = 0.1
Optimizer
ペーパー の公式に従うカスタム学習率スケジューラを持つ Adam optimizer を使用します。
class CustomSchedule(tf.keras.optimizers.schedules.LearningRateSchedule): def __init__(self, d_model, warmup_steps=4000): super(CustomSchedule, self).__init__() self.d_model = d_model self.d_model = tf.cast(self.d_model, tf.float32) self.warmup_steps = warmup_steps def __call__(self, step): arg1 = tf.math.rsqrt(step) arg2 = step * (self.warmup_steps ** -1.5) return tf.math.rsqrt(self.d_model) * tf.math.minimum(arg1, arg2)
learning_rate = CustomSchedule(d_model) optimizer = tf.keras.optimizers.Adam(learning_rate, beta_1=0.9, beta_2=0.98, epsilon=1e-9)
temp_learning_rate_schedule = CustomSchedule(d_model) plt.plot(temp_learning_rate_schedule(tf.range(40000, dtype=tf.float32))) plt.ylabel("Learning Rate") plt.xlabel("Train Step")
Text(0.5, 0, 'Train Step')
損失とメトリクス
ターゲット・シークエンスはパディングされていますので、損失を計算するときパディング・マスクを適用することは重要です。
loss_object = tf.keras.losses.SparseCategoricalCrossentropy( from_logits=True, reduction='none')
def loss_function(real, pred): mask = tf.math.logical_not(tf.math.equal(real, 0)) loss_ = loss_object(real, pred) mask = tf.cast(mask, dtype=loss_.dtype) loss_ *= mask return tf.reduce_mean(loss_)
train_loss = tf.keras.metrics.Mean(name='train_loss') train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy( name='train_accuracy')
訓練とチェックポインティング
transformer = Transformer(num_layers, d_model, num_heads, dff, input_vocab_size, target_vocab_size, dropout_rate)
def create_masks(inp, tar): # Encoder padding mask enc_padding_mask = create_padding_mask(inp) # Used in the 2nd attention block in the decoder. # This padding mask is used to mask the encoder outputs. dec_padding_mask = create_padding_mask(inp) # Used in the 1st attention block in the decoder. # It is used to pad and mask future tokens in the input received by # the decoder. look_ahead_mask = create_look_ahead_mask(tf.shape(tar)[1]) dec_target_padding_mask = create_padding_mask(tar) combined_mask = tf.maximum(dec_target_padding_mask, look_ahead_mask) return enc_padding_mask, combined_mask, dec_padding_mask
チェックポイント・パスとチェックポイント・マネージャを作成します。これは n エポック毎にチェックポイントをセーブするために使用されます。
checkpoint_path = "./checkpoints/train" ckpt = tf.train.Checkpoint(transformer=transformer, optimizer=optimizer) ckpt_manager = tf.train.CheckpointManager(ckpt, checkpoint_path, max_to_keep=5) # if a checkpoint exists, restore the latest checkpoint. if ckpt_manager.latest_checkpoint: ckpt.restore(ckpt_manager.latest_checkpoint) print ('Latest checkpoint restored!!')
ターゲットは tar_inp と tar_real に分割されます。tar_inp はデコーダへの入力として渡されます。tar_real は 1 シフトされたそれと同じ入力です : tar_input の各位置において、tar_real は予測されるべき次のトークンを含みます。
例えば、sentence = "SOS A lion in the jungle is sleeping EOS"
tar_inp = "SOS A lion in the jungle is sleeping"
tar_real = "A lion in the jungle is sleeping EOS"
transformer は自己回帰モデルです : それは一度に一つのパートの予測を行ないます、そして次に何を行なうかを決めるためにそこまでのその出力使用します。
訓練の間この例は (テキスト生成チュートリアル 内のように) teacher-forcing を使用します。teacher forcing は現在の時間ステップでモデルが何を予測するかにかかわらず、次の時間ステップに対して真の出力を渡します。
transformer が各単語を予測するとき、self-attention は次の単語をより良く予測するためにそれに入力シークエンスの前の単語を見ることを許容します。
モデルが期待される出力でピークに達することを防ぐためにモデルは look-ahead マスクを使用します。
EPOCHS = 20
@tf.function def train_step(inp, tar): tar_inp = tar[:, :-1] tar_real = tar[:, 1:] enc_padding_mask, combined_mask, dec_padding_mask = create_masks(inp, tar_inp) with tf.GradientTape() as tape: predictions, _ = transformer(inp, tar_inp, True, enc_padding_mask, combined_mask, dec_padding_mask) loss = loss_function(tar_real, predictions) gradients = tape.gradient(loss, transformer.trainable_variables) optimizer.apply_gradients(zip(gradients, transformer.trainable_variables)) train_loss(loss) train_accuracy(tar_real, predictions)
ポルトガル語は入力言語として使用されて英語はターゲット言語として使用されます。
for epoch in range(EPOCHS): start = time.time() train_loss.reset_states() train_accuracy.reset_states() # inp -> portuguese, tar -> english for (batch, (inp, tar)) in enumerate(train_dataset): train_step(inp, tar) if batch % 500 == 0: print ('Epoch {} Batch {} Loss {:.4f} Accuracy {:.4f}'.format( epoch + 1, batch, train_loss.result(), train_accuracy.result())) if (epoch + 1) % 5 == 0: ckpt_save_path = ckpt_manager.save() print ('Saving checkpoint for epoch {} at {}'.format(epoch+1, ckpt_save_path)) print ('Epoch {} Loss {:.4f} Accuracy {:.4f}'.format(epoch + 1, train_loss.result(), train_accuracy.result())) print ('Time taken for 1 epoch: {} secs\n'.format(time.time() - start))
Epoch 1 Batch 0 Loss 4.2989 Accuracy 0.0000 Epoch 1 Batch 500 Loss 3.6015 Accuracy 0.0373 Epoch 1 Loss 3.3603 Accuracy 0.0516 Time taken for 1 epoch: 1183.8238337039948 secs Epoch 2 Batch 0 Loss 2.6611 Accuracy 0.0990 Epoch 2 Batch 500 Loss 2.4153 Accuracy 0.1205 Epoch 2 Loss 2.3610 Accuracy 0.1255 Time taken for 1 epoch: 59.42831206321716 secs Epoch 3 Batch 0 Loss 2.2400 Accuracy 0.1385 Epoch 3 Batch 500 Loss 2.1346 Accuracy 0.1485 Epoch 3 Loss 2.1008 Accuracy 0.1515 Time taken for 1 epoch: 61.30026984214783 secs Epoch 4 Batch 0 Loss 1.9878 Accuracy 0.1736 Epoch 4 Batch 500 Loss 1.9165 Accuracy 0.1728 Epoch 4 Loss 1.8791 Accuracy 0.1767 Time taken for 1 epoch: 58.594091176986694 secs Epoch 5 Batch 0 Loss 1.7749 Accuracy 0.1979 Epoch 5 Batch 500 Loss 1.6912 Accuracy 0.2004 Saving checkpoint for epoch 5 at ./checkpoints/train/ckpt-1 Epoch 5 Loss 1.6600 Accuracy 0.2032 Time taken for 1 epoch: 58.794411182403564 secs Epoch 6 Batch 0 Loss 1.5714 Accuracy 0.2166 Epoch 6 Batch 500 Loss 1.5055 Accuracy 0.2208 Epoch 6 Loss 1.4784 Accuracy 0.2231 Time taken for 1 epoch: 58.68531322479248 secs Epoch 7 Batch 0 Loss 1.3653 Accuracy 0.2448 Epoch 7 Batch 500 Loss 1.3229 Accuracy 0.2414 Epoch 7 Loss 1.2951 Accuracy 0.2441 Time taken for 1 epoch: 58.634846687316895 secs Epoch 8 Batch 0 Loss 1.1872 Accuracy 0.2604 Epoch 8 Batch 500 Loss 1.1627 Accuracy 0.2610 Epoch 8 Loss 1.1418 Accuracy 0.2628 Time taken for 1 epoch: 63.22854280471802 secs Epoch 9 Batch 0 Loss 1.0552 Accuracy 0.2804 Epoch 9 Batch 500 Loss 1.0437 Accuracy 0.2758 Epoch 9 Loss 1.0284 Accuracy 0.2769 Time taken for 1 epoch: 59.79085969924927 secs Epoch 10 Batch 0 Loss 0.9547 Accuracy 0.2947 Epoch 10 Batch 500 Loss 0.9574 Accuracy 0.2864 Saving checkpoint for epoch 10 at ./checkpoints/train/ckpt-2 Epoch 10 Loss 0.9452 Accuracy 0.2872 Time taken for 1 epoch: 59.45661449432373 secs Epoch 11 Batch 0 Loss 0.8765 Accuracy 0.3056 Epoch 11 Batch 500 Loss 0.8882 Accuracy 0.2959 Epoch 11 Loss 0.8775 Accuracy 0.2965 Time taken for 1 epoch: 59.00851774215698 secs Epoch 12 Batch 0 Loss 0.8012 Accuracy 0.3108 Epoch 12 Batch 500 Loss 0.8317 Accuracy 0.3032 Epoch 12 Loss 0.8224 Accuracy 0.3038 Time taken for 1 epoch: 59.61574602127075 secs Epoch 13 Batch 0 Loss 0.7604 Accuracy 0.3212 Epoch 13 Batch 500 Loss 0.7867 Accuracy 0.3095 Epoch 13 Loss 0.7779 Accuracy 0.3101 Time taken for 1 epoch: 61.540324449539185 secs Epoch 14 Batch 0 Loss 0.7314 Accuracy 0.3242 Epoch 14 Batch 500 Loss 0.7459 Accuracy 0.3155 Epoch 14 Loss 0.7372 Accuracy 0.3159 Time taken for 1 epoch: 59.23609805107117 secs Epoch 15 Batch 0 Loss 0.6704 Accuracy 0.3338 Epoch 15 Batch 500 Loss 0.7071 Accuracy 0.3212 Saving checkpoint for epoch 15 at ./checkpoints/train/ckpt-3 Epoch 15 Loss 0.7007 Accuracy 0.3212 Time taken for 1 epoch: 59.86083674430847 secs Epoch 16 Batch 0 Loss 0.6571 Accuracy 0.3312 Epoch 16 Batch 500 Loss 0.6749 Accuracy 0.3257 Epoch 16 Loss 0.6680 Accuracy 0.3260 Time taken for 1 epoch: 58.48514103889465 secs Epoch 17 Batch 0 Loss 0.6378 Accuracy 0.3368 Epoch 17 Batch 500 Loss 0.6461 Accuracy 0.3304 Epoch 17 Loss 0.6402 Accuracy 0.3304 Time taken for 1 epoch: 59.11438250541687 secs Epoch 18 Batch 0 Loss 0.5862 Accuracy 0.3455 Epoch 18 Batch 500 Loss 0.6217 Accuracy 0.3336 Epoch 18 Loss 0.6156 Accuracy 0.3337 Time taken for 1 epoch: 59.84031653404236 secs Epoch 19 Batch 0 Loss 0.5931 Accuracy 0.3424 Epoch 19 Batch 500 Loss 0.5977 Accuracy 0.3377 Epoch 19 Loss 0.5921 Accuracy 0.3378 Time taken for 1 epoch: 58.689385175704956 secs Epoch 20 Batch 0 Loss 0.5611 Accuracy 0.3485 Epoch 20 Batch 500 Loss 0.5770 Accuracy 0.3408 Saving checkpoint for epoch 20 at ./checkpoints/train/ckpt-4 Epoch 20 Loss 0.5721 Accuracy 0.3408 Time taken for 1 epoch: 59.26789140701294 secs
評価する
評価のために次のステップが使用されます :
- ポルトガル語字句解析器 (tokenizer_pt) を使用して入力センテンスをエンコードします。更に、start と end トークンを追加しますので入力はモデルがそれで訓練されたものと等値です。これがエンコーダ入力です。
- デコーダ入力は "start token == tokenizer_en.vocab_size" です。
- パディング・マスクと look ahead マスクを計算する。
- それからデコーダはエンコーダ出力とそれ自身の出力 (self-attention) を見て予測を出力します。
- 最後の単語を選択してその argmax を計算します。
- 予測された単語をデコーダ入力へそれをデコーダに渡すとき結合します。
- このアプローチでは、デコーダはそれが予測した前の単語を基にして次の単語を予測します。
Note: ここで使用されたモデルは例題を比較的高速に保つためにより少ないキャパシティを持ちますので多分予測は少し正しくないでしょう。ペーパーの結果を再現するためには、上のパラメータを変更することにより、データセット全体と基本 transformer モデルか transformer XL を使用します。
def evaluate(inp_sentence): start_token = [tokenizer_pt.vocab_size] end_token = [tokenizer_pt.vocab_size + 1] # inp sentence is portuguese, hence adding the start and end token inp_sentence = start_token + tokenizer_pt.encode(inp_sentence) + end_token encoder_input = tf.expand_dims(inp_sentence, 0) # as the target is english, the first word to the transformer should be the # english start token. decoder_input = [tokenizer_en.vocab_size] output = tf.expand_dims(decoder_input, 0) for i in range(MAX_LENGTH): enc_padding_mask, combined_mask, dec_padding_mask = create_masks( encoder_input, output) # predictions.shape == (batch_size, seq_len, vocab_size) predictions, attention_weights = transformer(encoder_input, output, False, enc_padding_mask, combined_mask, dec_padding_mask) # select the last word from the seq_len dimension predictions = predictions[: ,-1:, :] # (batch_size, 1, vocab_size) predicted_id = tf.cast(tf.argmax(predictions, axis=-1), tf.int32) # return the result if the predicted_id is equal to the end token if tf.equal(predicted_id, tokenizer_en.vocab_size+1): return tf.squeeze(output, axis=0), attention_weights # concatentate the predicted_id to the output which is given to the decoder # as its input. output = tf.concat([output, predicted_id], axis=-1) return tf.squeeze(output, axis=0), attention_weights
def plot_attention_weights(attention, sentence, result, layer): fig = plt.figure(figsize=(16, 8)) sentence = tokenizer_pt.encode(sentence) attention = tf.squeeze(attention[layer], axis=0) for head in range(attention.shape[0]): ax = fig.add_subplot(2, 4, head+1) # plot the attention weights ax.matshow(attention[head][:-1, :], cmap='viridis') fontdict = {'fontsize': 10} ax.set_xticks(range(len(sentence)+2)) ax.set_yticks(range(len(result))) ax.set_ylim(len(result)-1.5, -0.5) ax.set_xticklabels( ['']+[tokenizer_pt.decode([i]) for i in sentence]+[' '], fontdict=fontdict, rotation=90) ax.set_yticklabels([tokenizer_en.decode([i]) for i in result if i < tokenizer_en.vocab_size], fontdict=fontdict) ax.set_xlabel('Head {}'.format(head+1)) plt.tight_layout() plt.show()
def translate(sentence, plot=''): result, attention_weights = evaluate(sentence) predicted_sentence = tokenizer_en.decode([i for i in result if i < tokenizer_en.vocab_size]) print('Input: {}'.format(sentence)) print('Predicted translation: {}'.format(predicted_sentence)) if plot: plot_attention_weights(attention_weights, sentence, result, plot)
translate("este é um problema que temos que resolver.") print ("Real translation: this is a problem we have to solve .")
Input: este é um problema que temos que resolver. Predicted translation: this is a problem that we have to solve .......... ..... .............. of that Real translation: this is a problem we have to solve .
translate("os meus vizinhos ouviram sobre esta ideia.") print ("Real translation: and my neighboring homes heard about this idea .")
Input: os meus vizinhos ouviram sobre esta ideia. Predicted translation: my neighbors heard about this idea . Real translation: and my neighboring homes heard about this idea .
translate("vou então muito rapidamente partilhar convosco algumas histórias de algumas coisas mágicas que aconteceram.") print ("Real translation: so i 'll just share with you some stories very quickly of some magical things that have happened .")
Input: vou então muito rapidamente partilhar convosco algumas histórias de algumas coisas mágicas que aconteceram. Predicted translation: so i 'm going to share with you a lot of stories that are very quickly that actually buzzs on top of the data . Real translation: so i 'll just share with you some stories very quickly of some magical things that have happened .
デコーダの異なる層と attention ブロックをプロット・パラメータに渡すことができます。
translate("este é o primeiro livro que eu fiz.", plot='decoder_layer4_block2') print ("Real translation: this is the first book i've ever done.")
Input: este é o primeiro livro que eu fiz. Predicted translation: this is the first book that i did it .
Real translation: this is the first book i've ever done.
要約
このチュートリアルでは、位置エンコーディング、マルチヘッド attention、マスキングの重要性そして transformer をどのように作成するかを学習しました。
transformer を訓練するために異なるデータセットを使用してみてください。貴方はまた上のハイパーパラメータを変更して基本 transformer か transformer XL を作成することもできます。また BERT を作成して最先端モデルを訓練するためにここで定義された層を使用することもできます。更に、より良い予測を得るためにビームサーチを実装できます。
以上