Tokenizing ด้วย TF Text

ดูบน TensorFlow.org ทำงานใน Google Colab ดูบน GitHub ดาวน์โหลดโน๊ตบุ๊ค ดูรุ่น TF Hub

ภาพรวม

Tokenization เป็นกระบวนการแบ่งสตริงออกเป็นโทเค็น โดยทั่วไป โทเค็นเหล่านี้เป็นคำ ตัวเลข และ/หรือเครื่องหมายวรรคตอน tensorflow_text แพคเกจให้จำนวนของ tokenizers ใช้ได้สำหรับ preprocessing ข้อความต้องตามรูปแบบขึ้นอยู่กับข้อความของคุณ เมื่อดำเนินการแปลงเป็นโทเค็นในกราฟ TensorFlow คุณจะไม่ต้องกังวลเกี่ยวกับความแตกต่างระหว่างเวิร์กโฟลว์การฝึกอบรมและการอนุมาน และการจัดการสคริปต์ก่อนการประมวลผล

คู่มือนี้จะกล่าวถึงตัวเลือกการสร้างโทเค็นจำนวนมากที่มีให้โดย TensorFlow Text เมื่อคุณอาจต้องการใช้ตัวเลือกหนึ่งแทนอีกตัวเลือกหนึ่ง และการเรียกใช้โทเค็นเหล่านี้จากภายในโมเดลของคุณอย่างไร

ติดตั้ง

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 หรือเทนเซอร์มอมแมมที่มีโทเค็นที่กำหนดที่ประกอบเข้าด้วยกัน

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

Tokenizers

ด้านล่างนี้คือชุดของ tokenizers ที่จัดเตรียมโดย TensorFlow Text อินพุตสตริงจะถือว่าเป็น UTF-8 โปรดอ่าน คู่มือ Unicode สำหรับการแปลงสตริงให้เป็น UTF-8

tokenizers ทั้งคำ

tokenizers เหล่านี้พยายามแยกสตริงด้วยคำ และเป็นวิธีที่ใช้งานง่ายที่สุดในการแยกข้อความ

WhitespaceTokenizer

text.WhitespaceTokenizer เป็น tokenizer พื้นฐานที่สุดซึ่งแยกสายในห้องไอซียูที่กำหนดตัวอักษรช่องว่าง (เช่น. พื้นที่แท็บบรรทัดใหม่) ซึ่งมักจะดีสำหรับการสร้างแบบจำลองต้นแบบอย่างรวดเร็ว

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

คุณอาจสังเกตเห็นข้อบกพร่องของ tokenizer นี้คือ เครื่องหมายวรรคตอนจะรวมอยู่ในคำเพื่อประกอบเป็นโทเค็น ที่จะแยกคำและเครื่องหมายวรรคตอนลงในราชสกุลแยก UnicodeScriptTokenizer ควรจะใช้

UnicodeScriptTokenizer

UnicodeScriptTokenizer แยกสตริงขึ้นอยู่กับขอบเขต Unicode สคริปต์ รหัสสคริปต์ที่ใช้สอดคล้องกับค่า 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'.']]

tokenizers คำย่อย

โทเค็นไนเซอร์คำย่อยสามารถใช้กับคำศัพท์ที่มีขนาดเล็กลง และอนุญาตให้โมเดลมีข้อมูลบางอย่างเกี่ยวกับคำใหม่จากคำย่อยที่สร้างมันขึ้นมา

เราสั้นหารือเกี่ยวกับตัวเลือก tokenization Subword ด้านล่าง แต่ 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

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

SentencepieceTokenizer เป็นโทเคนย่อยโทเค็นที่สามารถกำหนดค่าได้สูง นี้ได้รับการสนับสนุนโดยห้องสมุดประโยค เช่นเดียวกับ 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 สิ่งนี้ยังมีประโยชน์สำหรับการสร้าง ngram ของอักขระ เช่น 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 จะคล้ายกัน แต่มันแทนยอมรับคู่ค่า logit จากเครือข่ายประสาทที่คาดการณ์ถ้าตัวละครแต่ละตัวควรจะแยกออกเป็นสตริงใหม่หรือรวมเข้าเป็นหนึ่งในปัจจุบัน

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

Detokenization

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 Data

TF Data เป็น API ที่ทรงพลังสำหรับการสร้างไปป์ไลน์อินพุตสำหรับโมเดลการฝึกอบรม Tokenizers ทำงานตามที่คาดไว้กับ 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!']]