하위 단어 토크나이저

TensorFlow.org에서 보기 Google Colab에서 실행 GitHub에서 소스 보기 노트북 다운로드

이 튜토리얼 데이터 세트에서 subword 어휘를 생성하고 구축하는 데 사용하는 방법을 보여줍니다 text.BertTokenizer 어휘에서.

하위 단어 토크나이저의 주요 이점은 단어 기반 토큰화와 문자 기반 토큰화 사이를 보간한다는 것입니다. 일반적인 단어는 어휘에 슬롯이 있지만 토크나이저는 알려지지 않은 단어에 대해 단어 조각과 개별 문자로 대체할 수 있습니다.

개요

tensorflow_text 패키지는 많은 일반적인 tokenizers의 TensorFlow 구현이 포함되어 있습니다. 여기에는 세 가지 하위 단어 스타일 토크나이저가 포함됩니다.

  • text.BertTokenizer - BertTokenizer 클래스는 높은 수준의 인터페이스입니다. 그것은 BERT의 토큰 분할 알고리즘과 포함 WordPieceTokenizer . 그것은 입력으로 문장을 받아 토큰 ID를 반환합니다.
  • text.WordpeiceTokenizer - WordPieceTokenizer 클래스는 낮은 수준의 인터페이스입니다. 그것은 단지 구현 WordPiece 알고리즘을 . 호출하기 전에 텍스트를 표준화하고 단어로 분할해야 합니다. 그것은 입력으로 단어를 취하고 토큰 ID를 반환합니다.
  • text.SentencepieceTokenizer - SentencepieceTokenizer 더 복잡한 설정이 필요합니다. 이니셜라이저는 사전 훈련된 문장 모델이 필요합니다. 참고 항목 구글 / sentencepiece 저장소 이러한 모델 중 하나를 구축하는 방법에 대한 지침을. 토큰 화 때 입력으로 문장을 받아 들일 수 있습니다.

이 튜토리얼은 기존 단어에서 시작하여 하향식 방식으로 Wordpiece 어휘를 구축합니다. 일본어, 중국어 또는 한국어에는 명확한 다중 문자 단위가 없기 때문에 이 프로세스는 작동하지 않습니다. conside 사용하여 이러한 언어 토큰 화하려면 text.SentencepieceTokenizer , text.UnicodeCharTokenizer 또는 이 방법을 .

설정

pip install -q -U tensorflow-text
pip install -q tensorflow_datasets
import collections
import os
import pathlib
import re
import string
import sys
import tempfile
import time

import numpy as np
import matplotlib.pyplot as plt

import tensorflow_datasets as tfds
import tensorflow_text as text
import tensorflow as tf
2021-08-11 18:46:18.414452: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
tf.get_logger().setLevel('ERROR')
pwd = pathlib.Path.cwd()

데이터세트 다운로드

에서 포르투갈어 / 영어 번역 데이터 세트 가져 오기 TFDS를 :

examples, metadata = tfds.load('ted_hrlr_translate/pt_to_en', with_info=True,
                               as_supervised=True)
