Halaman ini diterjemahkan oleh Cloud Translation API.
Switch to English

Klasifikasi teks dasar

Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub Unduh buku catatan

Tutorial ini mendemonstrasikan klasifikasi teks mulai dari file teks biasa yang disimpan di disk. Anda akan melatih pengklasifikasi biner untuk melakukan analisis sentimen pada set data IMDB. Di akhir buku catatan, ada latihan untuk Anda coba, di mana Anda akan melatih pengklasifikasi multikelas untuk memprediksi tag untuk pertanyaan pemrograman di Stack Overflow.

import matplotlib.pyplot as plt
import os
import re
import shutil
import string
import tensorflow as tf

from tensorflow.keras import layers
from tensorflow.keras import losses
from tensorflow.keras import preprocessing
from tensorflow.keras.layers.experimental.preprocessing import TextVectorization
print(tf.__version__)
2.3.1

Analisis sentimen

Notebook ini melatih model analisis sentimen untuk mengklasifikasikan ulasan film sebagai positif atau negatif , berdasarkan teks ulasan. Ini adalah contoh klasifikasi biner —atau dua kelas —, jenis masalah pembelajaran mesin yang penting dan dapat diterapkan secara luas.

Anda akan menggunakan Kumpulan Data Ulasan Film Besar yang berisi teks 50.000 ulasan film dari Internet Movie Database . Ini dibagi menjadi 25.000 ulasan untuk pelatihan dan 25.000 ulasan untuk pengujian. Set pelatihan dan pengujian seimbang , artinya berisi ulasan positif dan negatif dalam jumlah yang sama.

Unduh dan jelajahi kumpulan data IMDB

Mari unduh dan ekstrak dataset, lalu jelajahi struktur direktori.

url = "https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz"

dataset = tf.keras.utils.get_file("aclImdb_v1.tar.gz", url,
                                    untar=True, cache_dir='.',
                                    cache_subdir='')

dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')
Downloading data from https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
84131840/84125825 [==============================] - 2s 0us/step

os.listdir(dataset_dir)
['train', 'test', 'README', 'imdb.vocab', 'imdbEr.txt']
train_dir = os.path.join(dataset_dir, 'train')
os.listdir(train_dir)
['labeledBow.feat',
 'unsupBow.feat',
 'urls_neg.txt',
 'pos',
 'neg',
 'urls_pos.txt',
 'unsup',
 'urls_unsup.txt']

Direktori aclImdb/train/pos dan aclImdb/train/neg berisi banyak file teks, yang masing-masing merupakan ulasan film tunggal. Mari kita lihat salah satunya.

sample_file = os.path.join(train_dir, 'pos/1181_9.txt')
with open(sample_file) as f:
  print(f.read())
Rachel Griffiths writes and directs this award winning short film. A heartwarming story about coping with grief and cherishing the memory of those we've loved and lost. Although, only 15 minutes long, Griffiths manages to capture so much emotion and truth onto film in the short space of time. Bud Tingwell gives a touching performance as Will, a widower struggling to cope with his wife's death. Will is confronted by the harsh reality of loneliness and helplessness as he proceeds to take care of Ruth's pet cow, Tulip. The film displays the grief and responsibility one feels for those they have loved and lost. Good cinematography, great direction, and superbly acted. It will bring tears to all those who have lost a loved one, and survived.

Muat set data

Selanjutnya, Anda akan memuat data dari disk dan menyiapkannya ke dalam format yang sesuai untuk pelatihan. Untuk melakukannya, Anda akan menggunakan utilitas text_dataset_from_directory yang membantu, yang mengharapkan struktur direktori sebagai berikut.

main_directory/
...class_a/
......a_text_1.txt
......a_text_2.txt
...class_b/
......b_text_1.txt
......b_text_2.txt

Untuk menyiapkan class_a data untuk klasifikasi biner, Anda memerlukan dua folder pada disk, sesuai dengan class_a dan class_b . Ini akan menjadi review film positif dan negatif, yang dapat ditemukan di aclImdb/train/pos dan aclImdb/train/neg . Karena kumpulan data IMDB berisi folder tambahan, Anda akan menghapusnya sebelum menggunakan utilitas ini.

remove_dir = os.path.join(train_dir, 'unsup')
shutil.rmtree(remove_dir)

Selanjutnya, Anda akan menggunakan utilitas text_dataset_from_directory untuk membuat tf.data.Dataset berlabel. tf.data adalah kumpulan alat yang ampuh untuk bekerja dengan data.

Saat menjalankan eksperimen machine learning, praktik terbaiknya adalah membagi kumpulan data Anda menjadi tiga bagian: latih , validasi , dan uji .

Dataset IMDB telah dibagi menjadi train dan test, tetapi tidak memiliki set validasi. Mari buat set validasi menggunakan pemisahan 80:20 dari data pelatihan dengan menggunakan argumen validation_split bawah ini.

batch_size = 32
seed = 42

raw_train_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/train', 
    batch_size=batch_size, 
    validation_split=0.2, 
    subset='training', 
    seed=seed)
Found 25000 files belonging to 2 classes.
Using 20000 files for training.

Seperti yang Anda lihat di atas, ada 25.000 contoh dalam folder pelatihan, yang 80% (atau 20.000) akan digunakan untuk pelatihan. Seperti yang akan Anda lihat sebentar lagi, Anda dapat melatih model dengan meneruskan kumpulan data langsung ke model.fit . Jika Anda baru tf.data , Anda juga dapat mengulang tf.data data dan mencetak beberapa contoh sebagai berikut.

for text_batch, label_batch in raw_train_ds.take(1):
  for i in range(3):
    print("Review", text_batch.numpy()[i])
    print("Label", label_batch.numpy()[i])
Review b'"Pandemonium" is a horror movie spoof that comes off more stupid than funny. Believe me when I tell you, I love comedies. Especially comedy spoofs. "Airplane", "The Naked Gun" trilogy, "Blazing Saddles", "High Anxiety", and "Spaceballs" are some of my favorite comedies that spoof a particular genre. "Pandemonium" is not up there with those films. Most of the scenes in this movie had me sitting there in stunned silence because the movie wasn\'t all that funny. There are a few laughs in the film, but when you watch a comedy, you expect to laugh a lot more than a few times and that\'s all this film has going for it. Geez, "Scream" had more laughs than this film and that was more of a horror film. How bizarre is that?<br /><br />*1/2 (out of four)'
Label 0
Review b"David Mamet is a very interesting and a very un-equal director. His first movie 'House of Games' was the one I liked best, and it set a series of films with characters whose perspective of life changes as they get into complicated situations, and so does the perspective of the viewer.<br /><br />So is 'Homicide' which from the title tries to set the mind of the viewer to the usual crime drama. The principal characters are two cops, one Jewish and one Irish who deal with a racially charged area. The murder of an old Jewish shop owner who proves to be an ancient veteran of the Israeli Independence war triggers the Jewish identity in the mind and heart of the Jewish detective.<br /><br />This is were the flaws of the film are the more obvious. The process of awakening is theatrical and hard to believe, the group of Jewish militants is operatic, and the way the detective eventually walks to the final violent confrontation is pathetic. The end of the film itself is Mamet-like smart, but disappoints from a human emotional perspective.<br /><br />Joe Mantegna and William Macy give strong performances, but the flaws of the story are too evident to be easily compensated."
Label 0
Review b'Great documentary about the lives of NY firefighters during the worst terrorist attack of all time.. That reason alone is why this should be a must see collectors item.. What shocked me was not only the attacks, but the"High Fat Diet" and physical appearance of some of these firefighters. I think a lot of Doctors would agree with me that,in the physical shape they were in, some of these firefighters would NOT of made it to the 79th floor carrying over 60 lbs of gear. Having said that i now have a greater respect for firefighters and i realize becoming a firefighter is a life altering job. The French have a history of making great documentary\'s and that is what this is, a Great Documentary.....'
Label 1

