ホーム » TensorFlow 2.0 » TensorFlow 2.0 Beta : 上級 Tutorials : テキストとシークエンス :- 言語理解のための Transformer モデル

TensorFlow 2.0 Beta : 上級 Tutorials : テキストとシークエンス :- 言語理解のための Transformer モデル

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 モデルは RNNCNN の代わりに 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-次元空間で互いに近くなります。

それについて更に学習するために 位置エンコーディング のノートブックを見てください。位置エンコーディングを計算するための公式は次のようなものです :

$$\Large{PE_{(pos, 2i)} = sin(pos / 10000^{2i / d_{model}})} $$
$$\Large{PE_{(pos, 2i+1)} = cos(pos / 10000^{2i / d_{model}})} $$
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 重みを計算するために使用される等式は :

$$\Large{Attention(Q, K, V) = softmax_k(\frac{QK^T}{\sqrt{d_k}}) V} $$

ドット積 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.transposetf.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) から成ります :

  1. (パディング・マスクを持つ) マルチヘッド attention
  2. ポイント-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])

 

デコーダ層

各デコーダ層は次の副層から成ります :

  1. (look ahead マスクとパディングマスクを持つ) マスクされたマルチヘッド attention
  2. (パディングマスクを持つ) マルチヘッド attention。V (value) と K (key) はエンコーダ出力を入力として受け取ります。Q (query) はマスクされたマルチヘッド attention 副層からの出力を受け取ります。
  3. ポイント 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 を使用します。

$$\Large{lrate = d_{model}^{-0.5} * min(step{\_}num^{-0.5}, step{\_}num * warmup{\_}steps^{-1.5})}$$
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 を作成して最先端モデルを訓練するためにここで定義された層を使用することもできます。更に、より良い予測を得るためにビームサーチを実装できます。

 

以上






AI導入支援 #2 ウェビナー

スモールスタートを可能としたAI導入支援   Vol.2
[無料 WEB セミナー] [詳細]
「画像認識 AI PoC スターターパック」の紹介
既に AI 技術を実ビジネスで活用し、成果を上げている日本企業も多く存在しており、競争優位なビジネスを展開しております。
しかしながら AI を導入したくとも PoC (概念実証) だけでも高額な費用がかかり取組めていない企業も少なくないようです。A I導入時には欠かせない PoC を手軽にしかも短期間で認知度を確認可能とするサービの紹介と共に、AI 技術の特性と具体的な導入プロセスに加え運用時のポイントについても解説いたします。
日時:2021年10月13日(水)
会場:WEBセミナー
共催:クラスキャット、日本FLOW(株)
後援:働き方改革推進コンソーシアム
参加費: 無料 (事前登録制)
人工知能開発支援
◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
(株)クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com