واجهات برمجة تطبيقات SavedModel الشائعة للمهام النصية

توضح هذه الصفحة كيف ينبغي لـ TF2 SavedModels للمهام المتعلقة بالنص أن تقوم بتنفيذ Reusable SavedModel API . (يستبدل هذا ويوسع التوقيعات العامة للنص لتنسيق TF1 Hub المهمل الآن.)

ملخص

هناك العديد من واجهات برمجة التطبيقات (APIs) لحساب عمليات تضمين النص (المعروفة أيضًا بالتمثيلات الكثيفة للنص، أو نواقل ميزات النص).

  • يتم تنفيذ واجهة برمجة التطبيقات (API) لتضمين النص من مدخلات النص بواسطة SavedModel الذي يقوم بتعيين مجموعة من السلاسل لمجموعة من ناقلات التضمين. هذا سهل الاستخدام للغاية، وقد قامت العديد من الطرز الموجودة على TF Hub بتنفيذه. ومع ذلك، فإن هذا لا يسمح بضبط النموذج على TPU.

  • تحل واجهة برمجة التطبيقات (API) الخاصة بتضمين النص مع المدخلات المعالجة مسبقًا نفس المهمة، ولكن يتم تنفيذها بواسطة نموذجين منفصلين من SavedModels:

    • معالج مسبق يمكن تشغيله داخل خط أنابيب إدخال tf.data ويقوم بتحويل السلاسل والبيانات الأخرى ذات الطول المتغير إلى Tensors رقمية،
    • جهاز تشفير يقبل نتائج المعالج المسبق وينفذ الجزء القابل للتدريب من حساب التضمين.

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

  • تعمل واجهة برمجة التطبيقات الخاصة بتضمين النص باستخدام برامج تشفير المحولات على توسيع واجهة برمجة التطبيقات الخاصة بتضمين النص من المدخلات المعالجة مسبقًا إلى الحالة الخاصة لـ BERT وأجهزة تشفير المحولات الأخرى.

    • يتم توسيع المعالج المسبق لإنشاء مدخلات التشفير من أكثر من مقطع واحد من نص الإدخال.
    • يكشف برنامج ترميز المحول عن التضمينات المدركة للسياق للرموز الفردية.

في كل حالة، تكون مدخلات النص عبارة عن سلاسل مشفرة بترميز UTF-8، عادةً ما تكون نصًا عاديًا، ما لم تنص وثائق النموذج على خلاف ذلك.

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

تضمين النص من مدخلات النص

يقبل SavedModel لتضمين النص من مدخلات النص مجموعة من المدخلات في سلسلة موتر الشكل [batch_size] ويعينها إلى موتر float32 للشكل [batch_size, dim] مع تمثيلات كثيفة (ناقلات المعالم) للمدخلات.

ملخص الاستخدام

obj = hub.load("path/to/model")
text_input = ["A long sentence.",
              "single-word",
              "http://example.com"]
embeddings = obj(text_input)

تذكر من Reusable SavedModel API أن تشغيل النموذج في وضع التدريب (على سبيل المثال، للتسرب) قد يتطلب وسيطة كلمة رئيسية obj(..., training=True) ، وأن obj يوفر السمات .variables و .trainable_variables و .regularization_losses حسب الاقتضاء. .

في كيراس، كل هذا يتم الاعتناء به

embeddings = hub.KerasLayer("path/to/model", trainable=...)(text_input)

التدريب الموزع

إذا تم استخدام تضمين النص كجزء من نموذج يتم تدريبه باستخدام استراتيجية التوزيع، فسيتم استدعاء hub.load("path/to/model") أو hub.KerasLayer("path/to/model", ...) ، على التوالي، يجب أن يحدث داخل نطاق DistributionStrategy من أجل إنشاء متغيرات النموذج بالطريقة الموزعة. على سبيل المثال

  with strategy.scope():
    ...
    model = hub.load("path/to/model")
    ...

أمثلة

تضمين النص مع المدخلات المعالجة مسبقًا

