Dikkatle sinirsel makine çevirisi

TensorFlow.org'da görüntüleyin Google Colab'da çalıştırın Kaynağı GitHub'da görüntüleyinNot defterini indir

Bu defter dayalı İngiliz çeviri İspanyolca için sekans (seq2seq) modeline ilişkin diziyi trenler Dikkat tabanlı Sinir Makine Çevirisi Yaklaşımlar Etkili . Bu, aşağıdakiler hakkında biraz bilgi sahibi olduğunu varsayan gelişmiş bir örnektir:

  • Diziden diziye modeller
  • Keras katmanının altındaki TensorFlow temelleri:

Bu mimari biraz modası geçmiş olsa da durumu (gitmeden önce dikkat mekanizmalarının daha derin bir anlayış elde etmek yoluyla çalışmalarına çok yararlı bir proje hala Transformers ).

"? ¿Todavia estan en casa" Bu defterde modeli eğitim sonra, örneğin, bir İspanyol cümle giriş yapabilmek ve İngilizce çevirisini dönecektir: "Evde hala"

Elde edilen model olarak ihraç olan tf.saved_model diğer TensorFlow ortamlarda kullanılabilir, böylece.

Bir oyuncak örneği için çeviri kalitesi makul, ancak oluşturulan dikkat grafiği belki daha ilginç. Bu, çeviri yaparken giriş cümlesinin hangi bölümlerinin modelin dikkatini çektiğini gösterir:

ispanyolca-ingilizce dikkat planı

Kurmak

pip install tensorflow_text
import numpy as np

import typing
from typing import Any, Tuple

import tensorflow as tf

import tensorflow_text as tf_text

import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

Bu öğretici, sıfırdan birkaç katman oluşturur; özel ve yerleşik uygulamalar arasında geçiş yapmak istiyorsanız bu değişkeni kullanın.

use_builtins = True

Bu öğretici, şekilleri yanlış almanın kolay olduğu birçok düşük seviye API kullanır. Bu sınıf, öğretici boyunca şekilleri kontrol etmek için kullanılır.

Şekil denetleyicisi

Veri

Biz tarafından sağlanan bir dil veri kümesi kullanacağız http://www.manythings.org/anki/ Bu veri kümesi biçiminde dil çeviri çifti içerir:

May I borrow this book? ¿Puedo tomar prestado este libro?

Kullanılabilir çeşitli dilleri var, ancak İngilizce-İspanyolca veri setini kullanacağız.

Veri kümesini indirin ve hazırlayın

Kolaylık olması açısından, bu veri kümesinin bir kopyasını Google Cloud'da barındırdık, ancak kendi kopyanızı da indirebilirsiniz. Veri kümesini indirdikten sonra, verileri hazırlamak için atacağımız adımlar şunlardır:

  1. Her cümleye belirteç bir başlangıç ve bitiş ekleyin.
  2. Özel karakterleri kaldırarak cümleleri temizleyin.
  3. Bir kelime indeksi ve ters kelime indeksi oluşturun (kelime → id ve id → kelimeden eşlenen sözlükler).
  4. Her cümleyi maksimum uzunluğa yapıştırın.
# Download the file
import pathlib

path_to_zip = tf.keras.utils.get_file(
    'spa-eng.zip', origin='http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip',
    extract=True)

path_to_file = pathlib.Path(path_to_zip).parent/'spa-eng/spa.txt'
Downloading data from http://storage.googleapis.com/download.tensorflow.org/data/spa-eng.zip
2646016/2638744 [==============================] - 0s 0us/step
2654208/2638744 [==============================] - 0s 0us/step
def load_data(path):
  text = path.read_text(encoding='utf-8')

  lines = text.splitlines()
  pairs = [line.split('\t') for line in lines]

  inp = [inp for targ, inp in pairs]
  targ = [targ for targ, inp in pairs]

  return targ, inp
targ, inp = load_data(path_to_file)
print(inp[-1])
Si quieres sonar como un hablante nativo, debes estar dispuesto a practicar diciendo la misma frase una y otra vez de la misma manera en que un músico de banjo practica el mismo fraseo una y otra vez hasta que lo puedan tocar correctamente y en el tiempo esperado.
print(targ[-1])
If you want to sound like a native speaker, you must be willing to practice saying the same sentence over and over in the same way that banjo players practice the same phrase over and over until they can play it correctly and at the desired tempo.

Bir tf.data veri kümesi oluşturun

Dizeleri bu dizilerden bir oluşturabilir tf.data.Dataset olduğunu karıştırır ve toplu bunları verimli bir şekilde dizeleri:

BUFFER_SIZE = len(inp)
BATCH_SIZE = 64

dataset = tf.data.Dataset.from_tensor_slices((inp, targ)).shuffle(BUFFER_SIZE)
dataset = dataset.batch(BATCH_SIZE)
for example_input_batch, example_target_batch in dataset.take(1):
  print(example_input_batch[:5])
  print()
  print(example_target_batch[:5])
  break
tf.Tensor(
[b'No s\xc3\xa9 lo que quiero.' b'\xc2\xbfDeber\xc3\xada repetirlo?'
 b'Tard\xc3\xa9 m\xc3\xa1s de 2 horas en traducir unas p\xc3\xa1ginas en ingl\xc3\xa9s.'
 b'A Tom comenz\xc3\xb3 a temerle a Mary.' b'Mi pasatiempo es la lectura.'], shape=(5,), dtype=string)

tf.Tensor(
[b"I don't know what I want." b'Should I repeat it?'
 b'It took me more than two hours to translate a few pages of English.'
 b'Tom became afraid of Mary.' b'My hobby is reading.'], shape=(5,), dtype=string)

Metin ön işleme

Bu eğitimde amaçlarından biri bir olarak ihraç edilebilir bir model oluşturmaktır tf.saved_model . O ihraç modeli kullanışlı hale getirmek için onu almalı tf.string girdileri ve iade tf.string çıkışları: Tüm metin işleme modeli içinde gerçekleşir.

Standardizasyon

Model, sınırlı bir kelime dağarcığına sahip çok dilli metinlerle ilgilenmektedir. Bu nedenle, giriş metnini standartlaştırmak önemli olacaktır.

İlk adım, aksanlı karakterleri bölmek ve uyumluluk karakterlerini ASCII eşdeğerleriyle değiştirmek için Unicode normalleştirmesidir.

tensorflow_text paketi unicode normalleştirmek işlemi içerir:

example_text = tf.constant('¿Todavía está en casa?')

print(example_text.numpy())
print(tf_text.normalize_utf8(example_text, 'NFKD').numpy())
b'\xc2\xbfTodav\xc3\xada est\xc3\xa1 en casa?'
b'\xc2\xbfTodavi\xcc\x81a esta\xcc\x81 en casa?'

Unicode normalleştirme, metin standardizasyon işlevindeki ilk adım olacaktır:

def tf_lower_and_split_punct(text):
  # Split accecented characters.
  text = tf_text.normalize_utf8(text, 'NFKD')
  text = tf.strings.lower(text)
  # Keep space, a to z, and select punctuation.
  text = tf.strings.regex_replace(text, '[^ a-z.?!,¿]', '')
  # Add spaces around punctuation.
  text = tf.strings.regex_replace(text, '[.?!,¿]', r' \0 ')
  # Strip whitespace.
  text = tf.strings.strip(text)

  text = tf.strings.join(['[START]', text, '[END]'], separator=' ')
  return text