train_examples, val_examples = examples['train'], examples['validation']
2021-08-11 18:46:23.603290: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcuda.so.1
2021-08-11 18:46:24.267575: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.268580: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-11 18:46:24.268617: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-11 18:46:24.271953: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublas.so.11
2021-08-11 18:46:24.272053: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcublasLt.so.11
2021-08-11 18:46:24.273192: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcufft.so.10
2021-08-11 18:46:24.273527: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcurand.so.10
2021-08-11 18:46:24.274580: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusolver.so.11
2021-08-11 18:46:24.275559: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcusparse.so.11
2021-08-11 18:46:24.275779: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudnn.so.8
2021-08-11 18:46:24.275884: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.276973: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.277878: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-11 18:46:24.278525: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 AVX512F FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2021-08-11 18:46:24.279136: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.280033: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1733] Found device 0 with properties: 
pciBusID: 0000:00:05.0 name: Tesla V100-SXM2-16GB computeCapability: 7.0
coreClock: 1.53GHz coreCount: 80 deviceMemorySize: 15.78GiB deviceMemoryBandwidth: 836.37GiB/s
2021-08-11 18:46:24.280116: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.281009: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.281856: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1871] Adding visible gpu devices: 0
2021-08-11 18:46:24.281894: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0
2021-08-11 18:46:24.912068: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1258] Device interconnect StreamExecutor with strength 1 edge matrix:
2021-08-11 18:46:24.912106: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1264]      0 
2021-08-11 18:46:24.912115: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1277] 0:   N 
2021-08-11 18:46:24.912363: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.913392: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.914368: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:937] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2021-08-11 18:46:24.915336: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1418] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 14646 MB memory) -> physical GPU (device: 0, name: Tesla V100-SXM2-16GB, pci bus id: 0000:00:05.0, compute capability: 7.0)

이 데이터세트는 포르투갈어/영어 문장 쌍을 생성합니다.

for pt, en in train_examples.take(1):
  print("Portuguese: ", pt.numpy().decode('utf-8'))
  print("English:   ", en.numpy().decode('utf-8'))
2021-08-11 18:46:25.028577: I tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:176] None of the MLIR Optimization Passes are enabled (registered 2)
2021-08-11 18:46:25.029197: I tensorflow/core/platform/profile_utils/cpu_utils.cc:114] CPU Frequency: 2000165000 Hz
Portuguese:  e quando melhoramos a procura , tiramos a única vantagem da impressão , que é a serendipidade .
English:    and when you improve searchability , you actually take away the one advantage of print , which is serendipity .

위의 예시 문장에 대한 몇 가지 사항에 유의하세요.

  • 그들은 소문자입니다.
  • 구두점 주위에 공백이 있습니다.
  • 유니코드 정규화가 사용 중인지 여부는 명확하지 않습니다.
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)

어휘 생성

이 섹션에서는 데이터 세트에서 단어 단어 단어를 생성합니다. 당신은 이미 어휘 파일이 단지 구축하는 방법을 보려면 text.BertTokenizer 또는 text.Wordpiece 그 다음 당신이 앞서으로 건너 뛸 수와 토크 나이를 빌드 토크 나이의 섹션을 참조하십시오.

어휘 생성 코드가 포함되어 tensorflow_text PIP 패키지. 기본적으로 가져오지 않으며 수동으로 가져와야 합니다.

from tensorflow_text.tools.wordpiece_vocab import bert_vocab_from_dataset as bert_vocab

bert_vocab.bert_vocab_from_dataset 기능은 어휘를 생성합니다.

동작을 조정하기 위해 설정할 수 있는 인수가 많이 있습니다. 이 자습서에서는 대부분 기본값을 사용합니다. 더 많은 옵션에 대한 배우고 싶다면, 먼저 읽어 알고리즘 , 다음 번 봐 가지고 코드를 .

약 2분이 소요됩니다.

bert_tokenizer_params=dict(lower_case=True)
reserved_tokens=["[PAD]", "[UNK]", "[START]", "[END]"]

bert_vocab_args = dict(
    # The target vocabulary size
    vocab_size = 8000,
    # Reserved tokens that must be included in the vocabulary
    reserved_tokens=reserved_tokens,
    # Arguments for `text.BertTokenizer`
    bert_tokenizer_params=bert_tokenizer_params,
    # Arguments for `wordpiece_vocab.wordpiece_tokenizer_learner_lib.learn`
    learn_params={},
)
%%time
pt_vocab = bert_vocab.bert_vocab_from_dataset(
    train_pt.batch(1000).prefetch(2),
    **bert_vocab_args
)
CPU times: user 1min 36s, sys: 2.48 s, total: 1min 39s
Wall time: 1min 33s

다음은 결과 어휘의 일부입니다.

