Diese Seite wurde von der Cloud Translation API übersetzt.
Switch to English

Grundlegende Textklassifizierung

Ansicht auf TensorFlow.org In Google Colab ausführen Quelle auf GitHub anzeigen Notizbuch herunterladen

Dieses Tutorial zeigt die Textklassifizierung ausgehend von einfachen Textdateien, die auf der Festplatte gespeichert sind. Sie trainieren einen binären Klassifikator, um eine Stimmungsanalyse für einen IMDB-Datensatz durchzuführen. Am Ende des Notizbuchs können Sie eine Übung ausprobieren, in der Sie einen Klassifikator für mehrere Klassen trainieren, um das Tag für eine Programmierfrage zum Stapelüberlauf vorherzusagen.

 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.0

Stimmungsanalyse

Dieses Notizbuch trainiert ein Stimmungsanalysemodell, um Filmkritiken basierend auf dem Text der Rezension als positiv oder negativ zu klassifizieren. Dies ist ein Beispiel für eine binäre oder Zwei-Klassen-Klassifizierung, eine wichtige und weit verbreitete Art von maschinellem Lernproblem.

Sie verwenden das Large Movie Review Dataset , das den Text von 50.000 Filmkritiken aus der Internet Movie Database enthält . Diese sind in 25.000 Bewertungen für Schulungen und 25.000 Bewertungen für Tests unterteilt. Die Trainings- und Testsätze sind ausgewogen , dh sie enthalten die gleiche Anzahl positiver und negativer Bewertungen.

Laden Sie den IMDB-Datensatz herunter und erkunden Sie ihn

Lassen Sie uns den Datensatz herunterladen und extrahieren und dann die Verzeichnisstruktur untersuchen.

 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 [==============================] - 7s 0us/step

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

Die aclImdb/train/pos und aclImdb/train/neg enthalten viele Textdateien, von denen jede eine einzelne Filmkritik ist. Werfen wir einen Blick auf einen von ihnen.

 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.

Laden Sie den Datensatz

Als Nächstes laden Sie die Daten von der Festplatte und bereiten sie in einem für das Training geeigneten Format vor. Dazu verwenden Sie das hilfreiche Dienstprogramm text_dataset_from_directory , das eine Verzeichnisstruktur wie folgt erwartet.

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

Um ein Dataset für die binäre Klassifizierung vorzubereiten, benötigen Sie zwei Ordner auf der Festplatte, die class_a und class_b . Dies sind die positiven und negativen Filmkritiken, die in aclImdb/train/pos und aclImdb/train/neg . Da das IMDB-Dataset zusätzliche Ordner enthält, werden Sie diese entfernen, bevor Sie dieses Dienstprogramm verwenden.

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

Als Nächstes verwenden Sie das Dienstprogramm text_dataset_from_directory , um ein beschriftetes tf.data.Dataset zu erstellen. tf.data ist eine leistungsstarke Sammlung von Tools zum Arbeiten mit Daten.

Wenn Sie ein Experiment zum maschinellen Lernen ausführen, empfiehlt es sich, Ihren Datensatz in drei Teile zu unterteilen: Trainieren , Validieren und Testen .

Der IMDB-Datensatz wurde bereits in Zug und Test unterteilt, es fehlt jedoch ein Validierungssatz. Erstellen wir einen Validierungssatz unter Verwendung einer 80: 20-Aufteilung der Trainingsdaten, indem wir das folgende Argument validation_split .

 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.

Wie Sie oben sehen können, enthält der Schulungsordner 25.000 Beispiele, von denen Sie 80% (oder 20.000) für das Training verwenden. Wie Sie gleich sehen werden, können Sie ein Modell trainieren, indem Sie einen Datensatz direkt an model.fit . Wenn Sie mit tf.data , können Sie das Dataset auch tf.data und einige Beispiele wie folgt ausdrucken.

 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

Beachten Sie, dass die Überprüfungen Rohtext enthalten (mit Interpunktion und gelegentlichen HTML-Tags wie <br/> ). Im folgenden Abschnitt erfahren Sie, wie Sie damit umgehen.

Die Beschriftungen sind 0 oder 1. Um class_names welche davon positiven und negativen Filmkritiken entsprechen, können Sie die Eigenschaft class_names im Dataset überprüfen.

 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

Als Nächstes erstellen Sie einen Validierungs- und Testdatensatz. Sie werden die verbleibenden 5.000 Bewertungen aus dem Trainingssatz zur Validierung verwenden.

 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.

Bereiten Sie den Datensatz für das Training vor

Als Nächstes standardisieren, tokenisieren und vektorisieren Sie die Daten mithilfe der hilfreichen Ebene " preprocessing.TextVectorization .

