Acontinuación se realizará una regresión para predecir el puntaje de los clientes en un mall.
Para el análisis se considerará:
Un breve análisis exploratorio de los datos.
Dividir los datos en conjunto de entrenamiento y validación (80%-20%).
Con esto se realizarán diversos algoritmos de regresión mediante ternsorflow, donde el entrenamiento se realizará utilizando validación cruzada aleatoria (en cada algoritmo hay una descripción del procedimiento utilizado).
El conjunto de validación se utilizará para comparar el RMSE de cada método.
En el dataset se encuentra en el siguiente archivo 'Mall_Customers.csv' que contiene la siguiente información:
CustomerID: Identificador único para cada cliente.
Gender: Género de cada cliente (Másculino/Femenino).
Age: Edad del cliente.
Annual Income (k$): Ingresos anuales del cliente.
Spending Score (1-100): Puntaje asignado por el centro comercial en función del comportamiento y gasto del cliente.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import tensorflow as tf
import requests
from sklearn.preprocessing import normalize
import os.path
import csv
data_file_name = "Mall-Customers.csv"
data = []
with open(data_file_name, newline='') as csvfile:
csv_reader = csv.reader(csvfile)
data_header = next(csv_reader)
for row in csv_reader:
data.append(row)
pd.DataFrame(data, columns=data_header).head(10)
# Transformación de datos de string a float.
# Binarización del género (Male = 1, Female = 0).
x_vals = np.array([[float(x[0]), x[1]=='Male', float(x[2]), float(x[3])] for x in data]) # Male=1, Female=0
y_vals = np.array([float(x[4]) for x in data])
pd.DataFrame(x_vals, columns=data_header[0:4]).head(10)
# Gráfico del Puntaje vs cada Predictor
fig, axs = plt.subplots(4, 1, sharex=False, figsize=[10,10])
fig.suptitle(str(data_header[4])+' vs Predictores')
fig.subplots_adjust(hspace=0.4, wspace=0.4, top=0.92)
color = ['blue', 'red', 'green', 'orange']
for n1 in range(4):
axs[n1].scatter(x_vals[:,n1], y_vals, color=color[n1])
axs[n1].set_title(data_header[n1])
for ax in fig.get_axes():
ax.label_outer()
Para los análisis posteriores no se considerará la variable CustomerID, ya que no contiene información útil.
Acontinuación se separán los datos en conjuntos de prueba y entrenamiento. Posteriormente se normalizarán los datos en función de los valores del conjunto de entrenamiento.
f_train_test = 0.8
np.random.seed(123)
train_idx = np.random.choice(len(x_vals), round(len(x_vals)*f_train_test), replace=False)
test_idx = np.array(list(set(range(len(x_vals)))-set(train_idx)))
# Se elimina CustomerID del conjunto de datos.
x_vals = np.array([[x[1]=='Male', float(x[2]), float(x[3])] for x in data]) # Male=1, Female=0
x_vals_train = x_vals[train_idx,]
x_vals_test = x_vals[test_idx]
y_vals_train = y_vals[train_idx]
y_vals_test = y_vals[test_idx]
# función de normalización por columna.
def normalize_cols(m, col_min = np.array([None]), col_max = np.array([None])):
if not col_min[0]:
col_min = m.min(axis=0)
if not col_max[0]:
col_max = m.max(axis=0)
return(m-col_min)/(col_max-col_min), col_min, col_max
x_vals_train, train_min, train_max = np.nan_to_num(normalize_cols(x_vals_train))
x_vals_test,_,_ = np.nan_to_num(normalize_cols(x_vals_test, train_min, train_max))
pd.DataFrame(x_vals_train, columns=data_header[1:4]).head(10)
# Gráfico de Edad vs Predictores (queda fuera Spending Score)
predictor = 1
fig, axs = plt.subplots(3, 1, sharex=False, figsize=[10,10])
fig.suptitle(str(data_header[predictor+1])+' vs Predictores')
fig.subplots_adjust(hspace=0.4, wspace=0.4, top=0.92)
color = ['blue', 'red', 'green', 'orange']
for n1 in range(3):
axs[n1].scatter(x_vals_train[:,n1],
#y_vals_train,
x_vals_train[:,predictor],
color=color[n1])
axs[n1].set_title(data_header[n1+1])
for ax in fig.get_axes():
ax.label_outer()
Se utilizarán los siguientes modelos de regresión para predecir Spending Score:
# Declaración de Sesion y fijado de semillas aleatorias.
session = tf.Session()
tf.set_random_seed(123)
np.random.seed(123)
# Hiperparámetros
epochs = 8000
batch_size = int(np.round(x_vals_train.shape[0]*0.7)) # 70% del conjunto de entrenamiento
learning_rate = 0.01
elastic_param1 = tf.constant(1.0)
elastic_param2 = tf.constant(0.01)
# Declaración de placeholders
x_data = tf.placeholder(shape=[None, x_vals_train.shape[1]], dtype = tf.float32)
y_target = tf.placeholder(shape = [None, 1], dtype = tf.float32)
# Declaración de variables
A_ml = tf.Variable(tf.random_normal(shape = [x_vals_train.shape[1],1]))
b_ml = tf.Variable(tf.random_normal(shape = [1,1]))
# Declaración de predicción
y_pred = tf.add(tf.matmul(x_data, A_ml), b_ml)
# Función de pérdidas
l1_a_loss = tf.reduce_mean(tf.abs(A_ml))
l2_a_loss = tf.reduce_mean(tf.square(A_ml))
e1_term = tf.multiply(elastic_param1, l1_a_loss)
e2_term = tf.multiply(elastic_param2, l2_a_loss)
loss = tf.expand_dims(tf.sqrt(tf.add(tf.add(tf.reduce_mean(tf.square(y_target- y_pred)),e1_term), e2_term)), 0)
# Inicialización de varibles
init = tf.global_variables_initializer()
session.run(init)
# Optimizador
my_opt = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
train_step = my_opt.minimize(loss)
El entrenamiento se lleva a cabo de la siguiente manera:
loss_vec_train = []
loss_vec_test = []
loss_vec_val = []
for i in range(epochs):
# carga las observaciones aleatorias de entrenamiento y pruebas
rand_idx = np.random.choice(len(x_vals_train), size=batch_size)
rand_test_idx = np.array(list(set(range(len(x_vals_train)))-set(rand_idx)))
# separa conjunto de prueba aleatorio
rand_x = x_vals_train[rand_idx]
rand_y = np.transpose([y_vals_train[rand_idx]])
# separa conjunto de prueba aleatorio
rand_test_x = x_vals_train[rand_test_idx]
rand_test_y = np.transpose([y_vals_train[rand_test_idx]])
# entrenamiento iteracion i
session.run(train_step, feed_dict={x_data : rand_x, y_target: rand_y})
# perdidas del conjunto de entrenamiento
temp_loss_train = session.run(loss, feed_dict={x_data: rand_x, y_target: rand_y})[0]
loss_vec_train.append(temp_loss_train)
# perdidas del conjunto de prueba
temp_loss_test = session.run(loss, feed_dict={x_data: rand_test_x, y_target: rand_test_y})[0]
loss_vec_test.append(temp_loss_test)
# perdidas del conjunto de validación
temp_loss_val = session.run(loss, feed_dict={x_data: x_vals_test, y_target: np.transpose([y_vals_test])})[0]
loss_vec_val.append(temp_loss_val)
if(i+1)%np.round(epochs/30)==0:
acc_and_loss = [i+1, temp_loss_train, temp_loss_test, temp_loss_val]
print("Paso #{}, Train Loss {:.3f}, Test Loss {:.3f}, Valid Loss {:.3f}".format(*acc_and_loss))
# Ecuación regresión lineal
slope = session.run(A_ml)
[[intercept]] = session.run(b_ml)
print("Modelo de regresión: \n y="+str(slope[0][0])+"x1+"+str(slope[1][0])+"x2+"+str(slope[2][0])+"x3+"+str(intercept))
x_vals_norm,_,_= np.nan_to_num(normalize_cols(x_vals, train_min, train_max))
pred_test_rl = session.run(y_pred, feed_dict={x_data: x_vals_test})
plt.figure(figsize=(14,6))
plt.plot(loss_vec_train, 'b-.', label="Pérdidas en Entrenamiento")
plt.plot(loss_vec_test, 'r--', label="Pérdidas en Pruebas")
plt.plot(loss_vec_val, 'k-', label="Pérdidas en Validación")
plt.title("Regresión Lineal (ElasticNet)")
plt.xlabel("Iteración")
plt.ylabel("Pérdida")
plt.legend(loc="upper right")
plt.show()
plt.figure(figsize=(14,6))
plt.plot(abs(y_vals_test - pred_test_rl[0]), 'r-.',label="Error de Validación")
plt.axhline(y=np.sqrt(np.mean(np.square(y_vals_test - pred_test_rl[0]))), color='b', linestyle='-',
label = "RMSE "+str(np.round(np.sqrt(np.mean(np.square(y_vals_test - pred_test_rl[0]))),3)))
plt.title("Spending Score (Regresión Lineal)")
plt.xlabel("Iteración")
plt.ylabel("RMSE")
plt.legend(loc="upper right")
plt.show()
RMSE = []
RMSE.append(np.sqrt(np.mean(np.square(y_vals_test - pred_test_rl[0]))))
session.close()
# Declaración de Sesion y fijado de semillas aleatorias.
session = tf.Session()
tf.set_random_seed(123)
np.random.seed(123)
# Hiperparámetros
epochs = 8000
batch_size = int(np.round(x_vals_train.shape[0]*0.7))
learning_rate = 0.01
epsilon = tf.constant([0.3])
# Declaración de placeholders
x_data = tf.placeholder(shape=[None, x_vals_train.shape[1]], dtype = tf.float32)
y_target = tf.placeholder(shape = [None, 1], dtype = tf.float32)
# Declaración de variables
A_svm_l = tf.Variable(tf.random_normal(shape = [x_vals_train.shape[1],1]))
b_svm_l = tf.Variable(tf.random_normal(shape = [1,1]))
# Declaración de predicción
y_pred = tf.add(tf.matmul(x_data, A_svm_l), b_svm_l)
# Función de pérdida
loss_svm_l = tf.expand_dims(tf.reduce_mean(tf.maximum(0.0, tf.subtract(tf.abs(tf.subtract(y_pred, y_target)), epsilon))),0)
# Inicialización de variables
init = tf.global_variables_initializer()
session.run(init)
# Optimizador
my_opt = tf.train.GradientDescentOptimizer(learning_rate=learning_rate)
train_step = my_opt.minimize(loss_svm_l)
El entrenamiento se lleva a cabo de la siguiente manera:
loss_vec_train = []
loss_vec_test = []
loss_vec_val = []
for i in range(epochs):
# carga las observaciones aleatorias de entrenamiento y pruebas
rand_idx = np.random.choice(len(x_vals_train), size=batch_size)
rand_test_idx = np.array(list(set(range(len(x_vals_train)))-set(rand_idx)))
# separa conjunto de prueba aleatorio
rand_x = x_vals_train[rand_idx]
rand_y = np.transpose([y_vals_train[rand_idx]])
# separa conjunto de prueba aleatorio
rand_test_x = x_vals_train[rand_test_idx]
rand_test_y = np.transpose([y_vals_train[rand_test_idx]])
# entrenamiento iteracion i
session.run(train_step, feed_dict={x_data : rand_x, y_target: rand_y})
# perdidas del conjunto de entrenamiento
temp_loss_train = session.run(loss_svm_l, feed_dict={x_data: rand_x, y_target: rand_y})[0]
loss_vec_train.append(temp_loss_train)
# perdidas del conjunto de prueba
temp_loss_test = session.run(loss_svm_l, feed_dict={x_data: rand_test_x, y_target: rand_test_y})[0]
loss_vec_test.append(temp_loss_test)
# perdidas del conjunto de validación
temp_loss_val = session.run(loss_svm_l, feed_dict={x_data: x_vals_test, y_target: np.transpose([y_vals_test])})[0]
loss_vec_val.append(temp_loss_val)
if(i+1)%np.round(epochs/30)==0:
acc_and_loss = [i+1, temp_loss_train, temp_loss_test, temp_loss_val]
print("Paso #{}, Train Loss {:.3f}, Test Loss {:.3f}, Valid Loss {:.3f}".format(*acc_and_loss))
pred_test_svm = session.run(y_pred, feed_dict={x_data: x_vals_test})
plt.figure(figsize=(14,6))
plt.plot(loss_vec_train, 'b-.', label="Pérdidas en Entrenamiento")
plt.plot(loss_vec_test, 'r--', label="Pérdidas en Pruebas")
plt.plot(loss_vec_val, 'k-', label="Pérdidas en Validación")
plt.title("SVM lineal")
plt.xlabel("Iteración")
plt.ylabel("Pérdida")
plt.legend(loc="upper right")
plt.show()
plt.figure(figsize=(14,6))
plt.plot(abs(y_vals_test - pred_test_svm[0]), 'r-.',label="Error de Validación")
plt.axhline(y=np.sqrt(np.mean(np.square(y_vals_test - pred_test_svm[0]))), color='b', linestyle='-',
label = "RMSE "+str(np.round(np.sqrt(np.mean(np.square(y_vals_test - pred_test_svm[0]))),3)))
plt.title("Spending Score (SVM lineal)")
plt.xlabel("Iteración")
plt.ylabel("RMSE")
plt.legend(loc="upper right")
plt.show()
RMSE.append(np.sqrt(np.mean(np.square(y_vals_test - pred_test_svm[0]))))
session.close()
# Declaración de Sesion y fijado de semillas aleatorias.
session = tf.Session()
tf.set_random_seed(123)
np.random.seed(123)
# Hiperparámetros
k = 7
learning_rate = 0.01
#batch_size = int(np.ceil(len(x_vals_train)/2))
batch_size = len(x_vals_test)
# Declaración de placeholders
x_data_train = tf.placeholder(shape=[batch_size, x_vals_train.shape[1]], dtype=tf.float32)
x_data_test = tf.placeholder(shape=[batch_size, x_vals_train.shape[1]], dtype=tf.float32)
y_target_train = tf.placeholder(shape = [None, 1], dtype=tf.float32)
y_target_test = tf.placeholder(shape=[None, 1], dtype=tf.float32)
# Cálculo de matriz de ponderación de distancias
weight_matrix_2 = x_vals_train.std(0)
weight_matrix = tf.cast(tf.diag(weight_matrix_2), dtype=tf.float32)
# Cálculo de distancia
subs_term = tf.subtract(x_data_train, tf.expand_dims(x_data_test, 1))
first_prod = tf.matmul(subs_term, tf.tile(tf.expand_dims(weight_matrix,0), [batch_size, 1, 1]))
second_prod = tf.matmul(first_prod, tf.transpose(subs_term, perm=[0,2,1]))
distance = tf.sqrt(tf.matrix_diag_part(second_prod))
# Toma los k datos con menor distancia al valor dado y cálcula su valor promedio
top_k_xvals, top_k_idx = tf.nn.top_k(tf.negative(distance), k = k)
x_sums = tf.expand_dims(tf.reduce_sum(top_k_xvals,1),1)
x_sums_rep = tf.matmul(x_sums, tf.ones([1,k], tf.float32))
x_vals_w = tf.expand_dims(tf.divide(top_k_xvals, x_sums_rep),1) # -> wi = di / \sum(dj)
top_k_yvals = tf.gather(y_target_train, top_k_idx)
# Declaración de predicción
prediction = tf.squeeze(tf.matmul(x_vals_w, top_k_yvals),axis=[1]) ## \sum w_i y_i
# función de pérdida
mse = tf.reduce_mean(tf.abs(tf.subtract(prediction, y_target_test)))
Este algotrimo no se entrena, los resultados se calculan a partir de los promedios de los k vecinos más cercanos.
Para evaluar el desempeño de este algoritmo se tomarán 100 muestras aleatorias del conjunto de entrenamiento de tamaño batch_size. Con estos datos se calcularán los valores de Speding Score del resto del conjunto de entrenamiento fuera del batch_size.
En cada pasada se revisará el desempeño del conjunto de validación (pérdida en cada iteración y RMSE).
num_loops = 100
loss_vec_train = []
loss_vec_val = []
for i in range(num_loops):
# carga las observaciones aleatorias de entrenamiento y pruebas
rand_idx = np.random.choice(len(x_vals_train), size=batch_size)
rand_test_idx = np.array(list(set(range(len(x_vals_train)))-set(rand_idx)))
rand_test_idx = np.random.choice(rand_test_idx, size=batch_size)
# separa conjunto de entrenamiento aleatorio
rand_x = x_vals_train[rand_idx]
rand_y = np.transpose([y_vals_train[rand_idx]])
# separa conjunto de prueba aleatorio
rand_test_x = x_vals_train[rand_test_idx]
rand_test_y = np.transpose([y_vals_train[rand_test_idx]])
# separa conjunto de validacion aleatorio
rand_idx_val = np.random.choice(len(x_vals_test), size=batch_size)
#rand_val_x = x_vals_test[rand_idx_val]
rand_val_x = x_vals_test
#rand_val_y = np.transpose([y_vals_test[rand_idx_val]])
rand_val_y = np.transpose([y_vals_test])
temp_loss_train = session.run(mse, feed_dict={x_data_train: rand_x,
x_data_test: rand_test_x,
y_target_train: rand_y,
y_target_test: rand_test_y})
loss_vec_train.append(temp_loss_train)
temp_loss_val = session.run(mse, feed_dict={x_data_train: rand_x,
x_data_test: rand_val_x,
y_target_train: rand_y,
y_target_test: rand_val_y})
pred_test_knn = session.run(prediction, feed_dict={x_data_train: rand_x, x_data_test: rand_val_x,
y_target_train: rand_y, y_target_test: rand_val_y})
loss_vec_val.append(temp_loss_val)
if(i+1)%np.round(num_loops/10)==0:
acc_and_loss = [i+1, temp_loss_train]
print("Paso #{}, Loss {:.3f}".format(*acc_and_loss))
plt.figure(figsize=(14,6))
plt.plot(loss_vec_train, 'b-.', label="Pérdidas en Entrenamiento")
#plt.plot(loss_vec_test, 'r--', label="Pérdidas en Pruebas")
plt.plot(loss_vec_val, 'k-', label="Pérdidas en Validación")
plt.title("KNN k=7 (100 iteraciones)")
plt.xlabel("Iteración")
plt.ylabel("Pérdida")
plt.legend(loc="upper right")
plt.show()
plt.figure(figsize=(14,6))
plt.plot(abs(rand_val_y - pred_test_knn[0]), 'r-.',label="Error de Validación")
plt.axhline(y=np.sqrt(np.mean(np.square(rand_val_y - pred_test_knn[0]))), color='b', linestyle='-',
label = "RMSE "+str(np.round(np.sqrt(np.mean(np.square(rand_val_y - pred_test_knn[0]))),3)))
plt.title("Spending Score (KNN)")
plt.xlabel("Iteración")
plt.ylabel("RMSE")
plt.legend(loc="upper right")
plt.show()
RMSE.append(np.sqrt(np.mean(np.square(rand_val_y - pred_test_knn[0]))))
session.close()
# Declaración de Sesion y fijado de semillas aleatorias.
session = tf.Session()
tf.set_random_seed(123)
np.random.seed(123)
# Hiperparámetros
epochs = 2500
batch_size = int(np.round(x_vals_train.shape[0]*0.7))
learning_rate = 0.001
layers = [x_vals_train.shape[1], 45, 25, 11]
sd = 0.1
# Declaración de funciones weight y bias (variables)
def init_weight(shape, st_dev):
weight = tf.Variable(tf.random_normal(shape = shape, stddev=st_dev))
return weight
def init_bias(shape, st_dev):
bias = tf.Variable(tf.random_normal(shape = shape, stddev=st_dev))
return bias
# Definición de función de conexión
def full_connected(input_layer, weights, biases):
layer = tf.add(tf.matmul(input_layer, weights), biases)
layer = tf.nn.relu(layer)
return(layer)
# Arquitectura de la red
## Capa 1
w1 = init_weight(shape=[layers[0], layers[1]], st_dev=sd)
b1 = init_bias(shape=[layers[1]], st_dev=sd)
layer1 = full_connected(x_data, w1, b1)
## Capa 2
w2 = init_weight(shape=[layers[1], layers[2]], st_dev=sd)
b2 = init_bias(shape=[layers[2]], st_dev=sd)
layer2 = full_connected(layer1, w2, b2)
## Capa 3
w3 = init_weight(shape=[layers[2], layers[3]], st_dev=sd)
b3 = init_bias(shape=[layers[3]], st_dev=sd)
layer3 = full_connected(layer2, w3, b3)
## Capa 4
w4 = init_weight(shape=[layers[3],1], st_dev=sd)
b4 = init_bias(shape=[1], st_dev=sd)
layer4 = full_connected(layer3, w4, b4)
# función de pérdida y entrenamiento
loss_nn = tf.reduce_mean(tf.abs(y_target-layer4))
my_optim = tf.train.GradientDescentOptimizer(learning_rate)
train_step = my_optim.minimize(loss_nn)
# Inicialización de variables
init = tf.global_variables_initializer()
session.run(init)
El entrenamiento se lleva a cabo de la siguiente manera:
loss_vec_train = []
loss_vec_test = []
loss_vec_val = []
for i in range(epochs):
# carga las observaciones aleatorias de entrenamiento y pruebas
rand_idx = np.random.choice(len(x_vals_train), size=batch_size)
rand_test_idx = np.array(list(set(range(len(x_vals_train)))-set(rand_idx)))
# separa conjunto de prueba aleatorio
rand_x = x_vals_train[rand_idx]
rand_y = np.transpose([y_vals_train[rand_idx]])
# separa conjunto de prueba aleatorio
rand_test_x = x_vals_train[rand_test_idx]
rand_test_y = np.transpose([y_vals_train[rand_test_idx]])
# entrenamiento iteracion i
session.run(train_step, feed_dict={x_data : rand_x, y_target: rand_y})
# perdidas del conjunto de entrenamiento
temp_loss_train = session.run(loss_nn, feed_dict={x_data: rand_x, y_target: rand_y})
loss_vec_train.append(temp_loss_train)
# perdidas del conjunto de prueba
temp_loss_test = session.run(loss_nn, feed_dict={x_data: rand_test_x, y_target: rand_test_y})
loss_vec_test.append(temp_loss_test)
# perdidas del conjunto de validación
temp_loss_val = session.run(loss_nn, feed_dict={x_data: x_vals_test, y_target: np.transpose([y_vals_test])})
loss_vec_val.append(temp_loss_val)
if(i+1)%np.round(epochs/30)==0:
acc_and_loss = [i+1, temp_loss_train, temp_loss_test, temp_loss_val]
print("Paso #{}, Train Loss {:.3f}, Test Loss {:.3f}, Valid Loss {:.3f}".format(*acc_and_loss))
# Predicción conjunto validación
pred_test_nn = [x for x in session.run(layer4, feed_dict={x_data:x_vals_test})]
plt.figure(figsize=(14,6))
plt.plot(loss_vec_train, 'b-.', label="Pérdidas en Entrenamiento")
plt.plot(loss_vec_test, 'r--', label="Pérdidas en Pruebas")
plt.plot(loss_vec_val, 'k-', label="Pérdidas en Validación")
plt.title("Red Neuronal")
plt.xlabel("Iteración")
plt.ylabel("Pérdida")
plt.legend(loc="upper right")
plt.show()
plt.figure(figsize=(14,6))
plt.plot(abs(y_vals_test - pred_test_nn[0]), 'r-.',label="Error de Validación")
plt.axhline(y=np.sqrt(np.mean(np.square(y_vals_test - pred_test_nn[0]))), color='b', linestyle='-',
label = "RMSE "+str(np.round(np.sqrt(np.mean(np.square(y_vals_test - pred_test_nn[0]))),3)))
plt.title("Spending Score (Red Neuronal)")
plt.xlabel("Iteración")
plt.ylabel("Error")
plt.legend(loc="upper right")
plt.show()
RMSE.append(np.sqrt(np.mean(np.square(y_vals_test - pred_test_nn[0]))))
session.close()
pd.DataFrame(np.round(RMSE, 3),
columns=["RMSE"],
index=["Regresión Lineal", " SVM-Lineal", "KNN (k=7)", "Red Neuronal"])
Los resultados indican que los algortimos con mejor desempeño son KNN y Red Neuronal. Esto se debe a que la relación entre los datos es no-lineal.
El mejor resultado lo entrega KNN, pero tiene la desventaja que depende de los datos selecionados y no es mejorable, ya que toma el promedio de los datos con menores distancias.
Por otro lado la red neuronal, aunque presenta un resultado levemente peor, se puede mejorar optimizando los hiperparámetros (learning_rate y configuración de la red).
link: https://github.com/desareca/Proyectos_tensorflow/tree/master/Analisis-Consmidores