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.
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.
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:
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.
Scikit-learn: https://scikit-learn.org/stable/index.html
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:
Mushroom Data Set http://archive.ics.uci.edu/ml/datasets/Mushroom
También disponible en: data/mushrooms.csv
[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.
SVC(kernel='linear', random_state=0)
[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:
Fuente de la imagen [2]
La entropia de toda la población con respecto a \(\bullet\) y \(\star\) es:
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:
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
Isaac Lera and Gabriel Moya Universitat de les Illes Balears isaac.lera@uib.edu, gabriel.moya@uib.edu