Blog para documentar el trabajo de programación realizado en la clase de Aprendizaje Automático del Tec de Monterrey, Campus Estado de México. Enero-Mayo 2010.

Friday, April 30, 2010

Medio Ambiente:
Nuestro medio ambiente será como un tipo calabozo con forma de laberinto (el cuál será dinámico), constará de una entrada y dos tipos de agentes principales: buscador y guardián

El objetivo del buscador será evitar a los guardias el mayor tiempo posible y a la vez, donde los callejones sin salida influirán a veces si es atrapado o no.
En cuanto el agente buscador es atrapado por alguno de los guardianes, este regresa al inicio.

Actividad que aprende:
En este caso el agente aprenderá a moverse dentro del medio ambiente intentando no ser atrapado por los guardianes. Para esto se utiliza una red neuronal que se entrena.

Patrones de Entrada:
Los patrones de entrada en este caso, se generan mediante iteraciones.
Los patrones cuentan con los siguientes datos:
a) [0, 1] Un valor booleano de si hay o no centinelas en su rango de visión.
b) [1 ,2, 3, 4] El valor de la dirección tomada para el movimiento.
c) [0, 1] Un valor booleano que determina si encontró un callejón sin salida o no en su camino.
d) [0, 1] Una evaluación de si la acción fue buena o mala.
Se escogieron estos valores ya que son los más importantes en este ambiente para poder moverse en el medio y cumplir la meta que se quiere aprender.



Patrones de Salida:
La salida que se genera es un arreglo de 4 atributos cuyo valor es 0 o 1, de tal forma que el siguiente movimiento del buscador será decidido por el índice o “bandera” que este prendida en el patrón de salida.
Ejemplo de patrón de salida:
[0, 0, 1, 0] -> El cual indicaría que el movimiento deberá tomar un valor de 3 (“Muévete a la derecha”)

Red Neuronal:
En este caso, dado que lo que se quiere evaluar es algo complejo, se decidió utilizar una red neuronal multicapas utilizando un algoritmo de Backpropagation.

La red cuenta con 3 capas
• Entrada: Esta capa cuenta con 4 neuronas, de las cuales cada una toma uno de los atributos de los patrones de entrada.
• Secreta: Esta capa consta de 2 neuronas, se decidió de esta forma para facilitar un poco la implementación y reducir un poco los cálculos.
• Salida: Esta capa cuenta con 4 neuronas, para las cuales cada una sirve como bandera para elegir el siguiente movimiento, dependiendo de que neurona se active es el movimiento elegido.
En la red se utiliza un factor de aprendizaje de 0.3 ya que es un valor pequeño y un tanto “estándar”: ya que queremos que el aprendizaje sea preciso en cuanto a las salidas.
En este caso no se utilizo momento.
El número de iteraciones elegido para entrenar a la red fue de 1500 ya que al ser movimientos aleatorios al principio, es de interés que tenga una exploración del ambiente bastante grande para poder hacer la red neuronal más precisa


Ejemplos de corridas:


En la imagen se puede observar del lado izquierdo las entradas que va generando, y del lado derecho la salida que se debería dar, pero dado a que solo se está entrenando apenas la red, los valores de salida no son realmente acertados. Si se ven por ejemplo la primera línea:
Patrón de entrada: [0, 0, 4, 1]
Salida: [0, 0, 1, 0] Mientras que la salida esperada sería [0, 0, 0, 1]




Después de que ha terminado las iteraciones de entrenamiento podemos ver, que de acuerdo al patrón de entrada, ahora si nos envía la salida esperada:
Ejemplo de la primera línea:
Entrada: [0, 0, 4, 1]
Valor esperado: [0, 0, 0, 1]

Como se observa en la imagen, el valor obtenido es igual al esperado, por lo que la red aprendió efectivamente.

Conclusiones:
Las redes neuronales son bastante útiles para problemas como el presentado ya que sus cálculos y división en las salidas nos permite una evaluación y un aprendizaje más preciso asignando una dirección a cada salida. También es importante notar que la velocidad de los cálculos es importante, y en este caso es lo suficientemente rápida para un programa que se actualiza en tiempo real, lo que hace una red neuronal muy viable para este tipo de problemas.
La parte más difícil para este proyecto se podría considerar la codificación de entradas y salidas del medio ambiente, ya que todos los cálculos internos son automáticos del algoritmo.

