Saltar al contenido

1

Hace unos meses (aunque yo me he enterado por el radar de O'Reilly hace unos días y en sinapsis tienen un artículo muy completo), IBM presentó a Watson, un sistema de procesamiento de lenguaje natural pensado para participar en el concurso de preguntas y respuestas Jeopardy! Al igual que con la victoria de Deep Blue en ajedrez, IBM busca superar un reto en el que vencer a los humanos en tareas inalcanzables (por ahora) para los ordenadores. El video es muy bueno:

Algunas ideas que se me ocurren sobre Watson:

  • Procesa lenguaje natural escrito: No sólo procesa el lenguaje de manera estadística o semántica, sino que un concurso cómo Jeopardy! requiere que Watson sea capaz de comprender juegos de palabras, información omitida, dobles sentidos... Lenguaje en la forma más humana (e incomprensible para las máquinas actuales).
  • Agrupa múltiples paradigmas/tecnologías: Creo que alguna vez lo he comentado, mi opinión es que no se puede utilizar un sólo enfoque para crear un sistema de inteligencia artificial, a menos que esté dedicado a un problema muy concreto. En este caso, Watson integra las tecnologías de procesamiento de lenguaje natural, recuperación de información, representación del conocimiento y aprendizaje automático.
  • Utiliza varios algoritmos simultáneamente: no sólo agrupa varias tecnologías, sino que para hacer búsquedas utiliza varios algoritmos de búsqueda de información y los integra para calcular un valor de confianza en la respuesta. Si con el algoritmo X cálcula que una respuesta tiene un 50% de ser cierta y el algoritmo Y corrobora la respuesta, entonces la confianza en la respuesta aumenta.
  • Cálculo de confianza: Es el algoritmo fundamental de éste sistema. Tiene pinta de estar basado en cálculos probabilísticos complejos, diría que es donde más han trabajado y dónde los algoritmos de aprendizaje tienen más margen para funcionar. El hecho de que sea estadístico es también muy importante: demuestra que no es necesario que un ordenador tenga un pensamiento binario y le aporta cierta capacidad de fallo "discontinuo" más parecido al razonamiento humano.
  • Tiene problemas con las frases de doble sentido: es lógico que sea así, es la parte que peor procesa un ordenador. Cómo se ven en el video,  lo peor no es que se equivoque en la respuesta sino que se equivoque en el sentido de la respuesta: que le pregunten por una persona y conteste con el título de una película.
  • Test de Turing: es capaz de engañar a las personas. En muchos sitios los comentarios eran exagerados, cómo si Watson fuese capaz de mantener una conversación en lenguaje natural. Aunque el presentador lee la pregunta, a Watson le llega por escrito. Por otra parte es sorprendente el efecto sobre los concursantes humanos, se sienten amenzados (Humans! grita un concursante) y despierta un orgullo de defensa de la humanidad (cómo si Watson no fuese una creación humana que se apaga desconectando un botón).
  • Test de Turing(2): Si no está seguro de una respuesta, no contesta. En este caso se trata de una estrategia para ganar el juego, pero también supone una imitación del comportamiento humano. Es el tema del artículo de Mike Loukides, que el Test de Turing no consiste en demostrar que una máquina es inteligente sino que es inteligente en el sentido humano, incluyendo sus fallos. Por ahora no es así, cómo demuestra el tipo de errores que Watson comete. No son los fallos que comete una persona.
  • Utiliza uima de apache: una arquitectura open source para el procesamiento de información no estructurada. Aunque no vayan a liberar el código fuente, es importante que se utilicen plataformas open source, garantiza que al menos una parte del trabajo se compartirá.
(por ahora)

Esta vez he encontrado la inspiración para escribir una entrada en la publicidad contextual de google que tengo en este blog (como dije me parece un ejemplo de inteligencia artificial en la práctica). Éstos son algunos anuncios que han aparecido últimamente:

publicidad google

Los anuncios del bloque superior están relacionados con el tarot y las predicciones del futuro. El algoritmo de google de análisis de textos ha detectado que algunas de mis últimas entradas está relacionadas con el tarot y el futuro. Sin embargo estas entradas no tratan sobre el tarot o la futurología, sino sobre inteligencia artificial que simula un tarot o un divino del futuro. Es una diferencia sutil, difícil de discriminar incluso para una persona, pero es la forma en la que suelen fallar los algoritmos de comprensión de textos actuales. Sin embargo, el algoritmo es lo suficientemente bueno como se puede ver en el pantallazo en los anuncios del latral izquierdo: anuncios apropiados sobre robótica.

1

¿Qué tienen en común la ineligencia arificial, la semántica y la publicidad? En principio parece que muy poco, son términos que estamos acostumbrados a verlos por separado. Sin embargo como explican en el blog de google adsense la semántica y la publicidad están relacionadas. Y aunque no lo menciona, el análisis de textos para la comprensión semántica es un problema de la inteligencia artificial.

Se puede decir que actualmente es lo suficientemente buena para que la pubicidad contextual funcione, pero no es todo lo buena que podría ser. De forma elegante en esa entrada comentan que la definición de relevancia difiere de a definición humana. ¿Existe una definición de relevancia no humana? Creo que no, el problema es que la relevancia humana es dificil de modelar y por eso los resultados difieren.

En cualquier caso se trata de un problema complejo que está siendo resuelto con técnicas propias de la inteligencia artificial y que es relevante porque una empresa tecnológica como google depende de que su tecnología esté a la vanguardia.


1

El segundo elemento que tengo pensado utilizar en mi nuevo proyecto se trata de sbn una librería ruby que permite trabajar con redes bayesianas. Aunque la inferencia está limitada a un algoritmo de resolución aproximado, el Markov Chain Monte Carlo, por lo poco que he probado, esta librería es suficiente para mi proyecto, aunque me gustaría que tuviese algún método de inferencia exacto para poder comparar.

Voy a poner un código sencillo y de paso comento algún detalle de este proyecto sin nombre. Me he decidido a implementar un "adivino del futuro" que funciona de forma aleatoria. La idea es generar un mensaje de texto aleatorio que contiene una serie de variables aleatorias: suerte, amor, salud, riqueza. Hasta ahora he construido una red bayesiana que determina la relación entre estas variables y otras derivadas, para que el mensaje sea coherente (aunque sea aleatorio). Por ejemplo, en el caso del trabajo, si eres afortunado en la riqueza, es posible que que también tengas suerte en el trabajo, pero entonces puede que tu familia te eche de menos y no seas tan afortunado en el amor.

Por ahora he construido una red bayesiana que determina la relación entre una serie de parámetros. La red estará generada "a mano" para que los mensajes que se generen sean divertidos y más o menos coherentes. La descripción de los valores que maneja la red es esta:

  • suerte: es el factor principal de la red, el resto de variables dependen directa o indirectamente de la suerte.
  • amor: todos sabemos lo que es el amor. Por ahora el único hijo del amor es la familia, que depende directamente de nuestra suerte en el amor.
  • salud: fácil de saber qué significa. La salud determina nuestro trabajo, si no tenemos buena salud no podemos trabajar y viceversa.
  • riqueza: determina cúanto dinero y posesiones tenemos. Una parte de la riqueza depende del trabajo y otra del dinero (cómo algo genérico). He supuesto que si te va bien en la riqueza, lo más probable es que te vaya bien en el trabajo o en el dinero (juegos de azar, bolsa, etc) pero es improbable que te vayan bien o mal las dos cosas a la vez (aunque no es imposible).
  • trabajo: depende de la riqueza y de la salud.
  • familia: la más compleja de las variables de este ejemplo, depende del amor y del trabajo, que a su vez depende de la riqueza y de la salud. La idea es que si tenemos mucho trabajo, nuestra familia se resentirá, aunque le vaya bien a nuestra riqueza.

Se trata de un ejemplo muy sencillo, en el que hay que tener en cuenta que no hablo de causalidad, sino de correlación estadística, pero al fín y al cabo es lo que cuenta para "predecir el futuro" y para darle coherencia al mensaje. Una vez generado el mensaje de "suerte", el siguiente paso es vestirlo con palabras que expresen correlación o causalidad indistitamente. Éste es básicamente el truco que usan los adivinos: te va bien en el trabajo, entonces tu familia no estará muy contenta, veo tu futuro muy negro, no vas a tener suerte, pero en el amor te va a ir bien, porque tu familia te va a apoyar. Lo complicado será generar estas palabras que expresen la relación causal, espero que wordnet me sirva de ayuda.

El código ruby es muy sencillo y prácticamente se trata del ejemplo de uso de sbn adaptado a este caso:

require 'rubygems'
require 'sbn'

#puts "Starting: #{Time.now}"

net = Sbn::Net.new("Predicting your future")

#main variable
luck = Sbn::Variable.new(net, :luck, [0.5, 0.5])

#base variables
love = Sbn::Variable.new(net, :love)
luck.add_child(love)
love.set_probability(0.55, {:love => :true, :luck => :true})
love.set_probability(0.45, {:love => :false, :luck => :true})
love.set_probability(0.45, {:love => :true, :luck => :false})
love.set_probability(0.55, {:love => :false, :luck => :false})

health = Sbn::Variable.new(net, :health)
luck.add_child(health)
health.set_probability(0.55, {:health => :true, :luck => :true})
health.set_probability(0.45, {:health => :false, :luck => :true})
health.set_probability(0.45, {:health => :true, :luck => :false})
health.set_probability(0.55, {:health => :false, :luck => :false})

wealth = Sbn::Variable.new(net, :wealth)
luck.add_child(wealth)
wealth.set_probability(0.55, {:wealth => :true, :luck => :true})
wealth.set_probability(0.45, {:wealth => :false, :luck => :true})
wealth.set_probability(0.45, {:wealth => :true, :luck => :false})
wealth.set_probability(0.55, {:wealth => :false, :luck => :false})

#derived variables

work = Sbn::Variable.new(net, :work)
wealth.add_child(work)
health.add_child(work)

work.set_probability(0.8, {:work => :true, :wealth => :true, :health => :true})
work.set_probability(0.2, {:work => :false, :wealth => :true, :health => :true})
work.set_probability(0.2, {:work => :true, :wealth => :true, :health => :false})
work.set_probability(0.8, {:work => :false, :wealth => :true, :health => :false})
work.set_probability(0.5, {:work => :true, :wealth => :false, :health => :true})
work.set_probability(0.5, {:work => :false, :wealth => :false, :health => :true})
work.set_probability(0.1, {:work => :true, :wealth => :false, :health => :false})
work.set_probability(0.9, {:work => :false, :wealth => :false, :health => :false})

money = Sbn::Variable.new(net, :money)
wealth.add_child(money)

money.set_probability(0.6, {:money => :true, :wealth => :true})
money.set_probability(0.4, {:money => :false, :wealth => :true})
money.set_probability(0.2, {:money => :true, :wealth => :false})
money.set_probability(0.8, {:money => :false, :wealth => :false})

family = Sbn::Variable.new(net, :family)
love.add_child(family)
work.add_child(family)

family.set_probability(0.8, {:family => :true, :love => :true, :work => :true})
family.set_probability(0.2, {:family => :false, :love => :true, :work => :true})
family.set_probability(0.6, {:family => :true, :love => :true, :work => :false})
family.set_probability(0.4, {:family => :false, :love => :true, :work => :false})
family.set_probability(0.3, {:family => :true, :love => :false, :work => :true})
family.set_probability(0.7, {:family => :false, :love => :false, :work => :true})
family.set_probability(0.1, {:family => :true, :love => :false, :work => :false})
family.set_probability(0.9, {:family => :false, :love => :false, :work => :false})

#generate evidence
luck_value = rand(2) == 1  ? "true".to_sym : "false".to_sym

evidence = {:luck => luck_value}

net.set_evidence(evidence)

#lookup derived variables, and calculate a value

love_value = rand(0) < net.query_variable(:love)[:true] ? true : false
health_value = rand(0) < net.query_variable(:health)[:true] ? true : false
wealth_value = rand(0) < net.query_variable(:wealth)[:true] ? true : false
work_value = rand(0) < net.query_variable(:work)[:true] ? true : false
money_value = rand(0) < net.query_variable(:money)[:true] ? true : false
family_value = rand(0) < net.query_variable(:family)[:true] ? true : false

puts "luck: #{luck_value} [love: #{love_value} " + \
"(family: #{family_value}), health: #{health_value}, " + \
"wealth: #{wealth_value} (work: #{work_value}, money: #{money_value})]"

#puts "end: #{Time.now}"


Para la parte linguistica del proyecto de generación automática de lenguaje, he encontrado dos paquetes interesantes en ruby: ruby linguistics y ruby wordnet.

Ruby linguistics comprende una serie de utilidades para manejar palabras de forma "linguistica": permite cosas como estas:

  • pluralizar una palabra: "box".en.plural # => "boxes"
  • añadir artículo indefinido: "book".en.a # => "a book"
  • generar el gerundio: "runs".en.present_participle # => "running"
  • calcular ordinales de números: 5.en.ordinal # => "5th"
  • escribir un número en letra: 5.en.numwords # => "five"
  • cuantificar objetos: "cow".en.quantify(5) # => "several cows" || "cow".en.quantify( 1005 ) # => "thousands of cows"
  • infinitivos: "leaving".en.infinitive # => "leave"
  • generar conjunciones:

animals = %w{dog cow ox chicken goose goat cow dog rooster llama pig goat dog cat cat dog cow goat goose goose ox alpaca}
puts "The farm has: " + animals.en.conjunction
Produce:
The farm has: four dogs, three cows, three geese, three goats,two oxen, two cats, a chicken, a rooster, a llama, a pig, and an alpaca

Los ejemplos están tomados de la página del proyecto dónde hay más ejemplos.

Ruby wordnet es un interfaz ruby a Wordnet, una base de datos de palabras organizada de forma semántica. En esta base de datos las palabras se agrupan en synsets (conjuntos de palabras sinónimas) que se relacionan entre ellas con las siguientes relaciones:

  • Hiperonimia e hiponimia: una palabra es hipónima de otra si posée los rasgos semánticos de otra más general.
  • Holonimia y meronimia: si una palabra es parte de otra (ventana es una parte de una casa).
  • Términos coordinados: dos palabras son términos coordinados si comparten un hiperónimo.

He hecho una prueba simple con linguistics y wordnet que pongo aquí cómo ejemplo. En este caso se busca el synset de unas palabras ("happiness", "young" y "man") y se eligen aleatoriamente las palabras de cada synset para formar el mensaje resultado:

require 'rubygems'
require 'linguistics'
require 'wordnet'

Linguistics::use( :en )

puts "Your future"

happiness_synonyms = "happiness".en.synset(1,:noun).synonyms
happiness_word = happiness_synonyms[rand(happiness_synonyms.length)]

young_synonyms = "young".en.synset(1,:adjective).synonyms
young_word = young_synonyms[rand(young_synonyms.length)]

man_synonyms = "man".en.synset(1,:noun).synonyms
man_word = man_synonyms[rand(man_synonyms.length)]

message = "I wish you #{happiness_word}, #{young_word} #{man_word}."

puts message

Una prueba muy sencilla, pero que para poder ejecutarlo hay que resolver un par de problemillas:

  • Para instalar wordnet para ruby (gem install wordnet) hay que tener instalado previamente wordnet (en ubuntu, es simplemente sudo apt-get install wordnet) y el binding de berkeley db para ruby (en ubuntu, sudo apt-get install libdb-ruby1.8). Después de instalar, hay que migrar la base de datos wordnet a berkeley db, con un script incluido en ruby wordnet (./convertdb.rb)
  • Ruby linguistics, al menos en la versión que tengo instalada, la 1.0.5 (gem install Linguistics) está anticuado. Hay que sustituir la función lookupSynsets por lookup_synsets en el fichero wordnet.rb.

La ejecución de este programilla genera frases cómo esta:

I wish you felicity, offspring man.

I wish you happiness, young man.

I wish you happiness, offspring adult male.

Todavía muy simple para lo que quiero conseguir, pero parece que estas librerías pueden ser útiles.