Se usó la API de Cloud Translation para traducir esta página.
Switch to English

Clasificación de texto con reseñas de películas

Ver en Ejecutar en Google Colab Ver en GitHub Descargar cuaderno Ver modelo TF Hub

Este cuaderno clasifica las reseñas de películas como positivas o negativas utilizando el texto de la reseña. Este es un ejemplo de clasificación binaria, o de dos clases, un tipo de problema de aprendizaje automático importante y ampliamente aplicable.

Usaremos el conjunto de datos de IMDB que contiene el texto de 50.000 reseñas de películas de Internet Movie Database . Estos se dividen en 25,000 revisiones para capacitación y 25,000 revisiones para pruebas. Los conjuntos de entrenamiento y prueba están equilibrados , lo que significa que contienen un número igual de críticas positivas y negativas.

Este cuaderno usa tf.keras , una API de alto nivel para crear y entrenar modelos en TensorFlow, y TensorFlow Hub , una biblioteca y plataforma para el aprendizaje por transferencia. Para obtener un tutorial de clasificación de texto más avanzado utilizando tf.keras , consulte la Guía de clasificación de texto de MLCC .

Más modelos

Aquí puede encontrar modelos más expresivos o de rendimiento que podría utilizar para generar la incrustación de texto.


import numpy as np

import tensorflow as tf
import tensorflow_hub as hub
import tensorflow_datasets as tfds

import matplotlib.pyplot as plt

print("Version: ", tf.__version__)
print("Eager mode: ", tf.executing_eagerly())
print("Hub version: ", hub.__version__)
print("GPU is", "available" if tf.config.list_physical_devices('GPU') else "NOT AVAILABLE")
Version:  2.3.1
Eager mode:  True
Hub version:  0.10.0
GPU is available

Descarga el conjunto de datos de IMDB

El conjunto de datos de IMDB está disponible en los conjuntos de datos de TensorFlow . El siguiente código descarga el conjunto de datos de IMDB a su máquina (o el tiempo de ejecución de colab):

train_data, test_data = tfds.load(name="imdb_reviews", split=["train", "test"], 
                                  batch_size=-1, as_supervised=True)

train_examples, train_labels = tfds.as_numpy(train_data)
test_examples, test_labels = tfds.as_numpy(test_data)
Downloading and preparing dataset imdb_reviews/plain_text/1.0.0 (download: 80.23 MiB, generated: Unknown size, total: 80.23 MiB) to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/1.0.0...
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/1.0.0.incomplete3QPT2N/imdb_reviews-train.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/1.0.0.incomplete3QPT2N/imdb_reviews-test.tfrecord
Shuffling and writing examples to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/1.0.0.incomplete3QPT2N/imdb_reviews-unsupervised.tfrecord
Dataset imdb_reviews downloaded and prepared to /home/kbuilder/tensorflow_datasets/imdb_reviews/plain_text/1.0.0. Subsequent calls will reuse this data.

Explore los datos

Dediquemos un momento a comprender el formato de los datos. Cada ejemplo es una oración que representa la reseña de la película y una etiqueta correspondiente. La sentencia no se procesa previamente de ninguna manera. La etiqueta es un valor entero de 0 o 1, donde 0 es una reseña negativa y 1 es una reseña positiva.

print("Training entries: {}, test entries: {}".format(len(train_examples), len(test_examples)))
Training entries: 25000, test entries: 25000

Imprimamos los primeros 10 ejemplos.

array([b"This was an absolutely terrible movie. Don't be lured in by Christopher Walken or Michael Ironside. Both are great actors, but this must simply be their worst role in history. Even their great acting could not redeem this movie's ridiculous storyline. This movie is an early nineties US propaganda piece. The most pathetic scenes were those when the Columbian rebels were making their cases for revolutions. Maria Conchita Alonso appeared phony, and her pseudo-love affair with Walken was nothing but a pathetic emotional plug in a movie that was devoid of any real meaning. I am disappointed that there are movies like this, ruining actor's like Christopher Walken's good name. I could barely sit through it.",
       b'I have been known to fall asleep during films, but this is usually due to a combination of things including, really tired, being warm and comfortable on the sette and having just eaten a lot. However on this occasion I fell asleep because the film was rubbish. The plot development was constant. Constantly slow and boring. Things seemed to happen, but with no explanation of what was causing them or why. I admit, I may have missed part of the film, but i watched the majority of it and everything just seemed to happen of its own accord without any real concern for anything else. I cant recommend this film at all.',
       b'Mann photographs the Alberta Rocky Mountains in a superb fashion, and Jimmy Stewart and Walter Brennan give enjoyable performances as they always seem to do. <br /><br />But come on Hollywood - a Mountie telling the people of Dawson City, Yukon to elect themselves a marshal (yes a marshal!) and to enforce the law themselves, then gunfighters battling it out on the streets for control of the town? <br /><br />Nothing even remotely resembling that happened on the Canadian side of the border during the Klondike gold rush. Mr. Mann and company appear to have mistaken Dawson City for Deadwood, the Canadian North for the American Wild West.<br /><br />Canadian viewers be prepared for a Reefer Madness type of enjoyable howl with this ludicrous plot, or, to shake your head in disgust.',
       b'This is the kind of film for a snowy Sunday afternoon when the rest of the world can go ahead with its own business as you descend into a big arm-chair and mellow for a couple of hours. Wonderful performances from Cher and Nicolas Cage (as always) gently row the plot along. There are no rapids to cross, no dangerous waters, just a warm and witty paddle through New York life at its best. A family film in every sense and one that deserves the praise it received.',
       b'As others have mentioned, all the women that go nude in this film are mostly absolutely gorgeous. The plot very ably shows the hypocrisy of the female libido. When men are around they want to be pursued, but when no "men" are around, they become the pursuers of a 14 year old boy. And the boy becomes a man really fast (we should all be so lucky at this age!). He then gets up the courage to pursue his true love.',
       b"This is a film which should be seen by anybody interested in, effected by, or suffering from an eating disorder. It is an amazingly accurate and sensitive portrayal of bulimia in a teenage girl, its causes and its symptoms. The girl is played by one of the most brilliant young actresses working in cinema today, Alison Lohman, who was later so spectacular in 'Where the Truth Lies'. I would recommend that this film be shown in all schools, as you will never see a better on this subject. Alison Lohman is absolutely outstanding, and one marvels at her ability to convey the anguish of a girl suffering from this compulsive disorder. If barometers tell us the air pressure, Alison Lohman tells us the emotional pressure with the same degree of accuracy. Her emotional range is so precise, each scene could be measured microscopically for its gradations of trauma, on a scale of rising hysteria and desperation which reaches unbearable intensity. Mare Winningham is the perfect choice to play her mother, and does so with immense sympathy and a range of emotions just as finely tuned as Lohman's. Together, they make a pair of sensitive emotional oscillators vibrating in resonance with one another. This film is really an astonishing achievement, and director Katt Shea should be proud of it. The only reason for not seeing it is if you are not interested in people. But even if you like nature films best, this is after all animal behaviour at the sharp edge. Bulimia is an extreme version of how a tormented soul can destroy her own body in a frenzy of despair. And if we don't sympathise with people suffering from the depths of despair, then we are dead inside.",
       b'Okay, you have:<br /><br />Penelope Keith as Miss Herringbone-Tweed, B.B.E. (Backbone of England.) She\'s killed off in the first scene - that\'s right, folks; this show has no backbone!<br /><br />Peter O\'Toole as Ol\' Colonel Cricket from The First War and now the emblazered Lord of the Manor.<br /><br />Joanna Lumley as the ensweatered Lady of the Manor, 20 years younger than the colonel and 20 years past her own prime but still glamourous (Brit spelling, not mine) enough to have a toy-boy on the side. It\'s alright, they have Col. Cricket\'s full knowledge and consent (they guy even comes \'round for Christmas!) Still, she\'s considerate of the colonel enough to have said toy-boy her own age (what a gal!)<br /><br />David McCallum as said toy-boy, equally as pointlessly glamourous as his squeeze. Pilcher couldn\'t come up with any cover for him within the story, so she gave him a hush-hush job at the Circus.<br /><br />and finally:<br /><br />Susan Hampshire as Miss Polonia Teacups, Venerable Headmistress of the Venerable Girls\' Boarding-School, serving tea in her office with a dash of deep, poignant advice for life in the outside world just before graduation. Her best bit of advice: "I\'ve only been to Nancherrow (the local Stately Home of England) once. I thought it was very beautiful but, somehow, not part of the real world." Well, we can\'t say they didn\'t warn us.<br /><br />Ah, Susan - time was, your character would have been running the whole show. They don\'t write \'em like that any more. Our loss, not yours.<br /><br />So - with a cast and setting like this, you have the re-makings of "Brideshead Revisited," right?<br /><br />Wrong! They took these 1-dimensional supporting roles because they paid so well. After all, acting is one of the oldest temp-jobs there is (YOU name another!)<br /><br />First warning sign: lots and lots of backlighting. They get around it by shooting outdoors - "hey, it\'s just the sunlight!"<br /><br />Second warning sign: Leading Lady cries a lot. When not crying, her eyes are moist. That\'s the law of romance novels: Leading Lady is "dewy-eyed."<br /><br />Henceforth, Leading Lady shall be known as L.L.<br /><br />Third warning sign: L.L. actually has stars in her eyes when she\'s in love. Still, I\'ll give Emily Mortimer an award just for having to act with that spotlight in her eyes (I wonder . did they use contacts?)<br /><br />And lastly, fourth warning sign: no on-screen female character is "Mrs." She\'s either "Miss" or "Lady."<br /><br />When all was said and done, I still couldn\'t tell you who was pursuing whom and why. I couldn\'t even tell you what was said and done.<br /><br />To sum up: they all live through World War II without anything happening to them at all.<br /><br />OK, at the end, L.L. finds she\'s lost her parents to the Japanese prison camps and baby sis comes home catatonic. Meanwhile (there\'s always a "meanwhile,") some young guy L.L. had a crush on (when, I don\'t know) comes home from some wartime tough spot and is found living on the street by Lady of the Manor (must be some street if SHE\'s going to find him there.) Both war casualties are whisked away to recover at Nancherrow (SOMEBODY has to be "whisked away" SOMEWHERE in these romance stories!)<br /><br />Great drama.',
       b'The film is based on a genuine 1950s novel.<br /><br />Journalist Colin McInnes wrote a set of three "London novels": "Absolute Beginners", "City of Spades" and "Mr Love and Justice". I have read all three. The first two are excellent. The last, perhaps an experiment that did not come off. But McInnes\'s work is highly acclaimed; and rightly so. This musical is the novelist\'s ultimate nightmare - to see the fruits of one\'s mind being turned into a glitzy, badly-acted, soporific one-dimensional apology of a film that says it captures the spirit of 1950s London, and does nothing of the sort.<br /><br />Thank goodness Colin McInnes wasn\'t alive to witness it.',
       b'I really love the sexy action and sci-fi films of the sixties and its because of the actress\'s that appeared in them. They found the sexiest women to be in these films and it didn\'t matter if they could act (Remember "Candy"?). The reason I was disappointed by this film was because it wasn\'t nostalgic enough. The story here has a European sci-fi film called "Dragonfly" being made and the director is fired. So the producers decide to let a young aspiring filmmaker (Jeremy Davies) to complete the picture. They\'re is one real beautiful woman in the film who plays Dragonfly but she\'s barely in it. Film is written and directed by Roman Coppola who uses some of his fathers exploits from his early days and puts it into the script. I wish the film could have been an homage to those early films. They could have lots of cameos by actors who appeared in them. There is one actor in this film who was popular from the sixties and its John Phillip Law (Barbarella). Gerard Depardieu, Giancarlo Giannini and Dean Stockwell appear as well. I guess I\'m going to have to continue waiting for a director to make a good homage to the films of the sixties. If any are reading this, "Make it as sexy as you can"! I\'ll be waiting!',
       b'Sure, this one isn\'t really a blockbuster, nor does it target such a position. "Dieter" is the first name of a quite popular German musician, who is either loved or hated for his kind of acting and thats exactly what this movie is about. It is based on the autobiography "Dieter Bohlen" wrote a few years ago but isn\'t meant to be accurate on that. The movie is filled with some sexual offensive content (at least for American standard) which is either amusing (not for the other "actors" of course) or dumb - it depends on your individual kind of humor or on you being a "Bohlen"-Fan or not. Technically speaking there isn\'t much to criticize. Speaking of me I find this movie to be an OK-movie.'],

Imprimamos también las primeras 10 etiquetas.

array([0, 0, 0, 1, 1, 1, 0, 0, 0, 0])

Construye el modelo

La red neuronal se crea apilando capas; esto requiere tres decisiones arquitectónicas principales:

  • ¿Cómo representar el texto?
  • ¿Cuántas capas usar en el modelo?
  • ¿Cuántas unidades ocultas usar para cada capa?

En este ejemplo, los datos de entrada constan de frases. Las etiquetas para predecir son 0 o 1.

Una forma de representar el texto es convertir oraciones en vectores de incrustaciones. Podemos utilizar una incrustación de texto previamente entrenado como primera capa, lo que tendrá dos ventajas:

  • no tenemos que preocuparnos por el preprocesamiento de texto,
  • podemos beneficiarnos de la transferencia de aprendizaje.

Para este ejemplo, usaremos un modelo de TensorFlow Hub llamado google / tf2-preview / gnews-swivel-20dim / 1 .

Hay otros tres modelos para probar por el bien de este tutorial:

Primero creemos una capa de Keras que use un modelo de TensorFlow Hub para insertar las oraciones y probémoslo en un par de ejemplos de entrada. Tenga en cuenta que la forma de salida de las incrustaciones producidas es la esperada: (num_examples, embedding_dimension) .

model = ""
hub_layer = hub.KerasLayer(model, output_shape=[20], input_shape=[], 
                           dtype=tf.string, trainable=True)
<tf.Tensor: shape=(3, 20), dtype=float32, numpy=
array([[ 1.765786  , -3.882232  ,  3.9134233 , -1.5557289 , -3.3362343 ,
        -1.7357955 , -1.9954445 ,  1.2989551 ,  5.081598  , -1.1041286 ,
        -2.0503852 , -0.72675157, -0.65675956,  0.24436149, -3.7208383 ,
         2.0954835 ,  2.2969332 , -2.0689783 , -2.9489717 , -1.1315987 ],
       [ 1.8804485 , -2.5852382 ,  3.4066997 ,  1.0982676 , -4.056685  ,
        -4.891284  , -2.785554  ,  1.3874227 ,  3.8476458 , -0.9256538 ,
        -1.896706  ,  1.2113281 ,  0.11474707,  0.76209456, -4.8791065 ,
         2.906149  ,  4.7087674 , -2.3652055 , -3.5015898 , -1.6390051 ],
       [ 0.71152234, -0.6353217 ,  1.7385626 , -1.1168286 , -0.5451594 ,
        -1.1808156 ,  0.09504455,  1.4653089 ,  0.66059524,  0.79308075,
        -2.2268345 ,  0.07446612, -1.4075904 , -0.70645386, -1.907037  ,
         1.4419787 ,  1.9551861 , -0.42660055, -2.8022065 ,  0.43727064]],

Construyamos ahora el modelo completo:

model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(16, activation='relu'))

Model: "sequential"
Layer (type)                 Output Shape              Param #   
keras_layer (KerasLayer)     (None, 20)                400020    
dense (Dense)                (None, 16)                336       
dense_1 (Dense)              (None, 1)                 17        
Total params: 400,373
Trainable params: 400,373
Non-trainable params: 0

Las capas se apilan secuencialmente para construir el clasificador:

  1. La primera capa es una capa de TensorFlow Hub. Esta capa utiliza un modelo guardado previamente entrenado para mapear una oración en su vector de incrustación. El modelo que estamos usando ( google / tf2-preview / gnews-swivel-20dim / 1 ) divide la oración en tokens, incrusta cada token y luego combina la incrustación. Las dimensiones resultantes son: (num_examples, embedding_dimension) .
  2. Este vector de salida de longitud fija se canaliza a través de una capa completamente conectada ( Dense ) con 16 unidades ocultas.
  3. La última capa está densamente conectada con un solo nodo de salida. Esto genera logits: las probabilidades de registro de la clase verdadera, según el modelo.

Unidades ocultas

El modelo anterior tiene dos capas intermedias u "ocultas", entre la entrada y la salida. El número de salidas (unidades, nodos o neuronas) es la dimensión del espacio de representación de la capa. En otras palabras, la cantidad de libertad que se le permite a la red cuando aprende una representación interna.

Si un modelo tiene más unidades ocultas (un espacio de representación de mayor dimensión) y / o más capas, entonces la red puede aprender representaciones más complejas. Sin embargo, hace que la red sea más costosa computacionalmente y puede conducir al aprendizaje de patrones no deseados, patrones que mejoran el rendimiento en los datos de entrenamiento pero no en los datos de prueba. Esto se llama sobreajuste y lo exploraremos más adelante.

Función de pérdida y optimizador

Un modelo necesita una función de pérdida y un optimizador para el entrenamiento. Dado que este es un problema de clasificación binaria y el modelo genera una probabilidad (una capa de una sola unidad con una activación sigmoidea), usaremos la función de pérdida binary_crossentropy .

Esta no es la única opción para una función de pérdida, podría, por ejemplo, elegir mean_squared_error . Pero, en general, la binary_crossentropy es mejor para tratar con probabilidades: mide la "distancia" entre las distribuciones de probabilidad o, en nuestro caso, entre la distribución de verdad fundamental y las predicciones.

Más adelante, cuando estemos explorando problemas de regresión (digamos, para predecir el precio de una casa), veremos cómo usar otra función de pérdida llamada error cuadrático medio.

Ahora, configure el modelo para usar un optimizador y una función de pérdida:

              metrics=[tf.metrics.BinaryAccuracy(threshold=0.0, name='accuracy')])

