Modelling

Un modelo es una representación simplificada de la realidad creada para servir una propuesta. Las afirmaciones, la selección de características importantes, las restricciones y la trazabilidad definen un modelo.

Un ejemplo de un modelo es la representación cartográfica que nos posibilita posicionar lugares de interes, calcular distancias, o encontrar relaciones topológicas.

8dc28494a00f4a02aabd206701ba6f0d

Fuente imagen: Wikipedia

En ciencia de datos podemos tener modelos predictivos o modelos descriptivos. Los primeros forman un conjunto de formulas para estimar valores desconocidos. En el segundo caso, sirven para encontrar indicios subyacentes del proceso. Los datos del modelo representan hechos o evidencias de la realidad o de otro modelo. Siguiendo el ejemplo del mapa, represetan los bordes o contornos del territorio representado.

9790746473884c559dca5e19176a7235

Fuente de ambas figuras [2]

El proceso del aprendizaje automático

El proceso de aprendizaje automático consta de una serie de etapas: - 1. Preparación de datos. Carga, limpieza, transformación y división del conjunto de datos. - Selección de atributos o métricas adecuadas: análisis exploratorio de datos (EDA), selección de características. - 2º Selección de la técnica a aplicar: tipo de tarea, modelos disponibles, consideraciones prácticas. - 3º Ajuste de los hiperparámetros. Los hiperparámetros son configuraciones del modelo que no se aprenden directamente de los datos y deben ser ajustados manual o automáticamente. - 4º Evaluación del modelo. Pruebas iniciales, Metricas de evaluación, ajustes iterativos y pruebas finales.

Selección de atributos o métricas adecuadas

Dado un conjunto de muestras con un amplio número de características, el problema es elegir aquellas características que contribuyen al aprendizaje del modelo.

Dado este ejemplo de muestras: 8c81513dca6c48a39ce0b1276a0c5147

Las características disponibles son: - Forma de la cabeza: cuadrada o circular. - Forma del cuerpo: rectangular o ovalada. - Color del cuerpo: negro o blanco. - Función de su compra: yes/no.

Esta última características es el atributo objetivo (target value), es en definitiva lo que nos interesara predecir de futuros clientes. Para predecir este valor, deberíamos de plantear que atributos de estas personas nos van a portar valor. Si tan solo eligieramos el atributo body-color a negro, no tendríamos una representatividad del resto de la población.

Dejemos este problema de selección para más adelante con los conceptos de entropia y ganancia de información.

Primera aplicación de ML

Antes nos tenemos que plantear que librerías usaremos.

Vamos a proceder a instalar las siguientes versiones. Nota: para que todos trabajemos con las mismas versiones usaremos las de Google Colab (13/12/24) como referencia:

[ ]:
!pip install numpy==1.26.4
!pip install pandas==2.2.2
!pip install sklearn-pandas==2.2.0
!pip install scikit-learn==1.5.2
!pip install matplotlib==3.8.0
!pip install seaborn==0.13.2

Vamos a utilizar el siguiente catálogo de datos:

[12]:
import pandas as pd
import numpy as np

df = pd.read_csv("data/mushrooms.csv")

#Opcional:
es_col = [
    "clase",
    "forma del sombrero",
    "superficie del sombrero",
    "color del sombrero",
    "magulladuras",
    "olor",
    "unión de las láminas",
    "espaciamiento de las láminas",
    "tamaño de las láminas",
    "color de las láminas",
    "forma del tallo",
    "raíz del tallo",
    "superficie del tallo por encima del anillo",
    "superficie del tallo por debajo del anillo",
    "color del tallo por encima del anillo",
    "color del tallo por debajo del anillo",
    "tipo de velo",
    "color del velo",
    "número de anillos",
    "tipo de anillo",
    "color de la impresión de esporas",
    "población",
    "hábitat"
]
df.columns = es_col

print(df.shape)
print(df.columns)
print(df.head())
(8124, 23)
Index(['clase', 'forma del sombrero', 'superficie del sombrero',
       'color del sombrero', 'magulladuras', 'olor', 'unión de las láminas',
       'espaciamiento de las láminas', 'tamaño de las láminas',
       'color de las láminas', 'forma del tallo', 'raíz del tallo',
       'superficie del tallo por encima del anillo',
       'superficie del tallo por debajo del anillo',
       'color del tallo por encima del anillo',
       'color del tallo por debajo del anillo', 'tipo de velo',
       'color del velo', 'número de anillos', 'tipo de anillo',
       'color de la impresión de esporas', 'población', 'hábitat'],
      dtype='object')
  clase forma del sombrero superficie del sombrero color del sombrero  \
0     p                  x                       s                  n
1     e                  x                       s                  y
2     e                  b                       s                  w
3     p                  x                       y                  w
4     e                  x                       s                  g

  magulladuras olor unión de las láminas espaciamiento de las láminas  \
0            t    p                    f                            c
1            t    a                    f                            c
2            t    l                    f                            c
3            t    p                    f                            c
4            f    n                    f                            w

  tamaño de las láminas color de las láminas  ...  \
0                     n                    k  ...
1                     b                    k  ...
2                     b                    n  ...
3                     n                    n  ...
4                     b                    k  ...

  superficie del tallo por debajo del anillo  \
0                                          s
1                                          s
2                                          s
3                                          s
4                                          s

  color del tallo por encima del anillo color del tallo por debajo del anillo  \
0                                     w                                     w
1                                     w                                     w
2                                     w                                     w
3                                     w                                     w
4                                     w                                     w

  tipo de velo color del velo número de anillos tipo de anillo  \
0            p              w                 o              p
1            p              w                 o              p
2            p              w                 o              p
3            p              w                 o              p
4            p              w                 o              e

  color de la impresión de esporas población hábitat
0                                k         s       u
1                                n         n       g
2                                n         n       m
3                                k         s       u
4                                n         a       g

[5 rows x 23 columns]

¿Cuál es el objetivo que planteamos? ¿Qué valos nos interesa encontrar?

En este caso, según las características del hongo determinaremos su población: - population: abundant=a, clustered=c, numerous=n, scattered=s, several=v, solitary=y

[13]:
print(df["población"].head())
value,counts = np.unique(df["población"], return_counts=True)
print(value,counts)

0    s
1    n
2    n
3    s
4    a
Name: población, dtype: object
['a' 'c' 'n' 's' 'v' 'y'] [ 384  340  400 1248 4040 1712]

¿Qué tipo de poblema tenemos? - ¿Supervisado o No Supervisado? - ¿Clasificación, Regresión, Agrupamiento ?

Para la selección de características (feature selection) usaremos todas las muestras

[14]:
df_y = df["población"].copy()
df_x = df.drop(labels=["población"],axis=1).copy()

# 4º Data splitting. Crear modelo de testing.
# Datos de entrenamiento (train) y datos de comprobación (test)
# https://scikit-learn.org/stable/modules/classes.html#module-sklearn.model_selection

from sklearn.model_selection import train_test_split

x_train, x_test,  y_train, y_test = train_test_split(df_x,df_y, test_size=0.2, random_state = 0)
print("x_train:", x_train.shape)
print("x_test: ",x_test.shape)
print("y_train: ",y_train.shape)

x_train: (6499, 22)
x_test:  (1625, 22)
y_train:  (6499,)
[ ]:
# 5º Tipo de algoritmo - elección del algoritmo:
#
# Quizás podriamos usar... un ejemplo Máquinas de Vectores de Soporte (SVM)
# https://scikit-learn.org/stable/modules/svm.html
# https://scikit-learn.org/stable/modules/generated/sklearn.svm.SVC.html
#
from sklearn.svm import SVC

clf = SVC(C=1.0, random_state=0)
clf.fit(x_train, y_train)

# Alerta: Genera un error!!!

[18]:
# El error proviene porque los SVM tan solo funcionan con variables continuas
# entonces, podemos transformar variables categoricas a discrete integer values !

from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
for i in df.columns:
    df[i] = le.fit_transform(df[i])

print(df.head(2))
### ES UN PROCESO ITERATIVO.
### Dado nuestro primer error con la elección/preparación de datos, hemos de volver a preparar los datos
### Lo hacemos más rapido está vez.
x_train, x_test,  y_train, y_test  = train_test_split(df.drop('población', axis=1),df['población'], test_size=0.2,random_state=0)
   clase  forma del sombrero  superficie del sombrero  color del sombrero  \
0      1                   5                        2                   4
1      0                   5                        2                   9

   magulladuras  olor  unión de las láminas  espaciamiento de las láminas  \
0             1     6                     1                             0
1             1     0                     1                             0

   tamaño de las láminas  color de las láminas  ...  \
0                      1                     4  ...
1                      0                     4  ...

   superficie del tallo por debajo del anillo  \
0                                           2
1                                           2

   color del tallo por encima del anillo  \
0                                      7
1                                      7

   color del tallo por debajo del anillo  tipo de velo  color del velo  \
0                                      7             0               2
1                                      7             0               2

   número de anillos  tipo de anillo  color de la impresión de esporas  \
0                  1               4                                 2
1                  1               4                                 3

   población  hábitat
0          3        5
1          2        1