Biokterii Neural

Breve descripción del medio ambiente.
El medio consiste en el interior de un cuerpo de un ser vivo en el que un virus debe aprender a reconocer células a partir de la observación de sus características. Existen tres zonas diferentes de clasificación de células, unas de ellas son comestibles o de tipo Food, otras son aquellas que pueden atacar al virus o Enemy, por último nos encontramos con las células objetivo o de tipo Target. El virus deberá aprender, después de una muestra de 15 células, a determinar el tipo de las células que irán apareciendo, actuar en consecuencia y así poder sobrevivir dentro del cuerpo del ser vivo.

Descripción detallada de la actividad o acción que va a aprender tu agente.
El virus debe aprender a reconocer las células que irá encontrando dentro del cuerpo, para esto se basa en sus características físicas, es decir, su color, su forma, la dirección en la que rota, la figura que tiene la célula en su interior y el color de esta figura interna. Como ya se mencionó en un principio, se cuenta con 15 células que formarán parte del entrenamiento, que el usuario puede acomodar en cualquiera de las tres categorías: Target, Enemy o Food. Es muy importante mencionar que dependiendo como el usuario haga esta clasificación, será la forma en que la neurona aprenderá a clasificar las diferentes células que irán apareciendo en el ambiente, ya que el usuario crea interactivamente el training set.

Solución planteada al problema utilizando Redes Neuronales. Describe con detalle cada elemento del planteamiento:
Cada célula tiene 5 propiedades: Forma exterior color exterior, forma y color interior y rotación. De cada una se tiene un arreglo de bits que indican el tipo de característica que representa a cada célula, que se codifica concatenando los 5 arreglos de bits en un arreglo más grande que los contiene a todos. Cada bit alimenta a un nodo diferente en la capa de entrada. Dependiendo de la clasificación del usuario, es la forma en la que se entrena la red.

Se utilizó la biblioteca PyBrain para la construcción de las redes neuronales.

i. Patrones de aprendizaje para alimentar a la red.

OuterShape: ["Simple","CircleStroke","CircleFill","Square","DoubleSquare"]
InnerShape: ["None","CircleStroke","CircleFill","SquareStroke","SquareFill"]
OuterColor: ["Red","Green","Blue"]
InnerColor: ["Red", "Green", "Blue", "Black"]
Rotation: ["Left", "Right"]

OuterShape= [0,0,0,0,1]
InnerShape= [0,0,0,0,1]
OuterColor= [0,1,0]
InnerColor= [1,0,0,0]
Rotation= [1,0]

Clasificación (elegida por el usuario): ["Target","Enemy","Food"]
Clasificación = [0,1,0]

---------------------------------------------------------------------------------
Patrón de aprendizaje: ([0,0,0,0,1,0,0,0,0,1,0,1,0,1,0,0,0,1,0] , [0,1,0])
---------------------------------------------------------------------------------

Se alimentarán tantos patrones como células iniciales existan en el medio ambiente, seleccionando el 25% de ellas para pruebas durante el entrenamiento.

ii. Codificación de la salida de la red.

La red devuelve un arreglo de 3 datos que representan la probabilidad de ser clasificado como cada uno de los posibles tipos: objetivo enemigo y comida.

Se elige el tipo que tenga el número más alto de los tres para clasificar.

Ejemplo, la salida:

[0.0032896,0.2387263,0.87382436]

Clasifica a la célula como "Comida" pues el elemento más alto es el tercero, que representa la posibilidad de ser "Comida".

iii. Tipo de red neuronal a ser utilizada.

La red neuronal está formada por 3 capas, la primera es una capa lineal, la segunda sigmoidal y la tercera lineal, de tal forma que existe una conexión completa de los nodos entre cada capa llevando de los 19 parámetros de entrada originales a las 3 salidas deseadas.

iv. Numero de capas (entrada, intermedias, salida) y número de neuronas en cada capa.

v. Parámetros de la red (tasa de aprendizaje, momento, iteraciones).

Se utilizó una tasa de aprendizaje de 0.3 y un momento de 0.1.

Se realizan iteraciones hasta que converge la red o hasta que se cumplan 1000 iteraciones.

Video

Conclusiones
Una de las características observadas a lo largo de la programación del problema fue la importancia que tiene el training set para la solución del mismo. En particular, las redes neuronales requieren de mucho entrenamiento, lo cual implica tener un training set muy grande y diverso. Si no existiera suficiente entrenamiento la red sería muy suceptible al ruido, por lo que funcionan mejor learning rates más bajos, de tal forma que aprenda detalles más finos del training set. La red neuronal, sin embargo, requiere mucho más tiempo de entrenamiento que otros algoritmos como el ID3, por lo que su implementación no sería adecuada para generarse en un ambiente que requiera de cambios en tiempo real.

Actividad: Redes Neuronales

Descripción del medio ambiente

En una playa, están naciendo varias tortugas, y para sobrevivir, deben de llegar al mar lo más rápido posible, sin embargo, puede que haya depredadores cerca queriendo alimentarse de ellas, lo que las hace más vulnerables. Las tortugas nacen a cierta distancia del mar, además de nacer con cierta velocidad propia y cierta resistencia a los ataques de los depredadores.

Descripción de la actividad que aprenderá el agente

Dadas las características de cada tortuga (su distancia al mar, si hay o no depredador cerca, su velocidad y su resistencia), el agente debe de decidir si una tortuga se salva sin ayuda de nadie, si es necesaria la intervención humana para salvarla o si es imposible salvarla.

Descripción detallada de los patrones

Un patrón está formado de la siguiente manera:

{ Distancia, Depredador, Velocidad, Resistencia} ¿Se salva?

Donde:

Distancia mide si se encuentra entre 0-5 metros, 5-10 metros o más de 10 metros. Se eligió así para evitar que los patrones fueran continuos y se eligieron esos rangos porque creemos que son distancias que pueden hacer diferencia entre si la tortuga vive o no.

Depredador mide solamente si hay o no un depredador cerca de la tortuga, donde cerca se define dentro del área de sobrevivencia de la tortuga, que son de alrededor de 5 metros a la redonda. Nuevamente, creemos que este radio es el que puede hacer la diferencia entre si la tortuga sobrevive o no.

Velocidad, mide qué tan rápido se mueve la tortuga, y cae dentro de: Lento, Normal o Rápido.

Resistencia, mide que tanto la tortuga puede resistir a los ataques de los depredadores, y esta puede ser Alta, Media y Baja. Estos últimos dos atributos se discretizaron para poder trabajar con ellos y se escogieron ya que una tortuga rápida puede llegar al mar pese a estar lejos o viceversa, y ocurre similar con la resistencia.
Cada uno de los valores de entrada, están codificados en 0.1, 0.5 ó 0.9 según sea bajo, mediano o alto.

¿Se salva? Es un valor ternario (también entre 0.1,0.5 o 0.9) que identifica si la tortuga se salva sola, si hay que salvarla o si no hay forma alguna de salvarla.

Red Neuronal

Para poder lograr que la red aprendiese cuando y cuando no salvar a la tortuga, programamos una red neuronal estocástica con valores decimales entre 0 y 1.

Existen cinco valores de entrada, la primera corresponde al valor 1.0 (para pasar el threshold) y las siguientes cuatro corresponden a los cuatro valores de entrada de la red, organizados, como ya se explicó entre 0.1, 0.5 y 0.9.

Se cuenta con una capa oculta de tres neuronas ya que es el valor que se recomienda para redes pequeñas.

De salida sólo se tiene una neurona, que identifica de igual forma 0.1, 0.5 o 0.9 según sea la salida. No fue necesario poner tres neuronas cada una para identificar un tipo de clase ya que sí logró clasificar los ejemplos con una sola neurona.

Para entrenar la red, se utilizó una tasa de aprendizaje de 0.35, ya que un valor más alto causaba que los ejemplos de validación se alejaran más del resultado, lo cual atribuimos al overfitting, y un valor más pequeño hacía demasiado tardada el aprendizaje.

No se utilizó ningún momento porque no fue necesario implementarse para lograr que la red clasificara correctamente los ejemplos de salida.Se utilizaron 15 ejemplos de entrenamiento y cinco ejemplos de validación, aunque para terminar el programa, se decidió que cuando el error en los ejemplos de entrenamiento fuera menor a 0.1, este se detuviera.

Los resultados con los ejemplos de validación son los siguientes:

Antes del entrenamiento

0.9 0.1 0.1 0.9 à Salida esperada: 0.1 à Salida del programa: 0.4973

0.1 0.1 0.9 0.5 à Salida esperada: 0.1 à Salida del programa: 0.4933

