sábado, 30 de junio de 2012

El sentido de la vida (otra vez)


Hablando sobre el sentido de la vida:
 
¿Qué sentido puede tener algo surgido espontáneamente, por una fuerza ciega e irracional, por algo que sólo es y actúa por ímpetu? ¿Se te ocurriría afirmar qué un trozo cualquiera de materia navegando por el espacio siguiendo la ley de inercia sigue o tiene alguna finalidad? No, ¿verdad? Porque es obvio que la causa de dicho fenómeno es una ley de la naturaleza, y es evidente que una ley es simplemente una regularidad espontánea del Universo. Hay una regularidad en el Universo que "obliga" a los cuerpos a actuar de cierta manera, y aceptamos desde el inicio de la ciencia física que dichas regularidades no poseen conciencia ni racionalidad, simplemente son así.

La cuestión aquí, es que hace relativamente poco que la ciencia ha descubierto –gracias Darwin- algo sorprendente: ¡sorpresa!, finalmente la vida no es más que el producto de una ley natural. Una ley natural ni más ni menos ciega e irracional que el resto de leyes anteriormente conocidas. Esta nueva ley descubierta, ciega creadora de lo que entendemos por vida, es la famosa ley de la evolución. Ley que es consecuencia -y por lo tanto reducible- a leyes físicas más elementales.

Podemos entonces decir –porque se reduce a ellas- que la ley de la evolución, al igual que el resto de leyes, no sigue o persigue en esencia ninguna finalidad concreta, no tiene un sentido o dirección esencial, simplemente es como es: ni el meteoro moviéndose en el espacio vacío persigue un fin esencial por ser consecuencia de la ley de inercia, ni lo obtiene cuando la ley de la gravedad lo atrae hacia otro cuerpo más masivo, y; lo más importante, [b]un montoncito de materia reunida por efecto de la gravedad e inercia en un planeta, y organizadas espontáneamente por la ley de la evolución, tampoco va a tener o perseguir ningún sentido o finalidad esencial, más que el de ser consecuencia de dichas leyes. Simplemente ser.
[/b]

La cuestión es que, tras varios millones de años, dicha organización ciega y espontánea, ha dado lugar a un complejo grupo de moléculas, las cuales han tomado conciencia del mundo que les rodea. Pero, y ahí está la clave, la conciencia no es más que el producto de la ejecución de complejos algoritmos programados en una parte de su estructura –al que llamamos cerebro-.

Por lo tanto, todos nuestros actos y nuestras motivaciones -absolutamente todas; incluso las estéticas- están predeterminadas por la estructura cerebral que la regularidad evolutiva ha "creado" durante millones de años. Dichos actos, por tanto, jamás podrán tener en esencia una finalidad o sentido, puesto que la ley de las que son causa tampoco lo tiene.

La ilusión de un sentido para la vida que todos tenemos –en mayor o menor medida- viene determinado por cómo es nuestro cerebro. La evolución nos “obliga” mediante el sistema neuroendocrino, a actuar siempre de una manera muy determinada: nos da cierta libertad de acción –que históricamente sobrevaloramos como un libre albedrío- ¡pero en el fondo –sin que seamos normalmente conscientes- nos va conduciendo a que el fin de todos esos actos “libres” sea favorables a la supervivencia y durabilidad en el tiempo de las complejas estructuras moleculares que somos!
Mediante cambios en la concentración de diferentes compuestos químicos en el cerebro, la evolución se garantiza de que; salvo que ocurra alguna neuropatología, el organismo seguirá y perseguirá sus "intereses". [b]¡Y eso es todo lo que podemos hacer en nuestras vidas: utilizar nuestra relativa libertad de acción intentando maximizar en todo momento la concentración de endorfinas en nuestro cerebro![/b] ¿Hay posibilidad de elección? No, evidentemente. En otras palabras; podemos hacer lo que queramos, pero no decidir lo que queremos. Es todo una pantomima. No hay libertad de acción porque somos producto de una regularidad o ley natural. Y como las leyes naturales simplemente son como son, ya que no persiguen nada porque no son entes racionales y actúan por necesidad e ímpetu ciego, no hay lugar al sentido o finalidad esencial en ellas y por ende, tampoco en sus consecuencias, en este caso nosotros.

No hay diferencia entre una piedra movida por inercia en el espacio, o un conjunto de moléculas movidos en un planeta gracias a un algoritmo evolutivamente creado.

Al final fue cierto que el ser humano no se diferencia del más vil gusano…pero además este gusano no se diferencia en nada a la más vil bacteria, ni esta del más vil virus, ni este de los pocos miles de moléculas de ADN que lo forman, ni estas se diferencian en nada esencial de otras estructuras no "orgánicas" capaces de replicarse -como algunos cristales-, ni estos de otras moléculas sin capacidad de replicarse, ni dichas moléculas se diferencian en esencia de átomos "libres", y estos no son más que conjuntos de quarks con electrones girando alrededor, y por último, la mecánica cuántica nos dice que es posible que dichas partículas surjan y desaparezcan espontáneamente de la nada.

Llegamos así a la conclusión de que ni somos nada en esencia, ni nuestros actos persiguen un fin esencial, más allá de obedecer las leyes naturales; de la misma manera que la piedra obedece la ley de inercia en el espacio vacío.

Luego están las ilusiones irracionales religiosas, las metafísicas, y demás engañabobos. La realidad racional nos muestra claramente que lo arriba escrito es acertado: el nihilismo negativo es la respuesta que la humanidad lleva tanto buscando, y que ahora que ha encontrado se resiste a creer.



martes, 26 de junio de 2012

Arthur Schopenhauer - El hastío


En cada uno de los grados en que la voluntad aparece iluminada por el conocimiento, se reconoce como individuo. En el espacio infinito y en el tiempo infinito el individuo se encuentra dentro de su finitud y por consiguiente como una dimensión infinitamente pequeña, perdido en la inmensidad de aquello cuya existencia frente a dicha inmensidad no cuenta con un Cuando y Donde absolutos, pues su lugar y su duración son partes finitas de un todo infinito y sin límites. Su existencia está verdaderamente limitada al momento actual, cuyo fluir en el pasado es un caminar perpetuo hacia la muerte, un constante morir, porque su vida pasada, si hacemos abstracción de sus consecuencias para la presente y del testimonio que representa de la voluntad que en ella se imprime, está definitivamente terminada y muerta, ya no existe; por lo que pensando racionalmente lo mismo le debería dar haber sufrido que haber gozado. Pero el presente se convierte siempre en sus manos en pasado y el futuro es incierto y siempre de corta duración. Por lo cual, su existencia, si la consideramos sólo desde el punto de vista formal, es un constante caer del presente en el pasado muerto, un constante morir. Pero si consideramos ahora la cosa por el lado físico, es evidente que así como nuestro andar es siempre una caída evitada, la vida de nuestro cuerpo es un morir incesantemente evitado, una destrucción retardada de nuestro cuerpo; y finalmente la actividad de nuestro espíritu no es sino un hastío evitado. Cada uno de nuestros movimientos respiratorios nos evita el morir; por consiguiente, luchamos contra la muerte a cada segundo, y también el dormir, el comer, el calentarnos al fuego son medios de combatir una muerte inmediata. Pero la muerte ha de triunfar necesariamente de nosotros, porque le pertenecemos por el hecho mismo de haber nacido y no hace en último término sino jugar con su víctima antes de devorarla. Mientras tanto hacemos todo lo posible por conservar la vida, como inflaríamos una burbuja de jabón todo lo que se puede, aunque sabemos que al fin ha de estallar.

