Introducción al Aprendizaje Automático - TensorFlow | Original, traducido por IA
Este artículo fue escrito originalmente en chino. Ha sido traducido al inglés para facilitar su traducción a otros idiomas.
Dado que estamos aprendiendo Python
, definitivamente necesitamos hablar sobre aprendizaje automático. Muchas de sus bibliotecas están escritas en Python. Comencemos por instalarlas y probarlas.
TensorFlow
Vamos a instalarlo.
$ pip install tensorflow
ERROR: Could not find a version that satisfies the requirement tensorflow
ERROR: No matching distribution found for tensorflow
$ type python
python is aliased to `/usr/local/Cellar/python@3.9/3.9.1_6/bin/python3'
Sin embargo, TensorFlow 2
solo admite Python 3.5–3.8
. Estamos usando la versión 3.9
.
% type python3
python3 is /usr/bin/python3
% python3 -V
Python 3.8.2
Observa que el python3
en mi sistema es la versión 3.8.2
. ¿Dónde instala los paquetes el pip
correspondiente a esta versión de Python?
% python3 -m pip -V
pip 21.0.1 from /Users/lzw/Library/Python/3.8/lib/python/site-packages/pip (python 3.8)
El pip
correspondiente está aquí. Así que modificaré el archivo .zprofile
. Recientemente, cambié mi shell
, y .zprofile
es equivalente al anterior .bash_profile
. Agrega una línea:
alias pip3=/Users/lzw/Library/Python/3.8/bin/pip3
De esta manera, podemos usar python3
y pip3
para trabajar con TensorFlow
.
% pip3 install tensorflow
...
Successfully installed absl-py-0.12.0 astunparse-1.6.3 cachetools-4.2.1 certifi-2020.12.5 chardet-4.0.0 flatbuffers-1.12 gast-0.3.3 google-auth-1.27.1 google-auth-oauthlib-0.4.3 google-pasta-0.2.0 grpcio-1.32.0 h5py-2.10.0 idna-2.10 keras-preprocessing-1.1.2 markdown-3.3.4 numpy-1.19.5 oauthlib-3.1.0 opt-einsum-3.3.0 protobuf-3.15.6 pyasn1-0.4.8 pyasn1-modules-0.2.8 requests-2.25.1 requests-oauthlib-1.3.0 rsa-4.7.2 tensorboard-2.4.1 tensorboard-plugin-wit-1.8.0 tensorflow-2.4.1 tensorflow-estimator-2.4.0 termcolor-1.1.0 typing-extensions-3.7.4.3 urllib3-1.26.3 werkzeug-1.0.1 wheel-0.36.2 wrapt-1.12.1
Se instalaron muchas bibliotecas. Usemos un ejemplo del sitio web oficial.
import tensorflow as tf
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dropout(0.2),
tf.keras.layers.Dense(10)
])
predictions = model(x_train[:1]).numpy()
print(predictions)
Vamos a ejecutarlo.
$ /usr/bin/python3 tf.py
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
11493376/11490434 [==============================] - 10s 1us/step
[[ 0.15477428 -0.3877643 0.0994779 0.07474922 -0.26219758 -0.03550266
0.32226565 -0.37141111 0.10925996 -0.0115255 ]]
Como puedes ver, el conjunto de datos se descargó y se generaron los resultados.
A continuación, veamos un ejemplo de clasificación de imágenes.
# TensorFlow y tf.keras
import tensorflow as tf
# Bibliotecas auxiliares
import numpy as np
import matplotlib.pyplot as plt
print(tf.__version__)
Ocurrió un error.
ModuleNotFoundError: No module named 'matplotlib'
Vamos a instalarlo.
% pip3 install matplotlib
Ahora está correcto.
$ /usr/bin/python3 image.py
2.4.1
Copiemos y peguemos el código de ejemplo.
# TensorFlow y tf.keras
import tensorflow as tf
# Bibliotecas auxiliares
import numpy as np
import matplotlib.pyplot as plt
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
class_names = ['Camiseta/suéter', 'Pantalón', 'Sudadera', 'Vestido', 'Abrigo',
'Sandalia', 'Camisa', 'Zapatilla', 'Bolsa', 'Bota hasta el tobillo']
print(train_images.shape)
print(len(train_labels))
Se muestran los resultados. Observa que tenemos train_images
, train_labels
, test_images
y test_labels
. Estos se dividen en conjuntos de datos de entrenamiento y prueba.
(60000, 28, 28)
60000
A continuación, intentemos mostrar una imagen.
print(train_images[0])
Veamos el resultado.
[[ 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 1 0 0 13 73 0
0 1 4 0 0 0 0 1 1 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 3 0 36 136 127 62
54 0 0 0 1 3 4 0 0 3]
[ 0 0 0 0 0 0 0 0 0 0 0 0 6 0 102 204 176 134
144 123 23 0 0 0 0 12 10 0]
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 155 236 207 178
107 156 161 109 64 23 77 130 72 15]]
....
Aquí se extrae parte del resultado.
print(len(train_images[0][0]))
Muestra 28
. Así que está claro que esta es una matriz con un ancho de 28. Continuemos imprimiendo.
print(len(train_images[0][0][0]))
TypeError: object of type 'numpy.uint8' has no len()
Así que está claro. Cada imagen es un arreglo de 28*28*3
. La última dimensión almacena los valores RGB. Sin embargo, podríamos estar equivocados en esto.
print(train_images[0][1][20])
0
print(train_images[0][1])
[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]
Esto muestra que cada imagen es un arreglo de 28*28
. Después de algunas pruebas, finalmente descubrimos el secreto.
Primero, veamos la imagen de salida.
plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()
¿Ves la barra de colores a la derecha? Desde 0
a 250
. Resulta que esto es un degradado entre dos colores. Pero, ¿cómo sabe qué dos colores? ¿Dónde lo especificamos?
A continuación, imprimamos también la segunda imagen.
plt.imshow(train_images[1])
Muy interesante. ¿Es este el valor predeterminado de la biblioteca pyplot
? Continuemos ejecutando el código proporcionado por el sitio web oficial.
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5,5,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(class_names[train_labels[i]])
plt.show()
Observa que aquí se muestran las imágenes y sus clasificaciones. Finalmente, entendemos el parámetro cmap
. Si cmap
no se especifica, se utilizará el esquema de color que vimos anteriormente. Efectivamente.
plt.imshow(train_images[i])
Esta vez, buscamos pyplot cmap
y encontramos algunos recursos.
plt.imshow(train_images[i], cmap=plt.cm.PiYG)
Modifiquemos el código.
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(2,5,i+1) ## Línea modificada
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.Blues)
plt.xlabel(class_names[train_labels[i]])
plt.show()
Sin embargo, ocurrió un error.
ValueError: num must be 1 <= num <= 10, not 11
¿Qué significa esto? ¿Qué significa exactamente el anterior 5,5,i+1
? ¿Por qué no funciona cuando se cambia a 2
? Aunque intuitivamente entendemos que podría significar 5 filas y 5 columnas, ¿por qué ocurre este error? ¿Cómo se calcula 11
? ¿Qué significa num
? ¿Qué significa 10
? Observa que 2*5=10
. Entonces, quizá el error ocurre cuando i=11
. Cuando se cambia a for i in range(10):
, obtenemos el siguiente resultado.
Esta vez, después de revisar brevemente la documentación, aprendemos sobre subplot(nrows, ncols, index, **kwargs)
. Vale, ahora lo entendemos.
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5,5,i+1)
# plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.Blues)
plt.xlabel(class_names[train_labels[i]])
plt.show()
Observa que cosas como 0 25
se llaman xticks
. Cuando hacemos zoom dentro o fuera de este marco, la visualización cambia.
Observa que al hacer zoom dentro o fuera, xticks
y xlabels
se muestran de manera diferente.
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10)
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=10)
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print('\nTest accuracy:', test_acc)
Observa la forma en que se define el modelo aquí, usando la clase Sequential
. Presta atención a estos parámetros: 28,28
, 128
, relu
, 10
. Observa que necesitas compile
y fit
. fit
significa ajustar. Observa que 28,28
corresponde al tamaño de la imagen.
Epoch 1/10
1875/1875 [==============================] - 2s 928us/step - loss: 0.6331 - accuracy: 0.7769
Epoch 2/10
1875/1875 [==============================] - 2s 961us/step - loss: 0.3860 - accuracy: 0.8615
Epoch 3/10
1875/1875 [==============================] - 2s 930us/step - loss: 0.3395 - accuracy: 0.8755
Epoch 4/10
1875/1875 [==============================] - 2s 1ms/step - loss: 0.3071 - accuracy: 0.8890
Epoch 5/10
1875/1875 [==============================] - 2s 1ms/step - loss: 0.2964 - accuracy: 0.8927
Epoch 6/10
1875/1875 [==============================] - 2s 985us/step - loss: 0.2764 - accuracy: 0.8955
Epoch 7/10
1875/1875 [==============================] - 2s 961us/step - loss: 0.2653 - accuracy: 0.8996
Epoch 8/10
1875/1875 [==============================] - 2s 1ms/step - loss: 0.2549 - accuracy: 0.9052
Epoch 9/10
1875/1875 [==============================] - 2s 1ms/step - loss: 0.2416 - accuracy: 0.9090
Epoch 10/10
1875/1875 [==============================] - 2s 1ms/step - loss: 0.2372 - accuracy: 0.9086
313/313 - 0s - loss: 0.3422 - accuracy: 0.8798
Test accuracy: 0.879800021648407
El modelo ha sido entrenado. Ajustemos los parámetros.
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(28, activation='relu'), # 128 -> 28
tf.keras.layers.Dense(10)
])
Modifica el primer parámetro de Dense
.
Epoch 1/10
1875/1875 [==============================] - 2s 714us/step - loss: 6.9774 - accuracy: 0.3294
Epoch 2/10
1875/1875 [==============================] - 1s 715us/step - loss: 1.3038 - accuracy: 0.4831
Epoch 3/10
1875/1875 [==============================] - 1s 747us/step - loss: 1.0160 - accuracy: 0.6197
Epoch 4/10
1875/1875 [==============================] - 1s 800us/step - loss: 0.7963 - accuracy: 0.6939
Epoch 5/10
1875/1875 [==============================] - 2s 893us/step - loss: 0.7006 - accuracy: 0.7183
Epoch 6/10
1875/1875 [==============================] - 1s 747us/step - loss: 0.6675 - accuracy: 0.7299
Epoch 7/10
1875/1875 [==============================] - 1s 694us/step - loss: 0.6681 - accuracy: 0.7330
Epoch 8/10
1875/1875 [==============================] - 1s 702us/step - loss: 0.6675 - accuracy: 0.7356
Epoch 9/10
1875/1875 [==============================] - 1s 778us/step - loss: 0.6508 - accuracy: 0.7363
Epoch 10/10
1875/1875 [==============================] - 1s 732us/step - loss: 0.6532 - accuracy: 0.7350
313/313 - 0s - loss: 0.6816 - accuracy: 0.7230
Test accuracy: 0.7229999899864197
Observa el cambio en Test accuracy
antes y después. Los registros de Epoch
son generados por la función fit
. Observa que con 128
, accuracy
pasa de 0.7769
a 0.9086
. Con 28
, accuracy
pasa de 0.3294
a 0.7350
. Esta vez, vemos que primero usamos el conjunto de entrenamiento para optimizar loss
y accuracy
, luego usamos el conjunto de datos de prueba para evaluar. Veamos train_labels
.
print(train_labels)
[9 0 0 ... 3 0 5]
print(len(train_labels))
60000
Esto significa que los números del 0 al 9
representan estas categorías. Casualmente, class_names
también tiene 10 elementos.
class_names = ['Camiseta/suéter', 'Pantalón', 'Sudadera', 'Vestido', 'Abrigo',
'Sandalia', 'Camisa', 'Zapatilla', 'Bolsa', 'Bota hasta el tobillo']
Hagamos otro cambio.
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(28, activation='relu'),
tf.keras.layers.Dense(5) # 10 -> 5
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=10)
Ocurrió un error.
tensorflow.python.framework.errors_impl.InvalidArgumentError: Received a label value of 9 which is outside the valid range of [0, 5). Label values: 4 3 2 9 4 1 6 0 7 9 1 6 5 2 3 8 6 3 8 0 3 5 6 1 2 6 3 6 8 4 8 4
[[node sparse_categorical_crossentropy/SparseSoftmaxCrossEntropyWithLogits/SparseSoftmaxCrossEntropyWithLogits (defined at /curiosity-courses/ml/tf/image.py:53) ]] [Op:__inference_train_function_538]
Function call stack:
train_function
Cambiar el tercer parámetro de Sequential
, Dense
, a 15
resuelve el problema. Los resultados no son muy diferentes. Intentemos cambiar Epoch
.
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(28, 28)),
tf.keras.layers.Dense(28, activation='relu'),
tf.keras.layers.Dense(15)
])
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=15) # 10 -> 15
test_loss, test_acc = model.evaluate(test_images, test_labels, verbose=2)
print('\nTest accuracy:', test_acc)
Epoch 1/15
1875/1875 [==============================] - 2s 892us/step - loss: 6.5778 - accuracy: 0.3771
Epoch 2/15
1875/1875 [==============================] - 2s 872us/step - loss: 1.3121 - accuracy: 0.4910
Epoch 3/15
1875/1875 [==============================] - 2s 909us/step - loss: 1.0900 - accuracy: 0.5389
Epoch 4/15
1875/1875 [==============================] - 1s 730us/step - loss: 1.0422 - accuracy: 0.5577
Epoch 5/15
1875/1875 [==============================] - 1s 709us/step - loss: 0.9529 - accuracy: 0.5952
Epoch 6/15
1875/1875 [==============================] - 1s 714us/step - loss: 0.9888 - accuracy: 0.5950
Epoch 7/15
1875/1875 [==============================] - 1s 767us/step - loss: 0.8678 - accuracy: 0.6355
Epoch 8/15
1875/1875 [==============================] - 1s 715us/step - loss: 0.8247 - accuracy: 0.6611
Epoch 9/15
1875/1875 [==============================] - 1s 721us/step - loss: 0.8011 - accuracy: 0.6626
Epoch 10/15
1875/1875 [==============================] - 1s 711us/step - loss: 0.8024 - accuracy: 0.6622
Epoch 11/15
1875/1875 [==============================] - 1s 781us/step - loss: 0.7777 - accuracy: 0.6696
Epoch 12/15
1875/1875 [==============================] - 1s 724us/step - loss: 0.7764 - accuracy: 0.6728
Epoch 13/15
1875/1875 [==============================] - 1s 731us/step - loss: 0.7688 - accuracy: 0.6767
Epoch 14/15
1875/1875 [==============================] - 1s 715us/step - loss: 0.7592 - accuracy: 0.6793
Epoch 15/15
1875/1875 [==============================] - 1s 786us/step - loss: 0.7526 - accuracy: 0.6792
313/313 - 0s - loss: 0.8555 - accuracy: 0.6418
Test accuracy: 0.6417999863624573
Observa que cambiar a 15 no hace mucha diferencia. tf.keras.layers.Dense(88, activation='relu')
es importante. Al intentar cambiar 128 a 88 se obtuvo Test accuracy: 0.824999988079071
. Con 128, fue 0.879800021648407
. Con 28, fue 0.7229999899864197
. ¿Un valor más grande significa mejores resultados? Sin embargo, al cambiarlo a 256
, fue Test accuracy: 0.8409000039100647
. Esto nos hace reflexionar sobre el significado de loss
y accuracy
.
probability_model = tf.keras.Sequential([model,
tf.keras.layers.Softmax()])
A continuación, hagamos una predicción. Observa que Sequential
es el mismo que antes. Presta atención a los parámetros model
y tf.keras.layers.Softmax()
.
probability_model = tf.keras.Sequential([model,
tf.keras.layers.Softmax()])
predictions = probability_model.predict(test_images)
def plot_image(i, predictions_array, true_label, img):
true_label, img = true_label[i], img[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.imshow(img, cmap=plt.cm.binary)
predicted_label = np.argmax(predictions_array)
if predicted_label == true_label:
color = 'blue'
else:
color = 'red'
plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
100*np.max(predictions_array),
class_names[true_label]),
color=color)
def plot_value_array(i, predictions_array, true_label):
true_label = true_label[i]
plt.grid(False)
plt.xticks(range(10))
plt.yticks([])
thisplot = plt.bar(range(10), predictions_array, color="#777777")
plt.ylim([0, 1])
predicted_label = np.argmax(predictions_array)
thisplot[predicted_label].set_color('red')
thisplot[true_label].set_color('blue')
i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions[i], test_labels)
plt.show()
Esto indica que esta imagen tiene un 99% de probabilidad de ser una Bota hasta el tobillo
. Observa que plot_image
muestra el gráfico de la izquierda, y plot_value_array
genera el gráfico de la derecha.
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
plt.subplot(num_rows, 2*num_cols, 2*i+1)
plot_image(i, predictions[i], test_labels, test_images)
plt.subplot(num_rows, 2*num_cols, 2*i+2)
plot_value_array(i, predictions[i], test_labels)
plt.tight_layout()
plt.show()
Observa que esto solo muestra más resultados de prueba. Así que entendemos aproximadamente el flujo de uso. Todavía no sabemos cómo funcionan los cálculos detrás de escena, pero sabemos cómo usarlos. Detrás de todo esto está el cálculo. ¿Cómo entendemos el cálculo?
Por ejemplo, hay un número entre 1 y 100 para que lo adivines. Cada vez que adivinas, te digo si es demasiado bajo o demasiado alto. Adivinas 50. Digo demasiado bajo. Adivinas 80. Digo demasiado alto. Adivinas 65. Digo demasiado alto. Adivinas 55. Digo demasiado bajo. Adivinas 58. Digo, sí, lo adivinaste.
El aprendizaje automático simula un proceso similar detrás de escena, solo que más complejo. Podría implicar muchos rangos como 1 a 100
, adivinando muchos números a la vez. Cada adivinanza implica muchos cálculos, y determinar si es demasiado alto o demasiado bajo también requiere muchos cómputos.