Pensando en paralelo - Introduccion a Map/Reduce

Como se habrán dado cuenta (al menos si me siguen en Twitter o sino lo pueden ver ahora) me he pasado los últimos días jugando con Hadoop que es la implementación mas popular del paradigma MapReduce para procesamientos de datos en paralelo. Para ello comencé a escribir una serie de APIs para su fácil manipulación desde PHP, asi podriamos escribir aplicaciones que se ejecuten en paralelo desde PHP, lo que voy a usar por primera vez en mi proyecto del Google Summer of Code como un plus del proyecto. Este post es una introducción cuasi-teórica sobre la programación concurrente (o en paralelo).

Imagínense que el siguiente pseudo-código C se ejecuta en 8 minutos en una computadora normal, y esta optimizado al máximo.

int i;
long * result;
for (i = 0; i < 3984; i++) {
     *(result + i) = operation(i);
}

Asumiendo que la función operation no modifica ninguna variable global, el orden de la ejecución del ciclo no afectaría en nada el resultado. Partiendo de esa afirmación, si la computadora que ejecuta el programa cuenta con mas de 1 procesador/núcleo (para el ejemplo asumiremos que cuenta con 8), entonces podríamos crear 8 hilos/procesos y que cada uno vaya resolviendo una iteración del ciclo, así resolveríamos aproximadamente 8 veces mas rápido (hay una pequeña perdida por la partición de los datos y asignación de iteración a cada hilo/proceso). Este es el concepto de la computación concurrente o en paralelo.

Por supuesto, no todos los problemas pueden ser resueltos de forma concurrente, ejemplo el siguiente pseudo-código escrito en C. Esto no puede ser concurrente ya que esta vez la función operation recibe como parámetro el resultado de la iteración anterior, lo que fuerza la ejecución secuencial y en orden del ciclo.

int i;
long * result;
long xout = 0;
for (i = 0; i < 3984; i++) {
   xout  = operation(i, xout);
   *(result + i)  = xout;
}

Los lenguajes funcionales, son por naturaleza  concurrente, ya que no existen el concepto de variable y una cada función nunca modifica un dato, siempre crea una nueva copia y lo modifica, ademas las funciones son como funciones puramente matemáticas, en las que se verifican ciertas propiedades como la transparencia referencial y por tanto, la carencia total de efectos laterales.

Personalmente, entre las pocas cosas que aprendí estudiando se destaca ampliamente  Haskell, que me ayudó bastante a la hora de leer sobre MapReduce, altamente recomendado para ejercitar la mente para resolver problemas concurrentes.

Ahora que tenemos los conceptos básicos, podemos entrar un poco en lo que sea MapReduce. Básicamente existen dos funciones map y reduce que son aplicados a todos los datos. Al ser la programación concurrente, esto es divido en procesos que son proporcional al numero de datos a procesar y al numero de recurso de hardware disponible (núcleos, memoria ram, etc) para su ejecución. Los datos de entrada están formadas por una (clave, valor) y a cada dato es aplicada la función map, la cual produce una serie de resultados intermedios (de 0 a N), dichos resultados también están formados por (clave, valor). Luego, una vez finalizado todas las funciones map el proceso de MapReduce agrupa por clave los resultados intermedios, y a cada dato (clave, [resultado1, resultado2, ... resultadoN]) aplica la función reduce.

Hadoop (la mas popular Codigo Libre) o cualquier otra implementación del paradigma MapReduce, es una gran abstracción para el programador, que a diferencia de antes se centra en como resolver un problema utilizando solo funciones map y reduce. Parte de la abstracción incluye la partición de los datos, asignación y monitoreo de tareas, distribución de los datos a través de las maquinas utilizando un sistema de archivo distribuido, entre otras cosas. Este es un simple pseudo-código en PHP de como seria un indice invertido para Hadoop. (Funciona con la API que mencione anteriormente, esta en fase de desarrollo entonces me lo guardo para mi hasta que este terminado)

final class InvertedIndex extends Job
{
    function map($key, &$value)
    {
        $value = strtolower($value);
        foreach (preg_split("/[^a-záéíóúüñ]/i", $value) as $word) {
            $this->EmitIntermediate($word, $key);
        }
    }

    function reduce($key, &$values)
    {
        $this->Emit($key, array_unique($values));
    }

}

El código de arriba, toma datos (id, textos) y construye un Indice Invertido, que básicamente es una lista de palabras y las referencias de los documentos en donde aparece, ejemplo (palabra, [doc1, doc2, doc3]). Básicamente por cada documento, se emite la palabra y la clave del documento en el cual aparece (en la función map), y la función reduce retorna una lista única de palabras.

Obviamente, para la resolución de problemas, se necesitan varios trabajos, entiéndase como trabajo aplicar las funciones map y reduce a una serie de datos, un claro ejemplo seria el algoritmo de agrupación por similitud de datos K-means, que luego de terminar y optimizar la versión convencional del algoritmo lo estoy escribiendo en versión concurrente.

Espero que esta pequeña introducción les haya sido entendible y útil, mas adelante escribiré mas sobre Hadoop y ejemplos prácticos

