Ver en TensorFlow.org | Ejecutar en Google Colab | Ver fuente en GitHub | Descargar libreta |
import tensorflow as tf
import numpy as np
Los tensores son matrices multidimensionales con un tipo uniforme (llamado dtype
). Puede ver todos los dtypes
admitidos en tf.dtypes.DType
.
Si está familiarizado con NumPy , los tensores son (más o menos) como np.arrays
.
Todos los tensores son inmutables como los números y las cadenas de Python: nunca puede actualizar el contenido de un tensor, solo puede crear uno nuevo.
Lo esencial
Vamos a crear algunos tensores básicos.
Aquí hay un tensor "escalar" o "rango 0". Un escalar contiene un solo valor y ningún "eje".
# This will be an int32 tensor by default; see "dtypes" below.
rank_0_tensor = tf.constant(4)
print(rank_0_tensor)
tf.Tensor(4, shape=(), dtype=int32)
Un tensor "vector" o "rango 1" es como una lista de valores. Un vector tiene un eje:
# Let's make this a float tensor.
rank_1_tensor = tf.constant([2.0, 3.0, 4.0])
print(rank_1_tensor)
tf.Tensor([2. 3. 4.], shape=(3,), dtype=float32)
Un tensor de "matriz" o "rango 2" tiene dos ejes:
# If you want to be specific, you can set the dtype (see below) at creation time
rank_2_tensor = tf.constant([[1, 2],
[3, 4],
[5, 6]], dtype=tf.float16)
print(rank_2_tensor)
tf.Tensor( [[1. 2.] [3. 4.] [5. 6.]], shape=(3, 2), dtype=float16)
Un escalar, forma: [] | Un vector, forma: [3] | Una matriz, forma: [3, 2] |
---|---|---|
Los tensores pueden tener más ejes; aquí hay un tensor con tres ejes:
# There can be an arbitrary number of
# axes (sometimes called "dimensions")
rank_3_tensor = tf.constant([
[[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]],
[[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]],
[[20, 21, 22, 23, 24],
[25, 26, 27, 28, 29]],])
print(rank_3_tensor)
tf.Tensor( [[[ 0 1 2 3 4] [ 5 6 7 8 9]] [[10 11 12 13 14] [15 16 17 18 19]] [[20 21 22 23 24] [25 26 27 28 29]]], shape=(3, 2, 5), dtype=int32)
Hay muchas formas de visualizar un tensor con más de dos ejes.
Un tensor de 3 ejes, forma: [3, 2, 5] | ||
---|---|---|
Puede convertir un tensor en una matriz NumPy utilizando np.array
o el método tensor.numpy
:
np.array(rank_2_tensor)
array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16)
rank_2_tensor.numpy()
array([[1., 2.], [3., 4.], [5., 6.]], dtype=float16)
Los tensores a menudo contienen flotantes e enteros, pero tienen muchos otros tipos, que incluyen:
- números complejos
- instrumentos de cuerda
La clase base tf.Tensor
requiere que los tensores sean "rectangulares", es decir, a lo largo de cada eje, todos los elementos tienen el mismo tamaño. Sin embargo, existen tipos especializados de tensores que pueden manejar diferentes formas:
- Tensores irregulares (ver RaggedTensor a continuación)
- Tensores dispersos (ver SparseTensor a continuación)
Puede hacer operaciones matemáticas básicas con tensores, incluida la suma, la multiplicación por elementos y la multiplicación de matrices.
a = tf.constant([[1, 2],
[3, 4]])
b = tf.constant([[1, 1],
[1, 1]]) # Could have also said `tf.ones([2,2])`
print(tf.add(a, b), "\n")
print(tf.multiply(a, b), "\n")
print(tf.matmul(a, b), "\n")
tf.Tensor( [[2 3] [4 5]], shape=(2, 2), dtype=int32) tf.Tensor( [[1 2] [3 4]], shape=(2, 2), dtype=int32) tf.Tensor( [[3 3] [7 7]], shape=(2, 2), dtype=int32)
print(a + b, "\n") # element-wise addition
print(a * b, "\n") # element-wise multiplication
print(a @ b, "\n") # matrix multiplication
tf.Tensor( [[2 3] [4 5]], shape=(2, 2), dtype=int32) tf.Tensor( [[1 2] [3 4]], shape=(2, 2), dtype=int32) tf.Tensor( [[3 3] [7 7]], shape=(2, 2), dtype=int32)
Los tensores se utilizan en todo tipo de operaciones (ops).
c = tf.constant([[4.0, 5.0], [10.0, 1.0]])
# Find the largest value
print(tf.reduce_max(c))
# Find the index of the largest value
print(tf.argmax(c))
# Compute the softmax
print(tf.nn.softmax(c))
tf.Tensor(10.0, shape=(), dtype=float32) tf.Tensor([1 0], shape=(2,), dtype=int64) tf.Tensor( [[2.6894143e-01 7.3105854e-01] [9.9987662e-01 1.2339458e-04]], shape=(2, 2), dtype=float32)
Acerca de las formas
Los tensores tienen formas. Algo de vocabulario:
- Forma : La longitud (número de elementos) de cada uno de los ejes de un tensor.
- Rango : Número de ejes tensoriales. Un escalar tiene rango 0, un vector tiene rango 1, una matriz tiene rango 2.
- Eje o Dimensión : Una dimensión particular de un tensor.
- Tamaño : el número total de elementos en el tensor, el vector de forma del producto.
Los objetos Tensors y tf.TensorShape
tienen propiedades convenientes para acceder a estos:
rank_4_tensor = tf.zeros([3, 2, 4, 5])
Un tensor de rango 4, forma: [3, 2, 4, 5] | |
---|---|
print("Type of every element:", rank_4_tensor.dtype)
print("Number of axes:", rank_4_tensor.ndim)
print("Shape of tensor:", rank_4_tensor.shape)
print("Elements along axis 0 of tensor:", rank_4_tensor.shape[0])
print("Elements along the last axis of tensor:", rank_4_tensor.shape[-1])
print("Total number of elements (3*2*4*5): ", tf.size(rank_4_tensor).numpy())
Type of every element: <dtype: 'float32'> Number of axes: 4 Shape of tensor: (3, 2, 4, 5) Elements along axis 0 of tensor: 3 Elements along the last axis of tensor: 5 Total number of elements (3*2*4*5): 120
Si bien a menudo se hace referencia a los ejes por sus índices, siempre debe realizar un seguimiento del significado de cada uno. A menudo, los ejes se ordenan de global a local: primero el eje del lote, seguido de las dimensiones espaciales y, por último, las características de cada ubicación. De esta manera, los vectores de características son regiones contiguas de memoria.
Orden típico de los ejes |
---|
Indexación
Indexación de un solo eje
TensorFlow sigue las reglas de indexación estándar de Python, similares a la indexación de una lista o una cadena en Python , y las reglas básicas para la indexación de NumPy.
- los índices comienzan en
0
- los índices negativos cuentan hacia atrás desde el final
- los dos puntos,
:
, se utilizan para las porciones:start:stop:step
rank_1_tensor = tf.constant([0, 1, 1, 2, 3, 5, 8, 13, 21, 34])
print(rank_1_tensor.numpy())
[ 0 1 1 2 3 5 8 13 21 34]
La indexación con un escalar elimina el eje:
print("First:", rank_1_tensor[0].numpy())
print("Second:", rank_1_tensor[1].numpy())
print("Last:", rank_1_tensor[-1].numpy())
First: 0 Second: 1 Last: 34
La indexación con un segmento :
mantiene el eje:
print("Everything:", rank_1_tensor[:].numpy())
print("Before 4:", rank_1_tensor[:4].numpy())
print("From 4 to the end:", rank_1_tensor[4:].numpy())
print("From 2, before 7:", rank_1_tensor[2:7].numpy())
print("Every other item:", rank_1_tensor[::2].numpy())
print("Reversed:", rank_1_tensor[::-1].numpy())
Everything: [ 0 1 1 2 3 5 8 13 21 34] Before 4: [0 1 1 2] From 4 to the end: [ 3 5 8 13 21 34] From 2, before 7: [1 2 3 5 8] Every other item: [ 0 1 3 8 21] Reversed: [34 21 13 8 5 3 2 1 1 0]
Indexación multieje
Los tensores de rango superior se indexan pasando múltiples índices.
Las mismas reglas exactas que en el caso de un solo eje se aplican a cada eje de forma independiente.
print(rank_2_tensor.numpy())
[[1. 2.] [3. 4.] [5. 6.]]
Pasando un entero por cada índice, el resultado es un escalar.
# Pull out a single value from a 2-rank tensor
print(rank_2_tensor[1, 1].numpy())
4.0
Puede indexar utilizando cualquier combinación de enteros y sectores:
# Get row and column tensors
print("Second row:", rank_2_tensor[1, :].numpy())
print("Second column:", rank_2_tensor[:, 1].numpy())
print("Last row:", rank_2_tensor[-1, :].numpy())
print("First item in last column:", rank_2_tensor[0, -1].numpy())
print("Skip the first row:")
print(rank_2_tensor[1:, :].numpy(), "\n")
Second row: [3. 4.] Second column: [2. 4. 6.] Last row: [5. 6.] First item in last column: 2.0 Skip the first row: [[3. 4.] [5. 6.]]
Aquí hay un ejemplo con un tensor de 3 ejes:
print(rank_3_tensor[:, :, 4])
tf.Tensor( [[ 4 9] [14 19] [24 29]], shape=(3, 2), dtype=int32)
Selección de la última entidad en todas las ubicaciones en cada ejemplo del lote | |
---|---|
Lea la guía de división de tensores para aprender cómo puede aplicar la indexación para manipular elementos individuales en sus tensores.
Manipulación de formas
Reformar un tensor es de gran utilidad.
# Shape returns a `TensorShape` object that shows the size along each axis
x = tf.constant([[1], [2], [3]])
print(x.shape)
(3, 1)
# You can convert this object into a Python list, too
print(x.shape.as_list())
[3, 1]
Puede remodelar un tensor en una nueva forma. La operación tf.reshape
es rápida y económica ya que no es necesario duplicar los datos subyacentes.
# You can reshape a tensor to a new shape.
# Note that you're passing in a list
reshaped = tf.reshape(x, [1, 3])
print(x.shape)
print(reshaped.shape)
(3, 1) (1, 3)
Los datos mantienen su disposición en memoria y se crea un nuevo tensor, con la forma solicitada, apuntando a los mismos datos. TensorFlow usa el orden de memoria de "fila principal" de estilo C, donde incrementar el índice más a la derecha corresponde a un solo paso en la memoria.
print(rank_3_tensor)
tf.Tensor( [[[ 0 1 2 3 4] [ 5 6 7 8 9]] [[10 11 12 13 14] [15 16 17 18 19]] [[20 21 22 23 24] [25 26 27 28 29]]], shape=(3, 2, 5), dtype=int32)
Si aplana un tensor, puede ver en qué orden se presenta en la memoria.
# A `-1` passed in the `shape` argument says "Whatever fits".
print(tf.reshape(rank_3_tensor, [-1]))
tf.Tensor( [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29], shape=(30,), dtype=int32)
Por lo general, el único uso razonable de tf.reshape
es combinar o dividir ejes adyacentes (o agregar/eliminar 1
s).
Para este tensor 3x2x5, la remodelación a (3x2)x5 o 3x(2x5) son cosas razonables, ya que los cortes no se mezclan:
print(tf.reshape(rank_3_tensor, [3*2, 5]), "\n")
print(tf.reshape(rank_3_tensor, [3, -1]))
tf.Tensor( [[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14] [15 16 17 18 19] [20 21 22 23 24] [25 26 27 28 29]], shape=(6, 5), dtype=int32) tf.Tensor( [[ 0 1 2 3 4 5 6 7 8 9] [10 11 12 13 14 15 16 17 18 19] [20 21 22 23 24 25 26 27 28 29]], shape=(3, 10), dtype=int32)
Algunas buenas remodelaciones. | ||
---|---|---|
La remodelación "funcionará" para cualquier forma nueva con el mismo número total de elementos, pero no servirá de nada si no respeta el orden de los ejes.
El intercambio de ejes en tf.reshape
no funciona; necesitas tf.transpose
para eso.
# Bad examples: don't do this
# You can't reorder axes with reshape.
print(tf.reshape(rank_3_tensor, [2, 3, 5]), "\n")
# This is a mess
print(tf.reshape(rank_3_tensor, [5, 6]), "\n")
# This doesn't work at all
try:
tf.reshape(rank_3_tensor, [7, -1])
except Exception as e:
print(f"{type(e).__name__}: {e}")
tf.Tensor( [[[ 0 1 2 3 4] [ 5 6 7 8 9] [10 11 12 13 14]] [[15 16 17 18 19] [20 21 22 23 24] [25 26 27 28 29]]], shape=(2, 3, 5), dtype=int32) tf.Tensor( [[ 0 1 2 3 4 5] [ 6 7 8 9 10 11] [12 13 14 15 16 17] [18 19 20 21 22 23] [24 25 26 27 28 29]], shape=(5, 6), dtype=int32) InvalidArgumentError: Input to reshape is a tensor with 30 values, but the requested shape requires a multiple of 7 [Op:Reshape]
Algunas malas remodelaciones. | ||
---|---|---|
Puede encontrarse con formas no completamente especificadas. La forma contiene None
(se desconoce la longitud del eje) o la forma completa es None
(se desconoce el rango del tensor).
A excepción de tf.RaggedTensor , estas formas solo aparecerán en el contexto de las API simbólicas de creación de gráficos de TensorFlow:
Más sobre DTypes
Para inspeccionar el tipo de datos de tf.Tensor
, use la propiedad Tensor.dtype
.
Al crear un tf.Tensor
a partir de un objeto de Python, puede especificar opcionalmente el tipo de datos.
Si no lo hace, TensorFlow elige un tipo de datos que pueda representar sus datos. TensorFlow convierte números enteros de Python en tf.int32
y números de punto flotante de Python en tf.float32
. De lo contrario, TensorFlow usa las mismas reglas que usa NumPy cuando se convierte en matrices.
Puede transmitir de un tipo a otro.
the_f64_tensor = tf.constant([2.2, 3.3, 4.4], dtype=tf.float64)
the_f16_tensor = tf.cast(the_f64_tensor, dtype=tf.float16)
# Now, cast to an uint8 and lose the decimal precision
the_u8_tensor = tf.cast(the_f16_tensor, dtype=tf.uint8)
print(the_u8_tensor)
tf.Tensor([2 3 4], shape=(3,), dtype=uint8)
Radiodifusión
La transmisión es un concepto tomado de la función equivalente en NumPy . En resumen, bajo ciertas condiciones, los tensores más pequeños se "estiran" automáticamente para adaptarse a tensores más grandes cuando se ejecutan operaciones combinadas en ellos.
El caso más simple y común es cuando intentas multiplicar o sumar un tensor a un escalar. En ese caso, el escalar se transmite para que tenga la misma forma que el otro argumento.
x = tf.constant([1, 2, 3])
y = tf.constant(2)
z = tf.constant([2, 2, 2])
# All of these are the same computation
print(tf.multiply(x, 2))
print(x * y)
print(x * z)
tf.Tensor([2 4 6], shape=(3,), dtype=int32) tf.Tensor([2 4 6], shape=(3,), dtype=int32) tf.Tensor([2 4 6], shape=(3,), dtype=int32)
Del mismo modo, los ejes con longitud 1 se pueden estirar para que coincidan con los otros argumentos. Ambos argumentos se pueden estirar en el mismo cálculo.
En este caso, una matriz de 3x1 se multiplica por elementos por una matriz de 1x4 para producir una matriz de 3x4. Tenga en cuenta que el 1 inicial es opcional: la forma de y es [4]
.
# These are the same computations
x = tf.reshape(x,[3,1])
y = tf.range(1, 5)
print(x, "\n")
print(y, "\n")
print(tf.multiply(x, y))
tf.Tensor( [[1] [2] [3]], shape=(3, 1), dtype=int32) tf.Tensor([1 2 3 4], shape=(4,), dtype=int32) tf.Tensor( [[ 1 2 3 4] [ 2 4 6 8] [ 3 6 9 12]], shape=(3, 4), dtype=int32)
Una adición transmitida: un [3, 1] por un [1, 4] da un [3,4] |
---|
Aquí está la misma operación sin transmisión:
x_stretch = tf.constant([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3]])
y_stretch = tf.constant([[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]])
print(x_stretch * y_stretch) # Again, operator overloading
tf.Tensor( [[ 1 2 3 4] [ 2 4 6 8] [ 3 6 9 12]], shape=(3, 4), dtype=int32)
La mayoría de las veces, la transmisión es eficiente tanto en tiempo como en espacio, ya que la operación de transmisión nunca materializa los tensores expandidos en la memoria.
Verá cómo se ve la transmisión usando tf.broadcast_to
.
print(tf.broadcast_to(tf.constant([1, 2, 3]), [3, 3]))
tf.Tensor( [[1 2 3] [1 2 3] [1 2 3]], shape=(3, 3), dtype=int32)
A diferencia de una operación matemática, por ejemplo, broadcast_to
no hace nada especial para ahorrar memoria. Aquí, estás materializando el tensor.
Puede volverse aún más complicado. Esta sección del libro Python Data Science Handbook de Jake VanderPlas muestra más trucos de transmisión (nuevamente en NumPy).
tf.convert_to_tensor
La mayoría de las operaciones, como tf.matmul
y tf.reshape
toman argumentos de la clase tf.Tensor
. Sin embargo, notará que en el caso anterior, se aceptan objetos de Python con forma de tensores.
La mayoría de las operaciones, pero no todas, llaman a convert_to_tensor
en argumentos que no son tensores. Hay un registro de conversiones, y la mayoría de las clases de objetos como ndarray
de NumPy, TensorShape
, listas de Python y tf.Variable
se convertirán automáticamente.
Consulte tf.register_tensor_conversion_function
para obtener más detalles y, si tiene su propio tipo, le gustaría convertirlo automáticamente en un tensor.
Tensores irregulares
Un tensor con un número variable de elementos a lo largo de algún eje se llama "irregular". Use tf.ragged.RaggedTensor
para datos irregulares.
Por ejemplo, Esto no se puede representar como un tensor regular:
A tf.RaggedTensor , forma: [4, None] |
---|
ragged_list = [
[0, 1, 2, 3],
[4, 5],
[6, 7, 8],
[9]]
try:
tensor = tf.constant(ragged_list)
except Exception as e:
print(f"{type(e).__name__}: {e}")
ValueError: Can't convert non-rectangular Python sequence to Tensor.
En su lugar, cree un tf.RaggedTensor
usando tf.ragged.constant
:
ragged_tensor = tf.ragged.constant(ragged_list)
print(ragged_tensor)
<tf.RaggedTensor [[0, 1, 2, 3], [4, 5], [6, 7, 8], [9]]>
La forma de un tf.RaggedTensor
contendrá algunos ejes con longitudes desconocidas:
print(ragged_tensor.shape)
(4, None)
tensores de cuerda
tf.string
es un dtype
, lo que significa que puede representar datos como cadenas (matrices de bytes de longitud variable) en tensores.
Las cadenas son atómicas y no se pueden indexar como las cadenas de Python. La longitud de la cuerda no es uno de los ejes del tensor. Ver tf.strings
para funciones para manipularlos.
Aquí hay un tensor de cuerda escalar:
# Tensors can be strings, too here is a scalar string.
scalar_string_tensor = tf.constant("Gray wolf")
print(scalar_string_tensor)
tf.Tensor(b'Gray wolf', shape=(), dtype=string)
Y un vector de cadenas:
Un vector de cadenas, forma: [3,] |
---|
# If you have three string tensors of different lengths, this is OK.
tensor_of_strings = tf.constant(["Gray wolf",
"Quick brown fox",
"Lazy dog"])
# Note that the shape is (3,). The string length is not included.
print(tensor_of_strings)
tf.Tensor([b'Gray wolf' b'Quick brown fox' b'Lazy dog'], shape=(3,), dtype=string)
En la impresión anterior, el prefijo b
indica que tf.string
dtype no es una cadena Unicode, sino una cadena de bytes. Consulta el tutorial de Unicode para obtener más información sobre cómo trabajar con texto Unicode en TensorFlow.
Si pasa caracteres Unicode, están codificados en utf-8.
tf.constant("🥳👍")
<tf.Tensor: shape=(), dtype=string, numpy=b'\xf0\x9f\xa5\xb3\xf0\x9f\x91\x8d'>
Algunas funciones básicas con cadenas se pueden encontrar en tf.strings
, incluyendo tf.strings.split
.
# You can use split to split a string into a set of tensors
print(tf.strings.split(scalar_string_tensor, sep=" "))
tf.Tensor([b'Gray' b'wolf'], shape=(2,), dtype=string)
# ...but it turns into a `RaggedTensor` if you split up a tensor of strings,
# as each string might be split into a different number of parts.
print(tf.strings.split(tensor_of_strings))
<tf.RaggedTensor [[b'Gray', b'wolf'], [b'Quick', b'brown', b'fox'], [b'Lazy', b'dog']]>
Tres cuerdas divididas, forma: [3, None] |
---|
Y tf.string.to_number
:
text = tf.constant("1 10 100")
print(tf.strings.to_number(tf.strings.split(text, " ")))
tf.Tensor([ 1. 10. 100.], shape=(3,), dtype=float32)
Aunque no puede usar tf.cast
para convertir un tensor de cadena en números, puede convertirlo en bytes y luego en números.
byte_strings = tf.strings.bytes_split(tf.constant("Duck"))
byte_ints = tf.io.decode_raw(tf.constant("Duck"), tf.uint8)
print("Byte strings:", byte_strings)
print("Bytes:", byte_ints)
Byte strings: tf.Tensor([b'D' b'u' b'c' b'k'], shape=(4,), dtype=string) Bytes: tf.Tensor([ 68 117 99 107], shape=(4,), dtype=uint8)
# Or split it up as unicode and then decode it
unicode_bytes = tf.constant("アヒル 🦆")
unicode_char_bytes = tf.strings.unicode_split(unicode_bytes, "UTF-8")
unicode_values = tf.strings.unicode_decode(unicode_bytes, "UTF-8")
print("\nUnicode bytes:", unicode_bytes)
print("\nUnicode chars:", unicode_char_bytes)
print("\nUnicode values:", unicode_values)
Unicode bytes: tf.Tensor(b'\xe3\x82\xa2\xe3\x83\x92\xe3\x83\xab \xf0\x9f\xa6\x86', shape=(), dtype=string) Unicode chars: tf.Tensor([b'\xe3\x82\xa2' b'\xe3\x83\x92' b'\xe3\x83\xab' b' ' b'\xf0\x9f\xa6\x86'], shape=(5,), dtype=string) Unicode values: tf.Tensor([ 12450 12498 12523 32 129414], shape=(5,), dtype=int32)
El tf.string
dtype se usa para todos los datos de bytes sin procesar en TensorFlow. El módulo tf.io
contiene funciones para convertir datos hacia y desde bytes, incluida la decodificación de imágenes y el análisis de csv.
Tensores dispersos
A veces, sus datos son escasos, como un espacio de incrustación muy amplio. TensorFlow admite tf.sparse.SparseTensor
y operaciones relacionadas para almacenar datos dispersos de manera eficiente.
A tf.SparseTensor , forma: [3, 4] |
---|
# Sparse tensors store values by index in a memory-efficient manner
sparse_tensor = tf.sparse.SparseTensor(indices=[[0, 0], [1, 2]],
values=[1, 2],
dense_shape=[3, 4])
print(sparse_tensor, "\n")
# You can convert sparse tensors to dense
print(tf.sparse.to_dense(sparse_tensor))
SparseTensor(indices=tf.Tensor( [[0 0] [1 2]], shape=(2, 2), dtype=int64), values=tf.Tensor([1 2], shape=(2,), dtype=int32), dense_shape=tf.Tensor([3 4], shape=(2,), dtype=int64)) tf.Tensor( [[1 0 0 0] [0 0 2 0] [0 0 0 0]], shape=(3, 4), dtype=int32)