15. Operaciones básicas con tensores

15.1 Introducción

En el tema anterior vimos qué son los tensores y por qué ocupan un lugar central dentro de PyTorch. Aprendimos que los tensores representan datos, parámetros, salidas y gradientes.

Sin embargo, conocer un objeto no es suficiente. El paso siguiente es aprender a operar con él. En Deep Learning no solo almacenamos tensores: los sumamos, multiplicamos, reorganizamos, recortamos, combinamos y transformamos constantemente.

Por eso este tema es fundamental. Aquí veremos las operaciones básicas con tensores, explicadas de forma clara y gradual, pensando en un estudiante que está dando sus primeros pasos en PyTorch.

15.2 Qué significa operar con un tensor

Operar con un tensor significa aplicar sobre él alguna transformación matemática o estructural.

Por ejemplo:

  • Sumarlo con otro tensor.
  • Restarle un valor.
  • Multiplicarlo.
  • Cambiar su forma.
  • Extraer una parte de sus datos.
  • Combinarlo con otros tensores.

Estas operaciones son las que permiten que una red neuronal procese información y produzca resultados.

15.3 Por qué estas operaciones son importantes en Deep Learning

Cuando una red neuronal funciona, lo hace a través de operaciones sobre tensores. No hay magia: lo que ocurre en cada capa es, en gran parte, cálculo entre tensores.

Por ejemplo:

  • La entrada se combina con pesos.
  • Se agregan bias.
  • Se aplican funciones de activación.
  • Se calcula la pérdida comparando salidas con valores reales.
  • Se obtienen gradientes para actualizar parámetros.

Por eso, antes de construir modelos más completos, conviene dominar bien estas operaciones básicas.

15.4 Crear tensores para practicar

Para estudiar operaciones conviene partir de ejemplos pequeños. Por ejemplo:

import torch
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])

Con estos dos tensores podemos practicar suma, resta, multiplicación y otras operaciones de manera simple.

Trabajar con ejemplos pequeños tiene una ventaja didáctica muy importante: permite entender la lógica antes de pasar a estructuras más grandes.

15.5 Suma de tensores

Una de las operaciones más básicas es la suma. Si dos tensores tienen la misma forma, PyTorch puede sumarlos elemento por elemento.

import torch
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = a + b
print(c)

El resultado será un tensor con valores [5, 7, 9].

Es importante notar que la suma se hace posición por posición: el primer elemento con el primero, el segundo con el segundo y así sucesivamente.

15.6 Resta de tensores

La resta funciona de manera similar a la suma. Si dos tensores tienen formas compatibles, PyTorch resta cada elemento con el correspondiente.

import torch
a = torch.tensor([10, 20, 30])
b = torch.tensor([1, 2, 3])
c = a - b
print(c)

El resultado será [9, 18, 27].

Esta operación aparece constantemente cuando calculamos diferencias entre predicciones y valores reales.

15.7 Multiplicación elemento a elemento

Una cuestión importante es distinguir entre distintos tipos de multiplicación. Si escribimos:

import torch
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = a * b
print(c)

PyTorch realiza una multiplicación elemento a elemento.

El resultado será [4, 10, 18]. Cada posición se multiplica con la posición correspondiente.

Esta idea es muy importante, porque no debe confundirse con el producto matricial, que tiene otra lógica.

15.8 División elemento a elemento

También podemos dividir tensores elemento por elemento:

import torch
a = torch.tensor([10.0, 20.0, 30.0])
b = torch.tensor([2.0, 4.0, 5.0])
c = a / b
print(c)

La lógica vuelve a ser la misma: cada componente de un tensor se divide por el componente correspondiente del otro tensor.

En este caso conviene usar números de punto flotante para evitar confusiones y obtener resultados decimales cuando haga falta.

15.9 Operaciones con escalares

No siempre operamos tensor contra tensor. Muchas veces también combinamos un tensor con un número escalar.

import torch
x = torch.tensor([1, 2, 3])
print(x + 10)
print(x * 2)

En el primer caso, se suma 10 a cada elemento. En el segundo, cada elemento se multiplica por 2.

Esta forma de trabajo es muy frecuente y resulta muy cómoda para aplicar transformaciones uniformes.

15.10 Potencias y otras transformaciones

También podemos elevar cada elemento de un tensor a una potencia o aplicar otras operaciones matemáticas básicas.

import torch
x = torch.tensor([1.0, 2.0, 3.0])
print(x ** 2)

El resultado será [1.0, 4.0, 9.0].

Estas transformaciones son útiles en muchas expresiones matemáticas, por ejemplo en algunos tipos de funciones de pérdida.

15.11 Funciones matemáticas sobre tensores

PyTorch ofrece muchas funciones matemáticas que se aplican a tensores completos. Por ejemplo, se pueden calcular raíces, exponenciales, logaritmos, senos y otras transformaciones.

import torch
x = torch.tensor([1.0, 4.0, 9.0])
print(torch.sqrt(x))

Aquí la raíz cuadrada se calcula para cada elemento del tensor.