Standardisierung bezieht sich auf die Vorverarbeitung des Textes, normalerweise um Interpunktion oder HTML-Elemente zu entfernen, um das Dataset zu vereinfachen. Tokenisierung bezieht sich auf das Aufteilen von Zeichenfolgen in Token (z. B. Aufteilen eines Satzes in einzelne Wörter durch Aufteilen in Leerzeichen). Vektorisierung bezieht sich auf die Umwandlung von Token in Zahlen, damit sie in ein neuronales Netzwerk eingespeist werden können. Alle diese Aufgaben können mit dieser Ebene ausgeführt werden.

Wie Sie oben gesehen haben, enthalten die Bewertungen verschiedene HTML-Tags wie <br /> . Diese Tags werden vom Standardstandardisierer in der TextVectorization Ebene (die Text in Lowecase konvertiert und Interpunktion standardmäßig entfernt, HTML jedoch nicht entfernt) nicht entfernt. Sie schreiben eine benutzerdefinierte Standardisierungsfunktion, um den HTML-Code zu entfernen.

 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),
                                  '')
 

Als Nächstes erstellen Sie eine TextVectorization Ebene. Mit dieser Ebene können Sie unsere Daten standardisieren, tokenisieren und vektorisieren. Sie setzen den output_mode auf int , um eindeutige Ganzzahlindizes für jedes Token zu erstellen.

Beachten Sie, dass Sie die Standard-Split-Funktion und die oben definierte benutzerdefinierte Standardisierungsfunktion verwenden. Sie definieren auch einige Konstanten für das Modell, z. B. eine explizite maximale sequence_length , die bewirkt, dass die Ebene Sequenzen auf exakte sequence_length auffüllt oder abschneidet.

 max_features = 10000
sequence_length = 250

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

Als Nächstes rufen Sie adapt auf, um den Status der Vorverarbeitungsebene an das Dataset adapt . Dadurch erstellt das Modell einen Index von Zeichenfolgen zu Ganzzahlen.

 # 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)
 

Erstellen Sie eine Funktion, um das Ergebnis der Verwendung dieser Ebene zur Vorverarbeitung einiger Daten anzuzeigen.

 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>)

Wie Sie oben sehen können, wurde jedes Token durch eine Ganzzahl ersetzt. Sie können das Token (Zeichenfolge), dem jede Ganzzahl entspricht, .get_vocabulary() indem Sie .get_vocabulary() auf der Ebene aufrufen.

 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

Sie sind fast bereit, Ihr Modell zu trainieren. Als letzten Vorverarbeitungsschritt wenden Sie die zuvor erstellte TextVectorization-Ebene auf den Zug-, Validierungs- und Testdatensatz an.

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

Konfigurieren Sie das Dataset für die Leistung

Dies sind zwei wichtige Methoden, die Sie beim Laden von Daten verwenden sollten, um sicherzustellen, dass E / A nicht blockiert werden.

.cache() speichert Daten im Speicher, nachdem sie von der Festplatte geladen wurden. Dadurch wird sichergestellt, dass der Datensatz beim Training Ihres Modells nicht zu einem Engpass wird. Wenn Ihr Dataset zu groß ist, um in den Speicher zu passen, können Sie mit dieser Methode auch einen performanten Cache auf der Festplatte erstellen, der effizienter zu lesen ist als viele kleine Dateien.

.prefetch() überlappt die Datenvorverarbeitung und die Modellausführung während des Trainings.

Weitere Informationen zu beiden Methoden sowie zum Zwischenspeichern von Daten auf der Festplatte finden Sie im Handbuch zur Datenleistung .

 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)
 

Erstellen Sie das Modell

Es ist Zeit, unser neuronales Netzwerk aufzubauen:

 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
_________________________________________________________________

Die Ebenen werden nacheinander gestapelt, um den Klassifikator zu erstellen:

  1. Die erste Ebene ist eine Embedding . Diese Ebene verwendet die ganzzahlig codierten Überprüfungen und sucht für jeden Wortindex nach einem Einbettungsvektor. Diese Vektoren werden als Modellzüge gelernt. Die Vektoren fügen dem Ausgabearray eine Dimension hinzu. Die resultierenden Dimensionen sind: (batch, sequence, embedding) . Weitere Informationen zu Einbettungen finden Sie im Tutorial zum Einbetten von Wörtern .
  2. Als nächstes gibt eine GlobalAveragePooling1D Ebene für jedes Beispiel einen GlobalAveragePooling1D fester Länge zurück, indem über die Sequenzdimension gemittelt wird. Auf diese Weise kann das Modell Eingaben variabler Länge auf einfachste Weise verarbeiten.
  3. Dieser Ausgabevektor fester Länge wird durch eine vollständig verbundene ( Dense ) Schicht mit 16 verborgenen Einheiten geleitet.
  4. Die letzte Schicht ist dicht mit einem einzelnen Ausgangsknoten verbunden.

