FastAI Machine Learning (Lección 3)

Christian Tutivén Gálvez
Saturdays.AI
Published in
8 min readFeb 21, 2019

En las lecciones anteriores pudimos construir un modelo que es muy bueno prediciendo precios (usando la técnica Random Forest), pero algo más importante que haber construido este modelo es el saber interpretarlo para así entender que nos dice acerca de nuestros datos, es decir entender mejor los datos usando machine learning. Con esta lección se pretende demostrar que técnicas como Random Forest no son cajas negras y que de hecho la verdad es que Random Forest nos permite entender de manera más profunda nuestros datos y más rápida que los métodos tradicionales.

Obteniendo los datos

Esta lección la ejecutaremos en Google Colab por lo debemos instalar las distintas librerías que usaremos y descargar los set de datos que usaremos.

print (“ Installing FastAI libraries…”)
!pip install fastai==0.7.0 > /dev/null
print (“\n Installing required libraries…”)
!pip install feather > /dev/null
!pip install scikit-misc==0.1.0 > /dev/null
!pip install pdpbox==0.2.0 > /dev/null
!pip install treeinterpreter==0.2.2 > /dev/null
print (“\n Downloading train dataset…”)
!wget https://raw.githubusercontent.com/Giffy/Personal_dataset_repository/master/train.tar.gz
!tar xvf train.tar.gz > /dev/null
print (“\n Downloading dataframe processed …”)
!wget https://raw.githubusercontent.com/Giffy/Personal_dataset_repository/master/df_raw.tar.gz
!tar xvf df_raw.tar.gz > /dev/null

Importamos las librerías

%matplotlib inlinefrom fastai.imports import *
from fastai.structured import *
from pandas_summary import DataFrameSummary
from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier
from IPython.display import display
from sklearn import metrics

Cargamos automáticamente los módulos antes de ingresar a la ejecución

%load_ext autoreload
%autoreload 2

Cargamos nuestro archivo guardado de la lección anterior y ejecutamos proc_df, para convertir todo en enteros, cambiar los missing values por el valor medio, etc.

PATH = “/content/data/bulldozers/” import feather # Import feather library
df_raw=feather.read_dataframe(‘tmp/bulldozers-raw’)
df_trn, y_trn, nas = proc_df(df_raw, ‘SalePrice’)

Creamos nuestro set de validación

def split_vals(a,n): return a[:n], a[n:]
n_valid = 12000
n_trn = len(df_trn)-n_valid
X_train, X_valid = split_vals(df_trn, n_trn)
y_train, y_valid = split_vals(y_trn, n_trn)
raw_train, raw_valid = split_vals(df_raw, n_trn)

Creamos nuestra función para poder trabajar con la métrica RMSLE

def rmse(x,y): return math.sqrt(((x-y)**2).mean())def print_score(m):
res = [rmse(m.predict(X_train), y_train), rmse(m.predict(X_valid), y_valid),
m.score(X_train, y_train), m.score(X_valid, y_valid)]
if hasattr(m, ‘oob_score_’): res.append(m.oob_score_)
print(res)

Ahora sabemos como predecir un valor de una fila pero a veces lo que deseamos saber es que tan confiable es la predicción de esa fila. Qué tal si en lugar de tomar la media de cada uno de los arboles tomamos la desviación estándar de las predicciones de los arboles?. Si la desviación estándar de la predicción de los arboles es alta significa que cada árbol nos esta dando estimaciones muy distintas de la predicción de esa fila. Es decir, la desviación estándar nos indica que tan confiable es una predicción.

Seleccionamos 50000 ejemplos para entrenar. Se define este valor porque no queremos un modelo tan exacto en este caso, sino que deseamos entender la naturaleza de las relaciones y esta cantidad es suficiente alta para interpretar el modelo (si ejecuto esta línea varias veces me dan resultados similares).

set_rf_samples(50000)

Ajusto los datos al modelo de Random Forest

m = RandomForestRegressor(n_estimators=40, min_samples_leaf=3, max_features=0.5, n_jobs=-1, oob_score=True)
m.fit(X_train, y_train)
print_score(m)

Creamos una lista de todas las predicciones de cada uno de los arboles. Obtendremos predicciones de cada uno de los 40 árboles. Además, np.stack se utilizará para concatenar las predicciones una sobre la otra. Y calculamos la media del primer ejemplo y su desviación estándar. Hacemos predicciones en paralelo.

def get_preds(t): return t.predict(X_valid)
%time preds = np.stack(parallel_trees(m, get_preds))
np.mean(preds[:,0]), np.std(preds[:,0])

Podemos ver que diferentes árboles están dando diferentes estimaciones en esta subasta. Para ver cómo varía la confianza en la predicción, podemos agregar esto a nuestro conjunto de datos.

Creamos una copia de nuestros datos y creamos una columna adicional con la desviación estándar de las predicciones y además creamos una columna con el valor promedio.

Ahora, tomemos una variable del conjunto de datos y visualicemos su distribución y entendamos lo que realmente representa. Comenzaremos con la variable Enclosure .

1- Averiguamos el valor del conteo de cada categoría presente en la variable Enclosure:

x = raw_valid.copy()
x[‘pred_std’] = np.std(preds, axis=0)
x[‘pred’] = np.mean(preds, axis=0)
x.Enclosure.value_counts().plot.barh();

Viendo el gráfico podemos decidir descartar las tres primeras categorías.

2- Para cada categoría, a continuación se muestran los valores medios de saleprice , predicción y desviación estándar.

flds = [‘Enclosure’, ‘SalePrice’, ‘pred’, ‘pred_std’]
enc_summ = x[flds].groupby(‘Enclosure’, as_index=False).mean()
enc_summ