Esto refuerza una idea central: muchas operaciones en PyTorch se aplican de manera natural a todos los valores de la estructura.

15.12 Consultar máximos, mínimos y sumas

Además de transformar tensores, muchas veces necesitamos resumir la información que contienen.

Por ejemplo, podemos calcular:

  • La suma de todos los elementos.
  • El valor máximo.
  • El valor mínimo.
  • El promedio.

Estas operaciones se conocen como reducciones, porque toman muchos valores y producen un resultado más pequeño.

15.13 Sumar todos los elementos

Para obtener la suma total de un tensor, se usa sum():

import torch
x = torch.tensor([1, 2, 3, 4])
print(x.sum())

La salida será 10.

Esto puede ser útil, por ejemplo, al acumular errores, contar activaciones o construir algunas métricas.

15.14 Máximo, mínimo y media

PyTorch también permite obtener medidas básicas de un tensor:

import torch
x = torch.tensor([1.0, 2.0, 3.0, 4.0])
print(x.max())
print(x.min())
print(x.mean())

Estas funciones ayudan a inspeccionar valores y comprender la escala de los datos o de los resultados de una capa.

15.15 Indexación: acceder a elementos

Una operación muy importante es acceder a elementos específicos de un tensor. Esto se hace mediante índices, igual que en muchas estructuras de Python.

import torch
x = torch.tensor([10, 20, 30, 40])
print(x[0])
print(x[2])

El índice 0 accede al primer elemento y el índice 2 al tercero.

La indexación es fundamental porque nos permite inspeccionar o manipular partes concretas de un tensor.

15.16 Indexación en dos dimensiones

Cuando el tensor tiene dos dimensiones, necesitamos indicar fila y columna.

import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
print(x[0, 1])
print(x[1, 2])

En el primer caso accedemos a la fila 0, columna 1. En el segundo, a la fila 1, columna 2.

Esto resulta muy parecido a trabajar con una matriz.

Sin embargo, conviene notar una diferencia de sintaxis con las listas bidimensionales del lenguaje Python. En una lista común escribiríamos lista[0][1], mientras que en un tensor de PyTorch usamos x[0, 1], separando los índices con coma dentro de un único par de corchetes.

15.17 Slicing: extraer porciones

No siempre queremos un solo elemento. A veces necesitamos una parte del tensor. Para eso usamos slicing.

import torch
x = torch.tensor([10, 20, 30, 40, 50])
print(x[1:4])

El resultado será un tensor con [20, 30, 40].

El slicing permite recortar subconjuntos de datos, lo cual es muy útil en procesamiento y preparación de información.

15.18 Cambiar valores dentro de un tensor

También es posible modificar valores específicos:

import torch
x = torch.tensor([1, 2, 3])
x[1] = 99
print(x)

Después de la asignación, el tensor pasa a contener [1, 99, 3].

Esto puede ser útil en ejemplos simples, aunque en muchos flujos de trabajo de Deep Learning conviene evitar modificaciones innecesarias si afectan claridad o gradientes.

15.19 Cambiar la forma con reshape

Muchas veces los datos vienen con una forma y el modelo necesita otra. Para eso PyTorch permite reorganizar la estructura usando reshape().

import torch
x = torch.tensor([1, 2, 3, 4, 5, 6])
y = x.reshape(2, 3)
print(y)

Ahora el tensor deja de verse como una secuencia de seis elementos y pasa a verse como una tabla de dos filas por tres columnas.

Es importante notar que la cantidad total de valores no cambia; solo cambia la organización.

15.20 Aplanar un tensor

La operación inversa también es muy frecuente: convertir una estructura de varias dimensiones en una secuencia lineal. Esto suele llamarse flatten.

import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
y = x.reshape(-1)
print(y)

El resultado será un tensor unidimensional con los mismos seis valores.

Esta operación aparece, por ejemplo, al conectar la salida de una parte del modelo con una capa totalmente conectada.

15.21 Transposición

En tensores bidimensionales, otra operación útil es intercambiar filas por columnas. Esto se conoce como transposición.

import torch
x = torch.tensor([[1, 2, 3], [4, 5, 6]])
y = x.T
print(y)

La forma pasa de 2 por 3 a 3 por 2.

La transposición aparece con frecuencia en operaciones matriciales y en formulaciones matemáticas del entrenamiento.

15.22 Concatenar tensores

En algunos casos necesitamos unir tensores. Una forma común es usar torch.cat().

import torch
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = torch.cat((a, b))
print(c)

El resultado será [1, 2, 3, 4, 5, 6].

Concatenar resulta útil cuando queremos combinar información proveniente de distintas partes.

15.23 Apilar tensores

Otra operación relacionada es torch.stack(). A diferencia de concatenar en una dimensión ya existente, apilar suele crear una nueva dimensión.

import torch
a = torch.tensor([1, 2, 3])
b = torch.tensor([4, 5, 6])
c = torch.stack((a, b))
print(c)

En este caso, el resultado será una estructura de dos filas, donde la primera corresponde a a y la segunda a b.