print(example_text.numpy().decode())
print(tf_lower_and_split_punct(example_text).numpy().decode())
¿Todavía está en casa?
[START] ¿ todavia esta en casa ? [END]

Metin Vektörleştirme

Bu standardizasyon işlevi sarılmış olan tf.keras.layers.TextVectorization jeton dizilerine kelime çıkarma ve giriş metin dönüştürme idare edecek bir tabaka.

max_vocab_size = 5000

input_text_processor = tf.keras.layers.TextVectorization(
    standardize=tf_lower_and_split_punct,
    max_tokens=max_vocab_size)

TextVectorization tabakası ve diğer ön işleme tabakalar sahip adapt yöntem. Bu yöntem eğitim verileri zamanında bir devre okur ve gibi bir çok çalışır Model.fix . Bu adapt edilen verilerin dayalı katın başlatır. Burada kelime dağarcığını belirler:

input_text_processor.adapt(inp)

# Here are the first 10 words from the vocabulary:
input_text_processor.get_vocabulary()[:10]
['', '[UNK]', '[START]', '[END]', '.', 'que', 'de', 'el', 'a', 'no']

Bu İspanyol'dur TextVectorization kurmak ve şimdi, tabaka .adapt() İngilizce tek:

output_text_processor = tf.keras.layers.TextVectorization(
    standardize=tf_lower_and_split_punct,
    max_tokens=max_vocab_size)

output_text_processor.adapt(targ)
output_text_processor.get_vocabulary()[:10]
['', '[UNK]', '[START]', '[END]', '.', 'the', 'i', 'to', 'you', 'tom']

Artık bu katmanlar, bir dizi diziyi bir dizi simge kimliğine dönüştürebilir:

example_tokens = input_text_processor(example_input_batch)
example_tokens[:3, :10]
<tf.Tensor: shape=(3, 10), dtype=int64, numpy=
array([[   2,    9,   17,   22,    5,   48,    4,    3,    0,    0],
       [   2,   13,  177,    1,   12,    3,    0,    0,    0,    0],
       [   2,  120,   35,    6,  290,   14, 2134,  506, 2637,   14]])>

get_vocabulary yöntem metne geri belirteç kimlikleri dönüştürmek için kullanılabilir:

input_vocab = np.array(input_text_processor.get_vocabulary())
tokens = input_vocab[example_tokens[0].numpy()]
' '.join(tokens)
'[START] no se lo que quiero . [END]      '

Döndürülen belirteç kimlikleri sıfır dolguludur. Bu kolayca bir maskeye dönüştürülebilir:

plt.subplot(1, 2, 1)
plt.pcolormesh(example_tokens)
plt.title('Token IDs')

plt.subplot(1, 2, 2)
plt.pcolormesh(example_tokens != 0)
plt.title('Mask')
Text(0.5, 1.0, 'Mask')

png

Kodlayıcı/kod çözücü modeli

Aşağıdaki diyagram modele genel bir bakış gösterir. Her zaman adımında, kod çözücünün çıkışı, bir sonraki kelimeyi tahmin etmek için kodlanmış giriş üzerinden ağırlıklı bir toplamla birleştirilir. Diyagramı ve formüller vardır Luong en kağıt .

dikkat mekanizması

İçeri girmeden önce model için birkaç sabit tanımlayın:

embedding_dim = 256
units = 1024

kodlayıcı

Yukarıdaki diyagramın mavi kısmı olan kodlayıcıyı oluşturarak başlayın.

kodlayıcı:

  1. (Dan belirteç kimlikleri listesi Alır input_text_processor ).
  2. Her bir simge için bir gömme vektör yukarı bakar (bir kullanma layers.Embedding ).
  3. Yeni bir dizi halinde katıştırmalarını (a kullanarak işler layers.GRU ).
  4. İadeler:
    • İşlenmiş sıra. Bu, dikkat başlığına iletilecektir.
    • İç durum. Bu, kod çözücüyü başlatmak için kullanılacaktır.
class Encoder(tf.keras.layers.Layer):
  def __init__(self, input_vocab_size, embedding_dim, enc_units):
    super(Encoder, self).__init__()
    self.enc_units = enc_units
    self.input_vocab_size = input_vocab_size

    # The embedding layer converts tokens to vectors
    self.embedding = tf.keras.layers.Embedding(self.input_vocab_size,
                                               embedding_dim)

    # The GRU RNN layer processes those vectors sequentially.
    self.gru = tf.keras.layers.GRU(self.enc_units,
                                   # Return the sequence and state
                                   return_sequences=True,
                                   return_state=True,
                                   recurrent_initializer='glorot_uniform')

  def call(self, tokens, state=None):
    shape_checker = ShapeChecker()
    shape_checker(tokens, ('batch', 's'))

    # 2. The embedding layer looks up the embedding for each token.
    vectors = self.embedding(tokens)
    shape_checker(vectors, ('batch', 's', 'embed_dim'))

    # 3. The GRU processes the embedding sequence.
    #    output shape: (batch, s, enc_units)
    #    state shape: (batch, enc_units)
    output, state = self.gru(vectors, initial_state=state)
    shape_checker(output, ('batch', 's', 'enc_units'))
    shape_checker(state, ('batch', 'enc_units'))

    # 4. Returns the new sequence and its state.
    return output, state

Şimdiye kadar nasıl bir araya geldiği aşağıda açıklanmıştır:

# Convert the input text to tokens.
example_tokens = input_text_processor(example_input_batch)

# Encode the input sequence.
encoder = Encoder(input_text_processor.vocabulary_size(),
                  embedding_dim, units)
example_enc_output, example_enc_state = encoder(example_tokens)

print(f'Input batch, shape (batch): {example_input_batch.shape}')
print(f'Input batch tokens, shape (batch, s): {example_tokens.shape}')
print(f'Encoder output, shape (batch, s, units): {example_enc_output.shape}')
print(f'Encoder state, shape (batch, units): {example_enc_state.shape}')
Input batch, shape (batch): (64,)
Input batch tokens, shape (batch, s): (64, 14)
Encoder output, shape (batch, s, units): (64, 14, 1024)
Encoder state, shape (batch, units): (64, 1024)

Kodlayıcı, durumunun kod çözücüyü başlatmak için kullanılabilmesi için dahili durumunu döndürür.

Bir RNN'nin, bir diziyi birden çok çağrı üzerinden işleyebilmesi için durumunu döndürmesi de yaygındır. Dekoderi oluştururken bundan daha fazlasını göreceksiniz.

dikkat kafa

Kod çözücü, giriş dizisinin bölümlerine seçici olarak odaklanmak için dikkati kullanır. Dikkat, her örnek için girdi olarak bir dizi vektör alır ve her örnek için bir "dikkat" vektörü döndürür. Bu dikkat tabaka benzer layers.GlobalAveragePoling1D ama dikkat tabakası ağırlıklı ortalama gerçekleştirir.

Bunun nasıl çalıştığına bakalım:

dikkat denklemi 1

dikkat denklemi 2

Neresi:

  • \(s\) kodlayıcı endeksidir.
  • \(t\) dekoder endeksidir.
  • \(\alpha_{ts}\) dikkat ağırlık.
  • \(h_s\) kodlayıcı çıkışlarının dizisi (dikkat "anahtar" ve transformatörün terminolojisinde "değeri") katıldı ediliyor.
  • \(h_t\) dizisi (transformatör terminolojisinde dikkat "sorgusu") devam eden çözücü durumudur.
  • \(c_t\) elde edilen bağlam vektörüdür.
  • \(a_t\) "bağlam" ve "bir sorgu" birleştirme son verilir.

denklemler:

  1. Dikkat ağırlıkları, hesaplar \(\alpha_{ts}\)kodlayıcının çıkış dizisinin boyunca, SoftMax olarak,.
  2. Bağlam vektörünü kodlayıcı çıktılarının ağırlıklı toplamı olarak hesaplar.

Son bir \(score\) fonksiyonu. Görevi, her bir anahtar-sorgu çifti için bir skaler logit puanı hesaplamaktır. İki yaygın yaklaşım vardır:

dikkat denklemi 4

Bu öğretici kullanan Bahdanau en katkı dikkatini . TensorFlow her ikisinin uygulamaları içeren layers.Attention ve layers.AdditiveAttention . Kolları altındaki sınıfı bir çift ağırlık matrisleri layers.Dense tabakaları ve yerleşik uygulanmasını gerektirir.

class BahdanauAttention(tf.keras.layers.Layer):
  def __init__(self, units):
    super().__init__()
    # For Eqn. (4), the  Bahdanau attention
    self.W1 = tf.keras.layers.Dense(units, use_bias=False)
    self.W2 = tf.keras.layers.Dense(units, use_bias=False)

    self.attention = tf.keras.layers.AdditiveAttention()

  def call(self, query, value, mask):
    shape_checker = ShapeChecker()
    shape_checker(query, ('batch', 't', 'query_units'))
    shape_checker(value, ('batch', 's', 'value_units'))
    shape_checker(mask, ('batch', 's'))

    # From Eqn. (4), `W1@ht`.
    w1_query = self.W1(query)
    shape_checker(w1_query, ('batch', 't', 'attn_units'))

    # From Eqn. (4), `W2@hs`.
    w2_key = self.W2(value)
    shape_checker(w2_key, ('batch', 's', 'attn_units'))

    query_mask = tf.ones(tf.shape(query)[:-1], dtype=bool)
    value_mask = mask

    context_vector, attention_weights = self.attention(
        inputs = [w1_query, value, w2_key],
        mask=[query_mask, value_mask],
        return_attention_scores = True,
    )
    shape_checker(context_vector, ('batch', 't', 'value_units'))
    shape_checker(attention_weights, ('batch', 't', 's'))

    return context_vector, attention_weights

Dikkat katmanını test edin

Bir oluşturun BahdanauAttention tabakası:

attention_layer = BahdanauAttention(units)

Bu katman 3 girdi alır:

  • query : Bu, daha sonra, dekoder tarafından oluşturulur.
  • value : Bu kodlayıcı çıktısı olacak.
  • mask : dolgu dışlamak için example_tokens != 0
(example_tokens != 0).shape
TensorShape([64, 14])

Dikkat katmanının vektörleştirilmiş uygulaması, bir dizi sorgu vektörü dizisini ve bir dizi değer vektörü dizisini iletmenize izin verir. Sonuç:

  1. Bir dizi sonuç vektörü, sorguların boyutunu gösterir.
  2. Bir yığın dikkat boyutu ile, haritalar (query_length, value_length) .
# Later, the decoder will generate this attention query
example_attention_query = tf.random.normal(shape=[len(example_tokens), 2, 10])

# Attend to the encoded tokens

context_vector, attention_weights = attention_layer(
    query=example_attention_query,
    value=example_enc_output,
    mask=(example_tokens != 0))

print(f'Attention result shape: (batch_size, query_seq_length, units):           {context_vector.shape}')
print(f'Attention weights shape: (batch_size, query_seq_length, value_seq_length): {attention_weights.shape}')
Attention result shape: (batch_size, query_seq_length, units):           (64, 2, 1024)
Attention weights shape: (batch_size, query_seq_length, value_seq_length): (64, 2, 14)

Dikkat ağırlıkları toplamı gerekir 1.0 her dizisi için.

Burada en dizileri boyunca dikkat ağırlıklarıdır t=0 :

plt.subplot(1, 2, 1)
plt.pcolormesh(attention_weights[:, 0, :])
plt.title('Attention weights')

plt.subplot(1, 2, 2)
plt.pcolormesh(example_tokens != 0)
plt.title('Mask')
Text(0.5, 1.0, 'Mask')

png

Çünkü küçük rasgele başlatma dikkat ağırlıkları tüm yakın olan 1/(sequence_length) . Tek bir sırası için ağırlıklar yakınlaştırmak varsa, modeli genişletmek için öğrenmek ve istismar olduğunu bazı küçük farklılıklar olduğuna görebiliriz.

attention_weights.shape
TensorShape([64, 2, 14])
attention_slice = attention_weights[0, 0].numpy()
attention_slice = attention_slice[attention_slice != 0]

[<matplotlib.lines.Line2D at 0x7fb42c5b1090>]
<Figure size 432x288 with 0 Axes>

png

kod çözücü

Kod çözücünün işi, bir sonraki çıktı belirteci için tahminler oluşturmaktır.

  1. Kod çözücü, kodlayıcı çıktısının tamamını alır.
  2. Şimdiye kadar ne ürettiğini takip etmek için bir RNN kullanır.
  3. RNN çıktısını, kodlayıcının çıktısı üzerinden dikkat çekmek için sorgu olarak kullanır ve bağlam vektörünü üretir.
  4. "Dikkat vektörü" oluşturmak için Denklem 3'ü (aşağıda) kullanarak RNN ​​çıktısını ve bağlam vektörünü birleştirir.
  5. "Dikkat vektörüne" dayalı olarak bir sonraki belirteç için logit tahminleri üretir.

dikkat denklemi 3

İşte Decoder sınıfı ve başlatıcı. Başlatıcı, gerekli tüm katmanları oluşturur.

class Decoder(tf.keras.layers.Layer):
  def __init__(self, output_vocab_size, embedding_dim, dec_units):
    super(Decoder, self).__init__()
    self.dec_units = dec_units
    self.output_vocab_size = output_vocab_size
    self.embedding_dim = embedding_dim

    # For Step 1. The embedding layer convets token IDs to vectors
    self.embedding = tf.keras.layers.Embedding(self.output_vocab_size,
                                               embedding_dim)

    # For Step 2. The RNN keeps track of what's been generated so far.
    self.gru = tf.keras.layers.GRU(self.dec_units,
                                   return_sequences=True,
                                   return_state=True,
                                   recurrent_initializer='glorot_uniform')

    # For step 3. The RNN output will be the query for the attention layer.
    self.attention = BahdanauAttention(self.dec_units)

    # For step 4. Eqn. (3): converting `ct` to `at`
    self.Wc = tf.keras.layers.Dense(dec_units, activation=tf.math.tanh,
                                    use_bias=False)

    # For step 5. This fully connected layer produces the logits for each
    # output token.
    self.fc = tf.keras.layers.Dense(self.output_vocab_size)

call Bu katman için yöntem alır ve çoklu tensörleri döndürür. Bunları basit konteyner sınıfları halinde düzenleyin:

class DecoderInput(typing.NamedTuple):
  new_tokens: Any
  enc_output: Any
  mask: Any

class DecoderOutput(typing.NamedTuple):
  logits: Any
  attention_weights: Any

İşte uygulanmasıdır call yöntemiyle:

def call(self,
         inputs: DecoderInput,
         state=None) -> Tuple[DecoderOutput, tf.Tensor]:
  shape_checker = ShapeChecker()
  shape_checker(inputs.new_tokens, ('batch', 't'))
  shape_checker(inputs.enc_output, ('batch', 's', 'enc_units'))
  shape_checker(inputs.mask, ('batch', 's'))

  if state is not None:
    shape_checker(state, ('batch', 'dec_units'))

  # Step 1. Lookup the embeddings
  vectors = self.embedding(inputs.new_tokens)
  shape_checker(vectors, ('batch', 't', 'embedding_dim'))

  # Step 2. Process one step with the RNN
  rnn_output, state = self.gru(vectors, initial_state=state)

  shape_checker(rnn_output, ('batch', 't', 'dec_units'))
  shape_checker(state, ('batch', 'dec_units'))

  # Step 3. Use the RNN output as the query for the attention over the
  # encoder output.
  context_vector, attention_weights = self.attention(
      query=rnn_output, value=inputs.enc_output, mask=inputs.mask)
  shape_checker(context_vector, ('batch', 't', 'dec_units'))
  shape_checker(attention_weights, ('batch', 't', 's'))

  # Step 4. Eqn. (3): Join the context_vector and rnn_output
  #     [ct; ht] shape: (batch t, value_units + query_units)
  context_and_rnn_output = tf.concat([context_vector, rnn_output], axis=-1)

  # Step 4. Eqn. (3): `at = tanh(Wc@[ct; ht])`
  attention_vector = self.Wc(context_and_rnn_output)
  shape_checker(attention_vector, ('batch', 't', 'dec_units'))

  # Step 5. Generate logit predictions:
  logits = self.fc(attention_vector)
  shape_checker(logits, ('batch', 't', 'output_vocab_size'))

  return DecoderOutput(logits, attention_weights), state
Decoder.call = call

Kodlayıcı onun RNN tek bir çağrı ile tam giriş dizisine işler. Dekoderi bu uygulaması verimli eğitilmesi için de yapabilir. Ancak bu öğretici, kod çözücüyü birkaç nedenden dolayı bir döngüde çalıştıracaktır:

  • Esneklik: Döngüyü yazmak, eğitim prosedürü üzerinde doğrudan kontrol sahibi olmanızı sağlar.
  • Açıklık: Bu maskeleme hile yapmak ve kullanmak mümkündür layers.RNN veya tfa.seq2seq Tek bir çağrı içine bu tüm paketi için API'ler. Ancak bunu bir döngü olarak yazmak daha net olabilir.
    • Döngü ücretsiz eğitim gösterilmiştir Metin nesil tutiorial.

Şimdi bu kod çözücüyü kullanmayı deneyin.

decoder = Decoder(output_text_processor.vocabulary_size(),
                  embedding_dim, units)

Kod çözücü 4 giriş alır.

  • new_tokens - Son belirteç oluşturulur. Dekoder başlatma "[START]" belirteç.
  • enc_output - Tarafından Oluşturuldu Encoder .
  • mask - Bir boolean tensör yerini gösteren tokens != 0
  • state - önceki state dekoderden çıkışı (kod çözücünün RNN iç durumu). Geçiş None bunu sıfıra başlangıç durumuna. Orijinal kağıt, onu kodlayıcının son RNN durumundan başlatır.
# Convert the target sequence, and collect the "[START]" tokens
example_output_tokens = output_text_processor(example_target_batch)

start_index = output_text_processor.get_vocabulary().index('[START]')
first_token = tf.constant([[start_index]] * example_output_tokens.shape[0])
# Run the decoder
dec_result, dec_state = decoder(
    inputs = DecoderInput(new_tokens=first_token,
                          enc_output=example_enc_output,
                          mask=(example_tokens != 0)),
    state = example_enc_state
)

print(f'logits shape: (batch_size, t, output_vocab_size) {dec_result.logits.shape}')
print(f'state shape: (batch_size, dec_units) {dec_state.shape}')
logits shape: (batch_size, t, output_vocab_size) (64, 1, 5000)
state shape: (batch_size, dec_units) (64, 1024)

Logitlere göre bir jeton örneği alın:

sampled_token = tf.random.categorical(dec_result.logits[:, 0, :], num_samples=1)

Çıktının ilk kelimesi olarak belirteci deşifre edin:

vocab = np.array(output_text_processor.get_vocabulary())
first_word = vocab[sampled_token.numpy()]
first_word[:5]
array([['already'],
       ['plants'],
       ['pretended'],
       ['convince'],
       ['square']], dtype='<U16')

Şimdi ikinci bir logit kümesi oluşturmak için kod çözücüyü kullanın.

  • Aynı geçirin enc_output ve mask bu değişmemiştir.
  • Olarak belirteci örneklenmiş geçmek new_tokens .
  • Geçiş decoder_state RYSA Son seferinde kaldığı yerden bir bellek ile devam böylece, dekoder son kez döndü.
dec_result, dec_state = decoder(
    DecoderInput(sampled_token,
                 example_enc_output,
                 mask=(example_tokens != 0)),
    state=dec_state)
sampled_token = tf.random.categorical(dec_result.logits[:, 0, :], num_samples=1)
first_word = vocab[sampled_token.numpy()]
first_word[:5]
array([['nap'],
       ['mean'],
       ['worker'],
       ['passage'],
       ['baked']], dtype='<U16')

Eğitim

Artık tüm model bileşenlerine sahip olduğunuza göre, modeli eğitmeye başlama zamanı. İhtiyacın olacak:

  • Optimizasyonu gerçekleştirmek için bir kayıp işlevi ve optimize edici.
  • Her girdi/hedef grubu için modelin nasıl güncelleneceğini tanımlayan bir eğitim adımı işlevi.
  • Eğitimi yürütmek ve kontrol noktalarını kaydetmek için bir eğitim döngüsü.

Kayıp fonksiyonunu tanımlayın

class MaskedLoss(tf.keras.losses.Loss):
  def __init__(self):
    self.name = 'masked_loss'
    self.loss = tf.keras.losses.SparseCategoricalCrossentropy(
        from_logits=True, reduction='none')

  def __call__(self, y_true, y_pred):
    shape_checker = ShapeChecker()
    shape_checker(y_true, ('batch', 't'))
    shape_checker(y_pred, ('batch', 't', 'logits'))

    # Calculate the loss for each item in the batch.
    loss = self.loss(y_true, y_pred)
    shape_checker(loss, ('batch', 't'))

    # Mask off the losses on padding.
    mask = tf.cast(y_true != 0, tf.float32)
    shape_checker(mask, ('batch', 't'))
    loss *= mask

    # Return the total.
    return tf.reduce_sum(loss)

Eğitim adımını uygulayın

Bir örnek sınıfı ile başlayın, eğitim süreci olarak uygulanacaktır train_step bu model üzerinde yöntemle. Bkz Özelleştirme uygun detaylar için.

İşte train_step yöntemi etrafında sarıcı _train_step sonra gelecek uygulanması. Bu sarıcı açmak ve kapatmak için bir anahtar içermektedir tf.function daha kolay ayıklama yapmak, derleme.

class TrainTranslator(tf.keras.Model):
  def __init__(self, embedding_dim, units,
               input_text_processor,
               output_text_processor, 
               use_tf_function=True):
    super().__init__()
    # Build the encoder and decoder
    encoder = Encoder(input_text_processor.vocabulary_size(),
                      embedding_dim, units)
    decoder = Decoder(output_text_processor.vocabulary_size(),
                      embedding_dim, units)

    self.encoder = encoder
    self.decoder = decoder
    self.input_text_processor = input_text_processor
    self.output_text_processor = output_text_processor
    self.use_tf_function = use_tf_function
    self.shape_checker = ShapeChecker()

  def train_step(self, inputs):
    self.shape_checker = ShapeChecker()
    if self.use_tf_function:
      return self._tf_train_step(inputs)
    else:
      return self._train_step(inputs)

Genel için uygulama Model.train_step şöyle yöntemle geçerli:

  1. Bir toplu alma input_text, target_text gelen tf.data.Dataset .
  2. Bu ham metin girişlerini belirteç yerleştirmelerine ve maskelere dönüştürün.
  3. Üzerinde kodlayıcı Run input_tokens almak için encoder_output ve encoder_state .
  4. Kod çözücü durumunu ve kaybını başlatın.
  5. Üzerinde Döngü target_tokens :
    1. Kod çözücüyü her seferinde bir adım çalıştırın.
    2. Her adım için kaybı hesaplayın.
    3. Ortalama kaybı toplayın.
  6. Kaybının eğimi hesaplayın ve modelin yazılımınızı güncellemeniz optimize edici aracını trainable_variables .

_preprocess yöntem olup, aşağıdaki ilave uygular # 1 ve # 2 adım:

def _preprocess(self, input_text, target_text):
  self.shape_checker(input_text, ('batch',))
  self.shape_checker(target_text, ('batch',))

  # Convert the text to token IDs
  input_tokens = self.input_text_processor(input_text)
  target_tokens = self.output_text_processor(target_text)
  self.shape_checker(input_tokens, ('batch', 's'))
  self.shape_checker(target_tokens, ('batch', 't'))

  # Convert IDs to masks.
  input_mask = input_tokens != 0
  self.shape_checker(input_mask, ('batch', 's'))

  target_mask = target_tokens != 0
  self.shape_checker(target_mask, ('batch', 't'))

  return input_tokens, input_mask, target_tokens, target_mask
TrainTranslator._preprocess = _preprocess

_train_step altına eklenir yöntem, aslında dekoderin çalışan haricinde kalan adımları gerçekleştirir:

def _train_step(self, inputs):
  input_text, target_text = inputs  

  (input_tokens, input_mask,
   target_tokens, target_mask) = self._preprocess(input_text, target_text)

  max_target_length = tf.shape(target_tokens)[1]

  with tf.GradientTape() as tape:
    # Encode the input
    enc_output, enc_state = self.encoder(input_tokens)
    self.shape_checker(enc_output, ('batch', 's', 'enc_units'))
    self.shape_checker(enc_state, ('batch', 'enc_units'))

    # Initialize the decoder's state to the encoder's final state.
    # This only works if the encoder and decoder have the same number of
    # units.
    dec_state = enc_state
    loss = tf.constant(0.0)

    for t in tf.range(max_target_length-1):
      # Pass in two tokens from the target sequence:
      # 1. The current input to the decoder.
      # 2. The target for the decoder's next prediction.
      new_tokens = target_tokens[:, t:t+2]
      step_loss, dec_state = self._loop_step(new_tokens, input_mask,
                                             enc_output, dec_state)
      loss = loss + step_loss

    # Average the loss over all non padding tokens.
    average_loss = loss / tf.reduce_sum(tf.cast(target_mask, tf.float32))

  # Apply an optimization step
  variables = self.trainable_variables 
  gradients = tape.gradient(average_loss, variables)
  self.optimizer.apply_gradients(zip(gradients, variables))

  # Return a dict mapping metric names to current value
  return {'batch_loss': average_loss}
TrainTranslator._train_step = _train_step

_loop_step yöntem olup, aşağıdaki ilave kod çözücü yürütür ve artan kaybı ve yeni kod çözücü durumunun (hesaplar dec_state ).

def _loop_step(self, new_tokens, input_mask, enc_output, dec_state):
  input_token, target_token = new_tokens[:, 0:1], new_tokens[:, 1:2]

  # Run the decoder one step.
  decoder_input = DecoderInput(new_tokens=input_token,
                               enc_output=enc_output,
                               mask=input_mask)

  dec_result, dec_state = self.decoder(decoder_input, state=dec_state)
  self.shape_checker(dec_result.logits, ('batch', 't1', 'logits'))
  self.shape_checker(dec_result.attention_weights, ('batch', 't1', 's'))
  self.shape_checker(dec_state, ('batch', 'dec_units'))

  # `self.loss` returns the total for non-padded tokens
  y = target_token
  y_pred = dec_result.logits
  step_loss = self.loss(y, y_pred)

  return step_loss, dec_state
TrainTranslator._loop_step = _loop_step

Eğitim adımını test edin

Bir İnşa TrainTranslator ve kullanma eğitimi için yapılandırmak Model.compile yöntemi:

translator = TrainTranslator(
    embedding_dim, units,
    input_text_processor=input_text_processor,
    output_text_processor=output_text_processor,
    use_tf_function=False)

# Configure the loss and optimizer
translator.compile(
    optimizer=tf.optimizers.Adam(),
    loss=MaskedLoss(),
)

Test edin train_step . Bunun gibi bir metin modeli için kayıp şurada başlamalıdır:

np.log(output_text_processor.vocabulary_size())
8.517193191416236
%%time
for n in range(10):
  print(translator.train_step([example_input_batch, example_target_batch]))
print()
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=7.5849695>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=7.55271>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=7.4929113>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=7.3296022>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=6.80437>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=5.000246>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=5.8740363>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.794589>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.3175836>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.108163>}

CPU times: user 5.49 s, sys: 0 ns, total: 5.49 s
Wall time: 5.45 s

Bir olmaksızın Hata ayıklamak kolay olsa tf.function bir performans artışı sağlar yapar. Yani şimdi o _train_step yöntemi çalıştığını, denemek tf.function -wrapped _tf_train_step eğitimi sırasında performansını maksimize etmek,:

@tf.function(input_signature=[[tf.TensorSpec(dtype=tf.string, shape=[None]),
                               tf.TensorSpec(dtype=tf.string, shape=[None])]])
def _tf_train_step(self, inputs):
  return self._train_step(inputs)
TrainTranslator._tf_train_step = _tf_train_step
translator.use_tf_function = True

İlk çağrı, işlevi izlediği için yavaş olacaktır.

translator.train_step([example_input_batch, example_target_batch])
2021-12-04 12:09:48.074769: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] function_optimizer failed: INVALID_ARGUMENT: Input 6 of node gradient_tape/while/while_grad/body/_531/gradient_tape/while/gradients/while/decoder_1/gru_3/PartitionedCall_grad/PartitionedCall was passed variant from gradient_tape/while/while_grad/body/_531/gradient_tape/while/gradients/while/decoder_1/gru_3/PartitionedCall_grad/TensorListPopBack_2:1 incompatible with expected float.
2021-12-04 12:09:48.180156: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] layout failed: OUT_OF_RANGE: src_output = 25, but num_outputs is only 25
2021-12-04 12:09:48.285846: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] tfg_optimizer{} failed: INVALID_ARGUMENT: Input 6 of node gradient_tape/while/while_grad/body/_531/gradient_tape/while/gradients/while/decoder_1/gru_3/PartitionedCall_grad/PartitionedCall was passed variant from gradient_tape/while/while_grad/body/_531/gradient_tape/while/gradients/while/decoder_1/gru_3/PartitionedCall_grad/TensorListPopBack_2:1 incompatible with expected float.
    when importing GraphDef to MLIR module in GrapplerHook
2021-12-04 12:09:48.307794: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] function_optimizer failed: INVALID_ARGUMENT: Input 6 of node gradient_tape/while/while_grad/body/_531/gradient_tape/while/gradients/while/decoder_1/gru_3/PartitionedCall_grad/PartitionedCall was passed variant from gradient_tape/while/while_grad/body/_531/gradient_tape/while/gradients/while/decoder_1/gru_3/PartitionedCall_grad/TensorListPopBack_2:1 incompatible with expected float.
2021-12-04 12:09:48.425447: W tensorflow/core/common_runtime/process_function_library_runtime.cc:866] Ignoring multi-device function optimization failure: INVALID_ARGUMENT: Input 1 of node while/body/_1/while/TensorListPushBack_56 was passed float from while/body/_1/while/decoder_1/gru_3/PartitionedCall:6 incompatible with expected variant.
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.045638>}

Ama bundan sonra daha hızlı istekli daha genellikle 2-3x var train_step yöntemiyle:

%%time
for n in range(10):
  print(translator.train_step([example_input_batch, example_target_batch]))
print()
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.1098256>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.169871>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.139249>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=4.0410743>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=3.9664454>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=3.895707>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=3.8154407>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=3.7583396>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=3.6986444>}
{'batch_loss': <tf.Tensor: shape=(), dtype=float32, numpy=3.640298>}

CPU times: user 4.4 s, sys: 960 ms, total: 5.36 s
Wall time: 1.67 s

Yeni bir modelin iyi bir testi, tek bir girdi kümesine fazla sığabileceğini görmektir. Deneyin, kayıp hızla sıfıra gitmeli:

losses = []
for n in range(100):
  print('.', end='')
  logs = translator.train_step([example_input_batch, example_target_batch])
  losses.append(logs['batch_loss'].numpy())

print()
plt.plot(losses)
....................................................................................................
[<matplotlib.lines.Line2D at 0x7fb427edf210>]

png

Artık eğitim adımının işe yaradığından emin olduğunuza göre, sıfırdan eğitmek için modelin yeni bir kopyasını oluşturun:

train_translator = TrainTranslator(
    embedding_dim, units,
    input_text_processor=input_text_processor,
    output_text_processor=output_text_processor)

# Configure the loss and optimizer
train_translator.compile(
    optimizer=tf.optimizers.Adam(),
    loss=MaskedLoss(),
)

Modeli eğit

Kendi özel eğitim döngü yazma uygulamakla şeyin yolunda varken Model.train_step önceki bölümde olduğu gibi yöntem, çalıştırmak için izin verir Model.fit ve önlemek hepsi o kazan plakalı FEC.

Bu eğitimde çağ bir çift için sadece trenler, bu yüzden bir kullanmak callbacks.Callback planlamaktan, toplu kayıplar tarihini toplamak:

class BatchLogs(tf.keras.callbacks.Callback):
  def __init__(self, key):
    self.key = key
    self.logs = []

  def on_train_batch_end(self, n, logs):
    self.logs.append(logs[self.key])

batch_loss = BatchLogs('batch_loss')
train_translator.fit(dataset, epochs=3,
                     callbacks=[batch_loss])
Epoch 1/3
2021-12-04 12:10:11.617839: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] function_optimizer failed: INVALID_ARGUMENT: Input 6 of node StatefulPartitionedCall/gradient_tape/while/while_grad/body/_589/gradient_tape/while/gradients/while/decoder_2/gru_5/PartitionedCall_grad/PartitionedCall was passed variant from StatefulPartitionedCall/gradient_tape/while/while_grad/body/_589/gradient_tape/while/gradients/while/decoder_2/gru_5/PartitionedCall_grad/TensorListPopBack_2:1 incompatible with expected float.
2021-12-04 12:10:11.737105: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] layout failed: OUT_OF_RANGE: src_output = 25, but num_outputs is only 25
2021-12-04 12:10:11.855054: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] tfg_optimizer{} failed: INVALID_ARGUMENT: Input 6 of node StatefulPartitionedCall/gradient_tape/while/while_grad/body/_589/gradient_tape/while/gradients/while/decoder_2/gru_5/PartitionedCall_grad/PartitionedCall was passed variant from StatefulPartitionedCall/gradient_tape/while/while_grad/body/_589/gradient_tape/while/gradients/while/decoder_2/gru_5/PartitionedCall_grad/TensorListPopBack_2:1 incompatible with expected float.
    when importing GraphDef to MLIR module in GrapplerHook
2021-12-04 12:10:11.878896: E tensorflow/core/grappler/optimizers/meta_optimizer.cc:812] function_optimizer failed: INVALID_ARGUMENT: Input 6 of node StatefulPartitionedCall/gradient_tape/while/while_grad/body/_589/gradient_tape/while/gradients/while/decoder_2/gru_5/PartitionedCall_grad/PartitionedCall was passed variant from StatefulPartitionedCall/gradient_tape/while/while_grad/body/_589/gradient_tape/while/gradients/while/decoder_2/gru_5/PartitionedCall_grad/TensorListPopBack_2:1 incompatible with expected float.
2021-12-04 12:10:12.004755: W tensorflow/core/common_runtime/process_function_library_runtime.cc:866] Ignoring multi-device function optimization failure: INVALID_ARGUMENT: Input 1 of node StatefulPartitionedCall/while/body/_59/while/TensorListPushBack_56 was passed float from StatefulPartitionedCall/while/body/_59/while/decoder_2/gru_5/PartitionedCall:6 incompatible with expected variant.
1859/1859 [==============================] - 349s 185ms/step - batch_loss: 2.0443
Epoch 2/3
1859/1859 [==============================] - 350s 188ms/step - batch_loss: 1.0382
Epoch 3/3
1859/1859 [==============================] - 343s 184ms/step - batch_loss: 0.8085
<keras.callbacks.History at 0x7fb42c3eda10>
plt.plot(batch_loss.logs)
plt.ylim([0, 3])
plt.xlabel('Batch #')
plt.ylabel('CE/token')
Text(0, 0.5, 'CE/token')

png

Arsadaki görünür sıçramalar, dönem sınırlarındadır.

Çevirmek

Şimdi modeli eğitimli olduğunu, tam yürütmek için bir işlevi uygulamak text => text çeviri.

Bu modeli ihtiyaç ters çevirmek için için text => token IDs haritalama tarafından sağlanan output_text_processor . Ayrıca özel jetonların kimliklerini de bilmesi gerekir. Bunların hepsi yeni sınıfın yapıcısında uygulanır. Gerçek çeviri yönteminin uygulanması bunu takip edecektir.

Genel olarak bu, her zaman adımında kod çözücüye yapılan girdinin kod çözücünün son tahmininden bir örnek olması dışında eğitim döngüsüne benzer.

class Translator(tf.Module):

  def __init__(self, encoder, decoder, input_text_processor,
               output_text_processor):
    self.encoder = encoder
    self.decoder = decoder
    self.input_text_processor = input_text_processor
    self.output_text_processor = output_text_processor

    self.output_token_string_from_index = (
        tf.keras.layers.StringLookup(
            vocabulary=output_text_processor.get_vocabulary(),
            mask_token='',
            invert=True))

    # The output should never generate padding, unknown, or start.
    index_from_string = tf.keras.layers.StringLookup(
        vocabulary=output_text_processor.get_vocabulary(), mask_token='')
    token_mask_ids = index_from_string(['', '[UNK]', '[START]']).numpy()

    token_mask = np.zeros([index_from_string.vocabulary_size()], dtype=np.bool)
    token_mask[np.array(token_mask_ids)] = True
    self.token_mask = token_mask

    self.start_token = index_from_string(tf.constant('[START]'))
    self.end_token = index_from_string(tf.constant('[END]'))
translator = Translator(
    encoder=train_translator.encoder,
    decoder=train_translator.decoder,
    input_text_processor=input_text_processor,
    output_text_processor=output_text_processor,
)
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:21: DeprecationWarning: `np.bool` is a deprecated alias for the builtin `bool`. To silence this warning, use `bool` by itself. Doing this will not modify any behavior and is safe. If you specifically wanted the numpy scalar type, use `np.bool_` here.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations

Belirteç kimliklerini metne dönüştürün

Uygulanması için ilk yöntem olup tokens_to_text , insanlar tarafından okunabilir bir metin belirteç ID'lerinden olan dönüştürür.

def tokens_to_text(self, result_tokens):
  shape_checker = ShapeChecker()
  shape_checker(result_tokens, ('batch', 't'))
  result_text_tokens = self.output_token_string_from_index(result_tokens)
  shape_checker(result_text_tokens, ('batch', 't'))

  result_text = tf.strings.reduce_join(result_text_tokens,
                                       axis=1, separator=' ')
  shape_checker(result_text, ('batch'))

  result_text = tf.strings.strip(result_text)
  shape_checker(result_text, ('batch',))
  return result_text
Translator.tokens_to_text = tokens_to_text

Bazı rastgele belirteç kimlikleri girin ve ne ürettiğini görün:

example_output_tokens = tf.random.uniform(
    shape=[5, 2], minval=0, dtype=tf.int64,
    maxval=output_text_processor.vocabulary_size())
translator.tokens_to_text(example_output_tokens).numpy()
array([b'vain mysteries', b'funny ham', b'drivers responding',
       b'mysterious ignoring', b'fashion votes'], dtype=object)

Kod çözücünün tahminlerinden örnek

Bu işlev, kod çözücünün logit çıktılarını alır ve bu dağıtımdan belirteç kimliklerini örnekler:

def sample(self, logits, temperature):
  shape_checker = ShapeChecker()
  # 't' is usually 1 here.
  shape_checker(logits, ('batch', 't', 'vocab'))
  shape_checker(self.token_mask, ('vocab',))

  token_mask = self.token_mask[tf.newaxis, tf.newaxis, :]
  shape_checker(token_mask, ('batch', 't', 'vocab'), broadcast=True)

  # Set the logits for all masked tokens to -inf, so they are never chosen.
  logits = tf.where(self.token_mask, -np.inf, logits)

  if temperature == 0.0:
    new_tokens = tf.argmax(logits, axis=-1)
  else: 
    logits = tf.squeeze(logits, axis=1)
    new_tokens = tf.random.categorical(logits/temperature,
                                        num_samples=1)

  shape_checker(new_tokens, ('batch', 't'))

  return new_tokens
Translator.sample = sample

Bu işlevi bazı rastgele girişlerde test edin:

example_logits = tf.random.normal([5, 1, output_text_processor.vocabulary_size()])
example_output_tokens = translator.sample(example_logits, temperature=1.0)
example_output_tokens
<tf.Tensor: shape=(5, 1), dtype=int64, numpy=
array([[4506],
       [3577],
       [2961],
       [4586],
       [ 944]])>

Çeviri döngüsünü uygulayın

İşte metinden metne çeviri döngüsünün eksiksiz bir uygulaması.

Bu uygulama kullanmadan önce, piton listelerine sonuçları toplayan tf.concat tensörlerin ile birleştirmektir.

Bu uygulama statik olarak ortaya grafiği unrolls max_length tekrarlamalar. Python'da istekli yürütme ile bu sorun değil.

def translate_unrolled(self,
                       input_text, *,
                       max_length=50,
                       return_attention=True,
                       temperature=1.0):
  batch_size = tf.shape(input_text)[0]
  input_tokens = self.input_text_processor(input_text)
  enc_output, enc_state = self.encoder(input_tokens)

  dec_state = enc_state
  new_tokens = tf.fill([batch_size, 1], self.start_token)

  result_tokens = []
  attention = []
  done = tf.zeros([batch_size, 1], dtype=tf.bool)

  for _ in range(max_length):
    dec_input = DecoderInput(new_tokens=new_tokens,
                             enc_output=enc_output,
                             mask=(input_tokens!=0))

    dec_result, dec_state = self.decoder(dec_input, state=dec_state)

    attention.append(dec_result.attention_weights)

    new_tokens = self.sample(dec_result.logits, temperature)

    # If a sequence produces an `end_token`, set it `done`
    done = done | (new_tokens == self.end_token)
    # Once a sequence is done it only produces 0-padding.
    new_tokens = tf.where(done, tf.constant(0, dtype=tf.int64), new_tokens)

    # Collect the generated tokens
    result_tokens.append(new_tokens)

    if tf.executing_eagerly() and tf.reduce_all(done):
      break

  # Convert the list of generates token ids to a list of strings.
  result_tokens = tf.concat(result_tokens, axis=-1)
  result_text = self.tokens_to_text(result_tokens)

  if return_attention:
    attention_stack = tf.concat(attention, axis=1)
    return {'text': result_text, 'attention': attention_stack}
  else:
    return {'text': result_text}
Translator.translate = translate_unrolled

Basit bir girişte çalıştırın:

%%time
input_text = tf.constant([
    'hace mucho frio aqui.', # "It's really cold here."
    'Esta es mi vida.', # "This is my life.""
])

result = translator.translate(
    input_text = input_text)

print(result['text'][0].numpy().decode())
print(result['text'][1].numpy().decode())
print()
its a long cold here .
this is my life .

CPU times: user 165 ms, sys: 4.37 ms, total: 169 ms
Wall time: 164 ms

Bu modeli vermek istiyorsanız bir de bu yöntemi kaydırmak gerekir tf.function . Bunu yapmaya çalışırsanız, bu temel uygulamanın birkaç sorunu vardır:

  1. Ortaya çıkan grafikler çok büyüktür ve oluşturulması, kaydedilmesi veya yüklenmesi birkaç saniye sürer.
  2. Her zaman çalışacak, böylece bir statik unrolled döngüden bozamam max_length yinelemeleri tüm çıkışlar yapmış olsalar bile,. Ancak o zaman bile, istekli yürütmeden marjinal olarak daha hızlıdır.
@tf.function(input_signature=[tf.TensorSpec(dtype=tf.string, shape=[None])])
def tf_translate(self, input_text):
  return self.translate(input_text)

Translator.tf_translate = tf_translate

Run tf.function bunu derlemek için bir kez:

%%time
result = translator.tf_translate(
    input_text = input_text)
CPU times: user 18.8 s, sys: 0 ns, total: 18.8 s
Wall time: 18.7 s
%%time
result = translator.tf_translate(
    input_text = input_text)

print(result['text'][0].numpy().decode())
print(result['text'][1].numpy().decode())
print()
its very cold here .
this is my life .

CPU times: user 175 ms, sys: 0 ns, total: 175 ms
Wall time: 88 ms

[İsteğe bağlı] Sembolik bir döngü kullanın

Translator.translate = translate_symbolic