Perhatikan review berisi teks mentah (dengan tanda baca dan kadang-kadang tag HTML seperti <br/> ). Anda akan menunjukkan cara menanganinya di bagian berikut.

Labelnya adalah 0 atau 1. Untuk melihat mana dari berikut ini yang sesuai dengan ulasan film positif dan negatif, Anda dapat memeriksa properti class_names di kumpulan data.

print("Label 0 corresponds to", raw_train_ds.class_names[0])
print("Label 1 corresponds to", raw_train_ds.class_names[1])
Label 0 corresponds to neg
Label 1 corresponds to pos

Selanjutnya, Anda akan membuat validasi dan menguji dataset. Anda akan menggunakan sisa 5.000 tinjauan dari set pelatihan untuk validasi.

raw_val_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/train', 
    batch_size=batch_size, 
    validation_split=0.2, 
    subset='validation', 
    seed=seed)
Found 25000 files belonging to 2 classes.
Using 5000 files for validation.

raw_test_ds = tf.keras.preprocessing.text_dataset_from_directory(
    'aclImdb/test', 
    batch_size=batch_size)
Found 25000 files belonging to 2 classes.

Siapkan set data untuk pelatihan

Selanjutnya, Anda akan menstandarisasi, membuat token, dan membuat vektor data menggunakan lapisan preprocessing.TextVectorization bermanfaat.

Standardisasi mengacu pada pemrosesan awal teks, biasanya untuk menghapus tanda baca atau elemen HTML untuk menyederhanakan kumpulan data. Tokenisasi mengacu pada pemisahan string menjadi token (misalnya, membagi kalimat menjadi kata-kata individu, dengan memisahkan spasi). Vektorisasi mengacu pada pengubahan token menjadi angka sehingga dapat dimasukkan ke dalam jaringan saraf. Semua tugas ini dapat diselesaikan dengan lapisan ini.

Seperti yang Anda lihat di atas, ulasan berisi berbagai tag HTML seperti <br /> . Tag ini tidak akan dihapus oleh standardizer default di lapisan TextVectorization (yang mengubah teks menjadi huruf kecil dan menghapus tanda baca secara default, tetapi tidak menghapus HTML). Anda akan menulis fungsi standardisasi khusus untuk menghapus HTML.

def custom_standardization(input_data):
  lowercase = tf.strings.lower(input_data)
  stripped_html = tf.strings.regex_replace(lowercase, '<br />', ' ')
  return tf.strings.regex_replace(stripped_html,
                                  '[%s]' % re.escape(string.punctuation),
                                  '')

Selanjutnya, Anda akan membuat layer TextVectorization . Anda akan menggunakan lapisan ini untuk menstandarisasi, membuat token, dan membuat vektor data kita. Anda menyetel output_mode ke int untuk membuat indeks integer unik untuk setiap token.

Perhatikan bahwa Anda menggunakan fungsi pemisahan default, dan fungsi standardisasi kustom yang Anda tetapkan di atas. Anda juga akan menentukan beberapa konstanta untuk model, seperti sequence_length maksimum eksplisit, yang akan menyebabkan lapisan mengisi atau memotong urutan menjadi nilai sequence_length tepat.

max_features = 10000
sequence_length = 250

vectorize_layer = TextVectorization(
    standardize=custom_standardization,
    max_tokens=max_features,
    output_mode='int',
    output_sequence_length=sequence_length)

Selanjutnya, Anda akan memanggil adapt untuk menyesuaikan keadaan lapisan preprocessing ke dataset. Ini akan menyebabkan model membangun indeks string menjadi integer.

# Make a text-only dataset (without labels), then call adapt
train_text = raw_train_ds.map(lambda x, y: x)
vectorize_layer.adapt(train_text)

