IBM Watson 服務在 Bluemix 中的應用

IBM Watson 作爲認知計算的代表,近來受到諸多關注。以 Watson 爲核心構建的企業級應用已逐步上線,例如 Watson Engagement Advisor 和 Chef Watson。然而一般開發人員接觸到這些企業級應用的機會不多,因此 Watson 團隊提供了多個具有代表性的功能模塊,同時也部署在 Bluemix 中,作爲“服務”爲 Bluemix 中的應用提供與認知相關的計算能力。本文將會介紹 Bluemix 中的 Watson 服務,並通過一個簡單的 Web 應用程序,描述如何調用這些 Watson 服務實現相關功能,以及相關的注意事項。

Bluemix 中的 IBM Watson 服務介紹

使用 IBM id 登錄到 Bluemix 後,在 Catalog 中可以找到所有可用的 IBM Watson 服務,如圖 1 所示:

圖 1. Bluemix 中的 IBM Waston 服務

各個服務的功能描述和應用示例見表 1:

表 1.Bluemix 中 IBM Watson 服務的功能描述與應用舉例

服務名稱 功能描述 應用舉例
Concept Expansion 分析輸入的文字,給出在其他相似上下文中該文字所代表的含義 輸入“New York City”,Watson 會給出“The Big Apple”等相近概念
Language Identification 分析輸入的文字,給出該文字的語言種類 輸入“中國”,Watson 會給出“ZH-CN”
Machine Translation 分析輸入的文字,給出該文字所對應的目標語言的翻譯結果 輸入“我是中國人”,並指定目標語言爲英文,則 Watson 會給出“I am Chinese”
Message Resonance 分析輸入的文字,根據指定的目標受衆類型,給出該文字中所包含的單詞的接受程度。 輸入“Big data is popular”,並指定目標受衆類型爲“Cloud Computing”,則 Watson 會將輸入的每個單詞的接受度用不同顏色顯示出來。
Question Answer 分析輸入的問題,根據指定的問題領域,給出多個具有不同可信度的答案 輸入“How to stop smoking”,並指定問題領域爲“Healthcare”,則 Watson 會給出多個關於如何戒菸的方法以及相應的可信度。
Relationship Extraction 分析輸入的句子,給出其中每個單詞之間的相互關係或是該單詞在 Wikipedia 中的具體解釋(如果存在) 輸入“Michael Jordan is a basketball player”,則 Watson 會高亮“Michael Jordan”和“player”,認爲他們代表同一個人,並會給出 Wikipedia 關於該人物的解釋
Speech Recognition 分析輸入的語音信息,給出該語音信息所表達的文字信息(並非直接地語音識別) 輸入語音信息“I usually get up at 6:00 in the morning”,則 Watson 會分析該語音並給出文字信息“I get up early every morning, usually at 6:00”
Travel Ideation label Service 缺少官方文檔說明 N/A
User Modeling 從社交網絡中分析抽取相關信息,識別潛在的購買意圖或是商品喜好 暫未對外開放

回頁首

Bluemix 中的 IBM Watson 服務的使用方法

Watson 服務在使用之前,需要將其綁定到一個 Bluemix 應用中。綁定之後,可在 Bluemix 的 Web 控制檯中看到該服務的 Credential 信息,即調用服務的資源地址、用戶名和密碼等信息。這些信息默認是存儲在 Bluemix 應用中的系統環境變量中,我們也可將這些信息賦值給其他變量,從而使 Watson 服務也可以被 Bluemix 平臺以外的其他應用進行調用,見清單 1:

清單 1.使用 Java 獲取 Bluemix 中 Watson 服務的 Crendential 信息:

String VCAP_SERVICES = System.getenv("VCAP_SERVICES");

if(VCAP_SERVICES ==null){
//將 Watson 服務的 Credential 信息拷貝至此,
VCAP_SERVICES="{   \"Watson QAAPI-0.1\" : [ {     \"name\" : \"mt-svc\"......
//此處略去後面具體用戶信息
}
if (VCAP_SERVICES != null) {
try {
JSONObject obj = (JSONObject)JSON.parse(VCAP_SERVICES);
JSONArray service = obj.getJSONArray("Watson QAAPI-0.1");

// 獲取該服務的 Credential 信息
JSONObject catalog = service.getJSONObject(0);

// 解析 Credential 信息,並把相關字段賦值給本地成員屬性
JSONObject credentials = catalog.getJSONObject("credentials");
endpoint = credentials.getString("uri");
username = credentials.getString("userid");
password = credentials.getString("password");

} catch (Exception e) {

e.printStackTrace();

}
}

清單 1 展示的是使用 Java 語言來獲取並解析 Watson 服務中的“Question and Answer”服務的 Credential 信息。如果該應用是部署在 Bluemix 的 Liberty 容器中,則默認情況下,VCAP_SERVICES 是非空的。而當該應用是在 Bluemix 外部時,我們需要指定 VCAP_SERVICES 的值,如清單 1 的第三行所示。VCAP_SERVICES 中存儲的是當前應用所綁定的所有 Service 的 Credential 信息,因此我們可以根據 Service 的標識 ID 取出相應的 Credential 信息。在清單 1 中,Question and Answer 服務的標識 ID 爲 Watson QAAPI-0.1。之後,對於 Credential 信息的解析以及賦值,同樣適應於其他 Bluemix 服務。在拿到了這些信息之後,便可根據 Bluemix 服務所提供的調用 API,進行各種請求操作。

回頁首

IBM Watson 練習工程實踐

爲使您能夠更方便直觀地使用 Watson 服務,本小節會通過一個具體的 Web 應用開發來展示如何使用 Watson 服務實現相應的功能需求。

Web 應用所要實現的功能,是使用 Watson 的 Question and Answer 服務,對用戶輸入的問題進行回答,然後使用 Watson 的 Machine Translation 服務,將返回的答案由英文翻譯成中文。

Web 工程構建

由於我們要實現的功能較簡單,我們可以使用 JSP + Servlet 來實現 Web 前端和後端服務。因此我們可以使用 Eclipse 構建一個 Dynamic Web Project,如圖 2:

圖 2.Eclipse 中構建 Dynamic Web Project

頁面功能設計

由於本練習工程主要側重後端 Watson 服務的使用,所以在前端方面設計較爲簡單即可。只需要構建兩個頁面,一個用於輸入用戶信息(即具體問題),一個用於顯示 Watson 的返回信息(即問題的答案)。這兩個頁面如下圖所示:

圖 3.Watson 練習工程中的信息輸入頁面

圖 4.Watson 練習工程中的返回信息顯示頁面


頁面 1 的文本輸入域可放在一個 Form 中,通過 ask Watson 按鈕提交到後端的一個 servlet 進行處理。頁面 2 的返回信息可通過在 servlet 中使用 request.setAttribute()方法將 Watson 返回的信息放入其中,然後在頁面 2 中調用 request.getAttribute()將其取出並在頁面顯示。由於頁面邏輯較爲簡單,因此這裏不再給出有關頁面方面的代碼。

後端業務處理

在後端,我們需要有一個 servlet 來處理通過前端頁面提交過來的請求,還需要有一個服務類來專門處理同 Watson 服務之間的交互,因此,後端的類圖如下:

圖 5.Watson 練習工程後端類圖


QAServlet 比較簡單,它所調用的大部分方法都在 WatsonService 類中。WatsonService 負責獲取與 Watson 服務相關的各種認證信息,以及與遠程 Watson 服務之間的交互。關於認證信息的獲取,這裏會在 WatsonService 類被創建時來初始化 username 和 password 等,具體方法請參照前面的清單一。

與 Watson 服務之間的交互方面,按照設想我們會使用兩個服務,所以每個服務各對應一個方法。askWatson 方法的具體代碼如清單二所示:

清單 2.Watson 練習工程後端 askWatson 方法代碼

public static String askWatson(String question) throws Exception { 	
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
provider.setCredentials(AuthScope.ANY, credentials);

SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
CloseableHttpClient client = HttpClients.custom().
setSSLSocketFactory(sslsf).
setDefaultCredentialsProvider(provider).
build();

HttpPost httpPost = new HttpPost(requestURI);
StringEntity ent = new StringEntity("{\"question\" : {\"questionText\" : \"" + question + "\"}}");
ent.setContentType("application/json");
httpPost.setEntity(ent);
httpPost.setHeader("X-SyncTimeOut", "60");

HttpResponse response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
String responseString = EntityUtils.toString(entity, "UTF-8");

return responseString;
}

askWatson 方法接受一個字符串類型的參數作爲方法的輸入,然後使用 Apache 的 httpclient 包中的相關類構建了一個 client 對象,包含相關 credential 信息。接下來構造一個 HttpPost 對象,它的 Entity 需要使用 StringEntity,String 的格式需要參考 Watson 服務的 API(關於 Watson 服務的 API,它的文檔鏈接都會放在該服務在 Bluemix 中的圖標下面)。然後使用 client 對象來執行 HttpPost 請求,並將返回結果保存到 HttpResponse 對象中,再進行進一步處理,就可得到返回的答案字符串。

WatsonService 類中的另一方法 translateText 會對返回的答案字符串進行翻譯。由於 Question and Answer 服務返回的都是英文結果,因此我們可以使用 Watson 的翻譯服務將其由英文翻譯爲中文。

translateText 方法同樣接受一個字符串類型的參數作爲方法的輸入,方法的返回值即是對輸入參數的翻譯結果。具體代碼如清單三所示:

清單 3.Watson 練習工程後端 translateText 方法代碼

public String translateText(String text){
//指定字符串的語言類型及所需翻譯的目標語言類型
String sid = "mt-enus-zhcn";
String result = "";
try {
String post = "rt=text&sid=" + URLEncoder.encode(sid, "UTF-8") +
"&txt=" + URLEncoder.encode(text, "UTF-8");
// 準備 Http 連接
HttpURLConnection conn = (HttpURLConnection)new URL(endpoint).openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "*/*");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
String auth = username + ":" + password;
String encoded = new String(Base64.encodeBase64(auth.getBytes()));
conn.setRequestProperty("Authorization", String.format("Basic %s", encoded));
DataOutputStream output = new DataOutputStream(conn.getOutputStream());
//建立連接
conn.connect();
//發送 post 請求
output.writeBytes(post);
output.flush();
output.close();
// 讀取返回結果
BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
if (conn.getResponseCode()==HttpURLConnection.HTTP_OK)
System.out.println("Response OK  from: "+conn.getURL().toString());
else
System.err.println("Unsuccesful response: "+conn.getResponseCode()+ " from: "+conn.getURL().toString());
String line = "";
StringBuffer buf = new StringBuffer();
while ((line = rdr.readLine()) != null) {
buf.append(line);
buf.append("\n");
}
rdr.close();
return buf.toString();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}

return result;
}

不同於 askWatson 方法,這裏對 Http 連接和請求的構建,使用的是 JDK 的 API 而並非 Apache HttpClient,對返回結果的處理也同樣使用 JDK 的 API。當然,使用 Apahce HttpClient 也可實現這一功能。代碼中需要特別說明的是對 sid 變量的指定。sid 變量聲明的是輸入字符串的語言類型及所要翻譯的目標語言類型。該變量必須被包含在 HttpPost 請求中,否則 Watson 翻譯服務會認爲請求非法。目前 Bluemix 中的 Watson 翻譯服務只開放了有限的幾種語言之間的文字翻譯,其實用性還有待提高。

WatsonService 類中還有一個私有方法 parseResult,作用是對 Watson 服務所返回的字符串信息進行處理。本練習工程中用到的這兩個 Watson 服務所返回的字符串格式是不同的。Watson 翻譯服務所返回的字符串格式較爲簡單,不需要做特殊處理即可直接使用。而 Question and Answer 服務返回的是 JSON 字符串,因此需要使用 parseResult 方法來處理這個 JSON 字符串,將其中有用的域值抽取出來,作爲結果返回給調用層。由於對 JSON 字符串處理的代碼較爲常見,因此這裏不再給出 parseResult 方法的具體內容。

對於另一個類 QAServlet 而言,其 doGet 或 doPost 方法可直接調用 WatsonService 中提供的各種方法來對從前端得到的字符串進行處理,其代碼如下:

清單 4.Watson 練習工程後端 QAServlet 類的主要代碼

protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
  //讀取前端頁面輸入的問題
  String question = req.getParameter("question");
  WatsonService watsonSvc = new WatsonService();
  
  String answer =null;
  String translatedAnswer = null;
  try {
   //對問題進行回答
    answer = watsonSvc.askWatson(question);
    //對答案進行翻譯
    translatedAnswer = watsonSvc.translateText(answer);
    
    req.setAttribute("answer", translatedAnswer);
    req.getRequestDispatcher("result.jsp").forward(req,resp); 
    
  } catch (Exception e) {
    e.printStackTrace();
  }
}

QAServlet 會將從前端得到的問題進行回答並翻譯,然後放到 request 對象中,並使前端頁面跳轉到 result.jsp 來對結果進行顯示。

以上是對 Watson 練習工程中主要的類及方法的詳細說明。通過該練習工程,希望您在實際應用中能注意到,Bluemix 中的 Watson 服務有着不同的 API,在進行調用時,需要首先參考其 API 文檔,使用合適的方式進行調用。同時,不同的 Watson 服務所返回的結果格式也不盡相同,有可能需要做進一步處理才能夠使用。

Watson 練習工程可被導出成 WAR 包,部署在 Bluemix 外部的應用服務器上直接運行。Bluemix 提供了 Liberty server 來運行 Java Web 程序,因此我們也可以將該 WAR 包部署到 Bluemix 平臺中,具體的部署方法及步驟請參考 Bluemix 官方文檔 。

回頁首

結語

Bluemix 爲開發人員提供了一個可快速開發、部署和管理應用的平臺,它的開放性使得開發者可以提供自己的服務併發布到平臺上,供其他開發人員使用。IBM Watson 團隊目前在 Bluemix 平臺開發並提供的這 9 種服務,雖然成熟度不高,但這已彰顯出一種趨勢,即 Bluemix 所提供的平臺,是今後認知計算服務發揮作用的舞臺之一。開發都通過 Bluemix 使用認知計算服務,可以避免各種環境的重複搭建,更專注於認知計算本身的研究與使用。本文通過一個實際的練習工程的開發,希望起到拋磚引玉的作用,使更多開發人員對 Bluemix 和 Watson 服務產生興趣,並運用它們開發出更加富有創新性的應用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章