Verlustfunktion und Optimierer

Ein Modell benötigt eine Verlustfunktion und einen Optimierer für das Training. Da dies ein binäres Klassifizierungsproblem ist und das Modell eine Wahrscheinlichkeit ausgibt (eine Schicht mit einer Einheit mit einer Sigmoid-Aktivierung), verwenden losses.BinaryCrossentropy Verluste. losses.BinaryCrossentropy Verlustfunktion.

Konfigurieren Sie das Modell nun so, dass es einen Optimierer und eine Verlustfunktion verwendet:

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

Trainiere das Modell

Sie trainieren das Modell, indem Sie das dataset Objekt an die Anpassungsmethode übergeben.

 epochs = 10
history = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=epochs)
 
Epoch 1/10
625/625 [==============================] - 3s 5ms/step - loss: 0.6632 - binary_accuracy: 0.6931 - val_loss: 0.6135 - val_binary_accuracy: 0.7752
Epoch 2/10
625/625 [==============================] - 3s 4ms/step - loss: 0.5472 - binary_accuracy: 0.8003 - val_loss: 0.4968 - val_binary_accuracy: 0.8220
Epoch 3/10
625/625 [==============================] - 3s 4ms/step - loss: 0.4434 - binary_accuracy: 0.8459 - val_loss: 0.4187 - val_binary_accuracy: 0.8486
Epoch 4/10
625/625 [==============================] - 3s 4ms/step - loss: 0.3770 - binary_accuracy: 0.8660 - val_loss: 0.3726 - val_binary_accuracy: 0.8622
Epoch 5/10
625/625 [==============================] - 2s 4ms/step - loss: 0.3349 - binary_accuracy: 0.8786 - val_loss: 0.3442 - val_binary_accuracy: 0.8678
Epoch 6/10
625/625 [==============================] - 2s 4ms/step - loss: 0.3046 - binary_accuracy: 0.8889 - val_loss: 0.3253 - val_binary_accuracy: 0.8722
Epoch 7/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2807 - binary_accuracy: 0.8977 - val_loss: 0.3118 - val_binary_accuracy: 0.8726
Epoch 8/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2609 - binary_accuracy: 0.9046 - val_loss: 0.3026 - val_binary_accuracy: 0.8762
Epoch 9/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2443 - binary_accuracy: 0.9123 - val_loss: 0.2961 - val_binary_accuracy: 0.8774
Epoch 10/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2309 - binary_accuracy: 0.9163 - val_loss: 0.2915 - val_binary_accuracy: 0.8804

Bewerten Sie das Modell

Mal sehen, wie das Modell funktioniert. Es werden zwei Werte zurückgegeben. Verlust (eine Zahl, die unseren Fehler darstellt, niedrigere Werte sind besser) und Genauigkeit.

 loss, accuracy = model.evaluate(test_ds)

print("Loss: ", loss)
print("Accuracy: ", accuracy)
 
782/782 [==============================] - 2s 3ms/step - loss: 0.3097 - binary_accuracy: 0.8740
Loss:  0.30967268347740173
Accuracy:  0.8740400075912476

Dieser ziemlich naive Ansatz erreicht eine Genauigkeit von etwa 86%.

Erstellen Sie ein Diagramm der Genauigkeit und des Verlusts im Laufe der Zeit

model.fit() gibt ein History - Objekt , das ein Wörterbuch mit allem enthält , die während des Trainings passiert:

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

Es gibt vier Einträge: einen für jede überwachte Metrik während des Trainings und der Validierung. Sie können diese verwenden, um den Trainings- und Validierungsverlust zum Vergleich sowie die Trainings- und Validierungsgenauigkeit darzustellen:

 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

In diesem Diagramm stellen die Punkte den Trainingsverlust und die Genauigkeit dar, und die durchgezogenen Linien geben den Validierungsverlust und die Genauigkeit an.

Beachten Sie, dass der Trainingsverlust mit jeder Epoche abnimmt und die Trainingsgenauigkeit mit jeder Epoche zunimmt . Dies wird erwartet, wenn eine Gradientenabstiegsoptimierung verwendet wird - sie sollte die gewünschte Menge bei jeder Iteration minimieren.

Dies ist nicht der Fall für den Validierungsverlust und die Validierungsgenauigkeit - sie scheinen vor der Trainingsgenauigkeit ihren Höhepunkt zu erreichen. Dies ist ein Beispiel für eine Überanpassung: Das Modell arbeitet mit den Trainingsdaten besser als mit Daten, die es noch nie zuvor gesehen hat. Nach diesem Punkt optimiert das Modell die für die Trainingsdaten spezifischen Darstellungen, die nicht auf Testdaten verallgemeinert werden , und lernt sie übermäßig .