0.5 0.5 0.9 0.9 àSalida esperada: 0.5 à Salida del programa: 0.4976

0.9 0.5 0.1 0.1 à Salida esperada: 0.9 à Salida del programa: 0.4838

0.5 0.5 0.5 0.1 à Salida esperada: 0.5 à Salida del programa: 0.4943

Después del entrenamiento

0.9 0.1 0.1 0.9 à Salida esperada: 0.1 à Salida del programa: 0.1932

0.1 0.1 0.9 0.5 à Salida esperada: 0.1 à Salida del programa: 0.0587

0.5 0.5 0.9 0.9 àSalida esperada: 0.5 à Salida del programa: 0.3205

0.9 0.5 0.1 0.1 à Salida esperada: 0.9 à Salida del programa: 0.6738

0.5 0.5 0.5 0.1 à Salida esperada: 0.5 à Salida del programa: 0.5100

Tomando en cuenta que un valor pertenece a la clase siempre que el valor este entre 0.15 arriba o abajo del valor esperado, este clasifica correctamente, todos los patrones fueron clasificados.



Conclusiones

En conclusión, creemos que las redes neuronales son bastante difíciles de implementar, debido a que hay que estar probando con diferentes valores de capas intermedias, además de poner el número de neuronas correctas en cada capa, para lo cual, la única forma de saberlo es probarlo sobre la propia red. También hay que tener cuidado con la codificación de los datos de entrada y de los datos de salida, porque es posible que no llegue a generalizar si los valores de salida son muy cercanos.

Fuera de eso, fue muy entretenido generar la red e implementarla, aunque llegó a ser complejo por toda la cantidad de datos e información que hay que tomar en cuenta para lograr una correcta generalización.

Preferimos desarrollar una red neuronal a un algoritmo como ID3 ya que en ocasiones ID3 puede no tener un ejemplo para cierto patrón, en cambio una red tratará de dar el que más se acerque de acuerdo a los ejemplos de entrenamiento.

Thursday, April 8, 2010

Actividad de Programación 3: Algoritmo ID3

Medio Ambiente:
Nuestro medio ambiente será como un tipo calabozo con forma de laberinto (el cuál será dinámico), constará de una entrada y una salida Y tres tipos de agentes principales: buscador, guardián y espía. El agente buscador se encargará de buscar un objeto y huir hacia la salida, evitando lo más que pueda al agente guardián. El agente guardián se encargará de hacer rondas en el calabozo en diferentes zonas (mientras las va conociendo y aprendiendo) y en cuanto encuentre al agente buscador, seguirlo para capturarlo. El agente espía se encargará de ayudar al agente buscador para encontrar el objeto verdadero, indicándole dónde hay atajos e interfiriendo en las comunicaciones de los agentes guardianes (solo en el caso que exista más de un agente guardián). Puede que existan más buscadores y de ésta forma los agentes guardianes se confundirán.

En caso de que solo existiera una entrada, se tendría que conseguir el objeto único que podrá convertir la entrada en la salida, para esto, nuestro agente tendría que recordar el camino por el que vino, siempre tratando de evitar al agente guardia.

Existirán agentes guardias, los cuales estarán recorriendo todo el calabozo para conocer el terreno y estar haciendo guardia en algunas de las zonas del mismo. Dentro del calabozo existen diversos objetos, de los cuales hay uno que es el que le abrirá la puerta a la salida, pero hay objetos falsos, copias, que tratarán de confundir al agente buscador.

Actividad que aprende:
En este caso el agente aprenderá a moverse dentro del medio ambiente intentando no ser atrapado por los centinelas. Utilizando el algoritmo de ID3 este aprende en qué dirección moverse de acuerdo a la retroalimentación obtenida.



Patrones:
Los patrones de entrenamiento en este caso, se generan por 30 segundos a partir de que el programa corre. Los patrones cuentan con los siguientes datos:
a) [0, 1] Un valor booleano de si hay o no centinelas en su rango de visión.
b) [1 ,2, 3, 4] El valor de la dirección tomada para el movimiento.
c) [0, 1] Un valor booleano que determina si encontró un callejón sin salida o no en su camino.
d) [0, 1] Una evaluación de si la acción fue buena o mala.
Se escogieron estos valores ya que son los más importantes en este ambiente para poder moverse en el medio y cumplir la meta que se quiere aprender. Además de que son valores fáciles de evaluar para la utilización del algoritmo.

