Memuat Teks

Lihat di TensorFlow.org Jalankan di Google Colab Lihat source di GitHub Unduh notebook

Tutorial ini menyediakan sebuah contoh cara penggunaan tf.data.TextLineDataset untuk memuat file-file teks. TextLineDataset didesain untuk membuat sebuah dataset dari file teks, dimana setiap contoh adalah baris dari teks tersebut. Hal ini bermanfaat untuk data teks yang line-based (contohnya, puisi dan log error).

Dalam tutorial ini, kita akan menggunakan tiga translasi Inggris yang berbeda dari pekerjaan yang sama yaitu, Homer's Illiad, dan melatih sebuah model untuk identifikasi translator dari sebuah baris teks.

Pengaturan

from __future__ import absolute_import, division, print_function, unicode_literals

try:
  # %tensorflow_version only exists in Colab.
  %tensorflow_version 2.x
except Exception:
  pass
import tensorflow as tf

import tensorflow_datasets as tfds
import os

Tiga translasi teks dilakukan oleh:

File teks yang digunakan dalam tutorial ini sudah mengalami beberapa tahap preprocessing, kebanyakan prosesnya adalah menghilangkan beberapa bagian — header dan footer dari dokumen, jumlah baris, judul bab. Unduh file ringan ini di lokal.

DIRECTORY_URL = 'https://storage.googleapis.com/download.tensorflow.org/data/illiad/'
FILE_NAMES = ['cowper.txt', 'derby.txt', 'butler.txt']

for name in FILE_NAMES:
  text_dir = tf.keras.utils.get_file(name, origin=DIRECTORY_URL+name)

parent_dir = os.path.dirname(text_dir)

parent_dir
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/cowper.txt
819200/815980 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/derby.txt
811008/809730 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/illiad/butler.txt
811008/807992 [==============================] - 0s 0us/step

'/home/kbuilder/.keras/datasets'

Memuat teks menjadi dataset

Iterasi file, muat setiap file menjadi datasetnya masing-masing.

Setiap contoh perlu diberi label, gunakan tf.data.Dataset.map untuk menggunakan fungsi pemberi label pada setiap contoh. Hal ini akan melakukan iterasi pada setiap contoh dalam dataset, dan mengembalikan pasangan (example, label).

def labeler(example, index):
  return example, tf.cast(index, tf.int64)  

labeled_data_sets = []

for i, file_name in enumerate(FILE_NAMES):
  lines_dataset = tf.data.TextLineDataset(os.path.join(parent_dir, file_name))
  labeled_dataset = lines_dataset.map(lambda ex: labeler(ex, i))
  labeled_data_sets.append(labeled_dataset)

Gabungkan dataset yang sudah diberikan label menjadi sebuah dataset, kemudian lakukan shuffle terhadap dataset tersebut.

BUFFER_SIZE = 50000
BATCH_SIZE = 64
TAKE_SIZE = 5000
all_labeled_data = labeled_data_sets[0]
for labeled_dataset in labeled_data_sets[1:]:
  all_labeled_data = all_labeled_data.concatenate(labeled_dataset)

all_labeled_data = all_labeled_data.shuffle(
    BUFFER_SIZE, reshuffle_each_iteration=False)

Anda dapat menggunakan tf.data.Dataset.take dan print untuk melihat seperti apa bentuk dari (example, label). Properti numpy menunjukkan nilai dari masing-masing Tensor.

for ex in all_labeled_data.take(5):
  print(ex)
(<tf.Tensor: shape=(), dtype=string, numpy=b'He shook two spears, and with determined strides'>, <tf.Tensor: shape=(), dtype=int64, numpy=0>)
(<tf.Tensor: shape=(), dtype=string, numpy=b'And instant knew Minerva. Flashed her eyes'>, <tf.Tensor: shape=(), dtype=int64, numpy=0>)
(<tf.Tensor: shape=(), dtype=string, numpy=b'There will I hurl him, and ye all shall know'>, <tf.Tensor: shape=(), dtype=int64, numpy=1>)
(<tf.Tensor: shape=(), dtype=string, numpy=b'He shook two spears, and challenged to the fight'>, <tf.Tensor: shape=(), dtype=int64, numpy=0>)
(<tf.Tensor: shape=(), dtype=string, numpy=b'With vows of sacrifice, Achilles calls'>, <tf.Tensor: shape=(), dtype=int64, numpy=1>)

Melakukan enkode baris teks sebagai angka

Model machine learning bekerja dengan angka, bukan kata-kata, sehingga nilai dari string harus dikonversi terlebih dahulu menjadi angka. Untuk melakukan hal tersebut, ubah kata unik menjadi bilangan integer yang unik.

Membangun kosa kata

Pertama-tama, buat sebuah kosa kata dengan melakukan tokenisasi dari teks menjadi sebuah koleksi kata-kata unik. Terdapat beberapa cara untuk melakukan hal ini baik di TensorFlow maupun Python. Untuk tutorial ini:

  1. Iterasi setiap nilai numpy dari contoh.
  2. Gunakan tfds.features.text.Tokenizer untuk memisahkannya menjadi token.
  3. Kumpulkan token-token tersebut menjadi sebuah Python set, untuk menghilangkan duplikat.
  4. Peroleh ukuran dari kosa kata yang akan digunakan selanjutnya.
tokenizer = tfds.features.text.Tokenizer()

vocabulary_set = set()
for text_tensor, _ in all_labeled_data:
  some_tokens = tokenizer.tokenize(text_tensor.numpy())
  vocabulary_set.update(some_tokens)

vocab_size = len(vocabulary_set)
vocab_size
17178