Según hemos visto, la esencia de la Naturaleza que no piensa es una constante aspiración sin fin y sin descanso, lo que vernos de una manera más clara en el animal y en el hombre. Querer y ambicionar: esta es su esencia como si nos sintiéramos poseídos de una sed que nada puede apagar. Pero la base de todo querer es la falta de algo, la privación, el sufrimiento. Por su origen y por su esencia, la voluntad está condenada al dolor. Cuando ha satisfecho todas sus aspiraciones siente un vacío aterrador, el tedio; es decir, en otros términos, que la existencia misma se convierte en una carga insoportable. La vida como péndulo, oscila constantemente entre el dolor y el hastío, que son en realidad sus elementos constitutivos. Este hecho ha sido simbolizado de una manera bien rara: habiendo puesto en el infierno todos los dolores y todos los tormentos, no se ha dejado para el cielo más que el aburrimiento.
El perpetuo anhelar que constituye en el fondo todo fenómeno de voluntad encuentra, en los grados superiores de su objetivación, su razón de ser principal y más común en que en ellos la voluntad se muestra a sí misma bajo la forma corporal que le exige imperiosamente el alimento; lo que da tanta fuerza a esta orden es que el cuerpo no es otra cosa que la voluntad de vivir objetivada. Siendo el hombre la objetivación más perfecta de la voluntad de vivir, es al mismo tiempo el ser que tiene más necesidades; no es en todas partes más que volición y necesidad concretas y puede decirse que es una concreción de mil necesidades. Y con todo esto se encuentra en el mundo abandonado a sí mismo, incierto de todo menos de su indigencia y de sus necesidades; de aquí que toda su vida la absorban los cuidados que reclama la conservación de su cuerpo. A esto se une luego el imperativo de la propagación de la especie, Por todas partes se acechan peligros de todo género y necesita desplegar una actividad infatigable, una constante vigilancia para evitarlas. Tiene que recorrer su camino con pies de plomo, escrutando con mirada recelosa, pues te acechan toda clase de contingencias y de adversarios. Así caminaba en el estado salvaje y así camina ahora en las sociedades civilizadas. Jamás se encuentra seguro.
Qualibus in tenebris vitae; quantisque periclis Degitur hocc'aevi, quodcunque est! (Lucrecio 11, 15)
La vida de la mayor parte de los hombres no es más que la lucha constante por su existencia misma, con la seguridad de perderla al fin. Pero lo que les hace persistir en esta fatigosa lucha no es tanto el amor a la vida como el temor a la muerte, que, sin embargo, está en el fondo y de un momento a otro puede avanzar. La vida misma es un mar sembrado de escollos y arrecifes que el hombre tiene que sortear con el mayor cuidado y destreza, si bien sabe que aunque logre evitarlos, cada paso que da le conduce al total e inevitable naufragio, la muerte. Ella es la postrera meta de la fatigosa jornada, que le asusta más que los escollos que evita.
Pero también es muy digno de atención por una parte que los mismos dolores y males de la vida son fáciles de evitar, y que la misma muerte, en huir de la cual empleamos el esfuerzo de nuestra vida, es de desear y a veces corremos hacia ella gustosos, y por otra parte que tan pronto como la necesidad y el sufrimiento nos conceden una tregua, estamos tan próximos al tedio que deseamos que pasen las horas rápidas.
Lo que a todo ser vivo le ocupa y le pone en movimiento es la lucha por la vida. Pero con la vida una vez asegurada no hemos hecho nada aún; necesitarnos sacudir la carga del hastío, hacerla insensible, matar el tiempo, es decir, matar el aburrimiento. En consecuencia con esto vemos que todas las personas que han conseguido ponerse a cubierto de la necesidad y las preocupaciones por la subsistencia después de haber sacudido todas las cargas, son ellos una carga para sí mismos y ven con alegría cada hora que matan, es decir, cualquier abreviación de su vida, en cuya posible prolongación habían empleado hasta entonces todas sus fuerzas. El aburrimiento no es un mal que se deba tener en poco, deja en el rostro la huella de una verdadera desesperación. Hace que seres corno los hombres que tan poco se aman se busquen unos a otros, siendo por esto el origen de la sociabilidad. El mismo Estado se previene contra el aburrimiento de los ciudadanos como contra otras calamidades, porque este mal, así como su contrario, el hambre, puede lanzar al hombre a los mayores excesos: el pueblo necesita panem et Circenses. El riguroso sistema penitenciario de Filadelfia convierte en instrumento de suplicio el aburrimiento por medio de la soledad y la inacción; y es tan temible que los penados recurren al suicidio. Así como la necesidad es el látigo del pueblo, el tedio lo es de las gentes principales. En la vida burguesa está representado por el domingo, así como la necesidad por los seis días restantes de la semana.
Ahora bien, entre el querer y el lograr se desliza la vida humana. El deseo es por su naturaleza doloroso; la satisfacción engendra al punto la saciedad; el fin era sólo aparente; la posesión mata el estímulo; el deseo aparece bajo una nueva figura, la necesidad vuelve otra vez, y cuando no sucede esto, la soledad, el vacío, el aburrimiento, nos atormentan y luchamos contra éstos tan dolorosamente como contra la necesidad. Para que una vida transcurra felizmente es necesario que entre el deseo y la satisfacción no medie un tiempo ni demasiado corto ni demasiado largo, porque de este modo se reduce el sufrimiento que ambos causan. Pues lo que constituye la parte más hermosa de la vida y nos proporciona los más puros goces, a saber, el conocimiento puro, extraño a toda volición, el gusto de lo bello, los depurados placeres del arte, precisamente porque nos apartan de la vida real, transformándonos en espectadores desinteresados, como exigen raras dotes, sólo es patrimonio de unos pocos y aun a estos mismos sólo les divierte como un sueño pasajero y aún les hace más susceptibles de grandes sufrimientos y los aísla de los otros marcadamente inferiores a ellos, por lo que todo viene a compensarse. Pero a la inmensa mayoría de los hombres le son poco accesibles los goces intelectuales; el placer que proporciona el conocimiento puro les es casi completamente desconocido; están completamente entregados al deseo. Por lo cual, si algo les interesa deberá ser (y esto ya va en la significación misma de la palabra) lo que excite su voluntad, aunque no sea sino por una relación lejana y sólo posible con ella; pero no deben pasar de aquí, porque su esencia consiste más en el querer que en el conocer; la acción y la reacción es su único elemento. Expresan de la manera más ingeniosa esta su naturaleza, y así, por ejemplo, escriben su nombre en los lugares célebres que visitan, reaccionando de esta manera, actuando sobre las cosas, ya que las cosas no actúan sobre ellos; no pueden limitarse a observar una animal raro y exótico, sino que lo estimulan, lo excitan, juegan con ellos para sentir la acción y la reacción. Pero sobre todo, esta necesidad de estimular la voluntad se manifiesta en la invención y cultivo de los juegos de naipes, que es la expresión más apropiada de este lamentable lado de la humanidad.
Pero sea cual sea la acción de la naturaleza y de la suerte y trátese de quien se trate y de lo que posea, no se sustraerá nunca al dolor de vivir.
HhleidhV d’ ymwxen, idwn eiV ouranon eurun. (Pelides autem ejulavit, intuitus in coelum latum.)
Y a su vez.
ZhnoV mpn paiV ma KronionoV, autar oizun Eixon areiresimn. (Jovis quidem Filius eram Saturnii; verum aerumnam Habebam infinitam.)
Los esfuerzos incesantes para desterrar el dolor no consiguen otra cosa que variar su figura: ésta es primordialmente carencia, necesidad, cuidados por la conservación de la vida. Al que tiene la fortuna de haber resuelto este problema, lo que pocas veces sucede, le sale de nuevo el dolor al paso en mil otras formas, distintas, según la edad y las circunstancias, como pasiones sexuales, amores desgraciados, envidia, celos, odios, terrores, ambición, codicia, enfermedades, etcétera. Y cuando no puede revestir otra forma toma el ropaje gris y tristón del fastidio y el aburrimiento, contra el cual tantas cosas se han inventado. Y aunque se consiguiese alejar éste, difícil sería que no volviese en cualquiera de las otras formas para empezar otra vez su ronda; pues entre dolor y aburrimiento se pasa la vida. Por mucho que nos abatan estas consideraciones, quiero, sin embargo, poner la atención en uno de sus aspectos, del cual podemos obtener un consuelo y quizá una estoica indiferencia ante nuestras propias desdichas. Pues la impaciencia con que las conllevamos depende en gran parte de que las consideramos contingentes, es decir, como traídas por una serie de causas que muy bien pudiera haber sido otra. Pues los males que consideramos como necesarios y generales, la vejez y la muerte y muchas molestias de la vida diaria, no suelen preocuparnos. La consideración de que se trata de una circunstancia casual es precisamente lo que nos proporciona dolor, lo que da al hecho su aguijón. Pero si llegáramos a convencernos de que el dolor como tal es esencial e inseparable de la vida y la forma en que se presenta, lo único accidental y, dependiente del acaso, de que nuestra vida presente ocupa un lugar en el que sin cesar pronto sería reemplazada por otra alejada ahora del mismo y que por consiguiente el destino poco nos puede quitar; tal reflexión, en caso de convertirse en viva persuasión, podría suministrarnos una buena dosis de ecuanimidad estoica, disminuyendo en gran parte nuestros angustiosos temores egoístas. Pero de hecho, tal poderoso dominio de la razón sobre los dolores que sentimos de un modo inmediato pocas veces o nunca se encuentra.
Por lo demás, esta consideración sobre la inevitabilidad del dolor y la sustitución de unos dolores por otros y de la aparición de nuevos males por la repetición de los anteriores, puede llevarse hasta sostener la hipótesis, paradójica, pero no absurda, de que en cada individuo está determinada de antemano la medida del dolor que ha de soportar por su naturaleza, medida que no puede contener ni más ni menos de lo que en ella cabe, aun cuando la forma del dolor pueda variar. Según esto, su próspera o adversa fortuna no procedería del exterior, sino del interior y variaría por su disposición física en las distintas épocas, pero en conjunto sería la misma y no distinta de lo que se llama temperamento, o más exactamente, el grado que posee de sensibilidad ligera o fuerte, o como Platón dice en el primer libro de la República: eukoloV o duskoloV de humor fácil o difícil.
El hecho, observado frecuentemente, de que los grandes dolores nos hacen insensibles a los pequeños y a la inversa, que a falta de grandes sufrimientos, las más pequeñas contrariedades nos atormentan e irritan, habla a favor de esta hipótesis; pero además, la experiencia nos enseña que cuando soportamos una gran desgracia que sólo con pensar en ella nos estremecíamos, nuestro ánimo sigue siendo siempre el mismo una vez experimentado el dolor primero; y, a la inversa, cuando alcanzamos una dicha largo tiempo apetecida, apenas nos sentimos más contentos ni más alegres que antes, una vez pasado el primer momento. En el instante mismo de producirse tales cambios la emoción es fuerte y se sale de lo corriente, manifestándose en exclamaciones de desesperación o de júbilo, pero como efecto de una ilusión cesa pronto. La desesperación o el júbilo no eran debidos al dolor ni al gozo presentes, sino a la perspectiva de un porvenir anticipado. Lo que tan anormales proporciones les permite adquirir es esta anticipación de lo futuro; por eso se comprende que no puede tener gran duración.
Podemos hacer notar también en apoyo de nuestra hipótesis de que el sentimiento está en parte, como el conocimiento, determinado a priori que la alegría o la tristeza humanas no son producto de circunstancias exteriores, como la riqueza o la posición social, puesto que hallamos tantas caras alegres entre los ricos como entre los pobres. Notemos también que los motivos de suicidio son diferentes según los hombres, pues con dificultad hallaríamos desgracia alguna tan grande que condujera al suicidio en todos los caracteres y pocas hay entre las más pequeñas que no hayan conducido alguna vez a esta determinación. Por consiguiente, no siendo igual en todos los momentos el grado de alegría o de tristeza no lo debemos atribuir al cambio de las condiciones exteriores sino al del estado interior o a la disposición física. Cuando la satisfacción va creciendo, hasta llegar a la alegría, el cambio se produce ordinariamente sin motivo alguno exterior. En efecto, nuestro dolor es muchas veces provocado por algún accidente exterior, y esto es indudablemente lo que nos perturba y aflige, porque creemos que si hubiera modo de suprimir dicho accidente experimentaríamos gran contento. Pero esto es mera ilusión. La cantidad de alegría y tristeza, según hemos dicho, es siempre la misma en cada instante, y con respecto a ella aquel motivo de tristeza es lo que para el cuerpo un vejigatorio que llama a sí todos los males humores repartidos por el organismo. Sin esta causa determinada e interior el dolor correspondiente a nuestra naturaleza, y por lo tanto inexcusable, estaría repartido en muchos puntos y se manifestaría bajo la forma de mil pequeñas contrariedades o caprichos extravagantes por cosas a las que no damos en el momento importancia alguna, porque nuestra capacidad de dolor está saturada por un padecimiento mayor que ha concentrado en un solo punto todos los dolores distribuídos hasta entonces en diferentes lugares. Igualmente, si el éxito en un negocio nos libera de la inquietud que nos angustiaba, ésta es luego sustituida inmediatamente por otra, cuya sustancia existía ya en nosotros pero que no lograba aparecer en la conciencia para agitarla por estar ésta colmada ya; tal motivo de inquietud pasaba inadvertido, como forma nebulosa y sombría, en el límite extremo de nuestra conciencia. Pero cuando ha encontrado ya un puesto se destaca como cuidado positivo y ocupa el trono de la inquietud dominante (prutaennonsai); y aun siendo más pequeño que el desaparecido, sabrá hincharse hasta igualar en magnitud y así, como primer cuidado del día, llenar completamente el trono.
Las alegrías excesivas y los más vivos dolores se suelen encontrar en una misma persona, pues aquéllas y éstos se condicionan recíprocamente y tienen por condición común una gran vivacidad de espíritu. Según hemos visto, ambos provienen no tanto de lo presente como de la consideración de lo porvenir. Pero siendo el dolor esencial de la vida y hallándose determinado en sus proporciones por la naturaleza del individuo, los cambios repentinos provenientes del exterior no pueden determinar su grado. Toda alegría excesiva (exultatio, insolens laetitia) nace siempre de creer que hemos hallado en la vida una cosa que no puede hallarse jamás: la desaparición definitiva de los cuidados que nos atormentan y que renacen sin cesar. Cada una de estas ilusiones nos es arrebatada más tarde y su pérdida nos produce entonces tanto dolor como alegría nos produjo su aparición. Pudiéramos compararla a una montaña escarpada a la cual no se debe subir porque no hay modo de bajarla más que dejándose caer desde su cima: una caída de este género es todo dolor repentino y exagerado y procede de la pérdida de la ilusión. Siendo esto así, deberíamos evitar todo extremo, procurando contemplar el conjunto y el encadenamiento de las cosas con ánimo sereno y sin atribuirles nunca los colores que desearíamos que tuviesen. El empeño principal de la moral estoica fue emancipar el ánimo de esta quimera y de su consecuencia e inculcarle en cambio una perfecta ecuanimidad. De este mismo sentimiento se encuentra penetrado Horacio en la conocida oda:
Aequam memento rebus in arduis Servare mentem, non secus in bonis Ab insolenti temperatem Laetitia.
Pero la mayor parte de las veces nos negamos a aceptar esta idea, como nos negaríamos a beber una medicina amarga, esta idea de que el dolor es esencial a la vida y no proviene del exterior, sino que cada uno de nosotros lo llevamos dentro de nosotros mismos, como un manantial que no se agota. Siempre buscamos una causa o un pretexto exterior del dolor que no se separa de nosotros; somos como el hombre libre que se crea un ídolo para tener un amo. Pues infatigablemente volamos de deseo en deseo, y aunque ninguna realización, por mucho que prometa, pueda satisfacernos y no sea más que un vergonzoso error, nos empeñamos, no obstante, en no comprender que estamos haciendo el trabajo de las Danaides y corremos incesantemente hacia nuevos deseos.
Sed, dum abest quod avemus, id exsuperare videtur Caetera; post aliud, quum contigit illud, avemus; Et sitis aequae tenet vitai semper hiantes. (Lucr., III, 1095)
Así continuamos hasta el infinito, lo que es más raro y ya impone una cierta fuerza de carácter, hasta que encontramos su deseo que no podemos satisfacer ni renunciar; entonces poseemos en cierto modo lo que anhelamos, a saber: algo a lo que podemos achacar siempre el ser la causa de nuestros dolores, en vez de acusar a nuestro propio ser; este algo nos malquista con la suerte, pero nos reconcilia con la vida porque aleja de nuestro espíritu la idea de que el dolor es parte de nuestra naturaleza y de que toda dicha es imposible. La consecuencia de este proceso es una disposición algo melancólica. El hombre lleva en sí entonces un grande y único dolor que le hace olvidar todas las alegrías y todas las aflicciones menores. Esto constituye ya una actitud más digna que no la carrera incesante en pos de fantasmas que varían continuamente.

