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 un mapa que nos posibilita posicionar lugares de interes, calcular distancias entre ellos, o encontrar relaciones topológicas.

3cda3c961a9747cea4abf44f45b11e99

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.

5befdef3d1784684b18dd1c224593f01 503bcfee71454ecd826a7fd21520b181

Fuente de ambas figuras [2]

El proceso de aprendizaje

El proceso de aprendizaje automático suele constar de una serie de pasos. Por lo general, estos son: - Preparación de datos. Carga y limpieza de datos. - Selección de atributos o métricas adecuadas. - Selección de la técnica a aplicar. - Ajuste de los hiperparámetros. - Evaluación del modelo.

Selección de Atributos

Dado un conjunto amplio de atributos que caracterizan los eventos a representar, el problema es elegir aquellos que puedan contribuir al modelo de aprendizaje.

En el ejemplo de las personas, los argumentos que podríamos tener son: - Forma de la cabeza: cuadrada o circular. - Forma del cuerpo: rectangular o óvalo. - Color del cuerpo: negro o blanco.

Si tuvieramos que segmentar dicha población deberíamos de plantear que atributos de estas personas nos permitiran identificar, por ejemplo, si cancelerán su compra (write-offs a yes/no). Este atributo es la variable objetivo: target variable. El diseño del modelo ha de contener una homogeneidad con respecto a esta variable. No puede existir un individuo que esté en ambos targets. Estaríamos tratando otro problema.

En este problema de segmentación, el objetivo sería encontrar una o varias variables que defininan puramente un grupo. Rara vez suele pasar. Por ejemplo, el atributo head-shape no lo es. Si tan solo eligieramos el atributo body-color a negro, no tendríamos una representatividad del resto de la población. E incluso, hay que tratar con valores númericos de atributos continuos o discretos.

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:

90a0eb6881ff481d9f366606e8640e41

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 $

Ganancia y Entropía de Información con Python

Vamos a utilizar el siguiente catálogo:

¿Qué librerías de ML se usan habitualmente en Python ?

Instalamos dependencias:

pip install pandas
pip install numpy
pip install matplotlib
pip install seaborn
pip install sklearn
[16]:
import pandas as pd

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

es_col = ["clase", "forma de la caperuza", "superficie de la caperuza", "color de la caperuza", "magulladuras", "olor",
    "tamaño de las branquias", "color de las branquias",
    "forma del tallo", "raíz del tallo", "superficie del tallo por encima del anillo",
    "superficie peduncular por debajo del anillo", "color peduncular 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 impresión de las esporas", "población", "hábitat"]

(8124, 23)
Index(['class', 'cap-shape', 'cap-surface', 'cap-color', 'bruises', 'odor',
       'gill-attachment', 'gill-spacing', 'gill-size', 'gill-color',
       'stalk-shape', 'stalk-root', 'stalk-surface-above-ring',
       'stalk-surface-below-ring', 'stalk-color-above-ring',
       'stalk-color-below-ring', 'veil-type', 'veil-color', 'ring-number',
       'ring-type', 'spore-print-color', 'population', 'habitat'],
      dtype='object')
[17]:
from math import log, e
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))
[18]:
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

Un ejemplo de ML algorithm

Veremos los principales métodos de Scikit-Learn library que nos proporciona la estructura basica de un algoritmo de ML.

[19]:
# Algoritmo: clasificación de hongos según su población
# Método supervisado

# 1º Datos
import numpy as np
import pandas as pd
df = pd.read_csv("data/mushrooms.csv")
print(df.shape)

# ¿Cuál es el objetivo que planteamos?
# 2º Target value.
# 21. population: abundant=a,clustered=c,numerous=n, scattered=s,several=v,solitary=y

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

print(df.population.head())
value,counts = np.unique(df.population, return_counts=True)
print(value,counts)


df_y = df.population.copy()
df_x = df.drop(labels=["population"],axis=1)

# 3º Feature selection: Todos.

# 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)

(8124, 23)
0    s
1    n
2    n
3    s
4    a
Name: population, dtype: object
['a' 'c' 'n' 's' 'v' 'y'] [ 384  340  400 1248 4040 1712]
x_train: (6499, 22)
x_test:  (1625, 22)
y_train:  (6499,)
[20]:
# 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!
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/03_MachineLearningNotes/02_PredictiveModelling.ipynb Cell 10 line <cell line: 10>()
      <a href='vscode-notebook-cell:/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/03_MachineLearningNotes/02_PredictiveModelling.ipynb#X12sZmlsZQ%3D%3D?line=6'>7</a> from sklearn.svm import SVC
      <a href='vscode-notebook-cell:/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/03_MachineLearningNotes/02_PredictiveModelling.ipynb#X12sZmlsZQ%3D%3D?line=8'>9</a> clf = SVC(C=1.0, random_state=0)
---> <a href='vscode-notebook-cell:/Users/isaac/Projects/TxADM_notebooks/notebooks/Part2/03_MachineLearningNotes/02_PredictiveModelling.ipynb#X12sZmlsZQ%3D%3D?line=9'>10</a> clf.fit(x_train, y_train)

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/sklearn/svm/_base.py:173, in BaseLibSVM.fit(self, X, y, sample_weight)
    171     check_consistent_length(X, y)
    172 else:
--> 173     X, y = self._validate_data(
    174         X,
    175         y,
    176         dtype=np.float64,
    177         order="C",
    178         accept_sparse="csr",
    179         accept_large_sparse=False,
    180     )
    182 y = self._validate_targets(y)
    184 sample_weight = np.asarray(
    185     [] if sample_weight is None else sample_weight, dtype=np.float64
    186 )

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/sklearn/base.py:596, in BaseEstimator._validate_data(self, X, y, reset, validate_separately, **check_params)
    594         y = check_array(y, input_name="y", **check_y_params)
    595     else:
--> 596         X, y = check_X_y(X, y, **check_params)
    597     out = X, y
    599 if not no_val_X and check_params.get("ensure_2d", True):

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/sklearn/utils/validation.py:1074, in check_X_y(X, y, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, multi_output, ensure_min_samples, ensure_min_features, y_numeric, estimator)
   1069         estimator_name = _check_estimator_name(estimator)
   1070     raise ValueError(
   1071         f"{estimator_name} requires y to be passed, but the target y is None"
   1072     )
-> 1074 X = check_array(
   1075     X,
   1076     accept_sparse=accept_sparse,
   1077     accept_large_sparse=accept_large_sparse,
   1078     dtype=dtype,
   1079     order=order,
   1080     copy=copy,
   1081     force_all_finite=force_all_finite,
   1082     ensure_2d=ensure_2d,
   1083     allow_nd=allow_nd,
   1084     ensure_min_samples=ensure_min_samples,
   1085     ensure_min_features=ensure_min_features,
   1086     estimator=estimator,
   1087     input_name="X",
   1088 )
   1090 y = _check_y(y, multi_output=multi_output, y_numeric=y_numeric, estimator=estimator)
   1092 check_consistent_length(X, y)

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/sklearn/utils/validation.py:856, in check_array(array, accept_sparse, accept_large_sparse, dtype, order, copy, force_all_finite, ensure_2d, allow_nd, ensure_min_samples, ensure_min_features, estimator, input_name)
    854         array = array.astype(dtype, casting="unsafe", copy=False)
    855     else:
--> 856         array = np.asarray(array, order=order, dtype=dtype)
    857 except ComplexWarning as complex_warning:
    858     raise ValueError(
    859         "Complex data not supported\n{}\n".format(array)
    860     ) from complex_warning

File ~/.pyenv/versions/3.9.7/envs/my397/lib/python3.9/site-packages/pandas/core/generic.py:2064, in NDFrame.__array__(self, dtype)
   2063 def __array__(self, dtype: npt.DTypeLike | None = None) -> np.ndarray:
-> 2064     return np.asarray(self._values, dtype=dtype)

ValueError: could not convert string to float: 'e'
[23]:
# 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())
   class  cap-shape  cap-surface  cap-color  bruises  odor  gill-attachment  \
0      1          5            2          4        1     6                1
1      0          5            2          9        1     0                1
2      0          0            2          8        1     3                1
3      1          5            3          8        1     6                1
4      0          5            2          3        0     5                1

   gill-spacing  gill-size  gill-color  ...  stalk-surface-below-ring  \
0             0          1           4  ...                         2
1             0          0           4  ...                         2
2             0          0           5  ...                         2
3             0          1           5  ...                         2
4             1          0           4  ...                         2

   stalk-color-above-ring  stalk-color-below-ring  veil-type  veil-color  \
0                       7                       7          0           2
1                       7                       7          0           2
2                       7                       7          0           2
3                       7                       7          0           2
4                       7                       7          0           2

   ring-number  ring-type  spore-print-color  population  habitat
0            1          4                  2           3        5
1            1          4                  3           2        1
2            1          4                  3           2        3
3            1          4                  2           3        5
4            1          0                  3           0        1

[5 rows x 23 columns]
[24]:
x_train, x_test,  y_train, y_test  = train_test_split(df.drop('population', axis=1),df['population'], test_size=0.2,random_state=0)

[25]:

clf = SVC(C=1.0, kernel="" random_state=0) clf.fit(x_train, y_train)
[25]:
SVC(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.
[11]:
y_pred = clf.predict(x_test)
[12]:
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
              precision    recall  f1-score   support

           0       0.47      1.00      0.64        65
           1       0.93      0.66      0.77        62
           2       0.17      0.01      0.02        93
           3       0.46      0.44      0.45       237
           4       0.69      0.83      0.76       827
           5       0.42      0.27      0.33       341

    accuracy                           0.61      1625
   macro avg       0.52      0.54      0.49      1625
weighted avg       0.57      0.61      0.58      1625

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:

c1264c5feae9441ca2a15a47b22f33d1

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

[13]:
from sklearn.metrics import accuracy_score


accuracy_score(y_test, y_pred)
[13]:
0.6092307692307692
[14]:
from sklearn import metrics

metrics.precision_score(y_test,y_pred,average="weighted")


[14]:
0.5725371139120128
[26]:
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.6092307692307692
F1_score: 0.5759066983339758

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