print(pt_vocab[:10])
print(pt_vocab[100:110])
print(pt_vocab[1000:1010])
print(pt_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"]
['no', 'por', 'mais', 'na', 'eu', 'esta', 'muito', 'isso', 'isto', 'sao']
['90', 'desse', 'efeito', 'malaria', 'normalmente', 'palestra', 'recentemente', '##nca', 'bons', 'chave']
['##–', '##—', '##‘', '##’', '##“', '##”', '##⁄', '##€', '##♪', '##♫']

어휘 파일 작성:

def write_vocab_file(filepath, vocab):
  with open(filepath, 'w') as f:
    for token in vocab:
      print(token, file=f)
write_vocab_file('pt_vocab.txt', pt_vocab)

해당 기능을 사용하여 영어 데이터에서 어휘를 생성합니다.

%%time
en_vocab = bert_vocab.bert_vocab_from_dataset(
    train_en.batch(1000).prefetch(2),
    **bert_vocab_args
)
CPU times: user 1min 8s, sys: 1.98 s, total: 1min 10s
Wall time: 1min 5s
print(en_vocab[:10])
print(en_vocab[100:110])
print(en_vocab[1000:1010])
print(en_vocab[-10:])
['[PAD]', '[UNK]', '[START]', '[END]', '!', '#', '$', '%', '&', "'"]
['as', 'all', 'at', 'one', 'people', 're', 'like', 'if', 'our', 'from']
['choose', 'consider', 'extraordinary', 'focus', 'generation', 'killed', 'patterns', 'putting', 'scientific', 'wait']
['##_', '##`', '##ย', '##ร', '##อ', '##–', '##—', '##’', '##♪', '##♫']

다음은 두 개의 어휘 파일입니다.

write_vocab_file('en_vocab.txt', en_vocab)
ls *.txt
en_vocab.txt  pt_vocab.txt

토크나이저 빌드

text.BertTokenizer (섹션 참조 첫 번째 인수로 어휘 파일의 경로를 전달하여 초기화 할 수 있습니다 tf.lookup 다른 옵션을) :

pt_tokenizer = text.BertTokenizer('pt_vocab.txt', **bert_tokenizer_params)
en_tokenizer = text.BertTokenizer('en_vocab.txt', **bert_tokenizer_params)

이제 일부 텍스트를 인코딩하는 데 사용할 수 있습니다. 영어 데이터에서 3개의 예를 일괄적으로 가져옵니다.

for pt_examples, en_examples in train_examples.batch(3).take(1):
  for ex in en_examples:
    print(ex.numpy())
b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .'
b'but what if it were active ?'
b"but they did n't test for curiosity ."

관통을 실행 BertTokenizer.tokenize 방법. 처음에는, 이것은 반환 tf.RaggedTensor 축이 (batch, word, word-piece) :

# Tokenize the examples -> (batch, word, word-piece)
token_batch = en_tokenizer.tokenize(en_examples)
# Merge the word and word-piece axes -> (batch, tokens)
token_batch = token_batch.merge_dims(-2,-1)

for ex in token_batch.to_list():
  print(ex)
[72, 117, 79, 1259, 1491, 2362, 13, 79, 150, 184, 311, 71, 103, 2308, 74, 2679, 13, 148, 80, 55, 4840, 1434, 2423, 540, 15]
[87, 90, 107, 76, 129, 1852, 30]
[87, 83, 149, 50, 9, 56, 664, 85, 2512, 15]

당신이 그들의 텍스트 표현으로 토큰 ID를 교체하는 경우 (사용 tf.gather ) 당신은 단어 첫 번째 예제에서 볼 수있는 "searchability""serendipity" 로 분해 된 "search ##ability""s ##ere ##nd ##ip ##ity" :

# Lookup each token id in the vocabulary.
txt_tokens = tf.gather(en_vocab, token_batch)
# Join with spaces.
tf.strings.reduce_join(txt_tokens, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'and when you improve search ##ability , you actually take away the one advantage of print , which is s ##ere ##nd ##ip ##ity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)>

추출 된 토큰에서 단어를 다시 조립하기 위해 사용 BertTokenizer.detokenize 방법 :

words = en_tokenizer.detokenize(token_batch)
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)>

사용자 정의 및 내보내기

이 튜토리얼에서 사용하는 텍스트 토크 나이와 detokenizer 구축 변압기 튜토리얼. 이 섹션은 사용 방법과 그 자습서를 단순화하는 처리 단계 및 보냅니다 tokenizers을 추가 tf.saved_model 그들이 다른 튜토리얼에서 가져올 수 있도록.

사용자 정의 토큰화

다운 스트림 튜토리얼 모두 토큰 화 된 텍스트를 포함 할 것으로 예상 [START][END] 토큰.

reserved_tokens 어휘의 시작 부분에 공간을 예약, 그래서 [START][END] 두 언어에 대해 동일한 인덱스가 :

START = tf.argmax(tf.constant(reserved_tokens) == "[START]")
END = tf.argmax(tf.constant(reserved_tokens) == "[END]")

def add_start_end(ragged):
  count = ragged.bounding_shape()[0]
  starts = tf.fill([count,1], START)
  ends = tf.fill([count,1], END)
  return tf.concat([starts, ragged, ends], axis=1)
words = en_tokenizer.detokenize(add_start_end(token_batch))
tf.strings.reduce_join(words, separator=' ', axis=-1)
<tf.Tensor: shape=(3,), dtype=string, numpy=
array([b'[START] and when you improve searchability , you actually take away the one advantage of print , which is serendipity . [END]',
       b'[START] but what if it were active ? [END]',
       b"[START] but they did n ' t test for curiosity . [END]"],
      dtype=object)>

사용자 정의 해독

토크나이저를 내보내기 전에 다운스트림 자습서를 위해 정리할 수 있는 몇 가지 사항이 있습니다.

  1. 그들은 너무 깨끗하고 텍스트 출력을 생성과 같은 예약 된 토큰을 삭제하려면 [START] , [END][PAD] .
  2. 그들은 그렇게 함께 참여하는 문자열 적용, 완전한 문자열에 관심이 words 결과의 축을.
def cleanup_text(reserved_tokens, token_txt):
  # Drop the reserved tokens, except for "[UNK]".
  bad_tokens = [re.escape(tok) for tok in reserved_tokens if tok != "[UNK]"]
  bad_token_re = "|".join(bad_tokens)

  bad_cells = tf.strings.regex_full_match(token_txt, bad_token_re)
  result = tf.ragged.boolean_mask(token_txt, ~bad_cells)

  # Join them into strings.
  result = tf.strings.reduce_join(result, separator=' ', axis=-1)

  return result
en_examples.numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n't test for curiosity ."], dtype=object)
token_batch = en_tokenizer.tokenize(en_examples).merge_dims(-2,-1)
words = en_tokenizer.detokenize(token_batch)
words
<tf.RaggedTensor [[b'and', b'when', b'you', b'improve', b'searchability', b',', b'you', b'actually', b'take', b'away', b'the', b'one', b'advantage', b'of', b'print', b',', b'which', b'is', b'serendipity', b'.'], [b'but', b'what', b'if', b'it', b'were', b'active', b'?'], [b'but', b'they', b'did', b'n', b"'", b't', b'test', b'for', b'curiosity', b'.']]>
cleanup_text(reserved_tokens, words).numpy()
array([b'and when you improve searchability , you actually take away the one advantage of print , which is serendipity .',
       b'but what if it were active ?',
       b"but they did n ' t test for curiosity ."], dtype=object)

내 보내다

다음 코드 블록은 빌드 CustomTokenizer 포함하는 클래스 text.BertTokenizer 경우, 사용자 정의 로직 및 @tf.function 수출에 필요한 래퍼를.

class CustomTokenizer(tf.Module):
  def __init__(self, reserved_tokens, vocab_path):
    self.tokenizer = text.BertTokenizer(vocab_path, lower_case=True)
    self._reserved_tokens = reserved_tokens
    self._vocab_path = tf.saved_model.Asset(vocab_path)

    vocab = pathlib.Path(vocab_path).read_text().splitlines()
    self.vocab = tf.Variable(vocab)

    ## Create the signatures for export:   

    # Include a tokenize signature for a batch of strings. 
    self.tokenize.get_concrete_function(
        tf.TensorSpec(shape=[None], dtype=tf.string))

    # Include `detokenize` and `lookup` signatures for:
    #   * `Tensors` with shapes [tokens] and [batch, tokens]
    #   * `RaggedTensors` with shape [batch, tokens]
    self.detokenize.get_concrete_function(
        tf.TensorSpec(shape=[None, None], dtype=tf.int64))
    self.detokenize.get_concrete_function(
          tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))

    self.lookup.get_concrete_function(
        tf.TensorSpec(shape=[None, None], dtype=tf.int64))
    self.lookup.get_concrete_function(
          tf.RaggedTensorSpec(shape=[None, None], dtype=tf.int64))

    # These `get_*` methods take no arguments
    self.get_vocab_size.get_concrete_function()
    self.get_vocab_path.get_concrete_function()
    self.get_reserved_tokens.get_concrete_function()

  @tf.function
  def tokenize(self, strings):
    enc = self.tokenizer.tokenize(strings)
    # Merge the `word` and `word-piece` axes.
    enc = enc.merge_dims(-2,-1)
    enc = add_start_end(enc)
    return enc

  @tf.function
  def detokenize(self, tokenized):
    words = self.tokenizer.detokenize(tokenized)
    return cleanup_text(self._reserved_tokens, words)

  @tf.function
  def lookup(self, token_ids):
    return tf.gather(self.vocab, token_ids)

  @tf.function
  def get_vocab_size(self):
    return tf.shape(self.vocab)[0]

  @tf.function
  def get_vocab_path(self):
    return self._vocab_path

  @tf.function
  def get_reserved_tokens(self):
    return tf.constant(self._reserved_tokens)

빌드 CustomTokenizer 각 언어에 대해 :

tokenizers = tf.Module()
tokenizers.pt = CustomTokenizer(reserved_tokens, 'pt_vocab.txt')
tokenizers.en = CustomTokenizer(reserved_tokens, 'en_vocab.txt')

로 tokenizers 보냅니다 saved_model :

model_name = 'ted_hrlr_translate_pt_en_converter'
tf.saved_model.save(tokenizers, model_name)
2021-08-11 18:49:08.917974: W tensorflow/python/util/util.cc:348] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.

다시로드 saved_model 하고 방법을 테스트 :

reloaded_tokenizers = tf.saved_model.load(model_name)
reloaded_tokenizers.en.get_vocab_size().numpy()
7010
tokens = reloaded_tokenizers.en.tokenize(['Hello TensorFlow!'])
tokens.numpy()
array([[   2, 4006, 2358,  687, 1192, 2365,    4,    3]])
text_tokens = reloaded_tokenizers.en.lookup(tokens)
text_tokens
<tf.RaggedTensor [[b'[START]', b'hello', b'tens', b'##or', b'##f', b'##low', b'!', b'[END]']]>
round_trip = reloaded_tokenizers.en.detokenize(tokens)

print(round_trip.numpy()[0].decode('utf-8'))
hello tensorflow !

