Tutorial

Tensores

Importamos las librerías necesarias

import tensorflow as tf
import numpy as np

Versión de TensorFlow

tf.__version__

Definimos un tensor

# Tensor con valores constantes 2D
tensor_a = tf.constant([[1, 2], [3, 4]])
# Tensor variable 2D
tensor_b = tf.Variable([[5, 6], [7, 8]])  

La diferencia principal entre un tensor constante y una variable en TensorFlow radica en su capacidad para cambiar de valor durante la ejecución de un programa.

Tensor Constante: Un tensor constante tiene un valor que no cambia durante la ejecución del programa. Su valor se define al momento de su creación y permanece constante a lo largo de toda la ejecución. Tensor Variable: Un tensor variable en puede cambiar su valor durante la ejecución del programa. Su valor puede ser actualizado y modificado a través de operaciones como assign, lo que permite que sea utilizado para representar pesos entrenables en modelos.

En resumen, los tensores constantes son útiles para representar valores fijos que no cambian, mientras que las variables son útiles para representar valores que deben ser ajustados y optimizados durante el entrenamiento de modelos de aprendizaje automático y aprendizaje profundo.

Otra manera de crear tensores variables

# Tensor de 1000x1000 elementos
otro_tensor = tf.random.normal([1000, 1000]) 

Algunas propiedades de los tensores:

print("Forma del tensor A:", tensor_a.shape)
print("Tipo de datos del tensor A:", tensor_a.dtype)
print("Es tensor A variable?", isinstance(tensor_a, tf.Variable))

tensor_a.shape muestra la forma (tamaño) del tensor tensor_a.

tensor_a.dtype muestra el tipo de datos del tensor tensor_a.

isinstance(tensor_a, tf.Variable) verifica si tensor_a es un tensor variable o no.

Repita las operaciones anteriores para la variables tensor_b

Tensores de unos y ceros.

# Tensor de unos
tensor_unos = tf.ones([2, 3]) 

# Tensor de ceros 
tensor_ceros = tf.zeros([3, 2])  

Tensores de mayor dimensión

"""
    Definir un tensor de 3 canales (R,G,B)
    (por ejemplo, una imagen de 64x64 píxeles)
"""
tensor_3canales = tf.random.uniform([64, 64, 3])  

Operaciones con tensores

Definir tensores

tensor_a = tf.constant([[1, 2], [3, 4]])
tensor_b = tf.constant([[5, 6], [7, 8]])

tensor_3d = tf.random.uniform([64, 64, 3])
constante = tf.constant(2.0)

Suma de tensores

suma = tf.add(tensor_a, tensor_b)
print(suma)

# Otra forma de hacerlo
_suma = tensor_3d + tensor_3d
print(_suma)

Resta de tensores

resta = tf.subtract(tensor_a, tensor_b)
print(resta)

Multiplicación de tensores

multiplicacion = tf.multiply(tensor_a, tensor_b)
print(multiplicacion)

# Otra forma de hacerlo
_multiplicacion = tensor_3d * constante
print(multiplicacion)

Remark:

TensorFlow espera que los tensores tengan la misma forma o dimensiones para realizar la multiplicación de manera válida. Esto significa que los tensores deben tener el mismo número de elementos en cada dimensión para que la operación pueda realizarse correctamente.

Si los tensores no tienen la misma forma pero son compatibles en términos de broadcasting, TensorFlow aplicará broadcasting automáticamente para que las dimensiones coincidan antes de realizar la multiplicación elemento por elemento. Esto es similar al comportamiento de NumPy en Python.

_tensor_a = tf.constant([[1, 2], [3, 4]])
_tensor_b = tf.constant([1, 2])
_tensor_c = tf.constant([1])

multi = _tensor_a * _tensor_b
_multi = _tensor_a * _tensor_c

print(f"Dimensión del tensor A: {_tensor_a.shape}")
print(f"Dimensión del tensor B: {_tensor_b.shape}")
print(f"Dimensión del tensor C: {_tensor_c.shape}")

print(f"Multiplicación de A con B {multi}")
print(f"Multiplicación de A con C {_multi}")

Un ejemplo donde el broadcasting automático de TensorFlow no puede aplicarse debido a las dimensiones incompatibles de los tensores:

_tensor_a = tf.constant([[1, 2], [3, 4]]) 
_tensor_b = tf.constant([1, 2, 3])        

print(f"Dimensión del tensor A: {_tensor_a.shape}")
print(f"Dimensión del tensor B: {_tensor_b.shape}")

# Esto causará un error
m = _tensor_a * _tensor_b 

Multiplicación matricial

multiplicacion_mat = tf.matmul(tensor_a, tensor_b)
print(multiplicacion_mat)

En este ejemplo, tf.matmul(matriz_a, matriz_b) realizará la multiplicación matricial estándar, mientras que matriz_a * matriz_b o multiply realizará una multiplicación elemento por elemento.

División de tensores

division = tf.divide(tensor_a, tensor_b)
print(division)

Potencia de un tensor

# Elevar a la potencia 2 (cuadrado)
potencia = tf.pow(tensor_a, 2)
print(potencia)

Nota algo inesperado en los prints de anteriores

Sessiones

En versiones anteriores de TensorFlow (antes de la versión 2.x), las sesiones (tf.Session()) eran parte fundamental del flujo de trabajo para ejecutar operaciones y evaluar tensores en un grafo de computación.

Las sesiones proporcionaban un contexto de ejecución para las operaciones y permitían controlar cómo y dónde se realizaban los cálculos en TensorFlow.