Los patrones se irán almacenando en un arreglo bidimensional.

Solución ID3:
i) Algoritmo utilizado:
a. ID3: Ya que fue interesante la implementación de este en el medio ambiente y porque es sencillo de comprender.
ii)
a. Aprendizaje: El número de patrones realmente no está definido ya que está delimitado por tiempo, en este caso se generan patrones, cada que el agente se mueve y que se repinta la pantalla durante 30 segundos. Terminando el lapso de 30 segundos el movimiento se determina de acuerdo al algoritmo.
b. Validación: En este caso no se utilizan patrones de validación ya que nosotros no tenemos control sobre el movimiento del agente, simplemente al ser movimientos aleatorios, el programa utiliza el árbol generado con los patrones de entrenamiento para ir evaluando paso por paso el movimiento del agente, en el caso de tener una retroalimentación buena sigue el camino, en caso de tener una retroalimentación mala, se mueve en dirección contraria.
c. Ejemplos:
iii) Este programa si funciona en tiempo real, ya que por eso se está corriendo por tiempo, durante un tiempo se entrena y todo lo que resta del tiempo está generando movimientos a partir de lo que aprendió con ID3 cada paso o movimiento que hace.
iv) No cuenta con overfitting ya que el movimiento del agente es aleatorio, entonces puede darse el caso de que muchos casos no estén considerados dentro del lapso de tiempo que se asignó para generar patrones de entrenamiento.
v) Mejorar el funcionamiento podría ser agregando más valores a los patrones de entrenamiento ya que en la forma que está ahorita sería una forma simple, pero en un ambiente más complicado podría llegar a no funcionar bien con tan pocos valores o patrones. También se podría aumentar el tiempo que genera patrones de entrenamiento.

Conclusiones:
Esta actividad nos ayudo a comprender mejor la forma en que se lleva a cabo la relación entre el árbol de decisiones generado por ID3 y el medio ambiente, llegando así a un aprendizaje. A la vez pudimos ver que no es un algoritmo muy complejo de programar, sino su dificultad está en adaptarse a un medio diferente por todas las variables que hay que tomar en cuenta, a la vez de que se deben decidir los patrones.
Probablemente la parte de la decisión de que valores utilizar para los patrones fue el más difícil ya que de esto dependía el funcionamiento completo del algoritmo.

Actividad ID3

Descripción del medio ambiente

En una playa, están naciendo varias tortugas, y para sobrevivir, deben de llegar al mar lo más rápido posible, sin embargo, puede que haya depredadores cerca queriendo alimentarse de ellas, lo que las hace más vulnerables. Las tortugas nacen a cierta distancia del mar, además de nacer con cierta velocidad propia y cierta resistencia a los ataques de los depredadores.

Descripción de la actividad que aprenderá el agente

Dadas las características de cada tortuga (su distancia al mar, si hay o no depredador cerca, su velocidad y su resistencia), el agente debe de decidir si una tortuga se salva sin ayuda de nadie, si es necesaria la intervención humana para salvarla o si es imposible salvarla.

Descripción detallada de los patrones

Un patrón está formado de la siguiente manera:

{ Distancia, Depredador, Velocidad, Resistencia} à ¿Se salva?

Donde:

Distancia mide si se encuentra entre 0-5 metros, 5-10 metros o más de 10 metros. Se eligió así para evitar que los patrones fueran continuos y se eligieron esos rangos porque creemos que son distancias que pueden hacer diferencia entre si la tortuga vive o no.

Depredador mide solamente si hay o no un depredador cerca de la tortuga, donde cerca se define dentro del área de sobrevivencia de la tortuga, que son de alrededor de 5 metros a la redonda. Nuevamente, creemos que este radio es el que puede hacer la diferencia entre si la tortuga sobrevive o no.

Velocidad, mide qué tan rápido se mueve la tortuga, y cae dentro de: Lento, Normal o Rápido.

Resistencia, mide que tanto la tortuga puede resistir a los ataques de los depredadores, y esta puede ser Alta, Media y Baja. Estos últimos dos atributos se discretizaron para poder trabajar con ellos y se escogieron ya que una tortuga rápida puede llegar al mar pese a estar lejos o viceversa, y ocurre similar con la resistencia.


Solución al problema