을 위해 그것을 보관 번역 자습서 :

zip -r {model_name}.zip {model_name}
adding: ted_hrlr_translate_pt_en_converter/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/variables/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/variables/variables.data-00000-of-00001 (deflated 51%)
  adding: ted_hrlr_translate_pt_en_converter/variables/variables.index (deflated 33%)
  adding: ted_hrlr_translate_pt_en_converter/assets/ (stored 0%)
  adding: ted_hrlr_translate_pt_en_converter/assets/pt_vocab.txt (deflated 57%)
  adding: ted_hrlr_translate_pt_en_converter/assets/en_vocab.txt (deflated 54%)
  adding: ted_hrlr_translate_pt_en_converter/saved_model.pb (deflated 91%)
du -h *.zip
184K    ted_hrlr_translate_pt_en_converter.zip

선택 사항: 알고리즘

WordPiece 알고리즘에는 상향식과 하향식의 두 가지 버전이 있습니다. 두 경우 모두 목표는 동일합니다. "훈련 말뭉치와 원하는 토큰 D의 수를 감안할 때 최적화 문제는 선택한 단어 조각 모델에 따라 분할할 때 결과 말뭉치가 단어 조각 수에서 최소가 되도록 D 단어 조각을 선택하는 것입니다. "

원래 상향식 WordPiece 알고리즘 에 기반 바이트 쌍 인코딩 . BPE와 마찬가지로 알파벳으로 시작하여 일반적인 bigram을 반복적으로 결합하여 단어 조각과 단어를 형성합니다.

