語音助手是這樣子的(二)


前一節我們介紹了語音助手的基本框架與核心技術,本節我們將優先介紹使用OpenDial來設計對話的管理與流程。OpenDial的功能很強大,可以實現NLU、DM以及NLG的所有功能,在官網上也有一系列的教程。在本系列博客中,我們僅僅使用OpenDial來進行對話管理(包括NLG),而把NLU剝離出來單獨實現。

再來看場景

前一節中我們給出了一個設置鬧鍾的場景,現在讓我們重新使用OpenDial的視角來看一下這個場景。

用戶:設置鬧鍾

Siri:請問您需要設置幾點的鬧鍾?

用戶:明天早上六點的

Siri:好的,已經把鬧鍾設置到了明天早上六點

在這個場景中,如果我們稍作抽象則可以這樣來看,“設置鬧鍾”可以抽象為一個動作叫做SetAlarm,而這個動作是來自用戶的,我們定義其為a_u(action of user),而“設置鬧鍾”是這個動作的一種語言表達u_u(utterance of user),語言的多樣性允許用戶使用其他的表述如“請為我設置一個鬧鍾”等等。同樣,Siri的答復和操作也可以抽象,系統的答復“請問您需要設置幾點的鬧鍾?”是u_m(utterance of machine)的一種語言表達,而詢問設置鬧鍾的具體時間可以抽象為一個叫做RequestTime的動作,即a_m(action of machine)是RequestTime。

如上的這些抽象其實是按照OpenDial的規范進行描述的,那么接下來我們就來研究如何把這樣的一個對話場景設置到OpenDial中。

OpenDial

OpenDial是一個使用概率規則與貝葉斯網絡實現的開源對對話系統引擎。讀者可以參考OpenDial用戶手冊中的介紹完整地學習OpenDial的使用,這里我們只針對本文中的場景介紹其簡單的使用與配置。

首先,啟動OpenDial的可視化工具(.\scripts\opendial.bat),新建一個領域“Domian -> New”,文件命名為Alarm.xml,點擊保存后在“Domain Editor”的標簽頁里可以設計對話狀態了。

OpenDial Example 1

OpenDial的配置文件按如下的規范進行構造,一個場景被認為是一個domain,一個domain中包含多個model,每個model由一個trigger觸發,通常我們設計三個model分別對應NLU,DM和NLG。NLU的model由u_u觸發,DM的model由a_u觸發,NLG的model由a_m觸發。所以,按照一次對話是由NLU -> DM -> NLG這樣的流程形成的思想,我們很自然地可以想象到u_u通過一定的規則(rule)條件(case)觸發NLU model,同時在NLU model中設置a_u變量,這樣a_u就可以根據一定的規則條件觸發DM model,此時只需再在DM model中設置a_m變量,就可以順利地到達NLG model,從而把需要響應用戶的回復設置到u_m中。所以,樣例場景就可以配置為如下的形式:

<?xml version="1.0" encoding="GB2312"?>
 <domain>
  <model trigger="u_u">
    <rule>
      <case>
        <condition>
          <if var="u_u" relation="=" value="設置鬧鍾"/>
        </condition>
        <effect prob="1">
          <set var="a_u" value="SetAlarm"/>
        </effect>
      </case>
      <case>
        <condition>
          <if var="u_u" relation="=" value="明天早上六點的"/>
        </condition>
        <effect prob="1">
          <set var="a_u" value="InformTime"/>
        </effect>
      </case>
    </rule>
  </model>
  <model trigger="a_u">
    <rule>
      <case>
        <condition>
          <if var="a_u" relation="=" value="SetAlarm"/>
        </condition>
        <effect prob="1">
          <set var="a_m" value="RequestTime"/>
        </effect>
      </case>
      <case>
        <condition>
          <if var="a_u" relation="=" value="InformTime"/>
        </condition>
        <effect prob="1">
          <set var="a_m" value="ToDoSetAlarm"/>
        </effect>
      </case>
    </rule>
  </model>
  <model trigger="a_m">
    <rule>
      <case>
        <condition>
          <if var="a_m" relation="=" value="RequestTime"/>
        </condition>
        <effect prob="1">
          <set var="u_m" value="請問您需要設置幾點的鬧鍾?"/>
        </effect>
      </case>
      <case>
        <condition>
          <if var="a_m" relation="=" value="ToDoSetAlarm"/>
        </condition>
        <effect prob="1">
          <set var="u_m" value="好的,已經把鬧鍾設置到了明天早上六點"/>
        </effect>
      </case>
    </rule>
  </model>
</domain>

保存如上的配置,在OpenDial工具中切換到“Interaction”標簽頁,就可以按照場景中的方式與OpenDial進行對話了。
OpenDial Example 1

OpenDial還有很多功能,比如支持正則表達式、可以設置變量的概率分布等,在這里我們不多介紹,感興趣的讀者可以參考官方指南。

歸一化表達

我們說u_u由於語言表達的多樣性存在很豐富的實例,我們不可能把所有的表達都枚舉到OpenDial的配置中,而且諸如“明天早上六點”這樣的時間信息,是需要自動傳遞到下一輪對話的,而不應該是固定寫死在配置文件中的。所以,我們將使用一種稱為“槽位填充(Slot Filling)”的思想來解決上述的問題。

槽位是來自於自然語言處理中經常使用的規則模板的一個概念,它是對相同語義概念的表達的一種歸一化表示。相對的,如果能填充到槽位里的表達就叫做相應的實例化表述。我們直觀地看一個例子:

【D:set】【D:clock】   =>   設置鬧鍾

【F:time】的   =>    明天早上六點的

我們用【】括起來的部分就是槽位,【D:set】表示的是可以表達“set”這個含義的詞,我們需要收集“設置”、“設定”等同義詞並且把這些詞都定義為“set”,同理可以理解【D:clock】;【F:time】是可以識別時間的一個函數。那么,顯然除了例子中的實例表述可以歸一化為上面定義的槽位模板,“設定鬧鈴”也是可以被歸一化為相同的槽位模板,“晚上七點的”也一樣。所以,多種表述在這里可以被歸一化為用槽位模板表達的形式,這樣我們可以一定程度上減少在OpenDial中配置太多樣的u_u。反過來,當用戶輸入的實例被泛化為槽位模板的表達時,就意味着每個槽位被相應的字符串填充了,此時我們取出槽位對應的字符串就實現了對關鍵信息的提取,那么a_m所要采取的動作就有了依據,而且u_m生成的回復也有了參考。

本節,我們介紹了借助OpenDial實現對話管理的功能,重點講解了如何配置該模塊的xml文件,最后引入了歸一化表達的思想,就是想實現NLU與DM的分離,使OpenDial只專注於DM。下一節,我們將詳細介紹實現NLU的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM