الترجمة الآلية العصبية باهتمام

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثبتحميل دفتر

هذا الكمبيوتر الدفتري يدرب تسلسل إلى تسلسل (seq2seq) نموذجا للترجمة من الإسبانية إلى الإنجليزية على أساس الفعالة النهج لالقائم على الاهتمام العصبية الترجمة الآلية . هذا مثال متقدم يفترض بعض المعرفة بما يلي:

  • تسلسل لتسلسل النماذج
  • أساسيات TensorFlow أسفل طبقة keras:

في حين عفا عليها الزمن هذا الهيكل إلى حد ما فإنه لا يزال مشروعا مفيدا جدا للعمل من خلال الحصول على فهم أعمق لآليات الانتباه (قبل ان يتوجه الى محولات ).

بعد تدريب نموذجية في هذا المحمول، وسوف تكون قادرة على إدخال الجملة الاسبانية، مثل، والعودة الترجمة الإنجليزية "¿todavia estan أون كاسا؟": "أنت لا تزال في المنزل"

النموذج الناتج هو أنه قابل للتصدير و tf.saved_model ، لذلك يمكن استخدامها في بيئات TensorFlow أخرى.

تعتبر جودة الترجمة معقولة بالنسبة لمثال لعبة ، ولكن ربما تكون حبكة الانتباه المتولدة أكثر إثارة للاهتمام. يوضح هذا أجزاء جملة الإدخال التي تحظى باهتمام النموذج أثناء الترجمة:

مؤامرة الاهتمام الإسبانية الإنجليزية

يثبت

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

يبني هذا البرنامج التعليمي بضع طبقات من البداية ، استخدم هذا المتغير إذا كنت تريد التبديل بين التطبيقات المخصصة والمضمنة.

use_builtins = True

يستخدم هذا البرنامج التعليمي الكثير من واجهات برمجة التطبيقات ذات المستوى المنخفض حيث يسهل الحصول على أشكال خاطئة. يستخدم هذا الفصل للتحقق من الأشكال في جميع أنحاء البرنامج التعليمي.

مدقق الشكل

البيانات

سنستخدم مجموعة بيانات اللغة التي تقدمها http://www.manythings.org/anki/ هذه بيانات يحتوي على أزواج الترجمة في شكل:

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

لديهم مجموعة متنوعة من اللغات المتاحة ، لكننا سنستخدم مجموعة البيانات الإنجليزية-الإسبانية.

قم بتنزيل وإعداد مجموعة البيانات

للراحة ، استضفنا نسخة من مجموعة البيانات هذه على Google Cloud ، ولكن يمكنك أيضًا تنزيل نسختك الخاصة. بعد تنزيل مجموعة البيانات ، إليك الخطوات التي سنتخذها لإعداد البيانات:

  1. إضافة بداية ونهاية رمزية إلى كل جملة.
  2. نظف الجمل بإزالة الأحرف الخاصة.
  3. قم بإنشاء فهرس للكلمات وعكس فهرس الكلمات (تعيين القواميس من كلمة → معرف ومعرف → كلمة).
  4. وسادة كل جملة لأقصى طول.
# 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.

قم بإنشاء مجموعة بيانات tf.data

من هذه صفائف السلاسل يمكنك إنشاء tf.data.Dataset من السلاسل التي المراوغات ودفعات لهم بكفاءة:

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)

معالجة النص

واحد من أهداف هذا البرنامج التعليمي هو بناء النموذج الذي يمكن أن تصدر في شكل tf.saved_model . لجعل هذا النموذج تصدير مفيدة ينبغي أن تأخذ tf.string المدخلات، والعودة tf.string المخرجات: جميع تجهيز النصوص يحدث داخل هذا النموذج.

التوحيد

يتعامل النموذج مع نص متعدد اللغات بمفردات محدودة. لذلك سيكون من المهم توحيد نص الإدخال.

الخطوة الأولى هي تسوية Unicode لتقسيم الأحرف المحركة واستبدال أحرف التوافق بمكافئاتها من ASCII.

و tensorflow_text تحتوي حزمة عملية يونيكود تطبيع:

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 الخطوة الأولى في وظيفة توحيد النص:

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]

تحويل النص

سوف تكون ملفوفة هذه الوظيفة التوحيد حتى في tf.keras.layers.TextVectorization طبقة التي سوف تتعامل مع استخراج المفردات وتحويل إدخال النص إلى تسلسل الرموز.

max_vocab_size = 5000

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

و TextVectorization طبقة والعديد من طبقات تجهيزها أخرى لها adapt طريقة. هذه الطريقة يقرأ حقبة واحدة من بيانات التدريب، ويعمل الكثير مثل Model.fix . هذا adapt طريقة تهيئة طبقة استنادا إلى البيانات. هنا تحدد المفردات:

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']

هذا هو الاسبانية TextVectorization طبقة، الآن بناء و .adapt() الإنجليزية احد:

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']

الآن يمكن لهذه الطبقات تحويل مجموعة من السلاسل إلى مجموعة من معرفات الرمز المميز:

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 يمكن استخدامها طريقة لتحويل معرفات رمزية إلى نص:

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]      '

معرفات الرمز المميز التي تم إرجاعها غير مبطن. يمكن تحويل هذا بسهولة إلى قناع:

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')

بي إن جي

نموذج جهاز التشفير / وحدة فك التشفير

يوضح الرسم البياني التالي نظرة عامة على النموذج. في كل خطوة زمنية يتم دمج خرج مفكك الشفرة مع مجموع مرجح على المدخلات المشفرة ، للتنبؤ بالكلمة التالية. الرسم البياني والصيغ هم من الورق لونغ ل .

آلية الانتباه

قبل الدخول فيه ، حدد بعض الثوابت للنموذج:

embedding_dim = 256
units = 1024

المشفر

ابدأ ببناء المشفر ، الجزء الأزرق من الرسم البياني أعلاه.

المشفر:

  1. يأخذ قائمة معرفات رمزية (من input_text_processor ).
  2. يبدو حتى متجه تضمين لكل رمز (باستخدام layers.Embedding ).
  3. يعالج التضمينات في سلسلة جديدة (باستخدام layers.GRU ).
  4. عائدات:
    • التسلسل المعالج. سيتم تمرير هذا إلى رأس الانتباه.
    • الدولة الداخلية. سيتم استخدام هذا لتهيئة وحدة فك الترميز
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

إليك كيف تتناسب معًا حتى الآن:

# 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)

يعيد المشفر حالته الداخلية بحيث يمكن استخدام حالته لتهيئة وحدة فك التشفير.

من الشائع أيضًا أن تقوم RNN بإرجاع حالتها بحيث يمكنها معالجة تسلسل عبر مكالمات متعددة. سترى المزيد من بناء وحدة فك التشفير.

رأس الانتباه

يستخدم مفكك التشفير الانتباه للتركيز بشكل انتقائي على أجزاء من تسلسل الإدخال. يأخذ الانتباه سلسلة من المتجهات كمدخلات لكل مثال ويعيد ناقل "انتباه" لكل مثال. هذه الطبقة اهتمام مشابهة ل layers.GlobalAveragePoling1D لكن طبقة الاهتمام ينفذ المتوسط الوزني.

لنلقِ نظرة على كيفية عمل ذلك:

معادلة الانتباه 1

معادلة الانتباه 2

أين:

  • \(s\) هو مؤشر التشفير.
  • \(t\) هو مؤشر فك.
  • \(\alpha_{ts}\) هو الأوزان الاهتمام.
  • \(h_s\) هو تسلسل النواتج التشفير يحضر إلى ( "مفتاح" الانتباه و "قيمة" في المصطلحات محول).
  • \(h_t\) هي الدولة فك يحضر إلى التسلسل (انتباه "استعلام" في المصطلحات محول).
  • \(c_t\) هو متجه السياق الناتجة عن ذلك.
  • \(a_t\) هو الناتج النهائي يجمع بين "السياق" و "الاستعلام".

المعادلات:

  1. بحساب الأوزان الاهتمام، \(\alpha_{ts}\)، كما softmax عبر تسلسل الناتج التشفير و.
  2. تحسب متجه السياق كمجموع مرجح لمخرجات وحدة التشفير.

الأخير هو \(score\) وظيفة. وتتمثل مهمتها في حساب درجة لوغاريتمية قياسية لكل زوج من طلبات البحث الرئيسية. هناك طريقتان شائعتان:

معادلة الانتباه 4

يستخدم هذا البرنامج التعليمي اهتماما Bahdanau في مضافة . يشمل TensorFlow تطبيقات على حد سواء كما layers.Attention و layers.AdditiveAttention . الطبقة تحت مقابض المصفوفات الوزن في زوج من layers.Dense طبقات، وتدعو تنفيذ المضمن.

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

اختبر طبقة الانتباه

إنشاء BahdanauAttention طبقة:

attention_layer = BahdanauAttention(units)

تأخذ هذه الطبقة 3 مداخل:

  • و query : هذا سيتم إنشاؤها بواسطة وحدة فك الترميز، في وقت لاحق.
  • و value : هذا وسوف يكون الناتج من التشفير.
  • و mask : لاستبعاد الحشو، example_tokens != 0
(example_tokens != 0).shape
TensorShape([64, 14])

يتيح لك التنفيذ المتجه لطبقة الانتباه تمرير مجموعة من متواليات متجهات الاستعلام ومجموعة من متواليات القيمة. النتيجه هي:

  1. مجموعة متتاليات من متجهات النتائج بحجم الاستعلامات.
  2. خرائط والاهتمام دفعة، مع حجم (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)

يجب أن الأوزان الاهتمام المبلغ إلى 1.0 لكل التسلسل.

وهنا الأوزان الاهتمام عبر متواليات في 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')

بي إن جي

بسبب التهيئة صغيرة عشوائية الأوزان اهتمام كلها قريبة إلى 1/(sequence_length) . إذا كنت في التكبير على أوزان للتسلسل واحد، يمكنك أن ترى أن هناك بعض الاختلافات الصغيرة أن هذا النموذج يمكن أن تتعلم لتوسيع، واستغلال.

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>

بي إن جي

جهاز فك التشفير

تتمثل مهمة وحدة فك التشفير في إنشاء تنبؤات لرمز الإخراج التالي.

  1. يتلقى مفكك التشفير إخراج جهاز التشفير بالكامل.
  2. يستخدم RNN لتتبع ما تم إنشاؤه حتى الآن.
  3. يستخدم ناتج RNN الخاص به كاستعلام للفت الانتباه على إخراج المشفر ، مما ينتج عنه متجه السياق.
  4. فهو يجمع بين ناتج RNN ومتجه السياق باستخدام المعادلة 3 (أدناه) لتوليد "متجه الانتباه".
  5. يقوم بإنشاء تنبؤات تسجيل الدخول للرمز المميز التالي بناءً على "متجه الانتباه".

معادلة الانتباه 3

هنا هو Decoder الطبقة ومهيئ لها. يقوم المُهيئ بإنشاء جميع الطبقات الضرورية.

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 طريقة لهذه الطبقة يأخذ وإرجاع التنسورات متعددة. قم بتنظيمها في فئات حاوية بسيطة:

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

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

هنا هو تنفيذ call الأسلوب:

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

التشفير عمليات تسلسل لها مدخلات الكامل مع مكالمة واحدة إلى RNN لها. هذا تنفيذ فك تستطيع أن تفعل ذلك أيضا للتدريب الفعال. لكن هذا البرنامج التعليمي سيعمل على تشغيل وحدة فك التشفير في حلقة لعدة أسباب:

  • المرونة: تمنحك كتابة الحلقة تحكمًا مباشرًا في إجراء التدريب.
  • وضوح: من الممكن أن تفعل الحيل اخفاء واستخدام layers.RNN ، أو tfa.seq2seq واجهات برمجة التطبيقات لحزم كل هذا في مكالمة واحدة. لكن كتابتها في شكل حلقة قد يكون أوضح.
    • ويتجلى حلقة تدريبية مجانية في الجيل نص tutiorial.

حاول الآن استخدام وحدة فك الترميز هذه.

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

يأخذ جهاز فك التشفير 4 مدخلات.

  • new_tokens - ورمز الماضي ولدت. تهيئة وحدة فك الترميز مع "[START]" رمز.
  • enc_output - ولدت بعد و Encoder .
  • mask - A موتر منطقية تشير إلى حيث tokens != 0
  • state - والسابق state الناتج من وحدة فك الترميز (الحالة الداخلية RNN وحدة فك الترميز ل). تمرير None لصفر تهيئة عليه. يقوم الورق الأصلي بتهيئته من حالة RNN النهائية لجهاز التشفير.
# 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)

قم بتجربة رمز وفقًا للسجلات:

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

فك شفرة الرمز باعتباره الكلمة الأولى في الإخراج:

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')

استخدم الآن وحدة فك التشفير لإنشاء مجموعة ثانية من السجلات.

  • تمرير نفس enc_output و mask ، وهذه لم تتغير.
  • تمرير عينات رمزية كما new_tokens .
  • تمرير decoder_state فك عاد آخر مرة، لذلك تواصل RNN مع ذاكرة من النقطة التي توقفت عندها في المرة السابقة.
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')

تمرين

الآن بعد أن أصبح لديك جميع مكونات النموذج ، حان الوقت لبدء تدريب النموذج. انك سوف تحتاج:

  • وظيفة الخسارة والمحسن لأداء التحسين.
  • وظيفة خطوة تدريب تحدد كيفية تحديث النموذج لكل دفعة إدخال / هدف.
  • حلقة تدريب لقيادة التدريب وحفظ نقاط التفتيش.

تحديد وظيفة الخسارة

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)

نفذ خطوة التدريب

تبدأ مع فئة النموذج، وسيتم تنفيذ عملية التدريب كما train_step الطريقة على هذا النموذج. انظر تخصيص تناسب لمزيد من التفاصيل.

هنا train_step الأسلوب هو التفاف حول _train_step التنفيذ والتي سوف تأتي لاحقا. ويشمل هذا المجمع التحول إلى تشغيل وإيقاف tf.function تجميع، لجعل التصحيح أسهل.

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)

عموما لتنفيذ Model.train_step الطريقة على النحو التالي:

  1. الحصول على دفعة من input_text, target_text من tf.data.Dataset .
  2. قم بتحويل مدخلات النص الخام تلك إلى رموز وأقنعة.
  3. تشغيل التشفير على input_tokens للحصول على encoder_output و encoder_state .
  4. تهيئة حالة وحدة فك التشفير والخسارة.
  5. حلقة على target_tokens :
    1. قم بتشغيل وحدة فك الترميز خطوة واحدة في كل مرة.
    2. احسب الخسارة لكل خطوة.
    3. تراكم متوسط ​​الخسارة.
  6. حساب الانحدار من الخسارة واستخدام محسن لتطبيق التحديثات على النموذج trainable_variables .

و _preprocess الطريقة، وأضاف أدناه، تنفذ خطوات رقم 1 ورقم 2:

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 الطريقة، وأضاف أدناه، ويعالج الخطوات المتبقية باستثناء بالفعل بتشغيل وحدة فك الترميز:

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 الطريقة، وأضاف أدناه، تنفيذ وحدة فك الترميز ويحسب الخسارة الإضافية ودولة فك الجديدة ( 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

اختبر خطوة التدريب

بناء TrainTranslator ، وتكوينه لتدريب باستخدام Model.compile الأسلوب:

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(),
)

اختبار ل train_step . بالنسبة لنموذج نصي مثل هذا ، يجب أن تبدأ الخسارة بالقرب من:

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

في حين أنه من الأسهل لتصحيح دون tf.function يفعل إعطاء دفعة الأداء. حتى الآن أن _train_step طريقة يعمل، حاول tf.function الملفوفة _tf_train_step ، لتعظيم الأداء أثناء التدريب:

@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

ستكون المكالمة الأولى بطيئة ، لأنها تتعقب الوظيفة.

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>}

ولكن بعد ذلك انها عادة ما تكون 2-3x أسرع من حريصة train_step الأسلوب:

%%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

الاختبار الجيد للنموذج الجديد هو معرفة أنه يمكن أن يتسع لمجموعة واحدة من المدخلات. جربها ، يجب أن تذهب الخسارة بسرعة إلى الصفر:

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>]

بي إن جي

الآن بعد أن أصبحت واثقًا من أن خطوة التدريب تعمل ، قم بإنشاء نسخة جديدة من النموذج للتدريب من البداية:

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(),
)

تدريب النموذج

بينما لا يوجد شيء خاطئ مع الكتابة الخاصة حلقة التدريب المخصصة، وتنفيذ Model.train_step الطريقة، كما هو الحال في الجزء السابق، ويسمح لك لتشغيل Model.fit وتجنب إعادة كتابة كل هذا الرمز المرجل لوحة.

هذا البرنامج التعليمي القطارات فقط لبضع العهود، لذلك استخدام callbacks.Callback لجمع تاريخ خسائر دفعة، بتهمة التآمر:

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')

بي إن جي

القفزات المرئية في الحبكة هي في حدود العصر.

ترجمة

والآن بعد أن يتم تدريب نموذج، وتنفيذ وظيفة لتنفيذ كامل text => text الترجمة.

لهذا احتياجات نموذج لقلب text => token IDs الخرائط التي تقدمها output_text_processor . يحتاج أيضًا إلى معرفة معرفات الرموز المميزة الخاصة. يتم تنفيذ كل هذا في المنشئ للفئة الجديدة. سيتبع تنفيذ طريقة الترجمة الفعلية.

بشكل عام ، هذا مشابه لحلقة التدريب ، باستثناء أن المدخلات إلى مفكك الشفرة في كل خطوة زمنية هي عينة من آخر تنبؤ لجهاز فك التشفير.

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

تحويل معرفات الرمز المميز إلى نص

الطريقة الأولى لتنفيذ هو tokens_to_text التي المتحولين من معرفات رمزية إلى نص مقروء الإنسان.

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

أدخل بعض معرفات الرموز المميزة العشوائية واطلع على ما يولده:

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)

عينة من توقعات وحدة فك التشفير

تأخذ هذه الوظيفة مخرجات اللوغاريتم الخاصة بوحدة فك التشفير وعينات من معرفات الرمز المميز من هذا التوزيع:

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

اختبر تشغيل هذه الوظيفة على بعض المدخلات العشوائية:

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]])>

نفِّذ حلقة الترجمة

فيما يلي تنفيذ كامل لحلقة ترجمة النص إلى نص.

هذا التطبيق بجمع النتائج في قوائم الثعبان، وقبل استخدام tf.concat للانضمام اليهم في التنسورات.

هذا التطبيق unrolls ثابت الرسم البياني إلى max_length التكرار. هذا جيد مع الإعدام الشغوف في بيثون.

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

قم بتشغيله بإدخال بسيط:

%%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

إذا كنت ترغب في تصدير هذا النموذج سوف تحتاج إلى التفاف هذه الطريقة في tf.function . يحتوي هذا التنفيذ الأساسي على بعض المشكلات إذا حاولت القيام بذلك:

  1. الرسوم البيانية الناتجة كبيرة جدًا وتستغرق بضع ثوانٍ في الإنشاء أو الحفظ أو التحميل.
  2. لا يمكنك كسر من حلقة بسطه بشكل ثابت، لذلك سيتم تشغيله دائما max_length التكرار، حتى لو تتم جميع النواتج. ولكن حتى مع ذلك ، يكون أسرع بشكل هامشي من التنفيذ المتلهف.
@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

تشغيل tf.function مرة واحدة لترجمة ذلك:

%%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

[اختياري] استخدم حلقة رمزية

Translator.translate = translate_symbolic

استخدم التطبيق الأولي قوائم بايثون لتجميع المخرجات. هذه الاستخدامات tf.range باسم مكرر حلقة، مما يسمح tf.autograph لتحويل الحلقة. أكبر تغيير في هذا التطبيق هو استخدام tf.TensorArray بدلا من الثعبان list لالتنسورات تتراكم. tf.TensorArray مطلوب لجمع عدد متغير من التنسورات في وضع الرسم البياني.

مع التنفيذ الحثيث ، يعمل هذا التنفيذ على قدم المساواة مع الأصل:

%%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

ولكن عند الانتهاء من ذلك في tf.function ستلاحظ اثنين من الخلافات.

@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

أولا: إنشاء الرسم البياني هو أسرع بكثير (~ 10X)، لأنه لا يخلق max_iterations نسخ من النموذج.

%%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

ثانيًا: الوظيفة المترجمة تكون أسرع بكثير في المدخلات الصغيرة (5x في هذا المثال) ، لأنها يمكن أن تنفصل عن الحلقة.

%%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

تصور العملية

الأوزان الاهتمام إرجاعها بواسطة translate عرض طريقة حيث كان النموذج "يبحث" عندما ولدت كل رمز الانتاج.

لذلك يجب أن يعيد مجموع الانتباه على المدخلات جميع الآحاد:

a = result['attention'][0]

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

فيما يلي توزيع الانتباه لخطوة الإخراج الأولى للمثال الأول. لاحظ كيف أصبح الاهتمام الآن أكثر تركيزًا مما كان عليه بالنسبة للنموذج غير المدرب:

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

بي إن جي

نظرًا لوجود بعض المحاذاة التقريبية بين كلمات الإدخال والإخراج ، فإنك تتوقع تركيز الانتباه بالقرب من القطر:

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

بي إن جي

إليك بعض التعليمات البرمجية لإنشاء مخطط اهتمام أفضل:

مؤامرات الانتباه المسمى

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

بي إن جي

ترجم بضع جمل أخرى ورسمها:

%%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

بي إن جي

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

بي إن جي

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

بي إن جي

غالبًا ما تعمل الجمل القصيرة جيدًا ، ولكن إذا كانت المدخلات طويلة جدًا ، يفقد النموذج التركيز ويتوقف عن تقديم تنبؤات معقولة. هناك سببان رئيسيان لهذا:

  1. تم تدريب النموذج بإجبار المعلم على تغذية الرمز الصحيح في كل خطوة ، بغض النظر عن تنبؤات النموذج. يمكن جعل النموذج أكثر قوة إذا تم تغذية تنبؤاته الخاصة به في بعض الأحيان.
  2. يمكن للنموذج الوصول إلى مخرجاته السابقة فقط من خلال حالة RNN. في حالة تلف حالة RNN ، فلا توجد طريقة لاسترداد النموذج. المحولات حل هذه باستخدام الاهتمام الذاتي في وفك التشفير.
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

بي إن جي

يصدر

وبمجرد الانتهاء من نموذج كنت راضيا قد ترغب في تصديره باعتباره tf.saved_model للاستخدام خارج هذا البرنامج الثعبان الذي أنشأه.

منذ هذا النموذج هو فئة فرعية من tf.Module (من خلال keras.Model )، ويتم تصنيف جميع وظائف للتصدير في tf.function نموذج يجب تصدير نظيفة مع tf.saved_model.save :

والآن بعد أن تم إرجاع الوظيفة التي يمكن تصديرها باستخدام 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

الخطوات التالية