import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import mean_squared_error
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import ConfusionMatrixDisplay
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
Sessió 3
Aprenentatge Automàtic
Els documents d’aquest curs contenen adaptacions de diferents materials que s’han anat utilitzant a altres cursos i conté aportacions de varis membres del Departament de Matemàtiques de la UAB.
El paquet Scikit-Learn
El paquet Scikit-Learn
té implementats molts mètodes per crear models d’aprenentatge automàtic. Un dels punts interessants de Scikit-Learn
és que els models es defineixen sempre amb la mateixa estructura, de forma que podem provar diferents tipus de models sobre un mateix conjunt de dades de forma molt àgil.
Per importar el paque Scikit-Learn
s’ha de fer amb el nom sklearn
, tot i que no s’acostuma a importar directament tot el paquet sino que s’importen només els mètodes que es volen fer servir.
Amb el paquet Scikit-Learn
podem reproduir el càlcul del model de regressió múltiple que vam implementar en la sessió anterior.
= np.array([[1, 1, 1], [4, 5, 6], [9, 8, 7], [0, 3, 3], [2, 1, 1], [6, 7, 8]])
X_reg = np.array([2, 3, 4, 5, 6, 7]) y_reg
= LinearRegression().fit(X_reg, y_reg) model
En Python
, si una variable acaba amb un guió baix (_
) vol dir que el programador considera que un usuari no hauria d’utilitzar aquesta variable directament. En els models de Scikit-Learn
, això és així perquè els models incorporen funcions que permeten fer prediccions directament sense consultar els valors dels paràmetres del model. De totes formes, podem accedir per comprovar que obtenim els mateixos valors que vam obtenir quan vam definir el model lineal manualment.
model.coef_
model.intercept_
Per fer noves prediccions directament amb Scikit-Learn
ho podem fer defininint un array
de Numpy
amb les dimensions adequades (tantes files com registres tinguem i tantes columnes com variables).
1, 2, 3]).reshape(1, -1)) model.predict(np.array([
= model.predict(X_reg) preds_reg
El paquet Scikit-Learn
incorpora també diverses funcions per calcular mètriques amb les que podem avaluar el rendiment dels models.
= mean_squared_error(y_reg, preds_reg)
mse mse
Els models lineals que hem treballat fins ara són models de regressió, però amb Scikit-Learn
també podem treballar amb problemes de classificació. Veiem exemples de problemes de classificació amb les dades del conjunt MNIST.
= load_digits(as_frame=True) data
= data.frame df
df
df.describe()
= plt.subplots(2, 5, figsize=(24, 9))
fig, ax
for i in range(10):
// 5, i % 5].imshow(df.iloc[i, :64].values.reshape(8, 8), cmap="gray")
ax[i // 5, i % 5].set_title("{}".format(df.iloc[i]["target"]))
ax[i // 5, i % 5].axis("off")
ax[i
plt.show()
En la majoria de models de machine learning és important normalitzar els valors de les variables. Pel cas del dataset dels dígits, normalitzar les variables entre \(0\) i \(1\) és equivalent a dividir el valor de cada píxel per \(16\).
for c in df.columns:
if c != "target":
= df[c] / 16 df[c]
Hem de definir llavors quines són les variables explicatives i quina és la variable que volem predir. En el nostre cas, la variable a predir correspon amb la columna target
del DataFrame
, mentre que la resta de columnes, que fan referència als valors dels píxels, són variables explicatives.
= [c for c in df.columns if "target" not in c]
variables_explicatives = "target" variable_target
Per comoditat, definim ara dues variables, X
i y
, on guardarem per separat la informació de les variables explicatives i la variable dependent.
= df[variables_explicatives]
X = df[variable_target] y
Els mètodes que fa servir Scikit-Learn
per definir i utilitzar els models segueix una estructura molt marcada. A continuació veiem com podem obtenir les prediccions d’un model de Random Forest
fent exactament la mateixa crida que amb la regressió lineal, però canviant les dades d’entrada.
Cada tipus de model en el paquet Scikit-Learn
pot rebre una sèrie de hiperparàmetres diferents. En cada cas, al especificar el nom del model que volem fer servir podem especificar els valors dels hiperparèmtres que considerem adequats (la resta d’hiperparàmetres no especificats tindran un valor per defecte, que es pot consultar a la documentació de Scikit-Learn
).
= RandomForestClassifier(max_depth=10, n_estimators=10).fit(X, y) model
= model.predict(X) preds
En un model de classificació les mètriques que s’acostumen a utilitzar per mesurar el rendiment del model tenen a veure amb l’encert del model.
La mètrica més bàsica que podem utilitzar és l’accuracy
, que compta el percentatge de casos on el model ha encertat la seva predicció.
accuracy_score(y, preds)
El propi paquet Scikit-Learn
conté també funcions per visualitzar diferents mètriques. A continuació veiem que es pot mostrar la matriu de confusió d’un model amb molt poques línies de codi.
= confusion_matrix(y, preds)
cm = ConfusionMatrixDisplay(confusion_matrix=cm)
disp disp.plot()
Aquest problema particular de predir el número present en una imatge sempre es considera un problema de classificació, però podem veure com es resoldria considerant-lo un problema de regressió, per mostrar que l’estructura del codi és molt semblant.
= RandomForestRegressor(max_depth=10, n_estimators=10).fit(X_train, y_train) model
= model.predict(X_train) preds_train
= model.predict(X_test) preds_test
Al tractar-ho com a problema de regressió, les prediccions del model no tenen perquè ser números enters. Això fa que mesurar l’accuracy
no tingui sentit. Per mesurar el rendiment del model hem d’utilitzar una mètrica de regressió, com el Mean Squared Error
.
= mean_squared_error(y_test, preds_test)
mse mse
En l’àmbit de l’aprenentatge automàtic l’objectiu principal acostuma a ser obtenir el millor model possible, en termes de capacitat de predicció i generalització. De totes formes, és molt habitual també necessitar entendre com el model fa les seves prediccions. La majoria de models d’aprenentatge automàtic són massa complexos com per poder analitzar directament com es fan les prediccions, però es poden aplicar tècniques per intentar entendre en quines variables es fixa més el model.
En el cas del Random Forest
, es pot obtenir un valor d’importància per a cada variable d’entrada al model. Com que en el nostre cas cada variable representa un píxel de la imatge, podem representar la importància associada a cada píxel també en una imatge, de forma que podem representar visualment en quines zones de la imatge el model posa més atenció a l’hora de fer les prediccions.
= RandomForestClassifier(max_depth=10, n_estimators=10).fit(X_train, y_train) model
8, 8), cmap="gray")
plt.imshow(model.feature_importances_.reshape( plt.show()
La següent funció ens permet seleccionar només aquelles imatges que pertanyen a un subconjunt de les categories. Amb aquesta funció podem generar un nou DataFrame
on les files siguin imatges que contenen només els digits que haguem seleccionat.
def filter_dataset(values):
return df.loc[df["target"].isin(values)]
= filter_dataset([0, 8]) df_2
El paquet Scikit-Learn
conté també mètodes d’aprenentatge automàtic no supervisats. És a dir, mètodes on l’objectiu no és fer una predicció, si no obtenir informació sobre la distribució de les nostres dades.
Un dels mètodes més coneguts d’aprenentatge no supervisat és el PCA
. A continuació veiem com utilitzar Scikit-Learn
per calcular la projecció de les imatges a dues dimensions utilitzant el mètode PCA
.
= df[variables_explicatives]
X = df[variable_target] y
= PCA(n_components=2).fit(X) pca
pca.explained_variance_ratio_
= pca.transform(X) X_pca
for n in range(0, 10):
= np.where(y == n)[0]
idx 0], X_pca[idx, 1], label="{}".format(n))
plt.scatter(X_pca[idx,
plt.legend() plt.show()