工作的程式師

與我對談,第 2 部:ELIZA

Ted Neward

 


當我們上次見面時,我們建立了一個簡單的系統,通過使用對流層,雲主持的語音短信服務電話語音輸入回應。我們只要能夠語音輸入回應並提供回應,希望幫你擺脫與你的另一半在 Xbox 上花太多時間,在假日的熱水。對於一些你,不幸的是,這不可能進行一天,和東西仍然是你們之間的緊張。這是時,作為一個富有同情心的人,我想能夠作為業餘的治療師提供我的服務。遺憾的是,不過,我不能擴展很好,不能跟你們每一個人。所以,相反,讓我們看一下另一種方法。當然,我發言的艾麗莎。

伊麗莎:歷史

對於那些不熟悉伊麗莎,"她"是 chatterbot — — 和人工智慧 (AI) 對第一和最受認可的步驟之一。伊麗莎由約瑟夫 · 魏岑鮑姆,口齒不清,在寫回六十年代是一個相對簡單 (以今天的標準) 的輸入回應處理器,分析"鍵"的使用者輸入,然後生成基於這些金鑰的人類的回應。所以,例如,如果你說的"我很難過,"艾麗莎,她可能回應,"你為什麼傷心?"或"不會跟我說話讓你傷心?"或者甚至"停止正在傷心!"事實上,反應可能是如此真實有時,一段時間,它被認為這可能是一種方法可以通過圖靈測試的程式。

四個幾十年後,但是,我們仍未有對話我們電腦亞瑟 C.的方式克拉克所想像的那樣"2001年:太空漫遊,",但這並不意味著我們應該忽略我們的程式中的"人樣"通信。我們開始看到自然語言處理 (NLP) 巧妙地更滑入電腦化系統,並結合語音文本識別器引擎,當的人機交互作用的全新管道開放。例如,甚至簡單伊麗莎樣金鑰識別器系統可以説明或路由客戶中嘗試創建的 Web 網站上的人權援助系統內一家大公司的右部而不需要非常令人沮喪,"為客戶服務請按 1,按 2 人力資源,按 3 為 … …"調用關係樹。

如果您已經永遠不會涉足連最簡單的自然語言處理,然而,嘗試這樣的事可以令人生畏。幸運的是,我們可以從甚至一些非常基本的嘗試中得到一些好的結果。有一個很大的教程,在 bit.ly/uzBSM9,後者相當於要下一步,做的事,我們的靈感是在 F # 編寫的伊麗莎實現。這裡的 F # 的選擇是雙重的:第一,作為在原始的伊麗莎,Lisp 的使用致敬因為這兩個功能的語言 ; 第二,因為我沒做列樣本 F # 中的在一段時間。自然,我們就叫她 F #-伊麗莎或短 (因為這聽起來很有異國),和作為 F # 庫實現她,所以她可以在各種不同的程式中嵌入的 Feliza。

Feliza:0 版本

Feliza 的介面應該簡短、 甜蜜、 簡單明瞭 — — 和隱藏一大堆的複雜性。從經典黑幫的四模式目錄,這是門面模式 (""),F # 將便於創建門面通過其"模組"功能的使用:

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)
  ()

然後,這構成我們測試的床。 它還使它易於 Feliza 嵌入在其他環境中,如果我們能堅持這個超級簡單的 API。

作為我們送去進行建設某些工作實現,順便說一句,請記住 Feliza 並不打算成為通用的 NLP 引擎 — — 這需要很多的工作,比我有此列中的空間。 事實上,微軟研究有專用於 NLP 整師 (請參見 research.microsoft.com/groups/nlp 詳細資訊對他們很正的調查)。 並請仔細注意,我不是其中之一。

Feliza:1 版本

到"工作"的第 1 版 Feliza 的最簡單方法是創建一個簡單的清單可能的回應和它們之間的隨機選擇:

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)]

一個陣列,在這裡使用慣用法上不是"F #-ish,"但它可以使它更容易選擇隨機從可能採取的對策。 它不是特別令人興奮的談話,雖然可能熟悉曾試圖正試圖在時間編寫代碼的程式師交談的人。 儘管如此,一會兒,其實感覺是真正的交談。 雖然我們可以做得更好。

Feliza:2 版本

下一個明顯的實現是創建對特定輸入從使用者的罐頭回應的"知識庫"。 這是輕鬆建模 F # 正在我們希望作出回應的輸入的短語的元組中的第一個元素和正在回應的第二個元素中使用元組中。 或者,這件事更有人性 (並避免在回應中的明顯重複),我們可以使第二個元素清單可能採取的對策,並隨機選擇一個從該清單中,如中所示圖 1

圖 1 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)

此版本不會輸入的一些簡單的清理和尋求一個匹配上的元組 ("知識基地"),清單中的第一部分,然後從清單中隨機選擇的回應。 如果沒有"關鍵短語"發現知識基礎,生成"未知"的回應,再次隨機從清單中選擇。 (當然,特別是效率低下的方式,完成清理但當我們談論人類溝通,延誤並不是一個問題。 事實上,某些 chatterbot 實現故意放慢反應和列印這些字元的字元,模仿有人在鍵盤上鍵入。)

如果因為輸入的短語必須是精確匹配來觸發回應,這是使用在真實世界的情況下,任何一種,我們顯然需要更大知識文庫的多,納入每種可能的排列的人類語言。 呃 — — 不是一個可擴展的解決方案。 更重要的是,我們真的失去了很多時候 Feliza 不回應使用者輸入更有意義的方式。 伊麗莎的原始優勢之一是如果你說的"我喜歡土豆,"她可以回應的"是土豆對您很重要嗎?"— — 使談話更多"個性化"。

Feliza:3 版本

此版本獲取要稍微複雜,但它還提供了更多的靈活性和電源。 實質上,我們把精確匹配演算法變成一個靈活的元組的清單轉換為清單中的函數,每個評估和有機會創建回應。 這將打開一個巨大 Feliza 輸入,可以進行交互和她可以如何挑從輸入的單詞,生成回應特定的選項清單。

它簡單清單開頭的"處理規則",在其中底部將會指示她不知道如何應對,道德相當於以前的版本中,從"unknownResponses"清單中所示的通用回應圖 2

圖 2 全部捕獲回應的規則

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)

此版本的核心是在最後一行 — — 該 List.choose 函數將每個 processingRule,並針對輸入,執行它和 processingRule 返回一些值,如果該值已被添加到清單返回給調用方。 現在我們可以添加新的規則,具有每一個返回值,然後要麼採取的第一個 (如圖所示,在圖 2,通過使用 List.head) 或甚至隨機選擇一個。 在未來的版本中,我們可能會產生一些值,則文本回應和適當,説明選擇是正確的答案"體重"。

現在書寫新規則很容易。 我們可以有一個規則關閉的輸入,這只是鍵使用 F # 模式匹配,便於匹配:

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

或者,我們可以使用"函數"做相同,構造,如中所示的速記圖 3

圖 3 函數構造

(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
);

大部分時間,不過,Feliza 不用做這些罐頭的片語,所以我們寧願她以她的靈感來自使用者的輸入,輸入的關鍵字為圖 4 指定。

圖 4 打結的關鍵字的回應

(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
);

F # 退伍軍人會注意到 F #"活動模式"將完美適合一些此 ; 那些不熟悉用 F # 活動格局構造 (或 F # 模式匹配中的語法一般) 可以找出更多關於在克爾潔西嘉的優秀兩部分系列 bit.ly/ys4jtobit.ly/ABQkSN

下一步:連接到 Feliza

Feliza 是偉大的但沒有超出了鍵盤的輸入通道,她並不很遠去。 Feliza 想要説明更多的人比只是那些人坐在前面的鍵盤 — — 她想要擁有行動電話或互聯網連接,任何人都可以訪問並在下一部分中,我們就會"掛鉤她"做這些事情。

編碼愉快 !

Ted Neward 是 Neudesic LLC 的建築顧問。他寫了一百多篇,是 C# MVP 和 INETA 的演講者和創作併合著十幾本書,包括最近發表"專業 F # 2.0"(Wrox,2010年)。他諮詢、 定期指導。他到達 ted@tedneward.com,如果你有興趣于將他來和你的團隊工作或閱讀他的博客,在 blogs.tedneward.com

多虧了以下的技術專家審查這篇文章: Matthew Podwysocki