يتم تنفيذ تضمين النص بمدخلات تمت معالجتها مسبقًا بواسطة نموذجين منفصلين من SavedModels:

  • معالج أولي يقوم بتعيين موتر سلسلة من الشكل [batch_size] إلى أمر من Tensor رقمي،
  • جهاز تشفير يقبل إملاء Tensors كما تم إرجاعه بواسطة المعالج المسبق، وينفذ الجزء القابل للتدريب من حساب التضمين، ويعيد إملاء المخرجات. الإخراج ضمن المفتاح "default" هو موتر float32 ذو الشكل [batch_size, dim] .

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

إنها تفاصيل التنفيذ التي تتضمن Tensors في مخرجات المعالج المسبق، والتي توجد Tensors إضافية (إن وجدت) إلى جانب "default" في مخرجات المشفر.

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

ملخص الاستخدام

text_input = tf.constant(["A long sentence.",
                          "single-word",
                          "http://example.com"])
preprocessor = hub.load("path/to/preprocessor")  # Must match `encoder`.
encoder_inputs = preprocessor(text_input)

encoder = hub.load("path/to/encoder")
encoder_outputs = encoder(encoder_inputs)
embeddings = encoder_outputs["default"]

تذكر من Reusable SavedModel API أن تشغيل برنامج التشفير في وضع التدريب (على سبيل المثال، للتسرب) قد يتطلب وسيطة كلمة رئيسية encoder(..., training=True) ، وأن encoder يوفر السمات .variables و .trainable_variables و .regularization_losses حسب الاقتضاء. .

قد يحتوي نموذج preprocessor على .variables ولكن ليس من المفترض أن يتم تدريبه بشكل أكبر. لا تعتمد المعالجة المسبقة على الوضع: إذا كان لدى preprocessor() وسيطة training=... على الإطلاق، فلن يكون له أي تأثير.

في كيراس، كل هذا يتم الاعتناء به

encoder_inputs = hub.KerasLayer("path/to/preprocessor")(text_input)
encoder_outputs = hub.KerasLayer("path/to/encoder", trainable=True)(encoder_inputs)
embeddings = encoder_outputs["default"]

التدريب الموزع

إذا تم استخدام برنامج التشفير كجزء من نموذج تم تدريبه باستخدام استراتيجية التوزيع، فسيتم استدعاء hub.load("path/to/encoder") أو hub.KerasLayer("path/to/encoder", ...) ، على التوالي، يجب أن يحدث في الداخل

  with strategy.scope():
    ...

من أجل إعادة إنشاء متغيرات التشفير بالطريقة الموزعة.

وبالمثل، إذا كان المعالج المسبق جزءًا من النموذج المُدرب (كما في المثال البسيط أعلاه)، فيجب أيضًا تحميله ضمن نطاق استراتيجية التوزيع. ومع ذلك، إذا تم استخدام المعالج المسبق في مسار إدخال (على سبيل المثال، في ملف قابل للاستدعاء تم تمريره إلى tf.data.Dataset.map() )، فيجب أن يحدث تحميله خارج نطاق استراتيجية التوزيع، من أجل وضع متغيراته (إن وجدت). ) على وحدة المعالجة المركزية المضيفة.

أمثلة

تضمين النص باستخدام محولات الترميز

تعمل مشفرات المحولات للنص على مجموعة من تسلسلات الإدخال، كل تسلسل يشتمل على مقاطع n ≥ 1 من النص المميز، ضمن بعض الارتباطات الخاصة بالنموذج على n . بالنسبة لـ BERT والعديد من امتداداته، هذا الحد هو 2، لذلك يقبلون المقاطع الفردية وأزواج المقاطع.

تعمل واجهة برمجة التطبيقات الخاصة بتضمين النص باستخدام برامج ترميز المحولات على توسيع واجهة برمجة التطبيقات الخاصة بتضمين النص باستخدام المدخلات المعالجة مسبقًا لهذا الإعداد.

المعالج المسبق

يقوم المعالج المسبق SavedModel لتضمين النص باستخدام أدوات تشفير المحولات بتنفيذ واجهة برمجة التطبيقات الخاصة بالمعالج المسبق SavedModel لعمليات تضمين النص ذات المدخلات المعالجة مسبقًا (انظر أعلاه)، مما يوفر طريقة لتعيين مدخلات النص ذات المقطع الفردي مباشرة إلى مدخلات التشفير.

بالإضافة إلى ذلك، يوفر المعالج المسبق SavedModel كائنات فرعية قابلة tokenize لرموز مميزة (بشكل منفصل لكل مقطع) و bert_pack_inputs لتعبئة عدد n من المقاطع المميزة في تسلسل إدخال واحد لجهاز التشفير. يتبع كل كائن فرعي واجهة برمجة تطبيقات SavedModel القابلة لإعادة الاستخدام .

ملخص الاستخدام

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

preprocessor = hub.load("path/to/preprocessor")

# Tokenize batches of both text inputs.
text_premises = tf.constant(["The quick brown fox jumped over the lazy dog.",
                             "Good day."])
tokenized_premises = preprocessor.tokenize(text_premises)
text_hypotheses = tf.constant(["The dog was lazy.",  # Implied.
                               "Axe handle!"])       # Not implied.
tokenized_hypotheses = preprocessor.tokenize(text_hypotheses)

# Pack input sequences for the Transformer encoder.
seq_length = 128
encoder_inputs = preprocessor.bert_pack_inputs(
    [tokenized_premises, tokenized_hypotheses],
    seq_length=seq_length)  # Optional argument.

في Keras، يمكن التعبير عن هذا الحساب كـ

tokenize = hub.KerasLayer(preprocessor.tokenize)
tokenized_hypotheses = tokenize(text_hypotheses)
tokenized_premises = tokenize(text_premises)

bert_pack_inputs = hub.KerasLayer(
    preprocessor.bert_pack_inputs,
    arguments=dict(seq_length=seq_length))  # Optional argument.
encoder_inputs = bert_pack_inputs([tokenized_premises, tokenized_hypotheses])

تفاصيل tokenize

يقبل استدعاء preprocessor.tokenize() سلسلة Tensor ذات الشكل [batch_size] ويعيد RaggedTensor ذات الشكل [batch_size, ...] والتي تكون قيمها عبارة عن معرفات رمزية int32 تمثل سلاسل الإدخال. يمكن أن يكون هناك r ≥ 1 أبعاد خشنة بعد batch_size ولكن لا يوجد بعد موحد آخر.

  • إذا كانت r =1، يكون الشكل [batch_size, (tokens)] ، ويتم ترميز كل إدخال ببساطة في تسلسل مسطح من الرموز المميزة.
  • إذا كان r >1، فهناك r -1 مستويات إضافية للتجميع. على سبيل المثال، يستخدم Tensorflow_text.BertTokenizer r =2 لتجميع الرموز المميزة حسب الكلمات وينتج الشكل [batch_size, (words), (tokens_per_word)] . الأمر متروك للنموذج المتوفر لتحديد عدد هذه المستويات (المستويات) الإضافية الموجودة، إن وجدت، وما هي المجموعات التي تمثلها.

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

فيما يتعلق بواجهة برمجة التطبيقات SavedModel القابلة لإعادة الاستخدام ، قد يحتوي كائن preprocessor.tokenize على .variables ولكن ليس من المفترض أن يتم تدريبه بشكل أكبر. لا يعتمد الترميز على الوضع: إذا كان لدى preprocessor.tokenize() وسيطة training=... على الإطلاق، فلن يكون له أي تأثير.

تفاصيل bert_pack_inputs

يقبل استدعاء preprocessor.bert_pack_inputs() قائمة Python للمدخلات المميزة (المجمعة بشكل منفصل لكل مقطع إدخال) ويعيد إملاء Tensors يمثل مجموعة من تسلسلات الإدخال ذات الطول الثابت لنموذج تشفير Transformer.

كل إدخال رمزي هو int32 RaggedTensor للشكل [batch_size, ...] ، حيث يكون الرقم r للأبعاد المتعرجة بعد Batch_size إما 1 أو كما هو الحال في إخراج preprocessor.tokenize(). (هذا الأخير هو من أجل الراحة فقط؛ حيث يتم تسوية الأبعاد الإضافية قبل التعبئة.)

تضيف التعبئة رموزًا خاصة حول مقاطع الإدخال كما هو متوقع بواسطة برنامج التشفير. ينفذ استدعاء bert_pack_inputs() بالضبط نظام التعبئة الذي تستخدمه نماذج BERT الأصلية والعديد من امتداداتها: يبدأ التسلسل المعبأ برمز مميز واحد لبداية التسلسل، متبوعًا بالمقاطع المميزة، وينتهي كل منها بمقطع واحد في نهاية التسلسل رمز مميز. يتم ملء المواضع المتبقية حتى seq_length، إن وجدت، برموز الحشو.

إذا تجاوز التسلسل المعبأ seq_length، فسيقوم bert_pack_inputs() باقتطاع مقاطعه إلى بادئات ذات أحجام متساوية تقريبًا بحيث يتناسب التسلسل المعبأ تمامًا مع seq_length.

التعبئة لا تعتمد على الوضع: إذا كان لدى preprocessor.bert_pack_inputs() وسيطة training=... على الإطلاق، فلن يكون لها أي تأثير. أيضًا، من غير المتوقع أن يحتوي preprocessor.bert_pack_inputs على متغيرات، أو يدعم الضبط الدقيق.

التشفير

يتم استدعاء برنامج التشفير وفقًا لإملاء encoder_inputs بنفس الطريقة كما هو الحال في واجهة برمجة التطبيقات (API) لتضمين النص مع مدخلات تمت معالجتها مسبقًا (انظر أعلاه)، بما في ذلك الأحكام الواردة من واجهة برمجة تطبيقات SavedModel القابلة لإعادة الاستخدام .

ملخص الاستخدام

encoder = hub.load("path/to/encoder")
encoder_outputs = encoder(encoder_inputs)

أو ما يعادله في Keras:

encoder = hub.KerasLayer("path/to/encoder", trainable=True)
encoder_outputs = encoder(encoder_inputs)

تفاصيل

إن encoder_outputs عبارة عن أمر من Tensors باستخدام المفاتيح التالية.

  • "sequence_output" : موتر float32 ذو الشكل [batch_size, seq_length, dim] مع تضمين مدرك للسياق لكل رمز مميز لكل تسلسل إدخال معبأ.
  • "pooled_output" : موتر float32 ذو الشكل [batch_size, dim] مع تضمين كل تسلسل إدخال ككل، مشتق من serial_output بطريقة قابلة للتدريب.
  • "default" ، كما هو مطلوب من قبل واجهة برمجة التطبيقات (API) لتضمين النص مع المدخلات المعالجة مسبقًا: موتر float32 للشكل [batch_size, dim] مع تضمين كل تسلسل إدخال. (قد يكون هذا مجرد اسم مستعار لـpooled_output.)

محتويات encoder_inputs ليست مطلوبة بشكل صارم بواسطة تعريف واجهة برمجة التطبيقات (API) هذا. ومع ذلك، بالنسبة لأجهزة التشفير التي تستخدم مدخلات نمط BERT، يوصى باستخدام الأسماء التالية (من مجموعة أدوات نمذجة NLP الخاصة بـ TensorFlow Model Garden ) لتقليل الاحتكاك في برامج التشفير المتبادلة وإعادة استخدام نماذج المعالج المسبق:

  • "input_word_ids" : موتر int32 للشكل [batch_size, seq_length] مع معرفات الرمز المميز لتسلسل الإدخال المعبأ (أي، بما في ذلك رمز بداية التسلسل، ورموز نهاية المقطع، والحشو).
  • "input_mask" : موتر int32 للشكل [batch_size, seq_length] مع القيمة 1 في موضع جميع رموز الإدخال الموجودة قبل الحشو والقيمة 0 لرموز الحشو المميزة.
  • "input_type_ids" : موتر int32 للشكل [batch_size, seq_length] مع فهرس مقطع الإدخال الذي أدى إلى ظهور رمز الإدخال في الموضع المعني. يشتمل مقطع الإدخال الأول (الفهرس 0) على رمز بداية التسلسل ورمز نهاية المقطع الخاص به. تتضمن المقاطع الثانية والأحدث (إذا كانت موجودة) رمز نهاية المقطع الخاص بها. تحصل رموز الحشو على الفهرس 0 مرة أخرى.

التدريب الموزع

لتحميل المعالج المسبق وكائنات التشفير داخل أو خارج نطاق استراتيجية التوزيع، تنطبق نفس القواعد كما هو الحال في واجهة برمجة التطبيقات (API) لتضمين النص مع المدخلات المعالجة مسبقًا (انظر أعلاه).

أمثلة