En este artículo, aprenderá formas prácticas y seguras de utilizar el aumento de datos para reducir el sobreajuste y mejorar la generalización en imágenes, texto, audio y conjuntos de datos tabulares.
Los temas que cubriremos incluyen:
Cómo funciona el aumento y cuándo ayuda. Estrategias de aumento en línea versus fuera de línea. Ejemplos prácticos para imágenes (TensorFlow/Keras), texto (NLTK), audio (librosa) y datos tabulares (NumPy/Pandas), además de los errores críticos de la fuga de datos.
Muy bien, vayamos a ello.
La guía completa sobre el aumento de datos para el aprendizaje automático
Imagen por autor
Supongamos que ha creado su modelo de aprendizaje automático, ha ejecutado los experimentos y ha observado los resultados preguntándose qué salió mal. La precisión del entrenamiento parece excelente, tal vez incluso impresionante, pero cuando verificas la precisión de la validación… no tanto. Puede resolver este problema obteniendo más datos. Pero eso es lento, costoso y, a veces, simplemente imposible.
No se trata de inventar datos falsos. Se trata de crear nuevos ejemplos de entrenamiento modificando sutilmente los datos que ya tienes sin cambiar su significado ni etiqueta. Estás mostrando a tu modelo el mismo concepto en múltiples formas. Estás enseñando lo que es importante y lo que se puede ignorar. El aumento ayuda a que su modelo generalice en lugar de simplemente memorizar el conjunto de entrenamiento. En este artículo, aprenderá cómo funciona el aumento de datos en la práctica y cuándo usarlo. Específicamente, cubriremos:
Qué es el aumento de datos y por qué ayuda a reducir el sobreajuste La diferencia entre el aumento de datos en línea y fuera de línea Cómo aplicar el aumento a datos de imágenes con TensorFlow Técnicas de aumento simples y seguras para datos de texto Métodos de aumento comunes para conjuntos de datos tabulares y de audio Por qué la fuga de datos durante el aumento puede dañar silenciosamente su modelo
Aumento de datos fuera de línea versus en línea
El aumento puede ocurrir antes del entrenamiento o durante el entrenamiento. El aumento sin conexión expande el conjunto de datos una vez y lo guarda. El aumento en línea genera nuevas variaciones en cada época. Los canales de aprendizaje profundo generalmente prefieren el aumento en línea porque expone el modelo a una variación efectivamente ilimitada sin aumentar el almacenamiento.
Aumento de datos para datos de imágenes
El aumento de datos de imágenes es el lugar más intuitivo para comenzar. Un perro sigue siendo un perro si se lo gira ligeramente, se le hace zoom o se lo ve en diferentes condiciones de iluminación. Su modelo necesita ver estas variaciones durante el entrenamiento. Algunas técnicas comunes de aumento de imágenes son:
Rotación Voltear Cambiar tamaño Recortar Hacer zoom Desplazar Cortar Cambios de brillo y contraste
Estas transformaciones no cambian la etiqueta, sólo la apariencia. Demostrémoslo con un ejemplo simple usando TensorFlow y Keras:
1. Importación de bibliotecas
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.layers import Dense, Flatten, Conv2D, MaxPooling2D, Dropout
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
import tensorflow as tf
from tensorflow . keras . datasets import mnist
from tensorflow . keras . layers import Dense , Flatten , Conv2D , MaxPooling2D , Dropout
from tensorflow . keras . utils import to_categorical
from tensorflow . keras . preprocessing . image import ImageDataGenerator
from tensorflow . keras . models import Sequential
2. Cargando el conjunto de datos MNIST
(X_train, y_train), (X_test, y_test) = mnist.load_data()
# Normalize pixel values
X_train = X_train / 255.0
X_test = X_test / 255.0
# Reshape to (samples, height, width, channels)
X_train = X_train.reshape(-1, 28, 28, 1)
X_test = X_test.reshape(-1, 28, 28, 1)
# One-hot encode labels
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
( X_train , y_train ) , ( X_test , y_test ) = mnist . load_data ( )
# Normalize pixel values
X_train = X_train / 255.0
X_test = X_test / 255.0
# Reshape to (samples, height, width, channels)
X_train = X_train . reshape ( – 1 , 28 , 28 , 1 )
X_test = X_test . reshape ( – 1 , 28 , 28 , 1 )
# One-hot encode labels
y_train = to_categorical ( y_train , 10 )
y_prueba = to_categorical ( y_test , 10 )
Producción:
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
Downloading data from https : //storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz
3. Definición de ImageDataGenerator para aumento
datagen = ImageDataGenerator(
rotation_range=15, # rotate images by ±15 degrees
width_shift_range=0.1, # 10% horizontal shift
height_shift_range=0.1, # 10% vertical shift
zoom_range=0.1, # zoom in/out by 10%
shear_range=0.1, # apply shear transformation
horizontal_flip=False, # not needed for digits
fill_mode=”nearest” # fill missing pixels after transformations
)
datagen = ImageDataGenerator (
rotation_range = 15 , # rotate images by ±15 degrees
width_shift_range = 0.1 , # 10% horizontal shift
height_shift_range = 0.1 , # 10% vertical shift
zoom_range = 0.1 , # zoom in/out by 10%
shear_range = 0.1 , # apply shear transformation
horizontal_flip = False , # not needed for digits
fill_mode = ‘nearest’ # fill missing pixels after transformations
)
4. Construyendo un modelo CNN simple
model = Sequential([
Conv2D(32, (3, 3), activation=’relu’, input_shape=(28, 28, 1)),
MaxPooling2D((2, 2)),
Conv2D(64, (3, 3), activation=’relu’),
MaxPooling2D((2, 2)),
Flatten(),
Dropout(0.3),
Dense(64, activation=’relu’),
Dense(10, activation=’softmax’)
])
model.compile(optimizer=”adam”, loss=”categorical_crossentropy”, metrics=[‘accuracy’])
model = Sequential ( [
Conv2D ( 32 , ( 3 , 3 ) , activation = ‘relu’ , input_shape = ( 28 , 28 , 1 ) ) ,
MaxPooling2D ( ( 2 , 2 ) ) ,
Conv2D ( 64 , ( 3 , 3 ) , activation = ‘relu’ ) ,
MaxPooling2D ( ( 2 , 2 ) ) ,
Flatten ( ) ,
Dropout ( 0.3 ) ,
Dense ( 64 , activation = ‘relu’ ) ,
Dense ( 10 , activation = ‘softmax’ )
] )
model . compile ( optimizer = ‘adam’ , loss = ‘categorical_crossentropy’ , metrics = [ ‘accuracy’ ] )
5. Entrenando el modelo
batch_size = 64
epochs = 5
history = model.fit(
datagen.flow(X_train, y_train, batch_size=batch_size, shuffle=True),
steps_per_epoch=len(X_train)//batch_size,
epochs=epochs,
validation_data=(X_test, y_test)
)
batch_size = 64
epochs = 5
history = model . fit (
datagen . flow ( X_train , y_train , batch_size = batch_size , shuffle = True ) ,
steps_per_epoch = len ( X_train ) //batch_size,
epochs = epochs ,
validation_data = ( X_test , y_test )
)
Producción:
6. Visualización de imágenes aumentadas
import matplotlib.pyplot as plt
# Visualize five augmented variants of the first training sample
plt.figure(figsize=(10, 2))
for i, batch in enumerate(datagen.flow(X_train[:1], batch_size=1)):
plt.subplot(1, 5, i + 1)
plt.imshow(batch[0].reshape(28, 28), cmap='gray') plt.axis('off') si i == 4: romper plt.show()
importar matplotlib . trazado de datos como pl
# Visualice cinco variantes aumentadas de la primera muestra de entrenamiento
pl . figura ( tamaño de figura = ( 10 , 2 ) )
para i , lote en enumerar ( datagen . flow ( X_train [ : 1 ] , tamaño_lote = 1 ) ) :
pl . subtrama ( 1 , 5 , i + 1 )
pl . imshow ( lote [ 0 ] . remodelar ( 28 , 28 ) , cmap = 'gris' )
pl . eje ( 'apagado' )
si i == 4 :
romper
pl . espectáculo ( )
Producción:
Aumento de datos para datos textuales
El texto es más delicado. No se pueden reemplazar palabras al azar sin pensar en el significado. Pero los cambios pequeños y controlados pueden ayudar a que su modelo se generalice. Un ejemplo sencillo que utiliza la sustitución de sinónimos (con NLTK):
import nltk
from nltk.corpus import wordnet
import random
nltk.download(“wordnet”)
nltk.download(“omw-1.4”)
def synonym_replacement(sentence):
words = sentence.split()
if not words:
return sentence
idx = random.randint(0, len(words) – 1)
synsets = wordnet.synsets(words[idx])
if synsets and synsets[0].lemmas(): reemplazo = conjuntos sintéticos[0].lemas()[0].name().replace("_", " ") palabras[idx] = reemplazo return " ".join(words) text = "La película fue realmente buena" print(synonym_replacement(text))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
importar ntk
de ntk . importar corpus wordnet
importar aleatoriamente
nltk . descargar ( "wordnet" )
nltk . descargar ( "omw-1.4" )
def sinónimo_reemplazo ( oración ) :
palabras = oración . dividir ( )
si no palabras :
devolver oración
identificación = aleatorio . randint ( 0 , len ( palabras ) – 1 )
sintetizadores = red de palabras . conjuntos sintéticos ( palabras [ idx ] )
si sintetizadores y conjuntos de síntesis [ 0 ] . lemas ( ) :
reemplazo = conjuntos de síntesis [ 0 ] . lemas ( ) [ 0 ] . nombre ( ) . reemplazar ( "_" , " " )
palabras [ idx ] = reemplazo
devolver " " . unirse ( palabras )
texto = "La película fue realmente buena"
imprimir ( sinónimo_reemplazo ( texto ) )
Producción:
[nltk_data] Downloading package wordnet to /root/nltk_data…
The movie was truly good
[ nltk_data ] Downloading package wordnet to / root / nltk_data . . .
The movie was truly good
Mismo significado. Nuevo ejemplo de entrenamiento. En la práctica, las bibliotecas como nlpaug o las API de retrotraducción se suelen utilizar para obtener resultados más fiables.
Aumento de datos para datos de audio
Los datos de audio también se benefician enormemente del aumento. Algunas técnicas comunes de aumento de audio son:
Agregar ruido de fondo Estirar el tiempo Cambio de tono Escalar el volumen
Uno de los aumentos de audio más simples y utilizados es agregar ruido de fondo y alargar el tiempo. Estos ayudan a que los modelos de voz y sonido funcionen mejor en entornos ruidosos del mundo real. Entendamos con un ejemplo simple (usando librosa):
import librosa
import numpy as np
# Load built-in trumpet audio from librosa
audio_path = librosa.ex(“trumpet”)
audio, sr = librosa.load(audio_path, sr=None)
# Add background noise
noise = np.random.randn(len(audio))
audio_noisy = audio + 0.005 * noise
# Time stretching
audio_stretched = librosa.effects.time_stretch(audio, rate=1.1)
print(“Sample rate:”, sr)
print(“Original length:”, len(audio))
print(“Noisy length:”, len(audio_noisy))
print(“Stretched length:”, len(audio_stretched))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import librosa
import numpy as np
# Load built-in trumpet audio from librosa
audio_path = librosa . ex ( “trumpet” )
audio , sr = librosa . load ( audio_path , sr = None )
# Add background noise
noise = np . random . randn ( len ( audio ) )
audio_noisy = audio + 0.005 * noise
# Time stretching
audio_estirado = librosa . efectos . time_stretch ( audio , tasa = 1,1 )
print ( "Frecuencia de muestreo:" , Sr. )
imprimir ( "Longitud original:" , len ( audio ) )
print ( "Duración ruidosa:" , len ( audio_ruidoso ) )
imprimir ( "Longitud estirada:" , len ( audio_estirado ) )
Producción:
Downloading file ‘sorohanro_-_solo-trumpet-06.ogg’ from ‘https://librosa.org/data/audio/sorohanro_-_solo-trumpet-06.ogg’ to ‘/root/.cache/librosa’.
Sample rate: 22050
Original length: 117601
Noisy length: 117601
Stretched length: 106910
Downloading file ‘sorohanro_-_solo-trumpet-06.ogg’ from ‘https://librosa.org/data/audio/sorohanro_-_solo-trumpet-06.ogg’ to ‘/root/.cache/librosa’ .
Sample rate : 22050
Original length : 117601
Noisy length : 117601
Stretched length : 106910
Debes observar que el audio se carga a 22.050 Hz. Ahora, agregar ruido no cambia su duración, por lo que el audio ruidoso tiene el mismo tamaño que el original. La ampliación del tiempo acelera el audio y preserva el contenido.
Aumento de datos para datos tabulares
Los datos tabulares son el tipo de datos más confidencial para aumentar. A diferencia de las imágenes o el audio, no se pueden modificar valores arbitrariamente sin romper la estructura lógica de los datos. Sin embargo, existen algunas técnicas de aumento comunes:
Inyección de ruido: agregue ruido pequeño y aleatorio a las funciones numéricas mientras conserva la distribución general. SMOTE: Genera muestras sintéticas para clases minoritarias en problemas de clasificación. Mezclar: combine filas o columnas de manera que se mantenga la coherencia de las etiquetas. Transformaciones específicas de dominio: aplique cambios basados en la lógica según el conjunto de datos (por ejemplo, conversión de monedas, redondeo o normalización). Perturbación de características: altere ligeramente las características de entrada (p. ej., edad ± 1 año, ingresos ± 2%).
Ahora, entendamos con un ejemplo simple que usa la inyección de ruido para características numéricas (a través de NumPy y Pandas):
import numpy as np
import pandas as pd
# Sample tabular dataset
data = {
“age”: [25, 30, 35, 40],
“income”: [40000, 50000, 60000, 70000],
“credit_score”: [650, 700, 750, 800]
}
df = pd.DataFrame(data)
# Add small Gaussian noise to numerical columns
augmented_df = df.copy()
noise_factor = 0.02 # 2% noise
for col in augmented_df.columns:
noise = np.random.normal(0, noise_factor, size=len(df))
augmented_df[col] = augmented_df[col] * (1 + noise)
print(augmented_df)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import numpy as np
import pandas as pd
# Sample tabular dataset
data = {
“age” : [ 25 , 30 , 35 , 40 ] ,
“income” : [ 40000 , 50000 , 60000 , 70000 ] ,
“credit_score” : [ 650 , 700 , 750 , 800 ]
}
df = pd . DataFrame ( data )
# Add small Gaussian noise to numerical columns
augmented_df = df . copy ( )
noise_factor = 0.02 # 2% noise
for col in augmented_df . columns :
ruido = np . aleatorio . normal ( 0 , factor_ruido , tamaño = longitud ( df ) )
df_aumentado [ col ] = df_aumentado [ col ] * ( 1 + ruido )
imprimir ( aumentado_df )
Producción:
age income credit_score
0 24.399643 41773.983250 651.212014
1 30.343270 50962.007818 696.959347
2 34.363792 58868.638800 757.656837
3 39.147648 69852.508717 780.459666
age income credit _ score
0 24.399643 41773.983250 651.212014
1 30.343270 50962.007818 696.959347
2 34.363792 58868.638800 757.656837
3 39.147648 69852.508717 780.459666
Puede ver que esto modifica ligeramente los valores numéricos pero preserva la distribución general de los datos. También ayuda al modelo a generalizar en lugar de memorizar valores exactos.
El peligro oculto de la fuga de datos
Esta parte no es negociable. El aumento de datos debe aplicarse únicamente al conjunto de entrenamiento. Nunca debe aumentar los datos de validación o prueba. Si se filtran datos aumentados en la evaluación, sus métricas se vuelven engañosas. Su modelo lucirá genial en papel y fallará en producción. La separación limpia no es una buena práctica; es un requisito.
Conclusión
El aumento de datos ayuda cuando sus datos son limitados, hay sobreajuste y existen variaciones en el mundo real. No corrige etiquetas incorrectas, datos sesgados ni funciones mal definidas. Es por eso que comprender sus datos siempre viene antes de aplicar transformaciones. No es sólo un truco para competiciones o demostraciones de aprendizaje profundo. Es un cambio de mentalidad. No necesita buscar más datos, pero debe comenzar a preguntarse cómo podrían cambiar naturalmente sus datos existentes. Sus modelos dejan de sobreajustarse, comienzan a generalizar y finalmente se comportan como esperaba en primer lugar.