Proses enkode contoh

Buat sebuah enkoder dengan menggunakan vocabulary_set kepada tfds.features.text.TokenTextEncoder. Method dari enkoder encodememerlukan string dan mengembalikan list bilangan integer.

encoder = tfds.features.text.TokenTextEncoder(vocabulary_set)

Anda dapat mencobanya pada satu baris untuk melihat bagaimana bentuk dari outputnya.

example_text = next(iter(all_labeled_data))[0].numpy()
print(example_text)
b'He shook two spears, and with determined strides'

encoded_example = encoder.encode(example_text)
print(encoded_example)
[6523, 297, 4797, 6827, 8339, 4461, 13989, 6003]

Sekarang gunakan enkoder tersebut pada dataset dengan cara membungkusnya dalam tf.py_function dan gunakan hasilnya pada method dataset map.

def encode(text_tensor, label):
  encoded_text = encoder.encode(text_tensor.numpy())
  return encoded_text, label

def encode_map_fn(text, label):
  return tf.py_function(encode, inp=[text, label], Tout=(tf.int64, tf.int64))

all_encoded_data = all_labeled_data.map(encode_map_fn)

Bagi dataset menjadi batch tes dan train

Gunakan tf.data.Dataset.take dan tf.data.Dataset.skip untuk membuat dataset tes berukuran kecil dan set training berukuran lebih besar.

Sebelum digunakan pada model, perlu dibuat batch untuk dataset. Biasanya, data-data dalam satu batch harus memiliki bentuk dan ukuran yang sama. Akan tetapi, data-data dalam dataset ini tidak memiliki ukuran yang sama — setiap baris dari teks memiliki jumlah kata yang berbeda. Oleh karena itu, gunakan tf.data.Dataset.padded_batch (daripada batch) untuk membuat setiap data memiliki ukuran yang sama.

train_data = all_encoded_data.skip(TAKE_SIZE).shuffle(BUFFER_SIZE)
train_data = train_data.padded_batch(BATCH_SIZE, padded_shapes=([-1],[]))

test_data = all_encoded_data.take(TAKE_SIZE)
test_data = test_data.padded_batch(BATCH_SIZE, padded_shapes=([-1],[]))

Sekarang, test_data dan train_data bukan merupakan koleksi dari pasangan (example, label), tetapi koleksi dari batch. Setiap batch adalah pasangan dari (beberapa data, beberapa label) yang direpresentasikan sebagai array.

Sebagai ilustrasi:

sample_text, sample_labels = next(iter(test_data))

sample_text[0], sample_labels[0]
(<tf.Tensor: shape=(15,), dtype=int64, numpy=
 array([ 6523,   297,  4797,  6827,  8339,  4461, 13989,  6003,     0,
            0,     0,     0,     0,     0,     0])>,
 <tf.Tensor: shape=(), dtype=int64, numpy=0>)

Karena kita telah menggunakan sebuah token encoding yang baru (nilai nol digunakan untuk padding), jumlah kosa kata bertambah satu.

vocab_size += 1

Membangun model

model = tf.keras.Sequential()

Layer pertama mengkonversi representasi integer menjadi dense vector embeddings. Lihat tutorial word embeddings untuk detail lebih lanjut.

model.add(tf.keras.layers.Embedding(vocab_size, 64))

Layer selanjutnya adalah layer Long Short-Term Memory, yang dapat membuat model memahami hubungan antara kata-kata. Sebuah wrapper dua arah dalam LSTM membantu model untuk mempelajari hubungan antara titik data dengan titik data lain yang ada setelah dan sebelumnya.

model.add(tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(64)))

Kita akhirnya memiliki sebuah series dari satu atau lebih densely connected layer, dengan layer terakhir sebagai output layer. Output layer menghasilkan probabilitas dari masing-masing label. Label dengan probabilitas tertinggi merupakan prediksi dari model.

# Satu atau lebih dense layer.
# Edit list pada baris `for` untuk bereksperimen dengan ukuran layer
for units in [64, 64]:
  model.add(tf.keras.layers.Dense(units, activation='relu'))

# layer output. Argumen pertama adalah jumlah label.
model.add(tf.keras.layers.Dense(3, activation='softmax'))

Akhirnya, kita lakukan kompilasi terhadap model. Untuk model dengan kategorisasi softmax, gunakan sparse_categorical_crossentropy pada loss function. Anda dapat mencoba optimizer yang lainnya, tetapi adam adalah optimizer yang paling umum digunaknan.

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Latih model

Model yang dilatih menggunakan data ini menghasilkan hasil yang cukup baik (sekitar 83%).

model.fit(train_data, epochs=3, validation_data=test_data)
Epoch 1/3
697/697 [==============================] - 9s 13ms/step - loss: 0.5213 - accuracy: 0.7473 - val_loss: 0.4021 - val_accuracy: 0.8152
Epoch 2/3
697/697 [==============================] - 8s 12ms/step - loss: 0.3026 - accuracy: 0.8698 - val_loss: 0.3712 - val_accuracy: 0.8250
Epoch 3/3
697/697 [==============================] - 8s 12ms/step - loss: 0.2279 - accuracy: 0.9017 - val_loss: 0.3803 - val_accuracy: 0.8336

<tensorflow.python.keras.callbacks.History at 0x7f367036d588>
eval_loss, eval_acc = model.evaluate(test_data)

print('\nEval loss: {:.3f}, Eval accuracy: {:.3f}'.format(eval_loss, eval_acc))
79/79 [==============================] - 1s 16ms/step - loss: 0.3803 - accuracy: 0.8336

Eval loss: 0.380, Eval accuracy: 0.834