Grundlegende Textklassifizierung

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

Dieses Tutorial demonstriert die Textklassifizierung ausgehend von Nur-Text-Dateien, die auf der Festplatte gespeichert sind. Sie trainieren einen binären Klassifikator, um eine Sentimentanalyse für ein IMDB-Dataset durchzuführen. Am Ende des Notebooks gibt es eine Übung, in der Sie einen Klassifikator mit mehreren Klassen trainieren, um das Tag für eine Programmierfrage auf Stack Overflow 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.5.0

Stimmungsanalyse

Dieses Notebook bildet eine Stimmungsanalyse Modell zum Klassifizieren Filmkritiken als positiv oder negativ, basierend auf dem Text der Überprüfung. Dies ist ein Beispiel von binären -oder Zweiklasseneinstufung, eine wichtige und breit anwendbare Art des maschinellen Lernens Problem.

Sie finden Verwendung Large Movie Review Datensatz , der den Text von 50.000 Filmberichte aus der enthält Internet Movie Database . Diese verteilen sich auf 25.000 Bewertungen für Schulungen und 25.000 Bewertungen für Tests. Die Trainings- und Testsätze werden ausgeglichen, das heißt , sie eine gleiche Anzahl von positiven und negativen Bewertungen enthalten.

Laden Sie den IMDB-Datensatz herunter und erkunden Sie ihn

Lassen Sie uns das Dataset 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", 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)
['train', 'imdb.vocab', 'README', 'imdbEr.txt', 'test']
train_dir = os.path.join(dataset_dir, 'train')
os.listdir(train_dir)
['pos',
 'neg',
 'urls_neg.txt',
 'urls_pos.txt',
 'unsup',
 'unsupBow.feat',
 'labeledBow.feat',
 'urls_unsup.txt']

Die aclImdb/train/pos und aclImdb/train/neg Verzeichnisse enthalten viele Textdateien, von denen jede eine einzige Filmkritik. 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 ein für das Training geeignetes Format vor. Um dies zu tun, werden Sie die hilfreich verwenden text_dataset_from_directory Dienstprogramm, das eine Verzeichnisstruktur erwartet wie folgt.

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

Um einen Datensatz für binäre Klassifizierung vorzubereiten, benötigen Sie zwei Ordner auf der Festplatte, entsprechend class_a und class_b . Diese werden die positiven und negativen Filmkritiken sein, die gefunden werden kann aclImdb/train/pos und aclImdb/train/neg . Da der IMDB-Datensatz 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 werden Sie die Verwendung text_dataset_from_directory Dienstprogramm einen markierten erstellen tf.data.Dataset . tf.data ist eine leistungsstarke Sammlung von Werkzeugen für die Arbeit mit Daten.

Wenn ein maschinelles Lernen Experiment läuft, ist es eine bewährte Methode , Ihre Daten - Set in drei Splits zu teilen: Zug , Validierung und Test .

Der IMDB-Datensatz wurde bereits in Train und Test unterteilt, es fehlt jedoch ein Validierungssatz. Lassen Sie uns schaffen eine 80:20 Aufteilung der Trainingsdaten einen Validierungssatz mit durch die Verwendung von validation_split unter Argument.

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, befinden sich im Schulungsordner 25.000 Beispiele, von denen Sie 80% (oder 20.000) für die Schulung verwenden. Wie Sie gleich sehen werden, können Sie ein Modell trainieren , indem Sie einen Datensatz direkt vorbei model.fit . Wenn Sie neu sind tf.data , können Sie Iterierte auch über den Datensatz und ausdrucken ein paar Beispiele wie folgt.

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 die Bewertungen enthalten Rohtext (mit Interpunktion und gelegentlichen HTML - Tags wie <br/> ). Wie Sie mit diesen umgehen, zeigen Sie im folgenden Abschnitt.

Die Etiketten sind 0 oder 1, welche dieser entsprechen positiven und negativen Film - Bewertungen zu sehen, können Sie das überprüfen class_names Eigenschaft auf den Datensatz.

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 ein Validierungs- und Test-Dataset. Sie verwenden die verbleibenden 5.000 Bewertungen aus dem Trainingsset zur Validierung.

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 wird standardisieren Sie, tokenize und vektorisiert werden die Daten , die die hilfreich mit preprocessing.TextVectorization Schicht.

Standardisierung bezieht sich auf die Vorverarbeitung des Textes, typischerweise um Satzzeichen oder HTML-Elemente zu entfernen, um den Datensatz zu vereinfachen. Tokenisierung bezieht sich auf das Aufteilen von Zeichenfolgen in Token (z. B. das Aufteilen eines Satzes in einzelne Wörter durch Aufteilen auf Leerzeichen). Vektorisierung bezieht sich auf die Umwandlung von Token in Zahlen, damit sie in ein neuronales Netzwerk eingespeist werden können. All diese Aufgaben können mit dieser Schicht erledigt werden.

Wie Sie oben gesehen haben, enthalten die Bewertungen verschiedene HTML - Tags wie <br /> . Diese Tags werden nicht durch den Standard Standardizer in der entfernt werden TextVectorization Schicht (die Text in Kleinbuchstaben konvertiert und Streifen Interpunktionsstandardmäßig, aber nicht HTML Band). Sie werden eine benutzerdefinierte Standardisierungsfunktion schreiben, 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 werden Sie eine erstellen TextVectorization Schicht. Sie verwenden diese Ebene, um unsere Daten zu standardisieren, zu tokenisieren und zu vektorisieren. Sie setzen den output_mode zu int für jeden Token eindeutigen Integer - Indizes zu erstellen.

Beachten Sie, dass Sie die Standard-Aufteilungsfunktion und die oben definierte benutzerdefinierte Standardisierungsfunktion verwenden. Sie werden auch einige Konstanten für das Modell, wie ein expliziten maximal definieren sequence_length , die die Schicht - Pad oder truncate Sequenzen genau verursachen sequence_length Werte.

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 den Zustand der Vorverarbeitung Schicht auf den Datensatz zu passen. Dadurch erstellt das Modell einen Index von Strings zu ganzen Zahlen.

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

Lassen Sie uns eine Funktion erstellen, um das Ergebnis der Verwendung dieses Layers 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'Great movie - especially the music - Etta James - "At Last". This speaks volumes when you have finally found that special someone.', shape=(), dtype=string)
Label neg
Vectorized review (<tf.Tensor: shape=(1, 250), dtype=int64, numpy=
array([[  86,   17,  260,    2,  222,    1,  571,   31,  229,   11, 2418,
           1,   51,   22,   25,  404,  251,   12,  306,  282,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
           0,    0,    0,    0,    0,    0,    0,    0]])>, <tf.Tensor: shape=(), dtype=int32, numpy=0>)

Wie Sie oben sehen können, wurde jedes Token durch eine ganze Zahl ersetzt. Sie können die Token - Lookup (string) , dass jede ganze Zahl entspricht durch den Aufruf .get_vocabulary() auf der Schicht.

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 den zuvor erstellten TextVectorization-Layer auf das Train-, Validierungs- und Test-Dataset 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 die E/A nicht blockiert wird.

.cache() hält Daten im Speicher , nachdem es aus der Festplatte geladen wird . Dadurch wird sichergestellt, dass das Dataset beim Trainieren Ihres Modells nicht zu einem Engpass wird. Wenn Ihr Dataset zu groß ist, um in den Arbeitsspeicher zu passen, können Sie mit dieser Methode auch einen leistungsfähigen Cache auf der Festplatte erstellen, der effizienter zu lesen ist als viele kleine Dateien.

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

Sie können mehr über die beiden Methoden, als auch, wie auf Cache - Daten auf die Festplatte in der Lerndaten Performance Guide .

AUTOTUNE = tf.data.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)

Modell erstellen

Es ist Zeit, Ihr neuronales Netzwerk zu erstellen:

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 Schichten werden nacheinander gestapelt, um den Klassifikator zu erstellen:

  1. Die erste Schicht ist eine Embedding Diese Schicht nimmt die ganzzahlig codierten Bewertungen und sucht einen Einbettungsvektor für jeden Wortindex. Diese Vektoren werden als Modellzüge gelernt. Die Vektoren fügen dem Ausgabearray eine Dimension hinzu. Die resultierenden Abmessungen sind: (batch, sequence, embedding) . Um mehr zu erfahren über Einbettungen finden Sie im Wort Einbettung Tutorial .
  2. Als nächstes wird eine GlobalAveragePooling1D kehrt Schicht eine feste Länge Ausgangsvektor für jedes Beispiel durch Mittelung über die Sequenz Dimension. Dadurch kann das Modell Eingaben mit variabler Länge auf die einfachste Weise verarbeiten.
  3. Dieses feste Länge Ausgangsvektor wird durch eine vollständig verbundene (verrohrt Dense ) Schicht mit 16 versteckten Einheiten.
  4. Die letzte Schicht ist dicht mit einem einzigen Ausgangsknoten verbunden.

Verlustfunktion und Optimierer

Ein Modell benötigt eine Verlustfunktion und einen Optimierer für das Training. Da dies ein binäres Klassifikationsproblem ist und das Modell gibt eine Wahrscheinlichkeit (eine Single-Unit - Schicht mit einer Sigmoid - Aktivierung), werden Sie verwenden losses.BinaryCrossentropy Verlustfunktion.

Konfigurieren Sie nun das Modell 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 das Modell trainieren , indem Sie die vorbeiziehende dataset - dataset - Objekt auf die Fit - Methode.

epochs = 10
history = model.fit(
    train_ds,
    validation_data=val_ds,
    epochs=epochs)
Epoch 1/10
625/625 [==============================] - 4s 4ms/step - loss: 0.6662 - binary_accuracy: 0.6918 - val_loss: 0.6192 - val_binary_accuracy: 0.7720
Epoch 2/10
625/625 [==============================] - 2s 4ms/step - loss: 0.5538 - binary_accuracy: 0.7974 - val_loss: 0.5026 - val_binary_accuracy: 0.8216
Epoch 3/10
625/625 [==============================] - 2s 4ms/step - loss: 0.4483 - binary_accuracy: 0.8421 - val_loss: 0.4228 - val_binary_accuracy: 0.8470
Epoch 4/10
625/625 [==============================] - 2s 4ms/step - loss: 0.3807 - binary_accuracy: 0.8648 - val_loss: 0.3757 - val_binary_accuracy: 0.8600
Epoch 5/10
625/625 [==============================] - 2s 4ms/step - loss: 0.3378 - binary_accuracy: 0.8784 - val_loss: 0.3463 - val_binary_accuracy: 0.8670
Epoch 6/10
625/625 [==============================] - 2s 4ms/step - loss: 0.3069 - binary_accuracy: 0.8874 - val_loss: 0.3271 - val_binary_accuracy: 0.8706
Epoch 7/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2830 - binary_accuracy: 0.8967 - val_loss: 0.3136 - val_binary_accuracy: 0.8732
Epoch 8/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2644 - binary_accuracy: 0.9037 - val_loss: 0.3040 - val_binary_accuracy: 0.8760
Epoch 9/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2468 - binary_accuracy: 0.9107 - val_loss: 0.2972 - val_binary_accuracy: 0.8784
Epoch 10/10
625/625 [==============================] - 2s 4ms/step - loss: 0.2327 - binary_accuracy: 0.9161 - val_loss: 0.2923 - val_binary_accuracy: 0.8792

Bewerten Sie das Modell

Schauen wir uns an, wie sich das Modell verhält. 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 2ms/step - loss: 0.3108 - binary_accuracy: 0.8722
Loss:  0.3108080327510834
Accuracy:  0.8722400069236755

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

Erstellen Sie eine Darstellung 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 den Validierungsverlust und die Genauigkeit.

Beachten Sie die Ausbildung Verlust mit jeder Epoche ab , und die Trainings Genauigkeit steigt mit jeder Epoche. Dies wird erwartet, wenn eine Gradientenabstiegsoptimierung verwendet wird – sie sollte die gewünschte Menge bei jeder Iteration minimieren.

Dies gilt nicht für den Validierungsverlust und die Genauigkeit – sie scheinen ihren Höchststand vor der Trainingsgenauigkeit zu erreichen. Dies ist ein Beispiel für Überanpassung: Das Modell schneidet bei den Trainingsdaten besser ab als bei Daten, die es noch nie zuvor gesehen hat. Nach diesem Punkt das Modell über optimiert und spezifische Darstellungen lernt zu den Trainingsdaten, die Testdaten nicht verallgemeinern.

In diesem speziellen Fall können Sie eine Überanpassung verhindern, indem Sie das Training einfach beenden, wenn die Validierungsgenauigkeit nicht mehr zunimmt. Eine Möglichkeit , dies zu tun ist , um die verwenden tf.keras.callbacks.EarlyStopping Rückruf.

Modell exportieren

In dem obigen Code, angewendet Sie die TextVectorization Schicht auf den Datensatz vor Text zum Modell füttern. Wenn Sie Ihr Modell zur Verarbeitung von Roh - Strings fähig machen wollen (zum Beispiel zu vereinfachen es den Einsatz), können Sie die umfassen TextVectorization Schicht in Ihrem Modell. Dazu können Sie mit den gerade 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.3108 - accuracy: 0.8722
0.8722400069236755

Rückschluss auf neue Daten

Um die Prognosen für neue Beispiele zu erhalten, können Sie rufen Sie einfach model.predict() .

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

export_model.predict(examples)
array([[0.60349613],
       [0.42510134],
       [0.3445068 ]], dtype=float32)

Einschließlich der Text Vorverarbeitungslogik in Ihrem Modell ermöglicht es Ihnen , dass vereinfacht die Bereitstellung eines Modells für die Produktion zu exportieren, und reduziert das Potential für Zug / Test Skew .

Es gibt einen Leistungsunterschied, den Sie berücksichtigen müssen, wenn Sie auswählen, wo Ihre TextVectorization-Ebene angewendet werden soll. Wenn Sie es außerhalb Ihres Modells verwenden, können Sie beim Training auf der GPU asynchrone CPU-Verarbeitung und Pufferung Ihrer Daten durchführen. Wenn Sie also Ihr Modell auf der GPU trainieren, möchten Sie diese Option wahrscheinlich verwenden, um die beste Leistung bei der Entwicklung Ihres Modells zu erzielen. Wechseln Sie dann dazu, die TextVectorization-Ebene in Ihr Modell einzubeziehen, wenn Sie bereit sind, sich auf die Bereitstellung vorzubereiten .

Besuchen Sie diese Anleitung , um mehr über Modelle zu speichern.

Übung: Mehrklassenklassifizierung bei Stack Overflow-Fragen

In diesem Tutorial wurde gezeigt, wie Sie einen binären Klassifikator von Grund auf für das IMDB-Dataset trainieren. Als Übung können Sie dieses Notebook ändern , um einen Multi-Class - Klassifikator zu trainieren den Tag einer Programmier Frage auf vorherzusagen Stack - Überlauf .

Ein Daten - Set wurde vorbereitet für Sie den Körper von mehreren tausend Programmierung Fragen zu verwenden , die (zum Beispiel : „Wie kann ein Wörterbuch von Wert in Python sortieren?“) Zu Stack - Überlauf geschrieben. Jedes davon ist mit genau einem Tag gekennzeichnet (entweder Python, CSharp, JavaScript oder Java). Ihre Aufgabe besteht darin, eine Frage als Eingabe zu verwenden und das entsprechende Tag vorherzusagen, in diesem Fall Python.

Der Datensatz Sie arbeiten enthält mehrere tausend Fragen aus dem viel größeren öffentlichen Stack - Überlauf - Datensatz auf extrahiert BigQuery , die mehr als 17 Millionen Einträge enthält.

Nachdem Sie den Datensatz heruntergeladen haben, werden Sie feststellen, dass er eine ähnliche Verzeichnisstruktur wie der IMDB-Datensatz hat, 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 Notebook so ändern, dass es mit dem Stack Overflow-Dataset funktioniert, indem Sie die folgenden Änderungen vornehmen:

  1. An der Spitze des Notebooks, aktualisieren Sie den Code , dass Downloads der IMDB - Datensatz mit Code den zum Download Stack - Überlauf - Datensatz , die prepreared wurde. Da das Stack Overflow-Dataset eine ähnliche Verzeichnisstruktur aufweist, müssen Sie nicht viele Änderungen vornehmen.

  2. Ändern Sie die letzte Schicht des Modells lesen Dense(4) , wie es jetzt vier Leistungsklassen sind.

  3. Wenn das Modell kompilieren, ändern Sie den Verlust an tf.keras.losses.SparseCategoricalCrossentropy . Dies ist die korrekte Verlustfunktion für ein Multi-Class - Klassifikationsproblem zu verwenden, wenn die Etikett für jede Klasse ganze Zahlen sind (in diesem Fall können sie 0, 1, 2, oder 3). Darüber hinaus ändern die Metriken metrics=['accuracy'] , da dies ein Multi-Class - Klassifikationsproblem ist ( tf.metrics.BinaryAccuracy ist nur für binäre Klassifizierer verwendet wird ).

  4. Beim Plotten Genauigkeit im Laufe der Zeit ändern binary_accuracy und val_binary_accuracy auf accuracy und val_accuracy ist.

  5. Sobald diese Änderungen abgeschlossen sind, können Sie einen Klassifikator mit mehreren Klassen trainieren.

Mehr lernen

In diesem Tutorial wurde die Textklassifizierung von Grund auf eingeführt. Um mehr über die Textklassifikation Workflow im Allgemeinen zu erfahren, besuchen Sie die Textklassifikation Führung von Google - Entwicklern.

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