jueves, 28 de marzo de 2013

Information Gain sobre mi ARFF

Tengo un archivo arff más o menos balanceado con 9506 observaciones que cubre dos dominios. El archivo tiene dos columnas: la primera es un texto que originalmente fue una página html, sólo que fue desprovisto de todas las etiquetas; la segunda es la clase (el dominio).

Para calcular el information gain utilicé primero el filtro StringToWordVector de Weka:


Luego discreticé las variables, que no entiendo por qué razón quedan como numéricas (probablemente hay algún parámetro que tenía que tocar).

Por último calculé el information gain.


Los resultados para los primeros 440 términos se muestran en el siguiente gráfico:


Los resultados para los primeros 40 en el siguiente:


..to be continued

Segundo ARFF

Tal como fue anunciado antes, modifiqué mi código PHP para que, en lugar de crear un único archivo plano con una página por línea, cree tantos archivos planos como páginas se procesen.

Quedó así:

--etiqueta PHP de inicio--

// INCLUDES

// adaptacion de la librería en http://www.howtocreate.co.uk/php/html2texthowto.html
include 'html2text_v3.php';

//FUNCTIONS

/*
Convierte los contenidos de un HTML a texto y genera un target_file por cada pagina html
Parámetros:
source_file_name: string que contiene el path al archivo de origen en formato html
target_directory: directorio de destino para los target files
prefix: prefijo del nombre de cada target file. La idea es usar las 3 primeras consonantes de cada clase (dominio)
i: como es un archivo por página, se usa como sufijo el orden en que se genera.
*/
function conversion($source_file_name, $target_directory, $prefix, $i) {
//Nombre del archivo destino
$target_file_name = $target_directory."/".$prefix."data".$i.".txt";
//Prepara el archivo destino para escritura
$target_file = fopen($target_file_name,"w");
//Lee los contenidos del archivo origen
$source_file = file_get_contents($source_file_name);
//Escribe los contenidos en el archivo destino
echo fwrite($target_file,html2text($source_file));
//Cierra el archivo destino
fclose($target_file);
}


//BODY

//Prefijo
$prefix="tlf";

//Nombre del directorio en el que se almacenarán los archivos destino
$target_directory = "target";

$i=1;
//Recorre el directorio source
// de: http://forums.codeguru.com/showthread.php?424910-PHP-Looping-through-files-in-directory
$handle = opendir('source');
while (false !== ($source_file = readdir($handle))){

conversion('source/'.$source_file, $target_directory, $prefix, $i );

$i = $i + 1;

}

-- etiqueta PHP de finalización --

Este ejemplo es para el dominio de teléfonos. Modifiqué a mano el prefijo y coloqué también a mano los archivos de entrada (las páginas html) en el directorio source. Que me perdonen los puristas de la parametrización en el código y la automatización extrema.

Después volví a ejecutar en el Simple CLI de Weka el comando:
java weka.core.converters.TextDirectoryLoader -dir "C:\Users\Ana\Documents\Data Mining\TESIS DM\EXTRACCION DE ESPECIFICACIONES\Weka\data\directorio_camaras_telefonia" > "C:\Users\Ana\Documents\Data Mining\TESIS DM\EXTRACCION DE ESPECIFICACIONES\Weka\data\directorio_camaras_telefonia\camaras_telefonia.arff"
Y ahora sí, obtuve un archivo ARFF con 9506 observaciones, cada una con su clase. Tiene dos columnas: la primera columna es un string (la página html desprovista de etiquetas) y la segunda columna es la clase.




domingo, 24 de marzo de 2013

Tengo mi primer ARFF pero no me sirve

Ya nos lo habían comentado en los talleres de tesis 1 y 2. La tesis es un laaarrrgo camino de errores pero uno sólo entrega el registro de los aciertos. La mía seguramente irá con un link a este blog para que nadie desespere... o para que desesperen concientemente.
Cuando intenté generar el archivo a partir de una estructura de directorios, tal como se indica en http://weka.wikispaces.com/Text+categorization+with+WEKA, Weka generó el siguiente error:

Investigando un poco, primero en la FAQ de Weka y luego acá: http://weka.wikispaces.com/Java+Virtual+Machine#Invocation, cambié el parámetro maxheap de RunWeka.ini. Estaba configurado en 1024m y ahora tiene 2048m.
Volví a ejecutar el comando:
java weka.core.converters.TextDirectoryLoader -dir "C:\Users\Ana\Documents\Data Mining\TESIS DM\EXTRACCION DE ESPECIFICACIONES\Weka\data\directorio_camaras_telefonia" > "C:\Users\Ana\Documents\Data Mining\TESIS DM\EXTRACCION DE ESPECIFICACIONES\Weka\data\directorio_camaras_telefonia\camaras_telefonia.arff"
y obtuve un mensaje esperanzador:
Finished redirecting output to 'C:\Users\Ana\Documents\Data Mining\TESIS DM\EXTRACCION DE ESPECIFICACIONES\Weka\data\directorio_camaras_telefonia\camaras_telefonia.arff'.
Precioso!! Dije en voz alta con tono de "feliz cumpleaños": tengo un ARFF de 34MB!!! Pero cuando lo abrí en Weka me di cuenta de que tengo sólo dos registros. Tooooodas las páginas de telefonía me quedaron como un único registro de clase "telefonía" y lo mismo con las cámaras.
Aparentemente la solución sería modificar mi archivito PHP para que, en lugar de generar un único archivo plano con una página por línea, genere un archivo plano de 1 línea por página. Esto es, en lugar de tener un .txt con 4000 líneas, tendría 4000 archivos .txt (para cada clase).
Sale la versión 2 de mi PHP que se ejecuta por línea de comando... coming soon.

lunes, 18 de marzo de 2013

Weka y el (pre)procesamiento de textos

Lo que sigue ahora es el preprocesamiento de los archivos necesario para comenzar a realizar tareas de clasificación.
En principio, tendría que realizar un crawling de algún otro dominio para poder tener archivos pertenecientes a diferentes clases. Luego, lo ideal sería poder utilizar la siguiente funcionalidad de weka, en la cual se puede transformar una determinada estructura de directorios en un archivo arff. Ver:
http://weka.wikispaces.com/Text+categorization+with+WEKA
Una vez que se tenga un archivo de entrada arff con una columna con texto y otra con la clase, habría que convertir cada texto en un vector de palabras usando el filtro StringToWordVector. Las siguientes páginas pueden servir de referencia para ello.
http://wiki.aigroup.com.ar/ci/opinion-mining/text-mining-con-weka---part-1
Pre-processando textos com Weka (StringToWordVector)
http://www.youtube.com/watch?v=ycbGUfY8BzM
Resta todavía saber cómo calcular chi-cuadrado, MI y PMI.

sábado, 16 de marzo de 2013

Cambios en la configuración de php para poder procesar 4000+ archivos

Tal como publiqué en el post "PHP en IIS", tuve que volver a cambiar las directivas de php.ini para poder procesar un número más grande de archivos. Tengo 4399 páginas html que requiero convertir en líneas únicas de texto dentro de un archivo plano.
Cambié las siguientes directivas en php.ini
max_execution_time = 480 (tenía 30, se mide en segundos)
max_input_time = 960
memory_limit = 256M (tenía 128)
Pero no funcionó. Si bien procesó más líneas, no terminó de procesar todos los archivos.
De manera que decidí utilizar PHP vía línea de comandos, de la siguiente manera:
C:\php-5.4.12\php.exe -f "c:/inetpub/wwwroot/tesis/conversion_command_line_v1.php"
y funcionó!

domingo, 10 de marzo de 2013

Usando el HTTrack para "crawlear"... y próximas tareas

