Lihat di TensorFlow.org | Jalankan di Google Colab | Lihat di GitHub | Unduh buku catatan |
Ringkasan
Prapemrosesan teks adalah transformasi ujung-ke-ujung dari teks mentah menjadi input integer model. Model NLP sering disertai dengan beberapa ratus (jika bukan ribuan) baris kode Python untuk teks prapemrosesan. Pemrosesan awal teks sering kali menjadi tantangan bagi model karena:
Kemiringan penyajian pelatihan. Menjadi semakin sulit untuk memastikan bahwa logika pemrosesan awal dari input model konsisten di semua tahap pengembangan model (misalnya, prapelatihan, penyempurnaan, evaluasi, inferensi). Menggunakan hyperparameter yang berbeda, tokenisasi, algoritme prapemrosesan string, atau sekadar mengemas input model secara tidak konsisten pada tahapan yang berbeda dapat menghasilkan efek yang sulit di-debug dan membawa bencana pada model.
Efisiensi dan fleksibilitas. Sementara prapemrosesan dapat dilakukan secara offline (misalnya dengan menuliskan keluaran yang diproses ke file pada disk dan kemudian menggunakan kembali data praproses tersebut dalam saluran masukan), metode ini menimbulkan biaya baca dan tulis berkas tambahan. Preprocessing offline juga merepotkan jika ada keputusan preprocessing yang perlu terjadi secara dinamis. Bereksperimen dengan opsi yang berbeda akan membutuhkan regenerasi dataset lagi.
Antarmuka model yang kompleks. Model teks jauh lebih mudah dipahami jika inputnya berupa teks murni. Sulit untuk memahami model ketika inputnya memerlukan langkah penyandian tidak langsung tambahan. Mengurangi kompleksitas prapemrosesan sangat dihargai untuk debugging model, penyajian, dan evaluasi.
Selain itu, antarmuka model yang lebih sederhana juga membuatnya lebih nyaman untuk mencoba model (misalnya inferensi atau pelatihan) pada kumpulan data berbeda yang belum dijelajahi.
Pemrosesan teks dengan TF.Text
Dengan menggunakan API prapemrosesan teks TF.Text, kita dapat membuat fungsi prapemrosesan yang dapat mengubah kumpulan data teks pengguna menjadi input integer model. Pengguna dapat mengemas preprocessing secara langsung sebagai bagian dari model mereka untuk mengatasi masalah yang disebutkan di atas.
Tutorial ini akan menunjukkan bagaimana menggunakan TF.Text preprocessing ops untuk mengubah data teks ke masukan untuk model Bert dan masukan untuk bahasa masking sebelum pelatihan tugas yang dijelaskan dalam "Masked LM dan Masking Prosedur" dari Bert: Pre-pelatihan dari Deep dua arah Transformers untuk Bahasa pemahaman . Prosesnya melibatkan teks tokenizing ke dalam unit subword, menggabungkan kalimat, pemangkasan konten ke ukuran tetap dan mengekstrak label untuk tugas pemodelan bahasa bertopeng.
Mempersiapkan
Mari impor paket dan perpustakaan yang kita butuhkan terlebih dahulu.
pip install -q -U tensorflow-text
import tensorflow as tf
import tensorflow_text as text
import functools
Data kami berisi dua fitur teks dan kita bisa membuat contoh tf.data.Dataset
. Tujuan kami adalah untuk menciptakan sebuah fungsi yang kami dapat menyediakan Dataset.map()
dengan yang akan digunakan dalam pelatihan.
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.'>}
Tokenisasi
Langkah pertama kami adalah menjalankan preprocessing string dan tokenize dataset kami. Hal ini dapat dilakukan dengan menggunakan text.BertTokenizer
, yang merupakan text.Splitter
yang dapat tokenize kalimat ke subwords atau wordpieces untuk model yang Bert diberi kosakata yang dihasilkan dari algoritma Wordpiece . Anda dapat mempelajari lebih lanjut tentang tokenizers subword lain yang tersedia di TF.Text dari sini .
Kosakata dapat berasal dari pos pemeriksaan BERT yang dibuat sebelumnya, atau Anda dapat membuatnya sendiri pada data Anda sendiri. Untuk tujuan contoh ini, mari kita buat kosakata mainan:
_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
)
Mari membangun sebuah text.BertTokenizer
menggunakan kosakata di atas dan tokenize input teks ke dalam 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']]]>
Teks output dari text.BertTokenizer
memungkinkan kita melihat bagaimana teks sedang tokenized, tetapi model tersebut membutuhkan ID integer. Kita dapat mengatur token_out_type
param untuk tf.int64
untuk mendapatkan bilangan bulat ID (yang merupakan indeks ke kosa kata).
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
mengembalikan RaggedTensor
dengan bentuk [batch, num_tokens, num_wordpieces]
. Karena kita tidak perlu ekstra num_tokens
dimensi untuk kasus penggunaan kami saat ini, kita dapat menggabungkan dua dimensi terakhir untuk mendapatkan RaggedTensor
dengan bentuk [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]]>
Pemangkasan Konten
Input utama untuk BERT adalah gabungan dari dua kalimat. Namun, BERT membutuhkan input dalam ukuran dan bentuk yang tetap dan kami mungkin memiliki konten yang melebihi anggaran kami.
Kita bisa mengatasi ini dengan menggunakan text.Trimmer
untuk memangkas konten kami untuk ukuran yang telah ditentukan (setelah digabungkan sepanjang sumbu terakhir). Ada yang berbeda text.Trimmer
jenis yang memilih konten untuk melestarikan menggunakan algoritma yang berbeda. text.RoundRobinTrimmer
misalnya akan mengalokasikan kuota sama untuk setiap segmen tetapi mungkin trim ujung-ujung kalimat. text.WaterfallTrimmer
akan memangkas mulai dari akhir kalimat terakhir.
Sebagai contoh, kita akan menggunakan RoundRobinTrimmer
yang menyeleksi item dari setiap segmen dengan cara kiri ke kanan.
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
sekarang berisi segmen di mana jumlah elemen di batch adalah 8 unsur (ketika digabungkan sepanjang sumbu = -1).
Menggabungkan segmen
Sekarang kita telah segmen dipangkas, kita bisa menggabungkan mereka bersama-sama untuk mendapatkan satu RaggedTensor
. Bert menggunakan token khusus untuk menunjukkan awal ( [CLS]
) dan akhir segmen ( [SEP]
). Kami juga membutuhkan RaggedTensor
yang menunjukkan item dalam gabungan Tensor
milik yang segmen. Kita dapat menggunakan text.combine_segments()
untuk mendapatkan kedua hal ini Tensor
dengan token khusus dimasukkan.
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]]>)
Tugas Model Bahasa Bertopeng
Sekarang kita memiliki input dasar kita, kita bisa mulai untuk mengekstrak masukan yang dibutuhkan untuk "Masked LM dan Masking Prosedur" tugas dijelaskan dalam Bert: Pre-pelatihan dari Deep dua arah Transformers untuk Memahami Bahasa
Tugas model bahasa bertopeng memiliki dua sub-masalah untuk kita pikirkan: (1) item apa yang harus dipilih untuk penyembunyian dan (2) nilai apa yang diberikan?
Pemilihan Barang
Karena kita akan memilih untuk memilih item secara acak untuk masking, kita akan menggunakan text.RandomItemSelector
. RandomItemSelector
secara acak memilih item dalam sebuah topik batch untuk pembatasan yang diberikan ( max_selections_per_batch
, selection_rate
dan unselectable_ids
) dan kembali masker boolean yang menunjukkan item yang dipilih.
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]]>
Memilih Nilai Bertopeng
Metodologi yang dijelaskan kertas BERT asli untuk memilih nilai untuk masking adalah sebagai berikut:
Untuk mask_token_rate
waktu, mengganti item dengan [MASK]
tanda:
"my dog is hairy" -> "my dog is [MASK]"
Untuk random_token_rate
waktu, mengganti item dengan kata acak:
"my dog is hairy" -> "my dog is apple"
Untuk 1 - mask_token_rate - random_token_rate
waktu, menjaga item tidak berubah:
"my dog is hairy" -> "my dog is hairy."
text.MaskedValuesChooser
merangkum logika ini dan dapat digunakan untuk fungsi preprocessing kami. Berikut ini adalah contoh dari apa yang MaskValuesChooser
kembali diberi mask_token_rate
dari 80% dan standar 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]]>
Ketika dilengkapi dengan RaggedTensor
input, text.MaskValuesChooser
mengembalikan RaggedTensor
dari bentuk yang sama dengan baik _MASK_VALUE
(0), ID acak, atau id tidak berubah sama.
Menghasilkan Input untuk Tugas Model Bahasa Bertopeng
Sekarang bahwa kita memiliki RandomItemSelector
untuk membantu kita memilih item untuk masking dan text.MaskValuesChooser
untuk menetapkan nilai-nilai, kita dapat menggunakan text.mask_language_model()
untuk merakit semua masukan dari tugas ini untuk model Bert kami.
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.
Menyelam Mari lebih dalam dan memeriksa output dari mask_language_model()
. Output dari masked_token_ids
adalah:
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]]>
Ingat bahwa input kita dikodekan menggunakan kosakata. Jika kita decode masked_token_ids
menggunakan kosa kata kita, kita mendapatkan:
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]']]>
Perhatikan bahwa beberapa token wordpiece telah diganti dengan baik [MASK]
, [RANDOM]
atau nilai ID yang berbeda. masked_pos
keluaran memberi kita indeks (dalam batch masing-masing) dari token yang telah diganti.
masked_pos
<tf.RaggedTensor [[2, 5, 8, 10], [5, 11]]>
masked_lm_ids
memberi kita nilai asli dari token.
masked_lm_ids
<tf.RaggedTensor [[9, 11, 14, 13], [8, 29]]>
Kami kembali dapat memecahkan kode ID di sini untuk mendapatkan nilai yang dapat dibaca manusia.
tf.gather(_VOCAB, masked_lm_ids)
<tf.RaggedTensor [[b'##onge', b'##uare', b'an', b'##ven'], [b'##gers', b'office']]>
Masukan Model Padding
Sekarang bahwa kita memiliki semua input untuk model kami, langkah terakhir dalam preprocessing kami adalah untuk paket mereka ke dalam fixed 2-dimensi Tensor
s dengan padding dan juga menghasilkan topeng Tensor
menunjukkan nilai-nilai yang nilai pad. Kita dapat menggunakan text.pad_model_inputs()
untuk membantu kami dengan tugas ini.
# 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]])>}
Tinjauan
Mari kita tinjau apa yang kita miliki sejauh ini dan kumpulkan fungsi preprocessing kita. Inilah yang kami miliki:
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
Kami sebelumnya dibangun tf.data.Dataset
dan kita sekarang dapat menggunakan kami berkumpul preprocessing fungsi bert_pretrain_preprocess()
di Dataset.map()
. Ini memungkinkan kita untuk membuat saluran input untuk mengubah data string mentah kita menjadi input integer dan mengumpankan langsung ke model kita.
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]])>}
Tutorial Terkait
Teks Mengklasifikasikan dengan Bert - Sebuah tutorial tentang cara menggunakan model Bert pretrained teks mengklasifikasikan. Ini adalah tindak lanjut yang bagus sekarang karena Anda sudah terbiasa dengan cara melakukan praproses input yang digunakan oleh model BERT.
Tokenizing dengan TF Text - Tutorial merinci jenis tokenizers yang ada di TF.Text.
Penanganan Teks dengan
RaggedTensor
- panduan lengkap tentang cara membuat, menggunakan dan memanipulasiRaggedTensor
s.