Aplicando Machine Learning a FinTech (1) Arboles de Decisión

Published on Author lopez

Siguiente Post

El año pasando, para la charla de JavaScript e Inteligencia Artificial de preparé para JSConf Argentina, había escrito una librería en JavaScript (para NodeJs y browser) que implementa Arboles de Decisión, basados en una descripción que encontré en el libro de Quinlan, “C 4.5 Programs for Machine Learning”.  Pueden encontrar una descripción del algoritmo en:

https://en.wikipedia.org/wiki/C4.5_algorithm

El ejemplo que había escrito usa un dataset de “Database for fitting contact lenses”, que encontré en:

http://archive.ics.uci.edu/ml/machine-learning-databases/lenses/

mencionado en el libro “Machine Learning in Action” de Peter Harrington (http://www.manning.com/pharrington/). Pueden ver el ejemplo browser en:

https://github.com/ajlopez/SimpleDT/tree/master/samples/lenses

Hoy publiqué una versión 0.0.2 de la librería SimpleDT:

https://github.com/ajlopez/SimpleDT

que permite decidir sobre atributos numéricos, no sólo por atributos de texto. Y lo apliqué para deducir un árbol de decisión de riesgo crediticio, tomando como dataset el publicado en:

https://archive.ics.uci.edu/ml/datasets/Statlog+(German+Credit+Data)

Pero veamos, ¿cómo eso de tomar un dataset como entrada y obtener un árbol de decisión? Primero, un dataset es una lista de ejemplos, cada uno con una cantidad de atributos. Por ejemplo, el dataset que usé para créditos, es un archivo de texto que comienza con:

A11 6 A34 A43 1169 A65 A75 4 A93 A101 4 A121 67 A143 A152 2 A173 1 A192 A201 1
A12 48 A32 A43 5951 A61 A73 2 A92 A101 2 A121 22 A143 A152 1 A173 1 A191 A201 2
A14 12 A34 A46 2096 A61 A74 2 A93 A101 3 A121 49 A143 A152 1 A172 2 A191 A201 1
A11 42 A32 A42 7882 A61 A74 2 A93 A103 4 A122 45 A143 A153 1 A173 2 A191 A201 1
A11 24 A33 A40 4870 A61 A73 3 A93 A101 4 A124 53 A143 A153 2 A173 2 A191 A201 2
A14 36 A32 A46 9055 A65 A73 2 A93 A101 4 A124 35 A143 A153 1 A172 2 A192 A201 1

Ver el archivo completo en:

https://github.com/ajlopez/SimpleDT/blob/master/samples/scoring/german/german.data.txt

La primera columna es un atributo cualitativo (no numérico) que indica la existencia de una cuenta de cheques, en marcos alemaneas, con valores posibles:

  • A11: … < 0 DM (importe negativo)
  • A12: 0 <= …. < 200 DM (importe entre 0 y 200 marcos)
  • A13: … > 200 DM (importe mayor o igual a 200 marcos)
  • A14: sin cuenta de cheques

Estos valores son ingresos mensuales en esa cuenta, de por lo menos un año.

La segunda columna, en cambio, es un atributo numérico que indica el plazo en meses del préstamo solicitado.

La tercera columna indica la historia de crédito, teniendo valores no numéricos:

  • A30: No tomó créditos, o todos sus créditos fueron pagados en término
  • A31: Todos los créditos en este banco fueron pagados
  • A32: Todos los créditos pagados pero todavía no fueron cancelados, faltan cuotas por vencer
  • A33: Tuvo atrasos en el pasado
  • A34: Cuenta en estado crítico

Y así. Pueden leer la descripción de las columnas en: https://github.com/ajlopez/SimpleDT/blob/master/samples/scoring/german/german.txt

O sea, cada renglón es un registro que describe una persona que pidió un crédito. La última columna toma dos valores posibles:

  • 1: la persona fue calificada como de buen crédito
  • 2: la persona tiene mal crédito

El algoritmo que apliqué permite analizar este dataset (son mil casos) y deducir un árbol de decisión para decidir si un caso cualquiera tiene buen o mal crédito, en base a la experiencia de este dataset.

Se ejecuta el programa (con NodeJS) con el comando:

node process

y obtendremos por pantalla el vuelco de un archivo JSON. El resultado de la última vez que lo ejecuté está en:

https://github.com/ajlopez/SimpleDT/blob/master/samples/scoring/german/tree.json

Veamos el comienzo de este resultado:

{
    "attribute": "0",
    "values": {
        "A11": {
            "attribute": "2",
            "values": {
                "A34": {
                    "attribute": "3",
                    "values": {
 ....

La línea:

    "attribute": "0",

Indica que el primer atributo a tomar en cuenta, por el que el árbol de decisión pregunta, es el 0 (corresponde al atributo 1 del documento original, usé base 0 como en JavaScript). Y las líneas:

    "values": {
        "A11": {
            "attribute": "2",
            "values": {
                "A34": {
....

Dicen “Si ese atributo tiene valor A11, seguir este otro árbol de decisión….” preguntando entonces por el atributo 2, y así.

Cuando un atributo que le interesa al algoritmo es numérico, ve cuál es la partición de valores que más información le da para seguir avanzando. Ejemplo:

"attribute": "1",
    "values": {
        "<= 11": {
            "value": 1
        },
        "> 11": {
            "attribute": "9",
            "values": {

Acá decide examinar el atributo 1, y si su valor es menor que 11, decide que la persona tiene buen crédito (resultado 1). Sino, sigue con otro árbol de decisión.

La forma de elegir por qué atributo preguntar y qué valores, la decide el algoritmo tomando siempre el próximo atributo que arroje más información (técnicamente, que disminuya la entropía).

Y el programa que lanza este proceso es muy simple:

var sdt = require('../../..');

var fs = require('fs');

var filetext = fs.readFileSync('german.data.txt').toString();

var lines = filetext.split('\n');

for (var k in lines)
    lines[k] = lines[k].trim();
    
var data = [];

for (var k in lines) {
    var values = lines[k].split(' ');
    for (var j in values) {
        var value = values[j];
        
        if (value[0] >= '0' && value[0] <= '9')
            values[j] = parseInt(value);
    }
    data.push(values);
}

var tree = sdt.tree(data, data[0].length - 1);

console.log(JSON.stringify(tree, null, 4));

Sólo es largo porque la librería todavía no reconoce por sí misma cuáles valores son numéricos y hay que alimentarla con los datos ya digeridos. Espero mejorar la salida, y hacer una implementación en el browser de este ejemplo, como había hecho para el caso de las lentes de contacto.

Imaginénse este tipo de algoritmo aplicado a datos recolectados en el tiempo dentro de un sitio P2P Lending como el que comenté ayer en este blog, MoneyPool. Como el algoritmo es genérico (no depende del dominio) también podría exponerse como una API. Una especie de Machine Learning usando Decision Trees, as a Service 😉

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez