Estoy enredando un poco con redes neuronales en torch, y para empezar he cogido el famoso ejemplo de Karpathy char-rnn, que es el tipico texto predictivo caracter a caracter. Y antes de ponerme a modificarlo para trabajar exactamente con la longitud de un tweet y que el output de la prediccion sea una clasificacion de los tweets, he lanzado unos ejemplos sin modificar el codigo pero que aspiran a intentar predecir el partido politico al que pertenece un tuitero. Os cuento.
Lo primero es instalarse el codigo, en ubuntu sin GPUs esto ha ido sin problemas, y curiosamente va mas rapido en una maquina de cuatro hilos que en una de 16, se ve que debido a la compilacion y optimizacion de BLAS y demas librerias. Vamos, que podeis intentarlo sin CPU.
Lo primero, claro, es tener una serie de tuits y saber de que partido cojea cada tuitero. Una pista es utilizar los nicks mas relevantes clasificados en los proyectos de Kampal Social sobre las elecciones generales. Tenemos en el canal público este mismo, funcionando desde marzo. Si seleccionais el mapa de Retweets y luego «comunidades» en el perfil de estadisticas, teneis una serie de listas de los retuiteros mas pertinaces, clickando en cada lista y ordenando por actividad o por relevancia podeis escoger vuestras fuentes de datos. O bueno, podeis fiaros de vuestro instinto y simplemente ir cortando y pegando por el cliente de twitter que mas os guste.
En mi caso para ponerlo un poco mas dificil he borrado todas las menciones, aunque no los hashtags. Aqui hay ya diferentes criterios pues incluso con la misma seleccion.
Dado que no queria modificar el codigo original, la forma de etiquetar un tuit como perteneciente a un partido dado ha sido añadirle al final una cadena con el formato ****XX**. Conviene recordar que el programa intenta conjeturar cada caracter a partir de su entorno, asi que conviene que las siglas sean diferentes para cada opcion… esto es una lata dado que los nombres de partido se empeñan en empezar por P.
Hay dos vias que podemos tomar para entrenar la red. La directa, alimentando como input.txt el fichero de tweets ya con las terminaciones, y la indirecta, primero entrenando a la red para reconocer tweets en general, y luego añadiendo las terminaciones. Eso si, no nos queda mas remedio que emplear la misma lista de tweets porque el programa dimensiona la red neuronal en funcion tambien del numero de caracteres diferentes (el «vocabulario», lo llama Karpathy) y si dos bloques de input tienen diferente numero de caracteres no es posible utilizar una red de uno en el otro.
El comando para entrenarlos es relativamente trivial. Lo unico remarcable es que si estamos reentrenando desde una red previa hay que usar -init_from:
th train.lua -rnn_size 256 -data_dir data/asignadosEPvoc -checkpoint_dir AsignadosRawEP2 -eval_val_every 500 -seq_length 100 -init_from debateEP2/lm_lstm_epoch30.20_1.0597.t7
La cuestion es, ¿cómo utilizo la red para obtener predicciones? Pues dandole como semilla el tweet que queremos precedir y limitandonos a mirar como lo completa:
th sample.lua -sample 0 -verbose 0 -length 150 -primetext "$i" $file | head -n +1 | grep -a -o "\*\*\*..\*"
Aqui el truco esta en que -sample 0 desconecta la variabilidad aleatoria que el programa emplea para generar texto al azar. Podemos hacer esto con una fraccion de la muestra de entrenamiento a ver que tal lo hace. Por ejemplo (usando el fichero de entrenamiento pero quitandole la etiqueta de identificacion del partido):
cat data/asignados/input.txt | sort -R | head -n +100 | sed "s/\*\*\*..\*//g" | tr \" \' | (while read i; do th sample.lua -sample 0 -verbose 0 $file -length 150 -primetext "$i" | head -n +1; done) | grep -a -o "\*\*\*..\*" | sort | uniq -c;
De hecho al hacer esto se nota que la red no reproduce ni siquiera la distribucion de tweets por partido del fichero de input: astutamente tiende a elegir en caso de duda el partido que mas tweets tenia en la muestra y a evitar los que tienen pocos. Asi que hay que andar con cuidado y procurar que haya al menos un 10% de tuits de cada partido, si no queremos que el algoritmo aplique su propia regla de corte, que deja en mantillas lo del tres por ciento de cada circunscripcion.
Ahora podemos hacer esta estimacion para selecciones del fichero de input que sean de un partido dado:
for partido in SO CS IU MS PP UD do echo sampling from $partido... grep -a "\*\*$partido\*" data/asignados/input.txt | sort -R | head -n +100 | sed "s/\*\*\*..\*//g" | tr \" \' | (while read i; do th sample.lua -sample 0 -verbose 0 $file -length 150 -primetext "$i" | head -n +1; done) | grep -a -o "\*\*\*..\*" | sort | uniq -c done
Y claro, nos encontramos que casi en ningun caso se acerca a etiquetar el 100% de los tuits como pertenecientes al partido original. No dicho, si no lo tiene muy claro, el sistema intenta mas bien hacer una apuesta a partir de las probabilidades de lo que ha aprendido. Lo que tendremos que hacer sera pues considerar no la prediccion mayoritaria al examinar una muestra sino la diferencia entre esta y el «background» que hemos calculado anteriormente.
Por ejemplo con un fichero de input entrenado previamente sin etiquetar, y que de momento lleva solo una ronda de entrenamiento con las etiquetas puestas, estoy obteniendo este resultado:
CS | IU | MS | PP | SO | UD | ||
---|---|---|---|---|---|---|---|
file stats | 12.2% | 4.3% | 36.6% | 10.8% | 23.1% | 12.0% | |
muestreo 100 | 12 | 0 | 16 | 9 | 47 | 9 | |
16 de cada partido | 12 | 2 | 16 | 16 | 42 | 4 | |
100 de SO | 5 | 0 | 11 | 7 | 73 | 3 | |
100 de CS | 27 | 0 | 14 | 0 | 53 | 4 | |
100 de IU | 6 | 4 | 23 | 4 | 52 | 6 | |
100 de MS | 5 | 0 | 48 | 6 | 33 | 4 | |
100 de PP | 5 | 0 | 12 | 45 | 33 | 1 | «Prediccion» |
100 de UD | 1 | 0 | 3 | 51 | 27 | 15 | |
—- Difererencias respecto a la muestra homogenea —- | |||||||
100 de SO | – | – | – | – | +31 | – | PSOE |
100 de CS | +15 | – | – | – | +11 | 0 | Ciudadanos o PSOE |
100 de IU | – | +2 | +7 | – | +10 | +2 | No es PP ni CS. |
100 de MS | – | – | +32 | – | – | 0 | Podemos |
100 de PP | – | – | – | +29 | – | – | Partido Popular |
100 de UD | – | – | – | +35 | – | +11 | PP o UPyD, probablemente UPyD |
De momento es todavia dificil distinguir los partidos con poca representacion en tuits; la confusion puede apañarse mediante una formula empirica, por ejemplo dividiendo el incremento entre el porcentaje inicial, para averiguar cual es el mas relevante (y asi identificando correctamente Izquierda Unida y UPyD) o poniendonos más rigurosos y estimando estadisticamente la likelihood de cada resultado dados los tamaños y sesgos muestrales.
Echando un vistazo a otros resultados, observo que no todas las redes que estoy entrenando apuestan por defecto a los mismos jinetes. En general se da la pauta de dar menos probabilidad a alguna de las tres menos representadas y dar mucha a alguna de las tres con mas representacion, pero vete a saber en que se basa la decision; con solo unas pocas horas de entrenamiento -acordaos de que no estoy usando GPU- cada red puede estar usando distintos rasgos distintivos.
Tampoco estoy encontrando resultados distintos entre los dos metodos de entrenamiento. Quizas porque a fin de cuentas el objetivo definido es el mismo, acertar el mayor numero de caracteres, aunque en realidad lo que queremos optimizar es que acierten los cinco caracteres finales. Toca pues ponerse a modificar codigo.
Por otro lado, seria interesante ver hasta donde se puede tirar con esta idea de mirar solo la prediccion de caracteres ascii, e intentar entrenar otros algoritmos mas tradicionales, cadenas de Markov o el ejemplo de Y. Goldberg, que es extremadamente simple, para predecir la «terminacion» de tweet.
Deja una respuesta