Elegimos utilizar ID3 porque es el que más se adapta a los patrones que creamos. Aunque utilizamos ciertos límites para no utilizar datos continuos, no utilizamos la poda del árbol que C4.5 ofrece, ya que consideramos el árbol generado no es tan grande como para necesitar la poda.

En cuanto a los patrones, debido a que sólo hay 54 patrones distintos para aprender, decidimos usar un conjunto de 20 patrones, 15 para aprendizaje y 5 para validación con el propósito de darle al programa una gran variedad de patrones distintos sin llegar a darle todos los existentes.

Ejemplos de patrones:

{5-10m, Hay depredador, Lento, Baja} à Muere

{0-5m, No hay depredador, Rápido, Alta} à Sobrevive solo

El programa es lo suficientemente rápido para ser utilizado en tiempo real, aunque debería de ser alimentado con datos reales obtenidos con la práctica y al momento de ser utilizado, que sea por alguien lo suficientemente rápido para crear los patrones.

Creemos que el programa no tiene overfitting porque no encontramos otro árbol que fuera más específico que el que nos dio el programa, además de que el porcentaje de patrones dados es considerablemente alto comparado con la totalidad.

El programa podría mejorarse si las diferentes posibilidades en las características fueran más específicas, de esa forma existirían más posibles patrones y el resultado podría ser más preciso.


Conclusiones

En conclusión, creemos que los programas de ID3 son buenos para clasificar patrones cuyas hipótesis pueden ser organizadas en forma de árbol, siempre y cuando no existan datos continuos. En cuanto a la programación, fue un poco más difícil que programas anteriores debido más que nada a la implementación dinámica del árbol.

Tuesday, April 6, 2010

Actividad de programación ID3 Biokterii

Breve descripción del medio ambiente.

El medio consiste una vez más en el interior de un cuerpo de un ser vivo (por tradición podemos decir que de un gato) en el que un virus debe aprender a reconocer a los diferentes tipos de células que existen dentro del dicho cuerpo. Existen tres tipos diferentes de células, unas de ellas son comestibles o de tipo Food, otras son aquellas que pueden atacar al virus o Enemy, por último nos encontramos con las células objetivo o de tipo Target. El virus deberá aprender, después de una muestra de 15 células, a determinar el tipo de las células que irán apareciendo y así poder sobrevivir dentro del cuerpo.

Descripción detallada de la actividad o acción que va a aprender tu agente.

El virus debe aprender a reconocer las células que irá encontrando dentro del cuerpo, para esto se basa en sus características físicas, es decir, su color, su forma, la dirección en la que rota y la figura que tiene la célula en su interior. Al comienzo se cuenta con una entrenamiento de 15 células, que el usuario puede acomodar en cualquiera de las tres categorías: Target, Enemy o Food. Es muy importante mencionar que dependiendo como el usuario haga esta clasificación, será la forma en que se realice el árbol de decisión.

Descripción detallada de los patrones obtenidos del medio ambiente para el entrenamiento. Explicar la razón por la que se escogieron cada uno de los atributos del patrón.

Como se mencionó anteriormente, el virus se basa en las características físicas de las células para poder determinar su tipo. Siendo más específicos, las características son las siguientes:
Color: rojo, azul o verde
Forma externa: círculo, círculo con una bolita, círculo con una bolita llena, cuadrado o doble cuadrado
Giro: derecha o izquierda
Forma interna: ninguna, círculo lleno, círculo vacío, cuadrado lleno o cuadrado vacío

La razón por la que se eligieron estos atributos fue porque esas son las características que definimos para la célula. Al ser nuestro programa más interactivo, el usuario puede clasificar los diferentes tipos de células como mejor le parezca, basándose en las características que su intuición le diga. Claro está, que si dicha clasificación se hace de forma aleatoria y sin seguir un patrón es posible que no se obtenga una buena clasificación o los resultados deseados.


Solución planteada

La solución planteada es realizar un árbol con ID3 basándose en las características ya mencionadas

Video



Conclusiones

ID3 es muy interesante y realmente es como mágica la forma en que logra hacer las clasificaciones, la codificación tuvo algunas complicaciones pues en la clase vimos el método intuitivo para poder hacer la clasificación y no el método formal, así que eso tomó un poco de tiempo al momento de realizar el programa. Sin embargo el problema se prestó mucho para una buena realización del mismo, pues las características eran muy definidas y los valores eran totalmente discretos.