天天看點

ros使用百度UNIT快速搭建機器人對話系統 人機語音互動

上篇,我們實作了可以使用百度技術語音轉文字,文字轉語音。

這裡深入一層。給出ros下使用百度UNIT快速搭建機器人對話系統的示例

套路上,在開放平台釋出應用,都會有ak 和sk。用這兩個k來擷取與應用通迅用的token

我分兩個節點實作功能

一個處理 擷取token并釋出。

一個等待token并使用它 與unit應用通迅,

這個節點訂閱 使用者語音轉成的文字topic,把文字發送到 unit 然後把傳回 的文字 publish到文字轉語音節點訂閱的主題上。

進而實作了語音到文字,文字查詢輸入-->baidu_unit-->查詢輸出文字,文字到語音的互動.

rqtgraph

ros使用百度UNIT快速搭建機器人對話系統 人機語音互動

python

node_token_main.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Copyright (c) 2017 zaizhizhuang.  All rights reserved.
This program is free software; you can redistribute it and/or modify.
"""

import rospy
from std_msgs.msg import String

import urllib, urllib2, sys
import ssl
import json

class baidu_unit_token_main():
 def __init__(self):
  self.define()
  self.get_token()
  #rospy.Subscriber(self.topic , String , self.talker)
  rospy.spin()





 def get_token(self):
  # client_id 為官網擷取的AK, client_secret 為官網擷取的SK
  host = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id='+self.baidu_unit_ak+'&client_secret='+self.baidu_unit_sk
  request = urllib2.Request(host)
  request.add_header('Content-Type', 'application/json; charset=UTF-8')
  response = urllib2.urlopen(request)
  content = response.read()
  if (content):
   print(content)
   #s = json.dumps(data)
   s1 = json.loads(content)
   if (s1['access_token']):
    self.token_string=s1['access_token']
    print(self.token_string)
    rospy.set_param('baidu_unit_token_string', self.token_string)
    self.say.publish(self.token_string)
   else:
    print('get token error!!!')


 def define(self):
  self.say=rospy.Publisher('baidu_unit_token_string', String, queue_size=1)
  if not rospy.has_param('~baidu_unit_ak'):
   rospy.set_param('~baidu_unit_ak','xxxxxxxxxxxxxxxxx')
  if not rospy.has_param('~baidu_unit_sk'):
   rospy.set_param('~baidu_unit_sk','xxxxxxxxxxxxxxxx')


  if not rospy.has_param('~baidu_unit_token_topic'):
   rospy.set_param('~baidu_unit_token_topic', 'baidu_unit_token_topic')
   
  self.baidu_unit_ak=rospy.get_param('~baidu_unit_ak')
  self.baidu_unit_sk=rospy.get_param('~baidu_unit_sk')
  #self.topic=rospy.get_param('~baidu_unit_token_topic')
 
 def talker(self,data):
  if data.data=='reset':
   self.get_token()

  else:
   pass
   
if __name__=="__main__":
 rospy.init_node('baidu_unit_token')
 rospy.loginfo("initialization  baidu_unit token")
 baidu_unit_token_main()
 rospy.loginfo("process done and quit")
           

node_talk_main.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Copyright (c) 2017 zaizhizhuang.  All rights reserved.
This program is free software; you can redistribute it and/or modify.
"""

import rospy
from std_msgs.msg import String

import urllib, urllib2, sys
import ssl
import json


import signal
import sys

from pprint import pprint

class baidu_unit_talk_main():
 def __init__(self):
  self.define()

  rospy.Subscriber(self.token_topic , String , self.set_token)
  #wait for token...
  while not self.access_token:
   rospy.logwarn('get token Retrying ...')
   rospy.sleep(1.0)
   self.access_token=rospy.get_param('/baidu_unit_token_string', '')
  #listen topic
  rospy.Subscriber(self.listen_topic , String , self.get_say)
  #zzz test
  #self.load("今天北京天氣怎麼樣?")
  rospy.spin()






 def get_say(self,data):
  self.load(data.data)


 def load(self,data):

  url = 'https://aip.baidubce.com/rpc/2.0/solution/v1/unit_utterance?access_token=' + self.access_token
  post_data = "{\"scene_id\":"+str(self.scene_id)+",\"query\":\""+str(data)+"\",\"session_id\":\""+str(self.session_id)+"\"}"
  print(post_data)
  request = urllib2.Request(url, post_data)
  request.add_header('Content-Type', 'application/json; charset=UTF-8')
  #request.add_header('Content-Type', 'application/json')
  response = urllib2.urlopen(request)
  content = response.read()
  if (content):
   print(content)
   #s = json.dumps(data)
   s1 = json.loads(content)
   #test
   #pprint(s1)
   if (s1[u"result"][u"session_id"]):
    self.session_id=s1[u"result"][u"session_id"]
   if (s1[u"result"][u"action_list"][0][u"say"]):
    self.words=s1[u"result"][u"action_list"][0][u"say"]
    self.say.publish(self.words)
    print (self.words)

 def define(self):
  self.say=rospy.Publisher('speak_string', String, queue_size=1)
  if not rospy.has_param('~baidu_unit_scene_id'):
   rospy.set_param('~baidu_unit_scene_id', 'xxxxxx')
  if not rospy.has_param('~baidu_unit_token_topic'):
   rospy.set_param('~baidu_unit_token_topic', 'baidu_unit_token_string')
  if not rospy.has_param('~baidu_unit_listen_topic'):
   rospy.set_param('~baidu_unit_listen_topic', 'baidu_unit_listen_string')

  self.token_topic=rospy.get_param('~baidu_unit_token_topic')
  self.listen_topic=rospy.get_param('~baidu_unit_listen_topic')
  self.scene_id=rospy.get_param('~baidu_unit_scene_id')
  self.access_token=''
  self.session_id=''

 def set_token(self,data):
  print(data)
  if data.data :
   self.access_token=data.data
  else:
   pass




def quit(signum, frame):
 print 'You choose to stop me.'
 sys.exit(0)
if __name__=="__main__":
 try:
  signal.signal(signal.SIGINT, quit)
  signal.signal(signal.SIGTERM, quit)
  rospy.init_node('baidu_unit_talk')
  rospy.loginfo("initialization  baidu_unit talking")
  baidu_unit_talk_main()
  rospy.loginfo("process done and quit")
 except Exception, exc:
  print exc
           

人和機器對話,實際上是要把語句轉化成某種指令句式

譬如簡單的

向右轉,向前看,走你!停!

但是人的自然語言的表達,不會向軍訓似的簡潔,同樣的指令不同人會有不同表達方式。

雖然現在有各種的機器學習算法,但是機器對自然語言的了解還是很難處理的。

特别是機器對語言的上下文的聯系,一些幽默諷刺,弦外之音的了解都是非常困難的。

即使機器學習很厲害,能從海量資料中抽象出人類不能了解的“概念”,但總的說來,海量資料還是太有局限

機器人類對比從根本上的一點就是沒有感覺,沒有生活常識,沒有情感。

如果給機器加各種傳感器,全方位的海量資料,加上深度學習,了解能力肯定會提高,甚至會有智慧和情感。

這種高深的問題留給高人去處理吧。

将來的事将來再說。

現階段百度的工具來解決當下的問題還是很好的,能省時省力。

下面以大衆語言解釋解釋,個人覺得比官方的好了解。

人機對話時機器需要捕捉每句話語的訓示和思想(意圖)

而表達的意圖,總離不開一些關鍵的詞類組合在一起。

例如,“北京天氣”,意圖是查天氣,這是一個典型執行個體 “北京”屬于地點類的詞彙 ,“天氣”是表達意圖的必要關鍵詞

凡是屬于 [地點]+"天氣"的說法(模型、模闆)我們就把它了解為查天氣的意圖。

這就類似分類,某些詞類的組合就是一個新的抽象類别。這個抽象類别就是意圖

是以開發者要定義這些類别,并且給這些類别留出空位,使用者來填詞。

就跟填空題似的,

還是查天氣的模闆為例

我們定義為 [地點]+天氣

[   ]的位置就是一個槽,需要使用者去填,詞槽的概念可以這樣了解。

要讓開發者具體實作操作,百度UNIT分了這樣幾個操作層面

場景

.>技能

...>單元

.....>對話

.....>問答

對話分場景,比如火車站購票、飯店吃飯點菜、在家看電視、讀書、聽歌等,好吧,我想不出來了

技能就是所在場景能實作的功能,比如火車站購票視窗,能查詢、購買、改簽、退款這些個都是技能

查詢為例: 我想要明天(從北京)到上海的車票。

從這句話我們就能制定出一個模闆 [時間]+[出發地]+[目的地]+車票

這時候詞槽也有了

單元有兩種,分對話單元和問答單元,有詞槽的隻能是對話模式。因為這個模式能引導使用者把空槽填滿。

如果少了資訊,系統可以有适當的提示。

譬如,

使用者:我想要到個車票。

系統:去哪?

使用者:上海

系統:是何處出發?

使用者:北京

系統:那天?

使用者:明天

系統 查詢出結果

問答就是針對一個意圖設定不同說法,并給出的各種應答語句,可以多種多樣,但是是固定的。

對話樣本、特征詞、對話模闆見下面的官方文檔

http://ai.baidu.com/tech/unit

unit要釋出到雲上,進行調用。

https://cloud.baidu.com

UNIT基礎名詞

UNIT是一個專業的技術平台, 了解一些名詞和概念非常有必要。

    場景

    一個場景對應一個獨立完整的對話系統,用來滿足您某個具體業務場景的需求。通常按垂類劃分(例如,銀行信用卡辦理場景、電視遙控器場景等)。

    技能

    某一個方向的對話能力;技能分為自定義技能和預置技能,自定義技能完全由使用者配置,預置技能為UNIT提供的通用能力,支援開發者後期幹預。

    單元

    在UNIT裡,對話單元用來定義系統在一個具體的對話任務下對使用者對話的了解、以及機器人的回應方式,是系統中的最小對話機關。對話任務例如查天氣、查詢信用卡年費、控制空調溫度等。

    對話單元

    在每個對話單元裡,您需要明确出使用者對話的目的是什麼(即意圖)?要達成使用者這個目的,需要了解哪些關鍵資訊,或讓使用者提供哪些篩選條件(即詞槽)? 除此之外,還需要告訴對話系統在什麼情況下應該用什麼來回應使用者(即規則和答複)。

    問答單元

    問答單元,用于圈定某一個範圍的問答内容。在每個問答單元裡,您需要明确出這類問答内容對應的主題(即意圖)

    意圖

    意圖表示使用者的目的(例如,”北京天氣”,意圖是查天氣)。

    詞槽

    是滿足使用者意圖時的關鍵資訊或限定條件,可以了解為使用者需要提供的篩選條件。例如在查詢天氣時,詞槽是地點和時間。

    例如:“換到中央台”中的”中央台”就是一個”電視台詞槽”,它會一定程度上影響系統對”換台”這個意圖的執行。

    對話樣本

    對話樣本就是您給對話系統做示範,教它在使用者說的具體句子裡,該如何了解意圖,哪個詞是重要資訊,對應的詞槽是什麼。

    例如:通過對話樣本标注告訴機器人“三亞明天會不會下雨”與“三亞明日會下雨嗎”都是詢問天氣的語句,其中“三亞”是對應城市city這個詞槽,“明日”和“明天”都是time詞槽。 這樣的訓練越多,機器人的了解能力便越強,這與在學習語言中的人類孩童的學習方式也是十分相似的。

    特征詞

    特征詞通常被用于限制某條對話模闆的比對範圍(例如,天氣、下雨、熱等類别關鍵詞)或提供一定限度的泛化能力(例如,嗯、了、吧等語氣詞);活用特征詞機制可以事半功倍的提高對話模闆的精度和覆寫度。 特征詞詞典:開發者需自行導入詞典,用于系統識别特征詞。

    對話模闆

    對話模闆是您給對話系統按具體文法、句式做出的示範,教它在某一個特定文法、句式中,該如何了解意圖,哪個詞是重要資訊,對應的詞槽、特征詞是什麼。

    例如:“[D:sys_loc][D:sys_time]天氣如何”,上述标注表示可以将所有滿足“[城市]+[時間]+天氣如何”這一規則的query解析為WEATHERINFO意圖。如“北京今天天氣如何?”,“天津明天天氣如何”等。其中“[D:sys_loc]”表示所有城市詞組成的集合,“[D:sys_time]”表示所有時間描述組成的集合。

    問答對

    問題與答案的組合,稱之為問答對。問答對支援一對一、一對多、多對一和多對多;當某一個問題對中包含多個答案時,答案随機呈現。

    問答集

    問答集是承載問答對的容器,與問答單元一一對應,您可以批量将問答内容導入問答集,也可以線上編輯。

    訓練模型

    即把場景下所有的配置、标注的對話樣本、對話模闆等打包送出給系統來訓練模型。 模型生效一般需要幾分鐘時間。

    沙盒

    每個場景都配有一個沙盒環境,将訓練好的模型生效到沙盒環境後,就可以進行效果驗證了,同時可接入到您自己的業務系統中使用。您可以生成多個模型版本,但隻能選擇一個放到沙盒環境中。