In diesem speziellen Fall können Sie eine Überanpassung verhindern, indem Sie das Training einfach abbrechen, wenn die Validierungsgenauigkeit nicht mehr zunimmt. Eine Möglichkeit hierfür ist die Verwendung des EarlyStopping-Rückrufs .

Exportieren Sie das Modell

Im obigen Code haben Sie die TextVectorization Ebene auf das Dataset angewendet, bevor Sie dem Modell Text TextVectorization haben. Wenn Sie Ihr Modell in die Lage TextVectorization , TextVectorization zu verarbeiten (z. B. um die Bereitstellung zu vereinfachen), können Sie die TextVectorization in Ihr Modell aufnehmen. Dazu können Sie mit den soeben trainierten Gewichten ein neues Modell erstellen.

 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 [==============================] - 3s 4ms/step - loss: 0.3097 - accuracy: 0.8740
0.8740400075912476

Wenn Sie die Textvorverarbeitungslogik in Ihr Modell aufnehmen, können Sie ein Modell für die Produktion exportieren, das die Bereitstellung vereinfacht und das Potenzial für Zug- / Testversatz verringert.

Bei der Auswahl des Anwendungsorts für Ihre TextVectorization-Ebene ist ein Leistungsunterschied zu berücksichtigen. Wenn Sie es außerhalb Ihres Modells verwenden, können Sie beim Training auf der GPU eine asynchrone CPU-Verarbeitung und Pufferung Ihrer Daten durchführen. Wenn Sie Ihr Modell auf der GPU trainieren, möchten Sie wahrscheinlich diese Option verwenden, um die beste Leistung bei der Entwicklung Ihres Modells zu erzielen, und dann die TextVectorization-Ebene in Ihr Modell aufnehmen, wenn Sie sich auf die Bereitstellung vorbereiten möchten .

Besuchen Sie dieses Tutorial , um mehr über das Speichern von Modellen zu erfahren.

Übung: Klassifizierung mehrerer Klassen bei Fragen zum Stapelüberlauf

Dieses Tutorial zeigte, wie ein binärer Klassifikator im IMDB-Dataset von Grund auf trainiert wird. Als Übung können Sie dieses Notebook ändern , um eine mehrklassige Klassifikator trainieren den Tag einer Programmier Frage auf vorherzusagen Stack - Überlauf .

Wir haben für Sie einen Datensatz vorbereitet, der mehrere tausend Programmierfragen enthält (z. B. "Wie kann ein Wörterbuch in Python nach Wert sortiert werden?"), Die an Stack Overflow gesendet werden. Jedes dieser Tags ist mit genau einem Tag gekennzeichnet (entweder Python, CSharp, JavaScript oder Java). Ihre Aufgabe ist es, eine Frage als Eingabe zu nehmen und das entsprechende Tag, in diesem Fall Python, vorherzusagen.

Das Dataset, mit dem Sie arbeiten, enthält mehrere tausend Fragen, die aus dem viel größeren öffentlichen Stack Overflow-Dataset in BigQuery extrahiert wurden , das mehr als 17 Millionen Beiträge enthält.

Nach dem Herunterladen des Datasets haben Sie eine ähnliche Verzeichnisstruktur wie das IMDB-Dataset, mit dem Sie zuvor gearbeitet haben:

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

Um diese Übung abzuschließen, sollten Sie dieses Notizbuch so ändern, dass es mit dem Stapelüberlauf-Dataset funktioniert, indem Sie die folgenden Änderungen vornehmen:

  1. Aktualisieren Sie oben in Ihrem Notizbuch den Code, der das IMDB-Dataset herunterlädt, mit Code, um das von uns vorbereitete Stack Overflow-Dataset herunterzuladen. Da das Stack Overflow-Dataset eine ähnliche Verzeichnisstruktur aufweist, müssen Sie nicht viele Änderungen vornehmen.

  2. Ändern Sie die letzte Ebene Ihres Modells, um Dense(4) zu lesen, da es jetzt vier Ausgabeklassen gibt.

  3. Ändern Sie beim Kompilieren Ihres Modells den Verlust in SparseCategoricalCrossentropy . Dies ist die richtige Verlustfunktion für ein Klassifizierungsproblem mit mehreren Klassen, wenn die Beschriftungen für jede Klasse Ganzzahlen sind (in unserem Fall können sie 0, 1 , 2 oder 3 sein ).

  4. Sobald diese Änderungen abgeschlossen sind, können Sie einen Klassifikator für mehrere Klassen trainieren.

Wenn Sie nicht weiterkommen, können Sie eine Lösung finden hier .

Mehr lernen

In diesem Tutorial wurde die Textklassifizierung von Grund auf neu eingeführt. Um mehr über den Textklassifizierungsworkflow im Allgemeinen zu erfahren, empfehlen wir, dieses Handbuch von Google Developers zu lesen.

 
#
# 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.