Mari buat sebuah fungsi untuk melihat hasil penggunaan lapisan ini untuk memproses beberapa data.

def vectorize_text(text, label):
  text = tf.expand_dims(text, -1)
  return vectorize_layer(text), label
# retrieve a batch (of 32 reviews and labels) from the dataset
text_batch, label_batch = next(iter(raw_train_ds))
first_review, first_label = text_batch[0], label_batch[0]
print("Review", first_review)
print("Label", raw_train_ds.class_names[first_label])
print("Vectorized review", vectorize_text(first_review, first_label))
Review tf.Tensor(b'Silent Night, Deadly Night 5 is the very last of the series, and like part 4, it\'s unrelated to the first three except by title and the fact that it\'s a Christmas-themed horror flick.<br /><br />Except to the oblivious, there\'s some obvious things going on here...Mickey Rooney plays a toymaker named Joe Petto and his creepy son\'s name is Pino. Ring a bell, anyone? Now, a little boy named Derek heard a knock at the door one evening, and opened it to find a present on the doorstep for him. Even though it said "don\'t open till Christmas", he begins to open it anyway but is stopped by his dad, who scolds him and sends him to bed, and opens the gift himself. Inside is a little red ball that sprouts Santa arms and a head, and proceeds to kill dad. Oops, maybe he should have left well-enough alone. Of course Derek is then traumatized by the incident since he watched it from the stairs, but he doesn\'t grow up to be some killer Santa, he just stops talking.<br /><br />There\'s a mysterious stranger lurking around, who seems very interested in the toys that Joe Petto makes. We even see him buying a bunch when Derek\'s mom takes him to the store to find a gift for him to bring him out of his trauma. And what exactly is this guy doing? Well, we\'re not sure but he does seem to be taking these toys apart to see what makes them tick. He does keep his landlord from evicting him by promising him to pay him in cash the next day and presents him with a "Larry the Larvae" toy for his kid, but of course "Larry" is not a good toy and gets out of the box in the car and of course, well, things aren\'t pretty.<br /><br />Anyway, eventually what\'s going on with Joe Petto and Pino is of course revealed, and as with the old story, Pino is not a "real boy". Pino is probably even more agitated and naughty because he suffers from "Kenitalia" (a smooth plastic crotch) so that could account for his evil ways. And the identity of the lurking stranger is revealed too, and there\'s even kind of a happy ending of sorts. Whee.<br /><br />A step up from part 4, but not much of one. Again, Brian Yuzna is involved, and Screaming Mad George, so some decent special effects, but not enough to make this great. A few leftovers from part 4 are hanging around too, like Clint Howard and Neith Hunter, but that doesn\'t really make any difference. Anyway, I now have seeing the whole series out of my system. Now if I could get some of it out of my brain. 4 out of 5.', shape=(), dtype=string)
Label neg
Vectorized review (<tf.Tensor: shape=(1, 250), dtype=int64, numpy=
array([[1287,  313, 2380,  313,  661,    7,    2,   52,  229,    5,    2,
         200,    3,   38,  170,  669,   29, 5492,    6,    2,   83,  297,
         549,   32,  410,    3,    2,  186,   12,   29,    4,    1,  191,
         510,  549,    6,    2, 8229,  212,   46,  576,  175,  168,   20,
           1, 5361,  290,    4,    1,  761,  969,    1,    3,   24,  935,
        2271,  393,    7,    1, 1675,    4, 3747,  250,  148,    4,  112,
         436,  761, 3529,  548,    4, 3633,   31,    2, 1331,   28, 2096,
           3, 2912,    9,    6,  163,    4, 1006,   20,    2,    1,   15,
          85,   53,  147,    9,  292,   89,  959, 2314,  984,   27,  762,
           6,  959,    9,  564,   18,    7, 2140,   32,   24, 1254,   36,
           1,   85,    3, 3298,   85,    6, 1410,    3, 1936,    2, 3408,
         301,  965,    7,    4,  112,  740, 1977,   12,    1, 2014, 2772,
           3,    4,  428,    3, 5177,    6,  512, 1254,    1,  278,   27,
         139,   25,  308,    1,  579,    5,  259, 3529,    7,   92, 8981,
          32,    2, 3842,  230,   27,  289,    9,   35,    2, 5712,   18,
          27,  144, 2166,   56,    6,   26,   46,  466, 2014,   27,   40,
        2745,  657,  212,    4, 1376, 3002, 7080,  183,   36,  180,   52,
         920,    8,    2, 4028,   12,  969,    1,  158,   71,   53,   67,
          85, 2754,    4,  734,   51,    1, 1611,  294,   85,    6,    2,
        1164,    6,  163,    4, 3408,   15,   85,    6,  717,   85,   44,
           5,   24, 7158,    3,   48,  604,    7,   11,  225,  384,   73,
          65,   21,  242,   18,   27,  120,  295,    6,   26,  667,  129,
        4028,  948,    6,   67,   48,  158,   93,    1]])>, <tf.Tensor: shape=(), dtype=int32, numpy=0>)