Crea un conjunto de validación

Al entrenar, queremos verificar la precisión del modelo en datos que no ha visto antes. Cree un conjunto de validación separando 10,000 ejemplos de los datos de entrenamiento originales. (¿Por qué no usar el conjunto de pruebas ahora? Nuestro objetivo es desarrollar y ajustar nuestro modelo utilizando solo los datos de entrenamiento, luego usar los datos de prueba solo una vez para evaluar nuestra precisión).

x_val = train_examples[:10000]
partial_x_train = train_examples[10000:]

y_val = train_labels[:10000]
partial_y_train = train_labels[10000:]

Entrena el modelo

Entrene el modelo durante 40 épocas en mini lotes de 512 muestras. Se trata de 40 iteraciones sobre todas las muestras en los tensores x_train e y_train . Durante el entrenamiento, supervise la pérdida y precisión del modelo en las 10,000 muestras del conjunto de validación:

history =,
                    validation_data=(x_val, y_val),
Epoch 1/40
30/30 [==============================] - 2s 54ms/step - loss: 0.7389 - accuracy: 0.5659 - val_loss: 0.6617 - val_accuracy: 0.6073
Epoch 2/40
30/30 [==============================] - 1s 47ms/step - loss: 0.6270 - accuracy: 0.6562 - val_loss: 0.6130 - val_accuracy: 0.6673
Epoch 3/40
30/30 [==============================] - 1s 46ms/step - loss: 0.5863 - accuracy: 0.6914 - val_loss: 0.5824 - val_accuracy: 0.6933
Epoch 4/40
30/30 [==============================] - 1s 45ms/step - loss: 0.5491 - accuracy: 0.7266 - val_loss: 0.5491 - val_accuracy: 0.7234
Epoch 5/40
30/30 [==============================] - 1s 45ms/step - loss: 0.5095 - accuracy: 0.7604 - val_loss: 0.5158 - val_accuracy: 0.7504
Epoch 6/40
30/30 [==============================] - 1s 45ms/step - loss: 0.4696 - accuracy: 0.7865 - val_loss: 0.4836 - val_accuracy: 0.7699
Epoch 7/40
30/30 [==============================] - 1s 45ms/step - loss: 0.4299 - accuracy: 0.8081 - val_loss: 0.4533 - val_accuracy: 0.7867
Epoch 8/40
30/30 [==============================] - 1s 45ms/step - loss: 0.3931 - accuracy: 0.8285 - val_loss: 0.4264 - val_accuracy: 0.8038
Epoch 9/40
30/30 [==============================] - 1s 45ms/step - loss: 0.3608 - accuracy: 0.8496 - val_loss: 0.4046 - val_accuracy: 0.8174
Epoch 10/40
30/30 [==============================] - 1s 46ms/step - loss: 0.3330 - accuracy: 0.8630 - val_loss: 0.3848 - val_accuracy: 0.8296
Epoch 11/40
30/30 [==============================] - 1s 47ms/step - loss: 0.3073 - accuracy: 0.8773 - val_loss: 0.3700 - val_accuracy: 0.8379
Epoch 12/40
30/30 [==============================] - 1s 46ms/step - loss: 0.2854 - accuracy: 0.8883 - val_loss: 0.3578 - val_accuracy: 0.8447
Epoch 13/40
30/30 [==============================] - 1s 47ms/step - loss: 0.2649 - accuracy: 0.8990 - val_loss: 0.3446 - val_accuracy: 0.8521
Epoch 14/40
30/30 [==============================] - 1s 47ms/step - loss: 0.2471 - accuracy: 0.9066 - val_loss: 0.3360 - val_accuracy: 0.8556
Epoch 15/40
30/30 [==============================] - 1s 47ms/step - loss: 0.2317 - accuracy: 0.9119 - val_loss: 0.3318 - val_accuracy: 0.8578
Epoch 16/40
30/30 [==============================] - 1s 47ms/step - loss: 0.2161 - accuracy: 0.9213 - val_loss: 0.3245 - val_accuracy: 0.8636
Epoch 17/40
30/30 [==============================] - 1s 47ms/step - loss: 0.2035 - accuracy: 0.9254 - val_loss: 0.3215 - val_accuracy: 0.8648
Epoch 18/40
30/30 [==============================] - 1s 47ms/step - loss: 0.1911 - accuracy: 0.9306 - val_loss: 0.3181 - val_accuracy: 0.8675
Epoch 19/40
30/30 [==============================] - 1s 47ms/step - loss: 0.1792 - accuracy: 0.9371 - val_loss: 0.3189 - val_accuracy: 0.8651
Epoch 20/40
30/30 [==============================] - 1s 47ms/step - loss: 0.1685 - accuracy: 0.9407 - val_loss: 0.3169 - val_accuracy: 0.8664
Epoch 21/40
30/30 [==============================] - 1s 46ms/step - loss: 0.1583 - accuracy: 0.9471 - val_loss: 0.3152 - val_accuracy: 0.8696
Epoch 22/40
30/30 [==============================] - 1s 45ms/step - loss: 0.1488 - accuracy: 0.9510 - val_loss: 0.3160 - val_accuracy: 0.8684
Epoch 23/40
30/30 [==============================] - 1s 47ms/step - loss: 0.1400 - accuracy: 0.9549 - val_loss: 0.3172 - val_accuracy: 0.8700
Epoch 24/40
30/30 [==============================] - 1s 48ms/step - loss: 0.1318 - accuracy: 0.9583 - val_loss: 0.3193 - val_accuracy: 0.8695
Epoch 25/40
30/30 [==============================] - 1s 47ms/step - loss: 0.1240 - accuracy: 0.9611 - val_loss: 0.3215 - val_accuracy: 0.8697
Epoch 26/40
30/30 [==============================] - 1s 46ms/step - loss: 0.1171 - accuracy: 0.9640 - val_loss: 0.3244 - val_accuracy: 0.8700
Epoch 27/40
30/30 [==============================] - 1s 46ms/step - loss: 0.1094 - accuracy: 0.9675 - val_loss: 0.3286 - val_accuracy: 0.8695
Epoch 28/40
30/30 [==============================] - 1s 47ms/step - loss: 0.1026 - accuracy: 0.9705 - val_loss: 0.3318 - val_accuracy: 0.8687
Epoch 29/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0962 - accuracy: 0.9726 - val_loss: 0.3358 - val_accuracy: 0.8698
Epoch 30/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0923 - accuracy: 0.9753 - val_loss: 0.3402 - val_accuracy: 0.8701
Epoch 31/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0851 - accuracy: 0.9775 - val_loss: 0.3454 - val_accuracy: 0.8693
Epoch 32/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0795 - accuracy: 0.9801 - val_loss: 0.3499 - val_accuracy: 0.8703
Epoch 33/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0747 - accuracy: 0.9819 - val_loss: 0.3549 - val_accuracy: 0.8699
Epoch 34/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0702 - accuracy: 0.9833 - val_loss: 0.3643 - val_accuracy: 0.8674
Epoch 35/40
30/30 [==============================] - 1s 48ms/step - loss: 0.0657 - accuracy: 0.9847 - val_loss: 0.3668 - val_accuracy: 0.8693
Epoch 36/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0614 - accuracy: 0.9865 - val_loss: 0.3733 - val_accuracy: 0.8685
Epoch 37/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0573 - accuracy: 0.9880 - val_loss: 0.3791 - val_accuracy: 0.8683
Epoch 38/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0535 - accuracy: 0.9896 - val_loss: 0.3861 - val_accuracy: 0.8685
Epoch 39/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0502 - accuracy: 0.9902 - val_loss: 0.3923 - val_accuracy: 0.8686
Epoch 40/40
30/30 [==============================] - 1s 47ms/step - loss: 0.0470 - accuracy: 0.9915 - val_loss: 0.4014 - val_accuracy: 0.8666