Los "Web Crawlers" o "Web Spiders" son programas que exploran la web de forma automática. Parten de una o varias URLs iniciales, examinan los enlaces que contienen y exploran nuevas páginas a partir de allí y así sucesiva y recursivamente.
Después de algunos intentos infructuosos por hacer andar el Heritrix y algún tímido intento por entender Websphinx, desistí de usar ambos y opté por HTTrack, que me parece bastante fácil de usar en comparación con los anteriores.
El primer dominio con el que estoy trabajando es el de celulares. Es un dominio que no tiene subcategorías pero que sí puede subdividirse por marcas. Algunas marcas tienen cantidades poco significativas de productos. Para mantener un balance entre las marcas más pobladas, como fue sugerido por mi tutor, incluí por separado cada una de las URLs de marcas en la lista de URLs que usé para la extracción. Ya había hecho una extracción general de unos 600 items antes, así que creo que voy a extraer al menos 3 veces esa cantidad. Cuando esté cerca de los 2000 ítems, detengo el crawler.
Los siguientes pasos serían:
1. Mejorar el script PHP que remueve las etiquetas HTML. Por un lado tendría que iterar sobre todos los archivos de un directorio en lugar de recibir el listado de archivos. Por otro lado tendría que remover absolutamente todas las etiquetas HTML (todavía falta perfeccionarlo en ese sentido). La salida debería ser un archivo plano que tenga una línea por página web que contenga sólo el texto de la página, desprovisto de cualquier tipo de código o etiqueta.
2. Sobre este archivo de salida, calcular las métricas PMI, MI y chi-cuadrado.

PHP en IIS

Necesito replicar lo que ya funcionó la PC que estaba usando (perteneciente a la empresa para la cual trabajo) en mi propia PC, ahora que la recuperé nuevamente. Para la instalación de PHP seguí las instrucciones descritas en http://php.net/manual/en/install.windows.iis7.php. En la otra máquina opté por usar WAMP y trabajar con Apache.
No me funcionaba el fopen del post anterior sobre el archivo target por un problema de permisos ("permission denied"). Se resolvió gracias al siguiente comentario dentro de http://www.php.net/manual/en/function.fopen.php:
In IIS you must add the group Authenticated Users with write and modify permissions in the file where you want to write if you are in a Protected directory (Basic or Digest authentication) and want to write to a file in a Unprotected directory (Anonymous Access) in order to get permission to do that. Otherwise you will get the message: PHP Warning: fopen(x.txt): failed to open stream: Permission denied in c:\web\x\x.php on line 3 PHP Warning: fwrite(): supplied argument is not a valid stream resource in c:\web\x\x.php on line 10
Es decir, se resolvió cambiando los permisos del archivo para el grupo Users de Windows.
Muy probablemente tenga que volver a revisar las "Optional Directives" de http://www.php.net/manual/en/install.windows.manual.php cuando el procesamiento comience a tomar más tiempo y a usar más memoria. Es muy probable que los valores por defecto sean insuficientes, pero por ahora lo dejo así.

Removiendo las etiquetas html de las páginas

Hice un código en PHP bastante rudimentario pero que logra el objetivo: convierte archivos HTML a texto y los deja en una única línea dentro de un .txt.
Copio abajo el código. La librería html2text_v3.php es esta:http://www.howtocreate.co.uk/php/html2texthowto.html; sólo que la modifiqué a conveniencia. En realidad, jugué con los parámetros.

__
ETIQUETA php de inicio

// INCLUDES
include 'html2text_v3.php';



//FUNCTIONS

/*
Convierte los contenidos de un HTML a texto y los agrega al target_file
Parámetros:
source_file_name: string que contiene el path al archivo de origen en formato html
target_file: archivo de destino
*/

function conversion($source_file_name, $target_file) {
$source_file = file_get_contents($source_file_name);
echo fwrite($target_file,html2text($source_file));
}


//BODY

//Nombre del archivo destino único
$target_file_name = "target/target_file.txt";

//Prepara el archivo destino para escritura
$target_file = fopen($target_file_name,"w");

//Listado de archivos origen
$source = array("source/MLA-433977790-celular-samsung-galaxy-y-pro-b5510-libre-de-fabrica-necxus-_JM.html",
"source/MLA-434142480-blackberry-8520-liberado-local-a-la-callelomas-de-zamora-_JM.html",
"source/MLA-434984962-apple-iphone-5-16gb-pantalla-4-wifi-liberado-chip-a6-ios6-_JM.html");

//Recorre el arreglo de archivos origen escribiendo los contenidos html convertidos a texto en el archivo destino
for ($i = 0; $i <= count($source)-1; $i++) {
conversion($source[$i], $target_file);
}


//Cierra el archivo destino
fclose($target_file);


ETIQUETA php de fin