Посмотреть на TensorFlow.org | Запускаем в Google Colab | Посмотреть исходный код на GitHub | Скачать блокнот |
В этом учебнике показано , как создать подслово словарь из набора данных, и использовать его для создания text.BertTokenizer
из словаря.
Основным преимуществом токенизатора подслов является то, что он выполняет интерполяцию между словарной и символьной разметкой. Обычные слова получают место в словарном запасе, но токенизатор может вернуться к частям слова и отдельным символам для неизвестных слов.
Обзор
tensorflow_text
пакет включает в себя реализацию TensorFlow многих распространенных tokenizers. Сюда входят три токенизатора в стиле подслова:
-
text.BertTokenizer
- TheBertTokenizer
класс является высокоуровневым интерфейсом. Она включает в себя лексемы алгоритм расщепления Берта иWordPieceTokenizer
. Он принимает предложения в качестве входных данных и возвращает токен-идентификаторы. -
text.WordpieceTokenizer
- TheWordPieceTokenizer
класс является нижним интерфейсом уровня. Он только реализует алгоритм WordPiece . Перед вызовом необходимо стандартизировать и разбить текст на слова. Он принимает слова в качестве входных данных и возвращает Token-идентификаторы. -
text.SentencepieceTokenizer
- TheSentencepieceTokenizer
требует более сложной настройки. Для его инициализатора требуется предварительно обученная модель предложения. Смотрите хранилище Google / sentencepiece для получения инструкций о том , как построить один из этих моделей. Он может принимать предложения в качестве входных данных при tokenizing.
В этом руководстве словарный запас 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
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']
Этот набор данных создает пары предложений на португальском и английском языках:
for pt, en in train_examples.take(1):
print("Portuguese: ", pt.numpy().decode('utf-8'))
print("English: ", en.numpy().decode('utf-8'))
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 .
Обратите внимание на несколько моментов в приведенных выше примерах предложений:
- Они строчные.
- Вокруг знаков препинания есть пробелы.
- Неясно, используется ли нормализация Unicode и какая.
train_en = train_examples.map(lambda pt, en: en)
train_pt = train_examples.map(lambda pt, en: pt)
Создайте словарный запас
В этом разделе создается словарный запас из набора данных. Если у вас уже есть файл словаря и просто хочу , чтобы увидеть , как построить text.BertTokenizer
или text.Wordpiece
Tokenizer с ним , то вы можете пропустить вперед к строить токенизатор разделе.
Код словаря поколение входит в tensorflow_text
пакет пип. По умолчанию он не импортируется, его нужно импортировать вручную:
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 30s, sys: 2.21 s, total: 1min 32s Wall time: 1min 28s
Вот несколько кусочков получившейся лексики.
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 3s, sys: 2.21 s, total: 1min 6s Wall time: 1min 2s
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]
Если заменить символические идентификаторы с текстовыми представлениями ( с помощью tf.gather
) вы можете увидеть , что в первом примере слова "searchability"
возможность "serendipity"
"search ##ability"
"s ##ere ##nd ##ip ##ity"
"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)>
Настройка и экспорт
Этот учебник построен текст Tokenizer и 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)>
Индивидуальная детокенизация
Перед экспортом токенизаторов есть несколько вещей, которые вы можете очистить для последующих руководств:
- Они хотят , чтобы генерировать чистый вывод текста, так что падение зарезервированные лексемы , как
[START]
,[END]
и[PAD]
. - Они заинтересованы в полных строк, поэтому применять строку присоединиться по
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-11-02 15:20:31.762976: 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/saved_model.pb (deflated 91%) 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%)
du -h *.zip
184K ted_hrlr_translate_pt_en_converter.zip
Необязательно: алгоритм
Здесь стоит отметить, что существует две версии алгоритма WordPiece: снизу вверх и сверху вниз. В обоих случаях цель одна и та же: «Учитывая обучающий корпус и количество желаемых токенов D, задача оптимизации состоит в том, чтобы выбрать D словесных частей таким образом, чтобы результирующий корпус был минимальным по количеству частей слова при сегментировании в соответствии с выбранной моделью словаря. "
Оригинальный алгоритм WordPiece снизу вверх , основан на кодировании байт-пару . Как и BPE, он начинается с алфавита и итеративно комбинирует общие биграммы для образования частей и слов.
Словарь генератор TensorFlow Text следует за нисходящую реализацию из BERT . Начните со слов и разбейте их на более мелкие компоненты, пока они не достигнут порога частоты или не могут быть разбиты дальше. В следующем разделе это подробно описано. Для японского, китайского и корейского языков этот подход сверху вниз не работает, поскольку нет явных единиц слова, с которых можно было бы начать. Для тех , кто вам нужен другой подход .
Выбор словарного запаса
Сверху вниз WordPiece алгоритм генерации занимает в наборе (слово, количество) пары и порогом T
и возвращает словарь , V
.
Алгоритм итерационный. Это выполняется для k
итераций, где обычно k = 4
, но только первые два действительно важны. Третий и четвертый (и последующие) просто идентичны второму. Обратите внимание , что каждый шаг двоичного поиска работает алгоритм с нуля для k
итераций.
Итерации, описанные ниже:
Первая итерация
- Итерация над каждым словом и количеству пар на входе, обозначенный как
(w, c)
. - Для каждого слова
w
, порождают все подстроки, обозначаемаяs
. Например, для словаhuman
, мы генерируем{h, hu, hum, huma, human, ##u, ##um, ##uma, ##uman, ##m, ##ma, ##man, #a, ##an, ##n}
. - Поддерживать подстроку-к-подсчета хэш - карту, и увеличивать количество каждого
s
помощьюc
. Например, если мы имеем(human, 113)
и(humas, 3)
в нашем входе, отсчетs = huma
будет113+3=116
. - После того, как мы собрали отсчеты каждой подстроки, перебирайте
(s, c)
пар , начиная с самой длиннойs
первым. - Храните
s
, что имеетc > T
. Например, еслиT = 100
и мы имеем(pers, 231); (dogs, 259); (##rint; 76)
, то мы будем держатьpers
иdogs
. - Когда
s
сохраняются, вычитать от его счетов от всех своих префиксов. Это является причиной для сортировки всеs
длиной в шаге 4. Это критическая часть алгоритма, поскольку в противном случае слова будут подсчитаны дважды. Например, допустим , что мы сохранилиhuman
и мы получаем(huma, 116)
. Мы знаем , что113
из этих116
пришли отhuman
, и3
пришлиhumas
. Однако теперь, когдаhuman
находится в нашем лексиконе, мы знаем , что мы никогда не сегментныйhuman
вhuma ##n
. Поэтому , как толькоhuman
был сохранен, тоhuma
имеет эффективную кол -3
.
Этот алгоритм будет генерировать набор словесных Pièces 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, ...}
- {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
После создания словаря WordPiece нам нужно иметь возможность применять его к новым данным. Алгоритм представляет собой простое «жадное» приложение с поиском наиболее длинного совпадения.
Например, рассмотрим сегментирование слово undeniable
.
Мы первый поиск undeniable
в нашем WordPiece словаре, и если он присутствует, мы сделали. Если нет, то мы уменьшаем конечную точку на один символ, и повторить, например, undeniabl
.
В конце концов, мы либо найдем подтенк в нашем словаре, либо перейдем к подтену, состоящему из одного символа. (В целом, мы считаем , что каждый символ в нашем лексиконе, хотя это может быть не так для редких символов Unicode. Если мы встречаем редкий символ Unicode , что это не в словаре , мы просто отобразить все слова <unk>
).
В этом случае, мы находим un
в нашем словаре. Итак, это наш первый отрывок из слов. Затем мы переходим к концу un
и повторить обработку, например, попытаться найти ##deniable
, то ##deniabl
и т.д. Это повторяется до тех пор, пока сегментирование слова целиком.
Интуиция
Интуитивно понятно, что токенизация WordPiece пытается решить две разные задачи:
Токенизировать данные в наименьшее число частей , как это возможно. Важно помнить, что алгоритм WordPiece не «хочет» разбивать слова. В противном случае, было бы просто разделить каждое слово в его символах, например,
human -> {h, ##u, ##m, ##a, #n}
. Это одна критической вещь , которая делает WordPiece отличается от морфологических разветвителей, которые разделят лингвистические морфемы даже для общих слов (например,unwanted -> {un, want, ed}
).Когда слово действительно нужно разбить на части, разбейте его на части, которые имеют максимальное количество в обучающих данных. Например, причина , почему слово
undeniable
будет разбит на{un, ##deni, ##able}
в{unde, ##niab, ##le}
un
##able
{un, ##deni, ##able}
, а не альтернативы , как{unde, ##niab, ##le}
является то , что счетчики дляun
и##able
в Особенность будет очень высокой, так как это общие префиксы и суффиксы. Даже несмотря на то, счетчик для##le
должен быть выше , чем##able
вunde
##niab
##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)