本篇介紹如何整合 LUIS (Language Understanding Intelligent Service) 去瞭解用戶說的話。
Amazon echo, Google Home, 天貓精靈 或是即將上市的 小米 AI 音箱 均是利用語音輸入完成用戶的任務。
口説的内容利用 自然語言處理 (Natural Language Processing, NLP) 技術訓練 AI ,讓它懂得用戶説出來内容,
例如:一段句子裏面有多少關鍵字,句子主要的意圖 等,再讓開發人員針對不同的意圖與關鍵字執行撰寫後面的處理邏輯。
在整個 Cortana Skill 架構裡,首先透過 Cortana 幫忙把 Speech 轉成文字,再交給設定在 Bot Framework portal 的 Messaging endpoint。
Messaging endpoint 可以整合 LUIS 或是其他第三方的 NLP 系統分析文字瞭解句子的意圖。
我在 簡單建立一個 Cortana Skill 也介紹過,如下圖:
如果 Messaging endpoint 有使用到 LUIS apps,可以在 Bot Framework portal 加入用到的 LUIS apps 讓 Bot 實現最佳的語音識別。
如何建立一個 LUIS app 本篇不多做説明,可以參考 [Azure] 建立Microsoft LUIS的App服務,進行語意識別的訓練並整合Bot Framework 或是官方文件 Learn about Language Understanding Intelligent Service (LUIS)。
Bot Framework SDK 提供 Microsoft.Bot.Builder.Luis,幫助開發人員將原本的 Message endpoint 加上支援 LUIS app。
加入的步驟:
1. 建立一個 class 並且繼承 LuisDialog,修改 Message Controller 指定的預設 Dialog (當然也可改用其他 Message Controller 處理)
[BotAuthentication]
public class MessagesController : ApiController
{
public async Task Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
// 改用自定義的新類別
await Conversation.SendAsync(activity, () => new Dialogs.MyLuisDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
}
2. 在繼承 LuisDialog 的 class,加入 LuisModelAttribute 的設定 (將使用的 LUIS app id 與 SubscriptionKey 加入)3. 在 class 中加入要處理該 Luis app 定義的 intent 的 methods (利用 LuisIntentAttribute),如下的程式片段:
[LuisModel("{ModelID}", "{SubscriptionKey}")]
[Serializable]
public class MyLuisDialog : LuisDialog<object>
{
[LuisIntent("None")]
public async Task None(IDialogContext context, LuisResult result)
{
// 回應對方說的内容無法被 LUIS 識別出來
string message = $"Sorry, I did not understand '{result.Query}'. Type 'help' if you need assistance.";
await context.PostAsync(message);
context.Wait(this.MessageReceived);
}
[LuisIntent("Question")]
public async Task Question(IDialogContext context, LuisResult result)
{
if (result.TopScoringIntent == null)
{
await None(context, result);
return;
}
// 利用 Entity Name 得到内容
EntityRecommendation accountEntity = null;
if (result.TryFindEntity("account", out accountEntity))
{
// 舉例 account 為 entity name, 如果可以拿到去必要的邏輯
}
context.Wait(this.MessageReceived);
}
}
這樣就完成了最簡單整合 LUIS,詳細的官方範例可以參考 Microsoft/BotBuilder-Samples。介紹剛才用到的關鍵元素:
- LuisDialogLuisDialog.cs 整合 LUIS 最重要的元件,搭配 LuisModelAttribute 設定預設的 LUIS app,或是加入多個 ILuisSErvice 可以同時處理多個 LuisResult。
- LuisModelAttribute定義 LUIS model 資訊,LuisDialog.cs 在建構子利用 MakeServicesFromAttributes()來抓出該 dialog 有標記多少個 LuisModel 。
- LuisIntentAttribute可以用來標記 dialog method 屬於那個 LUIS intent。被標記 Luis 會在 LuisDialog.cs 中被先截取出來,根據 LUIS app 回復的結構找到適合的 intent 來轉給標記的 method。
- LuisRequest包含所有要發送到 LUIS app 的相關屬性,可在 LuisService.cs 看到它如何被建立。
- LuisService實作 ILuisSErvice interface 負責與 Luis.ai 設定的 LUIS app 互動。透過 LuisService.cs 可以發現裏面利用設定的 ModelID 與 SubscriptionKey 組合成 Uri 搭配 HttpClient 來得到 LuisResult。
- LuisResult屬於 Microsoft.Bot.Builder.Luis.Models 下的類別,代表執行的結果,裏面有 EntityRecommendation,IntentRecommendation 等可以幫助判斷這次語言的識別是否符合預期找到特定的 Intent 與 Entity。
[補充]
想要同時使用多個 LUIS model 的話,要怎麽做? 根據官方的説明 How can I use more than one LUIS model? 是可以支援的。
根據 LuisDialog.cs 可以看到:
public ILuisService[] MakeServicesFromAttributes()
{
// 從標記 LuisModel 抓取看有多少 LuisService
var type = this.GetType();
var luisModels = type.GetCustomAttributes(inherit: true);
return luisModels.Select(m => new LuisService(m)).Cast().ToArray();
}
public LuisDialog(params ILuisService[] services)
{
// 如果建構子沒有指定給 ILuisService 就會利用 MakeServicesFromAttributes 來得到 LuisService
if (services.Length == 0)
{
services = MakeServicesFromAttributes();
}
SetField.NotNull(out this.services, nameof(services), services);
}
因此,您可以選擇使用其中一種方式來加入多個 Luis model, 如下:
一個 dialog 整合了多個 Luis model,你會遇到一個問題: 怎麽都只會進去某個特定 Luis model 的 intents。[BotAuthentication] public class MessagesController : ApiController { public async Task Post([FromBody]Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new Dialogs.MyLuisDialog( new LuisService(new LuisModelAttribute("{ModelID1}", "{SubscriptionKey1}")), new LuisService(new LuisModelAttribute("{ModelID2}", "{SubscriptionKey2}")) )); } } } // 或是 [LuisModel("{ModelID1}", "{SubscriptionKey1}")] [LuisModel("{ModelID2}", "{SubscriptionKey2}")] [Serializable] public class MyLuisDialog : LuisDialog<object> { }
問題就在於 BestResultFrom() 如下程式碼:
protected virtual LuisServiceResult BestResultFrom(IEnumerable results)
{
// 直接從 results 中抓出 BestIntent 分數最高的
return results.MaxBy(i => i.BestIntent.Score ?? 0d);
}
這樣的寫法在一些 Luis model 的判斷永遠都會是 intent 為 None 或是空白 這些預設的為最高分。因此,我們需要調整一下,如下:protected override LuisServiceResult BestResultFrom(IEnumerable results)
{
// 如果只有一個 result 就直接用預設的
if (results.Count() < 1)
{
return base.BestResultFrom(results);
}
else
{
// 多個 results ,檢查裏面的 intents 都是什麽類型
var emptyResult = results.Where(x => x.Result.TopScoringIntent.Intent == "None" || string.IsNullOrEmpty(x.Result.TopScoringIntent.Intent)).ToList();
if (emptyResult.Count == results.Count())
{
// 代表多個 results 都無法辨識這次的 query text
return base.BestResultFrom(results);
}
else
{
// 如果不是,則去掉 None 或是 空白的 intent
var bestResult = results.Where(x => x.Result.TopScoringIntent.Intent != "None" || !string.IsNullOrEmpty(x.Result.TopScoringIntent.Intent)).FirstOrDefault();
return bestResult;
}
}
}
這樣就可以正確找到預期的 intent 了。另外,要 注意 同一個 message endpoint 支援多個 LUIS model 需要考量幾個用途:
- Luis model 之間是否有關聯性? 因爲多個 Luis model 同時使用就需要從裏面找出最適合的 intent
- 整合太多的 Luis Model 會造成交易變慢,建議先定義出基本的識別結構,再轉給特定結構的 dialog (在 dialog 整合特定的 Luis model) 處理
Cortana Skill 目前只支援 English,所以上面的介紹都是用英文爲主。
像我們講中文的怎麽辦? LUIS 有支援中文或是選擇其他語言,只要先做好 Speech to Text,拿到 Text 之後都有辦法識別。
因爲有很多内容與 Bot Framework 相關,如果閲讀上有問題建議看一下 Bot Framework 的介紹。
希望對大家有幫助,謝謝。
References:
- Dialogs in the Bot Builder SDK for .NET
- Troubleshooting general problems
- Bot Framework
- Create messages
- Microsoft/BotBuilder-Samples
- Microsoft/AzureBot
- Custom BotFramework Intent Service Implementation
- LuisService.cs
- BotBuilder/CSharp/Library/Microsoft.Bot.Builder/Luis/LuisService.cs
- Multiple LUIS models from config
沒有留言:
張貼留言