El precio de venta real y los valores de predicción son casi similares en tres categorías: ‘EROPS’, ‘EROPS w AC’, ‘OROPS’ (el resto tiene valores nulos). Dado que estas columnas de valor nulo no agregan ninguna información adicional, las eliminaremos y visualizaremos los gráficos para el precio de venta y la predicción:

enc_summ = enc_summ[~pd.isnull(enc_summ.SalePrice)]
enc_summ.plot(‘Enclosure’, ‘SalePrice’, ‘barh’, xlim=(0,11));
enc_summ.plot(‘Enclosure’, ‘pred’, ‘barh’, xerr=’pred_std’, alpha=0.6, xlim=(0,11));

Tenga en cuenta que las barras negras pequeñas representan la desviación estándar. De la misma manera, veamos otra variable: ProductSize .

raw_valid.ProductSize.value_counts().plot.barh();
#categoría significa el precio de venta, la predicción y la desviación estándar
flds = ['ProductSize', 'SalePrice', 'pred', 'pred_std']
summ = x[flds].groupby(flds[0]).mean()
summ

Tomaremos una proporción de los valores de desviación estándar y la suma de las predicciones para comparar qué categoría tiene una desviación más alta.

(summ.pred_std/summ.pred).sort_values(ascending=False)

La desviación estándar es mayor para las categorías ‘Grande’ y ‘Compacto’. ¿Por qué haces eso? Tómese un momento para reflexionar sobre la respuesta antes de seguir leyendo.

Eche un vistazo a la gráfica de barras de valores para cada categoría en ProductSize. ¿Encontraste la razón? Tenemos un número menor de filas para estas dos categorías. Por lo tanto, el modelo está dando una precisión de predicción relativamente pobre para estas variables.

Con esta información, podemos decir que confiamos más en las predicciones para el tamaño de producto mini, mediano y mediano / grande y menos confiados en los pequeños, compactos y grandes.

Importancia de la característica

La importancia de las características es uno de los aspectos clave de un modelo de aprendizaje automático. Entender qué variable es la que más contribuye a un modelo es fundamental para interpretar los resultados. Esto es lo que luchan los científicos de datos cuando construyen modelos que necesitan ser explicados a partes interesadas no técnicas.

Nuestro conjunto de datos tiene múltiples características y, a menudo, es difícil entender qué característica es dominante. Aquí es donde la función de importancia de la característica del bosque aleatorio es tan útil. Veamos las 10 características más importantes de nuestro modelo actual (incluida la visualización por su importancia):

fi = rf_feat_importance(m, df_trn); fi[:10]
fi.plot(‘cols’, ‘imp’, figsize=(10,6), legend=False);

Esto nos dice que hay ciertas columnas que son realmente importantes y la mayoría en realidad no nos interesan.

Esa es una trama bastante intuitiva. Aquí hay una visualización de diagrama de barras de las 30 características principales:

Claramente, YearMade es la característica más importante, seguida por Coupler_System. La mayoría de las características parece tener poca importancia en el modelo final. Verifiquemos esta declaración eliminando estas características y verificando si esto afecta el rendimiento del modelo.

Por lo tanto, construiremos un modelo de bosque aleatorio usando solo las características que tienen una importancia de característica mayor que 0.005:

to_keep = fi[fi.imp>0.005].cols; len(to_keep)24

Tenemos 24 características con una importancia mayor a 0.005. Creamos una copia del dataframe original solo con estas características.

df_keep = df_trn[to_keep].copy()
X_train, X_valid = split_vals(df_keep, n_trn)

Ajustamos los nuevos datos a nuestro modelo

m = RandomForestRegressor(n_estimators=40, min_samples_leaf=3, max_features=0.5,
n_jobs=-1, oob_score=True)
m.fit(X_train, y_train)
print_score(m)

Cuando piensas en ello, eliminar columnas redundantes no debería disminuir la puntuación del modelo, ¿verdad? Y en este caso, el rendimiento del modelo ha mejorado ligeramente. Algunas de las características que descartamos anteriormente podrían haber sido altamente colineales con otras, por lo que eliminarlas no afectó negativamente al modelo. Revisemos la importancia de la característica nuevamente para verificar nuestra hipótesis:

fi = rf_feat_importance(m, df_keep)
plot_fi(fi);

La diferencia entre la importancia de la característica de las variables YearMade y C oupler_System es más significativa. De la lista de características eliminadas, algunas características fueron altamente colineales a YearMade, lo que resultó en la distribución de la importancia de las características entre ellas.

Al eliminar estas características, podemos ver que la diferencia entre la importancia de YearMade y CouplerSystem ha aumentado con respecto al gráfico anterior. Aquí hay una explicación detallada de cómo se calcula la importancia de la característica:

  • Calcula R² considerando todas las columnas: suponga que en este caso resulta ser 0,89
  • Ahora mezcla aleatoriamente los valores para cualquier columna , digamos YearMade. Esta columna no tiene relación con la variable objetivo.
  • Calcula de nuevo R²: R² ha bajado a 0,8. Esto muestra que la variable YearMade es una característica importante
  • Toma otra variable , por ejemplo Enclosure, y baraja aleatoriamente
  • Calcula R²: Ahora digamos que llegará a ser 0.84. Esto indica que la variable es importante pero comparativamente menos que la variable YearMade

¡Y eso concluye la implementación de la lección # 3! Los animo a probar estos códigos y experimentar con ellos en su propia máquina para comprender realmente cómo funciona cada aspecto de un modelo de bosque aleatorio.

--

--