En El mundo como voluntad y representación, libro cuarto, § 57

viernes, 14 de octubre de 2011

Computación evolutiva: Ejemplo VI

Abstract

Para el ejemplo VI de la serie práctica de algoritmos evolutivos, he implementado un OCR básico, a partir del ejemplo anterior donde realizamos un algoritmo capaz de reconocer los patrones de las vocales mediante aprendizaje evolutivo.

El OCR es bastante básico, pero permite comprobar la potencia de la computación evolutiva en relación al mundo del reconocimiento de patrones.

Ejemplo práctico VI: OCR básico

El programa que os presento a continuación; programado en un applet de Java para que podáis probarlo de primera mano, consigue precisamente eso: aprender a reconocer los patrones de las vocales, en formato Times New Roma, y un tamaño de 8 (8 x 5 = 40 píxeles). Se utiliza una red neuronal cuyos pesos se ajustan mediante una estrategia evolutiva.

El programa actua como un OCR, consiguiendo capturar la información textual dentro de una imagen (en formato GIF), y transcribiendo dicha información en una caja de texto.

Para agilizar la programación necesaria, el OCR sólo reconoce vocales en formato Times New Roman, y un tamaño de letra de 8.

En cualquier momento del entrenamiento, podemos pulsar en el botón Cancelar, y comprobar cómo va mejorando el reconocimiento del patrón.

Una vez entrenada la red neuronal, debemos introducir la ruta url de la imagen a procesar (en formato GIF), y pulsar sobre el botón Pasar OCR.

Las instrucción de uso se pueden esquematizar así:

1) Pulsa sobre el botón Entrenar para que la red neuronal aprenda a reconocer las vocales (espera hasta obtener una media de 120 en la función de evaluación).
2) Abre el Paint de Windows.
3) Crea un nuevo documento, y escribe con la herramienta de texto (con formato Times New Roman, y tamaño 8) todas las vocales que quieras, en cualquier lugar del documento (que pueden no ser consecutivos ni estar alineadas).
4) Guarda la imagen en formato GIF.
5) Sube la imagen a un hosting gratuito de imágenes. Por ejemplo: www.imgur.com.
6) Copia la ruta de la imagen subida (comprueba que la url termina en extensión ".gif").
7) Pega la url en el applet, donde dice URL de la imagen a procesar.
8) Pulsar sobre el botón Pasar OCR.

¡Y listo! Si todo va bien, el applet reconocerá todas las vocales del texto, y las introducirá en orden (y manteniedo los espacios en blanco entre ellas).


Debajo del siguiente applet encontrarás un enlace con el código fuente del ejemplo:

No puede ejecutar applets de Java. Instale la JVM, y vuelva a recargar la página, por favor.

Podéis descargar el código fuente del ejemplo VI desde este enlace.


martes, 11 de octubre de 2011

Computación evolutiva: Ejemplo V

Abstract

El siguiente ejemplo práctico, consiste en una revisión del ejemplo anterior donde realizamos un algoritmo capaz de reconocer las vocales mediante aprendizaje evolutivo.

En esta versión, hemos introducido algo de ruido durante el entrenamiento y prueba del reconocimiento conseguido. El ruido consiste en la introducción o borrado de píxeles en la imagen de la vocal a reconocer.

Ejemplo práctico V: Proyecto evolutivo de reconocimiento de patrones con ruido

El programa que os presento a continuación; programado en un applet de Java para que podáis probarlo de primera mano, consigue precisamente eso: aprender a reconocer los patrones de las vocales, en formato Times New Roma, y un tamaño de 8 (8 x 5 = 40 píxeles). No se usa ninguna técnica estratégica mediante heurística, sólo una red neuronal cuyos pesos se ajustan mediante una estrategia evolutiva.

El programa, al ejecutarse inicialmente el applet, sólo no tiene "conocimiento" alguno, y se limita a dar una respuesta aleatoria, cuando se le presenta los píxles de la imagen de una vocal en Times New Roman.

Para conseguir, de manera on-line; que el programa aprenda a reconocer el patrón de las vocales, debemos pulsar sobre el botón Entrenar. En ese momento, el programa comenzará a seleccionar evolutivamente, los individuos que mejor aproximen sus respuestas cuando se le interroga por las vocales.

En cualquier momento del entrenamiento, podemos pulsar en el botón Cancelar, y comprobar cómo va mejorando el reconocimiento del patrón.

Debajo del siguiente applet con el ejemplo V, explicaré más en profundidad la teoría que sigue el proyecto de aprendizaje automático, y encontrarás un enlace con el código fuente del ejemplo:

No puede ejecutar applets de Java. Instale la JVM, y vuelva a recargar la página, por favor.

Podéis descargar el código fuente del ejemplo V desde este enlace.


Explicación técnica del ejemplo


El proyecto de ejemplo V, tiene las siguientes características técnicas:

Todo el aprendizaje corre a cuenta de una red neuronal; con conexión hacia delante y una capa oculta (hidden layer).

La capa de entrada contiene 40 nodos; uno por cada posible contenido dentro del array que forma el conjunto de píxles de la imagen de la vocal. Hay 5 nodos de salida, los cuales indican la probabilidad de que la vocal pasada sea la que representa sicho nodo de salida.

Inicialmente, los pesos wij de la red neuronal son marcados aleatoriamente, por lo que la respuesta de la red neuronal ante el problema será aleatorio.

Hay pues que entrenar al programa para que aprenda, lo que vamos a conseguir ajustando evolutivamente los pesos de la red neuronal utilizada. Dicho entrenamiento evolutivo se realizará mediante una estrategia evolutiva.

La estrategia evolutiva será representada por un vector de 210 elementos de tipo real. Esos elementos o individuos de selección se van a corresponder con los pesos de los nodos que contiene la red neuronal, de manera que serán esos pesos los que irán evolucionando.

Así pues, la población en evolución, consistirá en n individuos (con n = 15), que tendrán n hijos, con variación exclusiva por mutación -sin recombinación- y cuya función de desempeño (fitness fuction) será calculada mediante competición -selección por torneo-.
Para el proceso de mutación, hay que tener en cuenta que cada individuo; además de un vector de pesos, contiene un vector de variables de ajuste, que también irá evolucionando junto con los pesos.

La mutación es de la forma:



Con alpha igual 0.2f, y donde xi indica el peso en la posición i del vector de pesos, y N(0,1) indica un valor tomado aleatoriamente de una distribución normal de desviación típica igual a 1, y media igual a 0. La otra variable que interviene en el proceso se corresponde con la variable de ajuste del elemento i, que; como se puede ver, muta antes de que lo haga el peso xi.

La evaluación de un individuo se realiza mediante el recuento de los aciertos conseguidos cuando se le pasa las 5 vocales. Se sumará uno cuando se acierta, y se restará uno si falla en el reconocimiento.

Finalmente, el proceso de selección consistirá en tomar los 15 mejores individuos de entre los 30 (15 padres + 15 hijos) individuos de la generación en curso. En caso de empate en la función de desempeño, se favorecerá a los individuos más longevos.

El paso de generaciones tendrá como resultado un ajuste en los pesos de la red neuronal, lo que dará lugar a un entrenamiento de la misma, que será lo que permitirá; a su vez, al programa a reconocer los patrones de las vocales dadas en una imagen GIF.


lunes, 10 de octubre de 2011

Computación evolutiva: Ejemplo IV

Abstract

Siguiendo con la serie de ejemplos prácticos -pulsa aquí para ver el ejemplo III: Aprendizaje automático en el juego 4 en raya- y disponibles con licencia GPL, de algoritmos evolutivos, voy a mostrar ahora otro ejemplo práctico. En esta ocasión se trata de diseñar un algoritmo capaz de aprender a reconocer las letras de las vocales dadas en un archivo GIF.

El esquema principal seguido es el mismo del ejemplo II y III de la serie de ejemplos evolutivos prácticos que estoy desarrollando. Es decir; haciendo uso de estrategias evolutivas y una red neuronal se consigue un apndizaje automático por parte de un programa.

Ejemplo práctico IV: Proyecto evolutivo de reconocimiento de patrones

El programa que os presento a continuación; programado en un applet de Java para que podáis probarlo de primera mano, consigue precisamente eso: aprender a reconocer los patrones de las vocales, en formato Times New Roma, y un tamaño de 8 (8 x 5 = 40 píxeles). No se usa ninguna técnica estratégica mediante heurística, sólo una red neuronal cuyos pesos se ajustan mediante una estrategia evolutiva.

El programa, al ejecutarse inicialmente el applet, sólo no tiene "conocimiento" alguno, y se limita a dar una respuesta aleatoria, cuando se le presenta los píxles de la imagen de una vocal en Times New Roman.

Para conseguir, de manera on-line; que el programa aprenda a reconocer el patrón de las vocales, debemos pulsar sobre el botón Entrenar. En ese momento, el programa comenzará a seleccionar evolutivamente, los individuos que mejor aproximen sus respuestas cuando se le interroga por las vocales.

En cualquier momento del entrenamiento, podemos pulsar en el botón Cancelar, y comprobar cómo va mejorando el reconocimiento del patrón.

Debajo del siguiente applet con el ejemplo IV, explicaré más en profundidad la teoría que sigue el proyecto de aprendizaje automático, y encontrarás un enlace con el código fuente del ejemplo:

No puede ejecutar applets de Java. Instale la JVM, y vuelva a recargar la página, por favor.

Podéis descargar el código fuente del ejemplo IV desde este enlace.


Explicación técnica del ejemplo


El proyecto de ejemplo IV, tiene las siguientes características técnicas:

Todo el aprendizaje corre a cuenta de una red neuronal; con conexión hacia delante y una capa oculta (hidden layer).

La capa de entrada contiene 40 nodos; uno por cada posible contenido dentro del array que forma el conjunto de píxles de la imagen de la vocal. Hay 5 nodos de salida, los cuales indican la probabilidad de que la vocal pasada sea la que representa sicho nodo de salida.

Inicialmente, los pesos wij de la red neuronal son marcados aleatoriamente, por lo que la respuesta de la red neuronal ante el problema será aleatorio.

Hay pues que entrenar al programa para que aprenda, lo que vamos a conseguir ajustando evolutivamente los pesos de la red neuronal utilizada. Dicho entrenamiento evolutivo se realizará mediante una estrategia evolutiva.

La estrategia evolutiva será representada por un vector de 210 elementos de tipo real. Esos elementos o individuos de selección se van a corresponder con los pesos de los nodos que contiene la red neuronal, de manera que serán esos pesos los que irán evolucionando.

Así pues, la población en evolución, consistirá en n individuos (con n = 15), que tendrán n hijos, con variación exclusiva por mutación -sin recombinación- y cuya función de desempeño (fitness fuction) será calculada mediante competición -selección por torneo-.
Para el proceso de mutación, hay que tener en cuenta que cada individuo; además de un vector de pesos, contiene un vector de variables de ajuste, que también irá evolucionando junto con los pesos.

La mutación es de la forma:



Con alpha igual 0.2f, y donde xi indica el peso en la posición i del vector de pesos, y N(0,1) indica un valor tomado aleatoriamente de una distribución normal de desviación típica igual a 1, y media igual a 0. La otra variable que interviene en el proceso se corresponde con la variable de ajuste del elemento i, que; como se puede ver, muta antes de que lo haga el peso xi.

La evaluación de un individuo se realiza mediante el recuento de los aciertos conseguidos cuando se le pasa las 5 vocales. Se sumará uno cuando se acierta, y se restará uno si falla en el reconocimiento.

Finalmente, el proceso de selección consistirá en tomar los 15 mejores individuos de entre los 30 (15 padres + 15 hijos) individuos de la generación en curso. En caso de empate en la función de desempeño, se favorecerá a los individuos más longevos.

El paso de generaciones tendrá como resultado un ajuste en los pesos de la red neuronal, lo que dará lugar a un entrenamiento de la misma, que será lo que permitirá; a su vez, al programa a reconocer los patrones de las vocales dadas en una imagen GIF.


lunes, 3 de octubre de 2011

Cómo desaparecer completamente


Tristeza. Es lo único que desde hace años veo en sus ojos.

Esa que está ahí no es ella (That there, that's not she), porque ella siempre va donde quiere (She goes, where she pleases). Ella ya no está aquí (She’s not here), aunque parezca mentira (This isn't happening). Y en poco tiempo se irá (In a little while she'll be gone). Su tiempo ya ha pasado (The moment's already passed). Ya no está aquí (She’s not here), y no me lo puedo creer (This isn't happening).

La vida es dura, y cruel. Eso lo sabemos todos. Y sin embargo, tenemos motivaciones para luchar. Pero llega un momento; y, por supuesto, con suerte, en el que éstas desaparecen. Lo que nos hacía levantarnos cada mañana, aquello que nos hacía soportar la vida tal y como es.

Debe ser tan sencillo como levantarse un día, y darse cuenta de que ya no eres el que solías ser. Que ese que está ahí, no eres tú (That there, that’s not me). Dejas de ser útil a la sociedad, y te conviertes en una carga, en un ser inválido (The moment's already passed Yeah, it's gone).

Casi sin darte cuenta, ya no puedes hacer nada por ti mismo, no puedes cruzar la puerta
(I go where I please. I walk through walls).

Tormento. No creo que exista otra palabra que describa mejor vivir una vida sin motivaciones. Tormento que podemos ver en los ojos de cualquier anciano, en los ojos de cualquier persona no válida. Desesperación. Tristeza. Se puede casi palpar su constante lucha por querer vivir. Y no hay posible consuelo. Sólo podemos besarles, abrazarles, y; por supuesto, en cuanto podamos mirar hacia otro lado.

No es sólo una muestra más, sino la muestra más clara de la insoportable levedad del ser.

Nota:

La canción que has podido escuchar de fondo, es “How to disappear completely” del grupo Radiohead. A continuación tenéis la letra completa:

That there, that's not me
I go where I please
I walk through walls
I float down the Liffey

I'm not here
This isn't happening
I'm not here, I'm not here

In a little while
I'll be gone
The moment's already passed
Yeah, it's gone

I'm not here
This isn't happening
I'm not here, I'm not here

Strobe lights and blown speakers
Fireworks and hurricanes

I'm not here
This isn't happening
I'm not here, I'm not here....



sábado, 1 de octubre de 2011

Computación evolutiva: Ejemplo III

Abstract

Siguiendo con la serie de ejemplos prácticos -pulsa aquí para ver el ejemplo II: Aprendizaje de estrategias no-loss en el juego 3 en raya o Tic-tac-toe- y disponibles con licencia GPL, de algoritmos evolutivos, voy a mostrar ahora otro ejemplo práctico. En esta ocasión se trata de diseñar un algoritmo capaz de aprender por si solo a jugar bien al famoso juego Conecta 4 -o cuatro en línea-.

El esquema principal seguido es el mismo del ejemplo II de la serie de ejemplos evolutivos prácticos que estoy desarrollando. Es decir; se sigue la idea detrás de Blondie24; un juego de damas, implementado por David B. Fogel, que; haciendo uso de Estrategias Evolutivas y una red neuronal, consiguió que el programa aprendiera, tras 8 meses de entrenamiento, a jugar bien a las damas. Y tan bien aprendió, que consiguió un rating de 2048 –un 99,6% mejor que cualquier jugador humano-.

Ejemplo práctico III: Proyecto Evolutivo para el juego Cuatro en Línea -o Conecta 4-

El programa que os presento a continuación; programado en un applet de Java para que podáis probarlo de primera mano, consigue precisamente eso: aprender a jugar al Cuatro en Línea, sin enseñarle a priori ninguna técnica estratégica mediante heurística, sólo usando una red neuronal cuyos pesos se ajustarán mediante una estrategia evolutiva.

El programa, al ejecutarse inicialmente el applet, sólo "conoce" las reglas básicas del juego, cuándo termina la partida y el resultado de la misma, y sólo sabe prever si tu próximo movimiento la hará perder. Pero no entiende de estrategias, ni es capaz de jugar demasiado bien.

Si pulsamos en el botón Estadísticas, podremos ver cuántas partidas empata o pierde en este momento el programa tras jugar 150 veces contra un jugador que puede ver 6 jugadas hacia delante (ply =6).

Si pulsamos sobre el botón Jugar, veremos que, en cuanto pensamos un poco y aplicamos una estrategia correcta, comenzamos a ganar partidas.

Para conseguir, de manera on-line; que el programa aprenda a jugar, debemos pulsar sobre el botón Entrenar. En ese momento, el programa comenzará a competir consigo mismo una y otra vez, mejorando con el paso de tiempo –de las generaciones- de manera automática su juego. Irá aprendiendo buenas estrategias de juego.

En cualquier momento del entrenamiento, podemos pulsar en el botón Cancelar, y comprobar cómo va mejorando el juego del programa. Si jugamos contra él, veremos que estrategias con las que antes le ganábamos, ahora ya no son efectivas, o que, si pulsamos sobre el botón Estadísticas, el porcentaje de partidas perdidas va disminuyendo.

Debajo del siguiente applet con el ejemplo III, explicaré más en profundidad la teoría que sigue el proyecto de aprendizaje automático, y encontrarás un enlace con el código fuente del ejemplo:

No puede ejecutar applets de Java. Instale la JVM, y vuelva a recargar la página, por favor.

Podéis descargar el código fuente del ejemplo III desde este enlace.


Explicación técnica del ejemplo


El proyecto de ejemplo III, tiene las siguientes características técnicas:

Toda la estrategia de juego, corre a cuenta de una red neuronal; con conexión hacia delante y una capaa oculta (hidden layer).

La capa de entrada contiene 42 nodos; uno por cada posible contenido dentro del array que forma el tablero de juego –con un 1 si la ficha de una casilla es propia, un -1 si la ficha es del adversario, y un 0 si la casilla está vacía-, y un nodo de salida, responsable de devolver el resultado del proceso neuronal: expresando lo bueno o malo que un movimiento concreto es.

Inicialmente, los pesos wij de la red neuronal son marcados aleatoriamente, por lo que la respuesta de la red neuronal ante el problema sobre qué buena o mala es una jugada será también aleatoria.

Hay pues que entrenar al programa para que aprenda a evaluar las jugadas, lo que vamos a conseguir ajustando evolutivamente los pesos de la red neuronal utilizada. Dicho entrenamiento evolutivo se realizará mediante una estrategia evolutiva.

La estrategia evolutiva será representada por un vector de 1848 elementos de tipo real. Esos elementos o individuos de selección se van a corresponder con los pesos de los nodos que contiene la red neuronal, de manera que serán esos pesos los que irán evolucionando.

Así pues, la población en evolución, consistirá en n individuos (con n = 15), que tendrán n hijos, con variación exclusiva por mutación -sin recombinación- y cuya función de desempeño (fitness fuction) será calculada mediante competición -selección por torneo-.
Para el proceso de mutación, hay que tener en cuenta que cada individuo; además de un vector de pesos, contiene un vector de variables de ajuste, que también irá evolucionando junto con los pesos.

La mutación es de la forma:



Con alpha igual 0.2f, y donde xi indica el peso en la posición i del vector de pesos, y N(0,1) indica un valor tomado aleatoriamente de una distribución normal de desviación típica igual a 1, y media igual a 0. La otra variable que interviene en el proceso se corresponde con la variable de ajuste del elemento i, que; como se puede ver, muta antes de que lo haga el peso xi.

La evaluación de un individuo se realiza mediante q partidas (con q = 15), jugadas entre el individuo a evaluar, y otro individuo de la población, tomado aleatoriamente sin reemplazamiento. Cada partida ganada le sumará 1 punto, las perdidas le restará 2, y los empates no suman nada. El valor final será su función de desempeño.

Finalmente, el proceso de selección consistirá en tomar los 15 mejores individuos de entre los 30 (15 padres + 15 hijos) individuos de la generación en curso. En caso de empate en la función de desempeño, se favorecerá a los individuos más longevos.

El paso de generaciones tendrá como resultado un ajuste en los pesos de la red neuronal, lo que dará lugar a un entrenamiento de la misma, que será lo que permitirá; a su vez, al programa a jugar bien, sin intervención heurística alguna.

Detalles adicionales


1º) Para que el aprendizaje automático tenga lugar, es necesario; al menos, prever un movimiento del contrario por adelantado -qué hará él si yo muevo aquí-. Esto se consigue mediante el uso de un árbol min-max de profundidad 2 (ply=2 ), lo que es insuficiente para que la máquina comprenda estrategias, pero que sí permite una base para el ajuste de pesos de la red neuronal. Pero siempre evaluar lo bueno o malo de una jugada será objeto de la red neuronal. Es decir; aunque hay un árbol min-max de ply igual a dos, la evaluación de la tabla de tu movimiento y el mejor movimiento del contrario, la desempeña la red neuronal y no ninguna regla a priori.

