الترميز مع نص TF

عرض على TensorFlow.org تشغيل في Google Colab عرض على جيثب تحميل دفتر انظر نماذج TF Hub

ملخص

الترميز هو عملية تقسيم سلسلة إلى رموز مميزة. عادةً ما تكون هذه الرموز عبارة عن كلمات و / أو أرقام و / أو علامات ترقيم. و tensorflow_text توفر حزمة عدد من tokenizers متاحة للتجهيزها النص المطلوب من قبل النماذج القائمة على النص. من خلال تنفيذ الترميز في الرسم البياني TensorFlow ، لن تحتاج إلى القلق بشأن الاختلافات بين تدريبات سير العمل والاستدلال وإدارة البرامج النصية للمعالجة المسبقة.

يناقش هذا الدليل العديد من خيارات الترميز التي يوفرها نص TensorFlow ، عندما قد ترغب في استخدام خيار على آخر ، وكيف يتم استدعاء هذه الرموز المميزة من داخل النموذج الخاص بك.

يثبت

pip install -q tensorflow-text
import requests
import tensorflow as tf
import tensorflow_text as tf_text

الفاصل API

الواجهات الرئيسية هي Splitter و SplitterWithOffsets التي لها أساليب واحدة split و split_with_offsets . و SplitterWithOffsets البديل (الذي يمتد Splitter يتضمن) خيار للحصول على إزاحة بايت. يسمح هذا للمتصل بمعرفة البايت في السلسلة الأصلية التي تم إنشاء الرمز المميز منها.

و Tokenizer و TokenizerWithOffsets إصدارات المتخصصة في Splitter التي توفر وسائل الراحة tokenize و tokenize_with_offsets على التوالي.

عموما، على أي مساهمة N-الأبعاد، الرموز عاد هم في N + 1 الأبعاد RaggedTensor مع البعد الداخلي الأكثر من الرموز رسم خرائط لسلاسل الفردية الأصلية.

class Splitter {
  @abstractmethod
  def split(self, input)
}

class SplitterWithOffsets(Splitter) {
  @abstractmethod
  def split_with_offsets(self, input)
}

وهناك أيضا Detokenizer اجهة. يمكن لأي رمز مميز ينفذ هذه الواجهة أن يقبل موترًا ممزقًا بأبعاد N من الرموز المميزة ، ويعيد عادةً موترًا N-1-dimensional أو موترًا ممزقًا يحتوي على الرموز المميزة المجمعة معًا.

class Detokenizer {
  @abstractmethod
  def detokenize(self, input)
}

الرموز

يوجد أدناه مجموعة الرموز المميزة التي يوفرها TensorFlow Text. يُفترض أن تكون مدخلات السلسلة UTF-8. يرجى مراجعة دليل يونيكود لتحويل السلاسل إلى UTF-8.

tokenizers كلمة كاملة

تحاول أدوات الرموز المميزة تقسيم سلسلة بكلمات ، وهي الطريقة الأكثر سهولة لتقسيم النص.

المسافة البيضاء

و text.WhitespaceTokenizer هو tokenizer أبسط الذي يقسم سلاسل على ICU تعريف الأحرف بيضاء (على سبيل المثال الفضاء، التبويب، خط جديد). هذا غالبًا ما يكون جيدًا لبناء نماذج أولية بسرعة.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
WARNING:tensorflow:From /tmpfs/src/tf_docs_env/lib/python3.7/site-packages/tensorflow/python/util/dispatch.py:206: batch_gather (from tensorflow.python.ops.array_ops) is deprecated and will be removed after 2017-10-25.
Instructions for updating:
`tf.batch_gather` is deprecated, please use `tf.gather` with `batch_dims=-1` instead.
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

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

UnicodeScriptTokenizer

و UnicodeScriptTokenizer يقسم سلاسل على أساس حدود النصي يونيكود. تتوافق أكواد البرنامج النصي المستخدمة مع قيم UScriptCode للمكونات الدولية لـ Unicode (ICU). انظر: http://icu-project.org/apiref/icu4c/uscript_8h.html

في الواقع، هذا هو مماثل ل WhitespaceTokenizer مع معظم واضح الفرق بينهما أن ذلك سيؤدي إلى انقسام علامات الترقيم (USCRIPT_COMMON) من نصوص اللغة (على سبيل المثال. USCRIPT_LATIN، USCRIPT_CYRILLIC، الخ)، في حين يفصل أيضا نصوص اللغة من بعضها البعض. لاحظ أن هذا سيؤدي أيضًا إلى تقسيم كلمات الانكماش إلى رموز منفصلة.