Esta diferencia entre concatenar y apilar conviene entenderla bien, porque cambia la forma del tensor resultante.

15.24 Producto matricial

Hasta ahora vimos multiplicación elemento a elemento. Pero en Deep Learning también es muy importante el producto matricial.

Una forma de hacerlo es mediante torch.matmul():

import torch
a = torch.tensor([[1, 2], [3, 4]])
b = torch.tensor([[5, 6], [7, 8]])
c = torch.matmul(a, b)
print(c)

Esta operación no multiplica simplemente posición por posición. Sigue las reglas del producto entre matrices.

Es una operación fundamental en redes neuronales, porque las capas lineales se basan precisamente en este tipo de cálculo.

15.25 Compatibilidad de formas

No todas las operaciones se pueden hacer entre tensores cualesquiera. Muchas requieren que las formas sean compatibles.

Por ejemplo, para sumar dos tensores elemento a elemento normalmente necesitamos que tengan la misma forma o una compatibilidad especial que permita broadcasting.

En el caso del producto matricial, también existen reglas específicas sobre cuántas columnas y filas deben coincidir.

Por eso, revisar shape antes de una operación importante es una costumbre excelente.

15.26 Broadcasting

Una característica poderosa de PyTorch es el broadcasting. Esto permite operar tensores de formas diferentes cuando esas formas son compatibles según ciertas reglas.

Un ejemplo simple sería sumar un escalar a un tensor:

import torch
x = torch.tensor([1, 2, 3])
print(x + 5)

Aunque 5 no tenga la misma forma que el tensor, PyTorch “expande” conceptualmente ese valor para aplicarlo a todos los elementos.

Más adelante veremos casos más complejos, pero desde ya conviene saber que broadcasting es una herramienta muy útil.

15.27 Operaciones in-place

PyTorch también permite algunas operaciones que modifican el tensor original en lugar de crear uno nuevo. Estas se llaman operaciones in-place.

Por ejemplo, ciertos métodos terminan en guion bajo. Sin embargo, al comenzar conviene usarlas con cuidado, porque pueden volver el código menos claro o interferir en algunos contextos con el sistema de gradientes.

Para aprender, suele ser mejor empezar con operaciones más explícitas y fáciles de seguir.

15.28 Relación entre operaciones y entrenamiento

Todas las operaciones que estamos viendo no son ejercicios aislados. Forman parte del trabajo real de un modelo de Deep Learning.

Por ejemplo:

  • Las entradas se reorganizan y convierten en tensores adecuados.
  • Las capas realizan productos matriciales.
  • Las activaciones aplican transformaciones matemáticas.
  • La pérdida resume diferencias mediante operaciones de reducción.
  • El entrenamiento completo depende de cadenas de operaciones entre tensores.

Por eso, aprender estas operaciones es prepararse para entender el funcionamiento interno de las redes.

15.29 Errores comunes al trabajar con operaciones

Los errores más frecuentes en este tema suelen venir de:

  • Confundir multiplicación elemento a elemento con producto matricial.
  • No revisar la forma de los tensores antes de una operación.
  • Suponer que dos tensores pueden sumarse aunque sus dimensiones no coincidan.
  • No distinguir entre concatenar y apilar.
  • Olvidar que algunas funciones producen un escalar y no otro tensor grande.

La mejor forma de evitar estos problemas es practicar con ejemplos pequeños y mirar siempre el resultado junto con su forma.

15.30 Buenas prácticas para estudiantes

Si estás empezando, estas recomendaciones suelen ayudar mucho:

  • Practicar primero con tensores pequeños.
  • Imprimir el tensor antes y después de cada operación.
  • Consultar shape con frecuencia.
  • Separar claramente en tu mente operaciones elemento a elemento y operaciones matriciales.
  • No pasar demasiado rápido a ejemplos complejos.

Comprender bien estas bases evita muchos problemas cuando luego se trabaja con modelos reales.

15.31 Qué debes recordar de este tema

  • PyTorch permite realizar muchas operaciones matemáticas y estructurales con tensores.
  • La suma, resta, multiplicación y división simples suelen aplicarse elemento por elemento.
  • También es posible operar tensores con escalares.
  • Las reducciones como sum, max, min y mean resumen información.
  • La indexación y el slicing permiten acceder a partes concretas del tensor.
  • reshape, transposición, concatenación y apilado modifican o reorganizan la estructura.
  • El producto matricial es distinto de la multiplicación elemento a elemento y es clave en redes neuronales.
  • Revisar la forma de los tensores es esencial para evitar errores.

15.32 Conclusión

Las operaciones básicas con tensores son una parte esencial del trabajo con PyTorch. A través de ellas, los datos dejan de ser simples valores almacenados y se convierten en estructuras activas que pueden transformarse, combinarse y analizarse.

Dominar estas operaciones significa estar mucho mejor preparado para entender cómo se construye una red, cómo procesa entradas y cómo aprende durante el entrenamiento.

En el próximo tema comenzaremos a dar un paso todavía más concreto: la construcción de una red neuronal simple con PyTorch.