2º) La red neuronal utilizada va a devolver siempre un valor real en el rango [-1,1]. Un -1 sólo cuando el movimiento a evaluar termina siendo una victoria del adversario, un 1 si la victoria es suya, y un valor entre (-1, 1) indicando lo bueno que es una jugada para el programa (valores cercanos a 1) o para el contrario (valores cercanos a 1).

3º) El campo llamado Info, en el lateral superior derecho del applet, contiene información sobre el estado del entrenamiento. Conforme pasan las generaciones, la pantalla se actualiza, y muestra datos sobre el mejor individuo de la última generación: su función de desempeño, su edad -cuanto tiempo lleva en el pool evolutivo-, y la media de su juego -resultados/edad-. Cuando juegues contra la máquina, te mostrará la misma información, pero sobre jugador contra el que estás jugando -el mejor que se encontró-.


viernes, 2 de septiembre de 2011

Computación evolutiva: Ejemplo II

Abstract

Siguiendo con la serie de ejemplos prácticos -pulsa aquí para ver el ejemplo I- y disponibles con licencia GPL, de algoritmos evolutivos, voy a mostrar ahora algo que, bien entendido; se comprende es un logro importante, que debemos a dos fundamentales ramas de la IA; como son la computación evolutiva y las redes neuronales.

Estoy hablando de la posibilidad de crear programas que adquieran “conocimiento” estratégico automáticamente sin necesidad de que un programador humano implemente ninguna regla heurística de aprendizaje.

La idea de este ejemplo parte de Blondie24; un juego de damas, implementado por David B. Fogel, que; haciendo uso de Estrategias Evolutivas y una red neuronal, consiguió que el programa aprendiera, tras 8 meses de entrenamiento, a jugar bien a las damas. Y tan bien aprendió, que consiguió un rating de 2048 –un 99,6% mejor que cualquier jugador humano-.

Lo increíble de Blondie24, era que el programador, en ningún momento implementó reglas heurísticas para el correcto funcionamiento del programa. No se escribió qué jugadas son mejores que otras, ni se propuso ningún mecanismo de recompensa por buenas jugadas, ni hizo falta la intervención humana en el aprendizaje en modo alguno. El programador sólo implementó de forma prestablecida las reglas básicas del juego: qué movimientos están permitidos hacer y cuándo alguien ha ganado el juego, y fue Blondie24 la que “aprendió” a jugar, como lo hace cualquier jugador humano –al que nadie le enseñe estrategias sino que aprenda por si mismo-: progresivamente mediante ensayo y error.

Ejemplo práctico II: Proyecto Noelia

Utilizando la teoría tras Blondie24, y algunos de los detalles geniales que David B. Fogel introdujo en su proyecto, me propuse hacer algo similar, pero a menor escala; para poder afrontarlo en poco tiempo, aunque sin perder su esencia.

Para conseguirlo, opté por implementar un programa que “aprenda” por si sólo buenas estrategias de un juego, mucho más elemental que las damas, y bastante utilizado en los ejemplos teóricos de IA: el juego del tres en raya, también conocido como tic-tac-toe. Aunque; tengo que reconocer, finalmente, me propuse algo un poco más ambisioso que eso, consguir no sólo que juegue buenas estrategias, sino que alcance estrategias con las que nunca pierda (no-loss-strategy).

El programa que os presento a continuación; programado en un applet de Java para que podáis probarlo de primera mano, consigue precisamente eso: aprender a jugar al tres en raya, sin enseñarle a priori ninguna técnica estratégica mediante heurística, sólo usando una red neuronal cuyos pesos se ajustarán mediante una estrategia evolutiva.

El programa, al ejecutarse inicialmente el applet, sólo "conoce" las reglas básicas del juego, cuándo termina la partida y el resultado de la misma, y sólo sabe prever si tu próximo movimiento la hará perder. Pero no entiende de estrategias, ni es capaz de jugar bien. Al comienzo, el programa tiene el nivel de una persona muy novata en el juego.

Si pulsamos en el botón Estadísticas, podremos ver cuántas partidas empata o pierde en este momento el programa tras jugar 50 veces contra un jugador experto -simulado mediante heurística, con un árbol min-max y profundidad (ply) de 9 movimientos-. De media, antes de entrenar, el programa perderá un 50% de sus partidas contra dicho jugador experto.

Si pulsamos sobre el botón Jugar, veremos que, en cuanto pensamos un poco y aplicamos una estrategia correcta, comenzamos a ganar partidas.

Para conseguir, de manera on-line; que el programa aprenda a jugar, debemos pulsar sobre el botón Entrenar. En ese momento, el programa comenzará a competir consigo mismo una y otra vez, mejorando con el paso de tiempo –de las generaciones- de manera automática su juego. Irá aprendiendo buenas estrategias de juego.

En cualquier momento del entrenamiento, podemos pulsar en el botón Cancelar, y comprobar cómo va mejorando el juego del programa. Si jugamos contra él, veremos que estrategias con las que antes le ganábamos, ahora ya no son efectivas, o que, si pulsamos sobre el botón Estadísticas, el porcentaje de partidas perdidas va disminuyendo.

Pasadas las generaciones –si se le permite entrenar bastante tiempo-, el programa será igual de efectivo que un jugador humano. Lo que significa que; lo mejor que podrás conseguir contra él serán tablas. El aprendizaje ocurrirá poco a poco, lo que significa que verás una mejora gradual en su calidad de juego. Por poner un ejemplo; es de notar que, tras pasar algunas generaciones de entrenamiento; el programa "comprende" que la mejor estrategia de juego consiste en mover a la casilla central siempre que esté libre.

Hay de tener en cuenta, que sólo notarás una mejora realmente significativa en la calidad de juego a partir de trascurridas algunas generaciones, y que un juego de estrategia no-loss suele llegar aproximadamente en 150 generaciones.

Debajo del siguiente applet con el ejemplo II, explicaré más en profundidad la teoría que sigue el proyecto de aprendizaje automático, y encontrarás un enlace con el código fuente del ejemplo:

No puede ejecutar applets de Java. Instale la JVM, y vuelva a recargar la página, por favor.

Podéis descargar el código fuente del ejemplo II desde este enlace.


Explicación técnica del ejemplo


El proyecto de ejemplo II, tiene las siguientes características técnicas:

Toda la estrategia de juego, corre a cuenta de una red neuronal; con conexión hacia delante y dos capas ocultas (hidden layer).

La capa de entrada contiene nueve nodos; uno por cada posible contenido dentro del array que forma el tablero de juego –con un 1 si la ficha de una casilla es propia, un -1 si la ficha es del adversario, y un 0 si la casilla está vacía-, y un nodo de salida, responsable de devolver el resultado del proceso neuronal: expresando lo bueno o malo que un movimiento concreto es.

Tenemos así una red neuronal compuesta de 9 nodos de entrada xi, 21 nodos en las capas ocultas, y un nodo de salida. Más otros 9 nodos de activación (nodos bias).

Inicialmente, los pesos wij de la red neuronal son marcados aleatoriamente, por lo que la respuesta de la red neuronal ante el problema sobre qué buena o mala es una jugada será también aleatoria.

Hay pues que entrenar al programa para que aprenda a evaluar las jugadas, lo que vamos a conseguir ajustando evolutivamente los pesos de la red neuronal utilizada. Dicho entrenamiento evolutivo se realizará mediante una estrategia evolutiva.

La estrategia evolutiva será representada por un vector de 9 elementos de tipo real. Esos elementos o individuos de selección se van a corresponder con los pesos de los nodos que contiene la red neuronal, de manera que serán esos pesos los que irán evolucionando.

Así pues, la población en evolución, consistirá en n individuos (con n = 15), que tendrán n hijos, con variación exclusiva por mutación -sin recombinación- y cuya función de desempeño (fitness fuction) será calculada mediante competición -selección por torneo-.
Para el proceso de mutación, hay que tener en cuenta que cada individuo; además de un vector de pesos, contiene un vector de variables de ajuste, que también irá evolucionando junto con los pesos.

La mutación es de la forma:



Con alpha igual 0.2f, y donde xi indica el peso en la posición i del vector de pesos, y N(0,1) indica un valor tomado aleatoriamente de una distribución normal de desviación típica igual a 1, y media igual a 0. La otra variable que interviene en el proceso se corresponde con la variable de ajuste del elemento i, que; como se puede ver, muta antes de que lo haga el peso xi.

La evaluación de un individuo se realiza mediante q partidas (con q = 15), jugadas entre el individuo a evaluar, y otro individuo de la población, tomado aleatoriamente sin reemplazamiento. Cada partida ganada le sumará 1 punto, las perdidas le restará 2, y los empates no suman nada. El valor final será su función de desempeño.

Finalmente, el proceso de selección consistirá en tomar los 15 mejores individuos de entre los 30 (15 padres + 15 hijos) individuos de la generación en curso. En caso de empate en la función de desempeño, se favorecerá a los individuos más longevos.

El paso de generaciones tendrá como resultado un ajuste en los pesos de la red neuronal, lo que dará lugar a un entrenamiento de la misma, que será lo que permitirá; a su vez, al programa a jugar bien, sin intervención heurística alguna.

Detalles adicionales


1º) Para que el aprendizaje automático tenga lugar, es necesario; al menos, prever un movimiento del contrario por adelantado -qué hará él si yo muevo aquí-. Esto se consigue mediante el uso de un árbol min-max de profundidad 2 (ply=2 ), lo que es insuficiente para que la máquina comprenda estrategias, pero que sí permite una base para el ajuste de pesos de la red neuronal. Pero siempre evaluar lo bueno o malo de una jugada será objeto de la red neuronal. Es decir; aunque hay un árbol min-max de ply igual a dos, la evaluación de la tabla de tu movimiento y el mejor movimiento del contrario, la desempeña la red neuronal y no ninguna regla a priori.

2º) La red neuronal utilizada va a devolver siempre un valor real en el rango [-1,1]. Un -1 sólo cuando el movimiento a evaluar termina siendo una victoria del adversario, un 1 si la victoria es suya, y un valor entre (-1, 1) indicando lo bueno que es una jugada para el programa (valores cercanos a 1) o para el contrario (valores cercanos a 1).

3º) Hay un check box llamado no-loss, que, cuando se activa, indica que el proceso de entrenamiento, se hará jugando contra el jugador experto simulado mediante heurística. De esa manera, el aprendizaje es más rápido y siempre termina (tras unas 100 generaciones aproximadamente) alcanzando una estrategia no-loss. Cuando el check está desactivado, el entrenamiento se realiza contra sí mismo y podría ser que la estrategia no-loss no se alcance -aunque siempre quedará cerca-.

4º) El campo llamado Info, en el lateral superior derecho del applet, contiene información sobre el estado del entrenamiento. Conforme pasan las generaciones, la pantalla se actualiza, y muestra datos sobre el mejor individuo de la última generación: su función de desempeño, su edad -cuanto tiempo lleva en el pool evolutivo-, y la media de su juego -resultados/edad-. Cuando juegues contra la máquina, te mostrará la misma información, pero sobre jugador contra el que estás jugando -el mejor que se encontró-.

5º) La mejor manera de saber si hemos llegado a una estrategia no-loss, es ir mirando la pantalla de información, y esperar a que aparezcan datos en los que el mejor jugador encontrado tiene f = 0.0 y una edad avanzada (más de 20). Si pulsamos sobre el botón cancelar, y luego sobre el botón Estadísticas, deberá devolver siempre un 100% de empates contra el jugador experto.

Conclusión

Lo que pensé iba a ser un ejemplo fácil de conseguir, ha resultado ser una verdadera pesadilla, que me ha llevado un par de semanas de mi tiempo libre.

Mi cabezonería me ha impedido abandonar, y ahora me alegro, porque aprendí muchísimo sobre redes neuronales, y he podido avanzar en este campo de la IA que me parece fundamental, y que creo hace un muy buen equipo con la computación evolutiva. Es más, me atrevería a decir, que el futuro desarrollo de ambas ramas podría llegar a conseguir una verdadera inteligencia artificial. No sé si viviré para verlo, pero aquí queda dicho :).

Gracias por compartir vuestro tiempo conmigo, espero que el proyecto les haya gustado, y el código fuente les pueda servir de ejemplo.

domingo, 21 de agosto de 2011

Computación evolutiva: Ejemplo I

Abstract

Entendemos por un algoritmo evolutivo, a aquel cuya técnica resolutiva se inspira en la evolución biológica de la naturaleza; imitando de esa manera, el proceso de selección natural que, desde Darwin, es aceptado como motor del proceso evolutivo de los seres vivos.
Existe en la red, abundante información teórica al respecto de la computación evolutiva, pero en muy pocos casos, me he encontrado con ejemplos prácticos concretos, donde se pongan en práctica dichos principios.

De manera que me he propuesto intentar poner mi granito de arena práctico en esta rama tan interesante de la inteligencia artificial, iniciando lo que espero sea una serie de artículos con ejemplos funcionales -de código abierto-, para ayudar a cualquiera que quiera adentrarse en la materia. Además, espero que realizar esta propuesta, me ayudará a profundizar en mis conocimientos sobre el tema, y prepararme así para realizar el doctorado en ingeniería, que me gustaría algún día tener tiempo de hacer.

Composición y funcionamiento de un algoritmo evolutivo (AE)

Sin duda, el mejor recurso bibliográfico para iniciarse en el mundo de la CE -compuactión evolutiva- es el magnífico libro Introduction to Evolutionary Computing, de los autores Agoston E. Eiben, y J.E. Smith.