[2 rows x 23 columns]
[21]:
clf = SVC(C=1.0, kernel="linear", random_state=0) #¿Que implica el kernel?
clf.fit(x_train, y_train)
[21]:
SVC(kernel='linear', random_state=0)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
[22]:
y_pred = clf.predict(x_test) #predecimos sobre los valores reservados
[23]:
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred)) #comprobamos el error
              precision    recall  f1-score   support

           0       0.44      0.58      0.50        65
           1       0.67      1.00      0.81        62
           2       0.48      0.33      0.39        93
           3       0.45      0.42      0.44       237
           4       0.79      0.63      0.70       827
           5       0.48      0.70      0.57       341

    accuracy                           0.61      1625
   macro avg       0.55      0.61      0.57      1625
weighted avg       0.64      0.61      0.62      1625

Entropía

¿Qué hubierá ocurrido con el ejemplo anterior si hubieramos descartado alguna característica?

En el campo de la Teoria de la Información, Shannon en 1948 definió dos conceptos importantes information gain (IG) y entropy.

La entropia es un indicador sobre el “desorden”: $ H = - :nbsphinx-math:`sum`^{W}_{i=1}p_i * log(p_i)$

\(p(write-off)= 7/12= 0.583\) \(p(non-write-off)= 5/12 = 0.416\)

$ H = - [ 7/12 * log(7/12) + 5/12 * log(5/12)] = 0.6791 $

La ganancia de información de un atributo con respecto a la variable objetivo representa cuanto aporta dicha variable para mejorar (reducir) la entropía debido al aumento de información.

$ IG(parent, children) = entropy(parent) - [ p(c_1)entropy(c_1)+ p(c_2)entropy(c_2)+…]$

Veamos un caso:

467df08d1c8a4bf08e875110c308a5e8

Fuente de la imagen [2]

La entropia de toda la población con respecto a \(\bullet\) y \(\star\) es:

$ entropy(parent) = - [ p(\bullet)log(:nbsphinx-math:`bullet`) + p(:nbsphinx-math:`star`)log(\star)] $ $ entropy(parent) = - [ 0.53-0.9 + 0.47-1.1] = 0.99 $
Nota: un valor muy alto, muy impuro.

Si seleccionamos el atributo de balance con un criterio de \(50k\) dispondremos de dos poblaciones nuevas con entropia diferentes.

$ entropy(balance < 50k) = - [ p(\bullet)log(:nbsphinx-math:`bullet`) + p(:nbsphinx-math:`star`)log(\star)] $ $ entropy(balance < 50k) = - [ 0.92 * -0.12 + 0.08*-3.7] = 0.39 $

$ entropy(balance \geq `50k) = - [ p(:nbsphinx-math:bullet`)log(:nbsphinx-math:`bullet`) + p(:nbsphinx-math:`star`)log(\star)] $ $ entropy(balance :nbsphinx-math:`geq `50k) = - [ 0.24 * -2.1 + 0.76*-0.39] = 0.79 $

\(IG = entropy(parent) - [ p(balance < 50k)*entropy(balance < 50k)+ p(balance \geq 50k)*entropy(balance \geq 50k) ]\) \(IG = entropy(parent) - [ 13/30 *entropy(balance < 50k)+ 17/30*entropy(balance \geq 50k) ]\)

$ IG = 0.99 - [0.43 * 0.39 + 0.57 * 0.79] = 0.37 $

[24]:
##  Ganancia y Entropía de Información con Python
from math import log, e
import pandas as pd
import numpy as np

def entropy(serie):
  value,counts = np.unique(serie, return_counts=True)
  norm_counts = counts / counts.sum()
  return -np.sum(norm_counts * np.log(norm_counts))
[25]:

df = pd.read_csv("data/mushrooms.csv") print("Attribute: class " , entropy(df["class"])) print("Attribute: cap-surface " , entropy(df["cap-surface"])) print("Attribute: gill-size " , entropy(df["gill-size"]))
Attribute: class  0.6925010959051001
Attribute: cap-surface  1.0920439563177977
Attribute: gill-size  0.6184649299084096

Donde más fácil resulta calcular la entropia es en árboles de decisión, por lo que se puede ver en la firma/signature de algunas funciones de scikit.

https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html

criterion{“gini”, “entropy”, “log_loss”}, default=”gini”
The function to measure the quality of a split. Supported criteria are “gini” for the Gini impurity and “log_loss” and “entropy” both for the Shannon information gain, see Mathematical formulation.
model = sklearn.tree.DecisionTreeClassifier(criterion='entropy')

Nota Profundizaremos este punto a medida que vayamos introduciendo algoritmos de ML con Python

La evaluación de un modelo de entrenamiento

La evaluación tiene como propósito dar validez y obtener confianza sobre el modelo propuesto. Dos perspectivas: Cualitativo y Cuantitativo. - Cualitativamente. ¿El modelo alcanza los objetivos de negocio, ayudando a la toma de decisiones? ¿Es posible integrarlo? ¿Se puede adaptar al stack tecnológico donde ha de aplicarse?, etc. - Cuantitativamente. Existen métricas sobre el rendimiento del consumo de recursos y el desempeño del modelo sobre el error de los resultados.

Métricas

Las principales métricas depende del modelo. Generalmente, en regresión y clasificación las métricas son.

En Clasificación:

e6881cc6ac2449d883f0cb39bc7cbdea

Source: NillsF blog

  • Accuracy: número de predicciones correctas como ratio de todas las predicciones hechas $ \frac{TP+TN}{Total}.$

  • Precision: porcentaje de resultados positivos correctos sobre el total de resultados positivos \(\frac{TP}{Results} = \frac{TP}{TP+FP}\)

  • Recall: \(\frac{TP}{Predictive Results} = \frac{TP}{TP+FN}\)

  • f1-score: es la media harmónica de la precisión y el recall: \(\frac{2}{recall^{-1}+precision^{-1}}\)

  • Area under curve (AUC): para problemas de clasificación binaria. La Receiver Operating Characteristic (ROC) curva de probabilidad y AUC representa el grado o medida de separación. Expresa el grado del modelo para distingir ambas clases.

  • Confusion matrix: esta compuesta por la matriz de valores actuales x los valores de predicción .

    \[\begin{split}\begin{equation} \begin{pmatrix} TP & FN \\ FP & TN \end{pmatrix} \end{equation}\end{split}\]

En Regresión: - Mean absolute error (MAE) es la suma de las diferencias absolutas entre las predicciones y el valor actual. - Mean squared error (MSE) representa la desviación estándard de las diferencias entre los valores de predicción y los valores observados. - R squared (:math:`R^2`) es un indicador de la bondad del entreno de la predicción al actual valor. - Adjusted R squared (Adj-:math:`R^2`) muestra como se ajusta la curva o linea de \(R^2\) para los valores del modelo.

Éstas y muchas más métricas están implementadas en la librería de Sci-kit - https://scikit-learn.org/stable/modules/classes.html#module-sklearn.metrics - https://scikit-learn.org/stable/modules/model_evaluation.html

Nota: Iremos viendo estas métricas a lo largo de las unidades siguientes

[26]:
from sklearn.metrics import accuracy_score

accuracy_score(y_test, y_pred)
[26]:
0.6123076923076923
[27]:
from sklearn import metrics

metrics.precision_score(y_test,y_pred,average="weighted")
[27]:
0.6413835956221657
[28]:
print("Recall:",metrics.recall_score(y_test,y_pred,average="weighted"))
print("F1_score:",metrics.f1_score(y_test,y_pred,average="weighted"))
Recall: 0.6123076923076923
F1_score: 0.6153073145466426

Rendimiento Computacional

Cada operación tiene un coste computacional. No sólo es tiempo sino memoria para almacenar las variables y valores intermedios. Las CPU/GPU tienen una capacidad computacional que suele medirse en Operaciones por Segundo. - MIPS - MFLOPS / GFLOPS

Ese ratio influye en el tiempo de ejecución de los modelos.

El lenguaje de programación y su compilador ayudan a obtener el mejor uso de la CPU/GPU. Son muchas las decisiones que se toman en la ejecución de un script. ¿Cuántos cores dispone una CPU/GPU para realizar la tarea? ¿Organización de las tareas y de las variables en memoría?, etc.

Librerías como Tensorflow[https://www.tensorflow.org/?hl=es-419] tienen funcionalidades que explotan los recursos que nos ofrecen las GPU en la computación paralela de operaciones.

gpus = tf.config.list_physical_devices('GPU')
if gpus:
  # Restrict TensorFlow to only allocate 1GB of memory on the first GPU
  try:
    tf.config.set_logical_device_configuration(
        gpus[0],
        [tf.config.LogicalDeviceConfiguration(memory_limit=1024)])
    logical_gpus = tf.config.list_logical_devices('GPU')
    print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
  except RuntimeError as e:
    # Virtual devices must be set before GPUs have been initialized
    print(e)

En general, hay dos modelos de computación para optimizar la ejecución de análisis: el uso de sistemas distribuidos (i.e. Cloud Computing) y la programación paralela.

En cualquier caso, la complejidad del modelo y la influencia de los datos (y su representación) tienen consecuencias sobre el rendimiento computacional y, por ende, en su aplicabilidad en un entorno real de explotación donde el tiempo de respuesta hacia el usuario sea razonable. Por ejemplo, un sistema de recomendación de productos que han de cargarse dinámicamente en una página web (i.e. Amazon)

https://scikit-learn.org/0.15/modules/computational_performance.html

License: CC BY 4.0 Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu