Partager via


Cet article a fait l'objet d'une traduction automatique.

Le travail programmeur

Parlez-moi, 2è partie : ELIZA

Ted Neward

 

Ted NewardLors de notre dernière rencontre, nous avons construit un système simple pour répondre aux entrées de la voix au téléphone à l'aide de Tropo, un service de voix et de SMS a accueilli les nuages. Nous avons comme étant capable de répondre aux entrées de la voix et d'offrir des réponses, j'espère que vous renflouer hors de l'eau chaude avec votre autre significatif pour dépenser trop de temps sur la Xbox au cours de la période des fêtes. Malheureusement, pour certains d'entre vous, qui ne pourrait pas avoir emporté et choses sont toujours tendues entre vous. C'est quand, comme un être humain compatissant, j'aimerais être en mesure d'offrir mes services comme un thérapeute amateur. Malheureusement, cependant, je ne l'échelle très bien et ne peut pas parler de tous et chacun de vous. Ainsi, au lieu de cela, examinons une alternative. Je parle, bien sûr, Eliza.

ELIZA : Une histoire

Pour ceux peu familiers avec ELIZA, « elle » est un agent conversationnel — et l'autre des premières et la plus reconnaissables des étapes vers l'intelligence artificielle (IA). Écrit de retour dans les années 1960 par Joseph Weizenbaum, en Lisp, ELIZA est un processeur d'entrée-réponse relativement simple (selon les critères actuels) qui analyse l'entrée d'utilisateur pour les « clés » et puis génère des réponses humaines comme basés sur ces touches. Ainsi, par exemple, si vous l'avez dit, « je suis triste, » d'ELIZA, elle pourrait répondre, « pourquoi êtes-vous triste? » ou « Talking to me fait-il vous triste? » ou encore « cesse d'être triste! » En fait, la réponse pourrait être tellement authentique parfois que, pendant un certain temps, on pensait que cela pourrait être un moyen d'obtenir un programme pour passer le test de Turing.

Quatre décennies plus tard, cependant, c'est toujours pas des conversations avec nos ordinateurs la façon dont Arthur c. Clarke imaginé dans "2001 : Une odyssée de l'espace », mais qui ne veut pas dire nous ne devons pas « homme-like » communication dans nos programmes. Nous commençons à voir le traitement du langage naturel (NLP) subtilement glisse de plus en plus dans des systèmes informatisés, et lorsqu'il est combiné avec des moteurs de reconnaissance de la parole-texte, entièrement nouvelles avenues d'interaction homme-ordinateur ouvrent. Par exemple, même un simple ELIZA clés-module système semblable peut être utile en essayant de créer des systèmes humains-aide sur les sites Web ou clients de routage au département droit dans une grande entreprise sans exiger la très frustrante, arbre d'appel « Appuyez sur 1 pour le Service à la clientèle, appuyez sur le 2 pour les ressources humaines, appuyez sur 3 pour … ».

Si vous n'avez jamais tâté à même la PNL plus simple, cependant, essayez quelque chose comme cela peut être un peu intimidant. Heureusement, nous pouvons obtenir des bons résultats de même quelques tentatives très basiques. Il y a un grand tutoriel à bit.ly/uzBSM9, qui sert l'inspiration pour ce que nous sommes sur le point de faire ensuite, qui consiste à écrire une implémentation de ELIZA en F #. Le choix de F # ici est double : tout d'abord, en hommage à l'utilisation de Lisp dans l'ELIZA originale, car les deux sont des langages fonctionnels et, deuxièmement, parce que je n'ai pas fait un échantillon de colonne en F # dans un certain temps. Naturellement, que nous appellerons sa F #-Eliza ou Feliza pour courtes (parce que cela sonne plus exotique) et sa mise en œuvre comme une bibliothèque F # donc elle peut être incorporée dans une variété de programmes différents.

Feliza : Version 0

L'interface de Feliza doit être court, doux, simple — et cacher tout un tas de complexité. Le catalogue de patrons Gang-de-Four classique, c'est le patron de la Façade (« »), et F # rend facile de créer une Façade grâce à l'utilisation de ses fonctionnalités de « module » :

module Feliza
open System
let respond input =
  "Hi, I'm Feliza"
Using Feliza in a console-mode program, for example, would then be as easy as this:
open Feliza
open System
let main =
  Console.WriteLine("Hello!")
  while true do
    Console.Write("> ")
      let input = Console.ReadLine()
      let responseText = respond input
      Console.WriteLine(responseText)
      if (input.ToLower().Equals("bye")) then
        Environment.Exit(0)
  ()

Cela forme ensuite, notre banc d'essai. Il est également plus facile à incorporer Feliza dans d'autres environnements, si nous pouvons nous en tenir à cette API über-simple.

Comme nous l'avons get va construire sur certaines implémentations de travail, en passant, n'oubliez pas que Feliza n'est pas destiné à être un moteur généraliste de la PNL — qui prend beaucoup plus de travail que j'ai la place pour cette colonne. En fait, Microsoft Research a toute une division dédiée à la PNL (voir research.microsoft.com/groups/nlp pour plus d'informations sur ce qu'ils vous étudier). Et prendre bonne note que je ne suis pas l'un d'entre eux.

Feliza : Version 1

La façon la plus simple pour obtenir une version de « travailler » 1 de Feliza est de créer une simple liste de réponses possibles et choisir au hasard parmi eux :

let respond input =
  let rand = new Random()
  let responseBase =
    [| "I heard you!";
      "Hmm.
I'm not sure I know what you mean.";
      "Continue, I'm listening...";
      "Very interesting.";
      "Tell me more..."
    |]
  responseBase.[rand.Next(responseBase.Length - 1)]

Travailler avec un tableau ici n'est pas idiomatique « F #-ish, « mais il le rend plus facile de sélectionner au hasard dans les réponses possibles. Il n'est pas une conversation particulièrement excitante, bien que cela puisse sembler familier à toute personne qui a déjà essayé de parler à un programmeur qui essaie d'écrire du code à l'époque. Encore, pour un peu de temps, il pourrait en fait sentir comme une vraie conversation. Nous pouvons faire mieux, mais.

Feliza : Version 2

Une implémentation évidente suivante consiste à créer une « base de connaissances « des réponses en conserve aux intrants particuliers de l'utilisateur. Cela est facilement modelé en F # à l'aide de n-uplets, avec le premier élément dans le tuple étant l'expression d'entrée qui nous tenons à répondre et le second élément étant la réponse. Ou, pour être plus humain à ce sujet (et éviter les répétitions évidentes dans les réponses), nous pouvons faire le deuxième élément, une liste de réponses possibles et choisir au hasard l'un dans cette liste, comme indiqué dans Figure 1.

Figure 1 la Base de connaissances Feliza

let knowledgeBase =
  [
    ( "Bye",
      [ "So long!
Thanks for chatting!";
        "Please come back soon, I enjoyed talking with you";
        "Eh, I didn't like you anyway" ] );
    ( "What is your name", 
      [ "My name is Feliza";
        "You can call me Feliza";
        "Who's asking?" ] );
    ( "Hi",
      [ "Hi there";
        "Hello!";
        "Hi yourself" ] );
    ( "How are you",
      [ "I'm fine, how are you?";
        "Just peachy";
        "I've been better" ] );
    ( "Who are you",
      [ "I'm an artificial intelligence";
        "I'm a collection of silicon chips";
        "That is a very good question" ] );
    ( "Are you intelligent",
      [ "But of course!";
        "What a stupid question!";
        "That depends on who's asking." ] );
    ( "Are you real",
      [ "Does that question really matter all that much?";
        "Do I seem real to you?";
        "Are you?" ] );
    ( "Open the pod bay doors",
      [ "Um...
No.";
        "My name isn't HAL, you dork.";
        "I don't know...
That didn't work so well last time." ] );
  ]
let unknownResponses =
  [ "I'm sorry, could you repeat that again?";
    "Wait, what?";
    "Huh?" ]
let randomResponse list =
  let listLength list = (List.toArray list).Length
  List.
nth list (rand.Next(listLength list))
let cleanInput (incoming : string) =
  incoming.
Replace(".", "").
Replace(",", "").
Replace("?", "").
Replace("!", "").
ToLower()
let lookup =
  (List.tryFind
    (fun (it : string * string list) ->
      (fst it).Equals(cleanInput input))
    knowledgeBase)
randomResponse (if Option.isSome lookup then
                    (snd (Option.get lookup))
                else
                    unknownResponses)

Cette version est un simple nettoyage de l'entrée et cherche un match sur la première partie de la liste des n-uplets (la « base de connaissances »), puis sélectionne une réponse au hasard dans la liste. Si aucun « phrase clé » ne se trouve dans la connaissance base, qu'une réponse « inconnue » est générée, une fois de plus choisis au hasard dans une liste. (Certes, le nettoyage est effectué d'une manière particulièrement inefficace, mais lorsque nous parlons de la communication humaine, les retards ne sont pas un problème. En fait, certaines implémentations agent conversationnel volontairement ralentissent les réponses et leur imprimer un caractère par caractère, pour imiter quelqu'un tapant sur un clavier.)

De toute évidence, si c'était pour être utilisé dans n'importe quel scénario réaliste, parce que l'expression d'entrée doit être une correspondance exacte pour déclencher la réponse, il faut une beaucoup, beaucoup plus grande base de connaissances, intégrant chaque permutation possible de la parole humaine. Que — pas une solution évolutive. De plus, nous perdons vraiment beaucoup quand Feliza ne réagit-il à l'entrée d'utilisateur de façon plus significative. Un des atouts originaux de ELIZA était que si vous l'avez dit, « J'aime les pommes de terre, » elle pouvait répondre avec, « Les pommes de terre importants pour vous? »— faire la conversation beaucoup plus « personnalisée ».

Feliza : Version 3

Cette version obtient un peu plus compliquée, mais elle offre aussi plus de souplesse et de puissance. Essentiellement, nous passons l'algorithme exact-match dans un flexible en convertissant la liste des n-uplets en une liste de fonctions qui sont chacune évalué et donné une chance de créer une réponse. Cela ouvre une énorme liste d'options pour comment Feliza peuvent interagir avec l'entrée, et comment elle peut choisir des mots de l'entrée pour générer des réponses particulières.

Elle commence par une simple liste de « règles de traitement », à dont le fond sera une réponse fourre-tout qui indique elle ne savait pas comment réagir, l'équivalent moral de la liste « unknownResponses » de la version précédente, comme le montre Figure 2.

La figure 2 Catchall règle pour répondre

let processingRules =
  [
    // ...
// Catchall rule for when nothing else matches before
    // this point; consider this the wildcard case.
This
    // must always be the last case considered!
(
      (fun (it : string) ->
        Some(randomResponse
          [
          "That didn't make sense.";
          "You cut out for a second there.
What did you say?";
          "Wait--the Seahawks are about to...
Never mind.
They lost.";
          "I'm sorry, could you repeat that again?";
          "Wait, what?";
          "Huh?"
          ]))
    )
  ]
List.head (List.choose (fun (it) -> it (cleanInput input)) processingRules)

Le noyau de cette version est dans la dernière ligne, la fonction List.choose prend chaque processingRule et l'exécute contre l'entrée, et si le processingRule renvoie à une certaine valeur, cette valeur obtient ajoutée à une liste retournée à l'appelant. Si maintenant nous pouvons ajouter de nouvelles règles, ont chacun un retour une valeur et puis prendre la première a (comme le montre Figure 2, à l'aide de List.head) ou même au hasard, choisir une. Dans une future version, nous pourrions donner une certaine valeur qui est une réponse de texte et un « poids » de l'opportunité, pour aider à choisir laquelle est la bonne réponse.

Rédaction de nouvelles règles devient plus facile maintenant. Nous pouvons avoir une règle qui touches juste au large de l'entrée, à l'aide de F # pattern matching pour rendre plus facile correspondre à :

(fun (it : string) ->
  match it with
  | "Hi" | "Howdy" | "Greetings" ->
    Some(randomResponse
      [
      "Hello there yourself!";
      "Greetings and salutations!";
      "Who goes there?"
      ])
  | _ -> None
);

Ou nous pouvons utiliser le raccourci « fonctionner » construire à faire de même, comme le montre Figure 3.

Figure 3 la construction de la fonction

(function
  | "How are you?" ->
    Some(randomResponse
      [
      "I'm fine, how are you?";
      "Just peachy";
      "I've been better"
      ])
  | "open the pod bay doors" ->
    Some(randomResponse
      [
      "Um ...
No.";
      "My name isn't HAL, you dork.";
      "I don't know ...
That didn't work so well last time."
            ])
  | _ -> None
);

La plupart du temps, cependant, Feliza ne recevoir ces phrases en conserve, donc nous voudrions plutôt elle prendre son repère de mots clés dans l'entrée de l'utilisateur, comme Figure 4 spécifie.

La figure 4, égalant les réponses pour les mots clés

(fun (it : string) ->
  if it.Contains("hate") || it.Contains("despise") then
    Some(randomResponse
      [ "Why do you feel so strongly about this?";
        "Filled with hate you are, young one.";
        "Has this always bothered you so much?" ])
  else
    None
);
(fun (it : string) ->
  if it.StartsWith("what is your") then
    let subject =
      it.Substring(it.IndexOf("what is your") +
                         "what is your".Length).Trim()
    match subject with
      | "name" ->
        Some(randomResponse
          [ "Feliza."; "Feliza.
What's yours?";
            "Names are labels.
Why are they so important to you?" ])
      | "age" ->
        Some(randomResponse
          [ "Way too young for you, old man."; "Pervert!";
            "I was born on December 6th, 2011" ])
      | "quest" ->
        Some("To find the Holy Grail!")
      | "favorite color" ->
        Some("It's sort of green but more dimensions")
      | _ ->
        Some("Enough about me.
What's yours?")
  else
    None
);

Anciens combattants F # constate que F # « actives patterns » serait un ajustement parfait pour certains ceux qui ne sont pas aussi familier avec la construction de modèles actif F # (ou avec la syntaxe pattern-matching F # en général) peuvent en savoir plus sur le sujet à de l'excellent série two-part de Jessica Kerr bit.ly/ys4jto et bit.ly/ABQkSN.

Suivante : Connexion à Feliza

Feliza est grande, mais sans un canal d'entrée qui va au-delà du clavier, elle n'est pas aller très loin. Feliza veut aider les gens beaucoup plus que seulement ceux qui sont assis devant le clavier, elle veut être accessible à toute personne ayant une connexion Internet ou le téléphone cellulaire, et dans le prochain article, nous allons « raccorder lui » faire exactement ce que.

Bon codage !

Ted Neward est un consultant en architecture avec LLC Neudesic. Il a écrit plus de 100 articles, est un MVP c# et INETA speaker a l'auteur et coauteur d'une douzaine de livres, y compris la récemment publié « professionnel F # 2.0 » (Wrox, 2010). Il consulte et mentors régulièrement. Joindre à ted@tedneward.com si vous êtes intéressés à lui faire venir travailler avec votre équipe, ou lire son blog à blogs.tedneward.com.

Merci à l'expert technique suivant d'avoir relu cet article : Matthew Podwysocki