Seperti yang Anda lihat di atas, setiap token telah diganti dengan bilangan bulat. Anda bisa mencari token (string) yang sesuai dengan setiap bilangan bulat dengan memanggil .get_vocabulary() pada lapisan.

print("1287 ---> ",vectorize_layer.get_vocabulary()[1287])
print(" 313 ---> ",vectorize_layer.get_vocabulary()[313])
print('Vocabulary size: {}'.format(len(vectorize_layer.get_vocabulary())))
1287 --->  silent
 313 --->  night
Vocabulary size: 10000

Anda hampir siap untuk melatih model Anda. Sebagai langkah preprocessing terakhir, Anda akan menerapkan layer TextVectorization yang Anda buat sebelumnya ke dataset train, validation, dan test.

train_ds = raw_train_ds.map(vectorize_text)
val_ds = raw_val_ds.map(vectorize_text)
test_ds = raw_test_ds.map(vectorize_text)

Konfigurasi set data untuk kinerja

Ini adalah dua metode penting yang harus Anda gunakan saat memuat data untuk memastikan bahwa I / O tidak menjadi pemblokiran.

.cache() menyimpan data dalam memori setelah dimuat dari disk. Ini akan memastikan kumpulan data tidak menjadi hambatan saat melatih model Anda. Jika kumpulan data Anda terlalu besar untuk dimasukkan ke dalam memori, Anda juga dapat menggunakan metode ini untuk membuat cache pada disk yang berkinerja baik, yang lebih efisien untuk dibaca daripada banyak file kecil.

.prefetch() tumpang tindih dengan .prefetch() data dan eksekusi model saat pelatihan.

Anda dapat mempelajari lebih lanjut tentang kedua metode tersebut, serta cara menyimpan data ke dalam cache ke disk di panduan kinerja data .

AUTOTUNE = tf.data.experimental.AUTOTUNE

train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
test_ds = test_ds.cache().prefetch(buffer_size=AUTOTUNE)

Buat modelnya

Saatnya membuat jaringan saraf kita:

embedding_dim = 16
model = tf.keras.Sequential([
  layers.Embedding(max_features + 1, embedding_dim),
  layers.Dropout(0.2),
  layers.GlobalAveragePooling1D(),
  layers.Dropout(0.2),
  layers.Dense(1)])