Soy más que sólo PHP

Update: Gracias al primer feedback de Roberto Alsina (un verdadero Guru del Python), ahora siempre retorna una lista de tuplas con (idioma, porcentaje).

Este un solo un post rápido para demostrar que soy mas que solo PHP ;)

El proyecto se relativamente simple, pero por algo se empieza. Básicamente implemento la teoría de este paper para detectar los idiomas con los N-Gramas que lo he usado bastante de forma privada para mis experimentos de Clustering (clasificación automática de textos) y para mi sitio Languess.

PY-Languess (así se llama el proyecto) está hosteado en Github, a continuación como se utiliza:

#!/usr/bin/python
#encoding: iso-8859-1
from languess import Languess
from glob import glob

def main():
    # Creamos el objeto
    lg = Languess()
    # Cargamos los modelos de los lenguajes
    # que fue entrenado por train.py
    klm = {}
    for lm in glob('knowledge/*.lm'):
        klm[ lm[10:-3] ] = [n.strip() for n in open(lm).readlines()]
    lg.load_knowledge( klm )

    # Ahora a obtener los idiomas
    print lg.getLang("This is just a bloody text")
    print lg.getLang("Pues esto es un texto en español")
    print lg.getLang("Mba'eteko chera'a mba'eichapa reiko?")
    # Se me acabaron los idiomas para probar :D 

if __name__ == "__main__":
    main()

Se preguntaran porque lo he re-implementado, pues la respuesta es simple, quería hacer algo con el AppEngine, y aproveche el sábado para pelearme un poco con el Python. Ahora esta el Webservice (o intento de WS) en linea, y funciona de una manera muy simple (me encantan las cosas simples). La URL tiene la siguiente forma http://textlang.appspot.com/service?text=TEXTO_A_OBTENER_EL_IDIOMA y retorna un objeto serializado con JSON.

La URL http://textlang.appspot.com/service?text=This is just a bloody text retornaría:

{"lang":[["english",100]],"text":"This is just a bloody text"}

En caso de existir dos candidatas (existe cuando los textos son muy cortos) retornaría un vector en vez de un string en la propiedad “lang”.

Saludos y Happy hacking.

“Windows se hace con las notebooks”, el gran hoygan del momento

hoygan

Para los que no esten aconstumbrado con el termino hoygan, encontre una imagen que lo describe perfectamente.

Generalmente, en mi blog trato al máximo de evitar escribir entradas de contenido político/filosófico (en otras palabras de contenido no-técnico), pero luego de leer una blasfemia de tal envergadura no resistí las ganas y tengo que escribir algo al Ing. (quien sabe de que facultad sera Ing. pero ya me imagino, sin animo ofender).

“Aunque lo aprenda en la escuela, el software libre no me serviría, por el escaso soporte técnico que tiene esta herramienta y un mercado laboral que todavía no demanda este tipo de software”

Es la cosa mas ridícula y errada que escucho/leo en meses, hasta parece una broma de Google en el April’s fool day. Si el Software libre no tiene tanto soporte como puede explicar el crecimiento exponencial de los sistemas operativos libres, y de la total perdida de territorio de Windows en el ambito de los servidores, si no me creen pueden mirar los mejores servidores de la Internet, o porque no explican porque Google utiliza GNU/Linux para sus maquinas (=~450,000 pcs).

“Como usuario genérico que usa Word, Excel, etc., no me inclinaría a un software libre, que para usarlo bien es preciso tener más conocimientos técnicos”

Totalmente errado, un día mi novia necesitaba un trabajo practico para su facultad, entonces le preste mi Acer Aspire One que tiene Debian (and no dual booting), y lo utilizo sin problemas, lo unico que le di fue mi nombre de usuario y contraseña y lo utilizo… así que, si una persona no informática (estudiante de Odonto en este caso) puede trabajar con Debian (considerada como muchos “gurus” no amigable) sin problemas, lo que demuestra que dicha afirmación es totalmente falsa.

“El software libre no tiene soportes que garanticen tener estabilidad en la plataforma del servicio web”

Opa, opa, se metieron en mi campo, entonces porque su ASP, Windows servers, SQL Server tiene una minoría totalmente despreciable en el mercado, en realidad solo conozco alguna pseudo-empresas que utilizan algunas de las “tecnologías” previamente citadas, o porque Microsoft esta apoyando a PHP, sino miren esta presentación de un amigo en Microsoft ¿como explicarían eso?

La innegable realidad es que estos ladrones tratan de justificar por los medios millonarias sumas de dinero en licencias (+ coimas claro)… y es lamentable dar tantos justificativos sin sentidos. Ni siquiera son personas conocedoras del tema, ni siquiera son de Microsoft, son solo políticos tratando de justificar sus robos…

Programando por la remera (v2.0)

Update: La lista oficial de parte de Wordpress esta online.

Como algunos ya saben este año fuí seleccionado otra para el Google Summer of Code para trabajar en plug-in para BuddyPress y Wordpress MU. Así que me pasaré todo el verano invierno programando para ganar la remera (y otros beneficios que casi los olvido).

Google T-shirt

El proyecto consiste en implementar algoritmos “sociales” y categorización de textos. Básicamente el proyecto recogerá información del usuario, y mediante algunos algoritmos, aprenderá sus gustos, y le sugerirá cosas (posibles amigos con los mismo gustos, artículos a leer según temas generalmente leídos, por lo que las personas similares leen, agrupar posts similares basados en visitantes en común, etc, etc).Para la parte de la categorización de textos, me enfocaré en agrupar textos similares por su contenido, también agregaré la detección de idiomas y más.

Escribiré regularmente aquí (y en Español) acerca de los avances del proyecto, y también crearé un repositorio público para los que quieran seguirlo.

Tremenda semana, ya que tuve una entrevista por ganar por segundo año el PHP Innovation award, esto sumado a lo del Google Summer of Code, me dan una semana grandiosa. Aún así nada podrá ponerme mejor, ya que es difícil superar el asesinato de un familiar muy cercano.

Saludos y happy hacking.

Mi primera extensión para PHP

Luego de algún tiempo de inactividad, buscando tema para GSoC, me he metido en territorio desconocido para mí, jugar un poco con el Zend Engine, de ahí que bajé algunas presentaciones sobre como incluír PHP en una aplicación. Luego leyendo un poco más vi unos ejemplos de lo que hace tiempo he querido hacer, escribir una extesión nativa para PHP. Nativa significa código escrito en C,  ya que la máquina virutal, el compilado y el resto de PHP está escrito en C.

Me puse manos a la obra, y lo que nació como un juego, hoy en día (una semana más tarde exáctamente) es la versión alfa de los wrappers del LibTextCat. El libTextcat es un interesante proyecto que aplica la teoría presentada en N-Gram-Based Text Categorization, paper que también lo he implemendado el PHP para Languess.

Seguramente se preguntaran ¿porqué escribir algo nativo si ya lo tenias en PHP? , la respuesta es simple, rapidez. El código PHP tenia algo así como 2000 ejecuciones de bytecode, mientas con la nativa solo unas cuantas, más o menos 10, para pasar parámetros a la función C. La diferencia es de alrededor 0.05 segundos contra 0.001 segundos, es bastante pero para pequeños requerimientos la diferencia no es mucha, pero para procesamientos a larga escala, es considerable.

Más adelante escribiré como crear una extensión nativa para PHP, es relativamente sencillo, pero requiere conocimientos del lenguaje C, y cosas como autoconf, configure, en realidad más de C que otra cosa, por obvias razones ;)

Básicamente el PHPTC  (para no escribir PHPLibTextCat), se utiliza de la siguiente manera,

<?php
/* crear el "recurso" de textcat */
$tc = textcat_init();
/*  agregar un source.lm que tiene la definición del lenguage "source lang" */
textcat_add_knowledge($tc,"source.lm","source lang");
?>

Los archivos *.lm (en este caso source.lm) contiene la definición del lenguaje o conjunto de textos similares los cuales se utilizaron como ejemplos, aquí un ejemplo de como entrenarlo, aunque la librería ya venga con algunos ejemplos ya procesados.

<?php
/* crear el "recurso" de textcat */
textcat_train("source.lm", # Archivo de salida
    "este es un texto, escrito en Español, en teoría podrían ir textos más largos",
    "y otros textos, todo referentes a la misma cosa, claro",
    "podrían ser del mismo tema, o el mismo idioma, depende de tí"
);
?>

Para clasificar ciertos textos, es relativamente sencillo:

<?php
/* crear el "recurso" de textcat */
$tc = textcat_init();
/*  cargar el "conocimiento" */
textcat_add_knowledge($tc,"english.lm","Inglés");
textcat_add_knowledge($tc,"spanish.lm","Español");

$cat = textcat_get_cat($tc,"este es un pequeño texto a clasificar");
if (is_string($cat)) {
    echo "El texto esá escrito en $cat";
} else if (is_array($cat)) {
    echo "El texto parece estar escrito en ".implode(",",$cat);
} else if ($ret == SHORT_TEXT) {
    echo "Imposible de obtener categoria, texto muy corto";
} else {
    echo "Ocurrio un error"; # a leer los warnings
}
?>

Pensé por un momento agregar hacer open source www.languess.com, pero sería tonto, ya que básicamente los códigos de arriba, son toda la página.

Para el futuro implementaré una API usando Class,y me enfocaré en re-escribir el proyecto LibTextcat para agregar mejor soporte para el manejo de memoria, agergar soporte para unsupervised learnings alias text clustering, utf8 por defecto y más características.

Como instalar

Bajar el código fuente

$ git clone "git://github.com/crodas/phplibtextcat.git"

Compilar

$ cd phplibtextcat
$ phpize
$ ./configure --with-textcat
$ make
$ make test
$ make install

Agregar la extension a PHP

$ echo "extension=textcat.so" > /etc/php.d/textcat.ini
$ /etc/init.d/httpd restart

Sus feedbacks, bug reports, features requests, serán bien recibidas aquí