tokenizer = tf_text.UnicodeScriptTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b'can', b"'", b't', b'explain', b',', b'but', b'you', b'feel', b'it', b'.']]

رموز الكلمات الفرعية

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

نناقش بإيجاز خيارات Subword tokenization أدناه، ولكن البرنامج التعليمي Subword Tokenization يذهب أكثر في عمق ويشرح كيفية إنشاء ملفات فوكب أيضا.

WordpieceTokenizer

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

يتوقع برنامج WordpieceTokenizer أن يكون الإدخال مقسمًا بالفعل إلى رموز مميزة. وبسبب هذا شرط أساسي، وكنت كثيرا ما تريد تقسيم باستخدام WhitespaceTokenizer أو UnicodeScriptTokenizer مسبقا.

tokenizer = tf_text.WhitespaceTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

بعد تقسيم السلسلة إلى الرموز، و WordpieceTokenizer يمكن استخدامها لانقسام في subtokens.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_wp_en_vocab.txt?raw=true"
r = requests.get(url)
filepath = "vocab.txt"
open(filepath, 'wb').write(r.content)
52382
subtokenizer = tf_text.UnicodeScriptTokenizer(filepath)
subtokens = tokenizer.tokenize(tokens)
print(subtokens.to_list())
[[[b'What'], [b'you'], [b'know'], [b'you'], [b"can't"], [b'explain,'], [b'but'], [b'you'], [b'feel'], [b'it.']]]

بيرتوكينيزر

يعكس BertTokenizer التنفيذ الأصلي للرمز المميز من ورقة BERT. يتم دعم هذا بواسطة WordpieceTokenizer ، ولكنه يؤدي أيضًا مهام إضافية مثل التطبيع والترميز للكلمات أولاً.

tokenizer = tf_text.BertTokenizer(filepath, token_out_type=tf.string, lower_case=True)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[[b'what'], [b'you'], [b'know'], [b'you'], [b'can'], [b"'"], [b't'], [b'explain'], [b','], [b'but'], [b'you'], [b'feel'], [b'it'], [b'.']]]

الجمله

SentencepieceTokenizer عبارة عن رمز مميز للرمز الفرعي قابل للتكوين بدرجة عالية. هذا مدعوم من مكتبة Sentencepiece. مثل BertTokenizer ، يمكن أن يشمل التسوية وتقسيم الرمز قبل الانقسام إلى رموز فرعية.

url = "https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/test_data/test_oss_model.model?raw=true"
sp_model = requests.get(url).content
tokenizer = tf_text.SentencepieceTokenizer(sp_model, out_type=tf.string)
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[b'\xe2\x96\x81What', b'\xe2\x96\x81you', b'\xe2\x96\x81know', b'\xe2\x96\x81you', b'\xe2\x96\x81can', b"'", b't', b'\xe2\x96\x81explain', b',', b'\xe2\x96\x81but', b'\xe2\x96\x81you', b'\xe2\x96\x81feel', b'\xe2\x96\x81it', b'.']]

مقسمات أخرى

UnicodeCharTokenizer

يؤدي هذا إلى تقسيم السلسلة إلى أحرف UTF-8. إنه مفيد للغات CJK التي لا تحتوي على مسافات بين الكلمات.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]

الإخراج هو Unicode codepoints. يمكن أن يكون هذا مفيدًا أيضًا في إنشاء أحرف ngrams ، مثل bigrams. لإعادة التحويل إلى أحرف UTF-8.

characters = tf.strings.unicode_encode(tf.expand_dims(tokens, -1), "UTF-8")
bigrams = tf_text.ngrams(characters, 2, reduction_type=tf_text.Reduction.STRING_JOIN, string_separator='')
print(bigrams.to_list())
[[b'Wh', b'ha', b'at', b't ', b' y', b'yo', b'ou', b'u ', b' k', b'kn', b'no', b'ow', b'w ', b' y', b'yo', b'ou', b'u ', b' c', b'ca', b'an', b"n'", b"'t", b't ', b' e', b'ex', b'xp', b'pl', b'la', b'ai', b'in', b'n,', b', ', b' b', b'bu', b'ut', b't ', b' y', b'yo', b'ou', b'u ', b' f', b'fe', b'ee', b'el', b'l ', b' i', b'it', b't.']]

HubModuleTokenizer

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

MODEL_HANDLE = "https://tfhub.dev/google/zh_segmentation/1"
segmenter = tf_text.HubModuleTokenizer(MODEL_HANDLE)
tokens = segmenter.tokenize(["新华社北京"])
print(tokens.to_list())
[[b'\xe6\x96\xb0\xe5\x8d\x8e\xe7\xa4\xbe', b'\xe5\x8c\x97\xe4\xba\xac']]

قد يكون من الصعب عرض نتائج سلاسل البايت المشفرة UTF-8. قم بفك تشفير قيم القائمة لتسهيل المشاهدة.

def decode_list(x):
  if type(x) is list:
    return list(map(decode_list, x))
  return x.decode("UTF-8")

def decode_utf8_tensor(x):
  return list(map(decode_list, x.to_list()))

print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

SplitMergeTokenizer

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

ل SplitMergeTokenizer ، يتم استخدام قيمة 0 للإشارة إلى بدء سلسلة جديدة، وقيمة 1 يشير الحرف هو جزء من سلسلة الحالية.

strings = ["新华社北京"]
labels = [[0, 1, 1, 0, 1]]
tokenizer = tf_text.SplitMergeTokenizer()
tokens = tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

و SplitMergeFromLogitsTokenizer مماثل، ولكن بدلا من ذلك يقبل أزواج قيمة اللوغاريتمي من الشبكة العصبية التي تتنبأ ما إذا كان يجب تقسيم كل حرف في سلسلة جديدة أو دمجها في واحدة الحالي.

strings = [["新华社北京"]]
labels = [[[5.0, -3.2], [0.2, 12.0], [0.0, 11.0], [2.2, -1.0], [-3.0, 3.0]]]
tokenizer = tf_text.SplitMergeFromLogitsTokenizer()
tokenizer.tokenize(strings, labels)
print(decode_utf8_tensor(tokens))
[['新华社', '北京']]

RegexSplitter

و RegexSplitter غير قادرة على سلاسل شريحة في نقاط التوقف التعسفية التي يحددها تعبير عادي المقدمة.

splitter = tf_text.RegexSplitter("\s")
tokens = splitter.split(["What you know you can't explain, but you feel it."], )
print(tokens.to_list())
[[b'What', b'you', b'know', b'you', b"can't", b'explain,', b'but', b'you', b'feel', b'it.']]

إزاحة

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

tokenizer = tf_text.UnicodeScriptTokenizer()
(tokens, start_offsets, end_offsets) = tokenizer.tokenize_with_offsets(['Everything not saved will be lost.'])
print(tokens.to_list())
print(start_offsets.to_list())
print(end_offsets.to_list())
[[b'Everything', b'not', b'saved', b'will', b'be', b'lost', b'.']]
[[0, 11, 15, 21, 26, 29, 33]]
[[10, 14, 20, 25, 28, 33, 34]]

تفكيك

Tokenizers التي تنفذ Detokenizer توفير detokenize الطريقة التي محاولات الجمع بين السلاسل. هذا لديه فرصة أن يكون ضياعًا ، لذلك قد لا تتطابق السلسلة التي تم فك ارتباطها دائمًا تمامًا مع السلسلة الأصلية التي تم ترميزها مسبقًا.

tokenizer = tf_text.UnicodeCharTokenizer()
tokens = tokenizer.tokenize(["What you know you can't explain, but you feel it."])
print(tokens.to_list())
strings = tokenizer.detokenize(tokens)
print(strings.numpy())
[[87, 104, 97, 116, 32, 121, 111, 117, 32, 107, 110, 111, 119, 32, 121, 111, 117, 32, 99, 97, 110, 39, 116, 32, 101, 120, 112, 108, 97, 105, 110, 44, 32, 98, 117, 116, 32, 121, 111, 117, 32, 102, 101, 101, 108, 32, 105, 116, 46]]
[b"What you know you can't explain, but you feel it."]

بيانات TF

TF Data هي واجهة برمجة تطبيقات قوية لإنشاء خط أنابيب إدخال لنماذج التدريب. تعمل الرموز المميزة كما هو متوقع مع API.

docs = tf.data.Dataset.from_tensor_slices([['Never tell me the odds.'], ["It's a trap!"]])
tokenizer = tf_text.WhitespaceTokenizer()
tokenized_docs = docs.map(lambda x: tokenizer.tokenize(x))
iterator = iter(tokenized_docs)
print(next(iterator).to_list())
print(next(iterator).to_list())
[[b'Never', b'tell', b'me', b'the', b'odds.']]
[[b"It's", b'a', b'trap!']]