TensorFlow 텍스트의 어휘 발전기의 하향식 구현 다음 BERT를 . 단어로 시작하여 빈도 임계값에 도달하거나 더 이상 나눌 수 없을 때까지 더 작은 구성 요소로 나눕니다. 다음 섹션에서는 이에 대해 자세히 설명합니다. 일본어, 중국어 및 한국어의 경우 이 하향식 접근 방식은 시작할 명시적인 단어 단위가 없기 때문에 작동하지 않습니다. 그들을 위해 당신이 필요로 다른 접근 방식을 .

어휘 선택

WordPiece 하향식 생성 알고리즘 (워드 카운트) 쌍과 임계치들의 세트에 걸리는 T , 복귀 어휘 V .

알고리즘은 반복적입니다. 그것은을 위해 실행 k 일반적으로 반복, k = 4 ,하지만 처음 두 정말 중요하다. 세 번째와 네 번째(및 그 이후)는 두 번째와 동일합니다. 참고 이진 검색의 각 단계에 대한 처음부터 알고리즘 실행되는 k 반복.

아래에 설명된 반복:

첫 번째 반복

  1. 반복 처리는 입력 모든 단어 카운트 쌍 이상으로 표시 (w, c) .
  2. 각 단어 w 로 표시된 모든 문자열 생성 s . 예 :이 단어에 대한 human , 우리는 생성 {h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n} .
  3. 문자열 - 투 - 수를 해시 맵을 유지하고, 각각의 수 증가 s 에 의해 c . 우리가있는 경우 예를 들면, (human, 113)(humas, 3) 우리 입력하여, 카운트의 s = huma 것이다 113+3=116 .
  4. 우리는 모든 문자열의 수를 수집하면 반복,은을 통해 (s, c)긴로 시작 s 첫번째.
  5. 어떤 유지 sc > T . 예컨대, 만약 T = 100 와 우리가 (pers, 231); (dogs, 259); (##rint; 76) , 우리는 계속 것이다 persdogs .
  6. s 유지되고, 그 접두사의 모든로부터 계산 떨어져 뺍니다. 이것은 모든 정렬에 대한 이유 s 다른 단어를 두 번 계산 될 수 있기 때문에,이 알고리즘의 중요한 부분을 4 단계에서 길이. 예를 들어, 우리가 유지했다고 가정 해 봅시다 human 우리는에 도착 (huma, 116) . 우리는 알고 113 그 중 116 에서 온 human , 그리고 3 에서 온 humas . 그러나 이제 human 우리의 어휘에, 우리는 우리가 결코 세그먼트 알고 humanhuma ##n . 그래서 일단 human 유지 된 후 huma 단지의 효과적인 수가있다 3 .

이 알고리즘은 단어 조각 세트 생성 s (전체 단어 일 것이다 많은 w 우리는 우리의 WordPiece 어휘로 사용할 수 있습니다).

그러나 문제가 있습니다. 이 알고리즘은 단어 조각을 심각하게 과도하게 생성합니다. 그 이유는 접두사 토큰의 수만 빼기 때문입니다. 우리가 단어를 유지하는 경우 따라서, human , 우리의 개수를 빼는 것 h, hu, hu, huma 가 아니라위한 ##u, ##um, ##uma, ##uman 등등. 우리는 모두 생성 할 수 있습니다 그래서 human##uman 에도 불구하고 단어 조각으로, ##uman 적용되지 않을 것입니다.

그래서 왜 그냥 모든 접두사, 모든 문자열에 대한 카운트를 빼지? 그러면 결국 카운트를 여러 번 뺄 수 있기 때문입니다. 이제 우린 처리한다고 가정 해 봅시다 s 길이가 5의 우리는 모두 유지 (##denia, 129)(##eniab, 137) , 65 그 개수의 단어에서 온 undeniable . 우리는 모든 문자열으로부터 뺄 경우에, 우리는 뺄 것 65 문자열에서 ##enia 우리가 한 번만 빼한다하더라도, 두 번. 그러나 접두사에서 빼기만 하면 정확히 한 번만 빼게 됩니다.

두 번째(및 세 번째 ...) 반복

위에서 언급한 과잉 생성 문제를 해결하기 위해 알고리즘을 여러 번 반복합니다.

이후 반복 한 가지 중요한 차이점으로, 처음과 동일하다 : 2 단계에서 대신 모든 문자열을 고려, 우리는 이전 반복의 어휘를 사용하여 WordPiece의 토큰 화 알고리즘을 적용하고, 만 분리 점에서 시작 문자열을 고려하십시오.

예를 들어, 우리가 알고리즘의 2 단계를 수행하고 단어가 발생하고 있다고 가정 해 봅시다 undeniable . 첫 번째 반복에서, 우리는 모든 문자열을 고려할 것입니다 예를 들어, {u, un, und, ..., undeniable, ##n, ##nd, ..., ##ndeniable, ...} .

이제 두 번째 반복에서는 이들 중 일부만 고려할 것입니다. 첫 번째 반복 후에 관련 단어 조각이 다음과 같다고 가정해 보겠습니다.

un, ##deni, ##able, ##ndeni, ##iable

에 WordPiece 알고리즘 의지 세그먼트이 un ##deni ##able (섹션 참조 적용 WordPiece 자세한 내용을). 이 경우, 우리는 단지 분할 지점에서 시작하는 문자열을 고려할 것입니다. 우리는 여전히 가능한 모든 끝 위치를 고려할 것입니다. 그래서 두 번째 반복하는 동안, 세트 s 에 대한 undeniable 있습니다 :

{u, un, und, unden, undeni, undenia, undeniab, undeniabl, undeniable, ##d, ##de, ##den, ##deni, ##denia, ##deniab, ##deniabl , ##deniable, ##a, ##ab, ##abl, ##able}

그 외에는 알고리즘이 동일합니다. 이 예에서, 첫 번째 반복에서, 알고리즘은 토큰 생성 suprious ##ndeni##iable . 이제 이러한 토큰은 고려되지 않으므로 두 번째 반복에서 생성되지 않습니다. 우리는 결과가 수렴하는지 확인하기 위해 여러 번 반복합니다(문자 그대로 수렴을 보장하지는 않음).

워드피스 적용하기

WordPiece 어휘가 생성되면 이를 새 데이터에 적용할 수 있어야 합니다. 알고리즘은 단순한 탐욕적인 가장 긴 일치 우선 응용 프로그램입니다.

예를 들어, 단어를 분할 고려 undeniable .

우리 첫째 조회 undeniable 우리의 WordPiece 사전, 그리고 그것의 존재한다면, 우리는 완료됩니다. 그렇지 않으면, 우리는 하나 개의 문자, 반복, 예를 들어,로 엔드 포인트를 감소 undeniabl .

결국 우리는 어휘에서 하위 토큰을 찾거나 단일 문자 하위 토큰으로 내려갑니다. (일반적으로, 우리는이 힘이 드문 유니 코드 문자의 경우하지 않지만 모든 문자는, 우리의 어휘에 있다고 가정합니다. 우리는 어휘에없는 희귀 한 유니 코드 문자가 발생하면 우리는 단순히 단어 전체에 매핑 <unk> ).

이 경우, 우리는 발견 un 우리의 어휘를. 이것이 우리의 첫 번째 단어입니다. 그럼 우리의 마지막으로 이동 un 하고 찾아보십시오, 예를 들면, 처리를 반복 ##deniable 한 후, ##deniabl 우리는 전체 단어를 분할 할 때까지이 반복 등.

직관

직관적으로 WordPiece 토큰화는 두 가지 다른 목표를 충족하려고 합니다.

  1. 가능한 부분의 최소 개수로 데이터를 토큰 화. WordPiece 알고리즘은 단어를 "분할"하지 않는다는 점을 염두에 두는 것이 중요합니다. 그렇지 않으면, 그냥 그 문자, 예를 들어,에 모든 단어를 분할 할 human -> {h, ##u, ##m, ##a, #n} . 이 경우에도 일반적인 단어에 대한 언어 형태소를 분할합니다 형태 스플리터에서 WordPiece의 다른하게 한 중요한 일이다 (예를 들어, unwanted -> {un, want, ed} ).

  2. 단어를 조각으로 분할해야 하는 경우 훈련 데이터에서 최대 개수를 갖는 조각으로 분할합니다. 예를 들어, 워드 이유 undeniable 로 분할 될 것이다 {un, ##deni, ##able} 오히려 같은 대안보다는 {unde, ##niab, ##le} 라는위한 카운트 un##able 의 이들은 일반적인 접두사와 접미사이기 때문에 특히 매우 높을 것입니다. 의 수가 비록 ##le 보다 높은 있어야합니다 ##able 의 낮은 카운트 unde##niab 이 알고리즘에 적은 "바람직한"토큰 화 할 것입니다.

선택 사항: tf.lookup

당신은 그것의 가치는 당신이 조회 테이블을 직접 구축하고 해당를 전달할 수 있다는 지적 어휘를 통해 액세스, 이상의 제어가 필요한 경우 BertTokenizer .

당신이 문자열을 전달하면, BertTokenizer 다음을 수행합니다

pt_lookup = tf.lookup.StaticVocabularyTable(
    num_oov_buckets=1,
    initializer=tf.lookup.TextFileInitializer(
        filename='pt_vocab.txt',
        key_dtype=tf.string,
        key_index = tf.lookup.TextFileIndex.WHOLE_LINE,
        value_dtype = tf.int64,
        value_index=tf.lookup.TextFileIndex.LINE_NUMBER)) 
pt_tokenizer = text.BertTokenizer(pt_lookup)

이제 토크나이저에서 사용되는 조회 테이블에 직접 액세스할 수 있습니다.

pt_lookup.lookup(tf.constant(['é', 'um', 'uma', 'para', 'não']))
<tf.Tensor: shape=(5,), dtype=int64, numpy=array([7765,   85,   86,   87, 7765])>

당신은 어휘 파일을 사용할 필요가 없습니다 tf.lookup 다른 초기화 옵션이 있습니다. 메모리에 어휘가있는 경우 사용할 수 있습니다 lookup.KeyValueTensorInitializer :

pt_lookup = tf.lookup.StaticVocabularyTable(
    num_oov_buckets=1,
    initializer=tf.lookup.KeyValueTensorInitializer(
        keys=pt_vocab,
        values=tf.range(len(pt_vocab), dtype=tf.int64))) 
pt_tokenizer = text.BertTokenizer(pt_lookup)