Reconocimiento de patrones con RNA

Autor: Mariano Maccarrone
Hoy mostrare un ejemplo muy sencillo de reconocimiento de patrones con redes neuronales usando la libreria fann (Fast Artificial Neural Network). Este programa reconoce circulos y cuadrados, y da error si hay cualquier otra figura en la entrada. El corazon del sistema esta formado por dos redes neuronales, una para los circulos y otra para los cuadrados, entrenadas con varios ejemplos de sus correspondientes figuras y otras erroneas. La entrada consta de un archivo de imagen bitmap de 25x25pixels al que se le realizan las siguientes conversiones: se descarta el header, se lo binariza, y se lo transforma en un array de unos y ceros. Este array es la entrada que toman las redes. La topologia de las redes es la clasica de una red backpropagation de 3 capas, 634 entradas, 10 neuronas en la capa oculta y 1 salida que se activa con un numero cercano a 1 si la entrada es verdadera y un valor cercano a -1 si la entrada es falsa. El reconocedor esta dividido en dos programas, el primero es el entrenador con sus respectivos archivos de ejemplos, y el segundo es el reconocedor en si con sus archivos de configuracion. Las figuras a reconocer son del siguiente tipo:
Para que funcione bien las figuras deben estar en el centro de la imagen. Esto quiere decir que para que reconozca bien cualquier imagen se deberia calcular el centroide y hacer el cambio de coordenadas correspondiente. Para hacer mas facil el ejemplo me tome la molestia de centrar la imagenes a mano. Aca esta el programa de entrenamiento:
#include "fann.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
        const unsigned int num_input = 634;
        const unsigned int num_output = 1;
        const unsigned int num_layers = 3;
        const unsigned int num_neurons_hidden_2 = 10;
        const float desired_error = (const float) 0.001;
        const unsigned int max_epochs = 50000;
        const unsigned int epochs_between_reports = 1000;

        struct fann *ann = fann_create_standard(num_layers, num_input, num_neurons_hidden_2, num_output);

        fann_set_activation_function_hidden(ann, FANN_SIGMOID_SYMMETRIC);
        fann_set_activation_function_output(ann, FANN_SIGMOID_SYMMETRIC);

        if (!strcmp(argv[1],"circulo"))
        {
                printf("Entrenando circulo\n.\n.\n");
                fann_train_on_file(ann, "circulo.dat", max_epochs, epochs_between_reports, desired_error);
                fann_save(ann, "circulo.net");
                printf("OK\n");
        }
        if (!strcmp(argv[1],"cuadrado"))
        {
                printf("Entrenando cuadrado\n.\n.\n");
                fann_train_on_file(ann, "cuadrado.dat", max_epochs, epochs_between_reports, desired_error);
                fann_save(ann, "cuadrado.net");
                printf("OK\n");
        }

        fann_destroy(ann);

  return 0;
}
Suponiendo que el nombre es entrenamiento.c se compila con: cc -o entrenamiento -lm -lfann entrenamiento.c Para ejecutarlo debemos tener los archivos circulo.dat y cuadrado.dat con los ejemplos a aprender. La sintaxis del programa es la siguiente, "./entrenamiento circulo" para entrenar la red del circulo y "./entrenamiento cuadrado" para la del cuadrado. El comando nos debe generar otros dos archivos, cuadrado.net y circulo.net, con la informacion de las redes. Aca pego el codigo del reconocedor en si:
#include <stdio.h>
#include <stdlib.h>
#include "floatfann.h"

#define UMBRAL_BINARIZACION 100
#define UMBRAL_RECONOCIMIENTO 0.6

void main(int argc, char *argv[])
{
        FILE *file_in;
        int r,g,b;
        fann_type *calc_out,*img_ptr,*calc_out2;
        fann_type entrada[634];

        struct fann *ann = fann_create_from_file("circulo.net");
        struct fann *ann2 = fann_create_from_file("cuadrado.net");

        file_in=fopen(argv[1],"r");
        img_ptr=&entrada[0];


        fseek(file_in,53,SEEK_SET);
        while(!feof(file_in))
        {
                r=getc(file_in);
                g=getc(file_in);
                b=getc(file_in);

                if (((r+g+b)/3)>UMBRAL_BINARIZACION)
                {
                        *img_ptr=1;
                        img_ptr++;
                }
                else
                {
                        *img_ptr=0;
                        img_ptr++;
                }
        }


         calc_out = fann_run(ann, entrada);
         calc_out2 = fann_run(ann2, entrada);

         if(*calc_out > UMBRAL_RECONOCIMIENTO && *calc_out2 < 0)
                 printf("Es circulo (%f,%f)\n\n",calc_out[0],calc_out2[0]);
         else
                 if (*calc_out < 0 && *calc_out2 > UMBRAL_RECONOCIMIENTO)
                         printf("Es cuadrado (%f,%f)\n\n",calc_out[0],calc_out2[0]);
                else
                        printf("Error o figura desconocida\n\n");

         fann_destroy(ann);
         fann_destroy(ann2);
         return 0;

        fclose(file_in);
}
                      
En las primeras lineas del codigo podemos ver dos macros, UMBRAL_BINARIZACION y UMBRAL_RECONOCIMIENTO, la primera es el umbral para realizar la binarizacion de la imagen y la segunda es para el umbral de reconocimiento, esto quiere decir que si la salida de una de las redes es superior a ese valor se toma la salida como verdadera. Pero para asegurarse que la figura es la que dice ser se verifica que la salida de la otra red sea menor que cero. Compilacion: al igual que el de entrenamiento si suponemos que el nombre es rpat.c lo compilamos con cc -o rpat -lm -lfann rpat.c y la forma de ejecucion es "./rpat archivo.bmp". Ahora viene una prueba de ejemplo con la siguiente imagen,
ejecutamos el comando y obtenemos esta salida
mpm@mpm:~/prog/c/rec$ ./rpat prueba3.bmp
Error o figura desconocida
Con esta imagen:
obtenemos esto:
mpm@mpm:~/prog/c/rec$ ./rpat circ2.bmp
Es circulo (0.749088,-0.826682)
Con la siguiente:
mpm@mpm:~/prog/c/rec$ ./rpat img/cosa5.bmp
Error o figura desconocida
Y con esta: .
mpm@mpm:~/prog/c/rec$ ./rpat cuad2.bmp
Es cuadrado (-0.977400,0.973707)
Tambien se pueden reconocer figuras de color, pero hay que tener mucho cuidado con el valor de umbral de la binarizacion, por ejemplo:
mpm@mpm:~/prog/c/rec$ ./rpat circn.bmp
Es circulo (0.898436,-0.916547)
En este link esta el codigo fuente y los archivos con los ejemplos de entrenamiento.
Espero que les haya gustado el articulo. Cualquier duda, mensaje, sugerencia o inquietud dejen un mesaje mas abajo. Saludos

taxon:

Comentarios