Upload
lamhuong
View
217
Download
0
Embed Size (px)
Citation preview
Capítulo 3. Implementación del Memory-Based Learner
48
3. Implementación del Memory-Based Learner.
3.1. Introducción.
Para llevar a cabo el desarrollo de un MBL, había que tener claro cuál era su
función y el propósito de su implementación. A la hora de procesar textos, nos
encontramos con una serie de tareas variadas cuya resolución podía verse mejorada con
un MBL: un elemento que es capaz de entrenar con una serie de ejemplos cuya solución
se conoce, para después enfrentarse a textos para los que se desconoce una solución. De
este modo, extrapolando resultados obtenidos en experiencias anteriores, podríamos ser
capaces de obtener una mayor precisión en la resolución de dichas tareas.
Un MBL requiere almacenar una estructura de datos, con la que ha entrenado, en
memoria. Esta estructura de datos se compone de un conjunto de ejemplos que se han
obtenido de los textos de entrenamiento. A su vez, por lo general, un ejemplo está
compuesto de una ventana (de tamaño variable) de rasgos (elementos que se consideran
de interés n la resolución de un problema en concreto), de una frecuencia (número de
veces que se repite una misma ventana de rasgos con una misma solución), de una
solución (para el problema que en cada caso nos ocupe) y de un comentario (para una
más fácil comprensión de los resultados, se estimó conveniente conservar la palabra de
la que se obtuvo el ejemplo). Esta estructura se ha empleado tanto para los ejemplos con
Capítulo 3. Implementación del Memory-Based Learner
49
los que se entrena como con los que se evalúan, para así poder reutilizar al máximo el
código de traducción de textos a ejemplos.
En un principio se pensó en el desarrollo del MBL en un único módulo, y así fue
como éste se llevó a cabo hasta la realización de las primeras pruebas. Pero se constató
que, para desarrollar una aplicación que puede ser empleada en la resolución de un
amplio abanico de tareas, lo mejor era estructurarlo en diferentes módulos. Para ello, era
necesario que las estructuras de los ejemplos fueran independientes de la tarea para la
que se estuviesen empleando, o de lo que en realidad representen en cada momento los
rasgos de los ejemplos.
Finalmente se optó por una representación numérica de los de los rasgos que
componen un ejemplo. Fuese cual fuese el significado de un rasgo (una letra, una
palabra, distancia de una sílaba el final de la palabra,...), éstos se tenían que traducir a
un formato numérico. Por tanto, el primer módulo que tenía que desarrollarse, y que iba
a ser el encargado realizar esa traducción de textos al formato pensado para los
ejemplos, era el único que no podía ser reutilizado. Los demás módulos ya no
entenderían de palabras, letras, tareas lingüísticas a resolver, etc. Únicamente
entenderían de estadística y de ejemplos. De ahí la importancia de emplear una misma
estructura de datos para los ejemplos.
A continuación se pasa a describir más en detalle el formato de los ejemplos que
se ha empleado, así como la estructura del MBL dividido en sus módulos. Todo el
desarrollo del MBL se ha realizado en C, pues en el Grupo de Tecnología del Habla se
empleaba el compilador Borland C++ Versión 5.02.
Capítulo 3. Implementación del Memory-Based Learner
50
3.2. Formato de los ejemplos.
Cuando se ha seleccionado un texto para que el MBL se entrene con él, éste va a
almacenar en memoria la información que considera útil de dicho texto en forma de
ejemplos. Para que pueda entrenar con dichos ejemplos, estos se han de presentar en el
formato apropiado, y eso es labor del módulo traductor. El único inconveniente que
tiene el haber separado el desarrollo del MBL en distintos módulos es que se requiere
que cada uno genere su salida en fichero. Como se permite la ejecución por separado de
cada módulo, los ejemplos han de figurar en fichero tras la ejecución de cada módulo.
No es como cuando se desarrolla todo en un mismo bloque, donde los ejemplos pueden
permanecer en memoria hasta la finalización de la ejecución.
Los ejemplos que se van a almacenar no son más que una lista (mediante
asignación dinámica de memoria) de ejemplos individuales. Cada uno de estos ejemplos
es una estructura compuesta por:
• Una ventana de rasgos, que no es más que un array de rasgos. Dependiendo
de cada caso, y según el problema en estudio, el rasgo representará una cosa
u otra: una letra, una palabra, si una palabra pertenece a una categoría
gramatical o no, etc. Por ello, independientemente de la semántica intrínseca
a cada tipo de rasgos, éstos se representan de forma única, mediante un
flotante.
• Una frecuencia asociada a cada ejemplo. En el caso de que en el conjunto de
ejemplos totales obtenidos a partir de los textos de entrada haya más de uno
con la misma ventana de rasgos y con la misma solución, éstos se agrupan
bajo un mismo ejemplo, con el parámetro de la frecuencia que corresponda
(mayor que uno).
Capítulo 3. Implementación del Memory-Based Learner
51
• Una solución calculada, conocida o estimada (según si nos encontramos en
la fase de entrenamiento o de evaluación) para cada ejemplo. Esta solución
también se representará en formato numérico, aunque en la mayoría de los
casos tratados, la solución pertenecía a un conjunto acotado de soluciones
discretas: si una letra está tildada o no, si una palabra es de la categoría
gramatical verbo o nombre, etc. Por tanto, se definirá un tipo enumerado
(TValorSolucion), al que pertenecerán las soluciones.
• Por último, y únicamente con la misión de facilitar la labor del análisis de
resultados, se añadió un comentario. No es más que un string que contiene
la palabra de la que se obtuvo el ejemplo.
A continuación se muestra la definición de todos estos componentes en el
correspondiente fichero cabecera del MBL:
typedef char TComentario[40];typedef float TValorRasgo;
typedef enum{ACENTUADA=1,NO_ACENTUADA=0,INDEFINIDO=-1,FINALIZADOR=-2}TValorSolucion;
typedef TValorRasgo TVentana[NUM_RASGOS];typedef struct
{TVentana rasgos;TFrecuencia frecuencia;TValorSolucion solucion;
TComentario comentario;} TEjemplo;
extern TEjemplo *ejemplos;
Por la lógica impuesta por los tipos de tareas a las que se iba a dedicar nuestro
MBL, la ventana de rasgos ha de estar formada por un número impar de ellos, aunque
esto es fácilmente reconfigurable. Se asume que el rasgo central es el que está en
estudio, y que los rasgos extremos de la ventana equidistan de él. La solución que figura
en el ejemplo, por tanto, se refiere a dicho rasgo central.
Capítulo 3. Implementación del Memory-Based Learner
52
Por ejemplo, para una tarea de silabicación, nos interesa que los rasgos en
estudio sean letras. Por tanto, una ventana de rasgos no es más que una ventana de letras
en un número impar, centrada sobre la letra o rasgo en estudio. Y la solución que nos
interesa conocer para cada letra es si es comienzo de sílaba o no. A continuación puede
apreciarse, en la Figura 3.1., cómo se obtendrían los distintos ejemplos a partir de cada
palabra para este caso de silabicación, con una ventana de tamaño 5.
Figura 3.1.
ÁRBOL
Ejemplo 1
Rasgos: [0 0 á r b ]
Frecuencia: 1
Solución: 1 (comienzo de sílaba)
Comentario: árbol
Ejemplo 2
Rasgos: [0 á r b o ]
Frecuencia: 1
Solución: 0 ( no comienzo de sílaba)
Comentario: árbol
Ejemplo 3
Rasgos: [á r b o l ]
Frecuencia: 1
Solución: 1 (comienzo de sílaba)
Comentario: árbol
Ejemplo 5
Rasgos: [b o l 0 0 ]
Frecuencia: 1
Solución: 0 (no comienzo de sílaba)
Comentario: árbol
Ejemplo 4
Rasgos: [r b o l 0 ]
Frecuencia: 1
Solución: 0 (no comienzo de sílaba)
Comentario: árbol
Capítulo 3. Implementación del Memory-Based Learner
53
3.3. Estructura del MBL.
Nuestro MBL está estructurado en cuatro módulos principales. Como ya se ha
explicado antes, con esta estructura dividida en módulos lo que se pretende es que si
bien uno de ellos ha de sufrir modificaciones para adaptarse a los distintos
requerimientos de las tareas en las que se pretenda emplearlo, los demás trabajan a
partir de unos datos con formato común, y pueden reutilizarse en cada caso.
Aunque más adelante se tratarán con detenimiento los módulos de los que se
compone nuestro MBL, daremos una breve descripción general de cada uno de ellos
para una mejor comprensión global del sistema:
• Módulo de traducción. Este módulo recibe como entrada texto en cualquier
tipo de formato, ya sea para que el sistema entrene con él o para evaluarlo.
Para que sea de utilidad para el resto de los módulos, este texto ha de ser
transformado en ejemplos, con el formato ya explicado en el punto anterior.
Ofrece como salida un fichero con un formato predefinido en el que cada
línea representa los distintos atributos de uno de los ejemplos obtenidos a
partir del texto de entrada.
En cada caso, y según el tipo de ejemplos que se quiera obtener, habrá
que realizar variaciones de código sobre este módulo, si bien el grueso del
módulo puede ser también reutilizado. Además, en algunos casos, se ha
creído conveniente generar una variación del módulo de traducción de textos
de prueba o de evaluación a partir del de traducción de textos de
entrenamiento.
• Módulo de entrenamiento. El módulo de traducción no nos proporciona
más que los ejemplos obtenidos a partir de un texto de entrada, sin ningún
tipo de procesamiento adicional sobre ellos. Es en el módulo de
entrenamiento donde estos ejemplos reciben un tratamiento adicional, que
Capítulo 3. Implementación del Memory-Based Learner
54
elimina redundancias. Es aquí donde el campo frecuencia de cada ejemplo es
rellenado una vez que todos los ejemplos que saca el módulo de
entrenamiento son cargados en memoria, ordenados y procesados. El
ordenamiento de todos ellos permite una búsqueda más eficiente cuando
haya que proceder con la evaluación.
La salida que proporciona el módulo de entrenamiento es lo que en
adelante llamaremos base de datos de ejemplos.
• Módulo de pesos. Una vez que se ha obtenido la base de datos de ejemplos,
necesitamos conocer cuál es el peso que se va a asignar a cada rasgo a la
hora de calcular la distancia entre ellos. Por tanto, y aunque más adelante se
explicará con más detalle por qué se ha elegido este tipo de métrica para
nuestro MBL, se asume que ésta está basada en pesos.
El módulo de pesos produce como salida un fichero con los pesos
asignados a cada rasgo, así como unas pequeñas estadísticas sobre la
entropía de cada rasgo y de la base de datos de ejemplos en conjunto.
• Módulo de evaluación. Este módulo carga en memoria los ejemplos que se
obtienen a partir del módulo de entrenamiento, carga también los ejemplos
que le proporciona el traductor para los textos de prueba o evaluación y
carga los pesos de cada rasgo que ha generado el módulo de pesos. En ese
momento, calcula la distancia entre cada ejemplo de prueba y todos los que
componen la base de datos, para determinar cuál o cuáles son sus vecinos
más próximos en el espacio de los rasgos. Cuando tenga conocimiento de
estos vecinos más próximos, extrapolará la solución de los mismos al
ejemplo de prueba, dando una solución estimada. Como con este ejemplo de
prueba nos viene también la solución sacada de los textos originales, se
compararán ambas, contabilizando como éxito o como error. Por último, nos
generará una serie de estadísticas, así como un informe de los errores
cometidos.
Éste módulo final puede también sufrir pequeñas modificaciones según la
tarea que se esté tratando, con la única finalidad de realizar alguna
Capítulo 3. Implementación del Memory-Based Learner
55
comprobación adicional con los resultados y presentar las correspondientes
estadísticas.
En la figura que se muestra a continuación se expresa de manera resumida la
inter-relación entre cada uno de los módulos, así como las características de sus entradas
y salidas. Posteriormente, se detalla el desarrollo de cada módulo por separado.
Figura 3.2. Estructura MBL.
Textos deentrenamiento
Textos deevaluación
MÓDULO DETRADUCCIÓN
MÓDULO DEENTRENAMIENTO
MÓDULO DEPESOS
MÓDULO DEEVALUACIÓN
Resultados
Estadísticas
Errores
fich_ej.dic
fich_ej_prue.dic
bd_ej.dic
pesos.dic
Capítulo 3. Implementación del Memory-Based Learner
56
3.3.1. Módulo de traducción.
El módulo de traducción actúa como la interfaz entre los textos de entrada al
sistema y nuestro espacio de ejemplos y rasgos, que es con lo que el sistema entrena y
evalúa. De ahí que sea éste módulo el único que necesita ser adaptado y modificado
para cada tarea que se pretenda resolver, según las necesidades de cada caso en
concreto. En el presente proyecto nos hemos encontrado con distintos motivos que nos
han hecho tener que generar un traductor específico. Entre ellos cabría destacar:
• El formato del texto de entrada no tiene por qué ser el mismo en todos los
casos.
Nosotros, para determinadas tareas, hemos entrenado al sistema con el
diccionario de la Real Academia Española. El diccionario del que se
disponía en el laboratorio estaba en formato lista: por cada línea, había una
palabra con su categoría asociada por cada línea.
Para otras tareas, sin embargo, se ha entrenado con la salida que
proporcionaba un preprocesador de textos en formato digital del periódico El
Mundo (del que se hablará en más detalle en capítulos posteriores). Esta
salida era un fichero en el que cada línea aparecía una palabra con
ambigüedad en su tildado, seguida de todas las categorías gramaticales a las
que podía pertenecer dicha palabra ambigua según tuviese distintas
posiciones de la tilde.
• Aunque para el resto de los componentes del sistema los ejemplos no sean
más que una colección de rasgos con una determinada solución asociada,
para el traductor el rasgo posee una simbología asociada. Dependiendo de la
tarea, un rasgo representará una cosa u otra.
Para la resolución del tildado de palabras, los rasgos que más nos
interesaban eran las letras que se encontraban alrededor de la vocal cuyo
tildado estaba en estudio (análogamente para la tarea de silabicación, pero
con ventanas de rasgos centradas tanto sobre vocales como sobre
Capítulo 3. Implementación del Memory-Based Learner
57
consonantes). Sin embargo, en una de las pruebas posteriores, se descubrió
que podía ser interesante incluir un rasgo adicional que fuese la distancia de
la sílaba en la que se encontraba dicha vocal al final de la palabra. Esto
requiere modificaciones adicionales sobre el módulo de traducción.
Para la resolución del tildado en palabras donde la existencia o no de
tilde dependía de ser de una categoría gramatical u otra, se optó por que los
rasgos fuesen palabras, y la ventana de rasgos estuviese formada por la
palabra en estudio, la anterior y la posterior.
Sin embargo, para resolver ambigüedades en la categorización gramatical
de palabras (decantarnos entre dos o más posibilidades a la hora de discernir
la categoría gramatical de una palabra), en cambio, nos interesaba que los
rangos fuesen binarios (CATEGORIA_SI o CATEGORIA_NO). La ventana
de rasgos estará compuesta por un número de rasgos predeterminados (uno
por cada tipo de categoría considerada) para la palabra que precede a la
palabra en estudio, y por otros tantos para la palabra que la sucede.
Por tanto, para tener conocimiento de la ventana de rasgos que forma
parte de un ejemplo, a veces puede ser suficiente con el conocimiento de la
palabra en estudio (pues ella misma contiene toda la información que
necesitamos para crear el ejemplo). Y otras, como es el caso de cuando los
rasgos son palabras, necesitaremos del contexto de la palabra en la frase para
poder formar los ejemplos.
También se han modificado en este proyecto, para un mismo tipo de rasgos, el
tamaño de su ventana. Una ventana de rasgos mayor implica un conocimiento mayor del
contexto que rodea al elemento en estudio, y, por tanto, se deberían obtener mejores
resultados. Sin embargo, estos cambios en el tamaño de la ventana no exigían la
modificación del módulo de traducción, sino tan sólo de uno de sus parámetros.
A continuación se describen las funciones que forman parte de este módulo. Se
explicará con más detalle el primer tipo, ya que constituye la base de principal de todos
ellos. Para el resto de tipos se describirán únicamente las funcionalidades adicionales
que presentan.
Capítulo 3. Implementación del Memory-Based Learner
58
3.3.1.1. Módulo para tareas de silabicación.
Se desarrolló un módulo de traducción para tareas de silabicación con la única
finalidad de comprobar el correcto funcionamiento del MBL cuando este estaba recién
terminado. Era una manera sencilla de ponerlo a prueba, ya que se disponía en el Grupo
de un silabicador (funciones de la librería silabica), que nos permitía evaluarlo en
términos de precisión, de velocidad de cálculo y de espacio en memoria.
En esta utilización del MBL, y por tanto, para este traductor, los rasgos son las
letras. Los ejemplos estarán formados por tanto por ventanas de rasgos (que en este
caso serán letras), centradas sobre la letra en estudio, para la que la solución es si es o
no comienzo de sílaba.
El programa es invocado con dos parámetros: el fichero de entrada y el de salida.
Aunque si se deja con los valores por defecto, el fichero de salida que producirá será
fich_ej.dic. Las principales funciones del módulo son:
• TIndiceEj EstimaNumEjemplosFich(char *directorio, char*NomFichero)
Como la variable que contiene todos los ejemplos se almacena en memoria
durante la ejecución mediante asignación dinámica, hay que realizar una estimación
del espacio que van a ocupar en memoria todos los ejemplos sin tratar. A partir del
fichero de entrada, calcula por cada palabra que lee la longitud de la misma (ya que
habrá tantos ejemplos por palabra como letras posea ésta). Para ello emplea la
función:
TIndicePal CalculaNumEjemplosPal(const char *pal)
Una vez invocada la función EstimaNumEjemplosFich , se realiza la reserva de
memoria correspondiente para todos los posibles ejemplos.
• TEjemplo *RellenaEjemplos(char *directorio,char *NomFichero)
Ésta es la función principal del traductor. Va almacenando en memoria toda la
estructura de los ejemplos que se van obteniendo del fichero de entrada,
devolviendo un puntero a dichos ejemplos. Esta función llama a su vez a otras:
Capítulo 3. Implementación del Memory-Based Learner
59
• char *LeePalabraDiccionario(FILE *fichero)
Para el fichero de entrada, va obteniendo las distintas palabras que vamos
a procesar como ejemplos. Esta función variará según el formato del fichero
de entrada que se proporcione. En el caso de la silabicación se empleaba
tanto para entrenar como para evaluar al Diccionario de la Real Academia
Española, con un formato una línea por palabra (siendo el primer campo de
la misma la palabra en cuestión).
• TVentana *DescomponeEnVentanas(char *pal)
Se le va pasando cada palabra que se va obteniendo del fichero de
entrada, y devuelve un puntero al tipo ventana de rasgos: tantas ventanas
como letras tenga la palabra, centradas cada ventana sobre cada una de las
letras de la misma. Los rasgos que forman parte de la ventana son las letras
que caen dentro de ella pasadas a formato numérico: se almacena el código
ASCII de cada una.
• TValorSolucion *CalculaSoluciones(char *pal)
Se le va pasando cada palabra que se va obteniendo del fichero de
entrada, y devuelve un puntero a las soluciones que encierra la palabra: un
array de tamaño igual a la longitud de la palabra con las soluciones de cada
letra de la palabra. La solución no es más que si es o no comienzo de sílaba.
Para ello se hace uso de la función Silabica antes mencionada:
void Silabica (char *palabra, int partir[MAX_NUM_SILABAS])
a la que se le pasa la palabra que se quiere silabicar y devuelve una array de
enteros con las posiciones en las que se encuentran los comienzos de sílabas.
Una vez que tiene las ventanas y las soluciones, no tiene más que ir rellenando
en memoria todos los ejemplos obtenidos.
• void EscribeFichero(char *directorio, char *NomFichero)
Con todos los ejemplos ya cargados en memoria, se limita esta función a ir
volcándolos en fichero con el formato apropiado.
Capítulo 3. Implementación del Memory-Based Learner
60
El formato del fichero de salida es común a todos los módulos traductores, para
que, de este modo, todo sea transparente para el resto de los módulos que componen el
MBL. Cada línea del fichero representará un ejemplo distinto, y cada campo de la línea
irá separado por espacios en blanco. Los distintos campos que describen el ejemplo en
el fichero son, por este orden:
• Número de rasgos (N): para que el resto de módulos sepa con qué tamaño de
ventana se está trabajando. Esta información es leída por ellos antes de
comenzar actividad.
• Rasgo 1, Rasgo 2,..., Rasgo N: a continuación vienen los distintos rasgos,
separados por blancos. Estos rasgos son siempre numéricos, sea cual sea su
naturaleza, para que el procesado del resto de módulos no varíe.
• Solución del ejemplo: valor de la misma, dependiendo su valor y su significado
de lo que se haya definido en cada traductor.
• Frecuencia: número de veces que se repite este mismo ejemplo entre el
conjunto obtenido del fichero de entrada. Este campo, a la salida del módulo
traductor, está siempre a 1, ya que este módulo se limita a transformar en
ejemplos el texto de entrada, pero sin realizar ningún tipo de depuración sobre
los mismos. Este campo será realmente rellenado con un valor significativo a la
salida del módulo de entrenamiento.
• Comentario: se rellena este campo con la palabra de la que se ha obtenido el
ejemplo. Tiene una funcionalidad meramente informativa, que será de utilidad
cuando se realice la depuración y evaluación del sistema.
Un ejemplo del fichero de salida podía ser el de la Figura 3.3. Dicho ejemplo es
realmente la salida de un traductor para tareas de tildado, con rasgos que son letras
Capítulo 3. Implementación del Memory-Based Learner
61
(detallado en el siguiente apartado), al que se le ha pasado como fichero de entrada el
Diccionario de la Real Academia Española.
Figura 3.3.
3.3.1.2. Módulos para tildado con rasgos letras.
El módulo de traducción explicado anteriormente fue el primero en
desarrollarse, y el más simple de todos. El módulo de traducción para tareas de tildado
se emplea cuando se quiere utilizar el MBL en la resolución de dicho tipo de tareas
partiendo, como el caso anterior, de rasgos letras. Por tanto, sólo difiere del módulo de
traducción para tareas de silabicación en que sólo las vocales de cada palabra pueden
constituir ejemplos. No tiene sentido que se incluyan como ejemplos aquellos que están
compuestos de una ventana de rasgos centrada sobre una consonante, que nunca va a
estar tildada.
5 0 0 97 0 0 0 1 a5 0 0 97 97 114 0 1 aaronita5 0 97 97 114 111 0 1 aaronita5 97 114 111 110 105 0 1 aaronita5 111 110 105 116 97 0 1 aaronita5 105 116 97 0 0 0 436 aaronita5 105 116 97 115 0 0 1 aaronitas5 97 114 111 110 105 1 1 aarónica5 111 110 105 99 97 0 1 aarónica5 105 99 97 0 0 0 1 aarónica5 105 99 97 115 0 0 1 aarónicas5 111 110 105 99 111 0 1 aarónico5 105 99 111 0 0 0 1 aarónico5 105 99 111 115 0 0 1 aarónicos5 0 0 97 98 97 0 1 ababa5 97 98 97 98 97 0 1 ababa5 97 98 97 0 0 0 1 ababa5 97 98 97 115 0 0 1 ababas5 97 98 97 98 111 0 1 ababol5 97 98 111 108 0 0 1 ababol5 97 98 111 108 101 0 1 ababoles5 111 108 101 115 0 0 1 ababoles5 97 98 97 99 97 0 1 abacaes
Capítulo 3. Implementación del Memory-Based Learner
62
Con ese fin se modificó la función DescomponeEnVentanas , para que realizara
llamadas a la nueva función:
int EsVocal (char c)
y sólo calculase las ventanas para dicho tipo de letras.
Otra diferencia importante es que las ventanas de rasgos que se van obteniendo
de cada palabra no son las letras de la misma sin más. Si dentro de una ventana cae una
vocal acentuada, este se destilda y se almacena de este modo en el ejemplo. Esto es
debido a que en el texto que se pretende evaluar, no sabemos si las palabras están
tildadas o no, o si lo están correctamente. De esta forma, cuando tenga que compararlos
con los ejemplos de evaluación (que también se destildarán por si no lo estuviesen ya),
estarán situados a la misma distancia de los tildados, y será el evaluador el que tendrá
que decidir entre todos estos ejemplos a priori equidistantes.
Ello requería que en la función DescomponeEnVentanas , a la hora de ir
recorriendo la palabra letra a letra, las vocales se destildaran con la función:
char elimina_tilde(char car)
para ser así almacenadas como rasgos.
Sin embargo, la tilde sí es tenida en cuenta a la hora calcular la solución de un
ejemplo, tanto si se están traduciendo textos de entrenamiento (y la solución se
memoriza para ese ejemplo), bien si están traduciendo textos para evaluar (y por tanto la
solución ha de ser la misma que acabe obteniendo con el MBL para que contabilice un
éxito).
Se desarrolló un módulo bastante similar al aquí expresado para intentar mejorar
la precisión del sistema en este tipo de tarea de tildado. La única diferencia estribaba en
que se quería almacenar por cada ejemplo un rasgo adicional: aparte del número impar
de rasgos que forman la ventana centrada sobre la vocal que se está estudiando, se
codificó como rasgo la distancia en sílabas desde donde se encontraba dicha vocal a la
sílaba final de la palabra. Esto idea surgió a raíz de observar los resultados que se
obtenían cuando se codificaban únicamente como rasgos las letras que formaban la
Capítulo 3. Implementación del Memory-Based Learner
63
ventana, donde se vio que era una característica que podía aportar información útil para
la resolución del problema.
Este módulo de traducción presenta adicionalmente las siguientes funciones:
TValorRasgo CalculaDistUltimaSilaba (char *pal, int num_letra)
Dada una palabra y la posición que ocupa la vocal en estudio dentro de la
palabra, la función devuelve el número de sílabas que la separan del final de la palabra.
Esta función es referenciada dentro de la función:
TVentana *DescomponeEnVentanas(char *pal)
a la hora de ir rellenando los rasgos de los ejemplos que se van generando. Una vez se
han rellenado los rasgos que componen la ventana es sí (en un número impar), se
adiciona este rasgo.
3.3.1.3. Módulo para tildado con rasgos palabras.
Como se explicará en un capítulo posterior, una parte de este proyecto consistió
en el desarrollo de una aplicación, basada en una librería ya desarrollada en el Grupo de
Tecnología del Habla: dicc.lib1. Algunas de las funciones de dicha librería fueron
modificadas con la finalidad de depurar los textos de noticias en formato electrónica del
diario El Mundo. Esta aplicación se encarga de ir separando en distintas tipologías las
ambigüedades que podía presentar una palabra respecto a la posición de la tilde. Para
ello va generando distintos tipos de ficheros de ambigüedades, pero todos ellos con el
mismo formato. Cada línea se corresponde con una palabra ambigua en su tildado,
dentro de uno de los tipos preestablecidos. El formato de cada línea es el siguiente, con
cada campo separado por un espacio en blanco: palabra en estudio, trío de palabras que
forman el contexto (anterior, central, posterior), primera posición de la tilde dentro de la
palabra encontrada (si es 0, no está tildada), categorías de la palabra con esa posición de
tilde, segunda posición de tilde y sus categorías, etc. Las categorías llevan el formato de
los textos 860, que se explicarán en un anexo posterior. Para una mayor claridad, se
1 [JIM99]
Capítulo 3. Implementación del Memory-Based Learner
64
puede observar el formato en la Figura 3.4., que se corresponde al fichero de
ambigüedades para palabras diacríticas.
Figura 3.4. Ejemplo de fichero de ambigüedades en tildado.
Una vez obtenidos estos resultados, se observó que el contexto que rodeaba una
palabra ambigua era importante, sobre todo para las palabras cuya ambigüedad dependía
de tener una categoría gramatical u otra. De ahí que se optara por codificar los rasgos de
los ejemplos como ventanas de tres palabras. La solución de estos ejemplos podría ser
únicamente: TILDADA, NO_TILDADA.
Hubo entonces que añadir varias funciones nuevas por el hecho de que los
rasgos fuesen palabras, y que éstas tuviesen que codificarse numéricamente. Cuando los
rasgos son letras, su transformación a número es inmediata, sin más que asignarle su
código ASCII. Pero el paso de una palabra a número requería que todas las palabras del
fichero de entrada, fuesen guardadas en memoria durante la ejecución. Mientras éstas se
cargaban, se les iba asignando un número (que no era más que el orden que ocupaban),
de tal forma que cuando hubiese que ir formando los ejemplos, se tuviese ya
conocimiento del rasgo numérico que le correspondía a cada palabra. Finalmente, todos
estos pares rasgo (palabra) - código numérico son también almacenados en fichero:
fich_rasgos.dic.
El El secretario 0 N00##S.M## D00##S.M## 1 R00##H.M##se No se va 2 V..01I.0.. V2902U.0.. 0 V..41W.0.. V..A3H.0.. R02##..N##se cuando se agoten 2 V..01I.0.. V2902U.0.. 0 V..41W.0.. V..A3H.0.. R02##..N##se España se tendrá 2 V..01I.0.. V2902U.0.. 0 V..41W.0.. V..A3H.0.. R02##..N##El El presidente 0 N00##S.M## D00##S.M## 1 R00##H.M##el demuestre el boicot 0 N00##S.M## D00##S.M## 1 R00##H.M##El « El único 0 N00##S.M## D00##S.M## 1 R00##H.M##se Y se agrava 2 V..01I.0.. V2902U.0.. 0 V..41W.0.. V..A3H.0.. R02##..N##se cuando se presentan 2 V..01I.0.. V2902U.0.. 0 V..41W.0.. V..A3H.0.. R02##..N##el en el boicot 0 N00##S.M## D00##S.M## 1 R00##H.M##más es más un 0 C19##N.0## B08..N.0## A11..P.F## 2 B08..N.0## B21..S.N##se si se aprueba 2 V..01I.0.. V2902U.0.. 0 V..41W.0.. V..A3H.0.. R02##..N##El El alcalde 0 N00##S.M## D00##S.M## 1 R00##H.M##el , el socialista 0 N00##S.M## D00##S.M## 1 R00##H.M##
Capítulo 3. Implementación del Memory-Based Learner
65
Las funciones nuevas que presenta este módulo son:
• TComentario *GeneraFichRasgos(char *directorio, char*NomFichero1, char *NomFichero2)
Esta función va leyendo del fichero de ambigüedades las palabras que va
a transformar posteriormente en rasgos. Por cada línea de dicho fichero
obtiene tres palabras: la palabra en estudio, la anterior y la posterior. Las
pone en minúsculas y sin tilde, si la tuviesen, para facilitar luego la
comparación. Para ello se sirve de la función PonEnMins , que se encarga de
realizar esas dos labores con la palabra que le pasen.
Por cada palabra que se lee, se comprueba que no existe ya como rasgo.
Una vez que se tienen todas las palabras-rasgos en memoria, se procede a su
volcado en fichero mediante la función:
void EscribeFichRasgos(char *directorio,char*NomFichero,TComentario *rasgos,TIndiceEj contador)
Esta función es llamada desde GeneraFichRasgos , y se le pasa un
contador con el número de rasgos generados y el nombre del fichero donde
se quiere que se vuelquen los rasgos. Por cada línea del fichero guarda la
palabra (en minúsculas y sin tilde) y el número que le corresponde. Estos
números son consecutivos, pues a cada palabra se le asigna el número de
orden en que surgió.
• int EsPalabraEnEstudio(char *pal)
Esta función se introdujo porque para el estudio de las palabras
interrogativas (cuyo tildado depende de que estén o no en frase interrogativa)
la salida que nos proporcionaba el preprocesador de textos de El Mundo para
este tipo de palabras ambiguas era un fichero único. Resultaba interesante
realizar una evaluación del MBL con cada una de ellas por separado. Por
tanto, si se quiere, se puede emplear esta función para sólo estudiar una
palabra en concreto. Pero lo mismo es aplicable a los distintos tipos de
ambigüedades detectadas, donde también interese un tratamiento
personalizado palabra a palabra, que es lo que a veces se ha llevado a cabo.
Capítulo 3. Implementación del Memory-Based Learner
66
• void LeePalabrasFichAmbiguas(FILE *fichero,TComentario*palabras)
Esta función es sustitución de la que existía anteriormente para la
captación de las palabras del texto de entrada. Ahora los textos que se
presentan al sistema tienen el formato que se aprecia en la Figura 3.4. A
partir de ellos, devuelve un array con las tres palabras que forman el
contexto de la que se estudia.
• void EscribeFicheros(char *directorio, char *NomFichero1,char *NomFichero2)
Ahora el módulo no deja sólo un fichero de salida (el fichero de los
ejemplos), sino que deja uno adicional: prob_entren.dic (si el módulo que se
está empleando es para la traducción de textos de evaluación, el fichero se
llamará prob_eval.dic). La función RellenaEjemplos se ha visto modificada
para que lleve la cuenta del número de palabras centrales tildadas y sin tildar
que está tratando a la hora de ir rellenando los ejemplos. Estos resultados se
reflejan el fichero de salida mencionado anteriormente. Lógicamente, esta
medida sólo es de utilidad cuando se esté estudiando el comportamiento con
una sóla palabra y se esté filtrando la entrada con la función
EsPalabraEnEstudio .
• TValorRasgo *CalculaVentanaRasgos(char *directorio,TComentario *palabras)
Esta función es llamada dentro de RellenaEjemplos . Simplemente, para un
trío de palabras (el contexto en estudio), devuelve la ventana
correspondiente. Para ello consulta la variable rasgos almacenada en
memoria, y que ha de contener a las tres palabras que se están pasando a la
función, con su correspondiente rasgo numérico asociado.
Finalmente indicar que para este módulo, así como para la mayoría de los ya
tratados, se mantienen dos variantes: una para tratar textos de entrenamiento y otra para
tratar textos de evaluación. Las diferencias entre ambos casi siempre son mínimas. A
Capítulo 3. Implementación del Memory-Based Learner
67
veces su única diferencia es la nomenclatura de los ficheros de salida, o su ubicación.
Pero se decidió seguir con este convenio ya que desde un principio se había seguido.
3.3.1.4. Módulo para tareas de categorización.
A partir del análisis de los textos de El Mundo, se disponía de una gran cantidad
de información acerca de la relación entre la categorización de las palabras y la
ambigüedad que alguna de ellas presenta respecto a la posición de su tilde. Para un
grupo numeroso de ellas, existía una relación unívoca entre una categoría y una posición
de la tilde. Es decir, que para dichas palabras, conocer su categorización implicaba
conocer cómo se tildaban.
Por ejemplo, la palabra tenia. Esta palabra, si no se tiene en cuenta la tilde,
puede ser nombre o verbo. Pero si sabemos que categoría gramatical tiene, sabemos
cómo se tilda, pues el hecho de ser una u otra va ligado intrínsecamente al tildado. Por
tanto, si somos capaces de detectar que es nombre, sabremos que no se tilda, pues
estaríamos hablando de la palabra tenia. Sin embargo, si fuese verbo, se trataría de la
palabra tenía, que sí se tilda. Por tanto, hay casos en los que el conocimiento de la
categoría lleva asociada la resolución de un posible problema de acentuación.
Como se disponía, gracias a anteriores proyectos, de bases de datos con textos
categorizados, se pensó en que se podía entrenar al sistema para desambiguar
categorizaciones. De este modo, una vez predicha la categoría, se podría también
estimar la posición de la tilde. En concreto, los ficheros que se emplearon para el
entrenamiento del sistema son los textos 860. Estos textos (ficheros con extensión .aps)
son listas corregidas manualmente de palabras categorizadas.
Para esta nueva problemática se requería un nuevo espacio de rasgos, que nos
proporcionase la suficiente información contextual de la palabra en estudio como para
que el sistema fuese capaz de evaluar correctamente. Se debía partir de los ficheros de
textos 860. Éstos estaban sacados de textos reales, cuyas palabras estaban categorizadas.
Capítulo 3. Implementación del Memory-Based Learner
68
Cada línea representa una palabra con su categoría, pero además una palabra en una
línea es la palabra anterior en el texto a la de la línea siguiente y la que sigue a la de la
línea anterior. Por tanto, de lo que disponíamos con estos textos era de la categoría de la
palabra y de su contexto, como se puede apreciar en la Figura 3.5.
Figura 3.5. Textos 860
Los textos 860 no presentan el número de ocurrencias que cabría desear, pero
para continuar avanzando en los resultados de corrección de tildado necesitábamos el
empleo de textos correctamente categorizados.
La solución por la que se optó fue la de que los rasgos fuesen binarios, y que
cada rasgo se asociase a una categoría para la palabra anterior y posterior. Se
distinguieron diez categorías fundamentales en los textos 860, y a cada una de ellas se
le iba a asociar un rasgo binario, que tan sólo podía tomar dos valores:
CATEGORIA_SI o CATEGORIA_NO. A continuación se enumeran estas diez
categorías. Aunque ya se describirá con detalle en el capítulo de pruebas, hubo algunos
casos de ambigüedades que requirieron algún tipo adicional de categorías, ya que se
requería un mayor detalle en la diferenciación de las mismas.
• RASGO 0: VERBO
• RASGO 1: NOMBRE
• RASGO 2: ADJETIVO
• RASGO 3: ADVERBIO
• RASGO 4: PRONOMBRE
• RASGO 5: PREPOSICIÓN
• RASGO 6: ARTICULO
Constituye V0801H.0..igualmente B03..N.0##un D01##S.M##poderoso A11..S.M##factor N00##S.M##de P00##N.0##progreso N00##S.M##
Capítulo 3. Implementación del Memory-Based Learner
69
• RASGO 7: CONJUNCIÓN
• RASGO 8: INTERJECCIÓN
• RASGO 9: MISCELÁNEA
Por tanto, a cada palabra que forme parte del contexto de la palabra en estudio,
le corresponderán ahora diez de estos rasgos binarios. Se hicieron dos tipos de pruebas,
según el número de palabras que se consideraran como contexto: por un lado se hizo un
módulo de traducción para sólo las dos palabras que flanquean la que se estudia (3_pal)
y otro que cubría dos palabras de cada lado de la central (5_pal).
A la palabra en estudio no se la representa esta vez con ningún rasgo, ya que no
se consideró que aportara información alguna. Por razones de que el tamaño total de la
ventana de rasgos tenía que ser impar, se puso un único rasgo fijo a 0 para dicha
palabra. Por tanto, en el caso de considerar como contexto a tres palabras, el tamaño de
la ventana de rasgos es de 21, y en el caso de cinco palabras, es de 41.
La solución de los ejemplos que se forman con estas ventanas es también
binaria: la idea es que el sistema sea capaz de discernir entre dos posibles categorías
para una palabra, por lo que la solución será una categoría u otra (por ejemplo,
ES_VERBO o ES_NOMBRE ante una ambigüedad verbo-nombre). Por tanto, se ideó un
tipo distinto de traductor para cada tipo de ambigüedad a resolver, con los distintos tipos
de soluciones. Así mismo, cada módulo distinto filtraría las categorías de un tipo y las
de otro a la hora de leer los textos 860 de entrenamiento, ya que, por ejemplo, para
resolver una ambigüedad verbo-nombre sólo se debe entrenar con verbos o con
nombres. No se requería un módulo distinto para cada caso, pero como ya se expresó
anteriormente, contribuía a tener un poco aislados los distintos problemas con los que se
estaba probando en cada momento.
En la Figura 3.6. se expresa lo dicho anteriormente de forma gráfica. Para un
contexto de 3 palabras, se observa cómo se forma el ejemplo correspondiente, con una
ventana de 21 rasgos: 10 para la palabra anterior, una ficticia para la central y otras 10
Capítulo 3. Implementación del Memory-Based Learner
70
para la posterior. El ejemplo corresponde a un módulo para ambigüedad del tipo verbo-
nombre, por lo que la solución del ejemplo no es más que una categoría u otra.
Figura 3.6.
En la figura anterior se observa la traducción de un ejemplo: la palabra factor,
que es nombre. Al tratarse de un contexto de tres palabras, como se ha dicho antes, va a
tener 21 rasgos el ejemplo. Así, en el fichero de ejemplos, se observa que el primer
campo es el número de rasgos (para que el resto de módulos tengan conocimiento). Las
primeras diez cifras binarias se corresponden con la palabra anterior (poderoso), que es
un adjetivo: de ahí que active el flag que se encuentra en la tercera posición. Detrás de
estas diez cifras viene una fija a 0 (la de la palabra en estudio), y después otras diez, que
corresponden la palabra siguiente, que es una preposición (de): se activa el flag que se
Textos 860...poderoso A11..S.M##factor N00##S.M##de P00##N.0##...
...poderoso 2factor 1de 5...
FicheroRasgos
...21 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 factor...
FicheroEjemplos
Capítulo 3. Implementación del Memory-Based Learner
71
encuentra en sexta posición. Los dos campos que siguen son, por este orden, la solución
(1=ES_NOMBRE) y la frecuencia (que a la salida del traductor siempre está a 1).
En la Figura 3.7. se observa la obtención del mismo ejemplo, para el mismo
problema de ambigüedad, pero tomando como contexto dos palabras a cada lado. Las
ventanas de rasgos son por tanto de 41 elementos.
Figura 3.7.
Textos 860...un D01##S.M##poderoso A11..S.M##factor N00##S.M##de P00##N.0##progreso N00##S.M##...
...un 6poderoso 2factor 1de 5progreso 1...
FicheroRasgos
...41 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 factor...
FicheroEjemplos
Capítulo 3. Implementación del Memory-Based Learner
72
Para implementar todos estos cambios en la concepción de los ejemplos, hubo
que realizar cambios en los módulos de traducción. Hasta ahora, todos los módulos que
se han explicado, podían considerarse bastante similares, pues no variaba el concepto de
lo que era un rasgo: una letra. A continuación se expresan los cambios que sufrieron las
distintas funciones, así como las que se crearon nuevas.
• TIndiceEj CalculaNumPalabrasCateg(char *directorio, charficheros[NUM_FICH_CATEG][MAX_PATH])
Como hay una nueva variable que guardar en memoria mediante
asignación dinámica, hay que conocer de antemano el espacio que debemos
reservar. Esta variable almacenará todas las palabras-rasgos que se vayan
generando. Por tanto, a la función se le pasa un array con los nombres de los
ficheros de entrada (los textos 860 se encuentran repartidos en ocho ficheros
.aps), y ésta calcula el número de palabras que contienen.
• TPalabraCateg *GeneraFichRasgos(char *directorio, charficheros[NUM_FICH_CATEG][MAX_PATH])
La diferencia entre esta función y la del módulo anterior radica en que la
entrada ahora son los ficheros de los textos 860. Se le pasan los mismos
parámetros que a la función anterior: un array con los nombres de los
ficheros de textos 860. Lo que no hace esta función es comprobar si una
palabra leída ha sido cargada ya como rasgo: las carga todas ya que de todas
se puede sacar un ejemplo (o pueden estar en el contexto de un ejemplo),
pues este tipo de textos contienen la frase completa. La función devuelve un
puntero a un nuevo tipo definido: TPalabraCateg , estructura con dos
campos, palabra y categoría. La categoría es de un tipo enumerado que
contiene a las diez categorías nombradas anteriormente.
Para la lectura de las palabras se apoya en la función
LeePalabraFichCateg.
Capítulo 3. Implementación del Memory-Based Learner
73
• TCategoria LeePalabraFichCateg(FILE *fichero, char *pal, intnum_fichero)
Esta función recibe como parámetros un string (que será donde devuelva
la palabra leída), y un entero y una variable tipo fichero que apuntan al
fichero de textos 860 que se está leyendo en el momento de ser llamada.
Devuelve la categoría de la palabra leída.
• TValorRasgo *CalculaVentanaRasgos(TIndiceEj indice)
Esta función es la encargada de formar las nuevas ventanas de rasgos
binarios, cada uno representando a un tipo de categoría para una palabra.
Recibe como parámetro un índice que le indica la posición que ocupa la
palabra en estudio en memoria, donde se encuentra almacenada con su
categoría, como palabra-rasgo. Para dicha posición, mira las palabras
anterior y posterior (o las dos anteriores y las dos posteriores), lee también la
categoría asociada a cada una de ellas y construye la ventana de rasgos (21 o
41, según el caso), que es lo que finalmente devuelve.
Esta función es llamada desde RellenaEjemplos , y los ejemplos que va
construyendo en memoria son finalmente volcados a fichero con la función
EscribeFichero .
3.3.1.5. Módulo para tareas de categorización con un entrenamiento de palabras
no ambiguas.
Como prueba final se quiso que el sistema, en la resolución de la
desambiguación de las categorías, entrenase únicamente con aquellos ejemplos que se
obtuviesen de palabras que no presentaran esta ambigüedad. Es decir, palabras que no
aparecen en los ficheros de entrada con dos o más categorías al mismo tiempo. Nos
referimos a ellas y a las que se podrían obtener si no se tuviese en cuenta la acentuación
(tildando o destildándola), ya que para el resolver el tildado de una palabra, que es el
objetivo final de todo, no distingue entre palabras que sólo se diferencien por la
posición de la tilde.
Capítulo 3. Implementación del Memory-Based Learner
74
Este módulo deberá preparar los textos de entrada en dos ficheros distintos de
ejemplos. Deberá entonces ser capaz de analizar si una palabra presenta ambigüedad, y
si es así no sacarla al fichero de ejemplos de entrenamiento, sino al de evaluación. Por
tanto genera cuatro ficheros de salida: dos para ejemplos y dos para las palabra-rasgo
(donde cada palabra tiene asociada su categoría en formato numérico). Éstos son, por
defecto, los ficheros fich_ej.dic, fich_ej_prue.dic, fich_rasgos.dic, fich_rasgos_prue.dic,
respectivamente.
Aparte de modificaciones en el código, fue necesario añadir un campo más a la
estructura del tipo de los ejemplos. Este campo, denominado ambig, no es más que un
booleano que indica si el ejemplo generado procede de una palabra ambigua o no.
typedef struct{TVentana rasgos;TFrecuencia frecuencia;TValorSolucion solucion;
TComentario comentario; bool ambig;
} TEjemplo;
La única ambigüedad estudiada, y con la que se han hecho pruebas, ha sido la de
verbo-nombre. No se ha querido repetir el experimento con más tipos ya que sólo se
trataba de una orientación sobre el comportamiento del sistema ante dichas condiciones
adversas para su correcto funcionamiento.
Las funciones nuevas o que han sufrido cambios respecto a las del módulo
anterior son:
• TPalabraCateg *GeneraFichRasgos(char *directorio, charficheros[NUM_FICH_CATEG][MAX_PATH])
Aunque esta función ya existía en el módulo anterior, hubo de ser
modificada. Ahora se guardan en memoria no sólo todas las palabras-rasgos
que se van generando (en la variable rasgos ), sino que además se va
almacenando una estructura paralela que contiene únicamente las palabras
que son o verbos o nombres, con un campo adicional que indica si tienen
ambigüedad verbo-nombre: rasgos_v_n (para posteriormente generar con
ellas el fichero de ejemplos de evaluación). Por defecto, cada palabra leída es
Capítulo 3. Implementación del Memory-Based Learner
75
introducida en rasgos ; y posteriormente se examina si ya había salido (sin
tener en cuenta su tilde). Y si es así, se observa si la categoría de entonces
coincide con la de ahora. Si cumple estas dos condiciones, se inserta también
en rasgos_v_n .
• void EscribeFichRasgos(char *directorio,char*NomFichero1,char *NomFichero2,TIndiceEj contador1,TIndiceEjcontador2)
Esta función se limita en volcar en fichero las dos estructuras de rasgos
generadas en la función anterior. Se le pasan como parámetros los nombres
de los dos ficheros, así como el número de rasgos generados de cada clase.
• TEjemplo *RellenaEjemplos()
Esta función también hubo de ser modificada. Sigue dedicándose a la
generación de los ejemplos en memoria a partir de los rasgos obtenidos de
los ficheros de entrada. Para ello se sirve de las funciones CalculaSolucion
y CalculaVentanaRasgos ya existentes. Lo que la diferencia de las
anteriores es que, como se ha comentado anteriormente, se ha añadido en
este módulo un nuevo campo a la estructura de los ejemplos (el campo
ambig), que indica si los mismos provienen o no de palabras con
ambigüedades. Para poder rellenar este campo se sirve de la función
EsAmbiguaV_N .
• bool EsAmbiguaV_N(char *pal)
A la hora de rellenar los ejemplos, es invocada para tener conocimiento
del flag de ambigüedad que habría que poner para dicho ejemplo. Para ello
se sirve de la estructura de rasgos_v_n que hay almacenada en memoria, ya
para cada palabra que se le pasa comprueba que no se encuentra allí con el
flag de ambigüedad activado. Sí es así, devuelve true, y la función
RellenaEjemplos ya sabe debe activar el flag de ambigüedad que ahora
llevan los ejemplos.
Capítulo 3. Implementación del Memory-Based Learner
76
• void EscribeFicheros(char *directorio, char *NomFichero1,char*NomFichero2)
Al tener que separar los ejemplos sin ambigüedad y con ella, es en esta
función donde se comprueba el flag puesto en la función RellenaEjemplos
y, según su posición, en esta función se separa su volcado a fichero: irán al
fichero de ejemplos de entrenamiento los que no lo tengan activado, y al de
evaluación los que sí lo tengan.
3.3.2. Módulo de entrenamiento.
La salida que produce el módulo de traducción, en cualquiera de sus variedades,
no es más que una adaptación de los textos que se sitúan a la entrada del sistema a
nuestras necesidades concretas, según el espacio de rasgos que se haya escogido para
evaluar al sistema. Pero no introduce ningún tipo de procesamiento: lee, traduce, y lo
plasma el fichero. Eso sí, lo que se obtiene ya puede ser interpretado por el resto de los
módulos del sistema.
Tampoco es que en lo que resta del sistema se vaya a realizar mucho más
tratamiento con los datos que proporciona el traductor. Como ya se explicó en el
capítulo 2 de esta memoria, la aproximación que se está siguiendo con este sistema está
basada en comportamiento, y no en conocimiento. Pero sí es cierto que los ejemplos
proporcionados por el traductor pueden llegar a ser bastante redundantes, pudiendo ser
también de un tamaño desproporcionado los ficheros que genera. Pero eso no quiere
decir que toda esa información redundante sea innecesaria, sino todo lo contrario. Si un
ejemplo se repite un elevado número de veces tendrá un peso mayor a la hora de tomar
la decisión final en la evaluación.
Se requiere que el diccionario de ejemplos sea lo más compacto posible, ya que
de otro modo no podría garantizarse un correcto funcionamiento a la hora de evaluar,
por el hecho de que todos los ejemplos de entrenamiento son almacenados en memoria
durante ese proceso final del sistema. Para ello, este módulo de entrenamiento realiza
Capítulo 3. Implementación del Memory-Based Learner
77
una única función muy específica: condensar toda la información de ejemplos
proporcionada por el traductor y presentar un nuevo conjunto de ejemplos (la base de
ejemplos) donde toda esa información quede reflejada pero ocupando el menor espacio
en memoria posible. Esa es la razón por la que ya desde el módulo traductor se
introdujo un campo en los ejemplos, que era la frecuencia o número de ocurrencias de
un ejemplo en el conjunto de entrenamiento.
En la Figura 3.8. se puede apreciar la transformación que este módulo provoca
en el fichero de ejemplos: todos aquellos que aparezcan con la misma ventana de rasgos
y con la misma solución, son considerados como el mismo ejemplo. A éste se le
aumenta la frecuencia para que no se pierda la información del número de ejemplos
originales que existían a la a la entrada del sistema. El caso de la figura se corresponde
con el de ejemplos con una ventana de rasgos de tres elementos. Dichos rasgos son
palabras, y el estudio se centra en distinguir el tildado de la palabra sobre/sobré, que
pertenecen a un tipo de ambigüedad que en nuestras pruebas hemos llamado
preposición-otros.
Figura 3.8. Funcionamiento del módulo de entrenamiento.
3 0 1 2 1 1 sobre3 16 1 4 1 1 sobre3 27 1 28 1 1 sobre3 31 1 15 1 1 sobre3 33 1 4 1 1 sobre3 40 1 15 1 1 sobre3 44 1 45 1 1 sobre3 54 1 15 1 1 sobre3 55 1 4 1 1 sobre3 65 1 4 1 1 sobre3 65 1 4 1 1 sobre3 66 1 4 1 1 sobre3 67 1 15 1 1 sobre3 6 1 2 1 1 sobre3 70 1 4 1 1 sobre3 71 1 72 1 1 sobre3 91 1 2 1 1 sobre3 0 1 2 1 1 sobre3 108 1 4 1 1 sobre...
3 0 1 2 1 2 sobre3 16 1 4 1 2 sobre3 27 1 28 1 1 sobre3 31 1 15 1 1 sobre3 33 1 4 1 2 sobre3 40 1 15 1 1 sobre3 44 1 45 1 1 sobre3 54 1 15 1 2 sobre3 55 1 4 1 1 sobre3 65 1 4 1 5 sobre3 66 1 4 1 1 sobre3 67 1 15 1 1 sobre3 6 1 2 1 4 sobre3 70 1 4 1 1 sobre3 71 1 72 1 2 sobre3 91 1 2 1 1 sobre3 108 1 4 1 2 sobre...
MÓDULO DEENTRENAMIENTO
fich_ej.dic
bd_ej.dic
Capítulo 3. Implementación del Memory-Based Learner
78
La nomenclatura de este módulo no debe confundirnos. No todo el proceso de
entrenamiento que realiza el sistema se lleva a cabo aquí. De todas formas, debe quedar
claro qué es para un MBL el entrenamiento: un simple almacenamiento en memoria, sin
ningún tipo de abstracción de los datos de entrada, para una posterior búsqueda en dicha
memoria de los ejemplos más similares al que estoy evaluando. La mayor complejidad
del sistema reside, sin duda, en el cálculo de los pesos que van a modelar la función
distancia que se va a emplear en la evaluación.
El módulo se invoca con dos parámetros, que son el fichero de entrada y de
salida. Si no se especifican, se toman los nombre por defecto: fich_ej.dic y bd_ej.dic,
respectivamente. A continuación describimos brevemente las funciones que
implementan este módulo:
• TEjemplo *RellenaEjemplos(char *directorio,char *NomFichero)
Esta función recoge en memoria todos los ejemplos que recibe del
fichero proporcionado por el traductor. Para realizar la correspondiente
reserva de memoria invoca a la función EstimaNumEjemplosFich , que
devuelve el número de ejemplos que se encuentran el fichero de entrada.
Este número va a diferir bastante de la cantidad de ejemplos con la que nos
vamos a encontrar a la salida del módulo. La función que emplea para la
obtención desde fichero es LeeEjemplo .
Según va cargándolos en memoria, comprueba si un ejemplo igual al que
se dispone a cargar ha sido cargado ya antes. Para ello se sirve de la función
ExisteEjemplo , que le devuelve un booleano indicando la existencia o no
existencia del mismo.
• TEjemplo LeeEjemplo(FILE *fichero, TIndiceEj i)
Devuelve una variable del tipo ejemplo por cada línea del fichero. Cada
línea del fichero está formada por la sucesión ordenada de los campos que
forman dicho ejemplo: número de rasgos, rasgos, solución, frecuencia y
comentario. Emplea las siguientes funciones para ello: LeeEntero (para leer
Capítulo 3. Implementación del Memory-Based Learner
79
el número de rasgos y la solución) y LeeFlotante (para la lectura de los
rasgos y de la frecuencia).
• TExistenciaEjemplo ExisteEjemplo(TEjemplo ejemplo)
Dado un ejemplo candidato a ser cargado en memoria, esta función busca
que no hay otro que haya sido cargado ya y que sea igual a éste. Para
descubrir si dos ejemplos son iguales se sirve de la función
comparaEjemplos . La búsqueda entre los que tiene cargados en memoria la
realiza mediante la función lfind : realiza una búsqueda lineal, pero se
observó que los tiempos de ejecución de ese módulo eran pequeños, por lo
que no se realizó ninguna modificación en el método de búsqueda. Además,
no tiene sentido ir ordenando los ejemplos en este módulo, pues
continuamente se están añadiendo nuevos, y habría que reordenarlos de
nuevo.
• int comparaEjemplos(const void *x, const void *y)
La misión de esta función se reduce a comparar los dos ejemplos que se
le pasan como parámetros. Si tienen la misma solución y todos sus rasgos
coinciden, se considera que son iguales; si no cumple alguna de estas
condiciones, son distintos. Como no se va a realizar ningún tipo de
ordenamiento en este módulo, no es necesario que devuelva más que sin
iguales o distintas (no es necesario que para este último caso distinga cuál es
mayor o menor).
Una vez todos los ejemplos están ya cargados en memoria con sus respectivas
frecuencias actualizadas, éstas son volcadas en fichero por la función EscribeFichero .
Capítulo 3. Implementación del Memory-Based Learner
80
3.3.3. Módulo de pesos.
Una vez se ha pasado el módulo de entrenamiento y se ha obtenido la base de
datos de ejemplos con la que el sistema ha de entrenar, se necesita calcular los pesos
que se han de aplicar a cada uno de los rasgos a la hora de calcular distancias entre
ejemplos. Esto se debe a que la distancia que se ha escogido para que el sistema evalúe
es una distancia por pesos.
Las distancias entre ejemplos son computadas en el módulo de evaluación,
cuando, para cada ejemplo del que se quiere conocer su solución estimada, se calcula la
distancia entre él y todos los ejemplos de la base. De este modo, sabemos cuál es el que
está situado más cerca con respecto a una métrica dada. Será al hablar de dicho módulo
cuando se describa la métrica adoptada. Pero al tratarse de una métrica por pesos, es en
este módulo donde se implementa el cómputo de los pesos para cada rasgo, según el
tipo de pesos escogido finalmente.
El comportamiento de este módulo, a la vista de los resultados que proporciona,
es fundamental para el análisis global del sistema. Es tras la ejecución de éste cuando
conocemos la importancia que se va a asignar a cada rasgo a la hora de calcular
distancias, y es por ello por lo que conoceremos si la elección de los rasgos es la
apropiada. Si se considera que, para la resolución de una determinada tarea, un
parámetro en concreto puede contribuir a mejorar los resultados por aportar más
información sobre el contexto en el que se está estudiando, es lógico que este parámetro
sea incluido como rasgo, y, por tanto, como elemento de los ejemplos que se han de
mantener en memoria en el MBL. Pero es cuando conocemos los datos de los pesos
cuando sabemos si en realidad dicho parámetro es o no significativo, o si la información
que aporta debe ser tenida en cuenta.
De entre los tipos de pesos que se barajaron en el segundo capítulo de la presente
memoria, finalmente se optó por los pesos IG, por ganancia de información. Es obvio
que para obtener unos resultados aceptables, no se pueden considerar a todos los rasgos
Capítulo 3. Implementación del Memory-Based Learner
81
con la misma importancia. De ahí la necesidad del cálculo de los pesos de los mismos
para el posterior cálculo de distancias.
Para realizar la asignación a cada rasgo de su peso por ganancia de información,
se habrá de tratar cada uno aisladamente, y medir con cuánta información contribuye a
nuestro conocimiento de la solución. Se interpreta el conjunto de entrenamiento como
una fuente de información capaz de generar distintos mensajes (las distintas soluciones
o etiquetas de categoría) con una determinada probabilidad. La entropía de información
de dicha fuente puede ser comparada para cada rasgo con la entropía de información
media de la fuente de información cuando el valor de dicho rasgo es conocido. Aquellos
rasgos que más hagan reducir la entropía serán los que nos aporten una mayor
información.
No se consideró necesario el empleo de pesos IB1-IG, por ratio de ganancia, ya
que es una versión normalizada de los pesos IG que se adapta mejor a situaciones en las
que los valores que pueden tomar los rasgos pueden variar mucho de unos a otros. Es
decir, que aquellos rasgos que tomasen un mayor número de valores a lo largo del
entrenamiento eran los que iban a ser beneficiados a la hora de asignarles el peso. Pero
desde un principio, los rasgos que se preveía que más se iban emplear eran las letras que
formaban el contexto en el que otra letra estaba siendo estudiada. Y los valores que
podían tomar los distintos rasgos eran siempre los mismos, pues el rango de los que
esos valores se podían tomar es el abecedario. En cualquier caso, podría ser interesante,
como una futura mejora, probar el sistema con un nuevo módulo de pesos que
implemente los pesos IB1-IG.
Las ecuaciones que expresan el cálculo de los pesos IG y que se muestran a
continuación, fueron detalladas en su momento en el segundo capítulo. Se trata de las
ecuaciones 2.3, 2.4. y 2.5. El peso de cada rasgo viene dado por la siguiente expresión:
[ ] ffDHDHfG ω=−= )()()(
Capítulo 3. Implementación del Memory-Based Learner
82
donde H(D) representa la entropía de información de la base de ejemplos, y se calcula a
través de la siguiente expresión:
donde pi es la probabilidad de cada posible solución. H(D[f] ) es la entropía de
información de un determinado rasgo:
Con la expresión D[f=v] nos referimos a aquellos patrones en la base de ejemplos
que tienen valor v para el rasgo f. V es el conjunto de posibles valores para el rasgo f.
Finalmente, |D| es el número de patrones en la (sub)base de ejemplos de entrenamiento.
Es decir, el peso de cada rasgo vendrá determinado por la ganancia de
información de dicho rasgo, y se obtendrá de computar la diferencia de entropía entre
las situaciones con y sin el conocimiento del valor de dicho rasgo. Para cada rasgo se
calcula entonces la ganancia de información que obtenemos si conocemos su valor. Para
ello se obtiene la entropía de información media para este rasgo, y se le resta a la
entropía de información de la base de ejemplos.
Las funciones que implementan los cálculos de los pesos serán detalladas más
adelante, junto al resto de las funciones que componen el módulo. Éste es invocado con
los nombres de tres ficheros como parámetros: el fichero que contiene la base de datos
de ejemplos y que tomará como entrada, el fichero resultado, con los pesos de cada
rasgo, y un fichero con estadísticas del proceso de obtención de los mismos. Si no se le
pasa ningún parámetro, los nombres por defecto son tomados: bd_ej.dic, pesos.dic y
pesos.est, respectivamente.
ip
i ppDHi
2log)( ∑−=
[ ] [ ][ ]
[ ]∑∑∈
=∈
== =⋅==
Vvivf
Vv
vf
vff
i
i
i
i
ivfPDH
D
DDHDH )()()()(
Capítulo 3. Implementación del Memory-Based Learner
83
A continuación se muestra una figura (Figura 3.9.) que resume la labor del
presente módulo. Se trata de ejemplos con ventanas de tres rasgos, los cuales
representan palabras, e intentan resolver tareas de tildado para la palabra sobre.
Figura 3.9. Funcionamiento del módulo de pesos.
El fichero del que posteriormente se servirá el evaluador se compone de una
única línea formada, a su vez, por una serie de campos separados por espacios en
blanco. El primer campo es el número de rasgos con los que se está trabajando, y los
campos posteriores se corresponden con cada uno de los pesos que se van a asignar a
cada rasgo.
• TEjemplo* CargaEjemplos(char *directorio, char *NomFichero)
El módulo de pesos ha de comenzar cargando el fichero diccionario de
ejemplos en memoria. Ahora ya sí que este fichero es la base de ejemplos,
pues es la salida del módulo de entrenamiento. Para realizar la pertinente
reserva de memoria, la función CalculaNumEjemplos le permite conocer el
número de ellos que contiene la base de ejemplos. Una vez los ha cargado
todos en memoria, los ordena mediante la función qsort , que se sirve a su
vez de comparaEjemplos . Esta función devuelve si un ejemplo es menor
que, mayor que o igual a otro, a diferencia de la función homónima del
3 0 1 2 1 2 sobre3 16 1 4 1 2 sobre3 27 1 28 1 1 sobre3 31 1 15 1 1 sobre3 33 1 4 1 2 sobre3 40 1 15 1 1 sobre3 44 1 45 1 1 sobre3 54 1 15 1 2 sobre3 55 1 4 1 1 sobre3 65 1 4 1 5 sobre3 66 1 4 1 1 sobre3 67 1 15 1 1 sobre3 6 1 2 1 4 sobre3 70 1 4 1 1 sobre3 71 1 72 1 2 sobre3 91 1 2 1 1 sobre3 108 1 4 1 2 sobre...
MÓDULODE PESOS
BD ejemplos3 53310624.000000 157351.531250 14728698.000000
Peso rasgo nº 0 : 53310624.000000Ganancia Entropía rasgo nº 0 : -53310624.000000Peso rasgo nº 1 : 157351.531250Ganancia Entropía rasgo nº 1 : -157351.531250Peso rasgo nº 2 : 14728698.000000Ganancia Entropía rasgo nº 2 : -14728698.000000Entropía Información de la BD: 0.000000
Fichero de pesosAl evaluador
Fichero informativo
Capítulo 3. Implementación del Memory-Based Learner
84
módulo de entrenamiento, que tan sólo distinguía si dos ejemplos eran
iguales o distintos, aunque ambas funciones se pueden fundir en una.
• void InicializaPesos()
Ésta es la función principal del módulo. Desde ella se realizan todas las
tareas, excepto el volcado al fichero diccionario de pesos. Fundamentalmente
se encarga de realizar los cálculos pertinentes (mediante llamadas a otras
funciones) para obtener los valores de los pesos de cada rasgo. Una vez los
ha conseguido, se encarga de cargarlos todos en una variable global.
Finalmente, escribe en fichero un resumen con los resultados obtenidos. Este
fichero tiene una función meramente informativa.
Como se ha explicado en un párrafo anterior, para calcular el peso se
necesita primero conocer la entropía de información de la base de ejemplos.
Y posteriormente, para cada rasgo, obtener su entropía media de
información. Restando ambas, se obtiene el valor de los pesos.
• TEntropia CalculaEntropiaInfoBD()
De esta función es de la que se sirve la función InicializaPesos para
calcular la entropía de información de la base de ejemplos. Para ello, a través
de las funciones CalculaMinSoluc y CalculaMaxSoluc , recorre todo el
diccionarios de ejemplos que tiene ya cargado en memoria y obtiene la
solución mínima y máxima. De este modo ya tiene el rango de las posibles
soluciones y puede obtener para cada una de ellas las frecuencias absolutas,
para que, dividiéndolas entre el número total de ejemplos, se conozca la
frecuencia relativa (probabilidad) de cada solución.
Cabe recalcar que en todos los casos que se han tratado a lo largo de este
proyecto, las soluciones han sido siempre binarias. Sin embargo este módulo
se implementó para que fuese capaz de adaptarse a cualquier número de
soluciones.
A cada probabilidad calculada se le aplica la función Entropia , que, para
el parámetro p que se le pase, realiza el siguiente cálculo: -p * log2(p).
Capítulo 3. Implementación del Memory-Based Learner
85
Realizando un sumatorio para cada valor de la solución, devuelve finalmente
H(D).
• TEntropia CalculaEntropiaRasgo(TIndiceRasgos NumRasgo)
Esta función recibe como parámetro el número del rasgo (rasgo f) del que
se quiere obtener su entropía, H(D[f] ). Para ello recorre todo el rango de
posibles valores que pueden tomar los rasgos (V), y para cada uno de ellos
(vi), crea un subconjunto de ejemplos para los que el rasgo f toma ese valor vi
en concreto. Los conjuntos de ejemplos se crean con la función
CreaConjuntoEj .
Con la cardinalidad de este subconjunto, podemos conocer la
probabilidad de que el rasgo f tome el valor vi: P(f=vi). Para obtenerla, se
emplea la función cardinal , a la que se le pasa un subconjunto de ejemplos
y devuelve el número de ellos que los forman. Una vez que se conoce dicha
probabilidad y la entropía de la base de ejemplos cuando restringimos a que
el rasgo f tome el valor vi (H(D[f=vi] )), podemos realizar el sumatorio en todo
V y tener así la entropía de información del rasgo f, H(D[f] ).
• TEjemplo *CreaConjuntoEj(TIndiceRasgos NumRasgo,TValorRasgoValorRasgo)
Esta función recibe como parámetros un número de rasgo en concreto (i),
y un posible valor que pueda tomar (v). Se encargará de recorrer toda la base
de ejemplos y devolver un subconjunto de ellos para los que el rasgo que
ocupa la posición i toma el valor v.
• void EscribeFicheroPesos()
Finalmente, cuando se tienen cargados en variables todos los pesos de
cada uno de los rasgos, se vuelcan al fichero diccionario de pesos, pesos.dic.
Este fichero contiene una única línea, en la que cada campo está separado
por un espacio en blanco. Primero viene el número de rasgos, y, después, los
pesos de cada rasgo. Este fichero será empleado por el módulo de
evaluación, para poder calcular las distancias entre los distintos ejemplos de
evaluación y los de entrenamiento.
Capítulo 3. Implementación del Memory-Based Learner
86
3.3.4. Módulo de evaluación.
Una vez se ha obtenido el diccionario o base de datos de ejemplos a través del
entrenamiento, y se conocen los pesos de cada rasgo para poder calcular las distancias
entre ejemplos, ya sólo restaría evaluar el sistema. Para ello se le presentan unos
ejemplos de prueba, y el sistema dará una solución estimada para cada uno de ellos. En
este módulo se evaluará si dichas predicciones son correctas o no, y se dan las
estadísticas de los resultados globales del sistema.
La métrica escogida para el cálculo de la distancia entre ejemplos es la métrica
de solapamiento, que ya se explicó en el segundo capítulo de esta memoria. A esta
métrica se le aplican los pesos IG explicados n el módulo anterior, resultando una
ecuación para la distancia entre ejemplos ya vista anteriormente (Ecuación 2.6.):
La δ que aparece en la ecuación viene dada por la Ecuación 2.2.a.:
Como ya se ha explicado en apartados anteriores, los rasgos que vamos a
manejar son finalmente codificados como números. Si son letras, se codifican con su
código ASCII. Si son palabras, se almacena un fichero con todas las palabras que han
aparecido en el entrenamiento y se realiza una correspondencia palabra-rasgo en el
mismo, para así poder conocer la codificación en rasgos de toda palabra. Pero los
rasgos en sí con los que se ha trabajado no son numéricos, sino simbólicos. No tiene
ningún sentido que a la hora de calcular la distancia entre ejemplos, se considere que
una r está más lejos de una a que una b, por el mero hecho de que sí lo estén sus
codificaciones ASCII. Sólo existen, pues, dos posibilidades: que los rasgos en una
∑=
⋅=∆n
iiii yxfGYX
1
),()(),( δ
≠=
−−
=
ii
ii
ii
ii
ii
yxSi
yxSi
noSinuméricoesSiminmax
yx
yx
1
0
:.
),(δ
Capítulo 3. Implementación del Memory-Based Learner
87
determinada posición del ejemplo coincidan o que no. Si dos rasgos (palabras, letras,...)
no coinciden, se considera una δ igual a la unidad, y si coinciden, la δ entonces es cero.
No se admite otro tipo de distancias entre rasgos, pues éstos son simbólicos. La
ecuación de la distancia entre rasgos que se ha aplicado en el módulo de evaluación
quedaría entonces de la siguiente manera:
En la Figura 3.10. se muestra gráficamente el modo de funcionamiento de este
módulo.
Figura 3.10. Funcionamiento del módulo de evaluación.
Anteriormente se había dicho que todos los módulos, excepto el de
entrenamiento, eran comunes a todas las tareas, y por tanto, reutilizables. Esto no es del
todo cierto para el módulo de evaluación. Se ha empleado el mismo módulo siempre
que se ha querido conocer la precisión del sistema a la hora de resolver una solución
para un ejemplo de prueba.
Pero como ya se contó en las descripciones de los módulos de traducción, se
hizo una prueba en la que, para resolver el tildado de palabras con ambigüedades
dependientes de su categoría, se entrenaba al sistema para resolver para cada palabra
≠=
=ii
iiii yxSi
yxSiyx
1
0),(δ
BD ejemplos
bd_ej.dic
Fichero de pesos
MÓDULO DEEVALUACIÓN
pesos.dic
Fichero de ejemplosde evaluación
fich_ej_prue.dic
Estadísticas
Resultados de laevaluación
estadis.est
evaluacion.est
Capítulo 3. Implementación del Memory-Based Learner
88
su categoría. De este modo, una vez estimada su categoría, seríamos capaces de
determinar su tildado. Si se hubiese empleado para esta tarea el evaluador estándar, éste
simplemente nos hubiese indicado la precisión del sistema a la hora de adivinar la
categoría de las palabras. Y a nosotros nos interesaba, especialmente, conocer qué tasa
de error cometeríamos a la hora de adivinar el tildado de dichas palabras ambiguas, una
vez resuelta la ambigüedad gracias a su categorización.
Además, hubo otro caso en el que se creó un módulo de evaluación especial.
Cuando se estaba analizando el tildado de palabras ambiguas individualmente, mediante
rasgos-palabras, se vio que en muchos casos la proporción entre los ejemplos donde
dichas palabras aparecían tildadas y sin tildar era bastante desproporcionada. Se pensó
entonces que se podrían mejorar los resultados si, además de evaluar con el MBL, se
aplicaba una técnica mixta de MBL y de solución más probable, mediante la
elaboración de otro módulo de evaluación. La técnica consistiría en lo siguiente: se
aplica el MBL de la forma habitual, pero si la estimación que realiza no la hace a partir
de ejemplos que se encuentren a distancia nula (es decir, si no se ha entrenado con
ningún ejemplo igual), entonces la solución que se adopta es la solución más probable.
Ésta se conoce porque en el módulo de traducción para rasgos se llevaba la cuenta de las
palabras de entrenamiento tildadas y sin tildar, y se salvaba en fichero (prob_entren.dic)
para su posterior utilización por este nuevo módulo de evaluación.
Por tanto, para estos últimos casos con los que se probó el sistema, se generaron
dos nuevos módulos de evaluación, basados en el que se había empleado anteriormente,
pero con algunas peculiaridades. A continuación se detallará el desarrollo de dicho
módulo estándar, para después, especificar las peculiaridades de los módulos especiales.
• TEjemplo* CargaEjemplos(char *directorio, char *NomFichero)
Al igual que el módulo de pesos, el módulo de evaluación ha de
comenzar cargando el fichero diccionario de ejemplos en memoria. Para
realizar la pertinente reserva de memoria, la función CalculaNumEjemplos
le permite conocer el número de ellos que contiene la base de ejemplos. Una
vez los ha cargado todos en memoria, los ordena mediante la función qsort ,
Capítulo 3. Implementación del Memory-Based Learner
89
que se sirve a su vez de comparaEjemplos . Esta función devuelve si un
ejemplo es menor que, mayor que o igual a otro, a diferencia de la función
homónima del módulo de entrenamiento, que tan sólo distinguía si dos
ejemplos eran iguales o distintos, aunque ambas funciones podrían ser
unificadas.
• TDistancia *InicializaPesos(char *directorio, char*NomFichero)
Para poder realizar el cómputo de las distancias, el módulo de evaluación
ha de conocer los pesos de cada rasgo. Para ello, tendrá que cargarlos del
fichero diccionario de pesos que generó el módulo anterior. Mediante esta
función, dichos pesos son leídos desde fichero y cargados en una variable,
para posibilitar su posterior utilización a lo largo de la ejecución del módulo.
• TValorSolucion DecisionSolucion(TValorRasgo *rasgos)
Según el módulo va leyendo ejemplos de evaluación, para cada uno de
ellos ha de estimar una solución. Eso lo hace con esta función, que para una
ventana de rasgos que se le pasa como parámetro, devuelve la solución que
el sistema estima que es la correcta. Para ello se sirve de las funciones que a
continuación se explicarán más en detalle. Llama a BuscaSoluciones para
obtener el conjunto de ejemplos que se encuentran situados a una distancia
menor de la ventana de rasgos que se le pasa como parámetro (la del ejemplo
de evaluación). Posteriormente se sirve de las funciones CalculaMinSoluc y
CalculaMaxSoluc para conocer el rango de variación de las soluciones de la
base de ejemplos (a partir de la solución mínima y máxima). Y por último,
llama a la función BuscaSolMasProbable para que en ella se decida entre
los distintos candidatos que han dado una distancia más pequeña. El
resultado que obtenga de esta última función es la solución que finalmente
devuelve.
Capítulo 3. Implementación del Memory-Based Learner
90
• TEjemplo *BuscaSoluciones(TValorRasgo *rasgos)
Esta función devuelve los ejemplos que han dado distancia mínima con la
ventana de rasgos que se le pasa como parámetro. Para ello se sirve de la
función BuscaKMinimos :
• TIndiceEj *BuscaKMinimos(TValorRasgo *rasgos)
A esta función le pasa como parámetro la ventana de rasgos, y ésta le
devuelve una lista de índices (la posición que ocupan dentro de la
variable global ejemplos ) de los ejemplos que han dado una distancia
mínima con la ventana de rasgos. En primer lugar realiza una búsqueda
binaria entre los ejemplos que tiene cargados en memoria, mediante la
función bsearch . Si encuentra alguno, se queda con su índice como
primer índice de distancia mínima (de hecho, el ejemplo que ocupa esa
posición tiene distancia cero con nuestra ventana de rasgos). Si no ha
encontrado ninguno, entonces se sirve de la función BuscaMinimo :
• TIndiceEj BuscaMinimo(TValorRasgo *rasgos)
Esta función es empleada por la anterior en aquellos casos en los
que no haya encontrado en la base de datos ningún ejemplo igual al
que estamos buscando, pues si no ya se hubiese encontrado
anteriormente en la búsqueda binaria. Sólo en ese caso hay que
recurrir a ella, ya que la búsqueda que realiza esta función es mucho
más lenta que la binaria que ya ha realizado.
Lo que realiza la función es recorrer toda la estructura de
ejemplos, calculando la distancia entre cada uno de ellos y nuestra
ventana de rasgos. La función devuelve el índice del primer ejemplo
que se encuentra a una distancia mínima. El comportamiento de esta
función podría mejorarse mediante la compilación en árbol y
realizando la búsqueda con programación dinámica.
Para ello se sirve de la función distancia , que implementa la
ecuación de la distancia entre ejemplos ya descrita (∆(X,Y)). A su
Capítulo 3. Implementación del Memory-Based Learner
91
vez, esta función emplea DistanciaRasgos , que implementa la
distancia entre rasgos (δ(xi,yi)).
Una vez que se ha obtenido el índice del primer ejemplo de la lista de
los ejemplos de entrenamiento que ha dado distancia mínima, bien
mediante una función de búsqueda exacta o bien de distancia mínima, se
calcula dicha distancia mínima. Entonces recorre la estructura de
ejemplos a partir del primer ejemplo de distancia mínima, y si encuentra
alguno cuya distancia a nuestra ventana de rasgos sea igual a la mínima
(para ello se sirve también de la función distancia ), entonces su índice
lo mete también en la lista de índices de distancia mínima. Hay que tener
en cuenta que la búsqueda la hace a partir del primer índice mínimo hacia
delante y hacia atrás, pues la búsqueda que realiza la función bsearch no
garantiza que el que haya encontrado sea el primero. Esta lista es la que
finalmente devuelve a la función BuscaSoluciones .
• TValorSolucion BuscaSolMasProbable(TFrecuencia*contador,TIndiceEj rangoSoluc,TIndiceEj minSoluc)
Esta función es empleada por la función DecisionSolucion una vez que
tiene que decidir entre los distintos ejemplos que se encuentran a una
distancia mínima de nuestra ventana de rasgos. Antes de invocarla,
DecisionSolucion obtiene una lista de contadores, que no es más que una
lista con las frecuencias de los ejemplos que han dado distancia mínima. Esta
lista de frecuencias o contadores es la que pasa a BuscaSolMasProbable
como parámetro, además de la solución mínima y del rango de posibles
soluciones, que ya había obtenido anteriormente. La función dará como
solución, finalmente, la solución del ejemplo, entre los que han dado
distancia mínima, que posee una mayor frecuencia: es decir, la solución
más probable que se dé entre los ejemplos que han dado una distancia
mínima. Hay que tener cuidado de no confundir ésta con la solución más
probable de toda la base de datos de ejemplos; coincidirán muchas veces
ambas soluciones, pero no tienen por qué.
Capítulo 3. Implementación del Memory-Based Learner
92
Para evitarse este paso, una posible mejora se conseguiría si se hubiese
compactado anteriormente la base de datos: es decir, los ejemplos con los
mismos rasgos pero con solución distinta se podrían haber reducido a uno
sólo, donde la solución de éste último fuese la del ejemplo de partida que
tuviese una frecuencia mayor.
Por último, si la solución que se ha estimado después de todo este proceso
coincide con la que traía el ejemplo de evaluación, se contabiliza como éxito. Tanto si
se ha tenido éxito como si no, se guarda en el fichero de evaluación, para el posterior
análisis de los resultados y, especialmente, de los errores. Dicho fichero (evaluacion.est)
posee el siguiente formato: una línea por ejemplo evaluado, con una serie de campos
separados por espacios en blanco (lo que significa cada campo, va explicado en una
cabecera que se añade al comienzo del fichero). Si se ha errado con un ejemplo, al final
de la línea se añade un campo adicional: _ERROR. En la Figura 3.11. se muestra un
ejemplo de dicho fichero, obtenido de la evaluación del tildado de la palabra
sobre/sobré en exclusiva. Como se puede apreciar en el ejemplo, en todos los casos se
ha acertado que se trata de la palabra sobre, sin tildar. Y en la Figura 3.12., el fichero de
resultados (a nivel de aciertos) para este mismo ejemplo.
Figura 3.11. Fichero análisis de la evaluación.
Num_Rasgos Rasgo1 ... RasgoN Soluc_Leida Soluc_Estimada Frec_SolMasProb Dist_Min Pal_leida
3 8 9 7 1 _1 4548644 68196672.000000 sobre3 13 9 5 1 _1 4548644 68196672.000000 sobre3 14 9 15 1 _1 4548644 53467976.000000 sobre3 14 9 16 1 _1 4548644 68196672.000000 sobre3 20 9 21 1 _1 4548644 68196672.000000 sobre3 22 9 7 1 _1 4548644 14886050.000000 sobre3 23 9 16 1 _1 4548644 68196672.000000 sobre3 6 9 5 1 _1 4548644 14886050.000000 sobre3 6 9 31 1 _1 4548644 14886050.000000 sobre3 33 9 34 1 _1 4548644 14886050.000000 sobre3 36 9 5 1 _1 4548644 68196672.000000 sobre3 6 9 37 1 _1 4548644 14886050.000000 sobre3 38 9 37 1 _1 4548644 68196672.000000 sobre3 44 9 5 1 _1 4548644 14886050.000000 sobre3 45 9 46 1 _1 4548644 14886050.000000 sobre3 58 9 16 1 _1 4548644 68196672.000000 sobre...
Capítulo 3. Implementación del Memory-Based Learner
93
Figura 3.12. Fichero de resultados.
3.3.4.1. Módulo mixto MBL-probabilidades.
En este módulo, se deja funcionar al evaluador como se ha descrito hasta ahora:
se invoca desde el programa a la función DecisionSolucion para que evalúe una
solución, y para ello se le pasa la ventana de rasgos del ejemplo que en cada momento
se está evaluando.
La diferencia radica en que, una vez que se han cargado en memoria los pesos de
los rasgos, se calcula la solución más probable en los ejemplos de entrenamiento para
dicho ejemplo, a través de la función CalculaSolMasProbEntren . Pero llegado al
punto anterior, cuando el sistema ya ha decidido una solución estimada, si la distancia
mínima a la que se encuentran los ejemplos más cercanos es distinta de cero (es decir, si
no se ha entrenado con el ejemplo que se está evaluando, y por tanto no se encuentran
ejemplos de entrenamiento que estén a distancia cero), entonces se toma la solución
más probable. Por supuesto, siempre que ésta no sea INDEFINIDO, ya que querría
decir que se han encontrado tantos ejemplos con la solución tildada que sin tildar.
El formato del fichero del que toma el módulo la información para calcular cuál
de las dos soluciones es la más probable se muestra en la Figura 3.13. Este fichero es
generado por el módulo traductor. Hay que tener en cuenta que esta variación del
módulo de traducción está pensada únicamente para cuando se han analizado palabras
aisladas (considerando los rasgos palabras), procedentes de los ficheros de
ambigüedades que se obtienen del preprocesamiento de los textos del diario El Mundo.
Estadísticas de la evaluación: Tasa de aciertos (en%): 100.000000 (218 de 218)
Capítulo 3. Implementación del Memory-Based Learner
94
Figura 3.13.
• TValorSolucion CalculaSolMasProbEntren(char *directorio, char*NomFichero)
A esta función se le pasa como parámetro el nombre del fichero donde el
traductor dejó las estadísticas del número de veces que una determinada
palabra estaba tildar y sin tildar. A partir de dichos datos, calcula cuál de las
dos soluciones es más probable, devolviendo como resultado una de las dos:
si está tildada o no. Si la probabilidad de que se den las dos soluciones es la
misma, entonces devuelve INDEFINIDO, para que entonces no se tenga en
cuenta esta decisión por probabilidad aunque la distancia mínima a la que se
encuentre un ejemplo sea mayor que cero. Hay que tener en cuenta que
cuando hablamos de la solución más probable nos referimos al nivel de toda
la base de datos. No hay que confundirlo con el caso de n ejemplos que estén
situados a igual distancia mínima, para los que el sistema escogerá también
la solución más probable (la de mayor frecuencia), pero sólo entre estos n
ejemplos. Normalmente coincidirá la solución más probable a escala global
de base de datos con la más probable entre un conjunto de ejemplos de
distancia mínima, pero no tiene por qué.
3.3.4.2. Módulo de evaluación para tareas de tildado a partir de la categorización.
El presente módulo se hizo necesario a la hora de conocer el acierto que se podía
obtener al estimar el tildado de una palabra ambigua a partir de la resolución mediante
MBL de su categorización. Con el módulo de evaluación original se podría conocer
únicamente el acierto que se obtendría en la resolución de la categoría de las palabras
0 #Nº de palabras 'sobre' ACENTUADAS en el fich. de entrenamiento1470 #Nº de palabras 'sobre' DESACENTUADAS el el fich. de entrenamiento
Capítulo 3. Implementación del Memory-Based Learner
95
ambiguas, pero no tendríamos ningún conocimiento acerca del porcentaje de acierto a la
hora de atinar con su tildado.
Es por tanto necesario que el módulo cargue en memoria las palabras-rasgos que
servían al módulo traductor para ir generando los ejemplos, en los que los rasgos eran
codificaciones numéricas de palabras. Por tanto, se sirve de los ficheros que éste
generaba para cargarlas dos veces en memoria: una con las palabras tal cual son y otra
con las palabras destildadas. La primera es para poder conocer si se ha acertado a la
hora de estimar la tilde a partir de una categoría predicha. Y la segunda es para poder
realizar la búsqueda de todos las palabras que sean como la que se estudia sin tener en
cuenta la tilde, y poder encontrar así a las distintas ambigüedades (un tipo de tildado por
categoría encontrada).
Como salida, el módulo devuelve los ficheros ya comentados anteriormente, y
uno adicional. En el fichero de estadísticas (estadis.est) añade una línea en la que se da
el porcentaje de acierto en l tildado, para aquellos casos en los que se ha acertado con la
categoría (cuyo porcentaje de acierto se muestra también en el fichero). En la Figura
3.14. puede apreciarse un ejemplo de los resultados obtenidos en este fichero.
Figura 3.14.
Además, genera un nuevo fichero, errores_tildado.est, que contiene todas las
palabras para las que se ha acertado la categorización y, sin embargo, se ha errado a la
hora de predecir su tilde.
A continuación se explican las nuevas funciones de este módulo y sus
diferencias con el estándar explicado anteriormente.
Estadísticas de la evaluación: Tasa de aciertos en la resolución de ambigüedad en la cat.(en%): 87.534485 (12057 de 13774) Tasa de aciertos en la resolución del tildado(en%): 99.338191 (11976 de 12057)
Capítulo 3. Implementación del Memory-Based Learner
96
• TPalabraCateg *CargaRasgos(char *directorio, char*NomFichero1, char *NomFichero2)
Con esta función se cargan en memoria las palabras-categorías de
entrenamiento y de evaluación, conservando su tilde. Para ello se sirve de la
función LeePalabraFichRasgos . La función CargaRasgos2 se emplea para
la misma operación pero destildando previamente todas las palabras que se
van leyendo.
• bool EvaluaTildesCat(TEjemplo ejemplo,TValorSolucionsolucion)
Esta es la función por la que se diferencia principalmente este módulo de
los anteriores. Una vez que, siguiendo el procedimiento normal se ha
estimado la solución (es decir, la categoría), se pasa a esta función el ejemplo
que se está evaluando, así como dicha solución estimada. Esta función
devolverá true si gracias a la categoría acertada se ha acertado a la hora de
calcular la posición de la tilde. Y devolverá false en el caso de que, a pesar
de haber acertado adivinando la categoría de la palabra, se ha errado en la
resolución de la posición de la tilde. Internamente emplea la función
CalculaPosicionTilde , a la que le entrega como parámetro una palabra y
ésta le devuelve la posición donde tiene situada la tilde (devuelve cero si no
está tildada).