Ciencia De Datos

Diapositivas de "Antikaggle: contra la homeopatía de datos"

He colgado las diapositivas de Antikaggle: contra la homeopatía de datos. Sobre todo, para que aquellos que aún conserven la pasión por saber más puedan visitar los enlaces que recopilé y que figuran en ella.

El vídeo, se dice, aparecerá pronto. Sin él, las diapositivas, puro soporte visual, quedan huérfanas.

Tema, tono y contenid son premeditadamente polémicos; las consecuencias, previsibles. Fe de ello dan los comentarios de los asistentes.

Una fina, tenue, somera capa de sintaxis

Estuve el otro día en una charla de José Luis Cañadas en el grupo de usuarios de R de Madrid sobre sparklyr. Hoy en otra de Juan Luis Rivero sobre, esencialmente, lo mismo, pero esta vez con Python. Y podría escribir “etc.”.

evolucion_convergente

Me centraré en la de José Luis, aunque podría decir lo mismo de cualquiera de las otras. No había trabajado con sparklyr. No soy siquiera fan de dplyr (aunque no es que no se lo recomiende a otros; es simplemente, como tantas cosas, que soluciona problemas que no tengo). Pero la seguí sin mayores problemas. Lo que tenía de nuevo era una fina, somera capa de sintaxis que enlazaba fundamentos con fundamentos.

Homeopatía de datos

Me mandan un whatsapp. Es de alguien que está en una charla de ciencia de datos. Acaba de oír decir al ponente que en una de esas competiciones de Kaggle le ha servido optimizar a lo largo del conjunto de semillas aleatorias. Sí, del set.seed().

homeopatia_funciona

Supongo que al ponente le funcionaría.

El éxito de la ciencia de datos parece tener aparejada una plaga de homeopatía de datos. Algo habrá que hacer. Por lo pronto, emplear el escepticismo para algo más que para asestar grandes lanzadas a moros muertos.

El principio de información

Tramontando el recetariado, llegamos a los principios. Y el más útil de todos ellos es el de la información (o cantidad de información).

(Sí, de un tiempo a esta parte busco la palabra información por doquier y presto mucha atención a los párrafos que la encierran; anoche, por ejemplo, encontré un capitulito titulado The Value of Perfect Information que vale más que todo Schubert; claro, que Schubert todavía cumple la función de proporcionar seudoplacer intelectual a mentes blandas y refractarias al concepto del valor de la información perfecta).

Recetas y principios

En algunas de las últimas charlas (de ML) a las que he asistido se han enumerado recetas con las que tratar de resolver distintos problemas. Pero no han explicado cuándo ni por qué es conveniente aplicarlas. Incluso cuando se han presentado dos y hasta tres recetas para el mismo problema.

receta

Me consta que parte de la audiencia quedó desconcertada y falta de algo más. ¿Tal vez una receta para aplicar recetas? ¿De una metarreceta?

El RMSE es Dios y XGBoost, su profeta

De los últimos foros de científicos de datos a los que he asistido, de las últimas conversaciones con científicos de datos que he mantenido, he salido con una gran duda: ¿soy yo el que tiende a juntarse con ellos o es que hay una plaga de talibanes del RMSE es Dios y XGBoost, su profeta?

herejes_hoguera

Lejos está ese lema simplificador de los principios que me mueven a escribir estas páginas. Por lo que, anuncio, estoy arrejuntando razones y papelotes con los que tratar de arrancar un movimiento herético.

La Consejería de Empleo de la Función General de la Comunidad Autónoma de Ordenación Provincia de la Audiencia Profesional

Ese es el nombre agramatical de una nueva consejería pergeñada por una red neuronal recurrente que he ajustado usando un año de BOEs.

El código, adaptado de aquí y sustancialmente mejorado, es

library(mxnet)

batch.size     <- 32
seq.len        <- 64
num.hidden     <- 128
num.embed      <- 8
num.lstm.layer <- 1
num.round      <- 1
learning.rate  <- 0.1
wd             <- 0.00001
clip_gradient  <- 1
update.period  <- 1

make.data <- function(dir.boe, seq.len = 32,
  max.vocab=10000, dic = NULL) {
  text <- lapply(dir(dir.boe), readLines)
  text <- lapply(text, paste, collapse = "\n")
  text <- paste(text, collapse = "\n")

  char.lst <- strsplit(text, '')[[1]]
  chars <- unique(char.lst)

  num.seq  <- floor(length(char.lst) / seq.len)
  char.lst <- char.lst[1:(num.seq * seq.len)]
  data <- matrix(match(char.lst, chars) - 1, seq.len, num.seq)

  dic <- as.list(1:length(chars))
  names(dic) <- chars

  lookup.table <- as.list(chars)

  return (list(data = data, dic = dic,
    lookup.table = lookup.table))
}


