Generación de datos sintéticos con Numpy y Scikit-Learn

En este artículo, cubriremos cómo generar datos sintéticos con Python, Numpy y Scikit Learn. Generaremos datos 1D, multietiqueta, clasificación multiclase y datos de regresión.

Introducción

En este tutorial, discutiremos los detalles de la generación de diferentes conjuntos de datos sintéticos utilizando las bibliotecas Numpy y Scikit-learn. Veremos cómo se pueden generar diferentes muestras a partir de varias distribuciones con parámetros conocidos.

También discutiremos la generación de conjuntos de datos para diferentes propósitos, como regresión, clasificación y agrupación. Al final, veremos cómo podemos generar un conjunto de datos que imite la distribución de un conjunto de datos existente.

La necesidad de datos sintéticos

En la ciencia de datos, los datos sintéticos juegan un papel muy importante. Nos permite probar un nuevo algoritmo bajo condiciones controladas. En otras palabras, podemos generar datos que prueben una propiedad o comportamiento muy específico de nuestro algoritmo.

Por ejemplo, podemos probar su rendimiento en conjuntos de datos equilibrados y desequilibrados, o podemos evaluar su rendimiento bajo diferentes niveles de ruido. Al hacer esto, podemos establecer una línea de base del rendimiento de nuestro algoritmo en varios escenarios.

Hay muchos otros casos en los que se pueden necesitar datos sintéticos. Por ejemplo, los datos reales pueden ser difíciles o costosos de adquirir, o pueden tener muy pocos puntos de datos. Otra razón es la privacidad, donde los datos reales no se pueden revelar a otros.

Configuración

Antes de escribir código para la generación de datos sintéticos, importemos las bibliotecas requeridas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import numpy as np

# Needed for plotting
import matplotlib.colors
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Needed for generating classification, regression and clustering datasets
import sklearn.datasets as dt

# Needed for generating data from an existing dataset
from sklearn.neighbors import KernelDensity
from sklearn.model_selection import GridSearchCV

Entonces, tendremos algunas variables útiles al principio:

1
2
3
4
5
6
7
# Define the seed so that results can be reproduced
seed = 11
rand_state = 11

# Define the color maps for plots
color_map = plt.cm.get_cmap('RdYlBu')
color_map_discrete = matplotlib.colors.LinearSegmentedColormap.from_list("", ["red","cyan","magenta","blue"])

Generación de muestras 1D a partir de distribuciones conocidas

Ahora, hablaremos sobre la generación de puntos de muestra a partir de distribuciones conocidas en 1D.

El módulo random de numpy ofrece una amplia gama de formas de generar números aleatorios muestreados a partir de una distribución conocida con un conjunto fijo de parámetros. Para fines de reproducción, pasaremos la semilla a la llamada RandomState y mientras usemos esa misma semilla, obtendremos los mismos números.

Definamos una lista de distribución, como uniforme, normal, exponencial, etc., una lista de parámetros y una lista de colores para que podamos discernir visualmente entre estos:

1
2
3
4
5
rand = np.random.RandomState(seed)    

dist_list = ['uniform','normal','exponential','lognormal','chisquare','beta']
param_list = ['-1,1','0,1','1','0,1','2','0.5,0.9']
colors_list = ['green','blue','yellow','cyan','magenta','pink']

Ahora, los empaquetaremos en subparcelas de una Figura para su visualización y generaremos datos sintéticos basados ​​en estas distribuciones, parámetros y les asignaremos los colores adecuados.

Esto se hace a través de la función eval(), que usamos para generar una expresión de Python. Por ejemplo, podemos usar rand.exponential(1, 5000) para generar muestras a partir de una distribución exponencial de escala 1 y el tamaño de 5000.

Aquí, usaremos nuestra dist_list, param_list y color_list para generar estas llamadas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
fig,ax = plt.subplots(nrows=2, ncols=3,figsize=(12,7))
plt_ind_list = np.arange(6)+231

for dist, plt_ind, param, colors in zip(dist_list, plt_ind_list, param_list, colors_list):
    x = eval('rand.'+dist+'('+param+',5000)') 
    
    plt.subplot(plt_ind)
    plt.hist(x,bins=50,color=colors)
    plt.title(dist)

fig.subplots_adjust(hspace=0.4,wspace=.3) 
plt.suptitle('Sampling from Various Distributions',fontsize=20)
plt.show()

Esto resulta en:

generating 1d samples with numpy

Datos sintéticos para regresión

El paquete sklearn.datasets tiene funciones para generar conjuntos de datos sintéticos para regresión. Aquí, discutimos los datos lineales y no lineales para la regresión.

La función make_regression() devuelve un conjunto de puntos de datos de entrada (regresores) junto con su salida (objetivo). Esta función se puede ajustar con los siguientes parámetros:

  1. n_features - número de dimensiones/características de los datos generados
  2. ruido - desviación estándar del ruido gaussiano
  3. n_samples - número de muestras

La variable de respuesta es una combinación lineal del conjunto de entrada generado.

Una variable de respuesta es algo que depende de otras variables, en este caso particular, una característica de destino que estamos tratando de predecir utilizando todas las demás características de entrada.

En el siguiente código, se generaron datos sintéticos para diferentes niveles de ruido y consisten en dos características de entrada y una variable de destino. El color cambiante de los puntos de entrada muestra la variación en el valor del objetivo, correspondiente al punto de datos. Los datos se generan en 2D para una mejor visualización, pero los datos de alta dimensión se pueden crear usando el parámetro n_features:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
map_colors = plt.cm.get_cmap('RdYlBu')
fig,ax = plt.subplots(nrows=2, ncols=3,figsize=(16,7))
plt_ind_list = np.arange(6)+231

for noise,plt_ind in zip([0,0.1,1,10,100,1000],plt_ind_list): 
    x,y = dt.make_regression(n_samples=1000,
                             n_features=2,
                             noise=noise,
                             random_state=rand_state) 
    
    plt.subplot(plt_ind)
    my_scatter_plot = plt.scatter(x[:,0],
                                  x[:,1],
                                  c=y,
                                  vmin=min(y),
                                  vmax=max(y),
                                  s=35,
                                  cmap=color_map)
    
    plt.title('noise: '+str(noise))
    plt.colorbar(my_scatter_plot)
    
fig.subplots_adjust(hspace=0.3,wspace=.3)
plt.suptitle('make_regression() With Different Noise Levels',fontsize=20)
plt.show()

Aquí, hemos creado un grupo de 1000 muestras, con dos variables de entrada (características). Dependiendo del nivel de ruido (0..1000), podemos ver cómo los datos generados difieren significativamente en el gráfico de dispersión:

generando datos sintéticos para regresión

La familia de funciones make_friedman

Hay tres versiones de la función make_friedman?() (reemplace ? con un valor de {1,2,3}).

Estas funciones generan la variable objetivo usando una combinación no lineal de las variables de entrada, como se detalla a continuación:

  • make_friedman1(): El argumento n_features de esta función tiene que ser al menos 5, por lo que genera un número mínimo de 5 dimensiones de entrada. Aquí el objetivo viene dado por:
    $$
    y(x) = 10 * \sin(\pi x_0 x_1) + 20(x_2 - 0.5)^2 + 10x_3 + 5x_4 + \text{noise}
    $$

  • make_friedman2(): Los datos generados tienen 4 dimensiones de entrada. La variable de respuesta viene dada por:

$$
y(x) = \sqrt{(x_0^2+x_1 x_2 - \frac{1}{(x_1 x_3)^2})} + \text{ruido}
$$

  • make_friedman3(): Los datos generados en este caso también tienen 4 dimensiones. La variable de salida viene dada por:

$$
y(x) = \arctan(\frac{x_1 x_2 -\frac{1}{(x_1 x_3)}}{x_0})+\text{ruido}
$$

