TensorFlow.org에서 보기 | Google Colab에서 실행 | GitHub에서 보기 | 노트북 다운로드 |
개요
텍스트 전처리는 원시 텍스트를 모델의 정수 입력으로 종단 간 변환하는 것입니다. NLP 모델에는 종종 텍스트를 전처리하기 위해 수백(수천은 아닐지라도) 라인의 Python 코드가 수반됩니다. 다음과 같은 이유로 텍스트 사전 처리는 종종 모델에 대한 도전 과제입니다.
훈련 서빙 스큐. 모델 입력의 사전 처리 논리가 모델 개발의 모든 단계(예: 사전 훈련, 미세 조정, 평가, 추론)에서 일관성을 유지하도록 하는 것이 점점 더 어려워지고 있습니다. 다른 초매개변수, 토큰화, 문자열 전처리 알고리즘을 사용하거나 단순히 다른 단계에서 모델 입력을 일관되지 않게 패키징하면 디버그하기 어렵고 모델에 치명적인 영향을 줄 수 있습니다.
효율성과 유연성. 전처리는 오프라인으로 수행할 수 있지만(예: 처리된 출력을 디스크의 파일에 쓴 다음 입력 파이프라인에서 전처리된 데이터를 다시 사용하여) 이 방법은 추가 파일 읽기 및 쓰기 비용을 발생시킵니다. 동적으로 발생해야 하는 전처리 결정이 있는 경우 오프라인 전처리도 불편합니다. 다른 옵션을 실험하려면 데이터 세트를 다시 생성해야 합니다.
복잡한 모델 인터페이스. 텍스트 모델은 입력이 순수한 텍스트일 때 훨씬 더 이해하기 쉽습니다. 입력에 추가 간접 인코딩 단계가 필요한 경우 모델을 이해하기 어렵습니다. 전처리 복잡성을 줄이는 것은 모델 디버깅, 제공 및 평가에서 특히 높이 평가됩니다.
또한 더 간단한 모델 인터페이스를 사용하면 탐색되지 않은 다른 데이터 세트에서 모델(예: 추론 또는 교육)을 보다 편리하게 시도할 수 있습니다.
TF.Text를 사용한 텍스트 전처리
TF.Text의 텍스트 전처리 API를 사용하여 사용자의 텍스트 데이터세트를 모델의 정수 입력으로 변환할 수 있는 전처리 기능을 구성할 수 있습니다. 사용자는 앞서 언급한 문제를 완화하기 위해 모델의 일부로 전처리를 직접 패키징할 수 있습니다.
이 튜토리얼은 "가면 LM 및 마스킹 절차"의에 설명 된 BERT 모델과 작업을 pretraining 마스킹 언어에 대한 입력에 대한 입력으로 텍스트 데이터를 변환하는 TF.Text 전처리 작전을 사용하는 방법을 보여줍니다 언어에 대한 깊은 양방향 변압기의 사전 교육 : BERT 이해 . 이 프로세스에는 텍스트를 하위 단어 단위로 토큰화하고, 문장을 결합하고, 콘텐츠를 고정 크기로 자르고, 마스킹된 언어 모델링 작업을 위한 레이블을 추출하는 작업이 포함됩니다.
설정
먼저 필요한 패키지와 라이브러리를 가져오겠습니다.
pip install -q -U tensorflow-text
import tensorflow as tf
import tensorflow_text as text
import functools
우리의 데이터는 두 개의 텍스트 기능을 포함하고 있으며 우리는 예를 생성 할 수 있습니다 tf.data.Dataset
. 우리의 목표는 우리가 제공 할 수있는 기능을 만드는 것입니다 Dataset.map()
교육에 사용하기로합니다.
examples = {
"text_a": [
b"Sponge bob Squarepants is an Avenger",
b"Marvel Avengers"
],
"text_b": [
b"Barack Obama is the President.",
b"President is the highest office"
],
}
dataset = tf.data.Dataset.from_tensor_slices(examples)
next(iter(dataset))
{'text_a': <tf.Tensor: shape=(), dtype=string, numpy=b'Sponge bob Squarepants is an Avenger'>, 'text_b': <tf.Tensor: shape=(), dtype=string, numpy=b'Barack Obama is the President.'>}
토큰화
첫 번째 단계는 문자열 전처리를 실행하고 데이터 세트를 토큰화하는 것입니다. 이는 사용하여 수행 할 수 text.BertTokenizer
A는, text.Splitter
위한 subwords wordpieces 또는 문장으로 토큰 화 수 BERT 모델 로부터 생성 된 어휘 주어진 Wordpiece 알고리즘 . 당신은에서 TF.Text에서 사용할 수있는 다른 subword의 tokenizers에 대해 자세히 배울 수 여기를 .
어휘는 이전에 생성된 BERT 체크포인트에서 가져오거나 자신의 데이터에서 직접 생성할 수 있습니다. 이 예의 목적을 위해 장난감 어휘를 만들어 보겠습니다.
_VOCAB = [
# Special tokens
b"[UNK]", b"[MASK]", b"[RANDOM]", b"[CLS]", b"[SEP]",
# Suffixes
b"##ack", b"##ama", b"##ger", b"##gers", b"##onge", b"##pants", b"##uare",
b"##vel", b"##ven", b"an", b"A", b"Bar", b"Hates", b"Mar", b"Ob",
b"Patrick", b"President", b"Sp", b"Sq", b"bob", b"box", b"has", b"highest",
b"is", b"office", b"the",
]
_START_TOKEN = _VOCAB.index(b"[CLS]")
_END_TOKEN = _VOCAB.index(b"[SEP]")
_MASK_TOKEN = _VOCAB.index(b"[MASK]")
_RANDOM_TOKEN = _VOCAB.index(b"[RANDOM]")
_UNK_TOKEN = _VOCAB.index(b"[UNK]")
_MAX_SEQ_LEN = 8
_MAX_PREDICTIONS_PER_BATCH = 5
_VOCAB_SIZE = len(_VOCAB)
lookup_table = tf.lookup.StaticVocabularyTable(
tf.lookup.KeyValueTensorInitializer(
keys=_VOCAB,
key_dtype=tf.string,
values=tf.range(
tf.size(_VOCAB, out_type=tf.int64), dtype=tf.int64),
value_dtype=tf.int64),
num_oov_buckets=1
)
하자의 구조 text.BertTokenizer
위의 어휘를 사용하고에 텍스트 입력 토큰 화 RaggedTensor
.`.
bert_tokenizer = text.BertTokenizer(lookup_table, token_out_type=tf.string)
bert_tokenizer.tokenize(examples["text_a"])
<tf.RaggedTensor [[[b'Sp', b'##onge'], [b'bob'], [b'Sq', b'##uare', b'##pants'], [b'is'], [b'an'], [b'A', b'##ven', b'##ger']], [[b'Mar', b'##vel'], [b'A', b'##ven', b'##gers']]]>
bert_tokenizer.tokenize(examples["text_b"])
<tf.RaggedTensor [[[b'Bar', b'##ack'], [b'Ob', b'##ama'], [b'is'], [b'the'], [b'President'], [b'[UNK]']], [[b'President'], [b'is'], [b'the'], [b'highest'], [b'office']]]>
에서 텍스트 출력 text.BertTokenizer
우리가 텍스트를 토큰 화되고 있는지 볼 수 있지만이 모델은 정수 ID를 필요로한다. 우리는 설정할 수 token_out_type
으로 PARAM을 tf.int64
(어휘로 인덱스이다) 정수 ID를 얻는다.
bert_tokenizer = text.BertTokenizer(lookup_table, token_out_type=tf.int64)
segment_a = bert_tokenizer.tokenize(examples["text_a"])
segment_a
<tf.RaggedTensor [[[22, 9], [24], [23, 11, 10], [28], [14], [15, 13, 7]], [[18, 12], [15, 13, 8]]]>
segment_b = bert_tokenizer.tokenize(examples["text_b"])
segment_b
<tf.RaggedTensor [[[16, 5], [19, 6], [28], [30], [21], [0]], [[21], [28], [30], [27], [29]]]>
text.BertTokenizer
반환 RaggedTensor
형상 [batch, num_tokens, num_wordpieces]
. 우리가 추가로 필요하지 않기 때문에 num_tokens
현재 사용 사례에 대한 치수를, 우리는 얻기 위해 마지막 두 차원을 병합 할 수 있습니다 RaggedTensor
모양 [batch, num_wordpieces]
:
segment_a = segment_a.merge_dims(-2, -1)
segment_a
<tf.RaggedTensor [[22, 9, 24, 23, 11, 10, 28, 14, 15, 13, 7], [18, 12, 15, 13, 8]]>
segment_b = segment_b.merge_dims(-2, -1)
segment_b
<tf.RaggedTensor [[16, 5, 19, 6, 28, 30, 21, 0], [21, 28, 30, 27, 29]]>
콘텐츠 트리밍
BERT의 주요 입력은 두 문장의 연결입니다. 그러나 BERT는 입력이 고정된 크기와 모양이어야 하며 예산을 초과하는 콘텐츠가 있을 수 있습니다.
우리는 사용하여이 문제를 해결할 수 text.Trimmer
(마지막 축을 따라 연결된 후) 소정의 크기로 콘텐츠 아래로 트림 할 수 있습니다. 다른있다 text.Trimmer
서로 다른 알고리즘을 사용하여 보존 할 콘텐츠를 선택 유형. text.RoundRobinTrimmer
예는 각 세그먼트에 대해 균등 용량 할당되지만 문장의 단부를 트리밍 할 수있다. text.WaterfallTrimmer
마지막 문장의 끝에서 시작 트림됩니다.
이 예를 들어, 우리가 사용 RoundRobinTrimmer
선택 항목 왼쪽에서 오른쪽으로의 각 세그먼트에서.
trimmer = text.RoundRobinTrimmer(max_seq_length=[_MAX_SEQ_LEN])
trimmed = trimmer.trim([segment_a, segment_b])
trimmed
[<tf.RaggedTensor [[22, 9, 24, 23], [18, 12, 15, 13]]>, <tf.RaggedTensor [[16, 5, 19, 6], [21, 28, 30, 27]]>]
trimmed
일괄 걸쳐 소자의 수가 8 개 요소 (축선 = -1 함께 연결될 때)이고 현재의 세그먼트를 포함한다.
세그먼트 결합
이제 우리는 세그먼트 손질 것을, 우리는 하나 얻기 위해 그들을 함께 결합 할 수 있습니다 RaggedTensor
. BERT는 시작 (나타내는 특수 토큰을 사용하여 [CLS]
세그먼트 (중)과 단부 [SEP]
). 우리는 또한 필요 RaggedTensor
결합에있는 항목을 나타내는 Tensor
하는 세그먼트에 속하는를. 우리는 사용할 수 text.combine_segments()
이 두 얻기 위해 Tensor
삽입 특수 토큰.
segments_combined, segments_ids = text.combine_segments(
[segment_a, segment_b],
start_of_sequence_id=_START_TOKEN, end_of_segment_id=_END_TOKEN)
segments_combined, segments_ids
(<tf.RaggedTensor [[3, 22, 9, 24, 23, 11, 10, 28, 14, 15, 13, 7, 4, 16, 5, 19, 6, 28, 30, 21, 0, 4], [3, 18, 12, 15, 13, 8, 4, 21, 28, 30, 27, 29, 4]]>, <tf.RaggedTensor [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]]>)
마스크 언어 모델 작업
이제 우리는 우리의 기본 입력을 가지고, 우리는 "마스크 LM 및 마스킹 절차"에 필요한 입력을 압축 해제 할 수 있습니다 작업에 설명 BERT : 언어 이해에 대한 깊은 양방향 변압기의 사전 교육
마스킹된 언어 모델 작업에는 (1) 마스킹을 위해 선택할 항목과 (2) 할당된 값의 두 가지 하위 문제가 있습니다.
아이템 선택
우리는 무작위로 마스킹을위한 항목을 선택할 것이기 때문에, 우리는 사용 text.RandomItemSelector
. RandomItemSelector
임의로 주어진 제한 (에 배치 될 수있는 상품 선택 max_selections_per_batch
, selection_rate
및 unselectable_ids
) 반환 항목이 선택되었던 나타내는 부울 마스크.
random_selector = text.RandomItemSelector(
max_selections_per_batch=_MAX_PREDICTIONS_PER_BATCH,
selection_rate=0.2,
unselectable_ids=[_START_TOKEN, _END_TOKEN, _UNK_TOKEN]
)
selected = random_selector.get_selection_mask(
segments_combined, axis=1)
selected
<tf.RaggedTensor [[False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, True, True, True, False, False], [False, False, False, False, False, True, False, False, False, False, False, True, False]]>
마스킹된 값 선택
마스킹 값을 선택하기 위한 원래 BERT 문서에 설명된 방법론은 다음과 같습니다.
들어 mask_token_rate
시간의에 항목을 교체 [MASK]
토큰 :
"my dog is hairy" -> "my dog is [MASK]"
들어 random_token_rate
의 시간, 임의의 단어로 항목을 대체 :
"my dog is hairy" -> "my dog is apple"
들어 1 - mask_token_rate - random_token_rate
시간의 변경되지 않은 항목을 유지 :
"my dog is hairy" -> "my dog is hairy."
text.MaskedValuesChooser
이 논리를 캡슐화하고 우리의 전처리 기능을 사용할 수 있습니다. 여기에 무엇의 예 MaskValuesChooser
반환 주어진 mask_token_rate
80 %는 기본적 random_token_rate
:
input_ids = tf.ragged.constant([[19, 7, 21, 20, 9, 8], [13, 4, 16, 5], [15, 10, 12, 11, 6]])
mask_values_chooser = text.MaskValuesChooser(_VOCAB_SIZE, _MASK_TOKEN, 0.8)
mask_values_chooser.get_mask_values(input_ids)
<tf.RaggedTensor [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1], [1, 10, 1, 1, 6]]>
공급 받아 RaggedTensor
입력 text.MaskValuesChooser
반환 RaggedTensor
하나와 동일한 형상의 _MASK_VALUE
(0), 랜덤 ID, 또는 동일한 ID를 변경.
마스킹된 언어 모델 작업에 대한 입력 생성
지금 우리가 가지고 RandomItemSelector
우리가 마스킹에 대한 항목을 선택하는 데 도움 text.MaskValuesChooser
값을 할당하기를, 우리가 사용할 수 있습니다 text.mask_language_model()
우리 BERT 모델이 작업의 모든 입력을 조립.
masked_token_ids, masked_pos, masked_lm_ids = text.mask_language_model(
segments_combined,
item_selector=random_selector, mask_values_chooser=mask_values_chooser)
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.
하자 다이빙 깊고의 출력 검사 mask_language_model()
. 출력 masked_token_ids
있다 :
masked_token_ids
<tf.RaggedTensor [[3, 22, 1, 24, 23, 1, 10, 28, 1, 15, 1, 7, 4, 16, 5, 19, 6, 28, 30, 21, 0, 4], [3, 18, 12, 15, 13, 1, 4, 21, 28, 30, 27, 1, 4]]>
우리의 입력은 어휘를 사용하여 인코딩된다는 것을 기억하십시오. 우리가 디코딩 경우 masked_token_ids
우리의 어휘를 사용하여, 우리는 얻을 :
tf.gather(_VOCAB, masked_token_ids)
<tf.RaggedTensor [[b'[CLS]', b'Sp', b'[MASK]', b'bob', b'Sq', b'[MASK]', b'##pants', b'is', b'[MASK]', b'A', b'[MASK]', b'##ger', b'[SEP]', b'Bar', b'##ack', b'Ob', b'##ama', b'is', b'the', b'President', b'[UNK]', b'[SEP]'], [b'[CLS]', b'Mar', b'##vel', b'A', b'##ven', b'[MASK]', b'[SEP]', b'President', b'is', b'the', b'highest', b'[MASK]', b'[SEP]']]>
일부 wordpiece 토큰 중 치환 된 알 [MASK]
, [RANDOM]
또는 다른 ID 값. masked_pos
출력은 우리 교환 된 토큰 (각 일괄 처리)의 인덱스를 제공한다.
masked_pos
<tf.RaggedTensor [[2, 5, 8, 10], [5, 11]]>
masked_lm_ids
우리에게 토큰의 원래 값을 제공합니다.
masked_lm_ids
<tf.RaggedTensor [[9, 11, 14, 13], [8, 29]]>
여기서 ID를 다시 디코딩하여 사람이 읽을 수 있는 값을 얻을 수 있습니다.
tf.gather(_VOCAB, masked_lm_ids)
<tf.RaggedTensor [[b'##onge', b'##uare', b'an', b'##ven'], [b'##gers', b'office']]>
모델 입력 채우기
이제 우리의 모델에 대한 모든 입력이 있는지 우리 전처리의 마지막 단계는 고정 된 2 차원으로 패키지화하는 Tensor
의 패딩도 마스크 생성 Tensor
패드 값이되는 값을 표시한다. 우리는 사용할 수 text.pad_model_inputs()
이 작업에 우리를 도와.
# Prepare and pad combined segment inputs
input_word_ids, input_mask = text.pad_model_inputs(
masked_token_ids, max_seq_length=_MAX_SEQ_LEN)
input_type_ids, _ = text.pad_model_inputs(
masked_token_ids, max_seq_length=_MAX_SEQ_LEN)
# Prepare and pad masking task inputs
masked_lm_positions, masked_lm_weights = text.pad_model_inputs(
masked_token_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
masked_lm_ids, _ = text.pad_model_inputs(
masked_lm_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
model_inputs = {
"input_word_ids": input_word_ids,
"input_mask": input_mask,
"input_type_ids": input_type_ids,
"masked_lm_ids": masked_lm_ids,
"masked_lm_positions": masked_lm_positions,
"masked_lm_weights": masked_lm_weights,
}
model_inputs
{'input_word_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 1, 24, 23, 1, 10, 28], [ 3, 18, 12, 15, 13, 1, 4, 21]])>, 'input_mask': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]])>, 'input_type_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 1, 24, 23, 1, 10, 28], [ 3, 18, 12, 15, 13, 1, 4, 21]])>, 'masked_lm_ids': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[ 9, 11, 14, 13, 0], [ 8, 29, 0, 0, 0]])>, 'masked_lm_positions': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[ 3, 22, 1, 24, 23], [ 3, 18, 12, 15, 13]])>, 'masked_lm_weights': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]])>}
검토
지금까지 우리가 가진 것을 검토하고 전처리 기능을 조립합시다. 여기 우리가 가진 것이 있습니다:
def bert_pretrain_preprocess(vocab_table, features):
# Input is a string Tensor of documents, shape [batch, 1].
text_a = features["text_a"]
text_b = features["text_b"]
# Tokenize segments to shape [num_sentences, (num_words)] each.
tokenizer = text.BertTokenizer(
vocab_table,
token_out_type=tf.int64)
segments = [tokenizer.tokenize(text).merge_dims(
1, -1) for text in (text_a, text_b)]
# Truncate inputs to a maximum length.
trimmer = text.RoundRobinTrimmer(max_seq_length=6)
trimmed_segments = trimmer.trim(segments)
# Combine segments, get segment ids and add special tokens.
segments_combined, segment_ids = text.combine_segments(
trimmed_segments,
start_of_sequence_id=_START_TOKEN,
end_of_segment_id=_END_TOKEN)
# Apply dynamic masking task.
masked_input_ids, masked_lm_positions, masked_lm_ids = (
text.mask_language_model(
segments_combined,
random_selector,
mask_values_chooser,
)
)
# Prepare and pad combined segment inputs
input_word_ids, input_mask = text.pad_model_inputs(
masked_input_ids, max_seq_length=_MAX_SEQ_LEN)
input_type_ids, _ = text.pad_model_inputs(
masked_input_ids, max_seq_length=_MAX_SEQ_LEN)
# Prepare and pad masking task inputs
masked_lm_positions, masked_lm_weights = text.pad_model_inputs(
masked_input_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
masked_lm_ids, _ = text.pad_model_inputs(
masked_lm_ids, max_seq_length=_MAX_PREDICTIONS_PER_BATCH)
model_inputs = {
"input_word_ids": input_word_ids,
"input_mask": input_mask,
"input_type_ids": input_type_ids,
"masked_lm_ids": masked_lm_ids,
"masked_lm_positions": masked_lm_positions,
"masked_lm_weights": masked_lm_weights,
}
return model_inputs
우리는 이전에 건설 tf.data.Dataset
우리는 지금 우리의 조립 전처리 기능을 사용할 수 있습니다 bert_pretrain_preprocess()
에 Dataset.map()
. 이를 통해 원시 문자열 데이터를 정수 입력으로 변환하고 모델에 직접 공급하기 위한 입력 파이프라인을 생성할 수 있습니다.
dataset = tf.data.Dataset.from_tensors(examples)
dataset = dataset.map(functools.partial(
bert_pretrain_preprocess, lookup_table))
next(iter(dataset))
{'input_word_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 9, 1, 4, 16, 5, 19], [ 3, 18, 1, 15, 4, 1, 28, 30]])>, 'input_mask': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]])>, 'input_type_ids': <tf.Tensor: shape=(2, 8), dtype=int64, numpy= array([[ 3, 22, 9, 1, 4, 16, 5, 19], [ 3, 18, 1, 15, 4, 1, 28, 30]])>, 'masked_lm_ids': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[24, 19, 0, 0, 0], [12, 21, 0, 0, 0]])>, 'masked_lm_positions': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[ 3, 22, 9, 1, 4], [ 3, 18, 1, 15, 4]])>, 'masked_lm_weights': <tf.Tensor: shape=(2, 5), dtype=int64, numpy= array([[1, 1, 1, 1, 1], [1, 1, 1, 1, 1]])>}
관련 튜토리얼
BERT로 분류 텍스트 - 분류 텍스트에 pretrained BERT 모델을 사용하는 방법에 대한 튜토리얼. 이것은 BERT 모델에서 사용하는 입력을 사전 처리하는 방법에 익숙해졌기 때문에 좋은 후속 조치입니다.
TF 텍스트와 토큰 화 - 자습서 TF.Text에 존재 tokenizers의 다른 유형을 자세히 설명.
와 텍스트를 처리
RaggedTensor
- 사용을 만들고 조작하는 방법에 대한 자세한 안내RaggedTensor
들.