ret <- make.data(".", seq.len=seq.len)

X   <- ret$data
dic <- ret$dic
lookup.table <- ret$lookup.table

vocab <- length(dic)

train.val.fraction <- 0.9
train.cols <- floor(ncol(X) * train.val.fraction)

drop.tail <- function(x, batch.size) {
  nstep <- floor(ncol(x) / batch.size)
  x[, 1:(nstep * batch.size)]
}

get.label <- function(X)
  matrix(c(X[-1], X[1]), nrow(X), ncol(X))

X.train.data   <- X[, 1:train.cols]
X.train.data   <- drop.tail(X.train.data, batch.size)
X.train.label  <- get.label(X.train.data)
X.train        <- list(data=X.train.data, label=X.train.label)

X.val.data     <- X[, -(1:train.cols)]
X.val.data     <- drop.tail(X.val.data, batch.size)
X.val.label    <- get.label(X.val.data)
X.val          <- list(data=X.val.data, label=X.val.label)


model <- mx.lstm(X.train, X.val,
    ctx=mx.cpu(),
    num.round=num.round,
    update.period=update.period,
    num.lstm.layer=num.lstm.layer,
    seq.len=seq.len,
    num.hidden=num.hidden,
    num.embed=num.embed,
    num.label=vocab,
    batch.size=batch.size,
    input.size=vocab,
    initializer=mx.init.uniform(0.1),
    learning.rate=learning.rate,
    wd=wd,
    clip_gradient=clip_gradient)


get.sample <- function(n, start = "<", random.sample = TRUE){

  make.output <- function(prob, sample = FALSE) {
    prob <- as.numeric(as.array(prob))
    if (!sample)
      return(which.max(as.array(prob)))
    sample(1:length(prob), 1, prob = prob^2)
  }

  infer.model <- mx.lstm.inference(
      num.lstm.layer=num.lstm.layer,
      input.size=vocab,
      num.hidden=num.hidden,
      num.embed=num.embed,
      num.label=vocab,
      arg.params=model$arg.params,
      ctx=mx.cpu())

  out <- start
  last.id <- dic[[start]]

  for (i in 1:(n-1)) {
    ret <- mx.lstm.forward(infer.model, last.id - 1, FALSE)
    infer.model <- ret$model
    last.id <- make.output(ret$prob, random.sample)
    out <- paste0(out, lookup.table[[last.id]])
  }
  out
}

cat(get.sample(1000, start = "A", random.sample = T))

Lo anterior genera cosas tales como:

k-medias es como las elecciones; k-vecinos, como los cumpleaños

El otro día asistí a la enésima confusión sobre k-medias y k-vecinos. Que lo es, más en general, sobre el clústering contra modelos locales de la clase que sean, desde k-vecinos hasta el filtrado colaborativo. Veamos si esta comparación que traigo hoy a mis páginas contribuye a erradicar dicha confusión.

k-medias es como las elecciones. Hace poco tuvimos unas en España. Alguien decidió (aproximadamente) que k = 4 y nos pidió, a nosotros, punticos del espacio, identificar el centroide más próximo a nosotros para que lo votásemos. Pues eso, la misma frustración que muchos dizque sintieron teniendo que elegir entre partidos/centroides subjetivamente igual de alejados de los intereses de uno es la que sienten nuestros punticos cuando los procrusteamos para asociarlos al totum revolutum de los clientes estrella, etc.

Caret y rejillas: ¿es necesario utilizar fuerza bruta?

Durante la charla de Carlos Ortega del pasado jueves sobre el paquete caret y sus concomitancias, se planteó el asunto de la optimización de los parámetros de un modelo usando rejillas (grids) de búsqueda.

Cuando un determinado algoritmo depende de, p.e., cuatro parámetros, se puede definir una rejilla como en

gbmGrid <-  expand.grid(interaction.depth = c(1, 5, 9),
      n.trees = (1:30)*50,
      shrinkage = 0.1,
      n.minobsinnode = 20)

y caret se encarga de ajustar el modelo bajo todas esas combinaciones de parámetros (90 en el ejemplo) para ver cuál de ellas es, con las debidas salvedades, óptima.