Artículo actualizado el 26 de Febrero 2020
Durante los últimos años he estado aprendiendo Inteligencia Artificial. Todo este tiempo, mi manera de trabajar ha sido buscar el código que necesito en el momento en que lo necesito, mientras iba haciendo mis proyectos de Data Science. Utilizando la famosa técnica de copy-paste y adaptando el código a las necesidades de mi proyecto.
Siempre he pensado que seria muy útil tener algún tipo de plantilla con todo el código necesario para llevar a cabo un proyecto básico de Data Science de principio a fin. Así que creé una.
En este artículo voy a mostrar mi plantilla de Data Science. Es un archivo de Python con todo el código necesario para un proyecto de Data Science, estructurado de una manera que hace que sea muy fácil de seguir.
Comencemos por el final. Puedes encontrar esta plantilla en mi Github:albertsl/toolkit
Ahora que tienes fácil acceso al código, explicaré cómo está estructurado. Ten en cuenta que seguiré actualizando esta plantilla en Github, pero no actualizaré este artículo muy a menudo, algunas partes de lo que escribo aquí pueden quedar obsoletas.
En primer lugar, seguí la estructura de un proyecto de ciencia de datos que puedes encontrar en el Apéndice B del libro Hands-on Machine Learning with Scikit-Learn and TensorFlow de Aurelien Geron
Tras crear un archivo vacío, siguiendo la estructura descrita en el libro y agregando la mayor parte del texto del Apéndice B como comentarios en el código para darle estructura, empecé a rellenar cada parte del documento con fragmentos de código relevantes. Los fragmentos provienen de muchas fuentes diferentes, del código que escribí para competiciones en las que participé en Kaggle, de otros proyectos, de amigos, de ejemplos en Internet, de libros, etc.
Mientras la creaba, estaba participando en la competición de Kaggle CareerCon 2019 — Help Navigate Robots de la que también escribí un artículo explicando el resultado. Al hacer mis primeras pruebas, decidí seguir la estrategia de fast.ai, lanzar un modelo lo más rápido posible y obtener una métrica de referencia. Haciendo eso, probé el modelo Random Forest y obtuve una accuracy del 39%. Entonces empecé a seguir esta plantilla y llegué al 65% !!!
Vamos a profundizar en el código.
Se pretende que sea un resumen de la estructura, comentando las partes más importantes, solo incluiré en el artículo fragmentos del código para que te sitúes. Si quieres ver el código completo, dirígete a mi Github.
Como siempre, comenzaremos con los imports necesarios:
import numpy as np import pandas as pd pd.set_option('display.max_rows', 500) pd.set_option('display.max_columns', 500) pd.set_option('display.width', 1000) import matplotlib.pyplot as plt import seaborn as sns from tqdm import tqdm #from tqdm import tqdm_notebook as tqdm sns.set() import numba #También me gusta iniciar el código mostrando las versiones de las librerías con las que trabajo import platform print("Operating system:", platform.system(), platform.release()) import sys print("Python version:", sys.version) print("Numpy version:", np.version.version) print("Pandas version:", pd.__version__) print("Seaborn version:", sns.__version__) print("Numba version:", numba.__version__)
La mayoría son las típicas librerías para Data Science. Los que vale la pena mencionar son seaborn, una biblioteca de visualización de datos que funciona sobre matplotlib, agregando funcionalidad adicional, diferentes tipos de gráficos y visuales en general más bonitos. Puedes ver más sobre librerías de visualización en este artículo. También vale la pena destacar tqdm, una biblioteca que genera barras de progreso para que puedas ver cuánto tardan en ejecutarse tus funciones.
Seguimos cargando nuestros datos. Puede haber muchas variaciones en este procedimiento dependiendo de cómo estén estructurados los datos, en qué tipo de archivo,… No vamos a cubrir esto aquí, solo el ejemplo más básico:
df = pd.read_csv(‘file.csv’)
Ahora visualizamos nuestros datos para poder ver rápidamente lo que tenemos en nuestras manos:
#Visualize data df.head() df.describe() df.info() df.columns df.nunique() df.unique() #For a categorical dataset we want to see how many instances of each category there are df['categorical_var'].value_counts() #Automated data visualization from pandas_profiling import ProfileReport prof = ProfileReport(df) prof.to_file(output_file='output.html') #Exploratory Data Analysis (EDA) sns.pairplot(df, hue='categorical_var') sns.distplot(df['column']) sns.countplot(df['column'])
Data pre-processing
El primer paso tras cargar y visualizar los datos es el pre-processing. Básicamente les estaremos dando a los datos el formato adecuado para entrarlos en el modelo de Machine Learning.
Primero, revisemos los errores en nuestro dataset y hagamos las correcciones oportunas, verifiquemos si hay NaN, números infinitos, valores duplicados, etc.
#Fix or remove outliers plt.boxplot(df['feature1']) plt.boxplot(df['feature2']) plt.scatter('var1', 'y') #Do this for all variables against y #Check for missing data total_null = df.isna().sum().sort_values(ascending=False) percent = 100*(df.isna().sum()/df.isna().count()).sort_values(ascending=False) missing_data = pd.concat([total_null, percent], axis=1, keys=['Total', 'Percent']) #Generate new features with missing data nanf = ['feature1', 'feature2', 'feature3'] for feature in nanf: df[feature + '_nan'] = df[feature].isna() #Also look for infinite data, recommended to check it also after feature engineering df.replace(np.inf,0,inplace=True) df.replace(-np.inf,0,inplace=True) #Check for duplicated data df.duplicated().value_counts() df['duplicated'] = df.duplicated() #Create a new feature
Luego pasamos a una fase de feature engineering. No voy a copiar ningún código aquí porque esta sección será totalmente diferente para cada proyecto en el que trabajes, en la plantilla están todas las funciones para feature engineering que he usado en proyectos anteriores. Solo son algunos ejemplos de feature engineering, ya que la cantidad de transformaciones que se pueden realizar es casi infinita y variará dependiendo de su proyecto y los tipos de datos con que se trabaje.
Selección y evaluación del modelo
Tras el pre-procesado de los datos y teniendo los datos en el formato adecuado, podemos comenzar a trabajar con los modelos.
Primero debemos definir una estrategia de validación como K-Fold Cross Validation o dividir el dataset en training/validation. Dependiendo de tu dataset y de tus objetivos, puedes optar por una opción u otra. Aquí está el código para algunas estrategias de validación:
#Train and validation set split from sklearn.model_selection import train_test_split X = df.drop('target_var', axis=1) y = df['column to predict'] X_train, X_val, y_train, y_val = train_test_split(X, y, test_size = 0.4, stratify = y.values, random_state = 101) #Cross validation from sklearn.model_selection import cross_val_score cross_val_score(model, X, y, cv=5) #StratifiedKFold from sklearn.model_selection import StratifiedKFold skf = StratifiedKFold(n_splits=5, random_state=101) for train_index, val_index in skf.split(X, y): X_train, X_val = X[train_index], X[val_index] y_train, y_val = y[train_index], y[val_index]
Finalmente, pasamos a entrenar modelos de Machine Learning, podemos probar varios modelos diferentes y evaluar su desempeño comparándolos entre sí, y así podemos elegir los más prometedores. En la plantilla se muestran implementaciones de muchos algoritmos diferentes. No los voy a mostrar todos aquí ya que serían más de 100 líneas de código. Sin embargo, mostraré como ejemplo la implementación de Random Forest, que es uno de los algoritmos más versátiles utilizados en Machine Learning.
######### # Random Forest ######### from sklearn.ensemble import RandomForestClassifier rfc = RandomForestClassifier(n_estimators=200, random_state=101, n_jobs=-1, verbose=3) rfc.fit(X_train, y_train) from sklearn.ensemble import RandomForestRegressor rfr = RandomForestRegressor(n_estimators=200, random_state=101, n_jobs=-1, verbose=3) rfr.fit(X_train, y_train) #Use model to predict y_pred = rfr.predict(X_val) #Evaluate accuracy of the model acc_rf = round(rfr.score(X_val, y_val) * 100, 2)
Debemos decidir qué métricas de rendimiento utilizaremos para evaluar el modelo. Hay muchas métricas diferentes y, como siempre, dependiendo del problema y los datos, deberemos elegir una u otra o quizás varias de ellas. No escribiré código para esta sección ya que hay una gran cantidad de métricas distintas.
Para llegar a tener un gran algoritmo, podemos ajustar los hiperparámetros sobre los mejores algoritmos que hayamos probado. Este es un ejemplo de cómo hacerlo utilizando un algoritmo de Grid Search.
from sklearn.model_selection import GridSearchCV param_grid = {'C':[0.1,1,10,100,1000], 'gamma':[1,0.1,0.01,0.001,0.0001]} grid = GridSearchCV(model, param_grid, verbose = 3) grid.fit(X_train, y_train) grid.best_params_ grid.best_estimator_
Ensembling de modelos
Cuando queremos obtener los mejores resultados posibles con unas predicciones, muchas veces nos pueden ayudar los métodos de ensembling. Una vez tengamos un primer modelo funcionando, entrenamos otro (o más) y unimos sus predicciones. Hay diversas técnicas de ensembling y tendrás que probar varias para ver cual se adapta mejor a tu problema. Aquí un ejemplo básico:
#Max Voting model1 = tree.DecisionTreeClassifier() model2 = KNeighborsClassifier() model3 = LogisticRegression() model1.fit(x_train, y_train) model2.fit(x_train, y_train) model3.fit(x_train, y_train) pred1=model1.predict(X_test) pred2=model2.predict(X_test) pred3=model3.predict(X_test) final_pred = np.array([]) for i in range(len(X_test)): final_pred = np.append(final_pred, mode([pred1[i], pred2[i], pred3[i]]))
Interpretabilidad de modelos
Obtener un buen resultado del modelo predictivo no es siempre el objetivo principal. A veces queremos construir un modelo que sea interpretable y podamos sacar conclusiones de él. Por ejemplo si queremos predecir qué pacientes van a sufrir una enfermedad, es muy interesante que aparte de predecir los pacientes, el modelo nos diga porqué estos pacientes van a tener la enfermedad. Es porque el paciente tiene el gen X? O porque le falta vitamina Y?
En la plantilla puedes encontrar código para generar Partial Dependence Plots (PDP), o utilizar los SHAP values entre otras muchas técnicas que nos pueden ayudar a interpretar los resultados de los modelos. Un ejemplo de SHAP Values:
import shap data_for_prediction = X_val.iloc[row_num] explainer = shap.TreeExplainer(model) #Use DeepExplainer for Deep Learning models, KernelExplainer for all other models shap_vals = explainer.shap_values(data_for_prediction) shap.initjs() shap.force_plot(explainer.expected_value[1], shap_vals[1], data_for_prediction)
Sección Deep Learning
En esta sección puedes encontrar código para generar redes neuronales de todo tipo. Es un tema muy extenso que da para su propio artículo dedicado únicamente a redes neuronales. No voy a comentarlo en profundidad, simplemente quiero que sepas que está ahí la sección por si la necesitas. Podrás encontrar código para crear redes neuronales con las principales librerías (Tensorflow, Keras y Pytorch)
Natural Language Processing (NLP)
Igual que en la sección anterior, es un tema muy extenso que no voy a comentar en este artículo. Aquí podrás encontrar las principales funciones para separa un texto por palabras y analizarlo aplicando las técnicas más utilizadas en la actualidad.
Mapas y datos geográficos
Llegamos a la sección final de la plantilla. Como en las anteriores secciones, no la voy a comentar en profundidad ya que da para su propio artículo. Podrás encontrar funciones para generar mapas y trabajar con datos geográficos. Puedes aprender más sobre dibujo de mapas en la parte final de este artículo.
Conclusión
Con esto he cubierto todos los pasos que vas a necesitar para la mayoría de proyectos de Data Science. Cada sección debería ampliarse con código específico para cada dataset y problema. Es necesaria tu experiencia y sabiduría para decidir qué pasos se deben seguir y cuáles no, qué pasos deben ampliarse con código específico para el proyecto y qué pasos pueden utilizar el código de la plantilla.