1. 概述
意图识别是在语音识别(ASR)的基础上,对识别出的语音文本信息进行语义理解,找出话者想要表达的确切意图。意图识别的结果依赖不同领域的大数据模型,针对不同的领域,需要使用不同的资源文件。综上意图识别是一种复合能力,它结合了语音识别(ASR)自由说和语义理解(NLU)两种能力。
在集成过程中如有疑问,可登录灵云开发者论坛,查找答案或与其他开发者交流。
本地意图识别:
本地意图识别即离线识别模式,加载本地模型并调用本地识别引擎进行意图识别工作。
云端意图识别:
云端识别即在线识别模式,调用云端引擎进行意图识别工作。
1.2 能力定义(Capkey)
需要说明的是本地意图识别能力(asr.local.dialog)在开发者网站并没有指定的能力可以选择,这是因为本地意图识别能力(asr.local.dialog)结合了本地自由说识别(asr.local.freetalk)和本地语音理解(nlu.local.recog)这两种能力。开发者在使用本地意图识别(asr.local.dialog)时,应该在开发者网站开通以上两种能力(asr.local.freetalk 和 nlu.local.recog)
capkey | 说明 | 领域 | 所需资源文件 |
---|---|---|---|
asr.local.dialog (dialogMode=freetalk) |
本地意图识别 (自由说+NLU) |
ASR freetalk模型 NLU engine模型 NLU resource模型(根据intention选择不同领域模型,如weather) |
ft_decoder.conf ispk_dnn.dat ispk_hclg.dat ispk_blm.dat ispk_slm.dat tdle_bi_tag.txt tdle_cmn.bin tdlr_ent.txt tdlr_ptr.txt tdlr_tag.txt |
asr.local.dialog (dialogMode=grammar) |
本地意图识别(语法+NLU) | ASR grammar模型 NLU engine模型 NLU resource模型(根据intention选择不同领域模型,如weather) |
grm_decoder.conf ispk_aux.dat ispk_dnn.dat ispk_g2p.dat tdle_bi_tag.txt tdle_cmn.bin tdlr_ent.txt tdlr_ptr.txt tdlr_tag.txt |
asr.cloud.dialog | 云端意图识别 |
2. 准备工作
本地意图识别在不同领域所需要的资源文件存在重名情况。其中 engine 资源(tblebi_tag.txt,tble_cmn.bin)与领域无关,在不同领域间是相同的,可以只导入一份到工程目录中,而 resource 资源(tdlr_ent.txt,tdlr_ptr.txt,tdlr_tag.txt)则存在同名情况,这时导入工程目录需将文件加相应前缀,不同于 resPrefix 参数设置的是,不同领域间的前缀固定为领域的命名,如天气(weather)领域,则需要加 `weather` 前缀以示区别。
加载前缀后,导入工程目录如下图所示。
3. 意图识别能力集成
3.1 通用模块初始化
第一步需要先初始化灵云 SDK 的 SYS 通用模块。详见灵云SDK开发手册
Android代码
// 创建初始化参数辅助类 InitParam initparam = new InitParam(); // 授权文件所在路径,此项必填 String authDirPath = context.getFilesDir().getAbsolutePath();; initparam.addParam(InitParam.PARAM_KEY_AUTH_PATH, authDirPath); // 灵云云服务的接口地址,此项必填 initparam.addParam(InitParam.PARAM_KEY_CLOUD_URL, "http://api.hcicloud.com:8888"); // 开发者密钥,此项必填,由捷通华声提供 initparam.addParam(InitParam.PARAM_KEY_DEVELOPER_KEY, "01234567890"); // 应用程序序号,此项必填,由捷通华声提供 initparam.addParam(InitParam.PARAM_KEY_APP_KEY, "1234abcd"); // 日志的路径,可选,如果不传或者为空则不生成日志 String logDirPath = "/storage/emulated/0/sinovoice/com.sinovoice.example/log"; initparam.addParam(InitParam.PARAM_KEY_LOG_FILE_PATH, logDirPath); // 日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试,SDK将输出小于等于logLevel的日志信息 initparam.addParam(InitParam.PARAM_KEY_LOG_LEVEL, "5"); // 灵云系统初始化 // 第二个参数在Android平台下,必须为当前的Context int errCode = HciCloudSys.hciInit(initparam.getStringConfig(), this); if(errCode != HciErrorCode.HCI_ERR_NONE) { // "系统初始化失败" return; }
C++代码
HCI_ERR_CODE err_code = HCI_ERR_NONE; // 配置串是由"字段=值"的形式给出的一个字符串,多个字段之间以','隔开。字段名不分大小写。 string init_config = ""; // 灵云应用序号 init_config += "appKey=1234abcd"; // 灵云开发者密钥 init_config += ",developerKey=1234567890"; // 灵云云服务的接口地址 init_config += ",cloudUrl=http://api.hcicloud.com:8888"; // 授权文件所在路径,保证可写 init_config += ",authpath=../../bin"; // 日志的路径 init_config += ",logfilepath=../../bin"; // 日志的等级 init_config += ",loglevel=5"; // 日志文件的大小 init_config += ",logfilesize=512"; // 其他配置使用默认值,不再添加,如果想设置可以参考开发手册 err_code = hci_init( init_config.c_str() );
iOS代码
HCI_ERR_CODE errCode = HCI_ERR_NONE; //appkey, developerKey, cloudUrl, authPath为必填项。其他可配置信息请查询灵云SDK开发手册hci_init函数说明。 NSString *appkey = @""; //开发者社区申请所得 NSString *developerKey = @""; //开发者社区申请所得 NSString *cloudUrl = @""; //开发者社区申请所得 NSString *authPath = @""; //授权文件所在路径,一般设置为沙盒Document路径 NSString *config = [NSString stringWithFormat:@"appkey=%@,developerKey=%@,cloudUrl=%@,authPath=%@", appkey, developerKey, cloudUrl, authPath]; errCode = hci_init(config.UTF8String); if (errCode != HCI_ERR_NONE && errCode != HCI_ERR_SYS_ALREADY_INIT) { NSLog(@"hci_init failed, error: %@", errCode); }else{ NSLog(@"hci_init success"); }
3.2 授权检测
在初始化灵云 SDK 的 SYS 通用模块后,还需要调用授权检测函数去云端获取授权。
Android代码
// 获取授权 private int checkAuthAndUpdateAuth() { // 获取系统授权到期时间 int initResult; AuthExpireTime objExpireTime = new AuthExpireTime(); initResult = HciCloudSys.hciGetAuthExpireTime(objExpireTime); if (initResult == HciErrorCode.HCI_ERR_NONE) { // 显示授权日期,如用户不需要关注该值,此处代码可忽略 Date date = new Date(objExpireTime.getExpireTime() * 1000); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd",Locale.CHINA); Log.i(TAG, "expire time: " + sdf.format(date)); if (objExpireTime.getExpireTime() * 1000 > System.currentTimeMillis()) { // 已经成功获取了授权,并且距离授权到期有充足的时间(>7天) Log.i(TAG, "checkAuth success"); return initResult; } } // 获取过期时间失败或者已经过期 initResult = HciCloudSys.hciCheckAuth(); if (initResult == HciErrorCode.HCI_ERR_NONE) { Log.i(TAG, "checkAuth success"); return initResult; } else { Log.e(TAG, "checkAuth failed: " + initResult); return initResult; } }
C++代码
//获取过期时间 int64 expire_time; int64 current_time = (int64)time( NULL ); HCI_ERR_CODE err_code = hci_get_auth_expire_time( &expire_time ); if( err_code == HCI_ERR_NONE ) { //获取成功则判断是否过期 if( expire_time > current_time ) { //没有过期 printf( "auth can use continue\n" ); return true; } } //获取过期时间失败或已经过期 //手动调用更新授权 err_code = hci_check_auth(); if( err_code == HCI_ERR_NONE ) { //更新成功 printf( "check auth success \n" ); return true; } else { //更新失败 printf( "check auth return (%d:%s)\n", err_code ,hci_get_error_info(err_code)); return false; }
iOS代码
//获取过期时间 int64 expireTime; int64 currentTime = (int64)time(NULL); HCI_ERR_CODE errCode = hci_get_auth_expire_time(&expireTime); if (errCode == HCI_ERR_NONE) { if (expireTime < currentTime) { errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ //没有过期 NSLog(@"auth can use continue"); return YES; } } //读取授权文件出现错误 else if(errCode == HCI_ERR_SYS_AUTHFILE_INVALID){ errCode = hci_check_auth(); if(errCode == HCI_ERR_NONE){ NSLog(@"check auth success"); return YES; }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; } }else{ NSLog(@"check auth failed, error: %@", errCode); return NO; }
3.3 能力初始化
在初始化灵云 SDK 的 SYS 通用模块和授权检测成功后,需调用 ASR 初始化函数。详见灵云SDK开发手册。
Android代码
// 创建初始化参数辅助类 AsrInitParam asrInitParam= new AsrInitParam (); // 设置本地资源库的路径 asrInitParam.addParam(AsrInitParam .PARAM_KEY_DATA_PATH, "/storage/emulated/0/sinovoice/com.sinovoice.example/data"); asrInitParam.addParam(AsrInitParam .PARAM_KEY_INIT_CAP_KEYS, "asr.local.dialog"); int errCode = HciCloudAsr.hciAsrInit(asrInitParam.getStringConfig());
C++代码
string asr_init_config = ""; //建议在initcapkeys中添加需要使用的所有能力以提高第一次识别的效率 asr_init_config += "initcapkeys=asr.local.dialog"; //如果是本地能力,此处指定本地能力依赖资源路径 asr_init_config += ",datapath=../../data"; err_code = hci_asr_init(asr_init_config.c_str()); if (err_code != HCI_ERR_NONE) { printf("hci_asr_init return (%d:%s) \n",err_code,hci_get_error_info(err_code)); return; }
iOS代码
//initCapKeys为必填项,如果为本地意图识别dataPath为必填项。其他可配置信息请查询开发手册hci_asr_init函数说明。 NSString *dataPath = [[NSBundle mainBundle] pathForResource:@"data" ofType:Nil]; //本地意图识别识别所需本地资源所在路径 NSString *initCapKeys = @"asr.local.dialog"; //语音识别(ASR)所需使用的能力列表,可以包含多种能力,用';'分号隔开 NSString *config = [NSString stringWithFormat:@"dataPath =%@, initCapKeys =%@", dataPath, initCapKeys]; HCI_ERR_CODE errCode = HCI_ERR_NONE; errCode = hci_asr_init(config.UTF8String); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_init failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_init success"); return YES;
3.4 开启会话(session)
ASR 是通过会话(session)来管理识别过程的。在ASR能力初始化成功之后,需要通过开启识别会话来完成识别。详见灵云SDK开发手册
Android代码
AsrConfig config = new AsrConfig(); // 启动 ASR Session int errCode = -1; //系统初始化可以添加多个能力,但每次对话session开启只能指定一个能力 config.addParam(AsrConfig.SessionConfig.PARAM_KEY_CAP_KEY, "asr.local.dialog"); config.addParam("intention", "weather"); //会话开启的字符串配置 String sSessionConfig = config.getStringConfig(); Session nSessionId = new Session(); errCode = HciCloudAsr.hciAsrSessionStart(sSessionConfig, nSessionId);
C++代码
int nSessionId = -1; string strSessionConfig = "capkey=asr.local.dialog,intention=weather"; //此处也可以传入其他配置,参见开发手册,此处其他配置采用默认值 err_code = hci_asr_session_start( strSessionConfig.c_str(), &nSessionId ); if( err_code != HCI_ERR_NONE ) { printf( "hci_asr_session_start return (%d:%s)\n", err_code ,hci_get_error_info(err_code)); return false; }
iOS代码
//开启session HCI_ERR_CODE errCode = HCI_ERR_NONE; NSString *config = @"capKey=asr.cloud.dialog,intention=weather"; int sessionID; //开启会话成功后,会返回的会话ID errCode = hci_asr_session_start(config.UTF8String, &sessionID); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_session_start failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_session_start success"); return YES;
3.5 语音识别
在启动会话(session)成功后,即可可以进行 ASR 识别过程。识别功能通过调用识别函数完成,如果是流式识别/实时反馈,则需要多次调用识别函数将语音数据送入 SDK。。配置参数详见灵云SDK开发手册
Android代码
String audioFile = "sinovoice.pcm"; // 从assets资源目录获取sinovoice.pcm 语音数据文件 byte[] voiceData = Tools.getAssetFileData(this,audioFile); if (null == voiceData) { // ShowMessage("Open input voice file" + audioFile + " error!"); return; } if (HciErrorCode.HCI_ERR_NONE != errCode) { // ShowMessage("hciAsrSessionStart error:" + HciCloudSys.hciGetErrorInfo(errCode)); return; } ShowMessage("hciAsrSessionStart Success"); // 识别结果 AsrRecogResult asrResult = new AsrRecogResult(); // 识别方法 此处的两个参数( 语音识别配置串,识别语法数据)在SessionStart的时候已经传入了,此处省略 errCode = HciCloudAsr.hciAsrRecog(nSessionId, voiceData, null, null, asrResult); if (HciErrorCode.HCI_ERR_NONE == errCode) { // 输出识别结果 printAsrResult(asrResult); } // 输出识别结果的方法 private void printAsrResult(AsrRecogResult recogResult) { if (recogResult.getRecogItemList().size() < 1) { ShowMessage("recognize result is null"); } for (int i = 0; i < recogResult.getRecogItemList().size(); i++) { if (recogResult.getRecogItemList().get(i).getRecogResult() != null) { String utf8 = recogResult.getRecogItemList().get(i).getRecogResult(); ShowMessage("result index:" + String.valueOf(i) + " result:" + utf8); } else { ShowMessage("result index:" + String.valueOf(i) + " result: null"); } } }
C++代码
ASR_RECOG_RESULT asrResult; string recog_config = "audioFormat=pcm16k16bit,encode=none"; err_code = hci_asr_recog( nSessionId, voiceData.buff_, voiceData.buff_len_, recog_config.c_str(), NULL, &asrResult ); if( err_code == HCI_ERR_NONE ) { printf( "hci_asr_recog success\n" ); // 输出识别结果 PrintAsrResult( asrResult ); // 释放识别结果 hci_asr_free_recog_result( &asrResult ); } else { printf( "hci_asr_recog return (%d:%s)\n", err_code ,hci_get_error_info(err_code)); }
iOS代码
HCI_ERR_CODE errCode = HCI_ERR_NONE; ASR_RECOG_RESULT asrResult; //识别结果保存 NSString *config = @"audioFormat=pcm16k16bit,encode=none"; //_sessionID:为开启会话(session)后获得的sessionID //_voiceData:为音频数据文件,数据类型为NSData errCode = hci_asr_recog(_sessionID, (void *)_voiceData.bytes, (unsigned int)_voiceData.length, config.UTF8String, NULL, &asrResult); if (errCode != HCI_ERR_NONE) { NSLog(@"hci_asr_recog failed, error:%@"errCode); }else{ NSLog("hci_asr_recog success"); NSString *strResult = nil; if (asrResult.psResultItemList > 0) { if (asrResult.psResultItemList[0].pszResult!= NULL){ strResult = [NSString stringWithUTF8String:asrResult.psResultItemList[0].pszResult]; } } if (strResult && strResult.length) { //识别结果 }else{ } hci_asr_free_recog_result(&asrResult); }
识别完成结果示例:
{ "result": "今天天气怎么样", "answer": { "content": { "date": "2017-08-28", "description": "晴", "direction": "北风", "high": 28, "location": "北京", "location_id": 101010100, "low": 17, "power": "3-4级" }, "intention": { "action": "query", "date": "20170828", "domain": "weather", "entry_score": 5002100 } } }
3.6 结束识别
最后我们需要反初始化,依次关闭会话,终止 ASR 能力,关闭灵云系统。
Android代码
// 终止session HciCloudAsr.hciAsrSessionStop(nSessionId); //反初始化ASR HciCloudAsr.hciAsrRelease(); //ShowMessage("hciAsrRelease"); HciCloudSys.hciRelease();
C++代码
HCI_ERR_CODE errCode = HCI_ERR_NONE; //关闭会话 errCode = hci_asr_session_stop(sessionID); //sessionID:已经开启的会话的sessionID //终止ASR能力 errCode = hci_asr_release(); //关闭灵云系统 errCode = hci_release();
iOS代码
//结束session后,调用hci_asr_release和hci_release函数结束能力调用 HCI_ERR_CODE errCode = HCI_ERR_NONE; errCode = hci_asr_session_stop(sessionID); //sessionID:已经开启的会话的sessionID if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_session_stop failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_session_stop success"); errCode = hci_asr_release(); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_asr_release failed, error:%@", errCode); return NO; } NSLog(@"hci_asr_release success"); errCode = hci_release(); if (errCode != HCI_ERR_NONE){ NSLog(@"hci_release failed, error:%@", errCode); return NO; } NSLog(@"hci_release success"); return YES;