Hemos dicho que la computación evolutiva es una ciencia computacional en la que sus algoritmos imitan el proceso evolutivo de la naturaleza. Veamos de qué partes consta, y cómo funcionan:

Cualquier AE seguirá el siguiente pseudocódigo:



Existe una población de n individuos, los cuales expresan una posible solución. La población es pues, un multiconjunto de genotipos, y forman la unidad de evolución.

Los individuos de la población se van renovando en sucesivas generaciones, que van convergiendo evolutivamente hacia la meta deseada, que no es otra que encontrar una solución a un problema determinado. La evolución se produce durante el paso de las generaciones, y cada generación cumple con el siguiente procedimiento:

La primera generación es especial, y sólo consiste en la generación (y evaluación) de una población inicial de n individuos, normalmente generados aleatoriamente.

El resto de generaciones comienzan con la selección de los individuos que se van a reproducir. Es decir, se seleccionan los padres que conformarán la descendencia.
Y dicha descendencia, será el resultado de un proceso de recombinación y mutación de esos padres previamente seleccionados. Tras la creación de la descendencia, se procede a evaluar su adaptabilidad. Es decir; se calcula, lo bien o mal que se adapta el nuevo individuo a las condiciones del medio ambiente. Este proceso se suele realizar, mediante el uso de una funcion de desempeño (fitness function). Dicha función representará la adaptabilidad del fenotivo expresado por el genotipo en cuestión. Una vez evaluada la progenie, se procede a seleccionar los individuos que finalmente prevalecerán para formar la siguiente generación.

Todo este proceso generacional, se repetirá mientras no se cumpla una condición de parada. La condición de parada normalmente será, o bien que uno o varios genomas expresan un fenotipo que es solución óptima del problema a resolver, o que se alcanzó el máximo de generaciones previstos programáticamente.

Ejemplo I: Proyecto Paula

A continuación, vamos a ver en acción un algoritmo evolutivo, que he escrito en un applet de Java, y que tenéis embebido justo debajo de estas líneas –a continuación del applet tenéis un manual de uso- :

No puede ejecutar applets de Java. Instale la JVM, y vuelva a recargar la página, por favor.

Podéis descargar el código fuente del ejemplo desde este enlace.

El mismo algoritmo escrito completamente en Javascript, utilizando hilos mediante la nueva clase Worker nativa de HTML5 (puedes obtener el código fuente en los recursos del navegador, ya que no hay código ofuscado):


Manual de uso

Mediante el algoritmo del ejemplo, vamos a intentar encontrar la solución óptima -si existe-, o la mejor aproximación posible, al siguiente problema:

Tenemos como entrada (inputs), una serie de puntos definidos para ciertos valores de una variable, y queremos buscar la función de interpolación que sea óptima -que pase por todos los puntos dados como entrada- o lo más aproximada posible.

Para introducir los inputs en el applet, debemos rellenar el campo de texto "Datos de interpolación", añadiendo cuantos puntos deseemos. Una entrada válida sería, por ejemplo:

f0 = 0, f1 = 1, f2 = 4, f3 = 9

Si ahora, pulsamos sobre el botón Ejecutar algoritmo evolutivo, el programa finalizará, encontrando una de las muchas funciones que interpolan esos puntos, por ejemplo:

f(x) = x*x ó f(x) = x*x+x-x

Para recalcar la potencia del proceso evolutivo por selección, se propone la posibilidad de intentar encontrar la solución, sin realizar la recombinación, mutación y selección de los más aptos, lo que resulta en un algoritmo completamente aleatorio que muy poco probablemente llegará a solucionar el problema. Para probar dicho algoritmo aleatorio, hay que pulsar sobre el botón Ejecutar algoritmo aleatorio.

Para probar la potencia del programa, se ha habilitado la posibilidad de calcular automáticamente los puntos a interpolar, a partir de una función de nuestra elección. Con esto, nos aseguramos además, de que existe solución óptima. Sólo hay que pulsar sobre el botón Buscar datos de interpolación, y se nos abrirá una ventana donde deberemos escribir la función generadora de n términos de interpolación.

Es importante señalar, que sólo se permite los operadores + - * /, y que la prioridad de los mismos es la tradicional. Se permiten números con decimales (con el punto como separador, y una precisión máxima de cuatro decimales), pero no el operador unitario -.

Al finalizar la ejecución del algoritmo, se abrirá una nueva ventana emergente con un par de gráficas, la primera representando los puntos de interpolación, y una segunda con los puntos representados por la función encontrada por el programa. Ambas gráficas coincidirán siempre y cuando se encuentre una solución óptima -si es que existe-.

Análisis del algoritmo

La representación del genotipo en nuestro ejemplo, consiste en un array de caracteres, conformando una cadena de texto, con los siguientes símbolos permitidos:{[0-9],x,+,-,/,*}

Además, los símbolos deben respetar un patrón que forme cadenas con sentido aritmético (pero no se permite usar el símbolo - como operador unitario), permitiéndose genotipos como 2*x*x-1/2*x+23, pero no otros como 2+-x/*.

El AE del ejemplo I responde al siguiente pseudocódigo:


t <- 0
Inicialización P(t)
Evaluación P(t)
hacer
S <- Seleccion_padres[P(t)]
Q = Operacion_variación[S]
Evaluación[Q)]
P(t+1) <- selección[P(t) U Q]
t <- t + 1
mientras no condición de parada



donde S, Q, y P son poblaciones de individuos, y t es una variable de tipo entera.

El algoritmo comienza creando aleatoriamente una población P de n individuos, y evaluando la función de desempeño de sus individuos -lo cerca o lejos que están de ser una solución óptima al problema-.

Posteriormente se entra en un bucle del que sólo se saldrá cuando se cumpla que, o bien en la población P(t) hay un individuo que es solución óptima, o bien se ha llegado al máximo de iteraciones previsto en la configuración del programa.

Cada iteración del bucle, se va a corresponder con una nueva generación de individuos, formada por parte de los padres de la generación anterior y por parte de su progenie.

Para realizar una generación se procede como sigue:

1) Se forma una población S con los individuos que van a poder procrear. En el caso concreto de mi ejemplo, todos los miembros de la población P van a tener descendencia. Es decir; S = P(t).

2) Se procede a crear la descendencia mediante recombinación, y mutación. En el ejemplo, se van seleccionando pares de individuos de S y se recombinan dando lugar a cuatro hijos.

Para recombinar, seleccionamos al padre y lo dividimos por un punto aleatorio p (quedando el padre partido en dos partes: padre1 y padre2). Hacemos lo mismo con la madre en un punto p', y se crean los hijos de la siguiente manera:

h1 = padre1 + madre2
h2 = madre1 + padre2
h3 = padre1
h4 = padre2


Posteriormente, cada hijo, sufrirá -con 100% de probabilidad- algún tipo de mutación puntual, que podrá ser más o menos leve, y que podrá modificar algún valor concreto del genotipo del individuo, o añadir o eliminar partes del mismo.

El proceso de reproducción, se repetirá 16 veces por cada par de padres, lo que dará un total de n*16*4 hijos en cada generación -siendo n el número de individuos en la población-.

3) El siguiente paso es evaluar la adaptabilidad de esta población Q que conforman la nueva descendencia.

4) Por último, se procede a seleccionar aquellos individuos que mejor se adaptan al medio de la unión del conjunto de padres de la generación anterior P(t) y el conjunto de sus hijos Q. Como resultado, se obtiene el conjunto P de la generación siguiente, con los supervivientes del proceso de selección.

En nuestro ejemplo, la evaluación de un genotipo se procesa mediante la aplicación de una función de desempeño (fitness function) al mismo. En este caso, la función será el resultado de sumar la diferencia en valor absoluto entre g(x) y el input en dicho punto: abs(y-g(x)).

Un genotipo óptimo, será aquel cuya función de desempeño tenga valor 0.

Para el AE del ejemplo, la selección generacional, se hace ordenando los individuos de [P(t) U Q] según su valor de adaptación -cuanto menos diferencia entre g(x) y los puntos de interpolación mejor-, y seleccionando los s primeros, desechándose de esa manera el resto.

Resumen

El algoritmo evolutivo propuesto, es capaz de encontrar; si existe, una solución óptima al problema con bastante frecuencia, aunque; como en todo AE existe la posibilidad de que la evolución generacional se localice en torno a una solución local no óptima -pero muy cercana de serlo-. Sin embargo, la aleatoriedad de la fase de mutación, permite teóricamente recomenzar un nuevo camino evolutivo en cualquier momento -aunque es menos probable que esto ocurra conforme pasan las generaciones-.