model.summary()
Model: "sequential"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
embedding (Embedding)        (None, None, 16)          160016    
_________________________________________________________________
dropout (Dropout)            (None, None, 16)          0         
_________________________________________________________________
global_average_pooling1d (Gl (None, 16)                0         
_________________________________________________________________
dropout_1 (Dropout)          (None, 16)                0         
_________________________________________________________________
dense (Dense)                (None, 1)                 17        
=================================================================
Total params: 160,033
Trainable params: 160,033
Non-trainable params: 0
_________________________________________________________________

Lapisan tersebut ditumpuk secara berurutan untuk membangun pengklasifikasi:

  1. Lapisan pertama adalah lapisan Embedding . Lapisan ini mengambil ulasan yang dienkode dengan bilangan bulat dan mencari vektor penyematan untuk setiap indeks kata. Vektor-vektor ini dipelajari saat model berlatih. Vektor menambahkan dimensi ke larik keluaran. Dimensi yang dihasilkan adalah: (batch, sequence, embedding) . Untuk mempelajari lebih lanjut tentang embeddings, lihat tutorial kata embedding .
  2. Selanjutnya, lapisan GlobalAveragePooling1D mengembalikan vektor keluaran dengan panjang tetap untuk setiap contoh dengan merata-ratakan pada dimensi urutan. Ini memungkinkan model untuk menangani input dengan panjang variabel, dengan cara yang paling sederhana.
  3. Vektor keluaran dengan panjang tetap ini disalurkan melalui lapisan yang sepenuhnya terhubung ( Dense ) dengan 16 unit tersembunyi.
  4. Lapisan terakhir terhubung secara padat dengan satu simpul keluaran.

Fungsi kerugian dan pengoptimal

Model membutuhkan fungsi kerugian dan pengoptimal untuk pelatihan. Karena ini adalah masalah klasifikasi biner dan model mengeluarkan probabilitas (lapisan unit tunggal dengan aktivasi sigmoid), Anda akan menggunakan fungsi kerugian losses.BinaryCrossentropy .

Sekarang, konfigurasikan model untuk menggunakan pengoptimal dan fungsi kerugian:

model.compile(loss=losses.BinaryCrossentropy(from_logits=True),
              optimizer='adam',
              metrics=tf.metrics.BinaryAccuracy(threshold=0.0))

Latih modelnya

Anda akan melatih model dengan meneruskan objek dataset ke metode fit.

epochs = 10
history = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=epochs)
Epoch 1/10
625/625 [==============================] - 4s 6ms/step - loss: 0.6653 - binary_accuracy: 0.6843 - val_loss: 0.6151 - val_binary_accuracy: 0.7706
Epoch 2/10
625/625 [==============================] - 3s 4ms/step - loss: 0.5484 - binary_accuracy: 0.7991 - val_loss: 0.4977 - val_binary_accuracy: 0.8216
Epoch 3/10
625/625 [==============================] - 3s 5ms/step - loss: 0.4442 - binary_accuracy: 0.8438 - val_loss: 0.4197 - val_binary_accuracy: 0.8470
Epoch 4/10
625/625 [==============================] - 3s 4ms/step - loss: 0.3776 - binary_accuracy: 0.8665 - val_loss: 0.3735 - val_binary_accuracy: 0.8602
Epoch 5/10
625/625 [==============================] - 3s 4ms/step - loss: 0.3355 - binary_accuracy: 0.8784 - val_loss: 0.3449 - val_binary_accuracy: 0.8674
Epoch 6/10
625/625 [==============================] - 3s 4ms/step - loss: 0.3062 - binary_accuracy: 0.8870 - val_loss: 0.3261 - val_binary_accuracy: 0.8712
Epoch 7/10
625/625 [==============================] - 3s 4ms/step - loss: 0.2816 - binary_accuracy: 0.8958 - val_loss: 0.3129 - val_binary_accuracy: 0.8736
Epoch 8/10
625/625 [==============================] - 3s 4ms/step - loss: 0.2632 - binary_accuracy: 0.9038 - val_loss: 0.3034 - val_binary_accuracy: 0.8754
Epoch 9/10
625/625 [==============================] - 3s 4ms/step - loss: 0.2473 - binary_accuracy: 0.9104 - val_loss: 0.2965 - val_binary_accuracy: 0.8766
Epoch 10/10
625/625 [==============================] - 3s 4ms/step - loss: 0.2323 - binary_accuracy: 0.9156 - val_loss: 0.2920 - val_binary_accuracy: 0.8778