Evaluar el modelo

Y veamos cómo funciona el modelo. Se devolverán dos valores. Pérdida (un número que representa nuestro error, los valores más bajos son mejores) y precisión.

results = model.evaluate(test_data, test_labels)

782/782 [==============================] - 3s 4ms/step - loss: 0.4303 - accuracy: 0.8552
[0.4302561283111572, 0.8551599979400635]

Este enfoque bastante ingenuo logra una precisión de aproximadamente el 87%. Con enfoques más avanzados, el modelo debería acercarse al 95%.

Cree un gráfico de precisión y pérdida a lo largo del tiempo devuelve un objeto History que contiene un diccionario con todo lo que sucedió durante el entrenamiento:

history_dict = history.history
dict_keys(['loss', 'accuracy', 'val_loss', 'val_accuracy'])

Hay cuatro entradas: una para cada métrica supervisada durante el entrenamiento y la validación. Podemos utilizarlos para graficar la pérdida de entrenamiento y validación para comparar, así como la precisión de entrenamiento y validación:

acc = history_dict['accuracy']
val_acc = history_dict['val_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.clf()   # clear figure

plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')


En este gráfico, los puntos representan la pérdida y la precisión del entrenamiento, y las líneas continuas son la pérdida y la precisión de la validación.

Observe que la pérdida de entrenamiento disminuye con cada época y la precisión del entrenamiento aumenta con cada época. Esto se espera cuando se usa una optimización de descenso de gradiente; debe minimizar la cantidad deseada en cada iteración.

Este no es el caso de la pérdida y precisión de la validación: parecen alcanzar su punto máximo después de unas veinte épocas. Este es un ejemplo de sobreajuste: el modelo funciona mejor en los datos de entrenamiento que en los datos que nunca antes había visto. Después de este punto, el modelo sobreoptimiza y aprende representaciones específicas de los datos de entrenamiento que no se generalizan a los datos de prueba.

Para este caso particular, podríamos evitar el sobreajuste simplemente deteniendo el entrenamiento después de unas veinte épocas. Más adelante, verá cómo hacer esto automáticamente con una devolución de llamada.

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