Capítulo 1: Introducción al Aprendizaje Profundo
1.3 Avances Recientes en el Aprendizaje Profundo
En los últimos años, el aprendizaje profundo ha realizado avances significativos, ampliando los límites de lo que la inteligencia artificial puede lograr. Estos avances están impulsados por una combinación de algoritmos mejorados, hardware más potente y la disponibilidad de grandes conjuntos de datos.
En esta sección, exploraremos algunos de los desarrollos más impactantes recientes en el aprendizaje profundo, incluidos los avances en arquitecturas de modelos, técnicas de entrenamiento y aplicaciones. Al comprender estas innovaciones de vanguardia, estarás mejor preparado para aprovechar las últimas tecnologías en tus proyectos.
1.3.1 Redes Transformer y Mecanismos de Atención
A lo largo de los años, el aprendizaje profundo ha visto numerosos avances, pero uno de los avances más significativos ha sido el desarrollo de las redes transformer. Estas redes innovadoras dependen en gran medida de lo que se llaman mecanismos de atención.
El concepto de redes transformer ha revolucionado completamente el campo del procesamiento del lenguaje natural (NLP). Anteriormente, los modelos procesaban secuencias de datos de manera secuencial. Sin embargo, con el advenimiento de las redes transformer, los modelos ahora son capaces de procesar secuencias completas de datos simultáneamente. Este cambio significativo en la arquitectura ha llevado a un procesamiento más eficiente y a mejores resultados.
Esta arquitectura revolucionaria ha allanado el camino para la creación de modelos altamente efectivos que han tenido un impacto profundo en el campo. Algunos de los modelos más notables incluyen BERT, GPT-3 y GPT-4. Cada uno de estos modelos ha realizado contribuciones sustanciales al campo, mejorando nuestra capacidad para entender e interpretar el lenguaje natural.
Ejemplo: Arquitectura Transformer
El modelo transformer consta de un codificador y un decodificador, ambos compuestos por múltiples capas de autoatención y redes neuronales de avance directo. El mecanismo de autoatención permite que el modelo pondere la importancia de diferentes partes de la secuencia de entrada, lo que le permite capturar dependencias de largo alcance.
import tensorflow as tf
from tensorflow.keras.layers import Dense, LayerNormalization, Dropout
from tensorflow.keras.models import Model
class MultiHeadAttention(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.num_heads = num_heads
self.depth = d_model // num_heads
self.wq = Dense(d_model)
self.wk = Dense(d_model)
self.wv = Dense(d_model)
self.dense = Dense(d_model)
def split_heads(self, x, batch_size):
x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
return tf.transpose(x, perm=[0, 2, 1, 3])
def call(self, v, k, q, mask):
batch_size = tf.shape(q)[0]
q = self.wq(q)
k = self.wk(k)
v = self.wv(v)
q = self.split_heads(q, batch_size)
k = self.split_heads(k, batch_size)
v = self.split_heads(v, batch_size)
scaled_attention, _ = scaled_dot_product_attention(q, k, v, mask)
scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])
concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model))
output = self.dense(concat_attention)
return output
def scaled_dot_product_attention(q, k, v, mask):
matmul_qk = tf.matmul(q, k, transpose_b=True)
dk = tf.cast(tf.shape(k)[-1], tf.float32)
scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
if mask is not None:
scaled_attention_logits += (mask * -1e9)
attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)
output = tf.matmul(attention_weights, v)
return output, attention_weights
# Sample transformer encoder layer
class EncoderLayer(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads, dff, rate=0.1):
super(EncoderLayer, self).__init__()
self.mha = MultiHeadAttention(d_model, num_heads)
self.ffn = tf.keras.Sequential([
Dense(dff, activation='relu'),
Dense(d_model)
])
self.layernorm1 = LayerNormalization(epsilon=1e-6)
self.layernorm2 = LayerNormalization(epsilon=1e-6)
self.dropout1 = Dropout(rate)
self.dropout2 = Dropout(rate)
def call(self, x, training, mask):
attn_output = self.mha(x, x, x, mask)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(x + attn_output)
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
out2 = self.layernorm2(out1 + ffn_output)
return out2
Este ejemplo demuestra la implementación de un modelo Transformer utilizando la biblioteca TensorFlow, específicamente la API de Keras. El modelo Transformer es un tipo de modelo de aprendizaje profundo que ha tenido particular éxito en el manejo de tareas de secuencia a secuencia, como la traducción de idiomas o el resumen de textos.
En primer lugar, se declara la clase MultiHeadAttention
. Esta clase representa el mecanismo de autoatención multi-cabeza en el modelo Transformer. Permite que el modelo se enfoque en diferentes posiciones de la secuencia de entrada al generar una secuencia de salida, haciendo posible capturar varios aspectos de la información de entrada.
La clase toma dos parámetros: d_model
, que es la dimensionalidad de la entrada, y num_heads
, que es el número de cabezas de atención. Dentro de la clase, se declaran varias capas densas para las transformaciones lineales de las consultas (queries), claves (keys) y valores (values). El método split_heads
remodela las consultas, claves y valores en múltiples cabezas, y el método call
aplica el mecanismo de atención en las consultas, claves y valores, y devuelve la salida.
A continuación, se define la función scaled_dot_product_attention
. Esta función calcula los pesos de atención y la salida para el mecanismo de atención. Calcula el producto punto de la consulta y la clave, lo escala por la raíz cuadrada de la profundidad (la última dimensión de la clave), aplica una máscara si se proporciona, y luego aplica una función softmax para obtener los pesos de atención. Estos pesos se utilizan luego para obtener una suma ponderada de los valores, que forma la salida del mecanismo de atención.
Finalmente, se define la clase EncoderLayer
. Esta clase representa una sola capa del codificador del Transformer. Cada capa del codificador consiste en un mecanismo de autoatención multi-cabeza y una red neuronal de avance punto a punto. El método call
aplica la autoatención en la entrada, seguido de dropout, conexión residual y normalización de capa. Luego, aplica la red de avance en la salida, seguida nuevamente de dropout, conexión residual y normalización de capa.
Cabe señalar que las capas densas se utilizan para transformar las entradas para el mecanismo de atención y dentro de la red de avance. Dropout se utiliza para prevenir el sobreajuste y LayerNormalization se utiliza para normalizar las salidas de cada subcapa. Todo el mecanismo de atención está encapsulado en la clase MultiHeadAttention
para su reutilización.
Este código sirve como base para construir modelos Transformer más complejos. Por ejemplo, se podrían apilar múltiples instancias de EncoderLayer
para formar la parte completa del codificador del Transformer, y se podrían definir capas similares para la parte del decodificador. Además, se podrían añadir componentes adicionales como codificación posicional y una capa de softmax de salida para completar el modelo.
1.3.2 Aprendizaje por Transferencia
El Aprendizaje por Transferencia es un método de aprendizaje automático donde un modelo desarrollado para una tarea se reutiliza como punto de partida para un modelo en una segunda tarea. Es un enfoque popular en el aprendizaje profundo donde los modelos preentrenados se utilizan como punto de partida en tareas de visión por computadora y procesamiento del lenguaje natural.
En otras palabras, el aprendizaje por transferencia es un método donde el conocimiento de un modelo adquirido de una tarea previa se aplica a un nuevo problema similar. Este enfoque es particularmente efectivo en el aprendizaje profundo debido a los vastos recursos computacionales y de tiempo necesarios para desarrollar modelos de redes neuronales en estos problemas y por los grandes saltos en habilidad que proporcionan en problemas relacionados.
Este enfoque se usa ampliamente en varias aplicaciones como el Procesamiento del Lenguaje Natural (NLP), la Visión por Computadora, e incluso en el campo de la música y el arte donde se están utilizando modelos generativos para crear nuevas obras de arte y componer música.
El Aprendizaje por Transferencia, por lo tanto, es una técnica poderosa que ayuda a mejorar el rendimiento de los modelos en tareas con datos limitados al aprovechar el conocimiento adquirido de tareas relacionadas con datos abundantes. Es uno de los avances significativos en el campo del Aprendizaje Profundo.
El aprendizaje por transferencia se ha convertido en una técnica poderosa en el aprendizaje profundo, permitiendo que los modelos entrenados en grandes conjuntos de datos se ajusten para tareas específicas con conjuntos de datos más pequeños. Este enfoque reduce significativamente los recursos computacionales y el tiempo requerido para el entrenamiento.
Ejemplo: Ajuste Fino de BERT para Clasificación de Texto
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had."]
labels = [1, 0] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Este ejemplo utiliza la biblioteca Transformers de Hugging Face. Este script demuestra cómo ajustar finamente un modelo BERT (Bidirectional Encoder Representations from Transformers) preentrenado para una tarea de clasificación binaria de texto. Desglosamos lo que hace el script.
El script comienza importando los módulos y clases necesarios. Trae BertTokenizer
y TFBertForSequenceClassification
de la biblioteca Transformers, que están específicamente diseñados para tareas que involucran modelos BERT. El BertTokenizer
se usa para convertir el texto de entrada en un formato que el modelo BERT puede entender, mientras que TFBertForSequenceClassification
es un modelo BERT con una capa de clasificación en la parte superior. El script también importa Adam
de la API de Keras de TensorFlow, que es el optimizador que se utilizará para entrenar el modelo.
A continuación, el script carga el modelo BERT preentrenado y su tokenizador asociado utilizando el método from_pretrained
. El argumento 'bert-base-uncased'
especifica que el script debe usar la versión "uncased" del modelo BERT base, lo que significa que el modelo no distingue entre letras mayúsculas y minúsculas. Este modelo ha sido entrenado en un gran corpus de texto en inglés y puede generar representaciones significativas para oraciones en inglés.
El script luego define algunos datos de muestra con fines de demostración. La variable texts
es una lista de dos oraciones en inglés, mientras que la variable labels
es una lista de dos enteros que representan el sentimiento de la oración correspondiente en la variable texts
(1 para sentimiento positivo, 0 para sentimiento negativo).
Después de definir los datos, el script tokeniza los textos de entrada utilizando el tokenizador cargado. La llamada al tokenizer
convierte las oraciones en la variable texts
en un formato que el modelo BERT puede entender. El método devuelve un diccionario que incluye varios objetos tipo tensor que el modelo necesita como entrada. El argumento return_tensors='tf'
especifica que estos objetos deben ser tensores de TensorFlow. El argumento padding=True
asegura que todas las oraciones se rellenen a la misma longitud, mientras que truncation=True
asegura que las oraciones más largas que la longitud máxima de entrada del modelo se recorten. El argumento max_length=128
especifica esta longitud máxima.
A continuación, el script compila el modelo especificando el optimizador, la función de pérdida y las métricas a rastrear durante el entrenamiento. El optimizador se establece en Adam con una tasa de aprendizaje de 2e-5. La función de pérdida se establece en el método compute_loss
integrado del modelo, que calcula la pérdida de clasificación. El script también especifica que se debe rastrear la precisión durante el entrenamiento.
Con el modelo ahora compilado, el script entrena el modelo con los datos de entrada. Se llama al método model.fit
con los tensores de entrada, las etiquetas y la configuración de entrenamiento adicional. El modelo se entrena durante 3 épocas, con un tamaño de lote de 8. Una época es un pase completo a través de todo el conjunto de datos de entrenamiento, y un tamaño de lote de 8 significa que los pesos del modelo se actualizan después de ver 8 muestras.
Finalmente, el script utiliza el modelo entrenado para hacer predicciones sobre los mismos datos de entrada. Se llama al método model.predict
con los tensores de entrada, y las predicciones resultantes se imprimen en la consola. Estas predicciones serían una medida de la confianza del modelo de que las oraciones de entrada tienen un sentimiento positivo.
1.3.3 Redes Generativas Antagónicas (GANs)
Las Redes Generativas Antagónicas (GANs) son una clase de algoritmos de inteligencia artificial utilizados en el aprendizaje automático no supervisado, implementados por un sistema de dos redes neuronales que compiten entre sí en un marco de juego de suma cero.
Las GANs consisten en dos partes, un Generador y un Discriminador. El Generador, que captura la distribución de los datos, comienza generando datos sintéticos y los alimenta al Discriminador junto con datos reales. El Discriminador, que estima la probabilidad de que una instancia dada provenga de los datos reales en lugar del Generador, se entrena para distinguir entre los dos tipos de datos.
En otras palabras, el Generador intenta engañar al Discriminador produciendo datos sintéticos cada vez mejores, mientras que el Discriminador se vuelve continuamente mejor para distinguir los datos reales de los falsos. Esto crea una especie de carrera armamentista entre los dos componentes, lo que lleva a la generación de datos sintéticos muy realistas.
Las GANs han visto una amplia aplicación en áreas como la generación de imágenes, la generación de videos y la generación de voz. Sin embargo, entrenar una GAN puede ser una tarea desafiante ya que requiere equilibrar el entrenamiento de dos redes diferentes.
Las GANs han revolucionado la modelización generativa al usar un generador y un discriminador en un entorno competitivo para producir datos sintéticos realistas. Las GANs se han aplicado a una amplia gama de tareas, desde la generación de imágenes hasta la mejora de datos.
Ejemplo: Implementación Básica de una GAN
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
Dense(1, activation='sigmoid')
])
return model
# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')
# Training the GAN
import numpy as np
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5 # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 128
epochs = 10000
for epoch in range(epochs):
# Train discriminator
idx = np.random.randint(0, x_train.shape[0], batch_size)
real_images = x_train[idx]
noise = np.random.normal(0, 1, (batch_size, 100))
fake_images = generator.predict(noise)
d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
# Train generator
noise = np.random.normal(0, 1, (batch_size, 100))
g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))
# Print progress
if epoch % 1000 == 0:
print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")
En el contexto de este código de ejemplo, el generador y el discriminador se construyen y compilan por separado. El generador utiliza una capa densa para mapear desde un espacio de ruido de 100 dimensiones a un espacio de 28281 dimensiones. El generador utiliza una función de activación LeakyReLU para la primera capa. La segunda capa es una capa densa con una función de activación tanh, seguida de una capa de remodelación para formar la imagen de salida.
El discriminador, por otro lado, es un clasificador que distingue entre imágenes reales y falsas (generadas). El modelo del discriminador toma como entrada una imagen de tamaño 28281, la aplana, la pasa a través de una capa densa con una función de activación LeakyReLU y finalmente a través de una capa densa con una función de activación sigmoide. El modelo del discriminador se compila luego con el optimizador adam y la pérdida de entropía cruzada binaria, ya que se trata de un problema de clasificación binaria.
El entrenamiento del GAN implica alternar entre entrenar el discriminador y el generador. Para entrenar el discriminador, se utilizan tanto imágenes reales (del conjunto de datos MNIST) como imágenes falsas (generadas por el generador). A las imágenes reales se les asigna una etiqueta de 1 y a las imágenes falsas se les asigna una etiqueta de 0. Luego, el discriminador se entrena en este conjunto de datos mixto.
Al entrenar el generador, el objetivo es engañar al discriminador. Por lo tanto, el generador intenta generar imágenes que sean clasificadas como reales (o 1) por el discriminador. El generador nunca ve realmente ninguna imagen real, solo recibe retroalimentación a través del discriminador.
El código también importa el conjunto de datos MNIST de los datasets de TensorFlow, normaliza las imágenes para que estén en el rango de [-1, 1] y las remodela para que tengan la forma (28, 28, 1).
El proceso de entrenamiento se repite durante un número determinado de épocas (iteraciones sobre todo el conjunto de datos), y en cada época se entrena primero el discriminador y luego el generador. La pérdida del discriminador (una medida de qué tan bien puede distinguir las imágenes reales de las falsas) y la pérdida del generador (una medida de qué tan bien puede engañar al discriminador) se imprimen después de cada época. De esta manera, se puede monitorear el proceso de entrenamiento.
Esta implementación básica de GAN sirve como un buen punto de partida para entender y experimentar con este tipo de redes. Sin embargo, en la práctica, las GANs pueden ser difíciles de entrenar y pueden requerir una selección cuidadosa de la arquitectura y los hiperparámetros.
1.3.4 Aprendizaje por Refuerzo
El aprendizaje por refuerzo (RL) ha visto avances significativos, particularmente con el desarrollo de redes de Q profundo (DQN) y métodos de gradiente de políticas. RL se ha aplicado con éxito en juegos, control robótico y conducción autónoma.
El aprendizaje por refuerzo es un tipo de aprendizaje automático en el que un agente aprende a tomar decisiones realizando acciones en un entorno para alcanzar un objetivo. El agente aprende de las consecuencias de sus acciones, en lugar de ser enseñado explícitamente, recibiendo recompensas o penalizaciones por sus acciones.
El agente aprende a alcanzar un objetivo en un entorno incierto y potencialmente complejo. En el aprendizaje por refuerzo, una inteligencia artificial se enfrenta a una situación similar a un juego. La computadora emplea prueba y error para encontrar una solución al problema. Para que la máquina haga lo que el programador quiere, la inteligencia artificial recibe recompensas o penalizaciones por las acciones que realiza. Su objetivo es maximizar la recompensa total.
A pesar de que el diseñador establece la política de recompensas, es decir, las reglas del juego, no le da al modelo pistas o sugerencias sobre cómo resolver el juego. Depende del modelo averiguar cómo realizar la tarea para maximizar la recompensa, comenzando con pruebas completamente aleatorias y terminando con tácticas sofisticadas y habilidades sobrehumanas. Al aprovechar el poder de la búsqueda y muchas pruebas, el aprendizaje por refuerzo es actualmente la forma más efectiva de estimular la creatividad de la máquina. A diferencia de los seres humanos, la inteligencia artificial puede acumular experiencia a partir de miles de juegos paralelos si un algoritmo de aprendizaje por refuerzo se ejecuta en una infraestructura informática suficientemente poderosa.
El aprendizaje por refuerzo se ha utilizado para enseñar a las máquinas a jugar juegos como Go y Ajedrez contra campeones mundiales, para simular la marcha bípeda, la conducción autónoma y otras tareas complejas que anteriormente se pensaba que solo los humanos podían lograr.
El futuro del aprendizaje por refuerzo es prometedor, ya que abre una vía para desarrollar máquinas que pueden aprender y adaptarse a escenarios complejos por sí solas. Sin embargo, al igual que cualquier otra tecnología de IA, también debe usarse de manera responsable, considerando todas sus implicaciones sociales y éticas.
Ejemplo: Q-Learning para Grid World
import numpy as np
# Environment setup
grid_size = 4
rewards = np.zeros((grid_size, grid_size))
rewards[3, 3] = 1 # Goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action selection
def choose_action(state):
if np
.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (3, 3):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
Este código de ejemplo implementa una forma básica de Q-Learning, un algoritmo de aprendizaje por refuerzo sin modelo, en un entorno simple de mundo cuadrícula.
La primera parte del código configura el entorno. Se define una cuadrícula de un tamaño determinado, con cada celda de la cuadrícula inicializada con una recompensa de cero. Sin embargo, al estado objetivo, ubicado en la celda de la cuadrícula (3,3), se le asigna una recompensa de uno. Este es el objetivo que el agente de aprendizaje debe esforzarse por alcanzar.
A continuación, se definen varios parámetros cruciales para el algoritmo de Q-Learning. El factor de descuento gamma
se establece en 0.9, lo que determina la importancia de las recompensas futuras. Un gamma
de 0 hace que el agente sea "miópico" (solo considerando recompensas actuales), mientras que un gamma
cercano a 1 hace que busque una recompensa alta a largo plazo. La tasa de aprendizaje alpha
se establece en 0.1, lo que determina en qué medida la información recién adquirida reemplaza la información antigua. La tasa de exploración epsilon
se establece en 0.1, lo que determina la frecuencia con la que el agente elige una acción aleatoria en lugar de la acción que cree que tiene el mejor efecto a largo plazo.
Luego se inicializa una tabla Q con ceros, que servirá como una tabla de búsqueda donde el agente puede encontrar la mejor acción a tomar mientras está en un determinado estado.
La función choose_action
es una implementación de la política epsilon-greedy. En este caso, la mayoría de las veces, el agente elegirá la acción que tiene la máxima recompensa futura esperada, que es la parte de explotación. Pero, un porcentaje epsilon del tiempo, el agente elegirá una acción aleatoria, que es la parte de exploración.
La parte principal del código es un bucle que simula 1000 episodios de interacción del agente con el entorno. Durante cada episodio, el agente comienza desde el estado inicial (0,0) y continúa eligiendo acciones y transitando al siguiente estado hasta llegar al estado objetivo (3,3). Para cada acción tomada, el valor Q de la acción para el estado actual se actualiza utilizando el algoritmo de Q-Learning, que actualiza el valor Q basado en la tasa de aprendizaje, la recompensa recibida y el valor Q máximo para el nuevo estado. Este proceso lleva de manera incremental a valores de acción cada vez mejores.
Al final del proceso de aprendizaje, el código imprime la tabla Q aprendida. Esta tabla indicará al agente el retorno esperado para cada acción en cada estado, guiando efectivamente al agente al objetivo de la manera más eficiente en cuanto a recompensas.
Este simple ejemplo de Q-Learning sirve como una base para entender la mecánica fundamental de este poderoso algoritmo de aprendizaje por refuerzo. Con entornos más complejos y mejoras en el algoritmo, Q-Learning puede resolver tareas mucho más complejas.
1.3.5 Aprendizaje Auto-supervisado
El aprendizaje auto-supervisado aprovecha los datos no etiquetados generando etiquetas sustitutas a partir de los propios datos. Este enfoque ha demostrado ser eficaz en tareas como el aprendizaje de representaciones y el preentrenamiento de modelos para tareas posteriores.
En el aprendizaje auto-supervisado, el sistema aprende a predecir algunas partes de los datos a partir de otras partes. Esto se hace creando una tarea "sustituta" para aprender a partir de una gran cantidad de datos no etiquetados, lo cual puede ser muy útil cuando los datos etiquetados son escasos o costosos de obtener. Las representaciones aprendidas suelen ser útiles para tareas posteriores, y el modelo puede ajustarse con un conjunto de datos etiquetado más pequeño para una tarea específica.
Por ejemplo, una tarea de aprendizaje auto-supervisado para imágenes podría ser predecir el color de una imagen en escala de grises. En este caso, el modelo aprendería características útiles sobre la estructura y el contenido de las imágenes sin necesidad de etiquetas proporcionadas por humanos.
El aprendizaje auto-supervisado ha mostrado gran promesa en una variedad de aplicaciones. Se ha utilizado con éxito para preentrenar modelos para tareas de procesamiento del lenguaje natural, donde primero se entrena un modelo para predecir la siguiente palabra en una oración, y luego se ajusta para una tarea específica como el análisis de sentimientos o la respuesta a preguntas. También ha mostrado promesa en la visión por computadora, donde los modelos preentrenados en una tarea auto-supervisada pueden ajustarse para tareas como la detección de objetos o la segmentación de imágenes.
Un ejemplo específico de aprendizaje auto-supervisado es un método llamado SimCLR (Aprendizaje Contrastivo Simple de Representaciones Visuales). En SimCLR, se entrena un modelo para reconocer si dos versiones aumentadas de una imagen son iguales o diferentes. El modelo aprende a extraer características que son consistentes en diferentes aumentos de la misma imagen, lo cual resulta ser una habilidad muy útil para muchas tareas de visión por computadora.
Ejemplo: Aprendizaje Contrastivo con SimCLR
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam
# Sample contrastive learning model (SimCLR)
def build_simclr_model(input_shape):
base_model = tf.keras.applications.ResNet50(include_top=False, input_shape=input_shape, pooling='avg')
base_model.trainable = True
model = Sequential([
base_model,
Flatten(),
Dense(128, activation='relu'),
Dense(128) # Projection head
])
return model
# Contrastive loss function
def contrastive_loss(y_true, y_pred):
temperature = 0.1
y_true = tf.cast(y_true, tf.int32)
y_pred = tf.math.l2_normalize(y_pred, axis=1)
logits = tf.matmul(y_pred, y_pred, transpose_b=True) / temperature
labels = tf.one_hot(y_true, depth=y_pred.shape[0])
return SparseCategoricalCrossentropy(from_logits=True)(labels, logits)
# Compile and train the model
input_shape = (224, 224, 3)
model = build_simclr_model(input_shape)
model.compile(optimizer=Adam(learning_rate=0.001), loss=contrastive_loss)
# Assuming 'x_train' and 'y_train' are the training data and labels (augmentations)
model.fit(x_train, y_train, epochs=10, batch_size=32, verbose=1)
El script comienza importando los módulos necesarios. Utiliza la biblioteca TensorFlow, una poderosa biblioteca de software de código abierto para el aprendizaje automático, y Keras, una API de redes neuronales de alto nivel que también es parte de TensorFlow.
La función build_simclr_model
se define para construir el modelo. La base del modelo es un modelo ResNet50 preentrenado, un modelo de aprendizaje profundo popular con 50 capas, ya entrenado en un gran conjunto de datos. include_top=False
significa que las capas de salida completamente conectadas del modelo utilizadas para la clasificación no están incluidas, y pooling='avg'
indica que se aplica un agrupamiento promedio global a la salida del último bloque de convolución de ResNet50, reduciendo la dimensionalidad de la salida. Luego se usa la API Sequential
para apilar capas sobre el modelo base. Se agrega una capa Flatten
para transformar el formato de las imágenes de una matriz bidimensional (de 28x28 píxeles) a una matriz unidimensional (de 28 * 28 = 784 píxeles). Se agregan dos capas Dense
, la primera con activación ReLU y la segunda sin activación, que sirve como la cabeza de proyección del modelo.
Después de la construcción del modelo, se define la función de pérdida contrastiva como contrastive_loss
. El aprendizaje contrastivo es un tipo de método de aprendizaje auto-supervisado que entrena modelos para aprender características similares a partir de datos similares. Esta función primero normaliza el vector de predicción, luego calcula el producto punto entre los vectores de predicción divididos por un parámetro de temperatura para crear los logits. Luego, crea etiquetas one-hot a partir de las etiquetas verdaderas y calcula la pérdida de entropía cruzada categórica escasa entre estas etiquetas y los logits.
El script luego compila y entrena el modelo SimCLR utilizando el optimizador Adam y la función de pérdida contrastiva. El optimizador Adam es una extensión del descenso de gradiente estocástico, un algoritmo popular para entrenar una amplia gama de modelos en el aprendizaje automático. La tasa de aprendizaje se establece en 0.001.
Luego, el modelo se ajusta a los datos de entrenamiento 'x_train' y 'y_train' durante 10 épocas con un tamaño de lote de 32. 'x_train' y 'y_train' son marcadores de posición en este contexto y serían reemplazados por los datos de entrenamiento reales y las etiquetas durante el entrenamiento en el mundo real. Una época es una medida del número de veces que se utilizan todos los vectores de entrenamiento una vez para actualizar los pesos en el proceso de entrenamiento.
1.3 Avances Recientes en el Aprendizaje Profundo
En los últimos años, el aprendizaje profundo ha realizado avances significativos, ampliando los límites de lo que la inteligencia artificial puede lograr. Estos avances están impulsados por una combinación de algoritmos mejorados, hardware más potente y la disponibilidad de grandes conjuntos de datos.
En esta sección, exploraremos algunos de los desarrollos más impactantes recientes en el aprendizaje profundo, incluidos los avances en arquitecturas de modelos, técnicas de entrenamiento y aplicaciones. Al comprender estas innovaciones de vanguardia, estarás mejor preparado para aprovechar las últimas tecnologías en tus proyectos.
1.3.1 Redes Transformer y Mecanismos de Atención
A lo largo de los años, el aprendizaje profundo ha visto numerosos avances, pero uno de los avances más significativos ha sido el desarrollo de las redes transformer. Estas redes innovadoras dependen en gran medida de lo que se llaman mecanismos de atención.
El concepto de redes transformer ha revolucionado completamente el campo del procesamiento del lenguaje natural (NLP). Anteriormente, los modelos procesaban secuencias de datos de manera secuencial. Sin embargo, con el advenimiento de las redes transformer, los modelos ahora son capaces de procesar secuencias completas de datos simultáneamente. Este cambio significativo en la arquitectura ha llevado a un procesamiento más eficiente y a mejores resultados.
Esta arquitectura revolucionaria ha allanado el camino para la creación de modelos altamente efectivos que han tenido un impacto profundo en el campo. Algunos de los modelos más notables incluyen BERT, GPT-3 y GPT-4. Cada uno de estos modelos ha realizado contribuciones sustanciales al campo, mejorando nuestra capacidad para entender e interpretar el lenguaje natural.
Ejemplo: Arquitectura Transformer
El modelo transformer consta de un codificador y un decodificador, ambos compuestos por múltiples capas de autoatención y redes neuronales de avance directo. El mecanismo de autoatención permite que el modelo pondere la importancia de diferentes partes de la secuencia de entrada, lo que le permite capturar dependencias de largo alcance.
import tensorflow as tf
from tensorflow.keras.layers import Dense, LayerNormalization, Dropout
from tensorflow.keras.models import Model
class MultiHeadAttention(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.num_heads = num_heads
self.depth = d_model // num_heads
self.wq = Dense(d_model)
self.wk = Dense(d_model)
self.wv = Dense(d_model)
self.dense = Dense(d_model)
def split_heads(self, x, batch_size):
x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
return tf.transpose(x, perm=[0, 2, 1, 3])
def call(self, v, k, q, mask):
batch_size = tf.shape(q)[0]
q = self.wq(q)
k = self.wk(k)
v = self.wv(v)
q = self.split_heads(q, batch_size)
k = self.split_heads(k, batch_size)
v = self.split_heads(v, batch_size)
scaled_attention, _ = scaled_dot_product_attention(q, k, v, mask)
scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])
concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model))
output = self.dense(concat_attention)
return output
def scaled_dot_product_attention(q, k, v, mask):
matmul_qk = tf.matmul(q, k, transpose_b=True)
dk = tf.cast(tf.shape(k)[-1], tf.float32)
scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
if mask is not None:
scaled_attention_logits += (mask * -1e9)
attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)
output = tf.matmul(attention_weights, v)
return output, attention_weights
# Sample transformer encoder layer
class EncoderLayer(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads, dff, rate=0.1):
super(EncoderLayer, self).__init__()
self.mha = MultiHeadAttention(d_model, num_heads)
self.ffn = tf.keras.Sequential([
Dense(dff, activation='relu'),
Dense(d_model)
])
self.layernorm1 = LayerNormalization(epsilon=1e-6)
self.layernorm2 = LayerNormalization(epsilon=1e-6)
self.dropout1 = Dropout(rate)
self.dropout2 = Dropout(rate)
def call(self, x, training, mask):
attn_output = self.mha(x, x, x, mask)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(x + attn_output)
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
out2 = self.layernorm2(out1 + ffn_output)
return out2
Este ejemplo demuestra la implementación de un modelo Transformer utilizando la biblioteca TensorFlow, específicamente la API de Keras. El modelo Transformer es un tipo de modelo de aprendizaje profundo que ha tenido particular éxito en el manejo de tareas de secuencia a secuencia, como la traducción de idiomas o el resumen de textos.
En primer lugar, se declara la clase MultiHeadAttention
. Esta clase representa el mecanismo de autoatención multi-cabeza en el modelo Transformer. Permite que el modelo se enfoque en diferentes posiciones de la secuencia de entrada al generar una secuencia de salida, haciendo posible capturar varios aspectos de la información de entrada.
La clase toma dos parámetros: d_model
, que es la dimensionalidad de la entrada, y num_heads
, que es el número de cabezas de atención. Dentro de la clase, se declaran varias capas densas para las transformaciones lineales de las consultas (queries), claves (keys) y valores (values). El método split_heads
remodela las consultas, claves y valores en múltiples cabezas, y el método call
aplica el mecanismo de atención en las consultas, claves y valores, y devuelve la salida.
A continuación, se define la función scaled_dot_product_attention
. Esta función calcula los pesos de atención y la salida para el mecanismo de atención. Calcula el producto punto de la consulta y la clave, lo escala por la raíz cuadrada de la profundidad (la última dimensión de la clave), aplica una máscara si se proporciona, y luego aplica una función softmax para obtener los pesos de atención. Estos pesos se utilizan luego para obtener una suma ponderada de los valores, que forma la salida del mecanismo de atención.
Finalmente, se define la clase EncoderLayer
. Esta clase representa una sola capa del codificador del Transformer. Cada capa del codificador consiste en un mecanismo de autoatención multi-cabeza y una red neuronal de avance punto a punto. El método call
aplica la autoatención en la entrada, seguido de dropout, conexión residual y normalización de capa. Luego, aplica la red de avance en la salida, seguida nuevamente de dropout, conexión residual y normalización de capa.
Cabe señalar que las capas densas se utilizan para transformar las entradas para el mecanismo de atención y dentro de la red de avance. Dropout se utiliza para prevenir el sobreajuste y LayerNormalization se utiliza para normalizar las salidas de cada subcapa. Todo el mecanismo de atención está encapsulado en la clase MultiHeadAttention
para su reutilización.
Este código sirve como base para construir modelos Transformer más complejos. Por ejemplo, se podrían apilar múltiples instancias de EncoderLayer
para formar la parte completa del codificador del Transformer, y se podrían definir capas similares para la parte del decodificador. Además, se podrían añadir componentes adicionales como codificación posicional y una capa de softmax de salida para completar el modelo.
1.3.2 Aprendizaje por Transferencia
El Aprendizaje por Transferencia es un método de aprendizaje automático donde un modelo desarrollado para una tarea se reutiliza como punto de partida para un modelo en una segunda tarea. Es un enfoque popular en el aprendizaje profundo donde los modelos preentrenados se utilizan como punto de partida en tareas de visión por computadora y procesamiento del lenguaje natural.
En otras palabras, el aprendizaje por transferencia es un método donde el conocimiento de un modelo adquirido de una tarea previa se aplica a un nuevo problema similar. Este enfoque es particularmente efectivo en el aprendizaje profundo debido a los vastos recursos computacionales y de tiempo necesarios para desarrollar modelos de redes neuronales en estos problemas y por los grandes saltos en habilidad que proporcionan en problemas relacionados.
Este enfoque se usa ampliamente en varias aplicaciones como el Procesamiento del Lenguaje Natural (NLP), la Visión por Computadora, e incluso en el campo de la música y el arte donde se están utilizando modelos generativos para crear nuevas obras de arte y componer música.
El Aprendizaje por Transferencia, por lo tanto, es una técnica poderosa que ayuda a mejorar el rendimiento de los modelos en tareas con datos limitados al aprovechar el conocimiento adquirido de tareas relacionadas con datos abundantes. Es uno de los avances significativos en el campo del Aprendizaje Profundo.
El aprendizaje por transferencia se ha convertido en una técnica poderosa en el aprendizaje profundo, permitiendo que los modelos entrenados en grandes conjuntos de datos se ajusten para tareas específicas con conjuntos de datos más pequeños. Este enfoque reduce significativamente los recursos computacionales y el tiempo requerido para el entrenamiento.
Ejemplo: Ajuste Fino de BERT para Clasificación de Texto
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had."]
labels = [1, 0] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Este ejemplo utiliza la biblioteca Transformers de Hugging Face. Este script demuestra cómo ajustar finamente un modelo BERT (Bidirectional Encoder Representations from Transformers) preentrenado para una tarea de clasificación binaria de texto. Desglosamos lo que hace el script.
El script comienza importando los módulos y clases necesarios. Trae BertTokenizer
y TFBertForSequenceClassification
de la biblioteca Transformers, que están específicamente diseñados para tareas que involucran modelos BERT. El BertTokenizer
se usa para convertir el texto de entrada en un formato que el modelo BERT puede entender, mientras que TFBertForSequenceClassification
es un modelo BERT con una capa de clasificación en la parte superior. El script también importa Adam
de la API de Keras de TensorFlow, que es el optimizador que se utilizará para entrenar el modelo.
A continuación, el script carga el modelo BERT preentrenado y su tokenizador asociado utilizando el método from_pretrained
. El argumento 'bert-base-uncased'
especifica que el script debe usar la versión "uncased" del modelo BERT base, lo que significa que el modelo no distingue entre letras mayúsculas y minúsculas. Este modelo ha sido entrenado en un gran corpus de texto en inglés y puede generar representaciones significativas para oraciones en inglés.
El script luego define algunos datos de muestra con fines de demostración. La variable texts
es una lista de dos oraciones en inglés, mientras que la variable labels
es una lista de dos enteros que representan el sentimiento de la oración correspondiente en la variable texts
(1 para sentimiento positivo, 0 para sentimiento negativo).
Después de definir los datos, el script tokeniza los textos de entrada utilizando el tokenizador cargado. La llamada al tokenizer
convierte las oraciones en la variable texts
en un formato que el modelo BERT puede entender. El método devuelve un diccionario que incluye varios objetos tipo tensor que el modelo necesita como entrada. El argumento return_tensors='tf'
especifica que estos objetos deben ser tensores de TensorFlow. El argumento padding=True
asegura que todas las oraciones se rellenen a la misma longitud, mientras que truncation=True
asegura que las oraciones más largas que la longitud máxima de entrada del modelo se recorten. El argumento max_length=128
especifica esta longitud máxima.
A continuación, el script compila el modelo especificando el optimizador, la función de pérdida y las métricas a rastrear durante el entrenamiento. El optimizador se establece en Adam con una tasa de aprendizaje de 2e-5. La función de pérdida se establece en el método compute_loss
integrado del modelo, que calcula la pérdida de clasificación. El script también especifica que se debe rastrear la precisión durante el entrenamiento.
Con el modelo ahora compilado, el script entrena el modelo con los datos de entrada. Se llama al método model.fit
con los tensores de entrada, las etiquetas y la configuración de entrenamiento adicional. El modelo se entrena durante 3 épocas, con un tamaño de lote de 8. Una época es un pase completo a través de todo el conjunto de datos de entrenamiento, y un tamaño de lote de 8 significa que los pesos del modelo se actualizan después de ver 8 muestras.
Finalmente, el script utiliza el modelo entrenado para hacer predicciones sobre los mismos datos de entrada. Se llama al método model.predict
con los tensores de entrada, y las predicciones resultantes se imprimen en la consola. Estas predicciones serían una medida de la confianza del modelo de que las oraciones de entrada tienen un sentimiento positivo.
1.3.3 Redes Generativas Antagónicas (GANs)
Las Redes Generativas Antagónicas (GANs) son una clase de algoritmos de inteligencia artificial utilizados en el aprendizaje automático no supervisado, implementados por un sistema de dos redes neuronales que compiten entre sí en un marco de juego de suma cero.
Las GANs consisten en dos partes, un Generador y un Discriminador. El Generador, que captura la distribución de los datos, comienza generando datos sintéticos y los alimenta al Discriminador junto con datos reales. El Discriminador, que estima la probabilidad de que una instancia dada provenga de los datos reales en lugar del Generador, se entrena para distinguir entre los dos tipos de datos.
En otras palabras, el Generador intenta engañar al Discriminador produciendo datos sintéticos cada vez mejores, mientras que el Discriminador se vuelve continuamente mejor para distinguir los datos reales de los falsos. Esto crea una especie de carrera armamentista entre los dos componentes, lo que lleva a la generación de datos sintéticos muy realistas.
Las GANs han visto una amplia aplicación en áreas como la generación de imágenes, la generación de videos y la generación de voz. Sin embargo, entrenar una GAN puede ser una tarea desafiante ya que requiere equilibrar el entrenamiento de dos redes diferentes.
Las GANs han revolucionado la modelización generativa al usar un generador y un discriminador en un entorno competitivo para producir datos sintéticos realistas. Las GANs se han aplicado a una amplia gama de tareas, desde la generación de imágenes hasta la mejora de datos.
Ejemplo: Implementación Básica de una GAN
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
Dense(1, activation='sigmoid')
])
return model
# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')
# Training the GAN
import numpy as np
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5 # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 128
epochs = 10000
for epoch in range(epochs):
# Train discriminator
idx = np.random.randint(0, x_train.shape[0], batch_size)
real_images = x_train[idx]
noise = np.random.normal(0, 1, (batch_size, 100))
fake_images = generator.predict(noise)
d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
# Train generator
noise = np.random.normal(0, 1, (batch_size, 100))
g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))
# Print progress
if epoch % 1000 == 0:
print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")
En el contexto de este código de ejemplo, el generador y el discriminador se construyen y compilan por separado. El generador utiliza una capa densa para mapear desde un espacio de ruido de 100 dimensiones a un espacio de 28281 dimensiones. El generador utiliza una función de activación LeakyReLU para la primera capa. La segunda capa es una capa densa con una función de activación tanh, seguida de una capa de remodelación para formar la imagen de salida.
El discriminador, por otro lado, es un clasificador que distingue entre imágenes reales y falsas (generadas). El modelo del discriminador toma como entrada una imagen de tamaño 28281, la aplana, la pasa a través de una capa densa con una función de activación LeakyReLU y finalmente a través de una capa densa con una función de activación sigmoide. El modelo del discriminador se compila luego con el optimizador adam y la pérdida de entropía cruzada binaria, ya que se trata de un problema de clasificación binaria.
El entrenamiento del GAN implica alternar entre entrenar el discriminador y el generador. Para entrenar el discriminador, se utilizan tanto imágenes reales (del conjunto de datos MNIST) como imágenes falsas (generadas por el generador). A las imágenes reales se les asigna una etiqueta de 1 y a las imágenes falsas se les asigna una etiqueta de 0. Luego, el discriminador se entrena en este conjunto de datos mixto.
Al entrenar el generador, el objetivo es engañar al discriminador. Por lo tanto, el generador intenta generar imágenes que sean clasificadas como reales (o 1) por el discriminador. El generador nunca ve realmente ninguna imagen real, solo recibe retroalimentación a través del discriminador.
El código también importa el conjunto de datos MNIST de los datasets de TensorFlow, normaliza las imágenes para que estén en el rango de [-1, 1] y las remodela para que tengan la forma (28, 28, 1).
El proceso de entrenamiento se repite durante un número determinado de épocas (iteraciones sobre todo el conjunto de datos), y en cada época se entrena primero el discriminador y luego el generador. La pérdida del discriminador (una medida de qué tan bien puede distinguir las imágenes reales de las falsas) y la pérdida del generador (una medida de qué tan bien puede engañar al discriminador) se imprimen después de cada época. De esta manera, se puede monitorear el proceso de entrenamiento.
Esta implementación básica de GAN sirve como un buen punto de partida para entender y experimentar con este tipo de redes. Sin embargo, en la práctica, las GANs pueden ser difíciles de entrenar y pueden requerir una selección cuidadosa de la arquitectura y los hiperparámetros.
1.3.4 Aprendizaje por Refuerzo
El aprendizaje por refuerzo (RL) ha visto avances significativos, particularmente con el desarrollo de redes de Q profundo (DQN) y métodos de gradiente de políticas. RL se ha aplicado con éxito en juegos, control robótico y conducción autónoma.
El aprendizaje por refuerzo es un tipo de aprendizaje automático en el que un agente aprende a tomar decisiones realizando acciones en un entorno para alcanzar un objetivo. El agente aprende de las consecuencias de sus acciones, en lugar de ser enseñado explícitamente, recibiendo recompensas o penalizaciones por sus acciones.
El agente aprende a alcanzar un objetivo en un entorno incierto y potencialmente complejo. En el aprendizaje por refuerzo, una inteligencia artificial se enfrenta a una situación similar a un juego. La computadora emplea prueba y error para encontrar una solución al problema. Para que la máquina haga lo que el programador quiere, la inteligencia artificial recibe recompensas o penalizaciones por las acciones que realiza. Su objetivo es maximizar la recompensa total.
A pesar de que el diseñador establece la política de recompensas, es decir, las reglas del juego, no le da al modelo pistas o sugerencias sobre cómo resolver el juego. Depende del modelo averiguar cómo realizar la tarea para maximizar la recompensa, comenzando con pruebas completamente aleatorias y terminando con tácticas sofisticadas y habilidades sobrehumanas. Al aprovechar el poder de la búsqueda y muchas pruebas, el aprendizaje por refuerzo es actualmente la forma más efectiva de estimular la creatividad de la máquina. A diferencia de los seres humanos, la inteligencia artificial puede acumular experiencia a partir de miles de juegos paralelos si un algoritmo de aprendizaje por refuerzo se ejecuta en una infraestructura informática suficientemente poderosa.
El aprendizaje por refuerzo se ha utilizado para enseñar a las máquinas a jugar juegos como Go y Ajedrez contra campeones mundiales, para simular la marcha bípeda, la conducción autónoma y otras tareas complejas que anteriormente se pensaba que solo los humanos podían lograr.
El futuro del aprendizaje por refuerzo es prometedor, ya que abre una vía para desarrollar máquinas que pueden aprender y adaptarse a escenarios complejos por sí solas. Sin embargo, al igual que cualquier otra tecnología de IA, también debe usarse de manera responsable, considerando todas sus implicaciones sociales y éticas.
Ejemplo: Q-Learning para Grid World
import numpy as np
# Environment setup
grid_size = 4
rewards = np.zeros((grid_size, grid_size))
rewards[3, 3] = 1 # Goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action selection
def choose_action(state):
if np
.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (3, 3):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
Este código de ejemplo implementa una forma básica de Q-Learning, un algoritmo de aprendizaje por refuerzo sin modelo, en un entorno simple de mundo cuadrícula.
La primera parte del código configura el entorno. Se define una cuadrícula de un tamaño determinado, con cada celda de la cuadrícula inicializada con una recompensa de cero. Sin embargo, al estado objetivo, ubicado en la celda de la cuadrícula (3,3), se le asigna una recompensa de uno. Este es el objetivo que el agente de aprendizaje debe esforzarse por alcanzar.
A continuación, se definen varios parámetros cruciales para el algoritmo de Q-Learning. El factor de descuento gamma
se establece en 0.9, lo que determina la importancia de las recompensas futuras. Un gamma
de 0 hace que el agente sea "miópico" (solo considerando recompensas actuales), mientras que un gamma
cercano a 1 hace que busque una recompensa alta a largo plazo. La tasa de aprendizaje alpha
se establece en 0.1, lo que determina en qué medida la información recién adquirida reemplaza la información antigua. La tasa de exploración epsilon
se establece en 0.1, lo que determina la frecuencia con la que el agente elige una acción aleatoria en lugar de la acción que cree que tiene el mejor efecto a largo plazo.
Luego se inicializa una tabla Q con ceros, que servirá como una tabla de búsqueda donde el agente puede encontrar la mejor acción a tomar mientras está en un determinado estado.
La función choose_action
es una implementación de la política epsilon-greedy. En este caso, la mayoría de las veces, el agente elegirá la acción que tiene la máxima recompensa futura esperada, que es la parte de explotación. Pero, un porcentaje epsilon del tiempo, el agente elegirá una acción aleatoria, que es la parte de exploración.
La parte principal del código es un bucle que simula 1000 episodios de interacción del agente con el entorno. Durante cada episodio, el agente comienza desde el estado inicial (0,0) y continúa eligiendo acciones y transitando al siguiente estado hasta llegar al estado objetivo (3,3). Para cada acción tomada, el valor Q de la acción para el estado actual se actualiza utilizando el algoritmo de Q-Learning, que actualiza el valor Q basado en la tasa de aprendizaje, la recompensa recibida y el valor Q máximo para el nuevo estado. Este proceso lleva de manera incremental a valores de acción cada vez mejores.
Al final del proceso de aprendizaje, el código imprime la tabla Q aprendida. Esta tabla indicará al agente el retorno esperado para cada acción en cada estado, guiando efectivamente al agente al objetivo de la manera más eficiente en cuanto a recompensas.
Este simple ejemplo de Q-Learning sirve como una base para entender la mecánica fundamental de este poderoso algoritmo de aprendizaje por refuerzo. Con entornos más complejos y mejoras en el algoritmo, Q-Learning puede resolver tareas mucho más complejas.
1.3.5 Aprendizaje Auto-supervisado
El aprendizaje auto-supervisado aprovecha los datos no etiquetados generando etiquetas sustitutas a partir de los propios datos. Este enfoque ha demostrado ser eficaz en tareas como el aprendizaje de representaciones y el preentrenamiento de modelos para tareas posteriores.
En el aprendizaje auto-supervisado, el sistema aprende a predecir algunas partes de los datos a partir de otras partes. Esto se hace creando una tarea "sustituta" para aprender a partir de una gran cantidad de datos no etiquetados, lo cual puede ser muy útil cuando los datos etiquetados son escasos o costosos de obtener. Las representaciones aprendidas suelen ser útiles para tareas posteriores, y el modelo puede ajustarse con un conjunto de datos etiquetado más pequeño para una tarea específica.
Por ejemplo, una tarea de aprendizaje auto-supervisado para imágenes podría ser predecir el color de una imagen en escala de grises. En este caso, el modelo aprendería características útiles sobre la estructura y el contenido de las imágenes sin necesidad de etiquetas proporcionadas por humanos.
El aprendizaje auto-supervisado ha mostrado gran promesa en una variedad de aplicaciones. Se ha utilizado con éxito para preentrenar modelos para tareas de procesamiento del lenguaje natural, donde primero se entrena un modelo para predecir la siguiente palabra en una oración, y luego se ajusta para una tarea específica como el análisis de sentimientos o la respuesta a preguntas. También ha mostrado promesa en la visión por computadora, donde los modelos preentrenados en una tarea auto-supervisada pueden ajustarse para tareas como la detección de objetos o la segmentación de imágenes.
Un ejemplo específico de aprendizaje auto-supervisado es un método llamado SimCLR (Aprendizaje Contrastivo Simple de Representaciones Visuales). En SimCLR, se entrena un modelo para reconocer si dos versiones aumentadas de una imagen son iguales o diferentes. El modelo aprende a extraer características que son consistentes en diferentes aumentos de la misma imagen, lo cual resulta ser una habilidad muy útil para muchas tareas de visión por computadora.
Ejemplo: Aprendizaje Contrastivo con SimCLR
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam
# Sample contrastive learning model (SimCLR)
def build_simclr_model(input_shape):
base_model = tf.keras.applications.ResNet50(include_top=False, input_shape=input_shape, pooling='avg')
base_model.trainable = True
model = Sequential([
base_model,
Flatten(),
Dense(128, activation='relu'),
Dense(128) # Projection head
])
return model
# Contrastive loss function
def contrastive_loss(y_true, y_pred):
temperature = 0.1
y_true = tf.cast(y_true, tf.int32)
y_pred = tf.math.l2_normalize(y_pred, axis=1)
logits = tf.matmul(y_pred, y_pred, transpose_b=True) / temperature
labels = tf.one_hot(y_true, depth=y_pred.shape[0])
return SparseCategoricalCrossentropy(from_logits=True)(labels, logits)
# Compile and train the model
input_shape = (224, 224, 3)
model = build_simclr_model(input_shape)
model.compile(optimizer=Adam(learning_rate=0.001), loss=contrastive_loss)
# Assuming 'x_train' and 'y_train' are the training data and labels (augmentations)
model.fit(x_train, y_train, epochs=10, batch_size=32, verbose=1)
El script comienza importando los módulos necesarios. Utiliza la biblioteca TensorFlow, una poderosa biblioteca de software de código abierto para el aprendizaje automático, y Keras, una API de redes neuronales de alto nivel que también es parte de TensorFlow.
La función build_simclr_model
se define para construir el modelo. La base del modelo es un modelo ResNet50 preentrenado, un modelo de aprendizaje profundo popular con 50 capas, ya entrenado en un gran conjunto de datos. include_top=False
significa que las capas de salida completamente conectadas del modelo utilizadas para la clasificación no están incluidas, y pooling='avg'
indica que se aplica un agrupamiento promedio global a la salida del último bloque de convolución de ResNet50, reduciendo la dimensionalidad de la salida. Luego se usa la API Sequential
para apilar capas sobre el modelo base. Se agrega una capa Flatten
para transformar el formato de las imágenes de una matriz bidimensional (de 28x28 píxeles) a una matriz unidimensional (de 28 * 28 = 784 píxeles). Se agregan dos capas Dense
, la primera con activación ReLU y la segunda sin activación, que sirve como la cabeza de proyección del modelo.
Después de la construcción del modelo, se define la función de pérdida contrastiva como contrastive_loss
. El aprendizaje contrastivo es un tipo de método de aprendizaje auto-supervisado que entrena modelos para aprender características similares a partir de datos similares. Esta función primero normaliza el vector de predicción, luego calcula el producto punto entre los vectores de predicción divididos por un parámetro de temperatura para crear los logits. Luego, crea etiquetas one-hot a partir de las etiquetas verdaderas y calcula la pérdida de entropía cruzada categórica escasa entre estas etiquetas y los logits.
El script luego compila y entrena el modelo SimCLR utilizando el optimizador Adam y la función de pérdida contrastiva. El optimizador Adam es una extensión del descenso de gradiente estocástico, un algoritmo popular para entrenar una amplia gama de modelos en el aprendizaje automático. La tasa de aprendizaje se establece en 0.001.
Luego, el modelo se ajusta a los datos de entrenamiento 'x_train' y 'y_train' durante 10 épocas con un tamaño de lote de 32. 'x_train' y 'y_train' son marcadores de posición en este contexto y serían reemplazados por los datos de entrenamiento reales y las etiquetas durante el entrenamiento en el mundo real. Una época es una medida del número de veces que se utilizan todos los vectores de entrenamiento una vez para actualizar los pesos en el proceso de entrenamiento.
1.3 Avances Recientes en el Aprendizaje Profundo
En los últimos años, el aprendizaje profundo ha realizado avances significativos, ampliando los límites de lo que la inteligencia artificial puede lograr. Estos avances están impulsados por una combinación de algoritmos mejorados, hardware más potente y la disponibilidad de grandes conjuntos de datos.
En esta sección, exploraremos algunos de los desarrollos más impactantes recientes en el aprendizaje profundo, incluidos los avances en arquitecturas de modelos, técnicas de entrenamiento y aplicaciones. Al comprender estas innovaciones de vanguardia, estarás mejor preparado para aprovechar las últimas tecnologías en tus proyectos.
1.3.1 Redes Transformer y Mecanismos de Atención
A lo largo de los años, el aprendizaje profundo ha visto numerosos avances, pero uno de los avances más significativos ha sido el desarrollo de las redes transformer. Estas redes innovadoras dependen en gran medida de lo que se llaman mecanismos de atención.
El concepto de redes transformer ha revolucionado completamente el campo del procesamiento del lenguaje natural (NLP). Anteriormente, los modelos procesaban secuencias de datos de manera secuencial. Sin embargo, con el advenimiento de las redes transformer, los modelos ahora son capaces de procesar secuencias completas de datos simultáneamente. Este cambio significativo en la arquitectura ha llevado a un procesamiento más eficiente y a mejores resultados.
Esta arquitectura revolucionaria ha allanado el camino para la creación de modelos altamente efectivos que han tenido un impacto profundo en el campo. Algunos de los modelos más notables incluyen BERT, GPT-3 y GPT-4. Cada uno de estos modelos ha realizado contribuciones sustanciales al campo, mejorando nuestra capacidad para entender e interpretar el lenguaje natural.
Ejemplo: Arquitectura Transformer
El modelo transformer consta de un codificador y un decodificador, ambos compuestos por múltiples capas de autoatención y redes neuronales de avance directo. El mecanismo de autoatención permite que el modelo pondere la importancia de diferentes partes de la secuencia de entrada, lo que le permite capturar dependencias de largo alcance.
import tensorflow as tf
from tensorflow.keras.layers import Dense, LayerNormalization, Dropout
from tensorflow.keras.models import Model
class MultiHeadAttention(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.num_heads = num_heads
self.depth = d_model // num_heads
self.wq = Dense(d_model)
self.wk = Dense(d_model)
self.wv = Dense(d_model)
self.dense = Dense(d_model)
def split_heads(self, x, batch_size):
x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
return tf.transpose(x, perm=[0, 2, 1, 3])
def call(self, v, k, q, mask):
batch_size = tf.shape(q)[0]
q = self.wq(q)
k = self.wk(k)
v = self.wv(v)
q = self.split_heads(q, batch_size)
k = self.split_heads(k, batch_size)
v = self.split_heads(v, batch_size)
scaled_attention, _ = scaled_dot_product_attention(q, k, v, mask)
scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])
concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model))
output = self.dense(concat_attention)
return output
def scaled_dot_product_attention(q, k, v, mask):
matmul_qk = tf.matmul(q, k, transpose_b=True)
dk = tf.cast(tf.shape(k)[-1], tf.float32)
scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
if mask is not None:
scaled_attention_logits += (mask * -1e9)
attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)
output = tf.matmul(attention_weights, v)
return output, attention_weights
# Sample transformer encoder layer
class EncoderLayer(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads, dff, rate=0.1):
super(EncoderLayer, self).__init__()
self.mha = MultiHeadAttention(d_model, num_heads)
self.ffn = tf.keras.Sequential([
Dense(dff, activation='relu'),
Dense(d_model)
])
self.layernorm1 = LayerNormalization(epsilon=1e-6)
self.layernorm2 = LayerNormalization(epsilon=1e-6)
self.dropout1 = Dropout(rate)
self.dropout2 = Dropout(rate)
def call(self, x, training, mask):
attn_output = self.mha(x, x, x, mask)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(x + attn_output)
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
out2 = self.layernorm2(out1 + ffn_output)
return out2
Este ejemplo demuestra la implementación de un modelo Transformer utilizando la biblioteca TensorFlow, específicamente la API de Keras. El modelo Transformer es un tipo de modelo de aprendizaje profundo que ha tenido particular éxito en el manejo de tareas de secuencia a secuencia, como la traducción de idiomas o el resumen de textos.
En primer lugar, se declara la clase MultiHeadAttention
. Esta clase representa el mecanismo de autoatención multi-cabeza en el modelo Transformer. Permite que el modelo se enfoque en diferentes posiciones de la secuencia de entrada al generar una secuencia de salida, haciendo posible capturar varios aspectos de la información de entrada.
La clase toma dos parámetros: d_model
, que es la dimensionalidad de la entrada, y num_heads
, que es el número de cabezas de atención. Dentro de la clase, se declaran varias capas densas para las transformaciones lineales de las consultas (queries), claves (keys) y valores (values). El método split_heads
remodela las consultas, claves y valores en múltiples cabezas, y el método call
aplica el mecanismo de atención en las consultas, claves y valores, y devuelve la salida.
A continuación, se define la función scaled_dot_product_attention
. Esta función calcula los pesos de atención y la salida para el mecanismo de atención. Calcula el producto punto de la consulta y la clave, lo escala por la raíz cuadrada de la profundidad (la última dimensión de la clave), aplica una máscara si se proporciona, y luego aplica una función softmax para obtener los pesos de atención. Estos pesos se utilizan luego para obtener una suma ponderada de los valores, que forma la salida del mecanismo de atención.
Finalmente, se define la clase EncoderLayer
. Esta clase representa una sola capa del codificador del Transformer. Cada capa del codificador consiste en un mecanismo de autoatención multi-cabeza y una red neuronal de avance punto a punto. El método call
aplica la autoatención en la entrada, seguido de dropout, conexión residual y normalización de capa. Luego, aplica la red de avance en la salida, seguida nuevamente de dropout, conexión residual y normalización de capa.
Cabe señalar que las capas densas se utilizan para transformar las entradas para el mecanismo de atención y dentro de la red de avance. Dropout se utiliza para prevenir el sobreajuste y LayerNormalization se utiliza para normalizar las salidas de cada subcapa. Todo el mecanismo de atención está encapsulado en la clase MultiHeadAttention
para su reutilización.
Este código sirve como base para construir modelos Transformer más complejos. Por ejemplo, se podrían apilar múltiples instancias de EncoderLayer
para formar la parte completa del codificador del Transformer, y se podrían definir capas similares para la parte del decodificador. Además, se podrían añadir componentes adicionales como codificación posicional y una capa de softmax de salida para completar el modelo.
1.3.2 Aprendizaje por Transferencia
El Aprendizaje por Transferencia es un método de aprendizaje automático donde un modelo desarrollado para una tarea se reutiliza como punto de partida para un modelo en una segunda tarea. Es un enfoque popular en el aprendizaje profundo donde los modelos preentrenados se utilizan como punto de partida en tareas de visión por computadora y procesamiento del lenguaje natural.
En otras palabras, el aprendizaje por transferencia es un método donde el conocimiento de un modelo adquirido de una tarea previa se aplica a un nuevo problema similar. Este enfoque es particularmente efectivo en el aprendizaje profundo debido a los vastos recursos computacionales y de tiempo necesarios para desarrollar modelos de redes neuronales en estos problemas y por los grandes saltos en habilidad que proporcionan en problemas relacionados.
Este enfoque se usa ampliamente en varias aplicaciones como el Procesamiento del Lenguaje Natural (NLP), la Visión por Computadora, e incluso en el campo de la música y el arte donde se están utilizando modelos generativos para crear nuevas obras de arte y componer música.
El Aprendizaje por Transferencia, por lo tanto, es una técnica poderosa que ayuda a mejorar el rendimiento de los modelos en tareas con datos limitados al aprovechar el conocimiento adquirido de tareas relacionadas con datos abundantes. Es uno de los avances significativos en el campo del Aprendizaje Profundo.
El aprendizaje por transferencia se ha convertido en una técnica poderosa en el aprendizaje profundo, permitiendo que los modelos entrenados en grandes conjuntos de datos se ajusten para tareas específicas con conjuntos de datos más pequeños. Este enfoque reduce significativamente los recursos computacionales y el tiempo requerido para el entrenamiento.
Ejemplo: Ajuste Fino de BERT para Clasificación de Texto
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had."]
labels = [1, 0] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Este ejemplo utiliza la biblioteca Transformers de Hugging Face. Este script demuestra cómo ajustar finamente un modelo BERT (Bidirectional Encoder Representations from Transformers) preentrenado para una tarea de clasificación binaria de texto. Desglosamos lo que hace el script.
El script comienza importando los módulos y clases necesarios. Trae BertTokenizer
y TFBertForSequenceClassification
de la biblioteca Transformers, que están específicamente diseñados para tareas que involucran modelos BERT. El BertTokenizer
se usa para convertir el texto de entrada en un formato que el modelo BERT puede entender, mientras que TFBertForSequenceClassification
es un modelo BERT con una capa de clasificación en la parte superior. El script también importa Adam
de la API de Keras de TensorFlow, que es el optimizador que se utilizará para entrenar el modelo.
A continuación, el script carga el modelo BERT preentrenado y su tokenizador asociado utilizando el método from_pretrained
. El argumento 'bert-base-uncased'
especifica que el script debe usar la versión "uncased" del modelo BERT base, lo que significa que el modelo no distingue entre letras mayúsculas y minúsculas. Este modelo ha sido entrenado en un gran corpus de texto en inglés y puede generar representaciones significativas para oraciones en inglés.
El script luego define algunos datos de muestra con fines de demostración. La variable texts
es una lista de dos oraciones en inglés, mientras que la variable labels
es una lista de dos enteros que representan el sentimiento de la oración correspondiente en la variable texts
(1 para sentimiento positivo, 0 para sentimiento negativo).
Después de definir los datos, el script tokeniza los textos de entrada utilizando el tokenizador cargado. La llamada al tokenizer
convierte las oraciones en la variable texts
en un formato que el modelo BERT puede entender. El método devuelve un diccionario que incluye varios objetos tipo tensor que el modelo necesita como entrada. El argumento return_tensors='tf'
especifica que estos objetos deben ser tensores de TensorFlow. El argumento padding=True
asegura que todas las oraciones se rellenen a la misma longitud, mientras que truncation=True
asegura que las oraciones más largas que la longitud máxima de entrada del modelo se recorten. El argumento max_length=128
especifica esta longitud máxima.
A continuación, el script compila el modelo especificando el optimizador, la función de pérdida y las métricas a rastrear durante el entrenamiento. El optimizador se establece en Adam con una tasa de aprendizaje de 2e-5. La función de pérdida se establece en el método compute_loss
integrado del modelo, que calcula la pérdida de clasificación. El script también especifica que se debe rastrear la precisión durante el entrenamiento.
Con el modelo ahora compilado, el script entrena el modelo con los datos de entrada. Se llama al método model.fit
con los tensores de entrada, las etiquetas y la configuración de entrenamiento adicional. El modelo se entrena durante 3 épocas, con un tamaño de lote de 8. Una época es un pase completo a través de todo el conjunto de datos de entrenamiento, y un tamaño de lote de 8 significa que los pesos del modelo se actualizan después de ver 8 muestras.
Finalmente, el script utiliza el modelo entrenado para hacer predicciones sobre los mismos datos de entrada. Se llama al método model.predict
con los tensores de entrada, y las predicciones resultantes se imprimen en la consola. Estas predicciones serían una medida de la confianza del modelo de que las oraciones de entrada tienen un sentimiento positivo.
1.3.3 Redes Generativas Antagónicas (GANs)
Las Redes Generativas Antagónicas (GANs) son una clase de algoritmos de inteligencia artificial utilizados en el aprendizaje automático no supervisado, implementados por un sistema de dos redes neuronales que compiten entre sí en un marco de juego de suma cero.
Las GANs consisten en dos partes, un Generador y un Discriminador. El Generador, que captura la distribución de los datos, comienza generando datos sintéticos y los alimenta al Discriminador junto con datos reales. El Discriminador, que estima la probabilidad de que una instancia dada provenga de los datos reales en lugar del Generador, se entrena para distinguir entre los dos tipos de datos.
En otras palabras, el Generador intenta engañar al Discriminador produciendo datos sintéticos cada vez mejores, mientras que el Discriminador se vuelve continuamente mejor para distinguir los datos reales de los falsos. Esto crea una especie de carrera armamentista entre los dos componentes, lo que lleva a la generación de datos sintéticos muy realistas.
Las GANs han visto una amplia aplicación en áreas como la generación de imágenes, la generación de videos y la generación de voz. Sin embargo, entrenar una GAN puede ser una tarea desafiante ya que requiere equilibrar el entrenamiento de dos redes diferentes.
Las GANs han revolucionado la modelización generativa al usar un generador y un discriminador en un entorno competitivo para producir datos sintéticos realistas. Las GANs se han aplicado a una amplia gama de tareas, desde la generación de imágenes hasta la mejora de datos.
Ejemplo: Implementación Básica de una GAN
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
Dense(1, activation='sigmoid')
])
return model
# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')
# Training the GAN
import numpy as np
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5 # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 128
epochs = 10000
for epoch in range(epochs):
# Train discriminator
idx = np.random.randint(0, x_train.shape[0], batch_size)
real_images = x_train[idx]
noise = np.random.normal(0, 1, (batch_size, 100))
fake_images = generator.predict(noise)
d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
# Train generator
noise = np.random.normal(0, 1, (batch_size, 100))
g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))
# Print progress
if epoch % 1000 == 0:
print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")
En el contexto de este código de ejemplo, el generador y el discriminador se construyen y compilan por separado. El generador utiliza una capa densa para mapear desde un espacio de ruido de 100 dimensiones a un espacio de 28281 dimensiones. El generador utiliza una función de activación LeakyReLU para la primera capa. La segunda capa es una capa densa con una función de activación tanh, seguida de una capa de remodelación para formar la imagen de salida.
El discriminador, por otro lado, es un clasificador que distingue entre imágenes reales y falsas (generadas). El modelo del discriminador toma como entrada una imagen de tamaño 28281, la aplana, la pasa a través de una capa densa con una función de activación LeakyReLU y finalmente a través de una capa densa con una función de activación sigmoide. El modelo del discriminador se compila luego con el optimizador adam y la pérdida de entropía cruzada binaria, ya que se trata de un problema de clasificación binaria.
El entrenamiento del GAN implica alternar entre entrenar el discriminador y el generador. Para entrenar el discriminador, se utilizan tanto imágenes reales (del conjunto de datos MNIST) como imágenes falsas (generadas por el generador). A las imágenes reales se les asigna una etiqueta de 1 y a las imágenes falsas se les asigna una etiqueta de 0. Luego, el discriminador se entrena en este conjunto de datos mixto.
Al entrenar el generador, el objetivo es engañar al discriminador. Por lo tanto, el generador intenta generar imágenes que sean clasificadas como reales (o 1) por el discriminador. El generador nunca ve realmente ninguna imagen real, solo recibe retroalimentación a través del discriminador.
El código también importa el conjunto de datos MNIST de los datasets de TensorFlow, normaliza las imágenes para que estén en el rango de [-1, 1] y las remodela para que tengan la forma (28, 28, 1).
El proceso de entrenamiento se repite durante un número determinado de épocas (iteraciones sobre todo el conjunto de datos), y en cada época se entrena primero el discriminador y luego el generador. La pérdida del discriminador (una medida de qué tan bien puede distinguir las imágenes reales de las falsas) y la pérdida del generador (una medida de qué tan bien puede engañar al discriminador) se imprimen después de cada época. De esta manera, se puede monitorear el proceso de entrenamiento.
Esta implementación básica de GAN sirve como un buen punto de partida para entender y experimentar con este tipo de redes. Sin embargo, en la práctica, las GANs pueden ser difíciles de entrenar y pueden requerir una selección cuidadosa de la arquitectura y los hiperparámetros.
1.3.4 Aprendizaje por Refuerzo
El aprendizaje por refuerzo (RL) ha visto avances significativos, particularmente con el desarrollo de redes de Q profundo (DQN) y métodos de gradiente de políticas. RL se ha aplicado con éxito en juegos, control robótico y conducción autónoma.
El aprendizaje por refuerzo es un tipo de aprendizaje automático en el que un agente aprende a tomar decisiones realizando acciones en un entorno para alcanzar un objetivo. El agente aprende de las consecuencias de sus acciones, en lugar de ser enseñado explícitamente, recibiendo recompensas o penalizaciones por sus acciones.
El agente aprende a alcanzar un objetivo en un entorno incierto y potencialmente complejo. En el aprendizaje por refuerzo, una inteligencia artificial se enfrenta a una situación similar a un juego. La computadora emplea prueba y error para encontrar una solución al problema. Para que la máquina haga lo que el programador quiere, la inteligencia artificial recibe recompensas o penalizaciones por las acciones que realiza. Su objetivo es maximizar la recompensa total.
A pesar de que el diseñador establece la política de recompensas, es decir, las reglas del juego, no le da al modelo pistas o sugerencias sobre cómo resolver el juego. Depende del modelo averiguar cómo realizar la tarea para maximizar la recompensa, comenzando con pruebas completamente aleatorias y terminando con tácticas sofisticadas y habilidades sobrehumanas. Al aprovechar el poder de la búsqueda y muchas pruebas, el aprendizaje por refuerzo es actualmente la forma más efectiva de estimular la creatividad de la máquina. A diferencia de los seres humanos, la inteligencia artificial puede acumular experiencia a partir de miles de juegos paralelos si un algoritmo de aprendizaje por refuerzo se ejecuta en una infraestructura informática suficientemente poderosa.
El aprendizaje por refuerzo se ha utilizado para enseñar a las máquinas a jugar juegos como Go y Ajedrez contra campeones mundiales, para simular la marcha bípeda, la conducción autónoma y otras tareas complejas que anteriormente se pensaba que solo los humanos podían lograr.
El futuro del aprendizaje por refuerzo es prometedor, ya que abre una vía para desarrollar máquinas que pueden aprender y adaptarse a escenarios complejos por sí solas. Sin embargo, al igual que cualquier otra tecnología de IA, también debe usarse de manera responsable, considerando todas sus implicaciones sociales y éticas.
Ejemplo: Q-Learning para Grid World
import numpy as np
# Environment setup
grid_size = 4
rewards = np.zeros((grid_size, grid_size))
rewards[3, 3] = 1 # Goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action selection
def choose_action(state):
if np
.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (3, 3):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
Este código de ejemplo implementa una forma básica de Q-Learning, un algoritmo de aprendizaje por refuerzo sin modelo, en un entorno simple de mundo cuadrícula.
La primera parte del código configura el entorno. Se define una cuadrícula de un tamaño determinado, con cada celda de la cuadrícula inicializada con una recompensa de cero. Sin embargo, al estado objetivo, ubicado en la celda de la cuadrícula (3,3), se le asigna una recompensa de uno. Este es el objetivo que el agente de aprendizaje debe esforzarse por alcanzar.
A continuación, se definen varios parámetros cruciales para el algoritmo de Q-Learning. El factor de descuento gamma
se establece en 0.9, lo que determina la importancia de las recompensas futuras. Un gamma
de 0 hace que el agente sea "miópico" (solo considerando recompensas actuales), mientras que un gamma
cercano a 1 hace que busque una recompensa alta a largo plazo. La tasa de aprendizaje alpha
se establece en 0.1, lo que determina en qué medida la información recién adquirida reemplaza la información antigua. La tasa de exploración epsilon
se establece en 0.1, lo que determina la frecuencia con la que el agente elige una acción aleatoria en lugar de la acción que cree que tiene el mejor efecto a largo plazo.
Luego se inicializa una tabla Q con ceros, que servirá como una tabla de búsqueda donde el agente puede encontrar la mejor acción a tomar mientras está en un determinado estado.
La función choose_action
es una implementación de la política epsilon-greedy. En este caso, la mayoría de las veces, el agente elegirá la acción que tiene la máxima recompensa futura esperada, que es la parte de explotación. Pero, un porcentaje epsilon del tiempo, el agente elegirá una acción aleatoria, que es la parte de exploración.
La parte principal del código es un bucle que simula 1000 episodios de interacción del agente con el entorno. Durante cada episodio, el agente comienza desde el estado inicial (0,0) y continúa eligiendo acciones y transitando al siguiente estado hasta llegar al estado objetivo (3,3). Para cada acción tomada, el valor Q de la acción para el estado actual se actualiza utilizando el algoritmo de Q-Learning, que actualiza el valor Q basado en la tasa de aprendizaje, la recompensa recibida y el valor Q máximo para el nuevo estado. Este proceso lleva de manera incremental a valores de acción cada vez mejores.
Al final del proceso de aprendizaje, el código imprime la tabla Q aprendida. Esta tabla indicará al agente el retorno esperado para cada acción en cada estado, guiando efectivamente al agente al objetivo de la manera más eficiente en cuanto a recompensas.
Este simple ejemplo de Q-Learning sirve como una base para entender la mecánica fundamental de este poderoso algoritmo de aprendizaje por refuerzo. Con entornos más complejos y mejoras en el algoritmo, Q-Learning puede resolver tareas mucho más complejas.
1.3.5 Aprendizaje Auto-supervisado
El aprendizaje auto-supervisado aprovecha los datos no etiquetados generando etiquetas sustitutas a partir de los propios datos. Este enfoque ha demostrado ser eficaz en tareas como el aprendizaje de representaciones y el preentrenamiento de modelos para tareas posteriores.
En el aprendizaje auto-supervisado, el sistema aprende a predecir algunas partes de los datos a partir de otras partes. Esto se hace creando una tarea "sustituta" para aprender a partir de una gran cantidad de datos no etiquetados, lo cual puede ser muy útil cuando los datos etiquetados son escasos o costosos de obtener. Las representaciones aprendidas suelen ser útiles para tareas posteriores, y el modelo puede ajustarse con un conjunto de datos etiquetado más pequeño para una tarea específica.
Por ejemplo, una tarea de aprendizaje auto-supervisado para imágenes podría ser predecir el color de una imagen en escala de grises. En este caso, el modelo aprendería características útiles sobre la estructura y el contenido de las imágenes sin necesidad de etiquetas proporcionadas por humanos.
El aprendizaje auto-supervisado ha mostrado gran promesa en una variedad de aplicaciones. Se ha utilizado con éxito para preentrenar modelos para tareas de procesamiento del lenguaje natural, donde primero se entrena un modelo para predecir la siguiente palabra en una oración, y luego se ajusta para una tarea específica como el análisis de sentimientos o la respuesta a preguntas. También ha mostrado promesa en la visión por computadora, donde los modelos preentrenados en una tarea auto-supervisada pueden ajustarse para tareas como la detección de objetos o la segmentación de imágenes.
Un ejemplo específico de aprendizaje auto-supervisado es un método llamado SimCLR (Aprendizaje Contrastivo Simple de Representaciones Visuales). En SimCLR, se entrena un modelo para reconocer si dos versiones aumentadas de una imagen son iguales o diferentes. El modelo aprende a extraer características que son consistentes en diferentes aumentos de la misma imagen, lo cual resulta ser una habilidad muy útil para muchas tareas de visión por computadora.
Ejemplo: Aprendizaje Contrastivo con SimCLR
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam
# Sample contrastive learning model (SimCLR)
def build_simclr_model(input_shape):
base_model = tf.keras.applications.ResNet50(include_top=False, input_shape=input_shape, pooling='avg')
base_model.trainable = True
model = Sequential([
base_model,
Flatten(),
Dense(128, activation='relu'),
Dense(128) # Projection head
])
return model
# Contrastive loss function
def contrastive_loss(y_true, y_pred):
temperature = 0.1
y_true = tf.cast(y_true, tf.int32)
y_pred = tf.math.l2_normalize(y_pred, axis=1)
logits = tf.matmul(y_pred, y_pred, transpose_b=True) / temperature
labels = tf.one_hot(y_true, depth=y_pred.shape[0])
return SparseCategoricalCrossentropy(from_logits=True)(labels, logits)
# Compile and train the model
input_shape = (224, 224, 3)
model = build_simclr_model(input_shape)
model.compile(optimizer=Adam(learning_rate=0.001), loss=contrastive_loss)
# Assuming 'x_train' and 'y_train' are the training data and labels (augmentations)
model.fit(x_train, y_train, epochs=10, batch_size=32, verbose=1)
El script comienza importando los módulos necesarios. Utiliza la biblioteca TensorFlow, una poderosa biblioteca de software de código abierto para el aprendizaje automático, y Keras, una API de redes neuronales de alto nivel que también es parte de TensorFlow.
La función build_simclr_model
se define para construir el modelo. La base del modelo es un modelo ResNet50 preentrenado, un modelo de aprendizaje profundo popular con 50 capas, ya entrenado en un gran conjunto de datos. include_top=False
significa que las capas de salida completamente conectadas del modelo utilizadas para la clasificación no están incluidas, y pooling='avg'
indica que se aplica un agrupamiento promedio global a la salida del último bloque de convolución de ResNet50, reduciendo la dimensionalidad de la salida. Luego se usa la API Sequential
para apilar capas sobre el modelo base. Se agrega una capa Flatten
para transformar el formato de las imágenes de una matriz bidimensional (de 28x28 píxeles) a una matriz unidimensional (de 28 * 28 = 784 píxeles). Se agregan dos capas Dense
, la primera con activación ReLU y la segunda sin activación, que sirve como la cabeza de proyección del modelo.
Después de la construcción del modelo, se define la función de pérdida contrastiva como contrastive_loss
. El aprendizaje contrastivo es un tipo de método de aprendizaje auto-supervisado que entrena modelos para aprender características similares a partir de datos similares. Esta función primero normaliza el vector de predicción, luego calcula el producto punto entre los vectores de predicción divididos por un parámetro de temperatura para crear los logits. Luego, crea etiquetas one-hot a partir de las etiquetas verdaderas y calcula la pérdida de entropía cruzada categórica escasa entre estas etiquetas y los logits.
El script luego compila y entrena el modelo SimCLR utilizando el optimizador Adam y la función de pérdida contrastiva. El optimizador Adam es una extensión del descenso de gradiente estocástico, un algoritmo popular para entrenar una amplia gama de modelos en el aprendizaje automático. La tasa de aprendizaje se establece en 0.001.
Luego, el modelo se ajusta a los datos de entrenamiento 'x_train' y 'y_train' durante 10 épocas con un tamaño de lote de 32. 'x_train' y 'y_train' son marcadores de posición en este contexto y serían reemplazados por los datos de entrenamiento reales y las etiquetas durante el entrenamiento en el mundo real. Una época es una medida del número de veces que se utilizan todos los vectores de entrenamiento una vez para actualizar los pesos en el proceso de entrenamiento.
1.3 Avances Recientes en el Aprendizaje Profundo
En los últimos años, el aprendizaje profundo ha realizado avances significativos, ampliando los límites de lo que la inteligencia artificial puede lograr. Estos avances están impulsados por una combinación de algoritmos mejorados, hardware más potente y la disponibilidad de grandes conjuntos de datos.
En esta sección, exploraremos algunos de los desarrollos más impactantes recientes en el aprendizaje profundo, incluidos los avances en arquitecturas de modelos, técnicas de entrenamiento y aplicaciones. Al comprender estas innovaciones de vanguardia, estarás mejor preparado para aprovechar las últimas tecnologías en tus proyectos.
1.3.1 Redes Transformer y Mecanismos de Atención
A lo largo de los años, el aprendizaje profundo ha visto numerosos avances, pero uno de los avances más significativos ha sido el desarrollo de las redes transformer. Estas redes innovadoras dependen en gran medida de lo que se llaman mecanismos de atención.
El concepto de redes transformer ha revolucionado completamente el campo del procesamiento del lenguaje natural (NLP). Anteriormente, los modelos procesaban secuencias de datos de manera secuencial. Sin embargo, con el advenimiento de las redes transformer, los modelos ahora son capaces de procesar secuencias completas de datos simultáneamente. Este cambio significativo en la arquitectura ha llevado a un procesamiento más eficiente y a mejores resultados.
Esta arquitectura revolucionaria ha allanado el camino para la creación de modelos altamente efectivos que han tenido un impacto profundo en el campo. Algunos de los modelos más notables incluyen BERT, GPT-3 y GPT-4. Cada uno de estos modelos ha realizado contribuciones sustanciales al campo, mejorando nuestra capacidad para entender e interpretar el lenguaje natural.
Ejemplo: Arquitectura Transformer
El modelo transformer consta de un codificador y un decodificador, ambos compuestos por múltiples capas de autoatención y redes neuronales de avance directo. El mecanismo de autoatención permite que el modelo pondere la importancia de diferentes partes de la secuencia de entrada, lo que le permite capturar dependencias de largo alcance.
import tensorflow as tf
from tensorflow.keras.layers import Dense, LayerNormalization, Dropout
from tensorflow.keras.models import Model
class MultiHeadAttention(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads):
super(MultiHeadAttention, self).__init__()
assert d_model % num_heads == 0
self.d_model = d_model
self.num_heads = num_heads
self.depth = d_model // num_heads
self.wq = Dense(d_model)
self.wk = Dense(d_model)
self.wv = Dense(d_model)
self.dense = Dense(d_model)
def split_heads(self, x, batch_size):
x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))
return tf.transpose(x, perm=[0, 2, 1, 3])
def call(self, v, k, q, mask):
batch_size = tf.shape(q)[0]
q = self.wq(q)
k = self.wk(k)
v = self.wv(v)
q = self.split_heads(q, batch_size)
k = self.split_heads(k, batch_size)
v = self.split_heads(v, batch_size)
scaled_attention, _ = scaled_dot_product_attention(q, k, v, mask)
scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])
concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model))
output = self.dense(concat_attention)
return output
def scaled_dot_product_attention(q, k, v, mask):
matmul_qk = tf.matmul(q, k, transpose_b=True)
dk = tf.cast(tf.shape(k)[-1], tf.float32)
scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)
if mask is not None:
scaled_attention_logits += (mask * -1e9)
attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)
output = tf.matmul(attention_weights, v)
return output, attention_weights
# Sample transformer encoder layer
class EncoderLayer(tf.keras.layers.Layer):
def __init__(self, d_model, num_heads, dff, rate=0.1):
super(EncoderLayer, self).__init__()
self.mha = MultiHeadAttention(d_model, num_heads)
self.ffn = tf.keras.Sequential([
Dense(dff, activation='relu'),
Dense(d_model)
])
self.layernorm1 = LayerNormalization(epsilon=1e-6)
self.layernorm2 = LayerNormalization(epsilon=1e-6)
self.dropout1 = Dropout(rate)
self.dropout2 = Dropout(rate)
def call(self, x, training, mask):
attn_output = self.mha(x, x, x, mask)
attn_output = self.dropout1(attn_output, training=training)
out1 = self.layernorm1(x + attn_output)
ffn_output = self.ffn(out1)
ffn_output = self.dropout2(ffn_output, training=training)
out2 = self.layernorm2(out1 + ffn_output)
return out2
Este ejemplo demuestra la implementación de un modelo Transformer utilizando la biblioteca TensorFlow, específicamente la API de Keras. El modelo Transformer es un tipo de modelo de aprendizaje profundo que ha tenido particular éxito en el manejo de tareas de secuencia a secuencia, como la traducción de idiomas o el resumen de textos.
En primer lugar, se declara la clase MultiHeadAttention
. Esta clase representa el mecanismo de autoatención multi-cabeza en el modelo Transformer. Permite que el modelo se enfoque en diferentes posiciones de la secuencia de entrada al generar una secuencia de salida, haciendo posible capturar varios aspectos de la información de entrada.
La clase toma dos parámetros: d_model
, que es la dimensionalidad de la entrada, y num_heads
, que es el número de cabezas de atención. Dentro de la clase, se declaran varias capas densas para las transformaciones lineales de las consultas (queries), claves (keys) y valores (values). El método split_heads
remodela las consultas, claves y valores en múltiples cabezas, y el método call
aplica el mecanismo de atención en las consultas, claves y valores, y devuelve la salida.
A continuación, se define la función scaled_dot_product_attention
. Esta función calcula los pesos de atención y la salida para el mecanismo de atención. Calcula el producto punto de la consulta y la clave, lo escala por la raíz cuadrada de la profundidad (la última dimensión de la clave), aplica una máscara si se proporciona, y luego aplica una función softmax para obtener los pesos de atención. Estos pesos se utilizan luego para obtener una suma ponderada de los valores, que forma la salida del mecanismo de atención.
Finalmente, se define la clase EncoderLayer
. Esta clase representa una sola capa del codificador del Transformer. Cada capa del codificador consiste en un mecanismo de autoatención multi-cabeza y una red neuronal de avance punto a punto. El método call
aplica la autoatención en la entrada, seguido de dropout, conexión residual y normalización de capa. Luego, aplica la red de avance en la salida, seguida nuevamente de dropout, conexión residual y normalización de capa.
Cabe señalar que las capas densas se utilizan para transformar las entradas para el mecanismo de atención y dentro de la red de avance. Dropout se utiliza para prevenir el sobreajuste y LayerNormalization se utiliza para normalizar las salidas de cada subcapa. Todo el mecanismo de atención está encapsulado en la clase MultiHeadAttention
para su reutilización.
Este código sirve como base para construir modelos Transformer más complejos. Por ejemplo, se podrían apilar múltiples instancias de EncoderLayer
para formar la parte completa del codificador del Transformer, y se podrían definir capas similares para la parte del decodificador. Además, se podrían añadir componentes adicionales como codificación posicional y una capa de softmax de salida para completar el modelo.
1.3.2 Aprendizaje por Transferencia
El Aprendizaje por Transferencia es un método de aprendizaje automático donde un modelo desarrollado para una tarea se reutiliza como punto de partida para un modelo en una segunda tarea. Es un enfoque popular en el aprendizaje profundo donde los modelos preentrenados se utilizan como punto de partida en tareas de visión por computadora y procesamiento del lenguaje natural.
En otras palabras, el aprendizaje por transferencia es un método donde el conocimiento de un modelo adquirido de una tarea previa se aplica a un nuevo problema similar. Este enfoque es particularmente efectivo en el aprendizaje profundo debido a los vastos recursos computacionales y de tiempo necesarios para desarrollar modelos de redes neuronales en estos problemas y por los grandes saltos en habilidad que proporcionan en problemas relacionados.
Este enfoque se usa ampliamente en varias aplicaciones como el Procesamiento del Lenguaje Natural (NLP), la Visión por Computadora, e incluso en el campo de la música y el arte donde se están utilizando modelos generativos para crear nuevas obras de arte y componer música.
El Aprendizaje por Transferencia, por lo tanto, es una técnica poderosa que ayuda a mejorar el rendimiento de los modelos en tareas con datos limitados al aprovechar el conocimiento adquirido de tareas relacionadas con datos abundantes. Es uno de los avances significativos en el campo del Aprendizaje Profundo.
El aprendizaje por transferencia se ha convertido en una técnica poderosa en el aprendizaje profundo, permitiendo que los modelos entrenados en grandes conjuntos de datos se ajusten para tareas específicas con conjuntos de datos más pequeños. Este enfoque reduce significativamente los recursos computacionales y el tiempo requerido para el entrenamiento.
Ejemplo: Ajuste Fino de BERT para Clasificación de Texto
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had."]
labels = [1, 0] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Este ejemplo utiliza la biblioteca Transformers de Hugging Face. Este script demuestra cómo ajustar finamente un modelo BERT (Bidirectional Encoder Representations from Transformers) preentrenado para una tarea de clasificación binaria de texto. Desglosamos lo que hace el script.
El script comienza importando los módulos y clases necesarios. Trae BertTokenizer
y TFBertForSequenceClassification
de la biblioteca Transformers, que están específicamente diseñados para tareas que involucran modelos BERT. El BertTokenizer
se usa para convertir el texto de entrada en un formato que el modelo BERT puede entender, mientras que TFBertForSequenceClassification
es un modelo BERT con una capa de clasificación en la parte superior. El script también importa Adam
de la API de Keras de TensorFlow, que es el optimizador que se utilizará para entrenar el modelo.
A continuación, el script carga el modelo BERT preentrenado y su tokenizador asociado utilizando el método from_pretrained
. El argumento 'bert-base-uncased'
especifica que el script debe usar la versión "uncased" del modelo BERT base, lo que significa que el modelo no distingue entre letras mayúsculas y minúsculas. Este modelo ha sido entrenado en un gran corpus de texto en inglés y puede generar representaciones significativas para oraciones en inglés.
El script luego define algunos datos de muestra con fines de demostración. La variable texts
es una lista de dos oraciones en inglés, mientras que la variable labels
es una lista de dos enteros que representan el sentimiento de la oración correspondiente en la variable texts
(1 para sentimiento positivo, 0 para sentimiento negativo).
Después de definir los datos, el script tokeniza los textos de entrada utilizando el tokenizador cargado. La llamada al tokenizer
convierte las oraciones en la variable texts
en un formato que el modelo BERT puede entender. El método devuelve un diccionario que incluye varios objetos tipo tensor que el modelo necesita como entrada. El argumento return_tensors='tf'
especifica que estos objetos deben ser tensores de TensorFlow. El argumento padding=True
asegura que todas las oraciones se rellenen a la misma longitud, mientras que truncation=True
asegura que las oraciones más largas que la longitud máxima de entrada del modelo se recorten. El argumento max_length=128
especifica esta longitud máxima.
A continuación, el script compila el modelo especificando el optimizador, la función de pérdida y las métricas a rastrear durante el entrenamiento. El optimizador se establece en Adam con una tasa de aprendizaje de 2e-5. La función de pérdida se establece en el método compute_loss
integrado del modelo, que calcula la pérdida de clasificación. El script también especifica que se debe rastrear la precisión durante el entrenamiento.
Con el modelo ahora compilado, el script entrena el modelo con los datos de entrada. Se llama al método model.fit
con los tensores de entrada, las etiquetas y la configuración de entrenamiento adicional. El modelo se entrena durante 3 épocas, con un tamaño de lote de 8. Una época es un pase completo a través de todo el conjunto de datos de entrenamiento, y un tamaño de lote de 8 significa que los pesos del modelo se actualizan después de ver 8 muestras.
Finalmente, el script utiliza el modelo entrenado para hacer predicciones sobre los mismos datos de entrada. Se llama al método model.predict
con los tensores de entrada, y las predicciones resultantes se imprimen en la consola. Estas predicciones serían una medida de la confianza del modelo de que las oraciones de entrada tienen un sentimiento positivo.
1.3.3 Redes Generativas Antagónicas (GANs)
Las Redes Generativas Antagónicas (GANs) son una clase de algoritmos de inteligencia artificial utilizados en el aprendizaje automático no supervisado, implementados por un sistema de dos redes neuronales que compiten entre sí en un marco de juego de suma cero.
Las GANs consisten en dos partes, un Generador y un Discriminador. El Generador, que captura la distribución de los datos, comienza generando datos sintéticos y los alimenta al Discriminador junto con datos reales. El Discriminador, que estima la probabilidad de que una instancia dada provenga de los datos reales en lugar del Generador, se entrena para distinguir entre los dos tipos de datos.
En otras palabras, el Generador intenta engañar al Discriminador produciendo datos sintéticos cada vez mejores, mientras que el Discriminador se vuelve continuamente mejor para distinguir los datos reales de los falsos. Esto crea una especie de carrera armamentista entre los dos componentes, lo que lleva a la generación de datos sintéticos muy realistas.
Las GANs han visto una amplia aplicación en áreas como la generación de imágenes, la generación de videos y la generación de voz. Sin embargo, entrenar una GAN puede ser una tarea desafiante ya que requiere equilibrar el entrenamiento de dos redes diferentes.
Las GANs han revolucionado la modelización generativa al usar un generador y un discriminador en un entorno competitivo para producir datos sintéticos realistas. Las GANs se han aplicado a una amplia gama de tareas, desde la generación de imágenes hasta la mejora de datos.
Ejemplo: Implementación Básica de una GAN
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
Dense(1, activation='sigmoid')
])
return model
# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')
# Training the GAN
import numpy as np
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5 # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 128
epochs = 10000
for epoch in range(epochs):
# Train discriminator
idx = np.random.randint(0, x_train.shape[0], batch_size)
real_images = x_train[idx]
noise = np.random.normal(0, 1, (batch_size, 100))
fake_images = generator.predict(noise)
d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
# Train generator
noise = np.random.normal(0, 1, (batch_size, 100))
g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))
# Print progress
if epoch % 1000 == 0:
print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")
En el contexto de este código de ejemplo, el generador y el discriminador se construyen y compilan por separado. El generador utiliza una capa densa para mapear desde un espacio de ruido de 100 dimensiones a un espacio de 28281 dimensiones. El generador utiliza una función de activación LeakyReLU para la primera capa. La segunda capa es una capa densa con una función de activación tanh, seguida de una capa de remodelación para formar la imagen de salida.
El discriminador, por otro lado, es un clasificador que distingue entre imágenes reales y falsas (generadas). El modelo del discriminador toma como entrada una imagen de tamaño 28281, la aplana, la pasa a través de una capa densa con una función de activación LeakyReLU y finalmente a través de una capa densa con una función de activación sigmoide. El modelo del discriminador se compila luego con el optimizador adam y la pérdida de entropía cruzada binaria, ya que se trata de un problema de clasificación binaria.
El entrenamiento del GAN implica alternar entre entrenar el discriminador y el generador. Para entrenar el discriminador, se utilizan tanto imágenes reales (del conjunto de datos MNIST) como imágenes falsas (generadas por el generador). A las imágenes reales se les asigna una etiqueta de 1 y a las imágenes falsas se les asigna una etiqueta de 0. Luego, el discriminador se entrena en este conjunto de datos mixto.
Al entrenar el generador, el objetivo es engañar al discriminador. Por lo tanto, el generador intenta generar imágenes que sean clasificadas como reales (o 1) por el discriminador. El generador nunca ve realmente ninguna imagen real, solo recibe retroalimentación a través del discriminador.
El código también importa el conjunto de datos MNIST de los datasets de TensorFlow, normaliza las imágenes para que estén en el rango de [-1, 1] y las remodela para que tengan la forma (28, 28, 1).
El proceso de entrenamiento se repite durante un número determinado de épocas (iteraciones sobre todo el conjunto de datos), y en cada época se entrena primero el discriminador y luego el generador. La pérdida del discriminador (una medida de qué tan bien puede distinguir las imágenes reales de las falsas) y la pérdida del generador (una medida de qué tan bien puede engañar al discriminador) se imprimen después de cada época. De esta manera, se puede monitorear el proceso de entrenamiento.
Esta implementación básica de GAN sirve como un buen punto de partida para entender y experimentar con este tipo de redes. Sin embargo, en la práctica, las GANs pueden ser difíciles de entrenar y pueden requerir una selección cuidadosa de la arquitectura y los hiperparámetros.
1.3.4 Aprendizaje por Refuerzo
El aprendizaje por refuerzo (RL) ha visto avances significativos, particularmente con el desarrollo de redes de Q profundo (DQN) y métodos de gradiente de políticas. RL se ha aplicado con éxito en juegos, control robótico y conducción autónoma.
El aprendizaje por refuerzo es un tipo de aprendizaje automático en el que un agente aprende a tomar decisiones realizando acciones en un entorno para alcanzar un objetivo. El agente aprende de las consecuencias de sus acciones, en lugar de ser enseñado explícitamente, recibiendo recompensas o penalizaciones por sus acciones.
El agente aprende a alcanzar un objetivo en un entorno incierto y potencialmente complejo. En el aprendizaje por refuerzo, una inteligencia artificial se enfrenta a una situación similar a un juego. La computadora emplea prueba y error para encontrar una solución al problema. Para que la máquina haga lo que el programador quiere, la inteligencia artificial recibe recompensas o penalizaciones por las acciones que realiza. Su objetivo es maximizar la recompensa total.
A pesar de que el diseñador establece la política de recompensas, es decir, las reglas del juego, no le da al modelo pistas o sugerencias sobre cómo resolver el juego. Depende del modelo averiguar cómo realizar la tarea para maximizar la recompensa, comenzando con pruebas completamente aleatorias y terminando con tácticas sofisticadas y habilidades sobrehumanas. Al aprovechar el poder de la búsqueda y muchas pruebas, el aprendizaje por refuerzo es actualmente la forma más efectiva de estimular la creatividad de la máquina. A diferencia de los seres humanos, la inteligencia artificial puede acumular experiencia a partir de miles de juegos paralelos si un algoritmo de aprendizaje por refuerzo se ejecuta en una infraestructura informática suficientemente poderosa.
El aprendizaje por refuerzo se ha utilizado para enseñar a las máquinas a jugar juegos como Go y Ajedrez contra campeones mundiales, para simular la marcha bípeda, la conducción autónoma y otras tareas complejas que anteriormente se pensaba que solo los humanos podían lograr.
El futuro del aprendizaje por refuerzo es prometedor, ya que abre una vía para desarrollar máquinas que pueden aprender y adaptarse a escenarios complejos por sí solas. Sin embargo, al igual que cualquier otra tecnología de IA, también debe usarse de manera responsable, considerando todas sus implicaciones sociales y éticas.
Ejemplo: Q-Learning para Grid World
import numpy as np
# Environment setup
grid_size = 4
rewards = np.zeros((grid_size, grid_size))
rewards[3, 3] = 1 # Goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action selection
def choose_action(state):
if np
.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (3, 3):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
Este código de ejemplo implementa una forma básica de Q-Learning, un algoritmo de aprendizaje por refuerzo sin modelo, en un entorno simple de mundo cuadrícula.
La primera parte del código configura el entorno. Se define una cuadrícula de un tamaño determinado, con cada celda de la cuadrícula inicializada con una recompensa de cero. Sin embargo, al estado objetivo, ubicado en la celda de la cuadrícula (3,3), se le asigna una recompensa de uno. Este es el objetivo que el agente de aprendizaje debe esforzarse por alcanzar.
A continuación, se definen varios parámetros cruciales para el algoritmo de Q-Learning. El factor de descuento gamma
se establece en 0.9, lo que determina la importancia de las recompensas futuras. Un gamma
de 0 hace que el agente sea "miópico" (solo considerando recompensas actuales), mientras que un gamma
cercano a 1 hace que busque una recompensa alta a largo plazo. La tasa de aprendizaje alpha
se establece en 0.1, lo que determina en qué medida la información recién adquirida reemplaza la información antigua. La tasa de exploración epsilon
se establece en 0.1, lo que determina la frecuencia con la que el agente elige una acción aleatoria en lugar de la acción que cree que tiene el mejor efecto a largo plazo.
Luego se inicializa una tabla Q con ceros, que servirá como una tabla de búsqueda donde el agente puede encontrar la mejor acción a tomar mientras está en un determinado estado.
La función choose_action
es una implementación de la política epsilon-greedy. En este caso, la mayoría de las veces, el agente elegirá la acción que tiene la máxima recompensa futura esperada, que es la parte de explotación. Pero, un porcentaje epsilon del tiempo, el agente elegirá una acción aleatoria, que es la parte de exploración.
La parte principal del código es un bucle que simula 1000 episodios de interacción del agente con el entorno. Durante cada episodio, el agente comienza desde el estado inicial (0,0) y continúa eligiendo acciones y transitando al siguiente estado hasta llegar al estado objetivo (3,3). Para cada acción tomada, el valor Q de la acción para el estado actual se actualiza utilizando el algoritmo de Q-Learning, que actualiza el valor Q basado en la tasa de aprendizaje, la recompensa recibida y el valor Q máximo para el nuevo estado. Este proceso lleva de manera incremental a valores de acción cada vez mejores.
Al final del proceso de aprendizaje, el código imprime la tabla Q aprendida. Esta tabla indicará al agente el retorno esperado para cada acción en cada estado, guiando efectivamente al agente al objetivo de la manera más eficiente en cuanto a recompensas.
Este simple ejemplo de Q-Learning sirve como una base para entender la mecánica fundamental de este poderoso algoritmo de aprendizaje por refuerzo. Con entornos más complejos y mejoras en el algoritmo, Q-Learning puede resolver tareas mucho más complejas.
1.3.5 Aprendizaje Auto-supervisado
El aprendizaje auto-supervisado aprovecha los datos no etiquetados generando etiquetas sustitutas a partir de los propios datos. Este enfoque ha demostrado ser eficaz en tareas como el aprendizaje de representaciones y el preentrenamiento de modelos para tareas posteriores.
En el aprendizaje auto-supervisado, el sistema aprende a predecir algunas partes de los datos a partir de otras partes. Esto se hace creando una tarea "sustituta" para aprender a partir de una gran cantidad de datos no etiquetados, lo cual puede ser muy útil cuando los datos etiquetados son escasos o costosos de obtener. Las representaciones aprendidas suelen ser útiles para tareas posteriores, y el modelo puede ajustarse con un conjunto de datos etiquetado más pequeño para una tarea específica.
Por ejemplo, una tarea de aprendizaje auto-supervisado para imágenes podría ser predecir el color de una imagen en escala de grises. En este caso, el modelo aprendería características útiles sobre la estructura y el contenido de las imágenes sin necesidad de etiquetas proporcionadas por humanos.
El aprendizaje auto-supervisado ha mostrado gran promesa en una variedad de aplicaciones. Se ha utilizado con éxito para preentrenar modelos para tareas de procesamiento del lenguaje natural, donde primero se entrena un modelo para predecir la siguiente palabra en una oración, y luego se ajusta para una tarea específica como el análisis de sentimientos o la respuesta a preguntas. También ha mostrado promesa en la visión por computadora, donde los modelos preentrenados en una tarea auto-supervisada pueden ajustarse para tareas como la detección de objetos o la segmentación de imágenes.
Un ejemplo específico de aprendizaje auto-supervisado es un método llamado SimCLR (Aprendizaje Contrastivo Simple de Representaciones Visuales). En SimCLR, se entrena un modelo para reconocer si dos versiones aumentadas de una imagen son iguales o diferentes. El modelo aprende a extraer características que son consistentes en diferentes aumentos de la misma imagen, lo cual resulta ser una habilidad muy útil para muchas tareas de visión por computadora.
Ejemplo: Aprendizaje Contrastivo con SimCLR
import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten
from tensorflow.keras.models import Sequential
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.optimizers import Adam
# Sample contrastive learning model (SimCLR)
def build_simclr_model(input_shape):
base_model = tf.keras.applications.ResNet50(include_top=False, input_shape=input_shape, pooling='avg')
base_model.trainable = True
model = Sequential([
base_model,
Flatten(),
Dense(128, activation='relu'),
Dense(128) # Projection head
])
return model
# Contrastive loss function
def contrastive_loss(y_true, y_pred):
temperature = 0.1
y_true = tf.cast(y_true, tf.int32)
y_pred = tf.math.l2_normalize(y_pred, axis=1)
logits = tf.matmul(y_pred, y_pred, transpose_b=True) / temperature
labels = tf.one_hot(y_true, depth=y_pred.shape[0])
return SparseCategoricalCrossentropy(from_logits=True)(labels, logits)
# Compile and train the model
input_shape = (224, 224, 3)
model = build_simclr_model(input_shape)
model.compile(optimizer=Adam(learning_rate=0.001), loss=contrastive_loss)
# Assuming 'x_train' and 'y_train' are the training data and labels (augmentations)
model.fit(x_train, y_train, epochs=10, batch_size=32, verbose=1)
El script comienza importando los módulos necesarios. Utiliza la biblioteca TensorFlow, una poderosa biblioteca de software de código abierto para el aprendizaje automático, y Keras, una API de redes neuronales de alto nivel que también es parte de TensorFlow.
La función build_simclr_model
se define para construir el modelo. La base del modelo es un modelo ResNet50 preentrenado, un modelo de aprendizaje profundo popular con 50 capas, ya entrenado en un gran conjunto de datos. include_top=False
significa que las capas de salida completamente conectadas del modelo utilizadas para la clasificación no están incluidas, y pooling='avg'
indica que se aplica un agrupamiento promedio global a la salida del último bloque de convolución de ResNet50, reduciendo la dimensionalidad de la salida. Luego se usa la API Sequential
para apilar capas sobre el modelo base. Se agrega una capa Flatten
para transformar el formato de las imágenes de una matriz bidimensional (de 28x28 píxeles) a una matriz unidimensional (de 28 * 28 = 784 píxeles). Se agregan dos capas Dense
, la primera con activación ReLU y la segunda sin activación, que sirve como la cabeza de proyección del modelo.
Después de la construcción del modelo, se define la función de pérdida contrastiva como contrastive_loss
. El aprendizaje contrastivo es un tipo de método de aprendizaje auto-supervisado que entrena modelos para aprender características similares a partir de datos similares. Esta función primero normaliza el vector de predicción, luego calcula el producto punto entre los vectores de predicción divididos por un parámetro de temperatura para crear los logits. Luego, crea etiquetas one-hot a partir de las etiquetas verdaderas y calcula la pérdida de entropía cruzada categórica escasa entre estas etiquetas y los logits.
El script luego compila y entrena el modelo SimCLR utilizando el optimizador Adam y la función de pérdida contrastiva. El optimizador Adam es una extensión del descenso de gradiente estocástico, un algoritmo popular para entrenar una amplia gama de modelos en el aprendizaje automático. La tasa de aprendizaje se establece en 0.001.
Luego, el modelo se ajusta a los datos de entrenamiento 'x_train' y 'y_train' durante 10 épocas con un tamaño de lote de 32. 'x_train' y 'y_train' son marcadores de posición en este contexto y serían reemplazados por los datos de entrenamiento reales y las etiquetas durante el entrenamiento en el mundo real. Una época es una medida del número de veces que se utilizan todos los vectores de entrenamiento una vez para actualizar los pesos en el proceso de entrenamiento.