İlk uygulama, çıktıları toplamak için python listelerini kullandı. Bu kullanımlar tf.range izin döngü iterasyon olarak tf.autograph döngü dönüştürmek. Bu uygulamada en büyük değişiklik kullanılmasıdır tf.TensorArray yerine piton list birikir tansörlerine. tf.TensorArray grafik modunda tensörlerinin değişken sayıda toplamak için gereklidir.

İstekli yürütme ile bu uygulama orijinali ile aynı performansı gösterir:

%%time
result = translator.translate(
    input_text = input_text)

print(result['text'][0].numpy().decode())
print(result['text'][1].numpy().decode())
print()
its very cold here .
this is my life .

CPU times: user 175 ms, sys: 0 ns, total: 175 ms
Wall time: 170 ms

Bir sarın Fakat tf.function iki farklar olduğunu göreceksiniz.

@tf.function(input_signature=[tf.TensorSpec(dtype=tf.string, shape=[None])])
def tf_translate(self, input_text):
  return self.translate(input_text)

Translator.tf_translate = tf_translate

Birincisi: Grafik oluşturma çok daha hızlıdır (~ 10x), bu yaratmaz çünkü max_iterations modelinin kopyaları.

%%time
result = translator.tf_translate(
    input_text = input_text)
CPU times: user 1.79 s, sys: 0 ns, total: 1.79 s
Wall time: 1.77 s

İkincisi: Derlenmiş işlev, küçük girdilerde (bu örnekte 5x) çok daha hızlıdır, çünkü döngüden çıkabilir.

%%time
result = translator.tf_translate(
    input_text = input_text)

print(result['text'][0].numpy().decode())
print(result['text'][1].numpy().decode())
print()
its very cold here .
this is my life .

CPU times: user 40.1 ms, sys: 0 ns, total: 40.1 ms
Wall time: 17.1 ms

Süreci görselleştirin

Tarafından döndürülen dikkat ağırlıkları translate her çıktı belirteci oluştururken modeldi yöntem gösterisi "görünümlü".

Bu nedenle, girdi üzerindeki dikkatin toplamı, hepsini döndürmelidir:

a = result['attention'][0]

print(np.sum(a, axis=-1))
[1.0000001  0.99999994 1.         0.99999994 1.         0.99999994]

İşte ilk örneğin ilk çıktı adımı için dikkat dağılımı. Dikkatin artık eğitimsiz modele göre çok daha fazla odaklandığına dikkat edin:

_ = plt.bar(range(len(a[0, :])), a[0, :])

png

Giriş ve çıkış sözcükleri arasında kabaca bir hizalama olduğundan, dikkatin köşegen yakınına odaklanmasını beklersiniz:

plt.imshow(np.array(a), vmin=0.0)
<matplotlib.image.AxesImage at 0x7faf2886ced0>

png

Daha iyi bir dikkat planı yapmak için bazı kodlar:

Etiketli dikkat grafikleri

i=0
plot_attention(result['attention'][i], input_text[i], result['text'][i])
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: FixedFormatter should only be used together with FixedLocator
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: FixedFormatter should only be used together with FixedLocator
  from ipykernel import kernelapp as app

png

Birkaç cümle daha çevirin ve onları çizin:

%%time
three_input_text = tf.constant([
    # This is my life.
    'Esta es mi vida.',
    # Are they still home?
    '¿Todavía están en casa?',
    # Try to find out.'
    'Tratar de descubrir.',
])

result = translator.tf_translate(three_input_text)

for tr in result['text']:
  print(tr.numpy().decode())

print()
this is my life .
are you still at home ?
all about killed .

CPU times: user 78 ms, sys: 23 ms, total: 101 ms
Wall time: 23.1 ms
result['text']
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'this is my life .', b'are you still at home ?',
       b'all about killed .'], dtype=object)>
i = 0
plot_attention(result['attention'][i], three_input_text[i], result['text'][i])
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: FixedFormatter should only be used together with FixedLocator
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: FixedFormatter should only be used together with FixedLocator
  from ipykernel import kernelapp as app

png

i = 1
plot_attention(result['attention'][i], three_input_text[i], result['text'][i])
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: FixedFormatter should only be used together with FixedLocator
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: FixedFormatter should only be used together with FixedLocator
  from ipykernel import kernelapp as app

png

i = 2
plot_attention(result['attention'][i], three_input_text[i], result['text'][i])
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: FixedFormatter should only be used together with FixedLocator
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: FixedFormatter should only be used together with FixedLocator
  from ipykernel import kernelapp as app

png

Kısa cümleler genellikle işe yarar, ancak girdi çok uzunsa, model kelimenin tam anlamıyla odağını kaybeder ve makul tahminler sunmayı bırakır. Bunun iki ana nedeni vardır:

  1. Model, modelin tahminlerinden bağımsız olarak, her adımda doğru belirteci beslemeye zorlayan öğretmenle eğitildi. Model, bazen kendi tahminleriyle beslenseydi daha sağlam hale getirilebilirdi.
  2. Model, yalnızca RNN durumu aracılığıyla önceki çıktısına erişebilir. RNN durumu bozulursa, modelin kurtarılmasının bir yolu yoktur. Transformers kodlayıcı ve kod çözücü kendinden dikkat kullanarak bunu çöz.
long_input_text = tf.constant([inp[-1]])

import textwrap
print('Expected output:\n', '\n'.join(textwrap.wrap(targ[-1])))
Expected output:
 If you want to sound like a native speaker, you must be willing to
practice saying the same sentence over and over in the same way that
banjo players practice the same phrase over and over until they can
play it correctly and at the desired tempo.
result = translator.tf_translate(long_input_text)

i = 0
plot_attention(result['attention'][i], long_input_text[i], result['text'][i])
_ = plt.suptitle('This never works')
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:14: UserWarning: FixedFormatter should only be used together with FixedLocator
  
/tmpfs/src/tf_docs_env/lib/python3.7/site-packages/ipykernel_launcher.py:15: UserWarning: FixedFormatter should only be used together with FixedLocator
  from ipykernel import kernelapp as app

png

İhracat

Bir kez sizinle şüpheniz yoksa bir model olarak dışa isteyebilirsiniz tf.saved_model oluşturulduğu bu piton programının kullanılması dışından için.

Model bir alt sınıfıdır yana tf.Module (aracılığıyla keras.Model ) ve ihracat için tüm işlevselliği bir derlenmektedir tf.function modeli ile temiz bir şekilde dışa gerekir tf.saved_model.save :

Şimdi işlevi kullanarak ihraç edilebilir takip edildiğini saved_model.save :

tf.saved_model.save(translator, 'translator',
                    signatures={'serving_default': translator.tf_translate})
2021-12-04 12:27:54.310890: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as encoder_2_layer_call_fn, encoder_2_layer_call_and_return_conditional_losses, decoder_2_layer_call_fn, decoder_2_layer_call_and_return_conditional_losses, embedding_4_layer_call_fn while saving (showing 5 of 60). These functions will not be directly callable after loading.
INFO:tensorflow:Assets written to: translator/assets
INFO:tensorflow:Assets written to: translator/assets
reloaded = tf.saved_model.load('translator')
result = reloaded.tf_translate(three_input_text)
%%time
result = reloaded.tf_translate(three_input_text)

for tr in result['text']:
  print(tr.numpy().decode())

print()
this is my life .
are you still at home ?
find out about to find out .

CPU times: user 42.8 ms, sys: 7.69 ms, total: 50.5 ms
Wall time: 20 ms

Sonraki adımlar