Evaluasi modelnya

Mari kita lihat performa model. Dua nilai akan dikembalikan. Loss (angka yang mewakili kesalahan kita, nilai yang lebih rendah lebih baik), dan akurasi.

loss, accuracy = model.evaluate(test_ds)

print("Loss: ", loss)
print("Accuracy: ", accuracy)
782/782 [==============================] - 3s 4ms/step - loss: 0.3104 - binary_accuracy: 0.8735
Loss:  0.3103727698326111
Accuracy:  0.8734800219535828

Pendekatan yang cukup naif ini mencapai akurasi sekitar 86%.

Buat plot akurasi dan kerugian seiring waktu

model.fit() mengembalikan objek History yang berisi kamus dengan semua yang terjadi selama pelatihan:

history_dict = history.history
history_dict.keys()
dict_keys(['loss', 'binary_accuracy', 'val_loss', 'val_binary_accuracy'])

Ada empat entri: satu untuk setiap metrik yang dipantau selama pelatihan dan validasi. Anda dapat menggunakannya untuk merencanakan kerugian pelatihan dan validasi untuk perbandingan, serta keakuratan pelatihan dan validasi:

acc = history_dict['binary_accuracy']
val_acc = history_dict['val_binary_accuracy']
loss = history_dict['loss']
val_loss = history_dict['val_loss']

epochs = range(1, len(acc) + 1)

# "bo" is for "blue dot"
plt.plot(epochs, loss, 'bo', label='Training loss')
# b is for "solid blue line"
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()

plt.show()

png

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')

plt.show()

png

Dalam plot ini, titik-titik mewakili kerugian dan akurasi pelatihan, dan garis padat adalah kerugian dan akurasi validasi.

Perhatikan kerugian pelatihan berkurang dengan setiap epoch dan akurasi pelatihan meningkat dengan setiap epoch. Ini diharapkan saat menggunakan pengoptimalan penurunan gradien — ini harus meminimalkan kuantitas yang diinginkan pada setiap iterasi.

Ini bukan kasus kerugian validasi dan akurasi — tampaknya memuncak sebelum akurasi pelatihan. Ini adalah contoh overfitting: model berperforma lebih baik pada data pelatihan daripada pada data yang belum pernah dilihat sebelumnya. Setelah titik ini, model mengoptimalkan secara berlebihan dan mempelajari representasi khusus untuk data pelatihan yang tidak digeneralisasi untuk data pengujian.

Untuk kasus khusus ini, Anda dapat mencegah overfitting hanya dengan menghentikan pelatihan saat akurasi validasi tidak lagi meningkat. Salah satu cara untuk melakukannya adalah dengan menggunakan callback EarlyStopping .

Ekspor modelnya

Pada kode di atas, Anda menerapkan layer TextVectorization ke dataset sebelum memasukkan teks ke model. Jika Anda ingin membuat model Anda mampu memproses string mentah (misalnya, untuk menyederhanakan penerapannya), Anda dapat menyertakan lapisan TextVectorization di dalam model Anda. Untuk melakukannya, Anda dapat membuat model baru menggunakan bobot yang baru saja Anda latih.

export_model = tf.keras.Sequential([
  vectorize_layer,
  model,
  layers.Activation('sigmoid')
])

export_model.compile(
    loss=losses.BinaryCrossentropy(from_logits=False), optimizer="adam", metrics=['accuracy']
)

# Test it with `raw_test_ds`, which yields raw strings
loss, accuracy = export_model.evaluate(raw_test_ds)
print(accuracy)
782/782 [==============================] - 4s 5ms/step - loss: 0.3104 - accuracy: 0.8735
0.8734800219535828

Inferensi pada data baru

Untuk mendapatkan prediksi untuk contoh baru, Anda cukup memanggil model.predict() .

examples = [
  "The movie was great!",
  "The movie was okay.",
  "The movie was terrible..."
]

export_model.predict(examples)
array([[0.6287589 ],
       [0.45188135],
       [0.3664309 ]], dtype=float32)

Menyertakan logika pemrosesan awal teks di dalam model Anda memungkinkan Anda mengekspor model untuk produksi yang menyederhanakan penerapan, dan mengurangi potensi kemiringan kereta / pengujian .

Ada perbedaan kinerja yang perlu diingat saat memilih tempat untuk menerapkan lapisan TextVectorization. Menggunakannya di luar model memungkinkan Anda melakukan pemrosesan CPU asinkron dan buffering data Anda saat melatih GPU. Jadi, jika Anda melatih model Anda pada GPU, Anda mungkin ingin menggunakan opsi ini untuk mendapatkan kinerja terbaik saat mengembangkan model Anda, lalu beralih ke menyertakan lapisan TextVectorization di dalam model Anda ketika Anda siap untuk mempersiapkan penerapan .

Kunjungi tutorial ini untuk mempelajari lebih lanjut tentang menyimpan model.

Latihan: klasifikasi multikelas pada pertanyaan Stack Overflow

Tutorial ini menunjukkan cara melatih pengklasifikasi biner dari awal pada set data IMDB. Sebagai latihan, Anda dapat memodifikasi notebook ini untuk melatih pengklasifikasi multikelas untuk memprediksi tag pertanyaan pemrograman di Stack Overflow .

Kami telah menyiapkan kumpulan data untuk Anda gunakan yang berisi tubuh dari beberapa ribu pertanyaan pemrograman (misalnya, "Bagaimana cara mengurutkan kamus berdasarkan nilai dengan Python?") Yang dikirim ke Stack Overflow. Masing-masing diberi label dengan tepat satu tag (baik Python, CSharp, JavaScript, atau Java). Tugas Anda adalah mengambil pertanyaan sebagai masukan, dan memprediksi tag yang sesuai, dalam hal ini, Python.

Kumpulan data yang akan Anda tangani berisi beberapa ribu pertanyaan yang diambil dari kumpulan data Stack Overflow publik yang jauh lebih besar di BigQuery , yang berisi lebih dari 17 juta postingan.

Setelah mengunduh kumpulan data, Anda akan menemukannya memiliki struktur direktori yang mirip dengan kumpulan data IMDB yang Anda gunakan sebelumnya:

train/
...python/
......0.txt
......1.txt
...javascript/
......0.txt
......1.txt
...csharp/
......0.txt
......1.txt
...java/
......0.txt
......1.txt

Untuk menyelesaikan latihan ini, Anda harus memodifikasi notebook ini agar berfungsi dengan set data Stack Overflow dengan melakukan modifikasi berikut:

  1. Di bagian atas buku catatan Anda, perbarui kode yang mengunduh kumpulan data IMDB dengan kode untuk mengunduh kumpulan data Stack Overflow yang telah kami persiapkan. Karena set data Stack Overflow memiliki struktur direktori yang serupa, Anda tidak perlu melakukan banyak modifikasi.

  2. Ubah lapisan terakhir model Anda untuk membaca Dense(4) , karena sekarang ada empat kelas keluaran.

  3. Saat Anda mengompilasi model Anda, ubah kerugian menjadi SparseCategoricalCrossentropy . Ini adalah fungsi kerugian yang benar untuk digunakan untuk masalah klasifikasi multikelas, jika label untuk setiap kelas adalah bilangan bulat (dalam kasus kami, label dapat berupa 0, 1 , 2 , atau 3 ).

  4. Setelah perubahan ini selesai, Anda akan dapat melatih pengklasifikasi multikelas.

Jika Anda mengalami kebuntuan, Anda dapat menemukan solusinya di sini .

Belajar lebih banyak

Tutorial ini memperkenalkan klasifikasi teks dari awal. Untuk mempelajari lebih lanjut tentang alur kerja klasifikasi teks secara umum, kami merekomendasikan untuk membaca panduan ini dari Pengembang Google.

# MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.