El siguiente código genera los conjuntos de datos utilizando estas funciones y traza las primeras tres características en 3D, con colores que varían según la variable de destino:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
fig = plt.figure(figsize=(18,5))

x,y = dt.make_friedman1(n_samples=1000,n_features=5,random_state=rand_state)
ax = fig.add_subplot(131, projection='3d')
my_scatter_plot = ax.scatter(x[:,0], x[:,1],x[:,2], c=y, cmap=color_map)
fig.colorbar(my_scatter_plot)
plt.title('make_friedman1')

x,y = dt.make_friedman2(n_samples=1000,random_state=rand_state)
ax = fig.add_subplot(132, projection='3d')
my_scatter_plot = ax.scatter(x[:,0], x[:,1],x[:,2], c=y, cmap=color_map)
fig.colorbar(my_scatter_plot)
plt.title('make_friedman2')

x,y = dt.make_friedman3(n_samples=1000,random_state=rand_state)
ax = fig.add_subplot(133, projection='3d')
my_scatter_plot = ax.scatter(x[:,0], x[:,1],x[:,2], c=y, cmap=color_map)
fig.colorbar(my_scatter_plot)
plt.suptitle('make_friedman?() for Non-Linear Data',fontsize=20)
plt.title('make_friedman3')

plt.show()

crear la familia de funciones de Friedman

Datos sintéticos para clasificación

Scikit-learn tiene funciones simples y fáciles de usar para generar conjuntos de datos para la clasificación en el módulo sklearn.dataset. Veamos un par de ejemplos.

make_classification() para problemas de clasificación de clases n

Para problemas de clasificación de clases n, la función make_classification() tiene varias opciones:

  1. class_sep: especifica si las diferentes clases deben estar más dispersas y más fáciles de discriminar
  2. n_features: Número de características
  3. n_redundant: número de características redundantes
  4. n_repeated: número de características repetidas
  5. n_classes: Número total de clases

Hagamos un conjunto de datos de clasificación para datos de entrada bidimensionales. Tendremos diferentes valores de class_sep para un problema de clasificación binaria. Los mismos puntos coloreados pertenecen a la misma clase. Vale la pena señalar que esta función también puede generar clases desequilibradas:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
fig,ax = plt.subplots(nrows=1, ncols=3,figsize=(16,5))
plt_ind_list = np.arange(3)+131

for class_sep,plt_ind in zip([0.1,1,10],plt_ind_list):
    x,y = dt.make_classification(n_samples=1000,
                                 n_features=2,
                                 n_repeated=0,
                                 class_sep=class_sep,
                                 n_redundant=0,
                                 random_state=rand_state)
    
    plt.subplot(plt_ind)
    my_scatter_plot = plt.scatter(x[:,0],
                                  x[:,1],
                                  c=y,
                                  vmin=min(y),
                                  vmax=max(y),
                                  s=35,
                                  cmap=color_map_discrete)
    plt.title('class_sep: '+str(class_sep))

fig.subplots_adjust(hspace=0.3,wspace=.3)
plt.suptitle('make_classification() With Different class_sep Values',fontsize=20)
plt.show()

make classification for n-classes

make_multilabel_classification() para problemas de clasificación de múltiples etiquetas

La función make_multilabel_classification() genera datos para problemas de clasificación de etiquetas múltiples. Tiene varias opciones, de las cuales la más destacable es n_label, que establece el número medio de etiquetas por punto de datos.

Consideremos un problema de etiquetas múltiples de 4 clases, con el vector objetivo de las etiquetas convertido en un solo valor para la visualización. Los puntos están coloreados de acuerdo con la representación decimal del vector de etiqueta binaria. El código lo ayudará a ver cómo el uso de un valor diferente para n_label cambia la clasificación de un punto de datos generado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fig,ax = plt.subplots(nrows=1, ncols=3,figsize=(16,5))
plt_ind_list = np.arange(3)+131

for label,plt_ind in zip([2,3,4],plt_ind_list):
    x,y = dt.make_multilabel_classification(n_samples=1000,
                                            n_features=2,
                                            n_labels=label,
                                            n_classes=4,
                                            random_state=rand_state)
    target = np.sum(y*[8,4,2,1],axis=1)
    
    plt.subplot(plt_ind)
    my_scatter_plot = plt.scatter(x[:,0],
                                  x[:,1],
                                  c=target,
                                  vmin=min(target),
                                  vmax=max(target),
                                  cmap=color_map)
    plt.title('n_labels: '+str(label))

fig.subplots_adjust(hspace=0.3,wspace=.3)
plt.suptitle('make_multilabel_classification() With Different n_labels Values',fontsize=20)
plt.show()

multilabel classification

Datos sintéticos para agrupamiento

Para la agrupación, sklearn.datasets proporciona varias opciones. Aquí, cubriremos las funciones make_blobs() y make_circles().

hacer_blobs()

La función make_blobs() genera datos a partir de distribuciones gaussianas isotrópicas. El número de características, el número de centros y la desviación estándar de cada grupo se pueden especificar como un argumento.

Aquí, ilustramos esta función en 2D y mostramos cómo cambian los puntos de datos con diferentes valores del parámetro cluster_std:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
fig,ax = plt.subplots(nrows=1, ncols=3,figsize=(16,5))
plt_ind_list = np.arange(3)+131

for std,plt_ind in zip([0.5,1,10],plt_ind_list):
    x, label = dt.make_blobs(n_features=2,
                             centers=4,
                             cluster_std=std,
                             random_state=rand_state)
    
    plt.subplot(plt_ind)    
    my_scatter_plot = plt.scatter(x[:,0],
                                  x[:,1],
                                  c=label,
                                  vmin=min(label),
                                  vmax=max(label),
                                  cmap=color_map_discrete)
    plt.title('cluster_std: '+str(std))

fig.subplots_adjust(hspace=0.3,wspace=.3)
plt.suptitle('make_blobs() With Different cluster_std Values',fontsize=20)
plt.show()

synthetic data for clustering

hacer_círculos()

La función make_circles() genera dos círculos concéntricos con el mismo centro, uno dentro del otro.

Usando el parámetro de ruido, se puede agregar distorsión a los datos generados. Este tipo de datos es útil para evaluar algoritmos de agrupamiento basados ​​en afinidad. El siguiente código muestra los datos sintéticos generados a diferentes niveles de ruido:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
fig,ax = plt.subplots(nrows=1, ncols=3,figsize=(16,5))
plt_ind_list = np.arange(3)+131

for noise,plt_ind in zip([0,0.1,1],plt_ind_list):
    x, label = dt.make_circles(noise=noise,random_state=rand_state)
    
    plt.subplot(plt_ind)    
    my_scatter_plot = plt.scatter(x[:,0],
                                  x[:,1],
                                  c=label,
                                  vmin=min(label),
                                  vmax=max(label),
                                  cmap=color_map_discrete)
    plt.title('noise: '+str(noise))

fig.subplots_adjust(hspace=0.3,wspace=.3)
plt.suptitle('make_circles() With Different Noise Levels',fontsize=20)
plt.show()

make synthetic data circles

Generación de muestras derivadas de un conjunto de datos de entrada {#generación de muestras derivadas de un conjunto de datos de entrada}

Hay muchas formas de generar muestras de datos adicionales a partir de un conjunto de datos existente. Aquí, ilustramos un método muy simple que primero estima la densidad del kernel de los datos utilizando un kernel gaussiano y luego genera muestras adicionales de esta distribución.

Para visualizar las muestras recién generadas, echemos un vistazo al conjunto de datos de caras de Olivetti, recuperable a través de sklearn.datasets.fetch_olivetti_faces(). El conjunto de datos tiene 10 imágenes faciales diferentes de 40 personas diferentes.

Esto es lo que haremos:

  1. Obtenga los datos de las caras
  2. Generar el modelo de densidad kernel a partir de datos
  3. Use la densidad del kernel para generar nuevas muestras de datos
  4. Muestre las caras originales y sintéticas.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Fetch the dataset and store in X
faces = dt.fetch_olivetti_faces()
X= faces.data

# Fit a kernel density model using GridSearchCV to determine the best parameter for bandwidth
bandwidth_params = {'bandwidth': np.arange(0.01,1,0.05)}
grid_search = GridSearchCV(KernelDensity(), bandwidth_params)
grid_search.fit(X)
kde = grid_search.best_estimator_

# Generate/sample 8 new faces from this dataset
new_faces = kde.sample(8, random_state=rand_state)

# Show a sample of 8 original face images and 8 generated faces derived from the faces dataset
fig,ax = plt.subplots(nrows=2, ncols=8,figsize=(18,6),subplot_kw=dict(xticks=[], yticks=[]))
for i in np.arange(8):
    ax[0,i].imshow(X[10*i,:].reshape(64,64),cmap=plt.cm.gray)   
    ax[1,i].imshow(new_faces[i,:].reshape(64,64),cmap=plt.cm.gray)    
ax[0,3].set_title('Original Data',fontsize=20)
ax[1,3].set_title('Synthetic Data',fontsize=20)
fig.subplots_adjust(wspace=.1)
plt.show()

generando datos sintéticos basados ​​en entrada

Las caras originales que se muestran aquí son una muestra de 8 caras elegidas de 400 imágenes, para tener una idea de cómo se ve el conjunto de datos original. Podemos generar tantos puntos de datos nuevos como queramos usando la función sample().

En este ejemplo, se generaron 8 nuevas muestras. Tenga en cuenta que las caras sintéticas que se muestran aquí no corresponden necesariamente a la cara de la persona que se muestra arriba.

Yendo más lejos: proyecto de extremo a extremo portátil

¿Tu naturaleza inquisitiva te hace querer ir más allá? Recomendamos consultar nuestro Proyecto guiado: ["Predicción práctica del precio de la vivienda: aprendizaje automático en Python"](https://wikihtp.com/courses/hands-on-house- precio-predicción-aprendizaje-máquina-en-python/#cta){target="_blank"}.

[](https://wikihtp.com/ cursos/predicción-de-precio-de-la-casa-práctica-aprendizaje-de-máquina-en-python/#cta)

En este proyecto guiado, aprenderá a crear potentes modelos tradicionales de aprendizaje automático, así como modelos de aprendizaje profundo, utilizar Ensemble Learning y capacitar a los meta-aprendices para predecir los precios de la vivienda a partir de una bolsa de modelos Scikit-Learn y Keras.

Con Keras, la API de aprendizaje profundo creada sobre Tensorflow, experimentaremos con arquitecturas, crearemos un conjunto de modelos apilados y entrenaremos una red neuronal meta-aprendizaje (modelo de nivel 1) para averiguar el precio de un casa.

El aprendizaje profundo es sorprendente, pero antes de recurrir a él, se recomienda intentar resolver el problema con técnicas más simples, como los algoritmos de aprendizaje superficial. Nuestro rendimiento de referencia se basará en un algoritmo de Regresión de bosque aleatorio. Además, exploraremos la creación de conjuntos de modelos a través de Scikit-Learn a través de técnicas como embalaje y votación.

Este es un proyecto integral y, como todos los proyectos de aprendizaje automático, comenzaremos con Análisis exploratorio de datos, seguido de Preprocesamiento de datos y, finalmente, Creación de modelos de aprendizaje superficial y Profundo para ajustarse a los datos que hemos explorado y limpiado previamente.

Conclusiones

En este artículo conocimos algunos métodos para generar conjuntos de datos sintéticos para varios problemas. Los conjuntos de datos sintéticos nos ayudan a evaluar nuestros algoritmos en condiciones controladas y establecer una línea de base para las medidas de rendimiento.

Python tiene una amplia gama de funciones que se pueden utilizar para la generación de datos artificiales. Es importante comprender qué funciones y API se pueden usar para sus requisitos específicos. ecíficos.