Básicamente, una sesión en TensorFlow se encargaba de:

  1. Establecer el entorno de ejecución: Una vez que se creaba una sesión, se establecía el entorno de ejecución en el que las operaciones en un grafo de TensorFlow podían ser ejecutadas.

  2. Ejecutar operaciones y evaluar tensores: Dentro de una sesión, se podían ejecutar operaciones específicas del grafo y evaluar tensores para obtener resultados concretos.

  3. Administrar recursos: Las sesiones gestionaban recursos como memoria y dispositivos de cómputo (CPU, GPU, etc.), permitiendo optimizar el uso de recursos durante la ejecución de operaciones.

Sin embargo, con la introducción de TensorFlow 2.x y su modo de ejecución ansiosa (eager execution), las sesiones ya no son necesarias en la mayoría de los casos. El modo de ejecución ansiosa permite ejecutar operaciones de manera más interactiva y flexible, sin necesidad de crear explícitamente sesiones ni construir un grafo de computación antes de la ejecución.

En resumen, las sesiones en TensorFlow eran utilizadas para controlar la ejecución de operaciones en un grafo de computación de manera controlada y eficiente. Sin embargo, con TensorFlow 2.x y el modo de ejecución ansiosa, el uso de sesiones ha disminuido significativamente en favor de un flujo de trabajo más interactivo y simplificado.

Para ver los resultados esperado necesitamos ajustar el código de la siguiente manera:

Definimos una sesión para poder ver los resultados

import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()
sess = tf.compat.v1.Session()

Suma de tensores

suma = tf.add(tensor_a, tensor_b)
print(suma)
suma = sess.run(suma)
print(suma)

Resta de tensores

resta = tf.subtract(tensor_a, tensor_b)
print(resta)
resta = sess.run(resta)
print(resta)

Multiplicación de tensores

multiplicacion = tf.multiply(tensor_a, tensor_b)
print(multiplicacion)
multiplicacion = sess.run(multiplicacion)
print(multiplicacion)

Multiplicación matricial

multiplicacion_mat = tf.matmul(tensor_a, tensor_b)
print(multiplicacion_mat)
multiplicacion_mat = sess.run(multiplicacion_mat)
print(multiplicacion_mat)

División de tensores

division = tf.divide(tensor_a, tensor_b)
print(division)
division = sess.run(division)
print(division)

Potencia de un tensor

# Elevar a la potencia 2 (cuadrado)
potencia = tf.pow(tensor_a, 2)
print(potencia)
potencia = sess.run(potencia)
print(potencia)

Devices

Los "devices" en TensorFlow se refieren a los recursos de hardware, como las CPU, las GPU y otros dispositivos, que se utilizan para realizar cálculos y ejecutar operaciones en un grafo de computación.

  1. CPU (Central Processing Unit):

    • La CPU es el dispositivo de hardware principal de una computadora y se utiliza para realizar cálculos y ejecutar operaciones de propósito general.

    • En TensorFlow, las operaciones y cálculos se ejecutan por defecto en la CPU si no se especifica lo contrario.

    • La CPU es útil para tareas que no requieren un gran poder de cómputo o para realizar cálculos en lotes pequeños de datos.

  2. GPU (Graphics Processing Unit):

    • La GPU es un tipo de dispositivo de hardware diseñado específicamente para realizar cálculos intensivos y paralelos, especialmente en tareas relacionadas con gráficos, aprendizaje automático y aprendizaje profundo.

    • En TensorFlow, se pueden utilizar las GPU para acelerar el entrenamiento de modelos de aprendizaje automático y la ejecución de operaciones matriciales de gran tamaño.

    • La GPU es especialmente útil para entrenar modelos grandes con conjuntos de datos extensos y operaciones matriciales complejas.

  3. TPU (Tensor Processing Unit):

    • La TPU es un tipo de dispositivo de hardware diseñado específicamente para acelerar el entrenamiento y la inferencia de modelos de aprendizaje automático en TensorFlow.

    • Las TPU están optimizadas para operaciones de tensor y matrices, lo que las hace muy eficientes para tareas de aprendizaje automático y aprendizaje profundo.

    • Google Cloud ofrece acceso a TPUs para realizar cálculos de aprendizaje automático a gran escala en la nube.

En resumen, los devices en TensorFlow representan los recursos de hardware que se utilizan para realizar cálculos y ejecutar operaciones en un grafo de computación. La elección del device adecuado depende de las necesidades de cómputo de tu aplicación, el tamaño de tus datos y la complejidad de tus modelos de aprendizaje automático.

En TensorFlow podemos decidir en que device queremos trabajar.

# Uso en GPU vs CPU
with tf.device('/CPU:0'):
    tensor_cpu = tf.constant([[1, 2], [3, 4]])
with tf.device('/GPU:0'):
    tensor_gpu = tf.constant([[5, 6], [7, 8]])

print("Tensor en CPU:", tensor_cpu.device)
print("Tensor en GPU:", tensor_gpu.device)

También podemos comparar la velocidad con la que cada device trabaja.

import time

# Operación de multiplicación de matrices
start_time = time.time()
# Utilizar la CPU
with tf.device('/CPU:0'):  
    result_cpu = tf.matmul(tensor_a, tensor_b)
print("Tiempo de ejecución en CPU:", time.time() - start_time, "segundos")

# Operación de multiplicación de matrices
start_time = time.time()
# Utilizar la GPU
with tf.device('/GPU:0'):  
    result_gpu = tf.matmul(tensor_a, tensor_b)
print("Tiempo de ejecución en GPU:", time.time() - start_time, "segundos")

Los resultados arrojan que los calculos en la GPU con más rápidos que en la CPU.

Last updated