3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf ·...

49
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

Transcript of 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf ·...

Page 1: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 2: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 3: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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).

Page 4: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 5: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 6: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 7: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 8: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 9: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 10: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 11: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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:

Page 12: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 13: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 14: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 15: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 16: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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]

Page 17: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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##

Page 18: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 19: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 20: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 21: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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##

Page 22: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 23: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 24: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 25: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 26: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 27: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 28: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 29: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 30: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 31: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 32: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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 .

Page 33: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 34: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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 ω=−= )()()(

Page 35: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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 )()()()(

Page 36: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 37: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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).

Page 38: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 39: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

:.

),(δ

Page 40: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 41: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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 ,

Page 42: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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.

Page 43: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 44: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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é.

Page 45: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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...

Page 46: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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)

Page 47: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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

Page 48: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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)

Page 49: 3. Implementación del Memory-Based Learnerlorien.die.upm.es/juancho/pfcs/JAA/capitulo3.pdf · empleaba el compilador Borland C++ Versión 5.02. Capítulo 3. Implementación del Memory-Based

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).